mBank is a compact Go reference project that demonstrates gRPC over mutual TLS (mTLS) with an ECC-only certificate workflow.
It combines a gRPC banking service, a demo client, local certificate generation, Docker support, health checks, graceful shutdown, and observability interceptors into one small codebase that is easy to run and inspect.
mBank is a demo and learning project. It is designed to showcase transport security, service composition, and developer workflow patterns rather than real-world banking rules.
- About
- Highlights
- Quick Start
- Docker Compose
- Project Layout
- Configuration
- Development
- Troubleshooting
- Contributing
- License
mBank shows how to build and test a secure gRPC service in Go with a clean internal structure.
The project demonstrates how to:
- authenticate both the client and server with mTLS
- generate local ECC certificates with OpenSSL
- expose the standard gRPC health service
- shut the server down gracefully on
SIGINTandSIGTERM - collect structured request logs and in-memory RPC metrics
- validate transport behavior with end-to-end integration tests
The demo client connects to the server, performs a health check, creates an account, deposits funds, reads the balance, and withdraws funds.
- Go 1.24 project with protobuf and gRPC contracts
- ECC-only local certificate workflow via
./generate-certs.sh - Enforced client certificate verification on the gRPC server
- Integer-cent money representation on the wire and in service logic
- Structured
slogrequest logging and interceptor-based metrics - Graceful shutdown and built-in gRPC health checks
- Local binaries, container images, and Docker Compose support
- Unit tests and integration tests for secure and insecure transport paths
- Go 1.24+
- OpenSSL
- Docker and Docker Compose (optional)
git clone https://github.com/liambeeton/go-grpc-over-mtls.git
cd go-grpc-over-mtls./generate-certs.shThis creates a local certs/ directory containing:
- a development CA certificate and private key
- a server certificate and private key
- a client certificate and private key
make buildmake runIn another terminal:
make run-clientThe client will:
- connect to the server over mTLS
- confirm the gRPC health endpoint is
SERVING - create a fresh demo account
- deposit
10000cents - fetch the current balance
- withdraw
5000cents
Generate certificates first, then run the stack with Docker Compose:
export PORT=8443
docker compose up --buildThe compose setup mounts ./certs into both containers and defaults to the certificate paths under
/usr/bin/certs.
If you already export certificate-related environment variables in your shell, Docker Compose will
use those values instead of the file defaults in docker-compose.yml.
cmd/client Demo client entrypoint
cmd/server Server entrypoint
internal/bank Banking service implementation
internal/config Shared CLI and environment configuration
internal/money Money helpers based on integer cents
internal/observability gRPC logging and metrics interceptors
internal/serverapp Server bootstrap, health, and shutdown behavior
internal/transport TLS and insecure transport wiring
pb/ Protobuf contracts and generated bindings
Both the client and server read configuration from flags or environment variables.
HOST: client target host or server bind addressPORT: TCP port for the gRPC connectionTLS_ENABLED: enables or disables TLS, defaults totrueCA_FILE: path to the PEM-encoded CA certificateCERT_FILE: path to the PEM-encoded certificate for the running processKEY_FILE: path to the PEM-encoded private key for the running process
export HOST="localhost"
export PORT="8443"
export TLS_ENABLED="true"
export CA_FILE="$PWD/certs/ca.crt"
export CERT_FILE="$PWD/certs/client.crt"
export KEY_FILE="$PWD/certs/client.key"For the server, point CERT_FILE and KEY_FILE at certs/server.crt and certs/server.key.
go test ./...
go test -race ./...
make lint
make prettier-check
make markdownlint
make proto-compile- The client performs a health check before issuing banking RPCs.
- The server exposes the standard gRPC health service and shuts down gracefully.
- Integration tests generate ephemeral ECDSA certificates in Go instead of relying on files in
certs/. make runandmake run-clientuse the localcerts/directory by default.
- If
docker composeresolves an unexpected certificate path, unsetCA_FILE,CERT_FILE, andKEY_FILEin your shell and retry. - If
make run-clienttimes out, confirm the server is running and listening on the configured host and port. - If port
8443is already in use, stop the conflicting process or overridePORT. - If you regenerate certificates, restart both the server and client so they reload the new files.
Issues, improvements, and refactors are welcome. If you plan to make a larger change, it helps to open an issue or discussion first so the direction is clear before implementation starts.
This project is licensed under the MIT License.