Skip to content

Latest commit

 

History

History

README.md

mTLS Examples

This directory contains practical examples of using mTLS certificates generated by the mtls tool.

Overview

Available Examples

1. Go

2. Node.js

3. Python

4. PHP

  • php-server - PHP server with mTLS (Apache/Nginx recommended for production)
  • php-client - PHP client using stream contexts

5. Rust

  • rust-server - High-performance Rust server with Axum and Rustls
  • rust-client - Rust client with Reqwest and Rustls

6. Caddy

  • caddy - Caddy web server with mTLS configuration

Quick Start

1. Generate Certificates

From the project root:

# Create a Root CA
./mtls ca create --batch \
  --cn "Example CA" \
  --org "Example Organization" \
  --country "KR" \
  --key-type rsa4096 \
  --years 10

# Create a server certificate
./mtls cert create --batch \
  --ca ./certs/ca \
  --cn "localhost" \
  --org "Example Server" \
  --dns "localhost,127.0.0.1" \
  --ip "127.0.0.1" \
  --key-type rsa2048 \
  --years 5

2. Choose Your Server

Option A: Go Server (Recommended for Learning)

cd examples/go-server
go run main.go

Option B: Node.js Server

cd examples/node-server
npm install  # First time only
npm start

Option C: Python Server

cd examples/python-server
python3 server.py

Option D: PHP Server

cd examples/php-server
./run.sh

Option E: Rust Server

cd examples/rust-server
cargo run --release

Option F: Caddy Server (Production-Ready)

cd examples/caddy
./run.sh

3. Test with Clients

Choose a client in any language:

# Go client
cd examples/go-client
go run main.go

# Node.js client
cd examples/node-client
npm install  # First time only
npm start

# Python client
cd examples/python-client
python3 client.py

# PHP client
cd examples/php-client
./run.sh

# Rust client
cd examples/rust-client
cargo run --release

Language Comparison

Language Server Framework TLS Library Async Dependencies Performance Best For
Go net/http crypto/tls ✅ Goroutines Zero (stdlib) ⭐⭐⭐⭐⭐ Microservices, CLI tools
Node.js https OpenSSL ✅ Event loop Zero (built-in) ⭐⭐⭐⭐ Web APIs, real-time
Python http.server ssl ❌ Sync Zero (stdlib) ⭐⭐⭐ Scripts, prototyping
PHP Built-in/Apache OpenSSL ❌ Sync Zero (ext-openssl) ⭐⭐ Legacy systems
Rust Axum Rustls ✅ Tokio 9 crates ⭐⭐⭐⭐⭐ High-performance, safety
Caddy Caddy Go crypto/tls ✅ Built-in Config only ⭐⭐⭐⭐⭐ Reverse proxy, edge

Key Considerations

Choose Go if you need microservices with excellent concurrency and zero dependencies.

Choose Node.js if you're building web APIs and your team knows JavaScript/TypeScript.

Choose Python if you're prototyping, writing scripts, or need data science integration.

Choose PHP if you have legacy PHP applications or use Apache/Nginx with PHP-FPM.

Choose Rust if you need maximum performance, memory safety, and systems-level programming.

Choose Caddy if you need a reverse proxy, automatic HTTPS, or configuration simplicity

Architecture

┌─────────────┐                    ┌─────────────┐
│             │  mTLS Connection   │             │
│  Go Client  │◄──────────────────►│   Server    │
│             │  (Mutual Auth)     │  (Go/Caddy) │
└─────────────┘                    └─────────────┘
      │                                    │
      │ ┌────────────────┐                │
      └─┤  Client Cert   │                │
        │  + Private Key │                │
        └────────────────┘                │
                                          │
                                ┌─────────▼──────┐
                                │  Server Cert   │
                                │  + Private Key │
                                └────────────────┘
                                          │
                        ┌─────────────────▼─────────────┐
                        │        Trusted CA Cert        │
                        │  (for verifying both sides)   │
                        └───────────────────────────────┘

