Skip to content

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.CertPool
  • credentials.NewTLS / credentials.NewClientTLSFromFile
  • Server-auth TLS vs mutual TLS (tls.RequireAndVerifyClientCert)

📖 Reading / Sources

📝 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))) where tlsCfg.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. And InsecureSkipVerify: true disables 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.RequireAndVerifyClientCert and tlsCfg.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 a GetCertificate callback in tls.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=localhost but no SAN, then dialed 127.0.0.1 → verification failed. Modern Go ignores CN; the name must be in DNSNames/IPAddresses. Added both.
  • Reached for InsecureSkipVerify: true to make the error go away — that's disabling security, not configuring it. Added the cert to the client's RootCAs instead.

❓ Open Questions

  • How does gRPC pick up a rotated cert without a restart? (The GetCertificate/GetConfigForClient callbacks — need to try one.)

🧠 Active Recall (answer without looking)

  1. Q: A client gets x509: certificate signed by unknown authority. What's wrong and how do you fix it properly?
AThe 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`.
  1. Q: What does mTLS add over ordinary server-auth TLS?
AThe 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)