mTLS Security Configuration¶
This guide covers how to configure mutual TLS (mTLS) authentication between VirtRigaud managers and providers.
Overview¶
mTLS provides strong authentication and encryption for gRPC communication between the VirtRigaud manager and provider services. It ensures:
- Authentication: Both client and server verify each other's certificates
- Encryption: All traffic is encrypted in transit
- Certificate Pinning: Specific certificate authorities are trusted
- Certificate Rotation: Automated certificate renewal
Certificate Management¶
1. Generate CA Certificate¶
# Create CA private key
openssl genrsa -out ca-key.pem 4096
# Create CA certificate
openssl req -new -x509 -key ca-key.pem -out ca-cert.pem -days 365 \
-subj "/C=US/ST=CA/L=San Francisco/O=VirtRigaud/CN=VirtRigaud CA"
2. Generate Server Certificate (Provider)¶
# Create server private key
openssl genrsa -out server-key.pem 4096
# Create server certificate signing request
openssl req -new -key server-key.pem -out server-csr.pem \
-subj "/C=US/ST=CA/L=San Francisco/O=VirtRigaud/CN=provider-service"
# Sign server certificate
openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -days 365 \
-extensions v3_req -extfile <(cat <<EOF
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = provider-service
DNS.2 = provider-service.default.svc.cluster.local
DNS.3 = localhost
IP.1 = 127.0.0.1
EOF
)
3. Generate Client Certificate (Manager)¶
# Create client private key
openssl genrsa -out client-key.pem 4096
# Create client certificate signing request
openssl req -new -key client-key.pem -out client-csr.pem \
-subj "/C=US/ST=CA/L=San Francisco/O=VirtRigaud/CN=manager-client"
# Sign client certificate
openssl x509 -req -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem \
-CAcreateserial -out client-cert.pem -days 365 \
-extensions v3_req -extfile <(cat <<EOF
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
EOF
)
Kubernetes Secret Configuration¶
Provider TLS Secret¶
apiVersion: v1
kind: Secret
metadata:
name: provider-tls
namespace: default
type: kubernetes.io/tls
data:
tls.crt: # base64 encoded server-cert.pem
tls.key: # base64 encoded server-key.pem
ca.crt: # base64 encoded ca-cert.pem
Manager TLS Secret¶
apiVersion: v1
kind: Secret
metadata:
name: manager-tls
namespace: virtrigaud-system
type: kubernetes.io/tls
data:
tls.crt: # base64 encoded client-cert.pem
tls.key: # base64 encoded client-key.pem
ca.crt: # base64 encoded ca-cert.pem
Provider Configuration¶
SDK Server Configuration¶
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"github.com/projectbeskar/virtrigaud/sdk/provider/server"
)
func main() {
// Load certificates
cert, err := tls.LoadX509KeyPair("/etc/tls/tls.crt", "/etc/tls/tls.key")
if err != nil {
panic(fmt.Sprintf("Failed to load server certificates: %v", err))
}
// Load CA certificate for client verification
caCert, err := ioutil.ReadFile("/etc/tls/ca.crt")
if err != nil {
panic(fmt.Sprintf("Failed to load CA certificate: %v", err))
}
caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
panic("Failed to parse CA certificate")
}
// Configure TLS
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caCertPool,
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
},
}
// Create server with mTLS
srv, err := server.New(&server.Config{
Port: 9443,
TLS: tlsConfig,
EnableTLS: true,
})
if err != nil {
panic(fmt.Sprintf("Failed to create server: %v", err))
}
// Register your provider implementation here
// providerv1.RegisterProviderServiceServer(srv.GRPCServer(), &YourProvider{})
if err := srv.Serve(); err != nil {
panic(fmt.Sprintf("Server failed: %v", err))
}
}
Helm Chart Values (Provider Runtime)¶
# values-mtls.yaml
tls:
enabled: true
secretName: provider-tls
# Mount TLS certificates
volumes:
- name: tls-certs
secret:
secretName: provider-tls
volumeMounts:
- name: tls-certs
mountPath: /etc/tls
readOnly: true
# Environment variables for TLS
env:
- name: TLS_ENABLED
value: "true"
- name: TLS_CERT_PATH
value: "/etc/tls/tls.crt"
- name: TLS_KEY_PATH
value: "/etc/tls/tls.key"
- name: TLS_CA_PATH
value: "/etc/tls/ca.crt"
Manager Configuration¶
Client TLS Configuration¶
// In manager code
func createProviderClient(endpoint string) (providerv1.ProviderServiceClient, error) {
// Load client certificates
cert, err := tls.LoadX509KeyPair("/etc/manager-tls/tls.crt", "/etc/manager-tls/tls.key")
if err != nil {
return nil, fmt.Errorf("failed to load client certificates: %w", err)
}
// Load CA certificate for server verification
caCert, err := ioutil.ReadFile("/etc/manager-tls/ca.crt")
if err != nil {
return nil, fmt.Errorf("failed to load CA certificate: %w", err)
}
caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
return nil, fmt.Errorf("failed to parse CA certificate")
}
// Configure TLS
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
ServerName: "provider-service", // Must match server certificate CN/SAN
MinVersion: tls.VersionTLS12,
}
// Create gRPC connection with mTLS
conn, err := grpc.Dial(endpoint,
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
)
if err != nil {
return nil, fmt.Errorf("failed to connect: %w", err)
}
return providerv1.NewProviderServiceClient(conn), nil
}
Certificate Rotation¶
Using cert-manager¶
# Install cert-manager first
# kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.12.0/cert-manager.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: virtrigaud-ca-issuer
spec:
ca:
secretName: virtrigaud-ca-secret
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: provider-tls
namespace: default
spec:
secretName: provider-tls
issuerRef:
name: virtrigaud-ca-issuer
kind: ClusterIssuer
commonName: provider-service
dnsNames:
- provider-service
- provider-service.default.svc.cluster.local
duration: 8760h # 1 year
renewBefore: 720h # 30 days before expiry
Manual Rotation Script¶
#!/bin/bash
# rotate-certs.sh
NAMESPACE=${1:-default}
SECRET_NAME=${2:-provider-tls}
echo "Rotating certificates for $SECRET_NAME in namespace $NAMESPACE"
# Generate new certificates (using the same process as above)
# ...
# Update Kubernetes secret
kubectl create secret tls $SECRET_NAME \
--cert=server-cert.pem \
--key=server-key.pem \
--namespace=$NAMESPACE \
--dry-run=client -o yaml | kubectl apply -f -
# Add CA certificate to the secret
kubectl patch secret $SECRET_NAME -n $NAMESPACE \
--patch="$(cat <<EOF
data:
ca.crt: $(base64 -w 0 ca-cert.pem)
EOF
)"
# Restart provider deployment to pick up new certificates
kubectl rollout restart deployment/provider-deployment -n $NAMESPACE
echo "Certificate rotation completed"
Security Best Practices¶
1. Certificate Validation¶
// Always validate certificate chains
func validateCertificate(cert *x509.Certificate, caCert *x509.Certificate) error {
roots := x509.NewCertPool()
roots.AddCert(caCert)
opts := x509.VerifyOptions{
Roots: roots,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
_, err := cert.Verify(opts)
return err
}
2. Certificate Pinning¶
// Pin specific certificate or CA
func createTLSConfigWithPinning(expectedCertFingerprint string) *tls.Config {
return &tls.Config{
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(rawCerts) == 0 {
return fmt.Errorf("no certificates provided")
}
cert, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return err
}
fingerprint := sha256.Sum256(cert.Raw)
if hex.EncodeToString(fingerprint[:]) != expectedCertFingerprint {
return fmt.Errorf("certificate fingerprint mismatch")
}
return nil
},
}
}
3. Monitoring and Alerting¶
# Prometheus AlertManager rules
groups:
- name: virtrigaud.certificates
rules:
- alert: CertificateExpiringSoon
expr: (cert_manager_certificate_expiration_timestamp_seconds - time()) / 86400 < 30
for: 1h
labels:
severity: warning
annotations:
summary: "Certificate expiring soon"
description: "Certificate {{ $labels.name }} expires in less than 30 days"
- alert: CertificateExpired
expr: cert_manager_certificate_expiration_timestamp_seconds < time()
for: 0m
labels:
severity: critical
annotations:
summary: "Certificate expired"
description: "Certificate {{ $labels.name }} has expired"
Troubleshooting¶
Common Issues¶
-
Certificate chain issues
-
SAN mismatch
-
TLS handshake failures
-
Clock skew issues