From 5d755990b79d992de1b3fd737d837e3aad0831f4 Mon Sep 17 00:00:00 2001 From: Steve Munene Date: Fri, 17 Jan 2025 10:20:41 +0300 Subject: [PATCH] SMQ-1485 - Add mutual TLS support for gRPC connections (#2646) Signed-off-by: nyagamunene --- docker/.env | 4 + docker/docker-compose.yml | 144 +++++++++++++++++++++++++++------ scripts/generate-grpc-certs.sh | 73 +++++++++++++++++ 3 files changed, 197 insertions(+), 24 deletions(-) create mode 100755 scripts/generate-grpc-certs.sh diff --git a/docker/.env b/docker/.env index 53a31560f9..989bad6e05 100644 --- a/docker/.env +++ b/docker/.env @@ -2,6 +2,10 @@ # SPDX-License-Identifier: Apache-2.0 # Docker: Environment variables in Compose +## Enable GRPC SSL +## If enabled run ./scripts/generate-grpc-certs.sh to generate the GRPC certs +GRPC_MTLS= + ## NginX SMQ_NGINX_HTTP_PORT=80 SMQ_NGINX_SSL_PORT=443 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a8fe685d11..b5623c24c5 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -278,6 +278,21 @@ services: target: /auth-grpc-client-ca${SMQ_DOMAINS_GRPC_CLIENT_CA_CERTS:+.crt} bind: create_host_path: true + - type: bind + source: ${SMQ_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /auth-grpc-client${SMQ_AUTH_GRPC_CLIENT_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_AUTH_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /auth-grpc-client${SMQ_AUTH_GRPC_CLIENT_KEY:+.key} + bind: + create_host_path: true + - type: bind + source: ${SMQ_AUTH_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} + target: /auth-grpc-server-ca${SMQ_AUTH_GRPC_SERVER_CA_CERTS:+.crt} + bind: + create_host_path: true invitations-db: image: postgres:16.2-alpine @@ -544,6 +559,22 @@ services: target: /channels-grpc-server-ca${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:+.crt} bind: create_host_path: true + # Group gRPC client certificates + - type: bind + source: ${SMQ_GROUPS_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /groups-grpc-client${SMQ_GROUPS_GRPC_CLIENT_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_GROUPS_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /groups-grpc-client${SMQ_GROUPS_GRPC_CLIENT_KEY:+.key} + bind: + create_host_path: true + - type: bind + source: ${SMQ_GROUPS_GRPC_SERVER_CERT:-ssl/certs/dummy/server_ca} + target: /groups-grpc-server-ca${SMQ_GROUPS_GRPC_SERVER_CERT:+.crt} + bind: + create_host_path: true channels-db: image: postgres:16.2-alpine @@ -643,6 +674,66 @@ services: target: /auth-grpc-server-ca${SMQ_AUTH_GRPC_SERVER_CA_CERTS:+.crt} bind: create_host_path: true + - type: bind + source: ${SMQ_CLIENTS_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /clients-grpc-client${SMQ_CLIENTS_AUTH_GRPC_CLIENT_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CLIENTS_AUTH_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /clients-grpc-client${SMQ_CLIENTS_AUTH_GRPC_CLIENT_KEY:+.key} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CLIENTS_AUTH_GRPC_SERVER_CERT:-ssl/certs/dummy/server_ca} + target: /clients-grpc-server-ca${SMQ_CLIENTS_AUTH_GRPC_SERVER_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_GROUPS_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /groups-grpc-client${SMQ_GROUPS_GRPC_CLIENT_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_GROUPS_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /groups-grpc-client${SMQ_GROUPS_GRPC_CLIENT_KEY:+.key} + bind: + create_host_path: true + - type: bind + source: ${SMQ_GROUPS_GRPC_SERVER_CERT:-ssl/certs/dummy/server_ca} + target: /groups-grpc-server-ca${SMQ_GROUPS_GRPC_SERVER_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CHANNELS_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CHANNELS_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_KEY:+.key} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CHANNELS_GRPC_SERVER_CERT:-ssl/certs/dummy/server_ca} + target: /channels-grpc-server${SMQ_CHANNELS_GRPC_SERVER_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CHANNELS_GRPC_SERVER_KEY:-ssl/certs/dummy/server_key} + target: /channels-grpc-server${SMQ_CHANNELS_GRPC_SERVER_KEY:+.key} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} + target: /channels-grpc-server-ca${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CHANNELS_GRPC_CLIENT_CA_CERTS:-ssl/certs/dummy/client_ca} + target: /channels-grpc-client-ca${SMQ_CHANNELS_GRPC_CLIENT_CA_CERTS:+.crt} + bind: + create_host_path: true users-db: image: postgres:16.2-alpine @@ -924,18 +1015,18 @@ services: create_host_path: true # Channels gRPC mTLS client certificates - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:+.crt} + source: ${SMQ_CHANNELS_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_CERT:+.crt} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:+.key} + source: ${SMQ_CHANNELS_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_KEY:+.key} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} - target: /channels-grpc-server-ca${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:+.crt} + source: ${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} + target: /channels-grpc-server-ca${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:+.crt} bind: create_host_path: true @@ -995,18 +1086,18 @@ services: create_host_path: true # Channels gRPC mTLS client certificates - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:+.crt} + source: ${SMQ_CHANNELS_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_CERT:+.crt} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:+.key} + source: ${SMQ_CHANNELS_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_KEY:+.key} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} - target: /channels-grpc-server-ca${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:+.crt} + source: ${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} + target: /channels-grpc-server-ca${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:+.crt} bind: create_host_path: true # Auth gRPC mTLS client certificates @@ -1082,18 +1173,23 @@ services: create_host_path: true # Channels gRPC mTLS client certificates - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:+.crt} + source: ${SMQ_CHANNELS_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${SMQ_CHANNELS_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_KEY:+.key} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:+.key} + source: ${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} + target: /channels-grpc-server-ca${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:+.crt} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} - target: /channels-grpc-server-ca${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:+.crt} + source: ${SMQ_CHANNELS_GRPC_CLIENT_CA_CERTS:-ssl/certs/dummy/client_ca} + target: /channels-grpc-client-ca${SMQ_CHANNELS_GRPC_CLIENT_CA_CERTS:+.crt} bind: create_host_path: true @@ -1153,18 +1249,18 @@ services: create_host_path: true # Channels gRPC mTLS client certificates - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_CERT:+.crt} + source: ${SMQ_CHANNELS_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_CERT:+.crt} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} - target: /channels-grpc-client${SMQ_CHANNELS_AUTH_GRPC_CLIENT_KEY:+.key} + source: ${SMQ_CHANNELS_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /channels-grpc-client${SMQ_CHANNELS_GRPC_CLIENT_KEY:+.key} bind: create_host_path: true - type: bind - source: ${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} - target: /channels-grpc-server-ca${SMQ_CHANNELS_AUTH_GRPC_SERVER_CA_CERTS:+.crt} + source: ${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} + target: /channels-grpc-server-ca${SMQ_CHANNELS_GRPC_SERVER_CA_CERTS:+.crt} bind: create_host_path: true # Auth gRPC mTLS client certificates diff --git a/scripts/generate-grpc-certs.sh b/scripts/generate-grpc-certs.sh new file mode 100755 index 0000000000..6cd33397a8 --- /dev/null +++ b/scripts/generate-grpc-certs.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Copyright (c) Abstract Machines +# SPDX-License-Identifier: Apache-2.0 + +mkdir -p docker/ssl/certs +cd docker + +if [ ! -f ssl/certs/ca.key ] || [ ! -f ssl/certs/ca.crt ]; then + echo "Generating new CA certificates..." + openssl genrsa -out ssl/certs/ca.key 4096 + openssl req -new -x509 -days 365 -key ssl/certs/ca.key -out ssl/certs/ca.crt -subj "/C=FR/ST=Paris/L=Paris/O=SuperMQ/OU=SuperMQ/CN=SuperMQ Root CA" +else + echo "Using existing CA certificates..." +fi + +generate_cert() { + local name=$1 + local type=$2 + local cn="$3" + + openssl genrsa -out "ssl/certs/${name}-grpc-${type}.key" 4096 + + openssl req -new \ + -key "ssl/certs/${name}-grpc-${type}.key" \ + -out "ssl/certs/${name}-grpc-${type}.csr" \ + -subj "/C=FR/ST=Paris/L=Paris/O=SuperMQ/OU=SuperMQ/CN=${cn}" + + cat > "ssl/certs/${name}-grpc-${type}.ext" << EOF +authorityKeyIdentifier=keyid,issuer +basicConstraints=CA:FALSE +keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = localhost +DNS.2 = ${name} +EOF + + openssl x509 -req \ + -in "ssl/certs/${name}-grpc-${type}.csr" \ + -CA ssl/certs/ca.crt \ + -CAkey ssl/certs/ca.key \ + -CAcreateserial \ + -out "ssl/certs/${name}-grpc-${type}.crt" \ + -days 365 \ + -extfile "ssl/certs/${name}-grpc-${type}.ext" + + rm "ssl/certs/${name}-grpc-${type}.csr" "ssl/certs/${name}-grpc-${type}.ext" +} + +# Generate server certificates +generate_cert "auth" "server" "auth.supermq.local" +generate_cert "groups" "server" "groups.supermq.local" +generate_cert "channels" "server" "channels.supermq.local" +generate_cert "clients" "server" "clients.supermq.local" + +# Generate client certificates +generate_cert "auth" "client" "auth-client.supermq.local" +generate_cert "domains" "client" "domains-client.supermq.local" +generate_cert "groups" "client" "groups-client.supermq.local" +generate_cert "channels" "client" "channels-client.supermq.local" +generate_cert "clients" "client" "clients-client.supermq.local" + +cd ssl/certs +chmod 644 *.crt +chmod 600 *.key + +for service in auth groups channels clients domains; do + ln -sf ca.crt "${service}-grpc-server-ca.crt" + ln -sf ca.crt "${service}-grpc-client-ca.crt" +done + +echo "Certificates generated successfully in docker/ssl/certs/"