octavia: generate certificates automatically

implemented as a separate command (kolla-ansible octavia-certificates)

Implements: blueprint implement-automatic-deploy-of-octavia

Co-Authored-By: wu.chunyang <wuchunyang@yovole.com>
Co-Authored-By: Radosław Piliszek <radoslaw.piliszek@gmail.com>

Change-Id: I2c5b26ce9e363f35c523865904a582f7960aa682
This commit is contained in:
Mark Goddard 2020-09-25 08:50:09 +01:00 committed by Radosław Piliszek
parent 6c5e9321e4
commit 894f4912ac
12 changed files with 304 additions and 42 deletions

View File

@ -0,0 +1,5 @@
---
- name: Apply role octavia-certificates
hosts: localhost
roles:
- octavia-certificates

View File

@ -0,0 +1,45 @@
---
#####################
# Certificate options.
#####################
octavia_certs_work_dir: "{{ node_config }}/octavia-certificates"
# OpenSSL configuration file path.
octavia_certs_openssl_cnf_path: openssl.cnf
# For more info see: https://en.wikipedia.org/wiki/Certificate_signing_request
# Country; The two-letter ISO code for the country where your organization is located
octavia_certs_country: US
# Province, Region, County or State
octavia_certs_state: Oregon
# Business name / Organization
octavia_certs_organization: OpenStack
# Department Name / Organizational Unit
octavia_certs_organizational_unit: Octavia
# Server CA.
octavia_certs_server_ca_expiry: 3650
octavia_certs_server_ca_country: "{{ octavia_certs_country }}"
octavia_certs_server_ca_state: "{{ octavia_certs_state }}"
octavia_certs_server_ca_organization: "{{ octavia_certs_organization }}"
octavia_certs_server_ca_organizational_unit: "{{ octavia_certs_organizational_unit }}"
octavia_certs_server_ca_common_name: server-ca.example.org
# Client CA.
octavia_certs_client_ca_expiry: 3650
octavia_certs_client_ca_country: "{{ octavia_certs_country }}"
octavia_certs_client_ca_state: "{{ octavia_certs_state }}"
octavia_certs_client_ca_organization: "{{ octavia_certs_organization }}"
octavia_certs_client_ca_organizational_unit: "{{ octavia_certs_organizational_unit }}"
octavia_certs_client_ca_common_name: client-ca.example.org
# Client certificate.
octavia_certs_client_expiry: 365
octavia_certs_client_req_country: "{{ octavia_certs_country }}"
octavia_certs_client_req_state: "{{ octavia_certs_state }}"
octavia_certs_client_req_organization: "{{ octavia_certs_organization }}"
octavia_certs_client_req_organizational_unit: "{{ octavia_certs_organizational_unit }}"
# NOTE(yoctozepto): This should ideally be per controller, i.e. controller
# generates its key&CSR and this CA signs it.
octavia_certs_client_req_common_name: client.example.org

View File

