TLS Everywhere¶
Introduction¶
TLS Everywhere chiffre toutes les communications entre les services OpenStack, les clients, et les backends. C'est une exigence pour les environnements de production sécurisés.
Prérequis¶
- OpenStack fonctionnel
- Autorité de certification (interne ou externe)
- Hardening CIS appliqué
Points à apprendre¶
Architecture TLS¶
graph TB
client["Client<br/>(CLI/Horizon)"]
subgraph Edge["Edge (TLS External)"]
haproxy["HAProxy<br/>TLS Termination<br/>Cert: public CA"]
end
subgraph Internal["Internal (TLS Internal)"]
keystone["Keystone<br/>TLS<br/>Cert: internal CA"]
nova["Nova<br/>TLS"]
neutron["Neutron<br/>TLS"]
glance["Glance<br/>TLS"]
end
subgraph Backend["Backend (TLS Backend)"]
mariadb["MariaDB<br/>TLS<br/>Galera Encryption"]
rabbitmq["RabbitMQ<br/>TLS"]
ceph["Ceph<br/>TLS"]
end
client -->|HTTPS<br/>TLS 1.3| haproxy
haproxy -->|HTTPS<br/>TLS 1.2+| keystone
keystone -->|TLS| mariadb
keystone -->|AMQPS| rabbitmq
Configuration Kolla TLS¶
# /etc/kolla/globals.yml
# === TLS External (clients → HAProxy) ===
kolla_enable_tls_external: "yes"
kolla_external_fqdn: "cloud.example.com"
kolla_external_fqdn_cert: "/etc/kolla/certificates/haproxy.pem"
# === TLS Internal (HAProxy → services) ===
kolla_enable_tls_internal: "yes"
kolla_internal_fqdn: "internal.cloud.example.com"
# === Backend TLS ===
kolla_enable_tls_backend: "yes"
# === CA Certificate ===
kolla_copy_ca_into_containers: "yes"
openstack_cacert: "/etc/pki/tls/certs/ca-bundle.crt"
# === TLS pour backends spécifiques ===
rabbitmq_enable_tls: "yes"
# mariadb_enable_tls: "yes" # Activé avec tls_backend
Génération des certificats¶
#!/bin/bash
# generate-certs.sh
DOMAIN="example.com"
CA_DIR="/etc/kolla/certificates/ca"
CERT_DIR="/etc/kolla/certificates"
mkdir -p $CA_DIR $CERT_DIR
# === Créer CA interne ===
openssl genrsa -out $CA_DIR/ca-key.pem 4096
openssl req -x509 -new -nodes \
-key $CA_DIR/ca-key.pem \
-sha256 -days 3650 \
-out $CA_DIR/ca.pem \
-subj "/C=FR/ST=IDF/L=Paris/O=MyOrg/OU=IT/CN=OpenStack Internal CA"
# === Certificat HAProxy (external) ===
cat > $CERT_DIR/haproxy.cnf << EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = FR
ST = IDF
L = Paris
O = MyOrg
OU = Cloud
CN = cloud.${DOMAIN}
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = cloud.${DOMAIN}
DNS.2 = *.cloud.${DOMAIN}
IP.1 = 10.0.0.10
EOF
openssl genrsa -out $CERT_DIR/haproxy-key.pem 2048
openssl req -new \
-key $CERT_DIR/haproxy-key.pem \
-out $CERT_DIR/haproxy.csr \
-config $CERT_DIR/haproxy.cnf
openssl x509 -req \
-in $CERT_DIR/haproxy.csr \
-CA $CA_DIR/ca.pem \
-CAkey $CA_DIR/ca-key.pem \
-CAcreateserial \
-out $CERT_DIR/haproxy-cert.pem \
-days 365 \
-sha256 \
-extensions v3_req \
-extfile $CERT_DIR/haproxy.cnf
# Combiner pour HAProxy
cat $CERT_DIR/haproxy-cert.pem $CERT_DIR/haproxy-key.pem > $CERT_DIR/haproxy.pem
# === Certificats internes (par service) ===
for service in keystone nova neutron glance cinder heat octavia placement; do
openssl genrsa -out $CERT_DIR/${service}-key.pem 2048
openssl req -new \
-key $CERT_DIR/${service}-key.pem \
-out $CERT_DIR/${service}.csr \
-subj "/C=FR/ST=IDF/L=Paris/O=MyOrg/OU=OpenStack/CN=${service}.internal"
openssl x509 -req \
-in $CERT_DIR/${service}.csr \
-CA $CA_DIR/ca.pem \
-CAkey $CA_DIR/ca-key.pem \
-CAcreateserial \
-out $CERT_DIR/${service}-cert.pem \
-days 365 \
-sha256
done
echo "Certificates generated in $CERT_DIR"
Configuration HAProxy TLS¶
# /etc/haproxy/haproxy.cfg (généré par Kolla)
global
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets
frontend keystone_public
bind 10.0.0.10:5000 ssl crt /etc/haproxy/certs/haproxy.pem
default_backend keystone_public_back
backend keystone_public_back
option httpchk GET /healthcheck
http-check expect status 200
server controller-1 10.0.0.11:5000 check ssl verify required ca-file /etc/haproxy/certs/ca.pem
server controller-2 10.0.0.12:5000 check ssl verify required ca-file /etc/haproxy/certs/ca.pem
server controller-3 10.0.0.13:5000 check ssl verify required ca-file /etc/haproxy/certs/ca.pem
Configuration MariaDB TLS¶
# /etc/kolla/config/mariadb/galera.cnf
[mysqld]
ssl-ca=/etc/mysql/ssl/ca.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
require_secure_transport=ON
# Galera SSL
wsrep_provider_options="socket.ssl_key=/etc/mysql/ssl/server-key.pem;socket.ssl_cert=/etc/mysql/ssl/server-cert.pem;socket.ssl_ca=/etc/mysql/ssl/ca.pem"
Configuration RabbitMQ TLS¶
# /etc/kolla/config/rabbitmq/rabbitmq.conf
listeners.ssl.default = 5671
ssl_options.cacertfile = /etc/rabbitmq/ssl/ca.pem
ssl_options.certfile = /etc/rabbitmq/ssl/server-cert.pem
ssl_options.keyfile = /etc/rabbitmq/ssl/server-key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
# Désactiver le port non-TLS
listeners.tcp = none
Configuration OpenStack clients¶
# /etc/kolla/config/nova/nova.conf
[DEFAULT]
transport_url = rabbit://openstack:password@controller-1:5671,controller-2:5671,controller-3:5671//
[oslo_messaging_rabbit]
ssl = True
ssl_ca_file = /etc/pki/tls/certs/ca-bundle.crt
[keystone_authtoken]
cafile = /etc/pki/tls/certs/ca-bundle.crt
[glance]
cafile = /etc/pki/tls/certs/ca-bundle.crt
[neutron]
cafile = /etc/pki/tls/certs/ca-bundle.crt
[cinder]
cafile = /etc/pki/tls/certs/ca-bundle.crt
Vérification TLS¶
#!/bin/bash
# verify-tls.sh
echo "=== HAProxy External ==="
echo | openssl s_client -connect cloud.example.com:443 2>/dev/null | \
openssl x509 -noout -subject -dates -issuer
echo -e "\n=== Keystone Internal ==="
echo | openssl s_client -connect 10.0.0.11:5000 2>/dev/null | \
openssl x509 -noout -subject -dates
echo -e "\n=== MariaDB ==="
docker exec mariadb mysql -e "SHOW STATUS LIKE 'Ssl_cipher';"
echo -e "\n=== RabbitMQ ==="
docker exec rabbitmq rabbitmqctl status | grep -A5 "SSL"
echo -e "\n=== Test OpenStack CLI ==="
openstack token issue
Diagramme flux TLS¶
sequenceDiagram
actor User
participant HAProxy as HAProxy<br/>(TLS Term)
participant Nova as Nova API
participant RabbitMQ
participant Compute as Nova Compute
participant Glance
participant DB as MariaDB
User->>HAProxy: HTTPS (TLS 1.3)<br/>POST /servers
HAProxy->>Nova: HTTPS (internal TLS)<br/>Client cert
Nova->>DB: TLS<br/>Store instance
Nova->>RabbitMQ: AMQPS<br/>Message
RabbitMQ->>Compute: AMQPS<br/>Schedule
Compute->>Glance: HTTPS<br/>Get image
Compute->>RabbitMQ: AMQPS<br/>Update status
RabbitMQ->>Nova: AMQPS
Nova->>DB: TLS<br/>Update
Nova-->>HAProxy: Response
HAProxy-->>User: HTTPS<br/>201 Created
Exemples pratiques¶
Let's Encrypt avec certbot¶
# Pour les certificats publics
apt install certbot
# Obtenir le certificat
certbot certonly --standalone \
-d cloud.example.com \
--agree-tos \
--email admin@example.com
# Créer le fichier HAProxy
cat /etc/letsencrypt/live/cloud.example.com/fullchain.pem \
/etc/letsencrypt/live/cloud.example.com/privkey.pem \
> /etc/kolla/certificates/haproxy.pem
# Renouvellement automatique
echo "0 3 * * * root certbot renew --quiet --post-hook 'docker restart haproxy'" \
> /etc/cron.d/certbot
Test de connexion TLS¶
# Vérifier les cipher suites
nmap --script ssl-enum-ciphers -p 443 cloud.example.com
# Vérifier le certificat
curl -v https://cloud.example.com:5000/v3 2>&1 | grep -E "(SSL|TLS|certificate)"
# Test avec openssl
openssl s_client -connect cloud.example.com:443 -tls1_3
Ressources¶
Checkpoint¶
- CA interne créée
- Certificats générés pour tous les services
- TLS external configuré (HAProxy)
- TLS internal configuré (services)
- TLS backend configuré (DB, MQ)
- Vérification TLS réussie (openssl)
- OpenStack CLI fonctionne avec TLS
- Renouvellement automatique configuré