Testing Scenarios

Scenario 1: Successful mTLS Connection

Both client and server have valid certificates signed by the same CA.

# Terminal 1
cd examples/go-server && go run main.go

# Terminal 2
cd examples/go-client && go run main.go

✅ Expected: All tests pass

Scenario 2: Missing Client Certificate

Modify the client to not send a certificate.

curl https://localhost:8443

❌ Expected: Connection refused (no client certificate)

Scenario 3: Invalid Certificate

Try using a certificate from a different CA.

❌ Expected: TLS handshake fails (certificate verification error)

Scenario 4: Direct cURL Test

curl --cert ../../certs/servers/localhost/server-cert.pem \
     --key ../../certs/servers/localhost/server-key.pem \
     --cacert ../../certs/ca/ca-cert.pem \
     https://localhost:8443

✅ Expected: Success response from server

Use Cases

Development & Testing

  • Local development with mTLS
  • Integration testing
  • API testing with mutual authentication

Microservices

  • Service-to-service authentication
  • Zero-trust networking
  • Internal API security

IoT & Edge Computing

  • Device authentication
  • Secure communication with edge devices
  • Certificate-based device identity

Enterprise Applications

  • Internal tools requiring strong authentication
  • Admin panels with certificate-based access
  • Secure API gateways

Security Notes

Best Practices

  1. Certificate Rotation: Rotate server certificates regularly (1-2 years)
  2. CA Security: Keep the CA private key extremely secure
  3. Certificate Revocation: Implement CRL or OCSP for production
  4. Monitoring: Log certificate expiration dates
  5. Access Control: Limit who can generate certificates

What mTLS Provides

  • Strong Authentication: Both client and server verify each other
  • Encrypted Communication: All traffic is TLS-encrypted
  • Non-repudiation: Certificate-based identity
  • Zero-Trust: No implicit trust, always verify

What mTLS Does NOT Provide

  • Authorization: You still need to implement access control
  • Rate Limiting: Implement separately if needed
  • DDoS Protection: Additional measures required
  • Application Security: Secure coding practices still necessary

Troubleshooting

Port Already in Use

# Find and kill the process using port 8443
lsof -ti:8443 | xargs kill -9

Certificate Expired

# Check certificate validity
openssl x509 -in certs/servers/localhost/server-cert.pem -noout -dates

Certificate Path Issues

Make sure you're running commands from the correct directory:

# For go-server and go-client, run from their respective directories
cd examples/go-server  # or go-client
go run main.go

# Certificate paths are relative: ../../certs/...

TLS Handshake Errors

Common causes:

  1. Certificate and CA mismatch
  2. Expired certificates
  3. Incorrect certificate paths
  4. TLS version mismatch

Enable debug logging:

# For Go programs
GODEBUG=x509roots=1 go run main.go

Advanced Examples

Custom Certificate Validation

See go-client/main.go for examples of:

  • Loading certificates
  • Configuring TLS
  • Custom verification logic

Multiple Certificates

Generate certificates for different purposes:

# Admin certificate
./mtls cert create --batch --ca ./certs/ca --cn "admin.local"

# Service certificate
./mtls cert create --batch --ca ./certs/ca --cn "service.local"

# IoT device certificate
./mtls cert create --batch --ca ./certs/ca --cn "device-001"

Certificate-Based Authorization

In your server code:

func authorizeAdmin(r *http.Request) bool {
    if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
        return false
    }
    
    cert := r.TLS.PeerCertificates[0]
    
    // Check if certificate has admin role
    for _, org := range cert.Subject.Organization {
        if org == "Admin" {
            return true
        }
    }
    
    return false
}

Resources

Contributing

Feel free to add more examples! Some ideas:

  • gRPC with mTLS
  • WebSocket with mTLS
  • Docker container with mTLS
  • Kubernetes ingress with mTLS
  • nginx configuration
  • Python client example