gRPC Mutual TLS

Standard TLS proves the identity of the server. The client checks a certificate and knows it talks to the real server. Mutual TLS, often written mTLS, adds the reverse check. The server also demands a certificate from the client. Both sides prove who they are before any message moves.

The Office Badge Analogy

A visitor shows a badge at the front desk of a secure office. The visitor also expects the receptionist to wear a company badge. Both people verify each other. Regular TLS checks only the receptionist's badge. Mutual TLS checks both.

Regular TLS                      Mutual TLS
Client ──"Show your cert"──▶ Server    Client ◀──"Show your cert"──▶ Server
Client ◀──server cert─────── Server    Client ◀──server cert──────── Server
                                       Client ───client cert───────▶ Server
Only server verified                   Both sides verified

When to Use mTLS

  • Service to service calls inside a private network
  • Banking, healthcare, and other regulated systems
  • Zero trust designs, where no network location earns automatic trust
  • Calls from known partner systems

Certificate Pieces

FileRole
ca.crtCertificate authority that both sides trust
server.crt and server.keyServer identity
client.crt and client.keyClient identity

Create Test Certificates

These commands build a small certificate authority for local learning. Production systems use a managed certificate authority.

openssl genrsa -out ca.key 4096
openssl req -x509 -new -key ca.key -sha256 -days 365 -subj "/CN=Demo CA" -out ca.crt

printf "subjectAltName=DNS:localhost" > san.ext

openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=localhost" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 90 -sha256 -extfile san.ext -out server.crt

openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=orders-service" -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 90 -sha256 -out client.crt

Go requires a subject alternative name on server certificates. The san.ext file supplies it.

Server Setup in Go

cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
caPEM, _ := os.ReadFile("ca.crt")
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(caPEM)

creds := credentials.NewTLS(&tls.Config{
  Certificates: []tls.Certificate{cert},
  ClientAuth:   tls.RequireAndVerifyClientCert,
  ClientCAs:    pool,
  MinVersion:   tls.VersionTLS13,
})

server := grpc.NewServer(grpc.Creds(creds))

The setting RequireAndVerifyClientCert rejects every client without a valid certificate signed by the trusted authority.

Client Setup in Go

cert, _ := tls.LoadX509KeyPair("client.crt", "client.key")
caPEM, _ := os.ReadFile("ca.crt")
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(caPEM)

creds := credentials.NewTLS(&tls.Config{
  Certificates: []tls.Certificate{cert},
  RootCAs:      pool,
  ServerName:   "localhost",
})

conn, err := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(creds))

Read the Caller Identity

The verified certificate tells the server exactly who is calling. Handlers read the name and make decisions.

p, ok := peer.FromContext(ctx)
if ok {
  tlsInfo := p.AuthInfo.(credentials.TLSInfo)
  caller := tlsInfo.State.PeerCertificates[0].Subject.CommonName
  log.Printf("call from %s", caller)
}

Certificate Rotation

Certificates expire. Short lifetimes, measured in days or hours, limit damage from a leaked key. Automate renewal with tools such as cert-manager or a service mesh. Reload certificates without a restart so that rotation causes no downtime.

Troubleshooting

SymptomLikely Cause
bad certificate errorClient certificate signed by an untrusted authority
certificate has expiredExpired file on either side
x509 SAN errorServer name missing from the certificate
Connection resets at handshakeClient sent no certificate

Summary

Mutual TLS verifies both sides of a connection. Create a certificate authority, issue certificates to servers and clients, require client verification on the server, and rotate certificates often. Strong identity at the connection level supports every higher security layer.