(Years ago I sort of had the same experience on Flickr, but I believe that more or less went away later for a while. It returned a couple of years ago, and I quietly switched from using Firefox on Flickr to mostly using Chrome.)
This result sort of surprises and depresses me (partly because using Chrome has its pains). My understanding is that in theory Firefox and Chrome are usually relatively neck and neck as far as performance goes, with Firefox at least competitive, and that especially on common sites Firefox should not be laggy. There are a number of things that could be causing this for me and not for other people, especially general users. For a start I'm on Linux and using Fedora's build of Firefox instead of the Mozilla build, while I think most performance comparisons are made on Windows or MacOS and use the official Mozilla builds.
(I'm also using a relatively odd Linux environment with relatively modest OpenGL and compositing support, which might hurt Firefox more than Chrome.)
(And I'm using some extensions in Chrome's incognito mode that I would expect to be sort of heavyweight, like uBlock Origin and a mouse gestures extension.)
PS: I care about this partly because I dislike some things Google does with Chrome and partly because I care about Firefox being competitive and good in general. The overall web ecology benefits when we have a real choice in browsers, and part of having a real choice is good performance.
(I also think that Mozilla fundamentally cares more about Linux for Firefox than Google does for Chrome. As a non-Windows, non-Mac user, I remember the days when I was a second class citizen on the web and I would rather like to not go too far back to them.)
Shrinking the partitions of a software RAID-1 swap partition
A few days ago I optimistically talked about my plans for a disk
shuffle on my office workstation,
by replacing my current 1 TB pair of drives (one of which had failed)
with a 1.5 TB pair. Unfortunately when I started putting things
into action this morning, one of the 1.5 TB drives failed immediately.
We don't have any more spare 1.5 TB drives (at least none that I
trust), but we did have what I believe is a trustworthy 1 TB drive,
so I pressed that into service and changed my plans around to be
somewhat less ambitious and more lazy. Rather than make a whole new
set of RAID arrays on the new disks (and go through the effort of
adding them to
/etc/mdadm.conf and so on), I opted to just move
most of the existing RAID arrays over to the new drives by attaching
and detaching mirrors.
This presented a little bit of a problem for my mirrored swap partition, which I wanted to shrink from 4 GB to 1 GB. Fortunately it turns out that it's actually possible to shrink a software RAID-1 array these days. After some research, my process went like this:
- Create the new 1 GB partitions for swap on the new disks as part
of partitioning them. We can't directly add these to the existing
/dev/md14, because they're too small.
- Stop using the swap partition because we're about to drop 3/4ths
of it. This is just '
- Shrink the amount of space to use on each drive of the RAID-1
array down to an amount of space that's smaller than the new
mdadm --grow -z 960M /dev/md14
I first tried using
--array-size) to shrink the array size non-persistently, but
mdadmstill rejected adding a too-small new array component. I suppose I can't blame it.
- Add in the new 1 GB partitions and pull out the old 4 GB partition:
mdadm --add /dev/md14 /dev/sdc3 # (wait for the resync to finish) mdadm --add /dev/md14 /dev/sdd3 mdadm --repace /dev/md14 /dev/sde4 # (wait for the resync to finish) madam -r /dev/md14 /dev/sde4
- Tell software RAID to use all of the space on the new partitions:
mdadm --grow -z max /dev/md14
At this point I almost just
swapon'd the newly resized swap
partition. Then it occurred to me that it probably still had a
swap label that claimed it was a 4 GB swap area, and the kernel
would probably be a little bit unhappy with me if I didn't fix
that with '
mkswap /dev/md14'. Indeed mkswap reported that it
was replacing an old swap label with a new one.
My understanding is that the same broad approach can be used to shift a software RAID-1 array for a filesystem to smaller partitions as well. For a filesystem that you want to keep intact, you first need to shrink the filesystem safely below the size you'll shrink the RAID array to, then at the end grow the filesystem back up. All things considered I hope that I never have to shrink or reshape the RAID array for a live filesystem this way; there are just too many places where I could blow my foot off.
(Life is easier if the filesystem is expendable and you're going to mkfs a new one on top of it later.)
You might ask why it's worth going through all of this instead of
just making a new software RAID-1 array. That's a good question,
and for me it comes down to how much of a pain it often is to set
up a new array. These days I prefer to change
/etc/fstab and so on as little as possible, which means that I
really want to preserve the name and MD UUID of existing arrays
when feasible instead of starting over from scratch.
This is also where I have an awkward admission: for some reason, I
thought that you couldn't use '
mdadm --detail --scan' on a single
RAID array, to conveniently generate the new line you need for
mdadm.conf when you create a new array. This is wrong; you
definitely can, so you can just do things like '
--scan /dev/mdNN >>/etc/mdadm.conf' to set it up. Of course
you may then have to regenerate your initramfs in order to make
(I hope I never need to do this sort of thing again, but if I do I want to have some notes about it. Sadly someday we may need to use a smaller replacement disk in a software RAID mirror in an emergency situation and I may get to call on this experience.)
Link: Introduction to Certificate Transparency for Server Operators
Alex Gaynor's Introduction to Certificate Transparency for Server Operators (via) is what it says in the title, and taught me some things about certificate transparency in practice. Sadly, one of the things it taught me is that once again Lighttpd seems to be coming up short as far as modern TLS goes. I really should switch over my personal site to using Apache, even if it will kind of be a pain because Fedora fumbled good Apache configuration.
(I also hadn't known about Cert Spotter, which has the advantage that you don't need a Facebook login to use it and thus don't have to helpfully (for Facebook) tie one or more domains to your Facebook login. All you need is an email address and on the modern Internet, you already need a source of individualized disposable ones.)
What I mostly care about for speeding up our Python programs
There are any number of efforts and technologies around these days that try to speed up Python, starting with the obvious PyPy and going on to things like Cython and grumpy. Every so often I think about trying to apply one of them to the Python code I deal with, and after doing this a few times (and even making some old experiments with PyPy) I've come to a view of what's important to me in this area.
What I've come around to caring about most is reducing the overall resource usage of short running programs that mostly use the Python standard library and additional pure-Python modules. By 'resource usage' I mean a combination of both CPU usage and memory usage; in our situation it's not exactly great if I make a program run twice as fast but use four times as much memory. In fact for some programs I probably care more about memory usage than CPU, because in practice our Python-based milter system probably spends most of its time waiting for our commercial anti-spam system to digest the email message and give it a verdict.
(Meanwhile, our attachment logger program is probably very close to being CPU bound. Yes, it has to read things off disk, but in most cases those files have just been written to disk so they're going to be in the OS's disk cache.)
I'm also interested in making DWiki (the code behind Wandering Thoughts) faster, but again I actually want it to be less resource-intensive on the systems it runs on, which includes its memory usage too. And while DWiki can run in a somewhat long-running mode, most of the time it runs as a short-lived CGI that just serves a single request. DWiki's long-running daemon mode also has some features that might make it play badly with PyPy, for example that it's a preforking network server and thus that PyPy is probably going to wind up doing a lot of duplicate JIT translation.
I think that all of this biases me towards up-front approaches like Cython and grumpy over on the fly ones such as PyPy. Up-front translation is probably going to work better for short running programs (partly because I pay the translation overhead only once, and in advance), and the results are at least reasonably testable; I can build a translated version and see in advance whether the result is probably worth it. I think this is a pity because PyPy is likely to be both the easiest to use and the most powerful accelerator, but it's not really aimed at my usage case.
(PyPy's choice here is perfectly sensible; bigger, long-running programs that are actively CPU intensive for significant periods of time are where there's the most payoff for speeding things up.)
PS: With all of this said, if I was serious here I would build the latest version of PyPy by hand and actually test it. My last look and the views I formed back then were enough years ago that I'm sure PyPy has changed significantly since then.
Migrating a separate
/boot filesystem into the root filesystem
In my notes on moving a software RAID-1 root filesystem around, I mentioned that I still had a
/boot partition on my home and office machines and I
wanted to merge it into the root filesystem at some point as I
no longer believe in a separate
/boot under most circumstances. Having now done this both in a test Fedora
25 virtual machine and on my office workstation, it's time to write
up the procedure. For a slight extra complexity, my
root filesystems are both in Linux software RAID-1 arrays.
First, make sure you're actually booting off your root drives, which may require BIOS fiddling. I probably still have to do this at home (on my new SSDs), but it was already done on my office machine a while back. Then my process is:
- Make the directory that will be the new mount point of your old
/bootfilesystem. I use
/etc/fstabto have your separate
/bootfilesystem mounted there.
/bootand remount it on
fstabupdated, this is just '
umount /boot; mount /boot2'.
- copy what is now
/boot. I reflexively do this with
cd /boot && dump -0f - /boot2 | restore -xf -
(We don't need to remake
/boot, because unmounting the filesystem from it leaves behind an empty directory that previously was the mountpoint.)
- You need to know the UUIDs of the root filesystem and your root
software RAID device. You can get these from
/etc/mdadm.conf, or you can use commands such as '
lsblk --fs /dev/mdNN' and '
mdadm -D /dev/mdNN'.
The software RAID UUID will have :'s in it, for example '35d6ec50:bd4d1f53:7401540f:6f971527'. You're going to need a version of the UUID without them, ie just one long string of hex digits.
- Also find the filesystem UUID of what is now
/boot2and the software RAID UUID of its array. One convenient way to do this is to look in your current
/boot/grub2/grub.cfgfor lines of the form:
search --no-floppy --fs-uuid --set=root 91aed8b1-c145-4673-8ece-119e19f7038f
Those are the MD UUID and the filesystem UUID respectively. As you may have guessed, you need a version of the MD UUID without colons, because that's what Grub2 wants.
- Now you need to edit
/boot/grub2/grub.cfgto make two sets of changes. The first one is to change the old
/boot2MD UUID and filesystem UUID to the MD UUID and filesystem UUID of your root filesystem, so that Grub2 is looking in the right filesystem for everything. This should be easy with something like_vim_ search and replace, but remember to make sure your command changes all occurrences on a line, not just the first one (eg, vim's
gmodifier on the
(Note that the MD UUIDs used in
grub.cfgmust be the version without :'s in it. Grub2 probably won't like you too much if you use the other form, although I haven't tested it. Yes, it is a bit annoying that Grub wants a different form of MD UUID than the tools normally produce.)
However, this isn't sufficient by itself, because
grub.cfgcontains filesystem-relative paths for the Linux kernel and its initial ramdisks. In your old
/boot2these were in the root of the filesystem, but now they are
/bootis now a filesystem subdirectory. So you need to edit every set of lines that are like:
linux /vmlinuz-[...] initrd /initramfs-[...]
Change them to add a
/bootat the front, like so:
linux /boot/vmlinuz-[...] initrd /boot/initramfs-[...]
Some versions of
grub.cfgmay use '
linux16' and '
initrd16' as the directives instead.
- Finally, run
grub2-installto reinstall the Grub boot blocks onto your boot drives, eg '
grub2-install /dev/sda'. This updates the embedded Grub so it knows about your new location for
/boot; if you skip this step, your Grub boot blocks will probably quietly keep on using what is now
To test that this is working, reboot your machine and go into the
Grub menu during boot. When you look at the text for a specific
boot entry, you should see that it's using '
linux /boot/...' and
so on; the presence of the
/boot bit is your proof that Grub is
actually reading your updated
/boot/grub2/grub.cfg instead of the
(As a final test you can erase everything in
/boot2 and then fill
it up with data from
/dev/urandom to overwrite all
of the disk blocks. If the system still boots happily, you are
definitely not using anything from the now thoroughly destroyed
/boot2. I feel that going this far is only appropriate in a test
environment; I did it with my Fedora 25 test virtual machine, but
not on my office workstation.)
At some point in the future when you decommission the
filesystem, you will want to edit the kernel command lines in
/boot/grub2/grub.cfg to take out the '
argument that is telling dracut to
try to bring up that software RAID array.
(You might wonder why we didn't have to add or change a
kernel command line argument for the
/boot migration. The answer
is that since
/boot is now on the root filesystem, the kernel's
command line already has a
rd.md.uuid argument to bring it up on
boot. Or at least it should.)
Sidebar: Why I haven't done this to my home machine yet
One version of why is that rebooting my home machine usually feels like more of a pain than rebooting my office machine, because I'm usually doing something with my home machine that I don't want to interrupt. So I drag my heels on anything and everything that can call for a reboot, such as Fedora kernel upgrades.
A more complete version is that not only do I have to reboot my machine, but I should really also open it up to take out my two old 500 GB system HDs, which won't really be in use after this shuffle (on my home machine, I put my small swap partition on my SSDs). And the reason I want to do this is that right now my pair of SSDs aren't actually mounted in drive bays, because I don't have enough. Instead they're just resting loose in a 5.25" drive bay, and the problem with that is the 5.25" drive bays get clearly less air circulation than the 3.5" drive bays.
I should really just do all of this shuffling, but I may have recently mentioned that I'm lazy. I really do want to do it soon, though. Hopefully having written this will motivate me to do it tomorrow.
Planning out a disk shuffle for my office workstation
As I mentioned yesterday, my office workstation lost one of its remaining HDs the other day. This HD is one side of a pair of 1 TB drives that's used for various mirrored partitions, so I haven't lost anything (unless the other side also fails before Monday, so let's hope not), but now I need to replace it with one of the ready spares I have sitting around for exactly this eventuality.
The obvious way to replace the failed disk is to do literally that; put in the new disk, partition it up to basically match the existing setup, and attach appropriate partitions to software RAID devices and my ZFS pool. That would certainly work, but it's not necessarily the right answer because my office workstation's current disk setup has mutated over time and the actual current setup of much of it is not what I would build from scratch.
Specifically, right now I have five drives with the following odd split up:
- two paired SSDs, used for
/and a SSD ZFS pool where my home directory and other important things live. (That my home directory and so on is on the SSDs is one reason I am not too worried about the current disk failure.)
- two paired 1 TB HDs that used to be my only
two drives. Because of that they still have partitions for my old
HD-based root filesystem (in two copies), the
/bootpartition I just recently migrated into the SSD
/, and finally my HD-based ZFS pool, which used to hold everything but now mostly holds less important data. Oh, and it turns out they also have my swap partition.
- One 500 GB HD that I used as overflow for unimportant virtual machines, back in the days when I thought I needed to conserve space on the mirrored 1 TB HDs (in fact it may date from the days when these were actually 750 GB HDs).
My replacements for the 1 TB drives are 1.5 TB, so this already gives me a space expansion, and there's now any number of things on those two 1 TB drives I don't need any more, and also one thing I would like to have. So my current plan is to replace both 1 TB drives (the dead and the still alive one) and set up the space on the new pair of 1.5 TB drives as follows:
- a small 1 GB swap partition, because it still seems like a good idea to give the Linux kernel some swap space to make it happy.
- a 200 GB or so ext4 partition, which will be used for an assortment
of things that aren't important enough to go in the SSD
/but that I don't want to have in ZFS, partly because they're things I may need to get back access to my ZFS pools if package upgrades go wrong.
- a 100 GB backup
/partition. As is my current habit, before major events like Fedora upgrades I'll copy the SSD
/to here so that I can kludge together a reversion to a good environment if something goes badly wrong.
- all the remaining space goes to my HD-based ZFS pool, which should expand the space a decent amount.
(All partitions and ZFS and so on will be mirrored between the two drives.)
Given the likely space expansion in my HD-based ZFS pool, I think I'll also get rid of that overflow 500 GB HD by folding its 120 GB or so of actual space usage into the main HD-based ZFS pool. It was always sort of a hack and a bit awkward (on top of having no redundancy). Plus this will simplify everything, and I can always put the drive (or a bigger replacement drive) back in and redo this if I turn out to actually need a bunch more space for virtual machines.
In a way, I'm grateful that my 1 TB HD finally died and also that it happened under the circumstances it did, where I couldn't immediately rush into replacing it in the most obvious way possible and had forced time to sit back and think about whether the obvious path was the right one. I'm probably going to wind up with a nicer, more sensibly set up system as a result of this disk failure, and I probably never would have done this rearrangement without being pushed.
Sometimes laziness doesn't pay off
My office workstation has been throwing out complaints about some of its disks for some time, which I have been quietly clearing up rather than replace the disks. This isn't because these are generally good disks; in fact they're some Seagate 1TB drives which we haven't had the best of luck with. I was just some combination of too lazy to tear my office machine apart to replace a drive and too parsimonious with hardware to replace a disk drive before it failed.
(Working in a place with essentially no hardware budget is a great way to pick up this reflex of hardware parsimony. Note that I do not necessarily claim that it's a good reflex, and in some ways it's a quite inefficient one.)
Recently things got a bit more extreme, when one disk went from nothing to suddenly reporting 92 new 'Offline uncorrectable sector' errors (along with 'Currently unreadable (pending) sectors', which seems to travel with offline uncorrectable sectors). I looked at this, thought that maybe this was a sign that I should replace the disk, but then decided to be lazy; rather than go through the hassle of a disk replacement, I cleared all the errors in the usual way. Sure, the disk was probably going to fail, but it's in a mirror and when it actually did fail I could swap it out right away.
(I actually have a pair of disks sitting on my desk just waiting to be swapped in in place of the current pair. I think I've had them set aside for this for about a year.)
Well, talking about that idea, let's go to Twitter:
@thatcks: I guess I really should have just replaced that drive in my office workstation when it reported 92 Offline uncorrectable sectors.
@thatcks: At 5:15pm, I'm just going to hope that the other side of the mirrored pair survives until Monday. (And insure I have pretty full backups.)
Yeah, I had kind of been assuming that the disk would fail at some convenient time, like during a workday when I wasn't doing anything important. There are probably worse times for my drive to fail than right in front of me at 5:15 pm immediately before a long weekend, especially when I have a bike ride that evening that I want to go to, but I can't think of many that are more annoying.
(The annoyance is in knowing that I could swap the drive on the spot, if I was willing to miss the bike ride. I picked the bike ride, and a long weekend is just short enough that I'm not going to come in in the middle of it to swap the drive.)
I have somewhat of a habit of being lazy about this sort of thing. Usually I get away with it, which of course only encourages me to keep on being lazy and do it more. Then some day things blow up in my face, because laziness doesn't always pay off. I need to be better about getting myself to do those annoying tasks sooner or later rather than putting them off until I have no choice about it.
(At the same time strategic laziness is important, so important that it can be called 'prioritization'. You usually can't possibly give everything complete attention, time, and resources, so you need to know when to cut some nominal corners. This usually shows up in security, because there are usually an infinite number of things that you could be doing to make your environment just a bit more secure. You have to stop somewhere.)
On today's web, a local Certificate Authority is fairly dangerous
In a comment on my entry on generating self-signed TLS certificates today, James suggested:
My go-to tool is OpenVPN's EasyRSA. Admittedly that creates a CA which you can then sign certificates with, but for your internal hosts it would mean you could install said CA into your browser and then trust them all.
Superficially, this is certainly an appealing idea. If you have a fleet of IPMIs or other internal websites that need TLS certificates and that have names where you can't get public certificates, you can avoid everyone having to trust them one by one. Just set up a local CA, sign all the internal website certificates with them, add the local CA certificate to your browser, and you're done.
Unfortunately if you do this, you have just loaded a fairly large security-defeating gun and pointed it straight at your face. It's not just that your local CA can be attacked to sign certificate for any host, not just your internal ones; more importantly, certificates signed by a manually added CA specifically bypass all of the modern TLS protections built into browsers. This isn't just things like HTTP Public Key Pinning headers that your browser may have memorized, it's also even critically important pinned keys hard-coded into browsers themselves. A certificate signed by a manually added CA bypasses all of those checks.
(For all of this we may blame HTTPS interception middleware. Browser vendors have extremely reluctantly bowed to the demands of businesses that want to deploy them and have them intercept absolutely everything, partly because businesses basically hold the cards here if they're willing to go far enough.)
As far as I know there's no way in either Firefox or Chrome to constrain a manually added CA to only have its certificates accepted for certain (sub)domains. This means that no matter what you want, your local CA intended for intranet websites has just as much TLS interception ability as the TLS CA for a mandatory HTTPS middleware box. If an attacker can compromise it, they gain complete HTTPS interception capabilities for web browsing, both internal and external. None of the usual precautions and warnings will protect you in the least.
This means that a local CA that you have people's browsers trust is a very big deal, even (or especially) if only the sysadmins are trusting it. If you're going to have one at all, I think that it should involve some sort of hardware security module, even a simple and cheap one. If you are not willing to strongly protect a local CA, at least to the level of buying basic HSM hardware for it, then you should not even think of having one; it's simply far too dangerous in the event of a serious attacker. Even if you buy HSM hardware for it, I think that the balance of risks versus gains are often not going to be in favour of a local CA.
(To be clear, all of this is specific to local CAs that you will have your browsers trust. There are perfectly sensible and not particularly dangerous uses for a local CA outside of this. The general way to know if you're safe is that every operation that is supposed to use the local CA should have to explicitly trust the local CA's root certificate, whether that's through a command-line option or a specific configuration file setting. You should never add a local CA to your general trust roots, whether those are the browser trust roots or the system's generic trust roots.)
(Years ago I sort of wrote about this here, but I didn't take it anywhere near far enough and in particular I didn't think of what an attacker could do with access to your local or organizational CA. Not that overzealous security people aren't a serious risk in and of themselves, and it's not as if middleware HTTPS interception has a good reputation. Rather the contrary.)
Generating good modern self-signed TLS certificates in today's world
Once upon a time, generating decently good self-signed certificates
for a host with OpenSSL was reasonably straightforward, especially if you didn't know about
some relevant nominal standards. The certificate's Subject name
field is a standard field with standard components, so OpenSSL would
prompt you for all of them, including the Common Name (CN) that
you'd put the hostname in. Then things changed and in modern
TLS, you really want to put the hostname in the Subject Alternative
Name field. SubjectAltName is
an extension, and because it's an extension '
openssl req' will not
prompt you to fill it in.
(The other thing is that you need to remember to specify
as one of the arguments; otherwise '
openssl req' will use SHA1
and various things will be unhappy with your certificate. Not all
examples you can find on the Internet use '
-sha256', so watch
You can get '
openssl req' to create a self-signed certificate
with a SAN, but since OpenSSL won't prompt for this you must use
an OpenSSL configuration file to specify everything about the
certificate, including the hostname(s). This is somewhat intricate, even if it turns out to be
possibly to do this more or less through the command line with
suitably complicated incantations.
I particularly admire the use of the shell's usually obscure
Given how painful this is, what we really need is a better tool to create self-signed certificates and fortunately for me, it turns out that there is just what I need sitting around in the Go source code as generate_cert.go. Grab this file, copy it to a directory, then:
$ go build generate_cert.go $ ./generate_cert --host www.example.com --duration 17520h 2017/04/11 23:51:21 written cert.pem 2017/04/11 23:51:21 written key.pem
This generates exactly the sort of modern self-signed certificate that I want; it uses SHA256, it has a 2048-bit RSA key (by default), and it's got SubjectAltName(s). You can use it to generate ECDSA based certificates if you're feeling bold.
Note that this generates a certificate without a CN. Since there are real CN-less certificates out there in the wild issued by real Certificate Authorities (including the one for this site), not having a CN should work fine with web browsers and most software, but you may run into some software that is unhappy with this. If so, it's only a small modification to add a CN value.
(You could make a rather more elaborate version of generate_cert.go with various additional useful options, and perhaps someone has already done so. I have so far resisted the temptation to start changing it myself.)
A rather more elaborate but more complete looking alternative is Cloudflare's CFSSL toolkit. CFSSL can generate self-signed certificates, good modern CSRs, and sign certificates with your own private CA certificate, which covers everything I can think of. But it has the drawback that you need to feed it JSON (even if you generate the JSON on the fly) and then turn its JSON output into regular .pem files with one of its included programs.
For basic, more or less junk self-signed certificates, generate_cert is the simple way to go. For instance my sinkhole SMTP server now uses one of these certs; SMTP senders don't care about details like good O values in your SMTP certificates, and even if they did in general spammers probably don't. If I was generating more proper self-signed certificates, one where people might see them in a browser or something, I would probably use CFSSL.
(Although if I only needed certificates with a constant Subject name, the lazy way to go would be to hardcode everything in a version of generate_cert and then just crank out a mass of self-signed certificates without having to deal with JSON.)
PS: We might someday want self-signed certificates with relatively proper O values and so on, for purely internal hosts that live in our own internal DNS zones. Updated TLS certificates for IPMI web interfaces are one potential case that comes to mind.
PPS: It's entirely possible that there's a better command line tool for this out there that I haven't stumbled over yet. Certainly this feels like a wheel that people must have reinvented several times; I almost started writing something myself before finding generate_cert.
How TLS certificates specify the hosts they're for
Any TLS-like system of public key cryptography plus Certificate Authorities that vouch for your identity needs a format for certificates. When Netscape came up with the very first version of SSL, they opted not to invent a certificate format to go with their new encryption protocol; instead they reused the certificate format that had already been proposed for email in 1993 in RFC 1422 (as part of PEM). This foundational work didn't define its own certificate format either; instead it opted to politely reuse an existing specification, X.509, which associated with the X.500 directory specification, which was as a broad whole intended to be part of the OSI network stack before OSI as a whole was completely run over by the Internet TCP/IP juggernaut. This reuse in PEM was apparently intended in part to help interoperability with X.500/X.509 based email systems that more or less never materialized.
(The Internet used to worry a great deal about talking to foreign email systems and how to map between SMTP email concepts and those systems.)
A standard part of X.509 certificates is their Subject name, ie what the certificate is about. X.509 subject names are not freeform text; instead they are a set of attributes making up an X.500 Distinguished Name. Since these attributes come from an OSI directory specification, they're focused on identifying people in some role, as you can see from the list of standard DN keys in RFC 1779. This presented Netscape with a little problem, namely how to use a certificate to identify an Internet host instead of a person. So back in 1995 and 1996 when SSL v2 and v3 were specified, people took the obvious way out. X.500 DNs have a field called the CN, or Common Name, which for people is your name. For host certificates, the CN field was reused as the hostname (with the other DN fields being used to identify the organization officially associated with the certificate and so on).
In the beginning this was fine. However, as SSL went on people started wanting to have more than one hostname associated with a single SSL certificate; if nothing else, people liked having both www.<domain> and plain <domain>. This is a problem because the CN only contained a single hostname. Fortunately X.509 certificates can contain extensions, and so by no later than RFC 2459 (which went with TLS 1.0) there was the Subject Alternative Name extension. A SubjectAltName was explicitly a sequence of things (including DNS hostnames), so you could put multiple hostnames in it.
Then things get complicated. As of RFC 3280 in 2002, including SANs in TLS certificates was theoretically mandatory (via), and in fact using a CN was deprecated in RFC 2818 in 2000. How SANs and the CN interacts depends on the software involved; per this Stackoverflow answer, RFC 5280 in 2008 said to look at both, while RFC 6125 in 2011 theoretically mandated checking SANs only if they were present (and they should always be present). See also this SO answer for more information about the CA/Browser Forum Baseline Requirements view on this, and there's also the discussion in this Let's Encrypt issue.
Given that there is plenty of pre-2011 software out there on the Internet, you can in practice find TLS certificates with all variants of this; with a CN only, with SANs only, with SANs and a CN that contains one of the SAN's names, and with SANs that do not contain the certificate's CN. With the widespread prevalence of multiple hostnames in SANs in certificates (sometimes a very long list of them, eg on Cloudflare's web servers), all even vaguely modern TLS software can validate certificates using only SAN names. I don't know if there's any browser or TLS software yet that ignores the CN or actively requires certificates to have a SAN, but it's probably not too far off; at this point, it's likely to be at least a little bit unsafe to use TLS certificates without a SAN.
(You probably can't get CA signed certificates without a SubjectAltName, because the CA will add them for you whether or not you like it. Self-signed certificates are a different matter entirely, but that's another entry.)