/home/zuul/src/opendev.org/openstack/openstack-ansible-os_octavia/tasks/octavia_certs.yml
---
# Copyright 2018, Rackspace US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# We set the python interpreter to the ansible runtime venv if
# the delegation is to localhost so that we get access to the
# appropriate python libraries in that venv. If the delegation
# is to another host, we assume that it is accessible by the
# system python instead.
- name: Prepare octavia_cert_setup_host for certificate generation
  delegate_to: "{{ octavia_cert_setup_host }}"
  vars:
    ansible_python_interpreter: >-
      {{ (octavia_cert_setup_host == 'localhost') | ternary(ansible_playbook_python, ansible_python['executable']) }}
  block:
    - name: Create certificate directories
      file:
        path: "{{ item.path }}"
        state: directory
        mode: "{{ item.mode }}"
        owner: "{{ octavia_cert_dir_owner }}"
      with_items:
        - { path: "{{ octavia_cert_dir }}", mode: '0750' }
        - { path: "{{ octavia_cert_dir }}/newcerts", mode: '0750'}
        - { path: "{{ octavia_cert_dir }}/private", mode: '0750'}

    # ansible's openssl_certificate can't create X509 extensions
    # but you need CA: true in Basic Constraints to have a CA cert

    # set up openssl for use
    - name: Touch index.txt
      file:
        path: "{{ octavia_cert_dir }}/index.txt"
        state: touch
        mode: 0755

    - name: Init serial
      copy:
        content: "01"
        dest: "{{ octavia_cert_dir }}/serial"
        force: no

    - name: Generate openssl.conf
      template:
        src: "templates/openssl.conf.j2"
        dest:  "{{ octavia_cert_dir }}/openssl.cnf"
        mode: 0440

    - name: Create the server CA private key
      openssl_privatekey:
        path: "{{ octavia_ca_private_key }}"
        passphrase: "{{ octavia_ca_private_key_passphrase }}"
        cipher: "{{ octavia_cert_cipher_server }}"
        size: "{{ octavia_cert_key_length_server }}"

    - name: Create server CA certificate
      command: >
        openssl req -x509 -passin pass:'{{ octavia_ca_private_key_passphrase }}' -new -nodes -key {{ octavia_ca_private_key }}  \
        -config {{ octavia_cert_dir }}/openssl.cnf \
        -subj "{{ octavia_cert_server_ca_subject }}" \
        -days {{ octavia_cert_validity_days }} \
        -out {{ octavia_ca_certificate }}
      args:
        chdir: "{{ octavia_cert_dir }}"
        creates: "{{ octavia_ca_certificate }}"

    - name: Store octavia ca private key
      slurp:
        src: "{{ octavia_ca_private_key }}"
      register: _octavia_ca_private_key
      changed_when: false

    - name: Store octavia ca cert
      slurp:
        src: "{{ octavia_ca_certificate }}"
      register: _octavia_ca_certificate
      changed_when: false

    # same as octavia ca cert
    - name: Store octavia server ca
      slurp:
        src: "{{ octavia_server_ca }}"
      register: _octavia_server_ca
      changed_when: false

# These are run at the very first installation of Octavia
# While Octavia acts as a CA for the server certificates,
# for the amphora it only needs a client certificate and
# the (public) certificate authority certificate.
# Generating the secret key here and storing it
# on the deploy host allows us to rotate the client
# certificate without recycling the amphora since
# we can keep the same CA.

- name: Generate keys/certificates on octavia_cert_setup_host
  delegate_to: "{{ octavia_cert_setup_host }}"
  vars:
    ansible_python_interpreter: >-
      {{ (octavia_cert_setup_host == 'localhost') | ternary(ansible_playbook_python, ansible_python['executable']) }}
  when: octavia_generate_client_cert | bool
  block:
    - name: Create the client CAs private key
      openssl_privatekey:
        path: "{{ octavia_client_ca_key }}"
        passphrase: "{{ octavia_cert_client_password }}"
        cipher: "{{ octavia_cert_cipher_client }}"
        size: "{{ octavia_cert_key_length_client }}"

    - name: Create client CA certificate
      command: >
        openssl req -x509 -passin pass:'{{ octavia_cert_client_password }}' -new -nodes -key {{ octavia_client_ca_key }}  \
        -config {{ octavia_cert_dir }}/openssl.cnf \
        -subj "{{ octavia_cert_client_ca_subject }}" \
        -days {{ octavia_cert_validity_days }} \
        -out {{ octavia_client_ca }}
      args:
        chdir: "{{ octavia_cert_dir }}"
        creates: "{{ octavia_client_ca }}"

    - name: Create the client cert private key
      openssl_privatekey:
        path: "{{ octavia_cert_dir }}/client.key"
        size: "{{ octavia_cert_key_length_client }}"

    - name: Create client cert CSR
      openssl_csr:
        path: "{{ octavia_cert_dir }}/client.csr"
        common_name: "{{ octavia_cert_client_req_common_name }}"
        country_name: "{{ octavia_cert_client_req_country_name }}"
        state_or_province_name: "{{ octavia_cert_client_req_state_or_province_name }}"
        locality_name: "{{ octavia_cert_client_req_locality_name }}"
        organization_name: "{{ octavia_cert_client_req_organization_name }}"
        privatekey_path: "{{ octavia_cert_dir }}/client.key"

    - name: Create client certificate
      command: >
        openssl ca -passin pass:'{{ octavia_ca_private_key_passphrase }}' -config {{ octavia_cert_dir }}/openssl.cnf \
        -in client.csr -days {{ octavia_cert_validity_days }} -out client-.pem -batch
      args:
        chdir: "{{ octavia_cert_dir }}"
        creates: "{{ octavia_cert_dir }}/client-.pem"

    # use cat to avoid mangling the certs
    - name: Generate single pem client.pem
      shell: "cat client-.pem client.key >{{ octavia_client_cert }}"
      args:
        chdir: "{{ octavia_cert_dir }}"
        creates: "{{ octavia_client_cert }}"
      tags:
          - skip_ansible_lint

    - name: Store octavia client ca
      slurp:
        src: "{{ octavia_client_ca }}"
      register: _octavia_client_ca
      changed_when: false

    - name: Store octavia client cert
      slurp:
        src: "{{ octavia_client_cert }}"
      register: _octavia_client_cert
      changed_when: false