From 8720df17c3965e0cc57eacda92b607d66dab511a Mon Sep 17 00:00:00 2001 From: Zolfa Date: Sat, 18 Apr 2020 19:15:34 +0200 Subject: [PATCH] Documentation for refactored roles And minor improvements/refactoring --- ldap.yaml | 1 - roles/gitlab/README.md | 68 +++++++++++++++++++++ roles/ldap/README.md | 92 +++++++++++++++++++++++++++++ roles/ldap/tasks/4_setup_tls.yaml | 4 -- roles/lxc_guest/README.md | 72 ++++++++++++++++++++++ roles/lxc_guest/defaults/main.yaml | 8 ++- roles/lxc_guest/handlers/main.yaml | 8 +-- roles/lxc_guest/tasks/main.yaml | 22 +------ roles/lxc_guest/templates/config.j2 | 2 +- roles/ssh_server/README.md | 76 ++++++++++++++++++++++++ roles/ssh_server/defaults/main.yaml | 2 +- roles/ssh_server/handlers/main.yaml | 5 +- roles/ssh_server/tasks/main.yaml | 78 ++++++++++++------------ 13 files changed, 361 insertions(+), 77 deletions(-) create mode 100644 roles/gitlab/README.md create mode 100644 roles/ldap/README.md create mode 100644 roles/lxc_guest/README.md create mode 100644 roles/ssh_server/README.md diff --git a/ldap.yaml b/ldap.yaml index 6bbc3b5..184fdac 100644 --- a/ldap.yaml +++ b/ldap.yaml @@ -11,7 +11,6 @@ vars: vm_name: '{{ inventory_hostname }}' vm_size: '1G' - vg_name: '{{ hostvars[ansible_lxc_host]["vg_name"] }}' delegate_to: '{{ ansible_lxc_host }}' # The host may not be directly reachable: use `ssh_lxc` proxy to # gather facts (setup) and configure SSH. diff --git a/roles/gitlab/README.md b/roles/gitlab/README.md new file mode 100644 index 0000000..3801a45 --- /dev/null +++ b/roles/gitlab/README.md @@ -0,0 +1,68 @@ +# Role: gitlab + +Set-up a Omnibus GitLab server + +## Configuration variables + +| Name | Description | +|-----------------|-----------------------------------------------------------| +| `fqdn` | [`$hosname.$domain`] | +| `ssh_port` | External SSH port. [`22`] | +| `ldap_server`* | LDAP server FQDN (must be valid for STARTTLS). | +| `ldap_basedn`* | LDAP base before ou=People (i.e.: `'dc=example,dc=com'`). | +| `enable_https` | Enable HTTPS. [`false`] | +| `ldap_admin_dn` | DN of a LDAP user with admin privileges. | +| `ldap_admin_pw` | Bind password of that user. | + +**Note**: The Ansible controller must have OpenLDAP properly configured +with root ca set in `~/.ldaprc`. + +## Minimal example + +group_vars/all.yaml: + + --- + domain: 'example.com' + ssl_subject_prefix: '/C=IT/L=Firenze/O=LILiK' + x509_suffix: 'o=LILiK,l=Firenze,st=IT' + user_ca_keys: + - "ssh-ed25519 ################### CA" + ssl_ca_cert: | + -----BEGIN CERTIFICATE----- + ########################### + -----END CERTIFICATE----- + +hosts: + + vm_gateway ansible_host=10.0.2.1 ansible_user=root + authorities_request ansible_host=10.0.1.8 ansible_user=request + host1 ansible_host=10.0.1.1 ansible_user=root + ldap1 ansible_host=10.0.2.2 ansible_user=root ansible_lxc_host=host1 + gitlab ansible_host=10.0.2.3 ansible_user=root ansible_lxc_host=host1 + +playbook.yaml: + + --- + # Configure LDAP on a Physical Host + - hosts: 'host1' + roles: + - role: 'dns_record' + - role: 'reverse_proxy' + hostname: 'projects' + - role: 'gitlab' + ldap_server: 'ldap.dmz.{{ domain }}' + ldap_basedn: 'dc=example,dc=com' + +Command line: + + ansible-playbook -i hosts playbook.yaml \ + -e ldap_admin_dn= -e \ + -e ldap_amdin_pw= + + +## Requirements + +On Ansible controller: + +- tasks/ca-dialog.yaml + diff --git a/roles/ldap/README.md b/roles/ldap/README.md new file mode 100644 index 0000000..81c6dc8 --- /dev/null +++ b/roles/ldap/README.md @@ -0,0 +1,92 @@ +# Role: ldap + +Set-up a LDAP server + +## Configuration variables + +| Name | Description | +|----------------------|-------------------------------------------------------------| +| `ldap_domain`* | Dot-form domain name (i.e.: `'lilik.it'`). | +| `ldap_organization`* | Organization (i.e.: `'LILiK'`). | +| `ssl_subject_prefix` | X.509 TLS Cert Subject (i.e: `'/ST=IT/L=Firenze/O=LILiK'`). | +| `fqdn_domain`* | Required for TLS certificate. | +| `x509_suffix`* | The same in LDAP form (i.e: `'o=LILiK,l=Firenze/st=IT'`). | +| `virtual_domains` | Required with `check_tree`: list of vds to init. | +| `ldap_tls_enabled` | Enables TLS, requires a *ca_manager*. [`true`] | +| `renew_rootdn_pw` | Create a new random password for RooDN. [`true`] | +| `check_tree` | Deploy initial tree configuration. [`true`] | + + +**Note:** If `ldap_tls_enabled` the *ca_manager* host should be configured +and TLS Root CA should be set in vars. + +## Minimal example + +group_vars/all.yaml: + + --- + domain: 'example.com' + ssl_subject_prefix: '/C=IT/L=Firenze/O=LILiK' + x509_suffix: 'o=LILiK,l=Firenze,st=IT' + user_ca_keys: + - "ssh-ed25519 ################### CA" + ssl_ca_cert: | + -----BEGIN CERTIFICATE----- + ########################### + -----END CERTIFICATE----- + +hosts: + + vm_gateay ansible_host=10.0.2.1 ansible_user=root + authorities_request ansible_host=10.0.1.8 ansible_user=request + host1 ansible_host=10.0.1.1 ansible_user=root + ldap1 ansible_host=10.0.2.2 ansible_user=root ansible_lxc_host=host1 + +playbook.yaml: + + --- + # Configure LDAP on a Physical Host + - hosts: 'host' + roles: + - role: ldap + ldap_domain: 'example.com' + ldap_organization: 'Example' + fqdn_domain: '{{ domain }}' + virtual_domains: + - 'example.com' + + # Configure LDAP on a LXC container + - hosts: 'ldap1' + gather_facts: false # host may not exist yet + tasks: + - import_role: name='lxc_guest' + vars: + vm_name: '{{ inventory_hostname }}' + vm_size: '1G' + delegate_to: '{{ ansible_lxc_host }}' + - set_fact: ansible_connection='ssh_lxc' + - setup: # gather facts + - include_role: name='ssh_server' + # Now the guest is ssh-reachable, don't need proxy anymore. + - set_fact: ansible_connection='ssh' + - hosts: 'ldap1' + roles: + - role: 'dns_record' + - role: 'ldap' + ldap_domain: 'example.com' + ldap_organization: 'Example' + fqdn_domain: '{{ domain }}' + virtual_domains: + - 'example.com' + +Command line: + + ansible-playbook -i hosts playbook.yaml + + +## Requirements + +On Ansible controller: + +- tasks/ca-dialog.yaml + diff --git a/roles/ldap/tasks/4_setup_tls.yaml b/roles/ldap/tasks/4_setup_tls.yaml index 92f252d..49f2d95 100644 --- a/roles/ldap/tasks/4_setup_tls.yaml +++ b/roles/ldap/tasks/4_setup_tls.yaml @@ -27,10 +27,6 @@ -out /etc/ldap/slapd.csr creates: '/etc/ldap/slapd.csr' -- name: 'set key ownership and permission' - file: - path: /etc/ldap - - name: 'lookup_ssl_ca_cert' when: ssl_ca_cert is not defined set_fact: diff --git a/roles/lxc_guest/README.md b/roles/lxc_guest/README.md new file mode 100644 index 0000000..c44779f --- /dev/null +++ b/roles/lxc_guest/README.md @@ -0,0 +1,72 @@ +# Role: lxc_guest + +This role creates a debian LXC container on an host previously with LXC +and network in bridged mode, connecting the container to the interface +`br0` on the host. + +The ip address and gateway of the container are automatically discovered +from the ansible inventory. The `vm_gateway` entry IP is used as gateway +while the entry associated with `vm_name` IP is used as static IP +address. + +## Configuration variables + +| Name | Description | +|--------------|-----------------------------------------| +| `vm_name`* | Name of the LXC container. | +| `vm_size` | Size of the VM logical volume. [`'5G'`] | +| `distro` | Debian release name. [`'buster'`] | +| `auto_start` | Auto-start container. [`true`] | +| `domain`* | The VM domain is set to dmz.$domain | +| `vg_name`** | LVM volume group name on the host. | + +**Note: If `vg_name` is not provided it will be derived from the + `ansible_lxc_host` variable in the inventory entry of the guest. + If the entry pointed by `ansible_lxc_host` doesn't set has an + alterntive `vg_name` set, it will default to `ansible_lxc_host`+'-vg'. + +## Minimal example + +group_vars/all.yaml: + + --- + domain: 'example.com' + +hosts: + + vm_gateway ansible_host=10.0.2.1 ansible_user=root + physical1 ansible_host=10.0.1.1 ansible_user=root vm_name=test-vg + vm1 ansible_host=10.0.2.10 ansible_user=root ansible_lxc_host=physical1 + +vm1.yaml: + + --- + - hosts: vm1 + gather_facts: false # host may not exist yet + tasks: + - import_role: name='lxc_guest' + vars: + vm_name: '{{ inventory_hostname }}' + vm_size: '1G' + delegate_to: '{{ ansible_lxc_host }}' + +Command line: + + ansible-playbook -i hosts vm1.yaml + +## Requirements + +On Ansible controller: + +- connection_plugins/ssh_lxc.py + +On LXC host: + +- python3-lxc module. + + +## See also + +The playbook `prepare_host.yaml` provides a working configuration for +the physical machine running LXC. + diff --git a/roles/lxc_guest/defaults/main.yaml b/roles/lxc_guest/defaults/main.yaml index b8826d2..3080ba1 100644 --- a/roles/lxc_guest/defaults/main.yaml +++ b/roles/lxc_guest/defaults/main.yaml @@ -1,5 +1,7 @@ --- auto_start: true -container_state: started -distro: buster -vm_size: 5G +container_state: 'started' +distro: 'buster' +vm_size: '5G' +vg_name: '{{ hostvars[ansible_lxc_host]["vg_name"] | default(ansible_lxc_host+"-vg") }}' +... diff --git a/roles/lxc_guest/handlers/main.yaml b/roles/lxc_guest/handlers/main.yaml index 780cb84..b99ee4f 100644 --- a/roles/lxc_guest/handlers/main.yaml +++ b/roles/lxc_guest/handlers/main.yaml @@ -1,7 +1,7 @@ -# handlers are run in the order listed not in the order notified --- -- name: restart container +- name: 'restart container' lxc_container: - name: "{{ vm_name }}" - state: restarted + name: '{{ vm_name }}' + state: 'restarted' register: container_restart +... diff --git a/roles/lxc_guest/tasks/main.yaml b/roles/lxc_guest/tasks/main.yaml index 4dae4f8..fbf4da0 100644 --- a/roles/lxc_guest/tasks/main.yaml +++ b/roles/lxc_guest/tasks/main.yaml @@ -21,7 +21,7 @@ name: '{{ vm_name }}' backing_store: 'lvm' fs_size: '{{ vm_size }}' - vg_name: '{{ vg_name | default(inventory_hostname+"vg") }}' + vg_name: '{{ vg_name }}' lv_name: 'vm_{{ vm_name }}' fs_type: 'xfs' container_log: true @@ -72,25 +72,7 @@ connection: 'ssh_lxc' notify: 'restart container' -#- name: 'install packages' -# apt: -# pkg: -# - 'python3' -# - 'ssh' -# state: 'present' -# update_cache: yes -# cache_valid_time: 3600 -# delegate_to: '{{ vm_name }}' -# connection: 'ssh_lxc' -# notify: restart container - - # Restart container when one in - # - container_dns_configuration - # - network conf has changed - # - install_packages - # - container_network - # is changed by executing handlers now -- meta: flush_handlers +- meta: 'flush_handlers' - name: 'add monitoring facts' set_fact: diff --git a/roles/lxc_guest/templates/config.j2 b/roles/lxc_guest/templates/config.j2 index a554fc1..bc4bd5f 100644 --- a/roles/lxc_guest/templates/config.j2 +++ b/roles/lxc_guest/templates/config.j2 @@ -1,7 +1,7 @@ lxc.include = /usr/share/lxc/config/debian.common.conf lxc.uts.name = {{ vm_name }} -lxc.rootfs.path = lvm:/dev/{{ vg_name | default(inventory_hostname+'vg') }}/vm_{{ vm_name }} +lxc.rootfs.path = lvm:/dev/{{ vg_name }}/vm_{{ vm_name }} lxc.apparmor.profile = generated lxc.apparmor.allow_nesting = 1 diff --git a/roles/ssh_server/README.md b/roles/ssh_server/README.md new file mode 100644 index 0000000..38f022b --- /dev/null +++ b/roles/ssh_server/README.md @@ -0,0 +1,76 @@ +# Role: ssh_server + +This role congigure an *OpenSSH* server configured with certifcates +provided by a local *ca_manager* instance. + +Root password login in disabled and *certificate authentication* is +enabled for users with certificate issued by the authorized authorities, +listed in the variables `user_ca_keys`. + +For the role to work the local certification authority must be +configured and reachable from the Ansible controller machine. + +The local user must be able to automatically login as the `request` use +to the *ca_manager* instance. + +## Configuration variables + +| Name | Description | +|-----------------|-----------------------------------------------------------------| +| `user_ca_keys`* | List of allowed CA certificate. First entry is the default one. | +| `server_fqdn` | Used for the host certificate. [`$host.$domain`] | + + +**Note: The *ca_manager* instance should be present in the inventory. + +## Minimal example + +group_vars/all.yaml: + + --- + domain: 'example.com' + user_ca_keys: + - 'ssh-ed25519 ############## Production CA' + - 'ssh-ed25519 ############## Backup CA' + +hosts: + + vm_gateay ansible_host=10.0.2.1 ansible_user=root + authorities_request ansible_host=10.0.1.8 ansible_user=request + host1 ansible_host=10.0.1.1 ansible_user=root + virtual1 ansible_host=10.0.2.2 ansible_user=root ansible_lxc_host=host1 + +playbook.yaml: + + --- + # Configure SSH on a Physical Host + - hosts: host1 + roles: + - role: ssh_server + + # Configure SSH on a new LXC Guest with ssh_lxc proxy + - hosts: virtual1 + gather_facts: false # host may not exist yet + tasks: + - import_role: name='lxc_guest' + vars: + vm_name: '{{ inventory_hostname }}' + vm_size: '1G' + delegate_to: '{{ ansible_lxc_host }}' + - set_fact: ansible_connection='ssh_lxc' + - setup: # gather facts + - include_role: name='ssh_server' + # Now the guest is ssh-reachable, don't need proxy anymore. + - set_fact: ansible_connection='ssh' + +Command line: + + ansible-playbook -i hosts playbook.yaml + + +## Requirements + +On Ansible controller: + +- tasks/ca-dialog.yaml + diff --git a/roles/ssh_server/defaults/main.yaml b/roles/ssh_server/defaults/main.yaml index 482f242..25e1014 100644 --- a/roles/ssh_server/defaults/main.yaml +++ b/roles/ssh_server/defaults/main.yaml @@ -1,2 +1,2 @@ --- -server_fqdn: "{{ ansible_ssh_lxc_name | default(inventory_hostname) }}.{{ domain }}" +server_fqdn: '{{ inventory_hostname }}.{{ domain }}' diff --git a/roles/ssh_server/handlers/main.yaml b/roles/ssh_server/handlers/main.yaml index 4b2b1eb..f0944fd 100644 --- a/roles/ssh_server/handlers/main.yaml +++ b/roles/ssh_server/handlers/main.yaml @@ -1,4 +1,3 @@ -# We can not use include_role here since it not share thje connection with the current role -- include: roles/service/handlers/main.yaml +- include: 'roles/service/handlers/main.yaml' vars: - service_name: ssh + service_name: 'ssh' diff --git a/roles/ssh_server/tasks/main.yaml b/roles/ssh_server/tasks/main.yaml index b2db2b1..22653d8 100644 --- a/roles/ssh_server/tasks/main.yaml +++ b/roles/ssh_server/tasks/main.yaml @@ -1,20 +1,18 @@ -# We can not use include_role here since it not -# share the connection with the current role --- -- include: roles/service/tasks/main.yaml +- include: 'roles/service/tasks/main.yaml' vars: - service_name: ssh + service_name: 'ssh' service_packages: - - openssh-server - - openssh-sftp-server + - 'openssh-server' + - 'openssh-sftp-server' -- name: Update container user CA key +- name: 'update user ca certs' template: - src: user_ca.pub.j2 - dest: "/etc/ssh/user_ca.pub" - notify: restart ssh + src: 'user_ca.pub.j2' + dest: '/etc/ssh/user_ca.pub' + notify: 'restart ssh' -- name: Validate SSH host certificate if any +- name: 'validate ssh cert if present' ssh_cert: register: ssh_verification ignore_errors: yes @@ -24,30 +22,30 @@ verbosity: 2 - block: - - name: Generate host request + - name: 'generate host cert request' cert_request: - host: "{{ server_fqdn }}" - path: "/etc/ssh/ssh_host_ed25519_key.pub" - proto: "ssh" + host: '{{ server_fqdn }}' + path: '/etc/ssh/ssh_host_ed25519_key.pub' + proto: 'ssh' register: ca_request - - name: start sign request - include: ca-dialog.yaml + - name: 'start sign request' + include: 'ca-dialog.yaml' vars: - ansible_connection: ssh + ansible_connection: 'ssh' - debug: var: request_result verbosity: 2 - set_fact: - request_output: "{{ request_result.stdout | from_json }}" + request_output: '{{ request_result.stdout | from_json }}' - debug: var: request_output verbosity: 2 - - name: generate get request + - name: 'generate get request' set_fact: ca_request: type: 'get_certificate' @@ -58,55 +56,55 @@ verbosity: 2 - debug: - msg: "Please manualy confirm sign request with id {{ request_output.requestID }}" + msg: 'Please manualy confirm sign request with id {{ request_output.requestID }}' - - name: wait for cert - include: ca-dialog.yaml + - name: 'wait for cert' + include: 'ca-dialog.yaml' vars: - ansible_connection: ssh + ansible_connection: 'ssh' - debug: var: request_result verbosity: 2 - set_fact: - cert_key: "{{ request_result.stdout | string | from_json }}" + cert_key: '{{ request_result.stdout | string | from_json }}' - - name: Write certificate to container + - name: 'write certificate to container' copy: - content: "{{ cert_key.result }}" - dest: "/etc/ssh/ssh_host_ed25519_key-cert.pub" + content: '{{ cert_key.result }}' + dest: '/etc/ssh/ssh_host_ed25519_key-cert.pub' register: set_pub_key - notify: restart ssh + notify: 'restart ssh' when: ssh_verification.failed -- name: add certificate to sshd config +- name: 'add certificate to sshd config' lineinfile: line: 'HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub' dest: '/etc/ssh/sshd_config' regexp: '^HostCertificate *' - notify: restart ssh + notify: 'restart ssh' -- name: trust user ca key +- name: 'trust user ca key' lineinfile: line: 'TrustedUserCAKeys /etc/ssh/user_ca.pub' dest: '/etc/ssh/sshd_config' regexp: '^TrustedUserCAKeys *' - notify: restart ssh + notify: 'restart ssh' -- name: permit root login only with certificate +- name: 'permit root login only with certificate' lineinfile: line: 'PermitRootLogin without-password' dest: '/etc/ssh/sshd_config' regexp: '^PermitRootLogin *' - notify: restart ssh + notify: 'restart ssh' -- meta: flush_handlers +- meta: 'flush_handlers' -- name: "waiting for ssh on {{ ansible_ssh_lxc_name | default(inventory_hostname) }} to start" +- name: 'waiting for ssh on {{ inventory_hostname }} to start' wait_for: - host: "{{ hostvars | ip_from_inventory(inventory_hostname) }}" + host: '{{ hostvars | ip_from_inventory(inventory_hostname) }}' port: 22 timeout: 30 - delegate_to: "{{ inventory_hostname }}" - delegate_facts: True + delegate_to: '{{ inventory_hostname }}' + delegate_facts: true