openfire-docker-compose/federation/scripts/generate-certificates.sh

206 lines
8.0 KiB
Bash
Executable File

#!/bin/bash
# OCSP server configuration
# Defines where the OCSP responder will be accessible in the Docker network
OCSP_URL="http://ocsp.localhost.example:8888"
# Base directory for all certificate-related files
# All paths in this script will be relative to this directory
CERT_DIR="./_data/certs"
XMPP_BASE_DIR="./_data/xmpp"
# Check if OpenSSL is installed
if ! command -v openssl &> /dev/null; then
echo "Error: OpenSSL is not installed"
exit 1
fi
# Function to check which server directories exist
get_server_instances() {
local instances=()
# Look for numbered directories in the xmpp base directory
for dir in "$XMPP_BASE_DIR"/*/; do
if [[ -d "$dir" ]]; then
# Extract the number from the directory name
local num=$(basename "$dir")
if [[ "$num" =~ ^[0-9]+$ ]]; then
instances+=("$num")
fi
fi
done
echo "${instances[@]}"
}
# Create the PKI directory structure:
# We only need:
# - root-ca: Root Certificate Authority files
# - intermediate-ca: Intermediate Certificate Authority files
# - ocsp-responder: OCSP responder certificates
mkdir -p "${CERT_DIR}/ca/root-ca/private" \
"${CERT_DIR}/ca/intermediate-ca/private" \
"${CERT_DIR}/ca/ocsp-responder"
chmod 700 "${CERT_DIR}/ca/root-ca/private" "${CERT_DIR}/ca/intermediate-ca/private"
# Initialize the certificate databases and serial number counters
# index.txt acts as a database of all certificates
# serial defines the next certificate serial number
echo -n > "${CERT_DIR}/ca/intermediate-ca/index.txt"
echo 1000 > "${CERT_DIR}/ca/intermediate-ca/serial"
# Generate the Root CA private key and certificate
# This is the top-level certificate authority that signs the intermediate CA
openssl genrsa -out "${CERT_DIR}/ca/root-ca/private/ca.key" 4096
openssl req -new -x509 -key "${CERT_DIR}/ca/root-ca/private/ca.key" \
-out "${CERT_DIR}/ca/root-ca/ca.crt" -days 3650 \
-subj "/C=GB/ST=London/L=London/O=Test Openfire/CN=Test Openfire Root CA"
# Generate the Intermediate CA private key and certificate signing request (CSR)
# The intermediate CA will be used to sign the server and OCSP responder certificates
openssl genrsa -out "${CERT_DIR}/ca/intermediate-ca/private/intermediate.key" 4096
openssl req -new -key "${CERT_DIR}/ca/intermediate-ca/private/intermediate.key" \
-out "${CERT_DIR}/ca/intermediate-ca/intermediate.csr" \
-subj "/C=GB/ST=London/L=London/O=Test Openfire/CN=Test Openfire Intermediate CA"
# Sign the Intermediate CA certificate with the Root CA
# The certificate includes:
# - CA capabilities (basicConstraints)
# - Permission to sign certificates (keyUsage)
# - Location of the OCSP responder (authorityInfoAccess)
openssl x509 -req -in "${CERT_DIR}/ca/intermediate-ca/intermediate.csr" \
-CA "${CERT_DIR}/ca/root-ca/ca.crt" \
-CAkey "${CERT_DIR}/ca/root-ca/private/ca.key" -CAcreateserial \
-out "${CERT_DIR}/ca/intermediate-ca/intermediate.crt" -days 1825 \
-extfile <(printf 'basicConstraints=critical,CA:true,pathlen:0\nkeyUsage=critical,digitalSignature,keyCertSign,cRLSign\nauthorityInfoAccess=OCSP;URI:%s' "$OCSP_URL")
# Function to generate a server certificate and add it to the certificate database
# Parameters:
# $1: instance number (1, 2, or 3 for xmpp1/xmpp2/xmpp3)
generate_server_cert() {
local instance=$1
local server_name="xmpp${instance}"
# Generate server private key and CSR
openssl genrsa -out "${CERT_DIR}/${server_name}.key" 2048
# Create OpenSSL config file for SAN support
cat > "${CERT_DIR}/${server_name}.cnf" << EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = GB
ST = London
L = London
O = Test Openfire
CN = ${server_name}.localhost.example
[v3_req]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${server_name}.localhost.example
DNS.2 = *.${server_name}.localhost.example
EOF
# Generate CSR with the config file
openssl req -new -key "${CERT_DIR}/${server_name}.key" \
-out "${CERT_DIR}/${server_name}.csr" \
-config "${CERT_DIR}/${server_name}.cnf"
# Create OpenSSL config for certificate signing
cat > "${CERT_DIR}/${server_name}_sign.cnf" << EOF
basicConstraints = critical,CA:false
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
authorityInfoAccess = OCSP;URI:${OCSP_URL}
[alt_names]
DNS.1 = ${server_name}.localhost.example
DNS.2 = *.${server_name}.localhost.example
EOF
# Sign the server certificate with the Intermediate CA
openssl x509 -req -in "${CERT_DIR}/${server_name}.csr" \
-CA "${CERT_DIR}/ca/intermediate-ca/intermediate.crt" \
-CAkey "${CERT_DIR}/ca/intermediate-ca/private/intermediate.key" -CAcreateserial \
-out "${CERT_DIR}/${server_name}.crt" -days 365 \
-extfile "${CERT_DIR}/${server_name}_sign.cnf"
# Create the full certificate chain
cat "${CERT_DIR}/${server_name}.crt" \
"${CERT_DIR}/ca/intermediate-ca/intermediate.crt" \
"${CERT_DIR}/ca/root-ca/ca.crt" > "${CERT_DIR}/${server_name}_chain.pem"
# Add to certificate database
SERIAL=$(openssl x509 -in "${CERT_DIR}/${server_name}.crt" -noout -serial | cut -d'=' -f2)
SUBJECT=$(openssl x509 -in "${CERT_DIR}/${server_name}.crt" -noout -subject | cut -d'=' -f2-)
printf 'V\t%s\t\t%s\tunknown\t%s\n' "$(date -u +%y%m%d%H%M%SZ)" "$SERIAL" "$SUBJECT" >> "${CERT_DIR}/ca/intermediate-ca/index.txt"
# Clean up temporary config files
rm -f "${CERT_DIR}/${server_name}.cnf" "${CERT_DIR}/${server_name}_sign.cnf"
# Display the certificate's subject and SANs for verification
echo "Certificate generated for ${server_name}:"
openssl x509 -in "${CERT_DIR}/${server_name}.crt" -noout -subject -ext subjectAltName
}
# Generate the OCSP responder certificate
# This certificate will be used to sign OCSP responses
openssl genrsa -out "${CERT_DIR}/ca/ocsp-responder/ocsp.key" 2048
openssl req -new -key "${CERT_DIR}/ca/ocsp-responder/ocsp.key" \
-out "${CERT_DIR}/ca/ocsp-responder/ocsp.csr" \
-subj "/C=GB/ST=London/L=London/O=Test Openfire/CN=ocsp.example.com"
# Sign the OCSP responder certificate
# The certificate includes:
# - Non-CA status (basicConstraints)
# - OCSP signing capability (extendedKeyUsage)
openssl x509 -req -in "${CERT_DIR}/ca/ocsp-responder/ocsp.csr" \
-CA "${CERT_DIR}/ca/intermediate-ca/intermediate.crt" \
-CAkey "${CERT_DIR}/ca/intermediate-ca/private/intermediate.key" -CAcreateserial \
-out "${CERT_DIR}/ca/ocsp-responder/ocsp.crt" -days 365 \
-extfile <(printf 'basicConstraints=critical,CA:false\nkeyUsage=critical,digitalSignature\nextendedKeyUsage=OCSPSigning')
# Get the list of server instances that exist
SERVER_INSTANCES=($(get_server_instances))
if [ ${#SERVER_INSTANCES[@]} -eq 0 ]; then
echo "Error: No server directories found in ${XMPP_BASE_DIR}"
exit 1
fi
echo "Found server directories for instances: ${SERVER_INSTANCES[*]}"
# Generate certificates for each existing server instance
for instance in "${SERVER_INSTANCES[@]}"; do
echo "Generating certificates for xmpp${instance}"
generate_server_cert "$instance"
done
# Verify the certificate chain for all generated certificates
# This ensures that all certificates are properly signed and trusted
echo "Verifying certificates..."
openssl verify -CAfile "${CERT_DIR}/ca/root-ca/ca.crt" "${CERT_DIR}/ca/intermediate-ca/intermediate.crt"
# Verify each server certificate
for instance in "${SERVER_INSTANCES[@]}"; do
openssl verify -CAfile <(cat "${CERT_DIR}/ca/root-ca/ca.crt" "${CERT_DIR}/ca/intermediate-ca/intermediate.crt") \
"${CERT_DIR}/xmpp${instance}.crt"
done
openssl verify -CAfile <(cat "${CERT_DIR}/ca/root-ca/ca.crt" "${CERT_DIR}/ca/intermediate-ca/intermediate.crt") \
"${CERT_DIR}/ca/ocsp-responder/ocsp.crt"
printf '\nCertificate generation complete.'
printf '\nOCSP URL configured as: %s\n' "$OCSP_URL"
printf 'Generated certificates for servers: %s\n\n' "${SERVER_INSTANCES[*]}"