diff --git a/ansible/roles/octavia_preconf/README.md b/ansible/roles/octavia_preconf/README.md new file mode 100644 index 00000000..93a71673 --- /dev/null +++ b/ansible/roles/octavia_preconf/README.md @@ -0,0 +1,85 @@ +## octavia_preconf + +This is a role for performing the pre-requisite tasks to enable amphora provider for octavia for example creating a network and a subnet for amphorae, uploading the image to glance, creating ssh keys etc. This is mainly intended for running octavia in a k8s environment + +## Requirements + +These are the requirements to run this role: +1. This role needs to be run on any of the nodes which has access to the openstack public endpoints +2. This role also needs access to the k8s cluster as it tries to create "octavia-certs" secret in the openstack namespace to it needs access to kubectl utility +3. It is recommended to create a virtual environment to run this role; steps will be shared below + +## Creating a virtual environment + +1. Install the required packages for creating the virutal environment with python: +``` +root@saturn-c1:~# apt-get install python3-venv python3-pip +``` +2. Create the virtual environment: +``` +root@saturn-c1:~# mkdir -p ~/.venvs +root@saturn-c1:~# python3 -m venv --system-site-packages ~/.venvs/octavia_preconf +``` +3. Install the required dependencies for the virtual environment: +``` +root@saturn-c1:~# source .venvs/octavia_preconf/bin/activate +(octavia_preconf) root@saturn-c1:~# +(octavia_preconf) root@saturn-c1:~# pip install --upgrade pip +(octavia_preconf) root@saturn-c1:~# pip install "ansible>=2.9" "openstacksdk>=1.0.0" "python-openstackclient==6.2.0" +``` +4. Download the kubectl binary and copy the kubeconfig from one of the k8s master nodes: +``` +(octavia_preconf) root@saturn-c1:~# curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +(octavia_preconf) root@saturn-c1:~# install -o root -g root -m 0755 kubectl /root/.venvs/octavia_preconf/bin +(octavia_preconf) root@saturn-c1:~# mkdir ~/.kube +(octavia_preconf) root@saturn-c1:~# mv config ~/.kube/ +``` +note that the kubeconfig in this step has been copied from the master node and it should be modified accordingly + +## Role Variables + ++ The available variables can be found in the defaults/main.yml file ++ The role variables can be used to modify + + quota values for cores, RAM, security groups, security group rules, server groups and others + + lb-mgmt-subet cidr, pool and gateway + + whether ssh should be enabled for amphora + + Validity and other certificate parameters \ +Refer to the role defaults for more detail + +## Dependencies + +The role has no external dependencies; only the steps shared above for creating the virutal environment are required + +## Example Playbook + +The role needs keystone admin credentials; they can be provided as environment variables \ +This is an example playbook for running the role: + +``` +(octavia_preconf) root@saturn-c1:~# cat octavia-preconf-main.yaml + +- name: Pre-requisites for enabling amphora provider in octavia + hosts: localhost + environment: + OS_ENDPOINT_TYPE: publicURL + OS_INTERFACE: publicURL + OS_USERNAME: 'admin' + OS_PASSWORD: 'XXXXX' + OS_PROJECT_NAME: 'admin' + OS_TENANT_NAME: 'admin' + OS_AUTH_TYPE: password + OS_AUTH_URL: 'https://keystone.lab.local/v3' + OS_USER_DOMAIN_NAME: 'default' + OS_PROJECT_DOMAIN_NAME: 'default' + OS_REGION_NAME: 'RegionOne' + OS_IDENTITY_API_VERSION: 3 + OS_AUTH_VERSION: 3 + NOVA_ENDPOINT_TYPE: publicURL + roles: + - /root/octavia_preconf +``` + +These are the required environment variables for the role; must be modified accordingly. The keystone admin password can be obtained from k8s as below: +``` +(octavia_preconf) root@saturn-c1:~# kubectl get secret -n openstack keystone-admin -o jsonpath='{ .data.password }' | base64 -d +``` diff --git a/ansible/roles/octavia_preconf/defaults/main.yml b/ansible/roles/octavia_preconf/defaults/main.yml new file mode 100644 index 00000000..ff2e3b6b --- /dev/null +++ b/ansible/roles/octavia_preconf/defaults/main.yml @@ -0,0 +1,76 @@ +--- +# defaults file for octavia_preconf + +# these are the defaults for quotas +num_amphora_instances: 100 +num_amphora_cores: "{{ (num_amphora_instances|int)*2 }}" # 2 cores for each amphora +amphora_ram_mb: "{{ (num_amphora_instances|int)*1024 }}" # 1G of ram for each amphora +num_amphora_ports: "{{ (num_amphora_instances|int)*5 }}" # considering 5 ports for amphora instance +octavia_num_secgroup: "{{ (num_amphora_instances|int)*1.5|int|abs }}" # average 3 listeners per lb +octavia_num_secgroup_rule: "{{ (octavia_num_secgroup|int)*5 }}" # considering 5 secgroup rules per sec group +num_amphora_server_groups: "{{ ((num_amphora_instances|int)*0.5)|int|abs }}" # 2 amphora per group for active/passive +num_amphora_server_group_member: 20 + +# these are the defaults for lb-mgmt-net +#lb_mgmt_net_physical_net +lb_mgmt_net_type: geneve +#lb_mgmt_net_segmentation_id + +# these are the defaults for lb-mgmt-subnet +lb_mgmt_subnet_cidr: '172.16.29.0/24' +lb_mgmt_subnet_pool_start: '172.16.29.2' +lb_mgmt_subnet_pool_end: '172.16.29.254' +lb_mgmt_subnet_gateway: '172.16.29.1' + +# these are the defaults for security groups +amphora_icmp_enabled: true +amphora_ssh_enabled: true +lb_health_mgr_secgrp_name: "lb-health-mgr-secgroup" +lb_mgmt_secgrp_name: "lb-mgmt-secgroup" + +# these are the defaults for the flavor, image and ssh keypair +amphora_ssh_key_name: amphora-ssh-key +amphora_flavor_name: m1.amphora +amphora_image_version: focal +amphora_image_name: amphora-ubuntu-{{ amphora_image_version }} + +# these are the defaults for certs +octavia_create_certs: true +octavia_certs_dir: "{{ lookup('env', 'HOME') }}/octavia_certs" +octavia_cert_key_bits: 4096 +octavia_key_type: "RSA" +octavia_key_passwd: 'not-secure-passphrase' + +octavia_server_ca_subj: '/C=US/ST=Texas/L=Local/O=RPC/OU=Racklab/CN=ServerRootCA' +octavia_client_ca_subj: '/C=US/ST=Texas/L=Local/O=RPC/OU=Racklab/CN=ClientRootCA' + +# CA and client cert and key files +octavia_serverca_privkey: "{{ octavia_certs_dir }}/private/serverca.key.pem" +octavia_server_ca_cert: "{{ octavia_certs_dir }}/certs/serverca.cert.pem" + +octavia_clientca_privkey: "{{ octavia_certs_dir }}/private/clientca.key.pem" +octavia_client_ca_cert: "{{ octavia_certs_dir }}/certs/clientca.cert.pem" + +octavia_client_csr: "{{ octavia_certs_dir }}/csr/client.csr.pem" +octavia_client_key: "{{ octavia_certs_dir }}/private/client.key.pem" +octavia_client_cert: "{{ octavia_certs_dir }}/certs/client.cert.pem" +octavia_client_key_cert: "{{ octavia_certs_dir }}/private/client-key-and-cert.pem" + +octavia_ca_valid_period: 1096 # about 3 years + + +octavia_client_key_bits: 2048 + +# Client CSR +client_cert_CN: "OctaviaController" +client_cert_CT: "US" +client_cert_ST: "Texas" +client_cert_LN: "Local" +client_cert_ORG: "RPC" +client_cert_OU: "Rackerlab" + +# Client cert validity (in days) +octavia_client_cert_valid_period: 1096 + +# octavia helm values for amphora provider +octavia_helm_values_file: "{{ lookup('env', 'HOME') }}/octavia_amphora_provider.yaml" diff --git a/ansible/roles/octavia_preconf/files/create_health_mgr_ports.sh b/ansible/roles/octavia_preconf/files/create_health_mgr_ports.sh new file mode 100755 index 00000000..b8e3ad64 --- /dev/null +++ b/ansible/roles/octavia_preconf/files/create_health_mgr_ports.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# We need to create the ports with shell scripts +# the ansible module currently doesn't provide +# --host argument + +set -xe + +# Obtain the network_id and secgroup_id from and +# cloud name from ansible task +NET_ID=$1 +SECGRP_ID=$2 +CLOUD_NAME=$3 + +export OS_CLOUD=$CLOUD_NAME + +# Obtain the list of kubernetes nodes with +# "openstack-control-plane=enabled" label +CONTROLLER_IP_PORT_LIST='' +CTRLS=$(kubectl get nodes -l openstack-control-plane=enabled -o name | awk -F"/" '{print $2}') +for node in $CTRLS +do + node_short=$(echo "$node" | awk -F"." '{print $1}') + PORTNAME=octavia-health-manager-port-$node_short + PORT_ID=$(openstack port create "$PORTNAME" --security-group "$SECGRP_ID" --device-owner Octavia:health-mgr --host="$node" -c id -f value --network "$NET_ID") + IP=$(openstack port show "$PORT_ID" -c fixed_ips -f yaml | grep ip_address | awk -F':' '{print $2}') + if [ -z "$CONTROLLER_IP_PORT_LIST" ]; then + CONTROLLER_IP_PORT_LIST=$IP:5555 + else + CONTROLLER_IP_PORT_LIST=$CONTROLLER_IP_PORT_LIST,$IP:5555 + fi +done + +echo "$CONTROLLER_IP_PORT_LIST" > /tmp/octavia_hm_controller_ip_port_list diff --git a/ansible/roles/octavia_preconf/files/create_k8s_secret.sh b/ansible/roles/octavia_preconf/files/create_k8s_secret.sh new file mode 100755 index 00000000..2af0b580 --- /dev/null +++ b/ansible/roles/octavia_preconf/files/create_k8s_secret.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# obtain the values for cert and keys +# from the ansible tasks; these will +# then be used to create the secret + +set -xe + +SERVER_CA=$1 +SERVER_CA_KEY=$2 +CLIENT_CA=$3 +CLIENT_KEY_CERT=$4 + +function encod_base64() +{ + local file_path=$1 + # shellcheck disable=SC2002 + cat "$file_path" | base64 -w0 | tr -d '\n' +} + +cat < /tmp/k8s_secret.yml +--- +apiVersion: v1 +kind: Secret +metadata: + name: octavia-certs + namespace: openstack +type: Opaque +data: + server_ca.cert.pem: $(encod_base64 "$SERVER_CA") + server_ca.key.pem: $(encod_base64 "$SERVER_CA_KEY") + client_ca.cert.pem: $(encod_base64 "$CLIENT_CA") + client.key-and-cert.pem: $(encod_base64 "$CLIENT_KEY_CERT") +EOF diff --git a/ansible/roles/octavia_preconf/handlers/main.yml b/ansible/roles/octavia_preconf/handlers/main.yml new file mode 100644 index 00000000..38c82a94 --- /dev/null +++ b/ansible/roles/octavia_preconf/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for octavia_preconf diff --git a/ansible/roles/octavia_preconf/meta/main.yml b/ansible/roles/octavia_preconf/meta/main.yml new file mode 100644 index 00000000..b75c6b77 --- /dev/null +++ b/ansible/roles/octavia_preconf/meta/main.yml @@ -0,0 +1,16 @@ +galaxy_info: + author: Punit Shankar Kundal + description: Create the required pre-requisite resources for enabling amphora provider in Octavia + company: Rackspace Technology + license: Apache-2.0 + min_ansible_version: 2.15.8 + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. diff --git a/ansible/roles/octavia_preconf/tasks/admin_tenant_quota_setup.yml b/ansible/roles/octavia_preconf/tasks/admin_tenant_quota_setup.yml new file mode 100644 index 00000000..ec92577b --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/admin_tenant_quota_setup.yml @@ -0,0 +1,43 @@ +--- +# This tasks modifies the quotas for the admin tenant to +# more suitable defaults as the resources for amphora +# provider are created in the "admin" tenant by default +- name: Fetch the current quotas for the admin tenant + openstack.cloud.quota: + name: "{{ admin_project_name | default('admin') }}" + interface: public + register: _current_admin_quota + +- name: Display the current quota values for the admin tenant + debug: + msg: + - "Current quota value for instances: {{ _current_admin_quota.quotas.compute.instances }}; New quota value for instances: {{ num_amphora_instances }}" + - "Current quota value for cores: {{ _current_admin_quota.quotas.compute.cores }}; New quota value for cores: {{ num_amphora_cores }}" + - "Current quota value for server_groups: {{ _current_admin_quota.quotas.compute.server_groups }}; New quota value for server_groups: {{ num_amphora_server_groups }}" + - "Current quota value for ram(MB): {{ _current_admin_quota.quotas.compute.ram }}; New quota value for ram(MB): {{ amphora_ram_mb }}" + - "Current quota value for ports: {{ _current_admin_quota.quotas.network.ports }}; New quota value for ports: {{ num_amphora_ports }}" + - "Current quota value for security_groups: {{ _current_admin_quota.quotas.network.security_groups }}; New quota value for security_groups: {{ octavia_num_secgroup }}" + - "Current quota value for security_group_rules: {{ _current_admin_quota.quotas.network.security_group_rules }}; New quota value for security_group_rules: {{ octavia_num_secgroup_rule }}" + - "Current quota value for server_group_members: {{ _current_admin_quota.quotas.compute.server_group_members }}; New quota value for server_group_members: {{ num_amphora_server_group_member }}" + +- name: modify the quotas for the admin tenant to suit to production environments + block: + - openstack.cloud.quota: + name: "{{ admin_project_name | default('admin') }}" + instances: "{{ num_amphora_instances }}" + cores: "{{ num_amphora_cores }}" + server_groups: "{{ num_amphora_server_groups }}" + ram: "{{ amphora_ram_mb }}" + port: "{{ num_amphora_ports }}" + security_group: "{{ octavia_num_secgroup }}" + security_group_rule: "{{ octavia_num_secgroup_rule }}" + server_group_members: "{{ num_amphora_server_group_member }}" + interface: public + rescue: + - debug: + msg: "WARNING: Failed to modify admin tenant quotas! trying to reset to defaults; please review the ansible error logs" + - name: set the quota to defaults for the admin tenant + openstack.cloud.quota: + state: absent + name: "{{ admin_project_name | default('admin') }}" + interface: public diff --git a/ansible/roles/octavia_preconf/tasks/main.yml b/ansible/roles/octavia_preconf/tasks/main.yml new file mode 100644 index 00000000..05ba0195 --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/main.yml @@ -0,0 +1,44 @@ +--- +# tasks file for octavia_preconf + +- name: import tasks for modifying the admin tenant quotas + import_tasks: admin_tenant_quota_setup.yml + tags: + - always + +- name: import tasks to create lb-mgmt-net for amphora + import_tasks: octavia_lb_net_setup.yml + tags: + - always + +- name: import tasks to create sec groups for amphora and health manager ports + import_tasks: octavia_sec_group.yml + tags: + - always + +- name: import tasks to create health mgr ports + import_tasks: octavia_health_mgr_ports.yml + tags: + - always + +- name: import tasks to create amphora image, flavor and ssh keypair + import_tasks: octavia_amphora_keypair_image_flavor.yml + tags: + - always + +- name: import tasks to create cert directories + import_tasks: octavia_cert_dir_setup.yml + tags: + - octavia_certs + when: octavia_create_certs + +- name: import tasks to create octavia certs + import_tasks: octavia_cert.yml + tags: + - octavia_certs + when: octavia_create_certs + +- name: import tasks to create helm values file for amphora provider + import_tasks: octavia_helm_amphorae_values.yml + tags: + - always diff --git a/ansible/roles/octavia_preconf/tasks/octavia_amphora_keypair_image_flavor.yml b/ansible/roles/octavia_preconf/tasks/octavia_amphora_keypair_image_flavor.yml new file mode 100644 index 00000000..2dfcdaa1 --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/octavia_amphora_keypair_image_flavor.yml @@ -0,0 +1,60 @@ +--- +# These are the tasks to create ssh keypair, flavor +# and image for the amphorae; creating keypair is +# optional; amphora image uploaded is suitable for +# test environments; the flavor specs are currently set +# to 2 vcpus, 1024 MB ram and 5 GB of disk +- name: Create ssh keypair for amphorae + community.crypto.openssh_keypair: + path: /tmp/amphora_ssh_key + size: 2048 + comment: "amphora ssh key" + when: amphora_ssh_enabled + +- name: Create ssh keypair in nova for amphora + openstack.cloud.keypair: + name: "{{ amphora_ssh_key_name }}" + public_key: "{{ lookup('file', '/tmp/amphora_ssh_key.pub') }}" + state: present + interface: public + when: amphora_ssh_enabled + +- name: Create flavor for amphorae + openstack.cloud.compute_flavor: + name: "{{ amphora_flavor_name }}" + state: present + disk: 5 + vcpus: 2 + ram: 1024 + is_public: false + interface: public + register: create_amphora_flavor + until: create_amphora_flavor is success + retries: 5 + delay: 5 + +- name: Get the image for amphora + get_url: + url: https://tarballs.opendev.org/openstack/octavia/test-images/test-only-amphora-x64-haproxy-ubuntu-{{ amphora_image_version }}.qcow2 + dest: /tmp/test-only-amphora-x64-haproxy-ubuntu-{{ amphora_image_version }}.qcow2 + register: download_amphora_image + until: download_amphora_image is success + retries: 5 + delay: 5 + +- name: push the amphora image + openstack.cloud.image: + name: "{{ amphora_image_name }}" + state: present + filename: /tmp/test-only-amphora-x64-haproxy-ubuntu-{{ amphora_image_version }}.qcow2 + container_format: bare + disk_format: qcow2 + visibility: public + protected: true + tags: + - amphora + interface: public + register: push_amphora_image + until: push_amphora_image is success + retries: 5 + delay: 5 diff --git a/ansible/roles/octavia_preconf/tasks/octavia_cert.yml b/ansible/roles/octavia_preconf/tasks/octavia_cert.yml new file mode 100644 index 00000000..9041f7f0 --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/octavia_cert.yml @@ -0,0 +1,133 @@ +--- +# These are the tasks to generate the required +# certificates and keys for octavia amphora and +# octavia worker processes; there are 2 different +# CA(s) for which certificates and keys will be +# created; client CA will be used to generate client +# certificate + +- name: Check if Kubernetes Secret certificate passphrase exists + k8s_info: + kind: Secret + namespace: openstack + name: octavia-certificates + register: secret_check + failed_when: false # Prevent failure if the secret doesn't exist + +- name: Set flag if secret exists + set_fact: + secret_exists: "{{ secret_check.resources | length > 0 }}" + +- name: Create secret if it does not exist + k8s: + state: present + definition: + apiVersion: v1 + kind: Secret + metadata: + name: octavia-certificates + namespace: openstack + data: + password: "{{ octavia_key_passwd | b64encode }}" + when: not secret_exists + +- name: Register the passphrase to be used with certs + k8s_info: + kind: Secret + namespace: openstack + name: octavia-certificates + register: cert_secret + +- name: Extract and decode the secret + set_fact: + certificate_passphrase: "{{ cert_secret.resources[0].data | dict2items | map(attribute='value') | map('b64decode') | first }}" + when: cert_secret.resources is defined + +- name: Generate Server CA private key + community.crypto.openssl_privatekey: + path: "{{ octavia_serverca_privkey }}" + state: present + passphrase: "{{ certificate_passphrase }}" + size: "{{ octavia_cert_key_bits }}" + cipher: "auto" + type: "{{ octavia_key_type }}" + +- name: Create Server CA cert + shell: > + openssl req -config {{ octavia_certs_dir }}/openssl.cnf -key {{ octavia_serverca_privkey }} \ + -new -x509 -days {{ octavia_ca_valid_period }} -sha256 -extensions v3_ca \ + -subj "{{ octavia_server_ca_subj }}" -out {{ octavia_server_ca_cert }} \ + -passin pass:'{{ certificate_passphrase }}' + args: + chdir: "{{ octavia_certs_dir }}" + creates: "{{ octavia_server_ca_cert }}" + tags: + - skip_ansible_lint + +- name: Generate Client CA private key + community.crypto.openssl_privatekey: + path: "{{ octavia_clientca_privkey }}" + state: present + passphrase: "{{ certificate_passphrase }}" + size: "{{ octavia_cert_key_bits }}" + cipher: "auto" + type: "{{ octavia_key_type }}" + +- name: Create Client CA cert + shell: > + openssl req -config {{ octavia_certs_dir }}/openssl.cnf -key {{ octavia_clientca_privkey }} \ + -new -x509 -days {{ octavia_ca_valid_period }} -sha256 -extensions v3_ca \ + -subj "{{ octavia_client_ca_subj }}" -out {{ octavia_client_ca_cert }} \ + -passin pass:'{{ certificate_passphrase }}' + args: + chdir: "{{ octavia_certs_dir }}" + creates: "{{ octavia_client_ca_cert }}" + tags: + - skip_ansible_lint + +- name: Create Client key (octavia-worker) + community.crypto.openssl_privatekey: + path: "{{ octavia_client_key }}" + state: present + size: "{{ octavia_client_key_bits }}" + type: "{{ octavia_key_type }}" + +- name: Create CSR for client cert + community.crypto.openssl_csr: + common_name: "{{ client_cert_CN }}" + country_name: "{{ client_cert_CT }}" + state_or_province_name: "{{ client_cert_ST }}" + locality_name: "{{ client_cert_LN }}" + organization_name: "{{ client_cert_ORG }}" + organizational_unit_name: "{{ client_cert_OU }}" + privatekey_path: "{{ octavia_client_key }}" + path: "{{ octavia_client_csr }}" + +- name: Create Client cert (octavia-worker) + shell: > + openssl ca -passin pass:'{{ certificate_passphrase }}' -config {{ octavia_certs_dir }}/openssl.cnf -extensions usr_cert \ + -notext -md sha256 -days {{ octavia_client_cert_valid_period }} -in {{ octavia_client_csr }} \ + -out {{ octavia_client_cert }} -batch + args: + chdir: "{{ octavia_certs_dir }}" + creates: "{{ octavia_client_cert }}" + tags: + - skip_ansible_lint + +- name: Create a single file for client key and cert (octavia-worker) + shell: + cmd: cat "{{ octavia_client_key }}" "{{ octavia_client_cert }}" > "{{ octavia_client_key_cert }}" + chdir: "{{ octavia_certs_dir }}" + creates: "{{ octavia_client_key_cert }}" + tags: + - skip_ansible_lint + +- name: Run the script to create the yaml file for octavia-certs secret + script: + cmd: create_k8s_secret.sh {{ octavia_server_ca_cert }} {{ octavia_serverca_privkey }} {{ octavia_client_ca_cert }} {{ octavia_client_key_cert }} + creates: /tmp/k8s_secret.yml + +- name: Create the "octavia-certs" secret in the openstack namespace + k8s: + state: present + src: /tmp/k8s_secret.yml diff --git a/ansible/roles/octavia_preconf/tasks/octavia_cert_dir_setup.yml b/ansible/roles/octavia_preconf/tasks/octavia_cert_dir_setup.yml new file mode 100644 index 00000000..53400917 --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/octavia_cert_dir_setup.yml @@ -0,0 +1,42 @@ +--- +# These are the tasks to create the certificate +# directories for client and server certificates +# as well other required files for openssl certs +- name: Create the base directory for octavia certs + file: + path: "{{ octavia_certs_dir }}" + state: directory + mode: '0700' + +- name: Create the sub directories for certificates + file: + path: "{{ octavia_certs_dir }}/{{ item }}" + state: directory + loop: + - certs + - crl + - csr + - newcerts + - private + +- name: Set the permissions on private key directory + file: + path: "{{ octavia_certs_dir }}/private" + state: directory + mode: '0700' + +- name: Create index file for the client and server certs + file: + path: "{{ octavia_certs_dir }}/index.txt" + state: touch + +- name: Create serial files for the client and server certs + copy: + content: '1000' + dest: "{{ octavia_certs_dir }}/serial" + +- name: Create openssl.cnf file required for the certificates + template: + src: "templates/openssl.cnf.j2" + dest: "{{ octavia_certs_dir }}/openssl.cnf" + mode: '0440' diff --git a/ansible/roles/octavia_preconf/tasks/octavia_health_mgr_ports.yml b/ansible/roles/octavia_preconf/tasks/octavia_health_mgr_ports.yml new file mode 100644 index 00000000..240cb5ae --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/octavia_health_mgr_ports.yml @@ -0,0 +1,22 @@ +--- +# These are the tasks for creating health_mgr +# ports for octavia; the ports are created with +# a shell script as the ansible modules currently +# don't support all the required params for creating +# ports +- name: Obtain the UUID of the lb-mgmt-net + openstack.cloud.networks_info: + name: lb-mgmt-net + interface: public + register: lb_mgmt_info + +- name: Obtain the UUID of the health_mgr secgroup + openstack.cloud.security_group_info: + name: "{{ lb_health_mgr_secgrp_name }}" + interface: public + register: lb_health_mgr_secgrp_info + +- name: run the shell script to create health_mgr ports if required + script: + cmd: create_health_mgr_ports.sh {{ lb_mgmt_info.networks[0].id }} {{ lb_health_mgr_secgrp_info.security_groups[0].id }} {{ lookup('env', 'OS_CLOUD') | default('openstack_helm') }} + creates: /tmp/octavia_hm_controller_ip_port_list diff --git a/ansible/roles/octavia_preconf/tasks/octavia_helm_amphorae_values.yml b/ansible/roles/octavia_preconf/tasks/octavia_helm_amphorae_values.yml new file mode 100644 index 00000000..c05d55a7 --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/octavia_helm_amphorae_values.yml @@ -0,0 +1,49 @@ +--- +# These are the tasks to generate the helm +# values file for the amphorae provider; this +# includes generating the values required for +# volume mounts for the octavia pods; values +# for amphorae network, security group, flavor +# ssh keys +- name: Obtain the owner id of the amphora image + openstack.cloud.image_info: + image: "{{ amphora_image_name }}" + interface: public + register: _amphora_image_info + +- name: Obtain the UUID of the amphora sec group + openstack.cloud.security_group_info: + name: "{{ lb_mgmt_secgrp_name }}" + interface: public + register: _amphora_sec_group_info + +- name: Obtain the UUID of the amphora flavor + openstack.cloud.compute_flavor_info: + name: "{{ amphora_flavor_name }}" + interface: public + register: _amphora_flavor_info + +- name: Obtain the UUID of lb-mgmt-net + openstack.cloud.networks_info: + name: lb-mgmt-net + interface: public + register: _amphora_lb_net_info + +- name: Obtain the password from the octavia-certificates secret + k8s_info: + kind: Secret + namespace: openstack + name: octavia-certificates + register: _octavia_cert_passwd + +- name: decode the secret for the octavia cert password + set_fact: + cert_passwd: "{{ _octavia_cert_passwd.resources[0].data.password | b64decode }}" + +- name: Create the helm values file for amphora provider + template: + src: templates/octavia_amphora_provider.yaml.j2 + dest: "{{ octavia_helm_values_file }}" + +- debug: + msg: "helm values file generated: {{ octavia_helm_values_file }}" diff --git a/ansible/roles/octavia_preconf/tasks/octavia_lb_net_setup.yml b/ansible/roles/octavia_preconf/tasks/octavia_lb_net_setup.yml new file mode 100644 index 00000000..679cdf46 --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/octavia_lb_net_setup.yml @@ -0,0 +1,32 @@ +--- +# These are the tasks to setup the lb-mgmt-net and +# subnet for amphora; this can either be a vlan, flat +# or a geneve or vxlan network +- name: Create lb-mgmt-net for the amphora + openstack.cloud.network: + name: lb-mgmt-net + state: present + provider_network_type: "{{ lb_mgmt_net_type }}" + provider_physical_network: "{{ lb_mgmt_net_physical_net | default(omit) }}" + provider_segmentation_id: "{{ lb_mgmt_net_segmentation_id | default(omit) }}" + interface: public + register: create_lb_mgmt_net + until: create_lb_mgmt_net is success + retries: 5 + delay: 5 + +- name: Create subnet for lb-mgmt-net + openstack.cloud.subnet: + name: lb-mgmt-subnet + state: present + enable_dhcp: true + cidr: "{{ lb_mgmt_subnet_cidr }}" + allocation_pool_start: "{{ lb_mgmt_subnet_pool_start }}" + allocation_pool_end: "{{ lb_mgmt_subnet_pool_end }}" + disable_gateway_ip: true + network_name: lb-mgmt-net + interface: public + register: create_lb_mgmt_subnet + until: create_lb_mgmt_subnet is success + retries: 5 + delay: 5 diff --git a/ansible/roles/octavia_preconf/tasks/octavia_sec_group.yml b/ansible/roles/octavia_preconf/tasks/octavia_sec_group.yml new file mode 100644 index 00000000..d7180176 --- /dev/null +++ b/ansible/roles/octavia_preconf/tasks/octavia_sec_group.yml @@ -0,0 +1,83 @@ +--- +# These are the tasks to create the security +# groups for amphorae as well as the health +# manager ports; amphora security group includes +# rules for allowing traffic on port 9443, icmp +# and ssh are optional; health manager secgroup +# only allows traffic on port 5555 +- name: Create security group for amphorae + openstack.cloud.security_group: + name: "{{ lb_mgmt_secgrp_name }}" + state: present + description: "security group for octavia amphorae" + interface: public + register: create_lb_mgmt_secgroup + until: create_lb_mgmt_secgroup is success + retries: 5 + delay: 5 + +- name: Create Security group rule to allow traffic on port 9443 for amphorae + openstack.cloud.security_group_rule: + security_group: "{{ create_lb_mgmt_secgroup.security_group.id }}" + state: present + protocol: tcp + port_range_min: 9443 + port_range_max: 9443 + remote_ip_prefix: "{{ lb_mgmt_subnet_cidr }}" + interface: public + register: lb_mgmt_secgroup_r1 + until: lb_mgmt_secgroup_r1 is success + retries: 5 + delay: 5 + +- name: Create Security group rule to allow icmp traffic for amphorae + openstack.cloud.security_group_rule: + security_group: "{{ create_lb_mgmt_secgroup.security_group.id }}" + state: present + protocol: icmp + remote_ip_prefix: "{{ lb_mgmt_subnet_cidr }}" + interface: public + register: lb_mgmt_secgroup_r2 + until: lb_mgmt_secgroup_r2 is success + retries: 5 + delay: 5 + when: amphora_icmp_enabled + +- name: Create Security group rule to allow ssh traffic for amphorae + openstack.cloud.security_group_rule: + security_group: "{{ create_lb_mgmt_secgroup.security_group.id }}" + state: present + protocol: tcp + port_range_min: 22 + port_range_max: 22 + remote_ip_prefix: "{{ lb_mgmt_subnet_cidr }}" + interface: public + register: lb_mgmt_secgroup_r3 + until: lb_mgmt_secgroup_r3 is success + retries: 5 + delay: 5 + when: amphora_ssh_enabled + +- name: Create security group for health manager ports + openstack.cloud.security_group: + name: "{{ lb_health_mgr_secgrp_name }}" + state: present + description: "security group for health manager ports" + interface: public + register: create_lb_health_mgr_secgroup + until: create_lb_health_mgr_secgroup is success + retries: 5 + delay: 5 + +- name: Create Security group rule to allow traffic on port 5555 for health manager + openstack.cloud.security_group_rule: + security_group: "{{ create_lb_health_mgr_secgroup.security_group.id }}" + state: present + protocol: udp + port_range_min: 5555 + port_range_max: 5555 + interface: public + register: lb_health_mgr_secgroup_r1 + until: lb_health_mgr_secgroup_r1 is success + retries: 5 + delay: 5 diff --git a/ansible/roles/octavia_preconf/templates/octavia_amphora_provider.yaml.j2 b/ansible/roles/octavia_preconf/templates/octavia_amphora_provider.yaml.j2 new file mode 100644 index 00000000..4c76181d --- /dev/null +++ b/ansible/roles/octavia_preconf/templates/octavia_amphora_provider.yaml.j2 @@ -0,0 +1,120 @@ +--- +# this file is generated for enabling amphora provider for octavia +# the pod mounts have been set to the recommended defaults; if you +# generated the certs manually then the pod mounts and volumes have +# to be modified accordingly + +pod: + mounts: + octavia_api: + octavia_api: + volumeMounts: + - name: octavia-certs + mountPath: /etc/octavia/certs/private/server_ca.key.pem + subPath: server_ca.key.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/server_ca.cert.pem + subPath: server_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/client_ca.cert.pem + subPath: client_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/private/client.key-and-cert.pem + subPath: client.key-and-cert.pem + - name: pod-run-octavia + mountPath: /var/run/octavia + volumes: + - name: octavia-certs + secret: + secretName: octavia-certs + defaultMode: 0644 + - name: pod-run-octavia + emptyDir: {} + octavia_worker: + octavia_worker: + volumeMounts: + - name: octavia-certs + mountPath: /etc/octavia/certs/private/server_ca.key.pem + subPath: server_ca.key.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/server_ca.cert.pem + subPath: server_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/client_ca.cert.pem + subPath: client_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/private/client.key-and-cert.pem + subPath: client.key-and-cert.pem + - name: pod-run-octavia + mountPath: /var/run/octavia + volumes: + - name: octavia-certs + secret: + secretName: octavia-certs + defaultMode: 0644 + - name: pod-run-octavia + emptyDir: {} + octavia_housekeeping: + octavia_housekeeping: + volumeMounts: + - name: octavia-certs + mountPath: /etc/octavia/certs/private/server_ca.key.pem + subPath: server_ca.key.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/server_ca.cert.pem + subPath: server_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/client_ca.cert.pem + subPath: client_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/private/client.key-and-cert.pem + subPath: client.key-and-cert.pem + volumes: + - name: octavia-certs + secret: + secretName: octavia-certs + defaultMode: 0644 + octavia_health_manager: + octavia_health_manager: + volumeMounts: + - name: octavia-certs + mountPath: /etc/octavia/certs/private/server_ca.key.pem + subPath: server_ca.key.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/server_ca.cert.pem + subPath: server_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/client_ca.cert.pem + subPath: client_ca.cert.pem + - name: octavia-certs + mountPath: /etc/octavia/certs/private/client.key-and-cert.pem + subPath: client.key-and-cert.pem + volumes: + - name: octavia-certs + secret: + secretName: octavia-certs + defaultMode: 0644 + +conf: + octavia: + certificates: + ca_certificate: /etc/octavia/certs/server_ca.cert.pem + ca_private_key: /etc/octavia/certs/private/server_ca.key.pem + ca_private_key_passphrase: {{ cert_passwd }} + controller_worker: + client_ca: /etc/octavia/certs/client_ca.cert.pem + amp_image_owner_id: {{ _amphora_image_info.images[0].owner }} + amp_secgroup_list: {{ _amphora_sec_group_info.security_groups[0].id }} + amp_flavor_id: {{ _amphora_flavor_info.flavors[0].id }} + amp_boot_network_list: {{ _amphora_lb_net_info.networks[0].id }} + amp_image_tag: amphora +{% if amphora_ssh_enabled %} + amp_ssh_key_name: {{ amphora_ssh_key_name }} +{% endif %} + haproxy_amphora: + client_cert: /etc/octavia/certs/private/client.key-and-cert.pem + server_ca: /etc/octavia/certs/server_ca.cert.pem + health_manager: + bind_port: 5555 + bind_ip: 0.0.0.0 + controller_ip_port_list: {{ lookup('file', '/tmp/octavia_hm_controller_ip_port_list') }} diff --git a/ansible/roles/octavia_preconf/templates/openssl.cnf.j2 b/ansible/roles/octavia_preconf/templates/openssl.cnf.j2 new file mode 100644 index 00000000..b311f98c --- /dev/null +++ b/ansible/roles/octavia_preconf/templates/openssl.cnf.j2 @@ -0,0 +1,106 @@ +# OpenSSL root CA configuration file. + +[ ca ] +# `man ca` +default_ca = CA_default + +[ CA_default ] +# Directory and file locations. +dir = {{ octavia_certs_dir }} +certs = $dir/certs +crl_dir = $dir/crl +new_certs_dir = $dir/newcerts +database = $dir/index.txt +serial = $dir/serial +RANDFILE = $dir/private/.rand + +# The root key and root certificate for the client CA +private_key = {{ octavia_clientca_privkey }} +certificate = {{ octavia_client_ca_cert }} + +# For certificate revocation lists. +crlnumber = $dir/crlnumber +crl = $dir/crl/ca.crl.pem +crl_extensions = crl_ext +default_crl_days = 30 + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +name_opt = ca_default +cert_opt = ca_default +default_days = 3650 +preserve = no +policy = policy_strict + +[ policy_strict ] +# The root CA should only sign intermediate certificates that match. +# See the POLICY FORMAT section of `man ca`. +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ req ] +# Options for the `req` tool (`man req`). +default_bits = 2048 +distinguished_name = req_distinguished_name +string_mask = utf8only + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +# Extension to add when the -x509 option is used. +x509_extensions = v3_ca + +[ req_distinguished_name ] +# See . +countryName = Country Name (2 letter code) +stateOrProvinceName = State or Province Name +localityName = Locality Name +0.organizationName = Organization Name +organizationalUnitName = Organizational Unit Name +commonName = Common Name +emailAddress = Email Address + +# Optionally, specify some defaults. +countryName_default = US +stateOrProvinceName_default = Texas +localityName_default = +0.organizationName_default = RPC +organizationalUnitName_default = rackerlab +emailAddress_default = +commonName_default = lab.local + +[ v3_ca ] +# Extensions for a typical CA (`man x509v3_config`). +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ usr_cert ] +# Extensions for client certificates (`man x509v3_config`). +basicConstraints = CA:FALSE +nsCertType = client, email +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection + +[ server_cert ] +# Extensions for server certificates (`man x509v3_config`). +basicConstraints = CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth + +[ crl_ext ] +# Extension for CRLs (`man x509v3_config`). +authorityKeyIdentifier=keyid:always diff --git a/ansible/roles/octavia_preconf/vars/main.yml b/ansible/roles/octavia_preconf/vars/main.yml new file mode 100644 index 00000000..138a47c7 --- /dev/null +++ b/ansible/roles/octavia_preconf/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for octavia_preconf