feat: Add OCSP support to federated environment
Adds Online Certificate Status Protocol (OCSP) support to the federated Openfire setup: - Add certificate generation script with full PKI hierarchy - Add certificate import script for Openfire keystores - Implement OCSP responder service via Docker compose - Update documentation with OCSP usage instructions The -o flag can now be used with start.sh to enable OCSP support.fix-revocation-examples
parent
3aac76fdcf
commit
7102765241
|
@ -101,6 +101,67 @@ For example:
|
|||
|
||||
`docker network connect openfire-testing_openfire-federated-net openfire-testing_xmpp1_1`
|
||||
|
||||
## Certificates
|
||||
|
||||
By default, the system uses self-signed certificates that have been pre-generated and added
|
||||
to the identity and trust stores for `xmpp1.localhost.example` and `xmpp2.localhost.example`.
|
||||
|
||||
This simplifies the setup process and avoids the need to generate certificates, but the
|
||||
limitations of this are that the certificates are not trusted by anything other than
|
||||
our own servers (`xmpp1` and `xmpp2`), and that they do not support OCSP (Online Certificate
|
||||
Status Protocol).
|
||||
|
||||
### OCSP Support
|
||||
A more fully featured solution is provided by the `-o` option of the `start.sh` script
|
||||
which enables OCSP support for the Openfire federated environment. When passed the `-o`
|
||||
option the script will generate a complete certificate hierarchy with OCSP support, and
|
||||
deploy an OCSP responder service configured to respond to OCSP requests for the server
|
||||
certificates.
|
||||
|
||||
Here's what the script creates:
|
||||
|
||||
* Root CA certificate (self-signed)
|
||||
* Intermediate CA certificate (signed by Root CA)
|
||||
* Two server certificates with OCSP information (one for each Openfire instance)
|
||||
* An OCSP responder certificate (for signing OCSP responses)
|
||||
* Full certificate chains for both servers (server + intermediate + root)
|
||||
* Certificate database (index.txt) for the OCSP responder to track certificate statuses
|
||||
|
||||
All certificates are stored in `./_data/certs/`.
|
||||
|
||||
```
|
||||
Root CA
|
||||
(Top level trust root)
|
||||
(Kept offline/secure)
|
||||
|
|
||||
v
|
||||
Intermediate CA
|
||||
(Day-to-day certificate issuer)
|
||||
(OCSP configuration point)
|
||||
|
|
||||
+--------------------+------------------+
|
||||
v v v
|
||||
XMPP1 Cert XMPP2 Cert OCSP Cert
|
||||
| | (Signs OCSP responses)
|
||||
v v
|
||||
XMPP1 Server XMPP2 Server
|
||||
[keystore] [keystore]
|
||||
(server's identity) (server's identity)
|
||||
[truststore] [truststore]
|
||||
(who to trust) (who to trust)
|
||||
```
|
||||
|
||||
This setup allows certificates to be checked for revocation status making a request to the
|
||||
OCSP responder:
|
||||
```bash
|
||||
openssl ocsp -url http://localhost:8888 \
|
||||
-issuer _data/certs/ca/intermediate-ca/intermediate.crt \
|
||||
-CAfile _data/certs/chain1.pem \
|
||||
-cert _data/certs/server1.crt \
|
||||
-text
|
||||
```
|
||||
|
||||
|
||||
## How it's built
|
||||
|
||||
To recreate the known good state for the system we first create base Openfire and Postgres containers.
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
services:
|
||||
# OCSP (Online Certificate Status Protocol) Responder Service
|
||||
#
|
||||
# This service provides real-time certificate validation for the development environment.
|
||||
# It works with certificates generated by ./scripts/generate-certificates.sh and imported
|
||||
# by ./scripts/import-certificates.sh.
|
||||
#
|
||||
# Configuration Parameters:
|
||||
# ------------------------
|
||||
# Port: 8888 - OCSP responder listens for validation requests
|
||||
# Index File: - Lists all issued certificates (/ca/intermediate-ca/index.txt)
|
||||
# CA Certificate: - Issuer's certificate (/ca/intermediate-ca/intermediate.crt)
|
||||
# OCSP Key Pair: - Responder credentials (/ca/ocsp-responder/ocsp.{key,crt})
|
||||
# Validity Period: - Responses valid for 1 day (-ndays 1)
|
||||
#
|
||||
# Test Certificate Status:
|
||||
# ----------------------
|
||||
# openssl ocsp -url http://localhost:8888 \
|
||||
# -issuer _data/certs/ca/intermediate-ca/intermediate.crt \
|
||||
# -CAfile _data/certs/chain1.pem \
|
||||
# -cert _data/certs/server1.crt \
|
||||
# -text # Adds human-readable output
|
||||
ocsp-responder:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- ./_data/certs/ca:/ca
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
apk add --no-cache openssl &&
|
||||
openssl ocsp -port 8888 -text \
|
||||
-index /ca/intermediate-ca/index.txt \
|
||||
-CA /ca/intermediate-ca/intermediate.crt \
|
||||
-rkey /ca/ocsp-responder/ocsp.key \
|
||||
-rsigner /ca/ocsp-responder/ocsp.crt \
|
||||
-ndays 1
|
||||
ports:
|
||||
- "8888:8888"
|
||||
networks:
|
||||
openfire-federated-net:
|
||||
ipv4_address: 172.50.0.30
|
|
@ -0,0 +1,160 @@
|
|||
#!/bin/bash
|
||||
|
||||
# OCSP server configuration
|
||||
# Defines where the OCSP responder will be accessible in the Docker network
|
||||
OCSP_URL="http://172.50.0.30:8888"
|
||||
|
||||
# Base directory for all certificate-related files
|
||||
# All paths in this script will be relative to this directory
|
||||
CERT_DIR="./_data/certs"
|
||||
|
||||
# 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 or 2 for xmpp1/xmpp2)
|
||||
generate_server_cert() {
|
||||
local instance=$1
|
||||
|
||||
# Generate server private key and CSR
|
||||
openssl genrsa -out "${CERT_DIR}/server${instance}.key" 2048
|
||||
|
||||
# Create OpenSSL config file for SAN support
|
||||
cat > "${CERT_DIR}/server${instance}.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 = xmpp${instance}.localhost.example
|
||||
|
||||
[v3_req]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = critical, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = xmpp${instance}.localhost.example
|
||||
DNS.2 = *.xmpp${instance}.localhost.example
|
||||
EOF
|
||||
|
||||
# Generate CSR with the config file
|
||||
openssl req -new -key "${CERT_DIR}/server${instance}.key" \
|
||||
-out "${CERT_DIR}/server${instance}.csr" \
|
||||
-config "${CERT_DIR}/server${instance}.cnf"
|
||||
|
||||
# Create OpenSSL config for certificate signing
|
||||
cat > "${CERT_DIR}/server${instance}_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 = xmpp${instance}.localhost.example
|
||||
DNS.2 = *.xmpp${instance}.localhost.example
|
||||
EOF
|
||||
|
||||
# Sign the server certificate with the Intermediate CA
|
||||
openssl x509 -req -in "${CERT_DIR}/server${instance}.csr" \
|
||||
-CA "${CERT_DIR}/ca/intermediate-ca/intermediate.crt" \
|
||||
-CAkey "${CERT_DIR}/ca/intermediate-ca/private/intermediate.key" -CAcreateserial \
|
||||
-out "${CERT_DIR}/server${instance}.crt" -days 365 \
|
||||
-extfile "${CERT_DIR}/server${instance}_sign.cnf"
|
||||
|
||||
# Create the full certificate chain
|
||||
cat "${CERT_DIR}/server${instance}.crt" "${CERT_DIR}/ca/intermediate-ca/intermediate.crt" "${CERT_DIR}/ca/root-ca/ca.crt" > "${CERT_DIR}/chain${instance}.pem"
|
||||
|
||||
# Add to certificate database
|
||||
SERIAL=$(openssl x509 -in "${CERT_DIR}/server${instance}.crt" -noout -serial | cut -d'=' -f2)
|
||||
SUBJECT=$(openssl x509 -in "${CERT_DIR}/server${instance}.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${instance}.cnf" "${CERT_DIR}/server${instance}_sign.cnf"
|
||||
|
||||
# Display the certificate's subject and SANs for verification
|
||||
echo "Certificate generated for instance ${instance}:"
|
||||
openssl x509 -in "${CERT_DIR}/server${instance}.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')
|
||||
|
||||
# Generate certificates for both Openfire instances
|
||||
generate_server_cert 1
|
||||
generate_server_cert 2
|
||||
|
||||
# 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"
|
||||
openssl verify -CAfile <(cat "${CERT_DIR}/ca/root-ca/ca.crt" "${CERT_DIR}/ca/intermediate-ca/intermediate.crt") "${CERT_DIR}/server1.crt"
|
||||
openssl verify -CAfile <(cat "${CERT_DIR}/ca/root-ca/ca.crt" "${CERT_DIR}/ca/intermediate-ca/intermediate.crt") "${CERT_DIR}/server2.crt"
|
||||
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\n' "$OCSP_URL"
|
|
@ -0,0 +1,126 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Default password for all keystores and truststores
|
||||
# This is Openfire's default password - changing it would require updating security.xml
|
||||
KEYSTORE_PASSWORD="changeit"
|
||||
|
||||
# Base directory where certificates were generated
|
||||
# This should match the CERT_DIR from the certificate generation script
|
||||
CERT_DIR="./_data/certs"
|
||||
|
||||
# Function to import certificates and set up keystores/truststores for one Openfire instance
|
||||
# Parameters:
|
||||
# $1: instance number (1 or 2 for xmpp1/xmpp2)
|
||||
import_certificates() {
|
||||
local instance=$1
|
||||
|
||||
# Directory where Openfire expects to find its certificates
|
||||
local conf_dir="_data/xmpp/${instance}/conf/security"
|
||||
|
||||
echo "Importing certificates for Openfire instance ${instance}..."
|
||||
|
||||
# Ensure the security directory exists
|
||||
mkdir -p "${conf_dir}"
|
||||
|
||||
# Remove any existing keystore to start fresh
|
||||
rm -f "${conf_dir}/keystore"
|
||||
|
||||
echo "Creating new keystore for instance ${instance}"
|
||||
# Create a new empty keystore by generating and immediately deleting a temporary keypair
|
||||
# This ensures the keystore is properly initialized with the correct format
|
||||
keytool -genkeypair \
|
||||
-keystore "${conf_dir}/keystore" \
|
||||
-storepass "${KEYSTORE_PASSWORD}" \
|
||||
-keypass "${KEYSTORE_PASSWORD}" \
|
||||
-alias "default" \
|
||||
-dname "CN=temporary" \
|
||||
-keyalg RSA \
|
||||
-validity 1
|
||||
# Remove the temporary keypair, leaving an empty keystore
|
||||
keytool -delete \
|
||||
-alias "default" \
|
||||
-keystore "${conf_dir}/keystore" \
|
||||
-storepass "${KEYSTORE_PASSWORD}"
|
||||
|
||||
# Import the server's certificate and private key
|
||||
# First convert them to PKCS12 format which can be imported into a Java keystore
|
||||
echo "Importing server certificate and private key..."
|
||||
openssl pkcs12 -export \
|
||||
-in "${CERT_DIR}/server${instance}.crt" \
|
||||
-inkey "${CERT_DIR}/server${instance}.key" \
|
||||
-chain \
|
||||
-CAfile "${CERT_DIR}/chain${instance}.pem" \
|
||||
-name "xmpp${instance}.localhost.example" \
|
||||
-out "${CERT_DIR}/server${instance}.p12" \
|
||||
-password "pass:${KEYSTORE_PASSWORD}"
|
||||
|
||||
# Import the PKCS12 file into the keystore
|
||||
keytool -importkeystore \
|
||||
-deststorepass "${KEYSTORE_PASSWORD}" \
|
||||
-destkeypass "${KEYSTORE_PASSWORD}" \
|
||||
-destkeystore "${conf_dir}/keystore" \
|
||||
-srckeystore "${CERT_DIR}/server${instance}.p12" \
|
||||
-srcstoretype PKCS12 \
|
||||
-srcstorepass "${KEYSTORE_PASSWORD}" \
|
||||
-alias "xmpp${instance}.localhost.example"
|
||||
|
||||
# Create or update truststore
|
||||
# truststore - used by the server to verify other servers
|
||||
if [ ! -f "${conf_dir}/truststore" ]; then
|
||||
echo "Creating new truststore for instance ${instance}"
|
||||
# Initialize a new truststore with a dummy entry
|
||||
keytool -importpass \
|
||||
-keystore "${conf_dir}/truststore" \
|
||||
-storepass "${KEYSTORE_PASSWORD}" \
|
||||
-storetype JKS \
|
||||
-alias "init" \
|
||||
-noprompt
|
||||
fi
|
||||
|
||||
# Add our CA certificates to truststore
|
||||
# We preserve existing entries to maintain trust for other certificates
|
||||
echo "Adding CA certificates to truststore..."
|
||||
|
||||
# Import root CA if not already present
|
||||
# grep -q checks if the alias already exists in the store
|
||||
if ! keytool -list -keystore "${conf_dir}/truststore" -storepass "${KEYSTORE_PASSWORD}" | grep -q "root-ca"; then
|
||||
keytool -import -noprompt \
|
||||
-keystore "${conf_dir}/truststore" \
|
||||
-storepass "${KEYSTORE_PASSWORD}" \
|
||||
-alias "root-ca" \
|
||||
-file "${CERT_DIR}/ca/root-ca/ca.crt"
|
||||
echo "Added root CA to truststore"
|
||||
else
|
||||
echo "Root CA already exists in truststore"
|
||||
fi
|
||||
|
||||
# Import intermediate CA if not already present
|
||||
if ! keytool -list -keystore "${conf_dir}/truststore" -storepass "${KEYSTORE_PASSWORD}" | grep -q "intermediate-ca"; then
|
||||
keytool -import -noprompt \
|
||||
-keystore "${conf_dir}/truststore" \
|
||||
-storepass "${KEYSTORE_PASSWORD}" \
|
||||
-alias "intermediate-ca" \
|
||||
-file "${CERT_DIR}/ca/intermediate-ca/intermediate.crt"
|
||||
echo "Added intermediate CA to truststore"
|
||||
else
|
||||
echo "Intermediate CA already exists in truststore"
|
||||
fi
|
||||
|
||||
echo "Certificate import completed for instance ${instance}"
|
||||
}
|
||||
|
||||
# Main script execution
|
||||
|
||||
echo "Starting certificate import process..."
|
||||
|
||||
# Import certificates for both Openfire instances
|
||||
import_certificates 1
|
||||
import_certificates 2
|
||||
|
||||
# Clean up temporary PKCS12 files that were created during the import process
|
||||
rm -f "${CERT_DIR}"/*.p12
|
||||
|
||||
# Display commands that can be used to verify the keystore contents
|
||||
echo -e "\nTo verify the keystores, run:"
|
||||
echo "keytool -list -keystore _data/xmpp/1/conf/security/keystore -storepass ${KEYSTORE_PASSWORD}"
|
||||
echo "keytool -list -keystore _data/xmpp/1/conf/security/truststore -storepass ${KEYSTORE_PASSWORD}"
|
|
@ -1,8 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
usage() { echo "Usage: $0 [-n openfire-tag] [-6] [-h]
|
||||
usage() { echo "Usage: $0 [-n openfire-tag] [-6] [-o] [-h]
|
||||
-n openfire-tag Launches all Openfire instances with the specified tag. This overrides the value in .env
|
||||
-6 Replace standard IPv4-based bridge networking with IPv6.
|
||||
-o Enable OCSP support, generates compatible certificates, & deploys associated OCSP responder
|
||||
-h Show this helpful information
|
||||
"; exit 0; }
|
||||
|
||||
|
@ -20,7 +21,7 @@ source "$SCRIPTPATH/../_common/functions.sh"
|
|||
|
||||
check_deps
|
||||
|
||||
while getopts n:6h o; do
|
||||
while getopts n:6oh o; do
|
||||
case "$o" in
|
||||
n)
|
||||
if [[ $OPTARG =~ " " ]]; then
|
||||
|
@ -34,6 +35,10 @@ while getopts n:6h o; do
|
|||
echo "Using IPv6"
|
||||
NETWORK_COMPOSE_FILE="docker-compose-network-dualstack.yml"
|
||||
;;
|
||||
o)
|
||||
echo "Enabling OCSP support"
|
||||
export ENABLE_OCSP=true
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
;;
|
||||
|
@ -60,5 +65,12 @@ mkdir _data
|
|||
cp -r xmpp _data/
|
||||
cp -r plugins _data/
|
||||
|
||||
if [ "$ENABLE_OCSP" = true ]; then
|
||||
echo "Enabling OCSP support"
|
||||
"$SCRIPTPATH"/scripts/generate-certificates.sh
|
||||
"$SCRIPTPATH"/scripts/import-certificates.sh
|
||||
COMPOSE_FILE_COMMAND+=("-f" "docker-compose-ocsp-responder.yml")
|
||||
fi
|
||||
|
||||
"${COMPOSE_FILE_COMMAND[@]}" up -d || popd
|
||||
popd
|
Loading…
Reference in New Issue