Day 122 — TLS & Secure Transport¶
Month 5 · Week 2 · ⬅ Day 121 · Day 123 ➡ · Journal index
🎯 Learning Objective¶
Secure a gRPC channel with TLS: present a server certificate, have the client verify it against a CA, and understand mutual TLS (mTLS).
📚 Topics¶
crypto/tls,crypto/x509,*x509.CertPoolcredentials.NewTLS/credentials.NewClientTLSFromFile- Server-auth TLS vs mutual TLS (
tls.RequireAndVerifyClientCert)
📖 Reading / Sources¶
-
crypto/tls·crypto/x509 - gRPC-Go —
credentialspackage - gRPC Authentication — TLS/mTLS
- Go blog — generating certs (
generate_cert.go)
📝 Notes¶
- TLS gives encryption + server authentication: the client proves it's really talking to the server it intended (no man-in-the-middle), and the bytes are encrypted. gRPC layers HTTP/2 on top → [[tls]] [[transport-security]].
- The trust model: the server holds a cert + private key; the client holds a CA pool (
*x509.CertPool). Verification checks the cert's signature chains to a trusted CA and that the requested ServerName matches the cert's SAN (DNSNames/IPAddresses). A name mismatch fails even with a valid chain → [[x509]] [[cert-verification]]. - On the server:
grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsCfg)))wheretlsCfg.Certificates = []tls.Certificate{cert}. On the client:grpc.NewClient(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{RootCAs: pool})))→ [[grpc-credentials]]. - Never use
insecure.NewCredentials()in production — that's plaintext. AndInsecureSkipVerify: truedisables verification, defeating the point; only acceptable in throwaway tests → [[insecure-trap]]. - Mutual TLS (mTLS): the server also verifies the client's cert. Set
tlsCfg.ClientAuth = tls.RequireAndVerifyClientCertandtlsCfg.ClientCAs = pool. Now identity can come from the client cert's subject — common in service meshes → [[mtls]]. - Prefer TLS 1.3 (
MinVersion: tls.VersionTLS13) to forbid downgrade attacks. Certs expire — automate rotation; gRPC can reload via aGetCertificatecallback intls.Config. - A self-signed cert (its own issuer) is fine for local dev if the client trusts that exact cert; in prod the chain ends at a real CA.
💻 Code Examples¶
// Server credentials from a cert/key pair on disk.
cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS13,
})
srv := grpc.NewServer(grpc.Creds(creds))
// Client: trust the CA that signed the server cert and verify the name.
pool, _ := x509.SystemCertPool()
pem, _ := os.ReadFile("ca.crt")
pool.AppendCertsFromPEM(pem)
conn, _ := grpc.NewClient("api.example.com:443",
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{RootCAs: pool})))
Stdlib rebuild (generate a self-signed cert, do a real TLS handshake, watch verification fail without trust):
examples/month-05/tls/main.go· Run:go run ./examples/month-05/tls
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
| Read & run the TLS example; flip RootCAs to see the verify error | ✅ | examples/month-05/tls |
(concept) Add ClientAuth: RequireAndVerifyClientCert for mTLS |
⬜ | day note snippet |
🐛 Mistakes Made¶
- Generated a cert with CN=
localhostbut no SAN, then dialed127.0.0.1→ verification failed. Modern Go ignores CN; the name must be inDNSNames/IPAddresses. Added both. - Reached for
InsecureSkipVerify: trueto make the error go away — that's disabling security, not configuring it. Added the cert to the client'sRootCAsinstead.
❓ Open Questions¶
- How does gRPC pick up a rotated cert without a restart? (The
GetCertificate/GetConfigForClientcallbacks — need to try one.)
🧠 Active Recall (answer without looking)¶
- Q: A client gets
x509: certificate signed by unknown authority. What's wrong and how do you fix it properly?
A
The server's cert doesn't chain to any CA in the client's pool. Fix by adding the signing CA (or, for self-signed, the cert itself) to the client's `RootCAs` — NOT by setting `InsecureSkipVerify: true`.- Q: What does mTLS add over ordinary server-auth TLS?
A
The server also authenticates the client: set `ClientAuth: RequireAndVerifyClientCert` and `ClientCAs`. Both sides present certs, so identity can be derived from the client cert — useful in service meshes.🪶 Feynman Reflection¶
TLS is a sealed envelope with a verified return address. The server slides its certificate under the door; the client checks the certificate was signed by an authority it trusts AND that the name on it matches who it dialed. Only then does it send anything — and everything after is encrypted. mTLS just makes both parties show ID instead of one.
🕳️ Knowledge Gaps¶
- Cert rotation/reloading without downtime, and how SPIFFE/SVID identities fit a mesh.
✅ Summary¶
I can configure server-auth TLS for gRPC, explain the cert/CA/SAN trust model, and describe how mTLS extends it. I understand why InsecureSkipVerify is a trap.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 123: grpc-gateway — exposing the same service as REST/JSON.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦🟦⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: feat(examples): TLS transport — self-signed cert & client verification (day 122)