diff --git a/README.md b/README.md index 35958c4..f2e089c 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ Listen port for the Swarm raft API. skip_engine: false skip_cli: false skip_swarm: false + skip_swarm_cert: false skip_group: false skip_docker_py: false skip_docker_compose: false @@ -157,6 +158,10 @@ Listen port for the Swarm raft API. Switches allowing to disable specific functionalities of the role. If you want to use this role to install `docker-engine` without enabling `swarm-mode` set `skip_swarm: true`. +If you want to use this role to gen API certs, set `skip_swarm_cert: true` + +If you want to keep certs to local folder, set `docker_api_certs_docker_local_cert_path: /your/local/path` + Swarm node labels ----------------- diff --git a/defaults/main.yml b/defaults/main.yml index 8e190cd..0051bc4 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -39,6 +39,33 @@ docker_service_enabled: "yes" # Docker Deamon configuration docker_daemon_config: {} +## +# Docker API cert +## + +# Generate new cert on groups['docker_swarm_manager'][0] +docker_api_certs_cert_gen: False + +# Base dir docker +docker_api_certs_docker_base_path: /etc/docker +# Base dir for cert.pem key.pem ca.pem (used for deamon.json) +docker_api_certs_docker_cert_path: "{{ docker_api_certs_docker_base_path }}/cert" + +# used for deamon.json +docker_api_certs_ca_name: ca.pem +docker_api_certs_cert_name: cert.pem +docker_api_certs_key_name: key.pem + +# Openssl config for generate CA +docker_api_certs_openssl_domain: swarm.localdomain.local +docker_api_certs_openssl_state: Ile-de-France +docker_api_certs_openssl_country: FR +docker_api_certs_openssl_email: docker@localhost +docker_api_certs_openssl_org: My ORG +docker_api_certs_openssl_ou: My OU + +docker_api_certs_docker_certificate_validity: 365 + ## # Docker CLI ## @@ -121,6 +148,12 @@ docker_group_users: # swarm cluster doesn't overlap with you infrastructure # docker_swarm_network: 10.10.8.0/24 +# Custom swarm init args. ex: customize default-addr-pool +# docker_swarm_init_extra_options: | +# --default-addr-pool 10.184.32.0/19 \ +# --default-addr-pool-mask-length 28 +docker_swarm_init_extra_options: "" + # You can set any interface, that is listened by docker engine. # e.g. docker_swarm_interface: "eth1" docker_swarm_interface: "{{ ansible_default_ipv4['interface'] }}" @@ -137,6 +170,7 @@ skip_containerd: false # if true, skips the setup of containerd skip_engine: false # if true, skips the docker engine installation skip_cli: false # if true, skips the docker cli installation skip_swarm: false # if true, skips the swarm setup +skip_swarm_cert: false # if true, skips the swarm cert gen and deploy skip_group: false # if true, does not add the docker_admin_users to the docker_group_name skip_docker_py: false # if true, skips the installation of docker-py skip_docker_compose: false # if true, skips the installation of docker-compose diff --git a/tasks/main.yml b/tasks/main.yml index f897ec6..ec886ee 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -14,6 +14,14 @@ # Install python-pip - include_tasks: setup-python-pip.yml +# Install docker-py +- include_tasks: setup-docker-py.yml + when: not skip_docker_py + +# Install docker-compose +- include_tasks: setup-docker-compose.yml + when: not skip_docker_compose + # Install the Docker repository - include_tasks: "repo-{{ ansible_os_family }}.yml" when: not skip_repo @@ -41,16 +49,10 @@ - block: - include_tasks: setup-swarm-cluster.yml - include_tasks: setup-swarm-labels.yml + - include_tasks: setup-docker-swarm-api-certs.yml + when: not skip_swarm_cert when: not skip_swarm # Adds the Docker admin users to the Docker group - include_tasks: setup-docker-group.yml when: not skip_group - -# Install docker-py -- include_tasks: setup-docker-py.yml - when: not skip_docker_py - -# Install docker-compose -- include_tasks: setup-docker-compose.yml - when: not skip_docker_compose diff --git a/tasks/setup-docker-swarm-api-certs.yml b/tasks/setup-docker-swarm-api-certs.yml new file mode 100644 index 0000000..cecdb19 --- /dev/null +++ b/tasks/setup-docker-swarm-api-certs.yml @@ -0,0 +1,182 @@ +--- +## +# check prerequisites +- name: Create certificate directory + file: path="{{ docker_api_certs_docker_cert_path }}" state=directory recurse=yes owner=root group=root mode=0755 + become: True + +- name: Create CA certificate directory + file: path="{{ docker_api_certs_docker_cert_path }}/ca" state=directory recurse=yes owner=root group=root mode=0755 + become: True + +- name: Create client certificate directory + file: path="{{ docker_api_certs_docker_cert_path }}/client" state=directory recurse=yes owner=root group=root mode=0755 + become: True + +- name: Install OpenSSL + package: + name: openssl + state: present + become: True + +## +# Gen cert files +- name: copy openssl configuration + template: + src: templates/openssl.cnf.j2 + dest: "{{ docker_api_certs_docker_cert_path }}/openssl-ca.cnf" + owner: root + group: root + mode: 0644 + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + become: True + +- name: generate CA private key + command: openssl genrsa -out {{ docker_api_certs_docker_cert_path }}/ca/ca-key.pem 2048 + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + become: True + +- name: generate CA public key + command: | + openssl req -config {{ docker_api_certs_docker_cert_path }}/openssl-ca.cnf -new -x509 \ + -days {{ docker_api_certs_docker_certificate_validity }} \ + -key {{ docker_api_certs_docker_cert_path }}/ca/ca-key.pem -sha256 \ + -out {{ docker_api_certs_docker_cert_path }}/ca.pem + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + become: True + +- name: generate private keys + command: openssl genrsa -out {{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_key_name }} 2048 + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + notify: restart docker + become: True + +- name: generate server csr (server.csr) + command: | + openssl req -subj "/CN={{ docker_api_certs_openssl_domain }}" -sha256 -new \ + -key {{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_key_name }} \ + -out {{ docker_api_certs_docker_cert_path }}/server.csr + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + become: True + +- name: generate public key + command: | + openssl x509 -req -days {{ docker_api_certs_docker_certificate_validity }} -sha256 \ + -in {{ docker_api_certs_docker_cert_path }}/server.csr \ + -CA {{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_ca_name }} \ + -CAkey {{ docker_api_certs_docker_cert_path }}/ca/ca-key.pem \ + -CAcreateserial -out {{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_cert_name }} \ + -extfile {{ docker_api_certs_docker_cert_path }}/openssl-ca.cnf + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + notify: restart docker + become: True + +- name: generate client key + command: openssl genrsa -out {{ docker_api_certs_docker_cert_path }}/client/client-{{ docker_api_certs_key_name }} 2048 + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + notify: restart docker + become: True + +- name: generate client csr (cert.csr) + command: | + openssl req -subj '/CN=client' -new \ + -key {{ docker_api_certs_docker_cert_path }}/client/client-{{ docker_api_certs_key_name }} \ + -out {{ docker_api_certs_docker_cert_path }}/client/client.csr + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + become: True + +- name: Generate client crt + shell: | + echo extendedKeyUsage = clientAuth > {{ docker_api_certs_docker_cert_path }}/client/extfile.cnf \ + && openssl x509 -req -days {{ docker_api_certs_docker_certificate_validity }} -sha256 \ + -in {{ docker_api_certs_docker_cert_path }}/client/client.csr \ + -CA {{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_ca_name }} \ + -CAkey {{ docker_api_certs_docker_cert_path }}/ca/ca-key.pem -CAcreateserial \ + -out {{ docker_api_certs_docker_cert_path }}/client/client-{{ docker_api_certs_cert_name }} \ + -extfile {{ docker_api_certs_docker_cert_path }}/client/extfile.cnf + when: inventory_hostname == groups['docker_swarm_manager'][0] and docker_api_certs_cert_gen + run_once: True + become: True + +## +# Get cert files if exist +- name: get {{ docker_api_certs_ca_name }} from {{ groups['docker_swarm_manager'][0] }} + slurp: + src: "{{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_ca_name }}" + delegate_to: "{{ groups['docker_swarm_manager'][0] }}" + register: docker_api_certs_ca + run_once: True + become: True + +- name: get {{ docker_api_certs_cert_name }} from {{ groups['docker_swarm_manager'][0] }} + slurp: + src: "{{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_cert_name }}" + delegate_to: "{{ groups['docker_swarm_manager'][0] }}" + register: docker_api_certs_cert + run_once: True + become: True + +- name: get ca.pem from {{ groups['docker_swarm_manager'][0] }} + slurp: + src: "{{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_key_name }}" + delegate_to: "{{ groups['docker_swarm_manager'][0] }}" + register: docker_api_certs_key + run_once: True + become: True + +## +# Push cert files if don't exist +- name: write ca.pem + copy: + dest: "{{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_ca_name }}" + content: "{{ docker_api_certs_ca['content'] | b64decode }}" + owner: root + group: root + mode: 0644 + when: inventory_hostname != groups['docker_swarm_manager'][0] + notify: restart docker + become: True + +- name: write cert.pem + copy: + dest: "{{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_cert_name }}" + content: "{{ docker_api_certs_cert['content'] | b64decode }}" + owner: root + group: root + mode: 0644 + when: inventory_hostname != groups['docker_swarm_manager'][0] + notify: restart docker + become: True + +- name: write key.pem + copy: + dest: "{{ docker_api_certs_docker_cert_path }}/{{ docker_api_certs_key_name }}" + content: "{{ docker_api_certs_key['content'] | b64decode }}" + owner: root + group: root + mode: 0644 + when: inventory_hostname != groups['docker_swarm_manager'][0] + notify: restart docker + become: True + +## +# get a local copy of certs + +- name: get certs to localdir from {{ groups['docker_swarm_manager'][0] }} + fetch: + src: "{{ docker_api_certs_docker_cert_path }}/{{ item }}" + dest: "{{ docker_api_certs_docker_local_cert_path }}/" + with_items: + - "{{ docker_api_certs_ca_name }}" + - "{{ docker_api_certs_cert_name }}" + - "{{ docker_api_certs_key_name }}" + when: docker_api_certs_docker_local_cert_path is defined and inventory_hostname == groups['docker_swarm_manager'][0] + become: True diff --git a/tasks/setup-swarm-cluster.yml b/tasks/setup-swarm-cluster.yml index 2ffc9fc..3bdcda3 100644 --- a/tasks/setup-swarm-cluster.yml +++ b/tasks/setup-swarm-cluster.yml @@ -1,16 +1,16 @@ --- -- name: Create a custom Swarm network. +- name: Create a custom Swarm network docker_network: name: docker_gwbridge driver_options: com.docker.network.bridge.enable_icc: "false" com.docker.network.bridge.enable_ip_masquerade: "true" com.docker.network.bridge.name: docker_gwbridge - ipam_options: - subnet: "{{ docker_swarm_network }}" - gateway: "{{ docker_swarm_network | ipaddr('net') | ipaddr('1') | ipaddr('ip') }}" - when: docker_swarm_network is defined and docker_swarm_network | ipaddr('net') + ipam_config: + - subnet: "{{ docker_swarm_network }}" + gateway: "{{ docker_swarm_network | ansible.utils.ipaddr('net') | ansible.utils.ipaddr('1') | ansible.utils.ipaddr('ip') }}" + when: docker_swarm_network is defined and docker_swarm_network | ansible.utils.ipaddr('net') - name: Check if "Swarm Mode" is enabled. shell: docker info @@ -22,7 +22,7 @@ - name: Init "Swarm Mode" on the first manager. shell: docker swarm init --listen-addr {{ docker_swarm_addr }}:{{ docker_swarm_port }} - --advertise-addr {{ docker_swarm_addr }} + --advertise-addr {{ docker_swarm_addr }} {{ docker_swarm_init_extra_options }} when: "docker_info.stdout.find('Swarm: active') == -1 and inventory_hostname == groups['docker_swarm_manager'][0]" tags: diff --git a/tasks/setup-swarm-labels.yml b/tasks/setup-swarm-labels.yml index 9e9b83e..c997858 100644 --- a/tasks/setup-swarm-labels.yml +++ b/tasks/setup-swarm-labels.yml @@ -4,7 +4,7 @@ command: >- docker inspect --format {% raw %}'{{ range $key, $value := .Spec.Labels }}{{ printf "%s\n" $key }}{{ end }}'{% endraw %} - {{ ansible_fqdn|lower }} + {{ ansible_hostname|lower }} register: docker_swarm_labels changed_when: false delegate_to: "{{ groups['docker_swarm_manager'][0] }}" @@ -13,7 +13,7 @@ - swarm_labels - name: Remove labels from swarm node. - command: docker node update --label-rm {{ item }} {{ ansible_fqdn|lower }} + command: docker node update --label-rm {{ item }} {{ ansible_hostname|lower }} with_items: "{{ docker_swarm_labels.stdout_lines }}" when: item not in swarm_labels delegate_to: "{{ groups['docker_swarm_manager'][0] }}" @@ -22,7 +22,7 @@ - swarm_labels - name: Assign labels to swarm nodes if any. - command: docker node update --label-add {{ item }}=true {{ ansible_fqdn|lower }} + command: docker node update --label-add {{ item }}=true {{ ansible_hostname|lower }} when: item not in docker_swarm_labels.stdout_lines with_items: - "{{ swarm_labels | default([]) }}" diff --git a/templates/openssl.cnf.j2 b/templates/openssl.cnf.j2 new file mode 100644 index 0000000..a8fbfe9 --- /dev/null +++ b/templates/openssl.cnf.j2 @@ -0,0 +1,11 @@ +[ req ] +prompt = no +distinguished_name = req_distinguished_name + +[ req_distinguished_name ] +commonName = {{ docker_api_certs_openssl_domain }} +stateOrProvinceName = {{ docker_api_certs_openssl_state }} +countryName = {{ docker_api_certs_openssl_country }} +emailAddress = {{ docker_api_certs_openssl_email }} +organizationName = {{ docker_api_certs_openssl_org }} +organizationalUnitName = {{ docker_api_certs_openssl_ou }} diff --git a/vars/Debian.yml b/vars/Debian.yml index fc281ca..1767571 100644 --- a/vars/Debian.yml +++ b/vars/Debian.yml @@ -1,10 +1,10 @@ --- python_pip_packages: - - python-pip + - "{% if ansible_facts.python.version.major is version('3', '=') %}python3-pip{% else %}python-pip{% endif %}" python_sni_support_packages: - - python-dev + - "{% if ansible_facts.python.version.major is version('3', '=') %}python3-dev{% else %}python-dev{% endif %}" - libssl-dev - libffi-dev