Step 1 of 5 · Reading · ~4 min
Read
Production Concerns
TLS Termination
HTTPS has to be decrypted somewhere, and the load balancer is the usual place. Terminating TLS at the balancer centralises the hard parts: one place to install and renew certificates, one place to set the allowed TLS versions, and, critically for this course, a plaintext request the balancer can read, which is what makes path routing, header rules and sticky cookies possible at all. The costs are cleartext traffic to the backends (keep it private, or re-encrypt) and a private key that becomes your crown jewel. The alternatives are passthrough (an L4 balancer forwards encrypted bytes untouched, so no L7 features) and mutual TLS to the backends, as service meshes do.
Here you build its first decision: which certificate do I present?
One address, many certificates
A server must show its certificate before any HTTP flows, so how can one IP serve shop.example.com and api.other.io? The client says which name it wants in the very first handshake message, in the clear. That is SNI, Server Name Indication, an extension inside the ClientHello.
Reading a ClientHello
The balancer sees the raw bytes of the first TLS record. Here is a complete, tiny ClientHello for the name a.io, one field per line, with every length counted in bytes:
16 03 01 00 3c record: type 0x16 handshake, version, length 60
01 00 00 38 handshake: type 0x01 ClientHello, length 56
03 03 client_version
00 x32 random (32 bytes)
00 session_id length 0
00 02 13 01 cipher_suites: length 2, one suite
01 00 compression_methods: length 1, null
00 0d extensions: length 13
00 00 00 09 extension type 0 (server_name), length 9
00 07 server_name_list length 7
00 00 04 61 2e 69 6f name_type 0 (host_name), length 4, "a.io"
A real browser's hello (about 500 bytes) has the same skeleton with a 32-byte session id, dozens of cipher suites and a dozen extensions, and server_name can sit anywhere among them, so walk the extensions by their lengths, never by position. The steps:
- The record must be type
0x16and the handshake message type0x01. Otherwise the peer is not speaking TLS the way a client should: answerALERT unexpected_message. - Skip the version, random, session id, cipher suites and compression methods, each by its length prefix (1, 2 and 1 bytes wide).
- If the message ends there, the client sent no extensions and so no SNI.
- Otherwise walk the extensions: 2-byte type, 2-byte length, data. Type 0 is
server_name. Inside it, a 2-byte list length, then entries of name type (0 = host name), a 2-byte length and the bytes. Take the first host name. - Any length that points past the data you actually have, or a list that does not add up, means the message is malformed:
ALERT decode_error.
Host names are case-insensitive, so lower-case the ASCII letters. Bytes after the record are ignored.
Matching a certificate
Certificates may name *.example.com, but a wildcard covers exactly one label, only the left-most:
*.example.commatchesapi.example.com- it does not match
a.b.example.com(two labels) - it does not match the bare apex
example.com, nor an empty label
The priority is exact name, then wildcard, then the default certificate (nginx's default_server). That is why deployments hold both an apex certificate and a wildcard.
Common mistakes
- Reading by fixed offsets. Extensions and the session id vary in size; only the length prefixes tell you where things are.
- Mixing up the 1-, 2- and 3-byte length widths, or counting fields instead of bytes.
- Matching any suffix for a wildcard, or letting it cover the apex.
- Forgetting the case-fold:
WWW.Example.COMandwww.example.comare one name. - Treating "no SNI" as an error. A client connecting by IP address sends none; it gets the default certificate.
Your exercise
The starter decodes the hex and handles CERT, LIST and the command loop. You write parse_sni and the certificate lookup. CERT <pattern> <name> registers or replaces a pattern (prints OK). LOOKUP <host> prints the certificate name. LIST prints <pattern> -> <name> sorted by pattern. HELLO <hex> takes the first TLS record as hex and prints <host> -> <certificate>, or - -> default when there is no SNI, or an ALERT line.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…