@ -0,0 +1,49 @@
[ client_ca ]
new_certs_dir = .
database = index.txt
serial = serial
RANDFILE = .rand
private_key = client_ca.key.pem
certificate = client_ca.cert.pem
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 3650
x509_extensions = client_cert
policy = policy_any
[ policy_any ]
countryName = supplied
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
[ req_distinguished_name ]
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
basicConstraints = critical, CA:TRUE
keyUsage = critical, cRLSign, keyCertSign
[ client_cert ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = clientAuth

View File

@ -0,0 +1,43 @@
---
- name: Create client_ca index.txt
copy:
content: ''
dest: "{{ octavia_certs_work_dir }}/client_ca/index.txt"
force: no
mode: 0660
- name: Create client_ca serial
copy:
content: "1000\n"
dest: "{{ octavia_certs_work_dir }}/client_ca/serial"
force: no
mode: 0660
- name: Create client_ca private key
command: >
openssl genrsa -aes256 -out client_ca.key.pem
-passout pass:{{ octavia_client_ca_password }} 4096
args:
chdir: "{{ octavia_certs_work_dir }}/client_ca"
creates: "{{ octavia_certs_work_dir }}/client_ca/client_ca.key.pem"
- name: Create client_ca certificate
vars:
client_ca_subject:
C: "{{ octavia_certs_client_ca_country }}"
ST: "{{ octavia_certs_client_ca_state }}"
O: "{{ octavia_certs_client_ca_organization }}"
OU: "{{ octavia_certs_client_ca_organizational_unit }}"
CN: "{{ octavia_certs_client_ca_common_name }}"
command: >
openssl req -new -x509 -config ../openssl.cnf
-key client_ca.key.pem
-days {{ octavia_certs_client_ca_expiry }}
-out client_ca.cert.pem
-subj "/{{ client_ca_subject.items() | map('join', '=') | join('/') }}"
-passin pass:{{ octavia_client_ca_password }}
-batch
args:
chdir: "{{ octavia_certs_work_dir }}/client_ca"
creates: "{{ octavia_certs_work_dir }}/client_ca/client_ca.cert.pem"

View File

@ -0,0 +1,50 @@
---
# NOTE(yoctozepto): This should ideally be per controller, i.e. controller
# generates its key&CSR and this CA signs it.
- name: Create a key for the client certificate
command: >
openssl genrsa -out client.key.pem 4096
args:
chdir: "{{ octavia_certs_work_dir }}/client_ca"
creates: "{{ octavia_certs_work_dir }}/client_ca/client.key.pem"
- name: Create the certificate request for the client certificate
vars:
client_req_subject:
C: "{{ octavia_certs_client_req_country }}"
ST: "{{ octavia_certs_client_req_state }}"
O: "{{ octavia_certs_client_req_organization }}"
OU: "{{ octavia_certs_client_req_organizational_unit }}"
CN: "{{ octavia_certs_client_req_common_name }}"
command: >
openssl req -new -config ../openssl.cnf
-key client.key.pem
-out client.csr.pem
-subj "/{{ client_req_subject.items() | map('join', '=') | join('/') }}"
-batch
args:
chdir: "{{ octavia_certs_work_dir }}/client_ca"
creates: "{{ octavia_certs_work_dir }}/client_ca/client.csr.pem"
- name: Sign the client certificate request
command: >
openssl ca -config ../openssl.cnf
-name client_ca
-days {{ octavia_certs_client_expiry }}
-in client.csr.pem
-out client.cert.pem
-key {{ octavia_client_ca_password }}
-notext
-batch
args:
chdir: "{{ octavia_certs_work_dir }}/client_ca"
creates: "{{ octavia_certs_work_dir }}/client_ca/client.cert.pem"
- name: Create a concatenated client certificate and key file
assemble:
regexp: ^client\.(cert|key)\.pem$
src: "{{ octavia_certs_work_dir }}/client_ca"
dest: "{{ octavia_certs_work_dir }}/client_ca/client.cert-and-key.pem"
mode: "0660"

View File

@ -0,0 +1,44 @@
---
# This play adapts https://docs.openstack.org/octavia/victoria/admin/guides/certificates.html
# Kolla-Ansible prepares the Server CA certificate and key for use by Octavia
# to generate Amphorae certificates.
# Kolla-Ansible prepares and controls the Client CA certificate and key.
# Client CA is used to generate certificates for Octavia controllers.
- name: Ensure server_ca and client_ca directories exist
file:
path: "{{ octavia_certs_work_dir }}/{{ item }}"
state: "directory"
mode: 0770
loop:
- server_ca
- client_ca
- name: Copy openssl.cnf
copy:
src: "{{ octavia_certs_openssl_cnf_path }}"
dest: "{{ octavia_certs_work_dir }}/openssl.cnf"
- import_tasks: server_ca.yml
- import_tasks: client_ca.yml
- import_tasks: client_cert.yml
- name: Ensure {{ node_custom_config }}/octavia directory exists
file:
path: "{{ node_custom_config }}/octavia"
state: "directory"
mode: 0770
- name: Copy the to-be-deployed keys and certs to {{ node_custom_config }}/octavia
copy:
src: "{{ octavia_certs_work_dir }}/{{ item.src }}"
dest: "{{ node_custom_config }}/octavia/{{ item.dest }}"
with_items:
- { src: "server_ca/server_ca.cert.pem", dest: "server_ca.cert.pem" }
- { src: "server_ca/server_ca.key.pem", dest: "server_ca.key.pem" }
- { src: "client_ca/client_ca.cert.pem", dest: "client_ca.cert.pem" }
- { src: "client_ca/client.cert-and-key.pem", dest: "client.cert-and-key.pem" }

View File

@ -0,0 +1,29 @@
---
- name: Generate server_ca private key
command: >
openssl genrsa -aes256 -out server_ca.key.pem
-passout pass:{{ octavia_ca_password }} 4096
args:
chdir: "{{ octavia_certs_work_dir }}/server_ca"
creates: "{{ octavia_certs_work_dir }}/server_ca/server_ca.key.pem"
- name: Create server_ca certificate
vars:
server_ca_subject:
C: "{{ octavia_certs_server_ca_country }}"
ST: "{{ octavia_certs_server_ca_state }}"
O: "{{ octavia_certs_server_ca_organization }}"
OU: "{{ octavia_certs_server_ca_organizational_unit }}"
CN: "{{ octavia_certs_server_ca_common_name }}"
command: >
openssl req -new -x509 -config ../openssl.cnf
-key server_ca.key.pem
-days {{ octavia_certs_server_ca_expiry }}
-out server_ca.cert.pem
-subj "/{{ server_ca_subject.items() | map('join', '=') | join('/') }}"
-passin pass:{{ octavia_ca_password }}
-batch
args:
chdir: "{{ octavia_certs_work_dir }}/server_ca"
creates: "{{ octavia_certs_work_dir }}/server_ca/server_ca.cert.pem"

View File

@ -170,6 +170,7 @@ manila_keystone_password:
octavia_database_password:
octavia_keystone_password:
octavia_ca_password:
octavia_client_ca_password:
searchlight_keystone_password:

View File

@ -16,6 +16,7 @@ function check_config {
for f in $(sudo find /etc/kolla \
-not -regex /etc/kolla/config.* \
-not -regex /etc/kolla/certificates.* \
-not -regex /etc/kolla/octavia-certificates.* \
-not -regex .*pem \
-not -regex .*key \
-not -regex ".*ca-certificates.*" \

View File

@ -194,24 +194,6 @@
dest: ironic-agent.kernel
when: scenario == "ironic"
- block:
- name: ensure octavia config directory exists
file:
path: /etc/kolla/config/octavia
state: directory
mode: 0777
- name: create dummy TLS certificates for octavia
file:
path: "/etc/kolla/config/octavia/{{ item }}"
state: touch
with_items:
- client.cert-and-key.pem
- client_ca.cert.pem
- server_ca.cert.pem
- server_ca.key.pem
when: scenario == 'magnum'
- name: ensure /etc/ansible exists
file:
path: /etc/ansible
@ -282,6 +264,13 @@
docker_image_tag: "{{ build_image_tag if need_build_image else (zuul.branch | basename) ~ docker_image_tag_suffix }}"
docker_image_prefix: "{{ 'primary:4000/lokolla/' if need_build_image else 'kolla/' }}"
# NOTE(yoctozepto): k-a octavia-certificates should run before k-a bootstrap-servers
# because the latter hijacks /etc/kolla permissions (due to same directory on the
# same host being used by both)
- name: create TLS certificates for octavia
command: kolla-ansible octavia-certificates
when: scenario == 'magnum'
# NOTE(mgoddard): We are using the script module here and later to ensure
# we use the local copy of these scripts, rather than the one on the remote
# host, which could be checked out to a previous release (in an upgrade

View File

@ -140,6 +140,7 @@ Commands:
reconfigure Reconfigure OpenStack service
stop Stop Kolla containers
certificates Generate self-signed certificate for TLS *For Development Only*
octavia-certificates Generate certificates for octavia deployment
upgrade Upgrades existing OpenStack Environment
upgrade-bifrost Upgrades an existing bifrost container
genconfig Generate configuration files for enabled OpenStack services
@ -179,6 +180,7 @@ pull
reconfigure
stop
certificates
octavia-certificates
upgrade
upgrade-bifrost
genconfig
@ -431,6 +433,10 @@ EOF
ACTION="Generate TLS Certificates"
PLAYBOOK="${BASEDIR}/ansible/certificates.yml"
;;
(octavia-certificates)
ACTION="Generate octavia Certificates"
PLAYBOOK="${BASEDIR}/ansible/octavia-certificates.yml"
;;
(genconfig)
ACTION="Generate configuration files for enabled OpenStack services"
EXTRA_OPTS="$EXTRA_OPTS -e kolla_action=config"

View File

@ -131,7 +131,7 @@
parent: kolla-ansible-base
voting: false
files:
- ^ansible/roles/(designate|magnum|octavia)/
- ^ansible/roles/(designate|magnum|octavia|octavia-certificates)/
- ^tests/test-dashboard.sh
- ^tests/test-magnum.sh
vars: