diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 9d18978e9..4596a56c3 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 11.5.0 +version: 11.5.1 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/configure_networking/tasks/proxmox/netplan.yml b/nova/core/roles/configure_networking/tasks/proxmox/netplan.yml index 2508d30e8..c1837fa75 100644 --- a/nova/core/roles/configure_networking/tasks/proxmox/netplan.yml +++ b/nova/core/roles/configure_networking/tasks/proxmox/netplan.yml @@ -3,17 +3,27 @@ become: false delegate_to: localhost block: - - name: Templating network configuration to localhost... + - name: Templating netplan configuration files... ansible.builtin.template: - src: netplan.yml - dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml - lstrip_blocks: true + src: "{{ item.src }}" + dest: "{{ item.dest }}" mode: "0644" + lstrip_blocks: true + loop_control: + label: "{{ item.src }}" + loop: + - src: 70-persistent-net.rules + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_70-persistent-net.rules + - src: netplan.yml + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml - - name: Getting network configuration file contents... + - name: Getting configuration files' contents ansible.builtin.slurp: - path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml + path: "{{ item }}" register: file_contents + loop: + - /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_70-persistent-net.rules + - /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml - name: Removing any existing netplan configurations... ansible.builtin.uri: @@ -33,18 +43,23 @@ - name: Including command run check task... ansible.builtin.include_tasks: command_run_check.yml - - name: Writing netplan configuration to {{ custom_vm_name | default(vm_name) }}... + - name: Writing network configuration to {{ custom_vm_name | default(vm_name) }}... ansible.builtin.uri: url: "{{ proxmox_api_url }}/nodes/{{ cfg_net_proxmox_node }}/qemu/{{ cfg_net_proxmox_vmid }}/agent/file-write" headers: Authorization: PVEAPIToken={{ proxmox_defaults.api_user }}!{{ proxmox_defaults.api_token_id }}={{ proxmox_defaults.api_token_secret }} method: POST body: - content: "{{ file_contents.content | b64decode }}" - file: /etc/netplan/01-netcfg.yaml + content: "{{ file_contents.results[file_index].content | b64decode }}" + file: "{{ item }}" body_format: json validate_certs: "{{ proxmox_defaults.validate_certs }}" register: network_config_command + loop_control: + index_var: file_index + loop: + - /etc/udev/rules.d/70-persistent-net.rules + - /etc/netplan/01-netcfg.yaml - name: Applying netplan... ansible.builtin.uri: diff --git a/nova/core/roles/configure_networking/tasks/proxmox/networkd.yml b/nova/core/roles/configure_networking/tasks/proxmox/networkd.yml index 234c91e5a..46e0b3172 100644 --- a/nova/core/roles/configure_networking/tasks/proxmox/networkd.yml +++ b/nova/core/roles/configure_networking/tasks/proxmox/networkd.yml @@ -103,8 +103,7 @@ command: - sh - -c - - "systemctl enable systemd-networkd && systemctl restart systemd-networkd && - systemctl enable systemd-udev-trigger && systemctl restart systemd-udev-trigger" + - "systemctl enable systemd-networkd --now && systemctl enable systemd-udev-trigger --now" body_format: json validate_certs: "{{ proxmox_defaults.validate_certs }}" register: network_config_command @@ -170,7 +169,7 @@ command: - sh - -c - - "update-initramfs -u" + - "apt purge dhcpcd-base -y && update-initramfs -u" body_format: json validate_certs: "{{ proxmox_defaults.validate_certs }}" register: network_config_command diff --git a/nova/core/roles/configure_networking/tasks/vsphere/netplan.yml b/nova/core/roles/configure_networking/tasks/vsphere/netplan.yml index 71a99fa99..e09b7b648 100644 --- a/nova/core/roles/configure_networking/tasks/vsphere/netplan.yml +++ b/nova/core/roles/configure_networking/tasks/vsphere/netplan.yml @@ -3,17 +3,27 @@ become: false delegate_to: localhost block: - - name: Templating network netplan.yml... + - name: Templating netplan configuration files... ansible.builtin.template: - src: netplan.yml - dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml - lstrip_blocks: true + src: "{{ item.src }}" + dest: "{{ item.dest }}" mode: "0644" - - - name: Getting file info... + lstrip_blocks: true + loop_control: + label: "{{ item.src }}" + loop: + - src: 70-persistent-net.rules + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_70-persistent-net.rules + - src: netplan.yml + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml + + - name: Getting configuration files' info... ansible.builtin.stat: - path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml + path: "{{ item }}" register: file_size + loop: + - /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_70-persistent-net.rules + - /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml # Since the vSphere API can be quite unstable especially under load, we implement a rescue loop here # to retry the network configuration up to 3 times before failing the task completely. @@ -30,12 +40,17 @@ spec: attributes: overwrite: true - size: "{{ file_size.stat.size }}" - path: /etc/netplan/01-netcfg.yaml + size: "{{ file_size.results[file_index].stat.size }}" + path: "{{ item }}" status_code: 201 body_format: json - validate_certs: "{{ validate_vmware_certs }}" + validate_certs: "{{ vmware_defaults.validate_certs }}" register: file_upload_prep + loop_control: + index_var: file_index + loop: + - /etc/udev/rules.d/70-persistent-net.rules + - /etc/netplan/01-netcfg.yaml - name: Removing existing netplan configuration file(s)... ansible.builtin.uri: @@ -58,14 +73,19 @@ - name: Uploading file... ansible.builtin.uri: - url: "{{ file_upload_prep.json }}" + url: "{{ file_upload_prep.results[file_upload_index].json }}" method: PUT headers: vmware-api-session-id: "{{ vcenter_session_api_key.json }}" - src: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml + src: "{{ item }}" status_code: 200 body_format: json - validate_certs: "{{ validate_vmware_certs }}" + validate_certs: "{{ vmware_defaults.validate_certs }}" + loop_control: + index_var: file_upload_index + loop: + - /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_70-persistent-net.rules + - /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_netplan.yml - name: Configuring network... ansible.builtin.uri: @@ -102,107 +122,3 @@ - name: Re-including vSphere network configuration tasks... ansible.builtin.include_tasks: main.yml - - # When using many IPs on a single interface, netplan will start having DNS resolution issues - # For those use cases we will use resolv.conf to configure DNS - - name: Configuring resolv.conf for multi-ip systems... - when: extra_ipv4 is defined - or extra_ipv6 is defined - block: - - name: Templating resolv.conf configuration script... - ansible.builtin.template: - src: resolv.conf - dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_resolv.conf - lstrip_blocks: true - mode: "0644" - - - name: Getting resolv.conf file info... - ansible.builtin.stat: - path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_resolv.conf - register: resolv_file_size - - # Since the vSphere API can be quite unstable especially under load, we implement a rescue loop here - # to retry the network configuration up to 3 times before failing the task completely. - - name: Including network configuration tasks... - block: - - name: Disabling systemd-resolved service... - ansible.builtin.uri: - url: https://{{ vmware_defaults.hostname }}/api/vcenter/vm/{{ configure_networking_vsphere_vm_id }}/guest/processes?action=create - method: POST - headers: - vmware-api-session-id: "{{ vcenter_session_api_key.json }}" - body: - credentials: "{{ rest_api_credentials }}" - spec: - arguments: -c "systemctl disable --now systemd-resolved" - path: /bin/bash - status_code: 201 - body_format: json - validate_certs: "{{ validate_vmware_certs }}" - register: network_config_command - - - name: Including command run check task... - ansible.builtin.include_tasks: command_run_check.yml - - - name: Removing existing resolv.conf file... - ansible.builtin.uri: - url: https://{{ vmware_defaults.hostname }}/api/vcenter/vm/{{ configure_networking_vsphere_vm_id }}/guest/processes?action=create - method: POST - headers: - vmware-api-session-id: "{{ vcenter_session_api_key.json }}" - body: - credentials: "{{ rest_api_credentials }}" - spec: - arguments: -c "rm -f /etc/resolv.conf" - path: /bin/bash - status_code: 201 - body_format: json - validate_certs: "{{ validate_vmware_certs }}" - register: network_config_command - - - name: Including command run check task... - ansible.builtin.include_tasks: command_run_check.yml - - - name: Preparing resolv.conf upload... - ansible.builtin.uri: - url: https://{{ vmware_defaults.hostname }}/api/vcenter/vm/{{ configure_networking_vsphere_vm_id }}/guest/filesystem?action=create - method: POST - headers: - vmware-api-session-id: "{{ vcenter_session_api_key.json }}" - body: - credentials: "{{ rest_api_credentials }}" - spec: - attributes: - overwrite: true - size: "{{ resolv_file_size.stat.size }}" - path: /etc/resolv.conf - status_code: 201 - body_format: json - validate_certs: "{{ validate_vmware_certs }}" - register: resolv_file_upload_prep - - - name: Uploading resolv.conf... - ansible.builtin.uri: - url: "{{ resolv_file_upload_prep.json }}" - method: PUT - headers: - vmware-api-session-id: "{{ vcenter_session_api_key.json }}" - src: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_resolv.conf - status_code: 200 - body_format: json - validate_certs: "{{ validate_vmware_certs }}" - - rescue: - - name: NETWORK CONFIGURATION ERROR - ansible.builtin.fail: - msg: | - Network configuration has failed {{ configure_networking_rescue_count | default(0) }} times. - Check the errors above and {{ inventory_hostname }} host logs for more details. - when: configure_networking_rescue_count | default(0) | int == 3 - - - name: Setting rescue loop count... - ansible.builtin.set_fact: - configure_networking_rescue_count: "{{ configure_networking_rescue_count | default(0) | int + 1 }}" - - - name: Re-including vSphere network configuration tasks... - ansible.builtin.include_tasks: main.yml diff --git a/nova/core/roles/configure_networking/tasks/vsphere/networkd.yml b/nova/core/roles/configure_networking/tasks/vsphere/networkd.yml index e6437bfc0..ebce273a1 100644 --- a/nova/core/roles/configure_networking/tasks/vsphere/networkd.yml +++ b/nova/core/roles/configure_networking/tasks/vsphere/networkd.yml @@ -177,9 +177,7 @@ body: credentials: "{{ rest_api_credentials }}" spec: - arguments: - -c "systemctl enable systemd-networkd && systemctl restart systemd-networkd && - systemctl enable systemd-udev-trigger && systemctl restart systemd-udev-trigger" + arguments: -c "systemctl enable systemd-networkd --now && systemctl enable systemd-udev-trigger --now" path: /bin/bash status_code: 201 body_format: json @@ -230,7 +228,8 @@ body: credentials: "{{ rest_api_credentials }}" spec: - arguments: -c "update-initramfs -u" + # Removal of the dhcpcd-base package to avoid it overwriting the /etc/resolv.conf + arguments: -c "apt purge dhcpcd-base -y && update-initramfs -u" path: /bin/bash status_code: 201 body_format: json diff --git a/nova/core/roles/configure_networking/templates/netplan.yml b/nova/core/roles/configure_networking/templates/netplan.yml index 75730ecb8..12234dabc 100644 --- a/nova/core/roles/configure_networking/templates/netplan.yml +++ b/nova/core/roles/configure_networking/templates/netplan.yml @@ -63,11 +63,7 @@ network: {% endif %} {% endfor %} {% endif %} - {% if extra_ipv4 is defined or extra_ipv6 is defined %} - {{ '# Since one of the interfaces contains multiple IP addresses,' }} - {{ '# The DNS servers are configured in /etc/resolv.conf' }} - {% else %} - {% if ((interface.connection) or (interface.egress)) and (dns_server_combined != []) %} + {% if interface.egress and dns_server_combined != [] %} nameservers: addresses: {% if (dns_servers != []) and (interface.addresses | map(attribute='mode') | regex_search(".*ipv4.*")) %} @@ -84,5 +80,4 @@ network: - {{ domain }} {% endif %} {% endif %} - {% endif %} {% endfor %} diff --git a/nova/core/roles/configure_package_mirrors/tasks/debian.yml b/nova/core/roles/configure_package_mirrors/tasks/debian.yml index 4c8172ffa..f0e197348 100644 --- a/nova/core/roles/configure_package_mirrors/tasks/debian.yml +++ b/nova/core/roles/configure_package_mirrors/tasks/debian.yml @@ -12,10 +12,11 @@ mode: "0644" when: not apt_backup_file.stat.exists -- name: Removing legacy /etc/apt/sources.list file... - ansible.builtin.file: - path: /etc/apt/sources.list - state: absent +- name: Emptying the legacy /etc/apt/sources.list file... + ansible.builtin.copy: + content: "" + dest: /etc/apt/sources.list + mode: "0644" - name: Getting {{ inventory_hostname }} architecture... ansible.builtin.command: dpkg --print-architecture diff --git a/nova/core/roles/configure_package_mirrors/tasks/kali.yml b/nova/core/roles/configure_package_mirrors/tasks/kali.yml index f5aba1323..f169e8949 100644 --- a/nova/core/roles/configure_package_mirrors/tasks/kali.yml +++ b/nova/core/roles/configure_package_mirrors/tasks/kali.yml @@ -36,10 +36,11 @@ mode: "0644" when: not apt_backup_file.stat.exists -- name: Removing legacy /etc/apt/sources.list file... - ansible.builtin.file: - path: /etc/apt/sources.list - state: absent +- name: Emptying the legacy /etc/apt/sources.list file... + ansible.builtin.copy: + content: "" + dest: /etc/apt/sources.list + mode: "0644" - name: Getting {{ inventory_hostname }} architecture... ansible.builtin.command: dpkg --print-architecture diff --git a/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml b/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml index 2ad4531b2..52a3ef044 100644 --- a/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml +++ b/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml @@ -36,10 +36,11 @@ mode: "0644" when: not apt_backup_file.stat.exists -- name: Removing legacy /etc/apt/sources.list file... - ansible.builtin.file: - path: /etc/apt/sources.list - state: absent +- name: Emptying the legacy /etc/apt/sources.list file... + ansible.builtin.copy: + content: "" + dest: /etc/apt/sources.list + mode: "0644" - name: Getting {{ inventory_hostname }} architecture... ansible.builtin.command: dpkg --print-architecture diff --git a/nova/core/roles/coredns/tasks/main.yml b/nova/core/roles/coredns/tasks/main.yml index fa428b178..a380163b2 100644 --- a/nova/core/roles/coredns/tasks/main.yml +++ b/nova/core/roles/coredns/tasks/main.yml @@ -46,7 +46,7 @@ mode: "0644" register: coredns_template_results loop: - - src: corefile + - src: "{{ coredns_corefile }}" dest: "{{ coredns_config_folder }}/Corefile" - src: docker-compose.yml diff --git a/nova/core/roles/docker/tasks/debian_os_default.yml b/nova/core/roles/docker/tasks/debian_os_default.yml index 366873e79..120b01b87 100644 --- a/nova/core/roles/docker/tasks/debian_os_default.yml +++ b/nova/core/roles/docker/tasks/debian_os_default.yml @@ -2,13 +2,6 @@ - name: Installing Docker on Debian based OS... when: ansible_facts.distribution not in docker_non_default_debian_os_list block: - - name: Downloading Docker repo key for {{ ansible_facts.distribution }}... - ansible.builtin.get_url: - url: "{{ docker_apt_proxy | lower }}/gpg" - dest: /etc/apt/trusted.gpg.d/docker.asc - mode: "0644" - timeout: 60 - - name: Getting {{ inventory_hostname }} architecture... ansible.builtin.command: dpkg --print-architecture changed_when: false @@ -23,7 +16,7 @@ uris: "{{ docker_apt_proxy | lower }}" suites: ["{{ ansible_facts.distribution_release | lower }}"] components: [stable] - signed_by: /etc/apt/trusted.gpg.d/docker.asc + signed_by: "{{ docker_apt_proxy | lower }}/gpg" - name: Removing legacy Docker repository... ansible.builtin.file: diff --git a/nova/core/roles/docker/tasks/debian_os_non_default.yml b/nova/core/roles/docker/tasks/debian_os_non_default.yml index 270d25cc8..e2aff6f50 100644 --- a/nova/core/roles/docker/tasks/debian_os_non_default.yml +++ b/nova/core/roles/docker/tasks/debian_os_non_default.yml @@ -1,16 +1,4 @@ --- -- name: Downloading Docker repo key for {{ ansible_facts.distribution }}... - ansible.builtin.get_url: - url: "{{ docker_apt_proxy | lower }}/gpg" - dest: /etc/apt/trusted.gpg.d/docker.asc - mode: "0644" - timeout: 60 - vars: - # Overriding ansible_facts.distribution and ansible_facts.distribution_release to match Debian - ansible_facts: - distribution_release: trixie - distribution: debian - - name: Getting {{ inventory_hostname }} architecture... ansible.builtin.command: dpkg --print-architecture changed_when: false @@ -25,7 +13,7 @@ uris: "{{ docker_apt_proxy | lower }}" suites: ["{{ ansible_facts.distribution_release | lower }}"] components: [stable] - signed_by: /etc/apt/trusted.gpg.d/docker.asc + signed_by: "{{ docker_apt_proxy | lower }}/gpg" vars: # Overriding ansible_facts.distribution and ansible_facts.distribution_release to match Debian ansible_facts: diff --git a/nova/core/roles/gitlab/defaults/main.yml b/nova/core/roles/gitlab/defaults/main.yml index b76ab930e..aa494ab7f 100644 --- a/nova/core/roles/gitlab/defaults/main.yml +++ b/nova/core/roles/gitlab/defaults/main.yml @@ -1,6 +1,6 @@ --- ### gitlab general -gitlab_version: 19.2.1-ee.0 +gitlab_version: 19.2.2-ee.0 gitlab_ssh_port: 10022 # Increase gitlab ssh MaxAuthTries to avoid connection issues for users with more than 6 keys in their SSH agent set to {} to disable gitlab_ssh_max_auth_tries: 20 diff --git a/nova/core/roles/keycloak/defaults/main.yml b/nova/core/roles/keycloak/defaults/main.yml index e152cd487..bed56c6aa 100644 --- a/nova/core/roles/keycloak/defaults/main.yml +++ b/nova/core/roles/keycloak/defaults/main.yml @@ -1,6 +1,6 @@ --- # The Keycloak version and the Keycloak Config CLI version should be compatible -keycloak_version: 26.7.0 +keycloak_version: 26.7.1 # https://github.com/adorsys/keycloak-config-cli # https://hub.docker.com/r/adorsys/keycloak-config-cli/tags diff --git a/nova/core/roles/machine_operations/defaults/main.yml b/nova/core/roles/machine_operations/defaults/main.yml index 08830dab5..636b02904 100644 --- a/nova/core/roles/machine_operations/defaults/main.yml +++ b/nova/core/roles/machine_operations/defaults/main.yml @@ -195,6 +195,10 @@ machine_operations_proxmox_hotplug: - network - usb +# Proxmox VM disk type SSD +# Set to false when using spinning disks +machine_operations_proxmox_disk_ssd: true + # Proxmox VM default disk machine_operations_proxmox_os_disk: scsi0 diff --git a/nova/core/roles/machine_operations/tasks/aws/create.yml b/nova/core/roles/machine_operations/tasks/aws/create.yml index 85e6a5890..6e7bb3d48 100644 --- a/nova/core/roles/machine_operations/tasks/aws/create.yml +++ b/nova/core/roles/machine_operations/tasks/aws/create.yml @@ -14,8 +14,7 @@ - name: Checking for the correct deploy mode... ansible.builtin.fail: - msg: | - Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! + msg: Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! when: - fresh_deploy is ansible.builtin.truthy - role_only is ansible.builtin.truthy diff --git a/nova/core/roles/machine_operations/tasks/azure/create.yml b/nova/core/roles/machine_operations/tasks/azure/create.yml index 4c64fb72b..1d35da3d5 100644 --- a/nova/core/roles/machine_operations/tasks/azure/create.yml +++ b/nova/core/roles/machine_operations/tasks/azure/create.yml @@ -6,16 +6,6 @@ Please define it in your project group or host vars when: azure_resource_group is ansible.builtin.falsy -- name: Checking for the correct deploy mode... - ansible.builtin.fail: - msg: | - Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! - when: - - fresh_deploy is ansible.builtin.truthy - - role_only is ansible.builtin.truthy - or role_only_wp is ansible.builtin.truthy - or single_role is defined - # For Windows templates the Administrator account gets restored with post-script since it's not allowed to be used when creating the VM # For Linux templates new user gets added so already using the ansible_deployer_username there - name: Setting template_username value based on OS... diff --git a/nova/core/roles/machine_operations/tasks/azure/main.yml b/nova/core/roles/machine_operations/tasks/azure/main.yml index b1af0893d..211bf3e65 100644 --- a/nova/core/roles/machine_operations/tasks/azure/main.yml +++ b/nova/core/roles/machine_operations/tasks/azure/main.yml @@ -57,6 +57,15 @@ fresh_deploy: true when: azure_vm_info.vms == [] or deploy_mode == "redeploy" + - name: Checking for the correct deploy mode... + ansible.builtin.fail: + msg: Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! + when: + - fresh_deploy is ansible.builtin.truthy + - role_only is ansible.builtin.truthy + or role_only_wp is ansible.builtin.truthy + or single_role is defined + - name: Including {{ custom_vm_name | default(vm_name) }} removal tasks... ansible.builtin.include_tasks: remove.yml when: deploy_mode in ['undeploy', 'redeploy'] diff --git a/nova/core/roles/machine_operations/tasks/google/main.yml b/nova/core/roles/machine_operations/tasks/google/main.yml index bd0569929..758734d16 100644 --- a/nova/core/roles/machine_operations/tasks/google/main.yml +++ b/nova/core/roles/machine_operations/tasks/google/main.yml @@ -35,6 +35,15 @@ fresh_deploy: true when: google_vm_info.resources == [] or deploy_mode == "redeploy" +- name: Checking for the correct deploy mode... + ansible.builtin.fail: + msg: Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! + when: + - fresh_deploy is ansible.builtin.truthy + - role_only is ansible.builtin.truthy + or role_only_wp is ansible.builtin.truthy + or single_role is defined + - name: Including {{ custom_vm_name | default(vm_name) }} removal tasks... ansible.builtin.include_tasks: remove.yml when: deploy_mode in ['undeploy', 'redeploy'] diff --git a/nova/core/roles/machine_operations/tasks/linode/main.yml b/nova/core/roles/machine_operations/tasks/linode/main.yml index 1f1aa1f08..341a711e9 100644 --- a/nova/core/roles/machine_operations/tasks/linode/main.yml +++ b/nova/core/roles/machine_operations/tasks/linode/main.yml @@ -1,8 +1,23 @@ --- -- name: Missing required variables... +- name: MISSING SECRETS ansible.builtin.fail: msg: | - Following variables are required to run this role: + You are missing following secrets: + + {% for variable in required_variables %} + {% if vars[variable] is defined %} + {% set evaluated = lookup('vars', variable) %} + {% if evaluated is ansible.builtin.falsy %} + - {{ variable }} + {% endif %} + {% else %} + - {{ variable }} + {% endif %} + {% endfor %} + + You can add them to your Ansible Vault file or define external lookups in your group_vars/host_vars files. + vars: + required_variables: - linode_api_token when: linode_api_token == {} diff --git a/nova/core/roles/machine_operations/tasks/openstack/main.yml b/nova/core/roles/machine_operations/tasks/openstack/main.yml index d86b06e25..6eae74585 100644 --- a/nova/core/roles/machine_operations/tasks/openstack/main.yml +++ b/nova/core/roles/machine_operations/tasks/openstack/main.yml @@ -1,14 +1,28 @@ --- -- name: Checking for required variables... +- name: MISSING SECRETS ansible.builtin.fail: msg: | - Following variables are required to run this role: + You are missing following secrets: + + {% for variable in required_variables %} + {% if vars[variable] is defined %} + {% set evaluated = lookup('vars', variable) %} + {% if evaluated is ansible.builtin.falsy %} + - {{ variable }} + {% endif %} + {% else %} + - {{ variable }} + {% endif %} + {% endfor %} + + You can add them to your Ansible Vault file or define external lookups in your group_vars/host_vars files. + vars: + required_variables: - openstack_auth_url - openstack_username - openstack_password - openstack_project_id - when: > - openstack_defaults.auth.auth_url is ansible.builtin.falsy + when: openstack_defaults.auth.auth_url is ansible.builtin.falsy or openstack_defaults.auth.username is ansible.builtin.falsy or openstack_defaults.auth.password is ansible.builtin.falsy or openstack_defaults.auth.project_id is ansible.builtin.falsy diff --git a/nova/core/roles/machine_operations/tasks/proxmox/create.yml b/nova/core/roles/machine_operations/tasks/proxmox/create.yml index cf654eab4..2e17ce343 100644 --- a/nova/core/roles/machine_operations/tasks/proxmox/create.yml +++ b/nova/core/roles/machine_operations/tasks/proxmox/create.yml @@ -1,14 +1,4 @@ --- -- name: Checking for the correct deploy mode... - ansible.builtin.fail: - msg: | - "{{ custom_vm_name | default(vm_name) }} Virtual Machine doesn't exist, use the deploy command first!" - when: - - fresh_deploy is ansible.builtin.truthy - - role_only is ansible.builtin.truthy - or role_only_wp is ansible.builtin.truthy - or single_role is defined - - name: Cloning and configuring VM... delegate_to: localhost become: false @@ -155,6 +145,16 @@ delay: 5 when: not machine_operations_proxmox_enable_ha + - name: Configuring {{ custom_vm_name | default(vm_name) }} VM disk... + community.proxmox.proxmox_disk: + discard: true + disk: "{{ machine_operations_proxmox_os_disk }}" + iothread: true + name: "{{ custom_vm_name | default(vm_name) }}" + ssd: "{{ machine_operations_proxmox_disk_ssd }}" + state: present + vmid: "{{ proxmox_vm_exists.proxmox_vms[0].vmid | default(machine_operations_proxmox_vmid) }}" + - name: Resizing {{ custom_vm_name | default(vm_name) }} VM disk... community.proxmox.proxmox_disk: name: "{{ custom_vm_name | default(vm_name) }}" diff --git a/nova/core/roles/machine_operations/tasks/proxmox/main.yml b/nova/core/roles/machine_operations/tasks/proxmox/main.yml index 0339a5972..3505ab61f 100644 --- a/nova/core/roles/machine_operations/tasks/proxmox/main.yml +++ b/nova/core/roles/machine_operations/tasks/proxmox/main.yml @@ -1,13 +1,27 @@ --- -- name: Checking for required variables... +- name: MISSING SECRETS ansible.builtin.fail: msg: | - Following variables are required to run this role: + You are missing following secrets: + + {% for variable in required_variables %} + {% if vars[variable] is defined %} + {% set evaluated = lookup('vars', variable) %} + {% if evaluated is ansible.builtin.falsy %} + - {{ variable }} + {% endif %} + {% else %} + - {{ variable }} + {% endif %} + {% endfor %} + + You can add them to your Ansible Vault file or define external lookups in your group_vars/host_vars files. + vars: + required_variables: - proxmox_api_user - proxmox_api_token_id - proxmox_api_token_secret - when: > - proxmox_defaults.api_user is ansible.builtin.falsy + when: proxmox_defaults.api_user is ansible.builtin.falsy or proxmox_defaults.api_token_id is ansible.builtin.falsy or proxmox_defaults.api_token_secret is ansible.builtin.falsy @@ -26,6 +40,15 @@ fresh_deploy: true when: proxmox_vm_exists.proxmox_vms == [] or deploy_mode == "redeploy" +- name: Checking for the correct deploy mode... + ansible.builtin.fail: + msg: Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! + when: + - fresh_deploy is ansible.builtin.truthy + - role_only is ansible.builtin.truthy + or role_only_wp is ansible.builtin.truthy + or single_role is defined + - name: Including {{ custom_vm_name | default(vm_name) }} removal tasks... ansible.builtin.include_tasks: remove.yml when: deploy_mode in ['undeploy', 'redeploy'] diff --git a/nova/core/roles/machine_operations/tasks/vsphere/main.yml b/nova/core/roles/machine_operations/tasks/vsphere/main.yml index 9b806841b..292e4960a 100644 --- a/nova/core/roles/machine_operations/tasks/vsphere/main.yml +++ b/nova/core/roles/machine_operations/tasks/vsphere/main.yml @@ -1,8 +1,23 @@ --- -- name: Missing required variables... +- name: MISSING SECRETS ansible.builtin.fail: msg: | - Following variables are required to run this role: + You are missing following secrets: + + {% for variable in required_variables %} + {% if vars[variable] is defined %} + {% set evaluated = lookup('vars', variable) %} + {% if evaluated is ansible.builtin.falsy %} + - {{ variable }} + {% endif %} + {% else %} + - {{ variable }} + {% endif %} + {% endfor %} + + You can add them to your Ansible Vault file or define external lookups in your group_vars/host_vars files. + vars: + required_variables: - vcenter_hostname - vcenter_username or project_deployer_username or deployer_username - vcenter_password or project_deployer_password or deployer_password @@ -24,8 +39,7 @@ - name: Checking for the correct deploy mode... ansible.builtin.fail: - msg: | - Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! + msg: Virtual Machine {{ custom_vm_name | default(vm_name) }} doesn't exist, use the deploy command first! when: - fresh_deploy is ansible.builtin.truthy - role_only is ansible.builtin.truthy diff --git a/nova/core/roles/monolith/defaults/main.yml b/nova/core/roles/monolith/defaults/main.yml index 713f2805c..598f2d8d3 100644 --- a/nova/core/roles/monolith/defaults/main.yml +++ b/nova/core/roles/monolith/defaults/main.yml @@ -57,7 +57,7 @@ monolith_default_caddy_servers: caddy_server_certificate_key_path: /srv/certs/{{ monolith_providentia_certificate_file_name | default(fqdn + '_key') }}.crt caddy_server_listen_addresses: - "{{ monolith_providentia_fqdn }}" - caddy_server_reverse_proxy_to_address: http://providentia-web:3000 + caddy_server_reverse_proxy_to_address: http://providentia-web-1:3000 - caddy_server_name: "{{ monolith_nexus_fqdn }}" caddy_server_certificate_path: /srv/certs/{{ monolith_nexus_certificate_file_name | default(fqdn + '_fullchain') }}.crt diff --git a/nova/core/roles/monolith/tasks/providentia.yml b/nova/core/roles/monolith/tasks/providentia.yml index 2f24c93a9..a93017217 100644 --- a/nova/core/roles/monolith/tasks/providentia.yml +++ b/nova/core/roles/monolith/tasks/providentia.yml @@ -7,6 +7,8 @@ secrets_vault_engine_path: monolith secrets_vault_secrets_path: providentia secrets: + - key: "{{ inventory_hostname }}_providentia_superuser_password" + value: "{{ lookup('password', '/dev/null length=32 chars=hexdigits') }}" - key: "{{ inventory_hostname }}_providentia_db_password" value: "{{ lookup('password', '/dev/null length=32 chars=hexdigits') }}" @@ -17,14 +19,17 @@ # Providentia providentia_install_docker: false providentia_external_docker_network: true - providentia_builtin_keycloak: false + providentia_builtin_sso: false providentia_builtin_reverse_proxy: false providentia_app_fqdn: "{{ monolith_providentia_fqdn }}" secrets_vault_engine_path: monolith secrets_vault_secrets_path: providentia - providentia_builtin_database_db_password: + providentia_builtin_database_app_password: "{{ lookup('community.hashi_vault.hashi_vault', vault_lookup_fragment + 'secret=' + secrets_vault_engine_path + '/data/' + secrets_vault_secrets_path + ':' + inventory_hostname + '_providentia_db_password') }}" + providentia_builtin_database_superuser_password: + "{{ lookup('community.hashi_vault.hashi_vault', vault_lookup_fragment + 'secret=' + secrets_vault_engine_path + + '/data/' + secrets_vault_secrets_path + ':' + inventory_hostname + '_providentia_superuser_password') }}" # SSO providentia_oidc_issuer: https://{{ monolith_keycloak_fqdn }}/realms/Apps diff --git a/nova/core/roles/nexus/defaults/main.yml b/nova/core/roles/nexus/defaults/main.yml index 0f363a82d..b26c967d2 100644 --- a/nova/core/roles/nexus/defaults/main.yml +++ b/nova/core/roles/nexus/defaults/main.yml @@ -9,7 +9,7 @@ nexus_configure_ldap: false # Set to true to also configure LDAP after installat nexus_create_repos: false # Set to true to also create default repositories after installation nexus_allow_anonymous_access: true # Set to false to disable anonymous access nexus_active_encryption_key_id: Primary Encryption Key # Name of the active encryption key that comes with this role -nexus_version: 3.94.1 # Nexus version to install +nexus_version: 3.95.0 # Nexus version to install # Default is the built-in Nexus admin user. If set to a different value than admin, the role will create the user. nexus_admin_username: admin diff --git a/nova/core/roles/providentia/README.md b/nova/core/roles/providentia/README.md index a3e9cf743..ff5f5ce81 100644 --- a/nova/core/roles/providentia/README.md +++ b/nova/core/roles/providentia/README.md @@ -5,6 +5,7 @@ This is a role for installing [Providentia](https://github.com/ClarifiedSecurity ## Requirements - Tested on Ubuntu 22.04 but should work on any Debian based system. +- Minimum supported Providentia version: **v25.4.0** ## Role Variables @@ -14,16 +15,22 @@ Required variables: - `providentia_app_fqdn` - which DNS name will be used for the application -If included, the keycloak will be using `keycloak.{{ providentia_app_fqdn }}` as its FQDN. +If included, the SSO will be using `sso-{{ providentia_app_fqdn }}` as its default FQDN. ## Dependencies - Depends on Docker and Docker Compose being installed on the host. By default, [nova.core.docker](https://github.com/novateams/nova.core/tree/main/nova/core/roles/docker) role is included, this can be disabled by setting `providentia_install_docker` to false. -- Certificates for reverse proxy, if used with TLS +- (Optional) Certificates for reverse proxy, if used with TLS, uses self-signed certificates if not provided ## Notes -The builtin Keycloak is _not_ secure by default: it is configured with HTTP (as Providentia does not trust self-signed certificates) by default and very weak passwords. This can and should be altered in real deployments! +The builtin SSO is _not_ secure by default and is meant as a placeholder for a real SSO setup + +- it is configured to work with HTTP (as Providentia does not trust self-signed certificates) +- the accounts created are described on [main repository page](https://github.com/ClarifiedSecurity/Providentia#demo-credentials) +- it allows redirection to any origin + +This can and should be altered in real deployments! By default, the prebuilt image will be pulled from github - setting `providentia_deploy_branch` variable will clone the repository and build the image on host instead. @@ -57,7 +64,7 @@ By default, the prebuilt image will be pulled from github - setting `providentia providentia_builtin_reverse_proxy_tls_pregenerated_cert: "/srv/certs/providentia.example.com_fullchain.crt" providentia_builtin_reverse_proxy_tls_pregenerated_key: "/srv/certs/providentia.example.com_key.crt" - providentia_builtin_keycloak: false + providentia_builtin_sso: false providentia_oidc_issuer: https://keycloak.example.com/realms/Providentia providentia_oidc_client_id: ProvidentiaLive providentia_oidc_client_secret: 12345 diff --git a/nova/core/roles/providentia/defaults/main.yml b/nova/core/roles/providentia/defaults/main.yml index e8df75847..868008b6e 100644 --- a/nova/core/roles/providentia/defaults/main.yml +++ b/nova/core/roles/providentia/defaults/main.yml @@ -13,17 +13,28 @@ providentia_threads_per_process: 5 providentia_concurrent_processes: "{{ ansible_facts.processor_vcpus }}" # dependencies -providentia_builtin_keycloak: true # Should keycloak instance be deployed with Providentia +providentia_builtin_sso: true # Should SSO instance be deployed with Providentia providentia_builtin_database: true # Should postgres instance be deployed with Providentia providentia_builtin_reverse_proxy: true # Should a reverse proxy be deployed with Providentia -# dependencies config -providentia_builtin_keycloak_fqdn: keycloak.{{ providentia_app_fqdn }} # The FQDN for builtin keycloak instance -providentia_builtin_keycloak_admin_password: secret # builtin keycloak admin user password -providentia_builtin_database_db_password: secret # builtin keycloak db credentials (defined in providentia repo) +# dependencies config (SSO) +providentia_builtin_sso_fqdn: sso-{{ providentia_app_fqdn }} # The FQDN for builtin sso instance +providentia_builtin_sso_admin_password: Password1! # builtin sso admin user password +providentia_builtin_sso_database_name: zitadel # The name for the builtin sso database +providentia_builtin_sso_database_user: zitadel_user # The user for the builtin sso database +providentia_builtin_sso_database_password: secret # The password for the builtin sso database + +providentia_builtin_database_image: postgres:15-alpine +providentia_builtin_database_superuser: postgres # postgres superuser +providentia_builtin_database_superuser_password: secret # postgres superuser password +providentia_builtin_database_name: providentia +providentia_builtin_database_app_user: providentia_user # dedicated providentia database user +providentia_builtin_database_app_password: secret # dedicated providentia database password + providentia_builtin_reverse_proxy_tls_mode: selfsigned # one of `selfsigned` or `pregenerated` -providentia_builtin_reverse_proxy_tls_pregenerated_cert: # path to TLS cert on host -providentia_builtin_reverse_proxy_tls_pregenerated_key: # path to TLS key on host +providentia_builtin_reverse_proxy_tls_certificates_folder: /srv/certs # folder where the self-signed or pregenerated certificates are stored +providentia_builtin_reverse_proxy_tls_pregenerated_cert: /srv/certs/cert.pem +providentia_builtin_reverse_proxy_tls_pregenerated_key: /srv/certs/key.pem # deployment config # if providentia_deploy_branch is set, the code is cloned from github and image will be built on host @@ -37,7 +48,9 @@ providentia_install_dir_git: /srv/providentia.git # path where git configuration providentia_app_fqdn: "{{ fqdn }}" # fqdn for deployed instance # full database URL used by the app, defaults to builtin postgres -providentia_db_url: postgres://providentia:{{ providentia_builtin_database_db_password }}@postgresql/providentia?pool=5 +providentia_db_url: + postgres://{{ providentia_builtin_database_app_user }}:{{ providentia_builtin_database_app_password + }}@postgresql/{{ providentia_builtin_database_name }}?pool=5 providentia_sentry_dsn: # Sentry exception tracking endpoint providentia_sentry_env: production # Sentry production value @@ -49,29 +62,31 @@ providentia_enabled_features: [] # app config (SSO) # The URL for OpenID Connect endpoint (.well-known should be under this) -providentia_oidc_issuer: "http://{{ providentia_builtin_keycloak_fqdn }}/realms/Providentia" +providentia_oidc_issuer: "https://{{ providentia_builtin_sso_fqdn }}" -providentia_auth_mode: resource_access # One of `resource_access` or `scope`, specifying where in access token the authorization fields are located providentia_oidc_client_id: Providentia # The client ID for OpenID Connect providentia_oidc_client_secret: 00000000-0000-0000-0000-000000000000 # The client secret for OpenID Connect (depending on configuration) -providentia_oidc_extra_scopes: "" # list of extra scopes to request from OpenID Connect, eg. "profile email" +providentia_oidc_extra_scopes: "profile,email" # comma separated list of extra scopes to request from OpenID Connect -# The claim in the OpenID Connect token that contains the roles, defaults to `resource_access.{{ providentia_oidc_client_id }}.roles` for Keycloak -providentia_oidc_authorization_roles_claim: resource_access.{{ providentia_oidc_client_id }}.roles +# The claim in the OpenID Connect token that contains the roles, +# defaults to Zitadel default value when builtin SSO included, old Keycloak pattern if not +providentia_oidc_authorization_roles_claim: + "{{ providentia_builtin_sso | ternary('urn:zitadel:iam:org:project:roles', 'resource_access.' + ~ providentia_oidc_client_id ~ '.roles') }}" ###################### # Permissions groups # ###################### # Permissions groups are used to control access to Providentia. -# If Keycloak is used with Active Directory (AD) as the user federation provider these will be the AD group names. +# If SSO is used with Active Directory (AD) as the user federation provider these will be the AD group names. # The group is required login to Providentia # This group must be assigned to all users need to login to Providentia, even members of the providentia_administrators_group -providentia_login_group: Providentia_User +providentia_login_group: "{{ providentia_builtin_sso | ternary('User', 'Providentia_User') }}" # The group that gives full administrator access to Providentia -providentia_administrators_group: Providentia_Super_Admin +providentia_administrators_group: "{{ providentia_builtin_sso | ternary('Super_Admin', 'Providentia_Super_Admin') }}" # The group that allows users to create environments in Providentia -providentia_environment_creator_group: Providentia_Environment_Creator +providentia_environment_creator_group: "{{ providentia_builtin_sso | ternary('Environment_Creator', 'Providentia_Environment_Creator') }}" diff --git a/nova/core/roles/providentia/tasks/certs.yml b/nova/core/roles/providentia/tasks/certs.yml new file mode 100644 index 000000000..86af14284 --- /dev/null +++ b/nova/core/roles/providentia/tasks/certs.yml @@ -0,0 +1,53 @@ +--- +- name: Making sure that {{ providentia_builtin_reverse_proxy_tls_certificates_folder }} folder exists... + ansible.builtin.file: + path: "{{ providentia_builtin_reverse_proxy_tls_certificates_folder }}" + state: directory + recurse: true + +- name: Checking for existing self-signed certificate... + ansible.builtin.stat: + path: "{{ providentia_builtin_reverse_proxy_tls_pregenerated_cert }}" + register: certificate + +- name: Getting info about existing self-signed certificate... + when: certificate.stat.exists + community.crypto.x509_certificate_info: + path: "{{ providentia_builtin_reverse_proxy_tls_pregenerated_cert }}" + valid_at: + certificate_valid: "+20d" + register: certificate_is_valid + +- name: Generating self-signed certificate... + when: (not certificate.stat.exists or not certificate_is_valid.valid_at.certificate_valid) + block: + - name: Generating selfsigned private key... + community.crypto.openssl_privatekey: + path: "{{ providentia_builtin_reverse_proxy_tls_pregenerated_key }}" + curve: secp256r1 + type: ECC + + - name: Generating selfsigned CSR... + community.crypto.openssl_csr: + path: /tmp/selfsigned_{{ providentia_app_fqdn }}.csr + privatekey_path: "{{ providentia_builtin_reverse_proxy_tls_pregenerated_key }}" + common_name: "{{ providentia_app_fqdn }}" + subject_alt_name: "DNS:{{ providentia_app_fqdn }},DNS:{{ providentia_builtin_sso_fqdn }}" + + - name: Signing selfsigned CSR... + community.crypto.x509_certificate: + path: "{{ providentia_builtin_reverse_proxy_tls_pregenerated_cert }}" + privatekey_path: "{{ providentia_builtin_reverse_proxy_tls_pregenerated_key }}" + csr_path: /tmp/selfsigned_{{ providentia_app_fqdn }}.csr + provider: selfsigned + + - name: Trust newly generated cert + ansible.builtin.copy: + remote_src: true + src: "{{ providentia_builtin_reverse_proxy_tls_pregenerated_cert }}" + dest: /usr/local/share/ca-certificates/providentia_selfsigned.crt + mode: "0644" + + - name: Update CA store + ansible.builtin.command: update-ca-certificates + changed_when: true diff --git a/nova/core/roles/providentia/tasks/clone_and_build.yml b/nova/core/roles/providentia/tasks/clone_and_build.yml index cf31ab4b7..e310d92f5 100644 --- a/nova/core/roles/providentia/tasks/clone_and_build.yml +++ b/nova/core/roles/providentia/tasks/clone_and_build.yml @@ -1,14 +1,8 @@ --- -- name: Install host dependencies - ansible.builtin.package: - name: - - git - - make - -- name: Check if previous installation is prebuilt +- name: Check if previous installation exists ansible.builtin.stat: - path: "{{ providentia_install_dir }}/docker-compose.yml" - register: providentia_prebuilt_compose_file + path: "{{ providentia_install_dir }}" + register: providentia_dir - name: Stop previous installation community.docker.docker_compose_v2: @@ -16,26 +10,15 @@ state: absent build: never wait: true - when: providentia_prebuilt_compose_file.stat.exists + when: providentia_dir.stat.exists -- name: Clear previous installation dir +- name: Removing following old installation folders... ansible.builtin.file: - path: "{{ providentia_install_dir }}" + path: "{{ item }}" state: absent - when: providentia_prebuilt_compose_file.stat.exists - -- name: Check for leftover git dir - ansible.builtin.stat: - path: "{{ providentia_install_dir_git }}" - register: providentia_pre_existing_git_dir - -- name: Clear previous git dir - ansible.builtin.file: - path: "{{ providentia_install_dir_git }}" - state: absent - when: - - providentia_prebuilt_compose_file.stat.exists - - providentia_pre_existing_git_dir.stat.exists + loop: + - "{{ providentia_install_dir }}" + - "{{ providentia_install_dir_git }}" - name: Clone Providentia repo ansible.builtin.git: @@ -46,39 +29,45 @@ force: true separate_git_dir: "{{ providentia_install_dir_git }}" -- name: Test if using newer image with entrypoint set on image - ansible.builtin.command: "grep ENTRYPOINT {{ providentia_install_dir }}/Dockerfile" +- name: Get current git tag # noqa: command-instead-of-module + ansible.builtin.command: + cmd: git describe --tags --abbrev=0 + chdir: "{{ providentia_install_dir }}" changed_when: false - ignore_errors: true - register: entrypoint_present + register: git_tag + +- name: Use latest support files + ansible.builtin.include_tasks: update_support_files.yml + +- name: Parse the semantic version git tag + ansible.builtin.set_fact: + providentia_worker_needed: "{{ providentia_version_major | int > 26 and providentia_version_minor | int >= 2 }}" + vars: + version_label: "{{ git_tag.stdout }}" + providentia_version_major: "{{ version_label | regex_search('v?(\\d+)\\.', '\\1') | int }}" + providentia_version_minor: "{{ version_label | regex_search('\\.(\\d+)\\.', '\\1') | int }}" + providentia_version_patch: "{{ version_label | regex_search('\\.(\\d+)(?:[-+]|$)', '\\1') | int }}" -- name: Template Providentia configuration +- name: Templating configuration ansible.builtin.template: - src: "{{ conf.src }}" - dest: "{{ providentia_install_dir }}/{{ conf.dest | default(conf.src) }}" + src: "{{ item }}" + dest: "{{ providentia_install_dir }}/" lstrip_blocks: true mode: "0644" - loop_control: - loop_var: conf loop: - - src: docker-compose.yml - - src: initdb_keycloak.sql - - src: keycloak-config.yml - - src: Makefile - vars: - cmd_and_entrypoint_needed: "{{ entrypoint_present.rc != 0 }}" - -- name: Replace app URI-s in keycloak config - ansible.builtin.replace: - path: "{{ providentia_install_dir }}/docker/prod/providentia.yaml" - regexp: 'providentia\.localhost' - replace: "{{ providentia_app_fqdn }}" - when: providentia_builtin_keycloak + - compose.yaml + - features.yml + - .env + - Makefile -- name: Update version file - ansible.builtin.shell: - cmd: "git describe --tags >CURRENT_VERSION" +- name: Update version file # noqa: command-instead-of-module + ansible.builtin.command: + cmd: git describe --tags >CURRENT_VERSION chdir: "{{ providentia_install_dir }}" + changed_when: false + +- name: Include db migration + ansible.builtin.include_tasks: migrate_db.yml - name: Start Providentia containers community.docker.docker_compose_v2: diff --git a/nova/core/roles/providentia/tasks/main.yml b/nova/core/roles/providentia/tasks/main.yml index b7feb6642..9187f7724 100644 --- a/nova/core/roles/providentia/tasks/main.yml +++ b/nova/core/roles/providentia/tasks/main.yml @@ -11,23 +11,40 @@ {% endfor %} For new variables refer to the Permissions groups section in defaults: - https://github.com/novateams/nova.core/blob/main/nova/core/roles/providentia/defaults/main.yml + https://github.com/ClarifiedSecurity/nova.core/blob/main/nova/core/roles/providentia/defaults/main.yml vars: deprecated_variables: - providentia_resource_prefix - providentia_resource_login - providentia_resource_superadmin - providentia_resource_env_creator + - providentia_builtin_keycloak + - providentia_builtin_database_db_password when: providentia_resource_prefix is defined or providentia_resource_login is defined or providentia_resource_superadmin is defined or providentia_resource_env_creator is defined + or providentia_builtin_keycloak is defined + or providentia_builtin_database_db_password is defined - name: Installing docker ansible.builtin.include_role: name: nova.core.docker when: providentia_install_docker +- name: Install host dependencies + ansible.builtin.package: + name: + - git + - make + - python3-cryptography + +- name: Generate selfsigned certificates + when: + - providentia_builtin_reverse_proxy_tls_mode == 'selfsigned' + - providentia_builtin_reverse_proxy + ansible.builtin.include_tasks: certs.yml + - name: Including clone and build tasks... ansible.builtin.include_tasks: clone_and_build.yml when: providentia_deploy_branch is truthy diff --git a/nova/core/roles/providentia/tasks/migrate_db.yml b/nova/core/roles/providentia/tasks/migrate_db.yml new file mode 100644 index 000000000..c680169a3 --- /dev/null +++ b/nova/core/roles/providentia/tasks/migrate_db.yml @@ -0,0 +1,30 @@ +--- +- name: Check if old docker volume exists + community.docker.docker_volume_info: + name: providentia_database_storage + register: old_volume + +- name: Check if new docker volume exists + community.docker.docker_volume_info: + name: providentia_postgresql_data + register: new_volume + +- name: Run the migration script + when: + - old_volume.exists + - not new_volume.exists + block: + - name: Migrate to new postgresql setup + ansible.builtin.template: + src: db_migrator.sh + dest: "{{ providentia_install_dir }}/db_migrator.sh" + mode: "0755" + + - name: Run the postgresql migrator + ansible.builtin.command: bash "{{ providentia_install_dir }}/db_migrator.sh" + changed_when: true + + - name: Remove the migrator script + ansible.builtin.file: + path: "{{ providentia_install_dir }}/db_migrator.sh" + state: absent diff --git a/nova/core/roles/providentia/tasks/prebuilt_image.yml b/nova/core/roles/providentia/tasks/prebuilt_image.yml index 5a014a67e..63c215e31 100644 --- a/nova/core/roles/providentia/tasks/prebuilt_image.yml +++ b/nova/core/roles/providentia/tasks/prebuilt_image.yml @@ -1,50 +1,64 @@ --- -- name: Check if previous installation is cloned +- name: Check if previous installation exists ansible.builtin.stat: - path: /{{ providentia_install_dir }}/CURRENT_VERSION - register: providentia_current_version_stat + path: "{{ providentia_install_dir }}/docker-compose.yml" + register: providentia_old_docker_compose - name: Stop previous installation - community.general.make: - chdir: "{{ providentia_install_dir }}" - target: stop - when: providentia_current_version_stat.stat.exists + when: providentia_old_docker_compose.stat.exists + community.docker.docker_compose_v2: + project_src: "{{ providentia_install_dir }}" + state: absent + remove_orphans: true + wait: true + +- name: Use latest support files + ansible.builtin.include_tasks: update_support_files.yml -- name: Pulling ghcr.io/clarifiedsecurity/providentia:{{ providentia_image_version }} +- name: Pull the app image community.docker.docker_image_pull: - name: ghcr.io/clarifiedsecurity/providentia:{{ providentia_image_version }} + name: ghcr.io/clarifiedsecurity/providentia + tag: "{{ providentia_image_version }}" -- name: Get image info +- name: Get app image info community.docker.docker_image_info: name: ghcr.io/clarifiedsecurity/providentia:{{ providentia_image_version }} - register: providentia_image + register: image_info -- name: Template Providentia configuration - ansible.builtin.template: - src: "{{ item }}" - dest: "{{ providentia_install_dir }}/" - lstrip_blocks: true - mode: "0644" - loop: - - docker-compose.yml - - Makefile +- name: Parse the semantic version of container image + ansible.builtin.set_fact: + providentia_worker_needed: "{{ providentia_version_major | int > 26 and providentia_version_minor | int >= 2 }}" vars: - cmd_and_entrypoint_needed: "{{ providentia_image.images[0].Config.Entrypoint is ansible.builtin.falsy }}" + version_label: "{{ image_info.images[0].Config.Labels['org.opencontainers.image.version'] }}" + providentia_version_major: "{{ version_label | regex_search('v?(\\d+)\\.', '\\1') | int }}" + providentia_version_minor: "{{ version_label | regex_search('\\.(\\d+)\\.', '\\1') | int }}" + providentia_version_patch: "{{ version_label | regex_search('\\.(\\d+)(?:[-+]|$)', '\\1') | int }}" -- name: Template builtin keycloak configuration +- name: Templating configuration ansible.builtin.template: src: "{{ item }}" dest: "{{ providentia_install_dir }}/" lstrip_blocks: true mode: "0644" loop: - - initdb_keycloak.sql - - keycloak-config.yml - when: providentia_builtin_keycloak + - compose.yaml + - features.yml + - .env + - Makefile + +- name: Include db migration + ansible.builtin.include_tasks: migrate_db.yml + +- name: Remove old docker compose config + when: providentia_old_docker_compose.stat.exists + ansible.builtin.file: + path: "{{ providentia_install_dir }}/docker-compose.yml" + state: absent - name: Composing Providentia... community.docker.docker_compose_v2: project_src: "{{ providentia_install_dir }}" state: present + remove_orphans: true build: never wait: true diff --git a/nova/core/roles/providentia/tasks/update_support_files.yml b/nova/core/roles/providentia/tasks/update_support_files.yml new file mode 100644 index 000000000..1911e0309 --- /dev/null +++ b/nova/core/roles/providentia/tasks/update_support_files.yml @@ -0,0 +1,45 @@ +--- +- name: Ensure directories exist + ansible.builtin.file: + path: "{{ providentia_install_dir }}/support" + state: directory + recurse: true + +- name: Downloading docker compose support (app) + ansible.builtin.get_url: + url: https://raw.githubusercontent.com/ClarifiedSecurity/Providentia/refs/heads/main/support/compose-web.yml + dest: "{{ providentia_install_dir }}/support/compose-web.yml" + mode: "0644" + force: true + +- name: Downloading docker compose support (database) + when: providentia_builtin_database + ansible.builtin.get_url: + url: https://raw.githubusercontent.com/ClarifiedSecurity/Providentia/refs/heads/main/support/compose-postgresql.yml + dest: "{{ providentia_install_dir }}/support/compose-postgresql.yml" + mode: "0644" + force: true + +- name: Downloading docker compose support (SSO) + when: providentia_builtin_sso + ansible.builtin.get_url: + url: https://raw.githubusercontent.com/ClarifiedSecurity/Providentia/refs/heads/main/support/compose-zitadel.yml + dest: "{{ providentia_install_dir }}/support/compose-zitadel.yml" + mode: "0644" + force: true + +- name: Downloading SSO configuration template + when: providentia_builtin_sso + ansible.builtin.get_url: + url: https://raw.githubusercontent.com/ClarifiedSecurity/Providentia/refs/heads/main/support/zitadel.tf + dest: "{{ providentia_install_dir }}/support/zitadel.tf" + mode: "0644" + force: true + +- name: Downloading docker compose support (reverse proxy) + when: providentia_builtin_reverse_proxy + ansible.builtin.get_url: + url: https://raw.githubusercontent.com/ClarifiedSecurity/Providentia/refs/heads/main/support/compose-caddy.yml + dest: "{{ providentia_install_dir }}/support/compose-caddy.yml" + mode: "0644" + force: true diff --git a/nova/core/roles/providentia/templates/.env b/nova/core/roles/providentia/templates/.env new file mode 100644 index 000000000..1dd305cc8 --- /dev/null +++ b/nova/core/roles/providentia/templates/.env @@ -0,0 +1,50 @@ +# rails config +RAILS_ENV=production +SECRET_KEY_BASE={{ providentia_secret_key_base }} +RAILS_MAX_THREADS={{ providentia_threads_per_process }} +WEB_CONCURRENCY={{ providentia_concurrent_processes }} +DATABASE_URL={{ providentia_db_url }} +BASE_URI=https://{{ providentia_app_fqdn }} +PROVIDENTIA_DOMAIN={{ providentia_app_fqdn }} + +# rails oidc config +OIDC_ISSUER={{ providentia_oidc_issuer }} +# client id and secret added dynamically for builtin SSO +{% if not providentia_builtin_sso %} +OIDC_CLIENT_ID={{ providentia_oidc_client_id }} +OIDC_CLIENT_SECRET={{ providentia_oidc_client_secret }} +{% endif %} +OIDC_EXTRA_SCOPES={{ providentia_oidc_extra_scopes }} +OIDC_AUTHORIZATION_ROLES_CLAIM={{ providentia_oidc_authorization_roles_claim }} +OIDC_RESOURCE_LOGIN={{ providentia_login_group }} +OIDC_RESOURCE_SUPERADMIN={{ providentia_administrators_group }} +OIDC_RESOURCE_ENV_CREATOR={{ providentia_environment_creator_group }} + +{% if providentia_sentry_dsn %} +# rails error tracking +SENTRY_DSN={{ providentia_sentry_dsn }} +SENTRY_ENV={{ providentia_sentry_env }} +{% endif %} + +{% if providentia_builtin_database %} +# database config +POSTGRES_IMAGE={{ providentia_builtin_database_image }} +POSTGRES_USER={{ providentia_builtin_database_superuser }} +POSTGRES_PASSWORD={{ providentia_builtin_database_superuser_password }} +PROVIDENTIA_DB_NAME={{ providentia_builtin_database_name }} +PROVIDENTIA_DB_USER={{ providentia_builtin_database_app_user }} +PROVIDENTIA_DB_PASS={{ providentia_builtin_database_app_password }} +{% endif %} + +{% if providentia_builtin_sso %} +ZITADEL_EXTERNALPORT=443 +ZITADEL_EXTERNALSECURE=true +ZITADEL_TLS_ENABLED=false +# SSO config +# ZITADEL_DEFAULTINSTANCE_DOMAINPOLICY_USERLOGINMUSTBEDOMAIN=true +ZITADEL_DOMAIN={{ providentia_builtin_sso_fqdn }} +ZITADEL_ADMIN_PASSWORD={{ providentia_builtin_sso_admin_password }} +ZITADEL_DB_NAME={{ providentia_builtin_sso_database_name }} +ZITADEL_DB_USER={{ providentia_builtin_sso_database_user }} +ZITADEL_DB_PASS={{ providentia_builtin_sso_database_password }} +{% endif %} diff --git a/nova/core/roles/providentia/templates/.makerc-vars b/nova/core/roles/providentia/templates/.makerc-vars deleted file mode 100644 index 8f1609278..000000000 --- a/nova/core/roles/providentia/templates/.makerc-vars +++ /dev/null @@ -1 +0,0 @@ -SUDO_COMMAND := sudo -E diff --git a/nova/core/roles/providentia/templates/Makefile b/nova/core/roles/providentia/templates/Makefile index 96f761094..2db499c4a 100644 --- a/nova/core/roles/providentia/templates/Makefile +++ b/nova/core/roles/providentia/templates/Makefile @@ -19,8 +19,9 @@ import-db: ## Import database from dump docker compose stop docker compose up -d postgresql --wait docker compose cp $(DUMP) postgresql:/dump.sql - docker compose exec postgresql psql -Uprovidentia -c '\i /dump.sql' - docker compose stop postgresql + docker compose exec postgresql dropdb -U{{ providentia_builtin_database_superuser }} --if-exists {{ providentia_builtin_database_name }} + docker compose exec postgresql createdb -U{{ providentia_builtin_database_superuser }} {{ providentia_builtin_database_name }} + docker compose exec postgresql psql -U{{ providentia_builtin_database_superuser }} {{ providentia_builtin_database_name }} -c '\i /dump.sql' clear-cache: ## Clear cache docker compose exec web bin/rails r 'Rails.cache.clear' diff --git a/nova/core/roles/providentia/templates/compose.yaml b/nova/core/roles/providentia/templates/compose.yaml new file mode 100644 index 000000000..5d2b147c9 --- /dev/null +++ b/nova/core/roles/providentia/templates/compose.yaml @@ -0,0 +1,100 @@ +#jinja2: trim_blocks: True, lstrip_blocks: True +--- +name: providentia + +include: + {% if providentia_builtin_database or providentia_builtin_sso %} + - support/compose-postgresql.yml + {% endif %} + {% if providentia_builtin_sso %} + - support/compose-zitadel.yml + {% endif %} + +services: + web: + extends: + file: support/compose-web.yml + service: web + depends_on: !override + {% if providentia_builtin_database or providentia_builtin_sso %} + postgresql: + condition: service_healthy + {% endif %} + {% if providentia_builtin_sso %} + zitadel-configurator: + condition: service_completed_successfully + {% endif %} + {% if providentia_deploy_branch %} + build: + context: . + dockerfile: Dockerfile + target: production + {% else %} + image: "ghcr.io/clarifiedsecurity/providentia:{{ providentia_image_version }}" + {% endif %} + volumes: !override + - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt + - ./features.yml:/srv/app/config/features.yml + {% if providentia_builtin_sso %} + - dynamic_env:/srv/env + {% endif %} + environment: !override {} + env_file: .env + labels: + caddy.tls: /certs/cert.pem /certs/key.pem + + {% if providentia_worker_needed %} + worker: + extends: + file: support/compose-web.yml + service: worker + depends_on: !override + {% if providentia_builtin_database or providentia_builtin_sso %} + postgresql: + condition: service_healthy + {% endif %} + {% if providentia_deploy_branch %} + build: + context: . + dockerfile: Dockerfile + target: production + {% else %} + image: "ghcr.io/clarifiedsecurity/providentia:{{ providentia_image_version }}" + {% endif %} + volumes: !override + - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt + - ./features.yml:/srv/app/config/features.yml + {% if providentia_builtin_sso %} + - dynamic_env:/srv/env + {% endif %} + environment: !override {} + env_file: .env + {% endif %} + + {% if providentia_builtin_reverse_proxy %} + caddy: + extends: + file: support/compose-caddy.yml + service: caddy + volumes: + - {{ providentia_builtin_reverse_proxy_tls_pregenerated_cert }}:/certs/cert.pem + - {{ providentia_builtin_reverse_proxy_tls_pregenerated_key }}:/certs/key.pem + labels: !override + caddy_0: + caddy_0.local_certs: + {% endif %} + +volumes: + {% if providentia_builtin_reverse_proxy %} + caddy_data: + {% endif %} + {% if providentia_builtin_sso %} + dynamic_env: + {% endif %} + {% if providentia_builtin_database or providentia_builtin_sso %} + database_storage: + {% endif %} + +{% if providentia_external_docker_network %} +{{ providentia_external_docker_network_config | to_nice_yaml(indent=2) }} +{% endif %} diff --git a/nova/core/roles/providentia/templates/db_migrator.sh b/nova/core/roles/providentia/templates/db_migrator.sh new file mode 100644 index 000000000..5dfa4ba8c --- /dev/null +++ b/nova/core/roles/providentia/templates/db_migrator.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -euo pipefail + +docker volume create --name providentia_postgresql_data +docker run --rm -v providentia_database_storage:/from -v providentia_postgresql_data:/to alpine sh -c "apk add --no-cache rsync; rsync -av /from/ /to/" +docker run --rm -d --name providentia_db_migration -v providentia_postgresql_data:/var/lib/postgresql/data {{ providentia_builtin_database_image }} + +until docker exec providentia_db_migration pg_isready -U providentia -d {{ providentia_builtin_database_name }}; do + echo "Waiting for Postgres..." + sleep 1 +done + +docker exec providentia_db_migration createuser -U providentia --superuser tempuser + +docker exec providentia_db_migration psql -U tempuser postgres \ + -c "ALTER USER providentia WITH PASSWORD '{{ providentia_builtin_database_superuser_password }}';" \ + -c "ALTER USER providentia RENAME TO {{ providentia_builtin_database_superuser }};"; + +docker exec providentia_db_migration psql -U {{ providentia_builtin_database_superuser }} {{ providentia_builtin_database_name }} \ + -c "DROP ROLE tempuser;" \ + -c "CREATE USER {{ providentia_builtin_database_app_user }} WITH PASSWORD '{{ providentia_builtin_database_app_password }}';" \ + -c "GRANT CONNECT ON DATABASE {{ providentia_builtin_database_name }} TO {{ providentia_builtin_database_app_user }};" \ + -c "GRANT USAGE ON SCHEMA public TO {{ providentia_builtin_database_app_user }};" \ + -c "ALTER DATABASE {{ providentia_builtin_database_name }} OWNER TO {{ providentia_builtin_database_app_user }};" \ + -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO {{ providentia_builtin_database_app_user }};" \ + -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO {{ providentia_builtin_database_app_user }};" \ + -c "GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO {{ providentia_builtin_database_app_user }};" \ + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO {{ providentia_builtin_database_app_user }};" \ + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO {{ providentia_builtin_database_app_user }};" \ + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO {{ providentia_builtin_database_app_user }};"; + +echo "SELECT 'ALTER TABLE '|| schemaname || '.\"' || tablename ||'\" OWNER TO {{ providentia_builtin_database_app_user }};' FROM pg_tables WHERE NOT schemaname IN ('pg_catalog', 'information_schema') ORDER BY schemaname, tablename; \gexec" | docker exec -i providentia_db_migration psql -U {{ providentia_builtin_database_superuser }} {{ providentia_builtin_database_name }} +echo "SELECT 'ALTER SEQUENCE '|| sequence_schema || '.\"' || sequence_name ||'\" OWNER TO {{ providentia_builtin_database_app_user }};' FROM information_schema.sequences WHERE NOT sequence_schema IN ('pg_catalog', 'information_schema') ORDER BY sequence_schema, sequence_name; \gexec" | docker exec -i providentia_db_migration psql -U {{ providentia_builtin_database_superuser }} {{ providentia_builtin_database_name }} + +docker rm --force providentia_db_migration +docker volume rm providentia_database_storage diff --git a/nova/core/roles/providentia/templates/docker-compose.yml b/nova/core/roles/providentia/templates/docker-compose.yml index 18a1a33c6..80864176a 100644 --- a/nova/core/roles/providentia/templates/docker-compose.yml +++ b/nova/core/roles/providentia/templates/docker-compose.yml @@ -49,7 +49,6 @@ services: - OIDC_RESOURCE_SUPERADMIN={{ providentia_administrators_group }} - OIDC_RESOURCE_ENV_CREATOR={{ providentia_environment_creator_group }} - BASE_URI=https://{{ providentia_app_fqdn }} - - AUTH_MODE={{ providentia_auth_mode }} {% if providentia_sentry_dsn %} - SENTRY_DSN={{ providentia_sentry_dsn }} - SENTRY_ENV={{ providentia_sentry_env }} diff --git a/nova/core/roles/providentia/templates/features.yml b/nova/core/roles/providentia/templates/features.yml new file mode 100644 index 000000000..51b237cca --- /dev/null +++ b/nova/core/roles/providentia/templates/features.yml @@ -0,0 +1,5 @@ +--- +production: + {% for feat in providentia_enabled_features %} + {{ feat }}: true + {% endfor %} diff --git a/nova/core/roles/providentia/templates/initdb_keycloak.sql b/nova/core/roles/providentia/templates/initdb_keycloak.sql deleted file mode 100644 index 28c02af1f..000000000 --- a/nova/core/roles/providentia/templates/initdb_keycloak.sql +++ /dev/null @@ -1,6 +0,0 @@ -\connect postgres -create database bitnami_keycloak; -create role bn_keycloak with encrypted password 'secret' LOGIN; - -\connect bitnami_keycloak -grant all privileges on schema public to bn_keycloak; \ No newline at end of file diff --git a/nova/core/roles/providentia/templates/keycloak-config.yml b/nova/core/roles/providentia/templates/keycloak-config.yml deleted file mode 100644 index 090a08b51..000000000 --- a/nova/core/roles/providentia/templates/keycloak-config.yml +++ /dev/null @@ -1,416 +0,0 @@ -realm: Providentia -enabled: true -roles: - client: - Providentia: - - name: Providentia_TE_GT - composite: false - clientRole: true - attributes: {} - - name: Providentia_TE_RT - composite: false - clientRole: true - attributes: {} - - name: Providentia_TE_Admin - composite: false - clientRole: true - attributes: {} - - name: Providentia_Admin - composite: false - clientRole: true - attributes: {} -groups: [] -clients: - - clientId: Providentia - baseUrl: "https://{{ providentia_app_fqdn }}" - surrogateAuthRequired: false - enabled: true - alwaysDisplayInConsole: false - clientAuthenticatorType: client-secret - secret: 00000000-0000-0000-0000-000000000000 - redirectUris: - - "https://{{ providentia_app_fqdn }}/*" - webOrigins: - - "https://{{ providentia_app_fqdn }}" - notBefore: 0 - bearerOnly: false - consentRequired: false - standardFlowEnabled: true - implicitFlowEnabled: true - directAccessGrantsEnabled: true - serviceAccountsEnabled: false - publicClient: true - frontchannelLogout: false - protocol: openid-connect - attributes: {} - authenticationFlowBindingOverrides: {} - fullScopeAllowed: true - nodeReRegistrationTimeout: -1 - defaultClientScopes: - - profile - - providentia-roles - - email - optionalClientScopes: [] -clientScopes: - - name: profile - description: "OpenID Connect built-in scope: profile" - protocol: openid-connect - attributes: - include.in.token.scope: "true" - display.on.consent.screen: "true" - consent.screen.text: "${profileScopeConsentText}" - protocolMappers: - - name: locale - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: locale - id.token.claim: "true" - access.token.claim: "true" - claim.name: locale - jsonType.label: String - - name: "given name" - protocol: openid-connect - protocolMapper: oidc-usermodel-property-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: firstName - id.token.claim: "true" - access.token.claim: "true" - claim.name: given_name - jsonType.label: String - - name: username - protocol: openid-connect - protocolMapper: oidc-usermodel-property-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: username - id.token.claim: "true" - access.token.claim: "true" - claim.name: preferred_username - jsonType.label: String - - name: "full name" - protocol: openid-connect - protocolMapper: oidc-full-name-mapper - consentRequired: false - config: - id.token.claim: "true" - access.token.claim: "true" - userinfo.token.claim: "true" - - name: "family name" - protocol: openid-connect - protocolMapper: oidc-usermodel-property-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: lastName - id.token.claim: "true" - access.token.claim: "true" - claim.name: family_name - jsonType.label: String - - name: "updated at" - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: updatedAt - id.token.claim: "true" - access.token.claim: "true" - claim.name: updated_at - jsonType.label: String - - name: profile - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: profile - id.token.claim: "true" - access.token.claim: "true" - claim.name: profile - jsonType.label: String - - name: birthdate - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: birthdate - id.token.claim: "true" - access.token.claim: "true" - claim.name: birthdate - jsonType.label: String - - name: picture - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: picture - id.token.claim: "true" - access.token.claim: "true" - claim.name: picture - jsonType.label: String - - name: website - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: website - id.token.claim: "true" - access.token.claim: "true" - claim.name: website - jsonType.label: String - - name: "middle name" - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: middleName - id.token.claim: "true" - access.token.claim: "true" - claim.name: middle_name - jsonType.label: String - - name: nickname - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: nickname - id.token.claim: "true" - access.token.claim: "true" - claim.name: nickname - jsonType.label: String - - name: gender - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: gender - id.token.claim: "true" - access.token.claim: "true" - claim.name: gender - jsonType.label: String - - name: zoneinfo - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: zoneinfo - id.token.claim: "true" - access.token.claim: "true" - claim.name: zoneinfo - jsonType.label: String - - name: email - description: "OpenID Connect built-in scope: email" - protocol: openid-connect - attributes: - include.in.token.scope: "true" - display.on.consent.screen: "true" - consent.screen.text: "${emailScopeConsentText}" - protocolMappers: - - name: email - protocol: openid-connect - protocolMapper: oidc-usermodel-property-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: email - id.token.claim: "true" - access.token.claim: "true" - claim.name: email - jsonType.label: String - - name: "email verified" - protocol: openid-connect - protocolMapper: oidc-usermodel-property-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: emailVerified - id.token.claim: "true" - access.token.claim: "true" - claim.name: email_verified - jsonType.label: boolean - - name: phone - description: "OpenID Connect built-in scope: phone" - protocol: openid-connect - attributes: - include.in.token.scope: "true" - display.on.consent.screen: "true" - consent.screen.text: "${phoneScopeConsentText}" - protocolMappers: - - name: "phone number verified" - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: phoneNumberVerified - id.token.claim: "true" - access.token.claim: "true" - claim.name: phone_number_verified - jsonType.label: boolean - - name: "phone number" - protocol: openid-connect - protocolMapper: oidc-usermodel-attribute-mapper - consentRequired: false - config: - userinfo.token.claim: "true" - user.attribute: phoneNumber - id.token.claim: "true" - access.token.claim: "true" - claim.name: phone_number - jsonType.label: String - - name: web-origins - description: "OpenID Connect scope for add allowed web origins to the access token" - protocol: openid-connect - attributes: - include.in.token.scope: "false" - display.on.consent.screen: "false" - consent.screen.text: "" - protocolMappers: - - name: "allowed web origins" - protocol: openid-connect - protocolMapper: oidc-allowed-origins-mapper - consentRequired: false - config: {} - - name: role_list - description: "SAML role list" - protocol: saml - attributes: - consent.screen.text: "${samlRoleListScopeConsentText}" - display.on.consent.screen: "true" - protocolMappers: - - name: "role list" - protocol: saml - protocolMapper: saml-role-list-mapper - consentRequired: false - config: - single: "false" - attribute.nameformat: Basic - attribute.name: Role - - name: address - description: "OpenID Connect built-in scope: address" - protocol: openid-connect - attributes: - include.in.token.scope: "true" - display.on.consent.screen: "true" - consent.screen.text: "${addressScopeConsentText}" - protocolMappers: - - name: address - protocol: openid-connect - protocolMapper: oidc-address-mapper - consentRequired: false - config: - user.attribute.formatted: formatted - user.attribute.country: country - user.attribute.postal_code: postal_code - userinfo.token.claim: "true" - user.attribute.street: street - id.token.claim: "true" - user.attribute.region: region - access.token.claim: "true" - user.attribute.locality: locality - - name: offline_access - description: "OpenID Connect built-in scope: offline_access" - protocol: openid-connect - attributes: - consent.screen.text: "${offlineAccessScopeConsentText}" - display.on.consent.screen: "true" - - name: providentia-roles - description: "" - protocol: openid-connect - attributes: - include.in.token.scope: "false" - display.on.consent.screen: "true" - consent.screen.text: "${rolesScopeConsentText}" - protocolMappers: - - name: "client roles" - protocol: openid-connect - protocolMapper: oidc-usermodel-client-role-mapper - consentRequired: false - config: - multivalued: "true" - user.attribute: foo - id.token.claim: "true" - access.token.claim: "true" - claim.name: "resource_access.${client_id}.roles" - jsonType.label: String - usermodel.clientRoleMapping.clientId: Providentia - - name: "realm roles" - protocol: openid-connect - protocolMapper: oidc-usermodel-realm-role-mapper - consentRequired: false - config: - multivalued: "true" - user.attribute: foo - id.token.claim: "false" - access.token.claim: "true" - claim.name: realm_access.roles - jsonType.label: String -users: - - username: providentia.admin - email: providentia.admin@localhost - enabled: true - firstName: Providentia - lastName: Admin - clientRoles: - Providentia: - - Providentia_Admin - credentials: - - type: password - value: providentia.admin-pass - - username: providentia.teadmin - email: providentia.teadmin@localhost - enabled: true - firstName: Providentia - lastName: Admin - clientRoles: - Providentia: - - Providentia_TE_Admin - credentials: - - type: password - value: providentia.teadmin-pass - - username: providentia.rt - email: providentia.rt@localhost - enabled: true - firstName: Red - lastName: Team - clientRoles: - Providentia: - - Providentia_TE_GT - - Providentia_TE_RT - credentials: - - type: password - value: providentia.rt-pass - - username: providentia.gt - email: providentia.gt@localhost - enabled: true - firstName: Green - lastName: Team - clientRoles: - Providentia: - - Providentia_TE_GT - credentials: - - type: password - value: providentia.gt-pass -defaultDefaultClientScopes: - - role_list - - profile - - email - - web-origins -defaultOptionalClientScopes: - - offline_access - - address - - phone - - microprofile-jwt diff --git a/nova/core/roles/samba/defaults/main.yml b/nova/core/roles/samba/defaults/main.yml index 159a800e0..034ffac12 100644 --- a/nova/core/roles/samba/defaults/main.yml +++ b/nova/core/roles/samba/defaults/main.yml @@ -20,6 +20,11 @@ samba_domain_disable_password_expiry_days: 0 # Nr of days before how long a user must wait to change their password. samba_domain_minimum_password_age_days: 0 +# The functional level of the domain. This can be set to one of the following values: +# 2000, 2003, 2008, 2008_R2, 2016 +# When left empty, the default functional level will be used for the version of Samba being used that's most likely 2008_R2 +samba_domain_functional_level: {} + ######### # LDAPs # ######### diff --git a/nova/core/roles/samba/tasks/dc.yml b/nova/core/roles/samba/tasks/dc.yml index bde9f3778..0efb06086 100644 --- a/nova/core/roles/samba/tasks/dc.yml +++ b/nova/core/roles/samba/tasks/dc.yml @@ -1,7 +1,11 @@ --- - name: Promoting {{ inventory_hostname }} to domain controller... - ansible.builtin.command: - samba-tool domain join {{ samba_domain_name }} DC --username={{ - samba_domain_name }}\\{{ samba_domain_admin_username }} --password={{ samba_domain_admin_password }} + ansible.builtin.command: > + samba-tool domain join {{ samba_domain_name }} DC \ + --username={{ samba_domain_name }}\\{{ samba_domain_admin_username }} \ + --password={{ samba_domain_admin_password }} + {% if samba_domain_functional_level is ansible.builtin.truthy %} + --option="ad dc functional level = {{ samba_domain_functional_level }}" + {% endif %} changed_when: true when: samba_first_run | default(false) diff --git a/nova/core/roles/samba/tasks/pdc.yml b/nova/core/roles/samba/tasks/pdc.yml index 103c9fbef..d17a98278 100644 --- a/nova/core/roles/samba/tasks/pdc.yml +++ b/nova/core/roles/samba/tasks/pdc.yml @@ -23,6 +23,10 @@ --realm={{ samba_domain_name }} --domain={{ samba_domain_netbios }} --adminpass={{ samba_domain_admin_password }} + {% if samba_domain_functional_level is ansible.builtin.truthy %} + --function-level={{ samba_domain_functional_level }} + --option="ad dc functional level = {{ samba_domain_functional_level }}" + {% endif %} changed_when: true when: samba_first_run | default(false) diff --git a/nova/core/roles/vault/defaults/main.yml b/nova/core/roles/vault/defaults/main.yml index a65c42099..9399ac49c 100644 --- a/nova/core/roles/vault/defaults/main.yml +++ b/nova/core/roles/vault/defaults/main.yml @@ -10,7 +10,7 @@ vault_configure: false # Run the initial default configuration for Vault vault_configure_ldap: false # Configuring LDAP authentication for Vault vault_configuration_uri: "https://{{ fqdn }}" # Defined separately for when configuring multiple Vaults vault_config_folder: /srv/vault -vault_version: 2.0.3 # Default Vault Docker image version +vault_version: 2.0.4 # Default Vault Docker image version vault_container_name: vault vault_server_log_level: debug # Debug gives better info about LDAP login failures and their reasons vault_audit_logging: false # Enable Vault audit logging