feat: reimplement various roles

develop
Dave S. 2 years ago
parent 5878ef2e31
commit eb5feb1fb8
  1. 2
      .gitignore
  2. 49
      all.yml
  3. 7
      group_vars/all.yml
  4. 2
      group_vars/infra.yml.template
  5. 7
      mappings.yml
  6. 15
      roles/common/tasks/main.yml
  7. 19
      roles/common/templates/dropbear_init.j2
  8. 2
      roles/container/defaults/main.yml
  9. 99
      roles/container/tasks/main.yml
  10. 8
      roles/coredns/defaults/main.yml
  11. 27
      roles/coredns/tasks/add.yml
  12. 21
      roles/coredns/tasks/add_record.yml
  13. 21
      roles/coredns/tasks/add_records.yml
  14. 14
      roles/coredns/tasks/increase_serial.yml
  15. 13
      roles/coredns/tasks/info.yml
  16. 93
      roles/coredns/tasks/install.yml
  17. 28
      roles/coredns/tasks/install_tls.yml
  18. 107
      roles/coredns/tasks/main.yml
  19. 25
      roles/coredns/tasks/tls.yml
  20. 8
      roles/coredns/templates/corefile.j2
  21. 2
      roles/coredns/templates/init.j2
  22. 12
      roles/coredns/templates/tls.j2
  23. 29
      roles/coredns/templates/zone.j2
  24. 7
      roles/ns-only/tasks/info.yml
  25. 3
      roles/ns-only/tasks/main.yml
  26. 22
      roles/ns/tasks/main.yml
  27. 9
      roles/proxmox/tasks/info.yml
  28. 15
      roles/proxmox/tasks/mail.yml
  29. 85
      roles/proxmox/tasks/main.yml
  30. 1
      roles/proxmox/tasks/rproxy.yml
  31. 4
      tasks/gen_ssh_key.yml
  32. 2
      tasks/includes/stage.yml
  33. 45
      tasks/pre_tasks.yml

2
.gitignore vendored

@ -1,2 +1,4 @@
hosts hosts
keys/ keys/
.vscode/
group_vars/infra.yml

@ -3,6 +3,8 @@
serial: "{{ hosts_per_batch | d(1) | int }}" serial: "{{ hosts_per_batch | d(1) | int }}"
strategy: "{{ hosts_strategy | d('linear') }}" strategy: "{{ hosts_strategy | d('linear') }}"
tasks: tasks:
# TODO: multiple roles
- name: get primary role - name: get primary role
set_fact: set_fact:
host_primary_role: "{%- if primary_role is defined -%}{{ primary_role }}\ host_primary_role: "{%- if primary_role is defined -%}{{ primary_role }}\
@ -11,33 +13,30 @@
{%- endif -%}" {%- endif -%}"
- name: import role mappings - name: check if role info file exists
import_tasks: mappings.yml stat:
path: "{{ (playbook_dir, 'roles', host_primary_role, 'tasks', 'info.yml') | path_join }}"
delegate_to: localhost
- name: fail if mappings are missing register: result
fail: no_log: yes
msg: role mappings are missing or invalid
when: (common_roles is not defined) or (common_roles | type_debug != 'list')
- name: warn if current role mapping is missing - name: get role info
debug: include_role:
msg: "mapping for role \"{{ host_primary_role }}\" is missing - using default role mapping at stage 6" name: "{{ host_primary_role }}"
when: (extra_roles | d({}))[host_primary_role] is not defined tasks_from: info.yml
apply:
no_log: yes
when: result.stat.exists
- name: build role mapping - name: build role dependency list and stage info
set_fact: set_fact:
role_mapping: "{{ (((extra_roles | d({}))[host_primary_role] | d([{ 'stage': 6, 'role': host_primary_role }])) + role_deps: "{{ (([{ 'stage': 6, 'role': host_primary_role }] if role_dependency is not defined else [role_dependency]) +
([] if host_primary_role in (no_common_roles | d([])) else common_roles) + ([role_dependency_common] if (role_dependency_no_common | d(false) == false) else []) +
([{ 'stage': 1, 'role': 'container' }] if 'containers' in group_names else []) + ([{ 'stage': 1, 'role': 'container' }] if 'containers' in group_names else []) +
([{ 'stage': 3, 'role': 'postgres', 'function': 'integrate' }] if host_primary_role in (database_roles | d([])) else []) ([{ 'stage': 3, 'role': 'postgres', 'function': 'integrate' }] if role_dependency_use_db | d(false) == true else [])
) | sort(attribute='stage') }}" ) | flatten(levels=1) | sort(attribute='stage') }}"
- name: remember selected stages
set_fact:
selected_stages: "{%- if stage is defined and ((stage | string) is search(',')) -%}{{ stage | string | split(',') | list | map('int') | list }}\ selected_stages: "{%- if stage is defined and ((stage | string) is search(',')) -%}{{ stage | string | split(',') | list | map('int') | list }}\
{%- elif (stage is not defined) or ((stage | int) == 0) -%}{{ [1,2,3,4,5,6,7,8,9] }}\ {%- elif (stage is not defined) or ((stage | int) == 0) -%}{{ [1,2,3,4,5,6,7,8,9] }}\
{%- else -%}{{ [stage | int] }}\ {%- else -%}{{ [stage | int] }}\
@ -47,9 +46,13 @@
- name: show deployment info - name: show deployment info
debug: debug:
msg: "deploying primary role \"{{ host_primary_role }}\" on host \"{{ inventory_hostname }}\", {{ msg: "deploying role \"{{ host_primary_role }}\" on host \"{{ inventory_hostname }}\", {{
(('stages ' if (selected_stages | length > 1) else 'stage ') ~ (selected_stages | join(', '))) (('stages ' if (selected_stages | length > 1) else 'stage ') ~ (selected_stages | join(', ')))
if ([1,2,3,4,5,6,7,8,9] | symmetric_difference(selected_stages) | list | length > 0) else 'all stages' }}" if ([1,2,3,4,5,6,7,8,9] | symmetric_difference(selected_stages) | list | length > 0) else 'all stages' }}\n\n{{
'dependencies:\n' ~ (role_deps | map(attribute='stage') | list | zip(
role_deps | map(attribute='role') | list
) | map('join', ': ') | list | join('\n'))
}}"
- name: run pre_tasks - name: run pre_tasks

@ -34,3 +34,10 @@ bogons:
services: {} services: {}
mail_server: {} mail_server: {}
role_dependency_common:
- {stage: 2, role: 'common'}
- {stage: 4, role: 'ns'}
- {stage: 5, role: 'mail-user'}
- {stage: 8, role: 'iptables'}
- {stage: 9, role: 'backup', function: 'setup'}

@ -7,7 +7,7 @@ int_net: 10.0.0.0/8
int_tld: "corp.{{ tld }}" int_tld: "corp.{{ tld }}"
maintainer_email: "admin@{{ tld }}" maintainer_email: "admin@{{ tld }}"
container_default_nameserver: 10.0.0.1 default_nameserver: 10.0.0.1
networks: networks:
srv: srv:

@ -1,12 +1,7 @@
- name: define role list - name: define role list
set_fact: set_fact:
# common roles for all primary roles # common roles for all primary roles
common_roles:
- {stage: 2, role: 'common'}
- {stage: 3, role: 'ns', function: 'add_records'}
- {stage: 5, role: 'mail-user'}
- {stage: 8, role: 'iptables'}
- {stage: 9, role: 'backup', function: 'setup'}
# these primary roles do not inherit common roles # these primary roles do not inherit common roles
no_common_roles: no_common_roles:

@ -15,7 +15,7 @@
set_fact: set_fact:
ssh_ok: no ssh_ok: no
host_key_checking: no host_key_checking: no
ansible_password: "{{ container_password | d(host_password) }}" ansible_password: "{{ host_password }}"
ansible_ssh_extra_args: "{{ ansible_ssh_extra_args | d('') }} -o StrictHostKeyChecking=no" ansible_ssh_extra_args: "{{ ansible_ssh_extra_args | d('') }} -o StrictHostKeyChecking=no"
- name: try to connect without key checking - name: try to connect without key checking
@ -87,21 +87,22 @@
notify: restart dropbear notify: restart dropbear
- name: copy dropbear init file - name: template dropbear init file
copy: template:
src: dropbear_init src: dropbear_init.j2
dest: /etc/init.d/dropbear dest: /etc/init.d/dropbear
force: yes force: yes
lstrip_blocks: no
notify: restart dropbear notify: restart dropbear
- name: ensure remote host has ansible key in authorized_keys file - name: ensure remote host has ansible key in authorized_keys file
lineinfile: lineinfile:
path: /root/.ssh/authorized_keys path: /root/.ssh/authorized_keys
line: "{{ container_key.public_key }}" line: "{{ host_ssh_key.public_key }}"
create: yes create: yes
mode: 0400 mode: 0400
when: container_key is defined and container_key.public_key is defined when: host_ssh_key is defined and host_ssh_key.public_key is defined
when: ansible_distribution == 'Alpine' when: ansible_distribution == 'Alpine'
@ -131,8 +132,8 @@
- name: add etc directory to backup plan - name: add etc directory to backup plan
include_role: include_role:
name: backup name: backup
tasks_from: add.yml
vars: vars:
function: add
backup_items: backup_items:
- /etc - /etc

@ -0,0 +1,19 @@
#!/sbin/openrc-run
depend() {
use logger dns
need net
after firewall
}
start() {
ebegin "Starting dropbear"
/usr/sbin/dropbear ${DROPBEAR_OPTS}
eend $?
}
stop() {
ebegin "Stopping dropbear"
start-stop-daemon --stop --pidfile /var/run/dropbear.pid
eend $?
}

@ -3,5 +3,5 @@ container_pool: production
container_distro: alpine container_distro: alpine
container_template: container_template:
alpine: alpine-3.15-default_20211202_amd64.tar.xz alpine: alpine-3.17-default_20221129_amd64.tar.xz
debian: debian-11-standard_11.3-1_amd64.tar.zst debian: debian-11-standard_11.3-1_amd64.tar.zst

@ -1,8 +1,9 @@
- name: specify connection parameters - name: specify connection parameters
set_fact: set_fact:
pm_api_host: "{{ hostvars[selected_node]['ansible_host'] | mandatory }}" pm_api_host: "{{ hostvars[selected_node]['ansible_host'] }}"
pm_api_user: "{{ hostvars[selected_node]['api_user'] | d('root@pam') }}" pm_api_user: "{{ hostvars[selected_node]['api_user'] | d('root@pam') }}"
pm_api_password: "{{ hostvars[selected_node]['api_password'] | d(hostvars[selected_node]['ansible_password']) }}" pm_api_password: "{{ hostvars[selected_node]['api_password'] |
d(hostvars[selected_node]['host_password'] | d(hostvars[selected_node]['ansible_password'])) }}"
pm_lxc_storage: "{{ hostvars[selected_node]['lxc_storage'] | d('local-zfs') }}" pm_lxc_storage: "{{ hostvars[selected_node]['lxc_storage'] | d('local-zfs') }}"
no_log: yes no_log: yes
@ -12,7 +13,7 @@
msg: some container parameters are missing or invalid msg: some container parameters are missing or invalid
when: (container_distro is not defined) or (container_template is not mapping) or when: (container_distro is not defined) or (container_template is not mapping) or
(container_template[container_distro] is not defined) or (container_template[container_distro] is not defined) or
(container_id is not defined) or (container_password is not defined) (container_id is not defined)
- name: ensure pool exists on cluster node - name: ensure pool exists on cluster node
@ -26,9 +27,12 @@
- block: - block:
- name: ensure pip3 is installed on local node - name: ensure python dependencies are installed on local node
package: package:
name: py3-pip name:
- py3-pip
- py3-requests
- py3-netaddr
run_once: yes run_once: yes
@ -55,6 +59,21 @@
timeout: 20 timeout: 20
- name: build container network section
set_fact:
container_network_cfg: "{{ {
'name': 'eth0',
'hwaddr': container_mac | d(mac_prefix | community.general.random_mac(seed=inventory_hostname)),
'ip': ansible_host ~ '/' ~ networks[container_network].gw | ansible.utils.ipaddr('prefix'),
'gw': networks[container_network].gw | ansible.utils.ipaddr('address'),
'bridge': container_bridge | d('vmbr0'),
'firewall': 0,
'tag': networks[container_network].tag | d(None),
'type': 'veth',
'mtu': container_mtu | d(hostvars[selected_node]['container_mtu'] | d(1500))
} | dict2items | rejectattr('value', 'equalto', None) | list | items2dict }}"
- name: create container if not exists - name: create container if not exists
community.general.proxmox: community.general.proxmox:
node: "{{ selected_node }}" node: "{{ selected_node }}"
@ -62,31 +81,31 @@
api_user: "{{ pm_api_user }}" api_user: "{{ pm_api_user }}"
api_password: "{{ pm_api_password }}" api_password: "{{ pm_api_password }}"
cores: "{{ hardware.cores }}" cores: "{{ host_hardware.cores }}"
cpus: "{{ hardware.cpus }}" cpus: "{{ host_hardware.cpus }}"
cpuunits: "{{ hardware.cpuunits }}" cpuunits: "{{ host_hardware.cpuunits }}"
disk: "{{ hardware.disk | string }}" disk: "{{ host_hardware.disk | string }}"
memory: "{{ hardware.memory }}" memory: "{{ host_hardware.memory }}"
swap: "{{ hardware.swap }}" swap: "{{ host_hardware.swap }}"
description: "{{ container_description | d(omit) }}" description: "{{ container_description | d(omit) }}"
hostname: "{{ inventory_hostname }}" hostname: "{{ host_name }}"
pool: "{{ container_pool | d(omit) }}" pool: "{{ container_pool | d(omit) }}"
vmid: "{{ container_id }}" vmid: "{{ container_id }}"
password: "{{ container_password }}" password: "{{ host_password }}"
pubkey: "{{ (container_key | d({})).public_key | d(omit) }}" pubkey: "{{ (host_ssh_key | d({})).public_key | d(omit) }}"
ostemplate: "local:vztmpl/{{ container_template[container_distro] }}" ostemplate: "local:vztmpl/{{ container_template[container_distro] }}"
netif: "{\"net0\":\ onboot: yes
\"name=eth0,hwaddr={{ container_mac | d(mac_prefix | community.general.random_mac(seed=inventory_hostname)) }},\ proxmox_default_behavior: no_defaults
ip={{ ansible_host }}/{{ networks[container_network].gw | ansible.utils.ipaddr('prefix') }},\ storage: "{{ pm_lxc_storage }}"
gw={{ networks[container_network].gw | ansible.utils.ipaddr('address') }},\ unprivileged: yes
bridge=vmbr0,\ timeout: 240
firewall=0,\
tag={{ networks[container_network].tag }},\ netif: "{{ { 'net0': container_network_cfg.keys() | zip(container_network_cfg.values()) |
type=veth,\ map('join', '=') | join(',') } | to_json(sort_keys=true) }}"
mtu={{ container_mtu | d(hostvars[selected_node]['container_mtu'] | d(1500)) }}\"}"
nameserver: "{%- if container_nameserver is defined -%}\ nameserver: "{%- if container_nameserver is defined -%}\
{{ hostvars[container_nameserver]['ansible_host'] }}\ {{ hostvars[container_nameserver]['ansible_host'] }}\
{%- elif services.filtering_ns is defined -%}\ {%- elif services.filtering_ns is defined -%}\
@ -95,16 +114,11 @@
{%- else -%} {%- else -%}
{{ hostvars[services.filtering_ns.hostname]['ansible_host'] }}\ {{ hostvars[services.filtering_ns.hostname]['ansible_host'] }}\
{%- endif -%} {%- endif -%}
{%- elif container_default_nameserver is defined -%}\ {%- elif nameserver is defined -%}\
{{ container_default_nameserver }}\ {{ nameserver }}\
{%- else -%}\ {%- else -%}\
{{ omit }}\ {{ omit }}\
{%- endif -%}" {%- endif -%}"
onboot: yes
proxmox_default_behavior: no_defaults
storage: "{{ pm_lxc_storage }}"
unprivileged: yes
timeout: 240
mounts: >- mounts: >-
{ {%- for item in (container_mounts | d([])) -%} { {%- for item in (container_mounts | d([])) -%}
@ -115,39 +129,39 @@
- block: - block:
- name: add features to lxc config - name: add features to lxc config
lineinfile: lineinfile:
path: "/etc/pve/lxc/{{ container_id }}.conf" path: "{{ ('/etc/pve/lxc', container_id ~ '.conf') | path_join }}"
line: "features: {{ container_features | join(',') }}" line: "features: {{ container_features | join(',') }}"
when: container_features | d([]) | length > 0 when: container_features | d([]) | length > 0
- name: check that lxc config is correct - name: check that lxc config is correct
lineinfile: lineinfile:
path: "/etc/pve/lxc/{{ container_id }}.conf" path: "{{ ('/etc/pve/lxc', container_id ~ '.conf') | path_join }}"
regexp: "^{{ item.name }}:(\\s*).*$" regexp: "^{{ item.name }}:(\\s*).*$"
line: "{{ item.name | mandatory }}:\\g<1>{{ item.value | mandatory }}" line: "{{ item.name | mandatory }}:\\g<1>{{ item.value | mandatory }}"
backrefs: yes backrefs: yes
loop: loop:
- { name: cpus, value: "{{ hardware.cpus }}" } - { name: cpus, value: "{{ host_hardware.cpus }}" }
- { name: cores, value: "{{ [hardware.cores, hostvars[selected_node]['max_cores'] | d(hardware.cores)] | min }}" } - { name: cores, value: "{{ [host_hardware.cores, hostvars[selected_node]['max_cores'] | d(host_hardware.cores)] | min }}" }
- { name: cpuunits, value: "{{ hardware.cpuunits }}" } - { name: cpuunits, value: "{{ host_hardware.cpuunits }}" }
- { name: memory, value: "{{ hardware.memory }}" } - { name: memory, value: "{{ host_hardware.memory }}" }
- { name: swap, value: "{{ hardware.swap }}" } - { name: swap, value: "{{ host_hardware.swap }}" }
- { name: onboot, value: "{{ '1' if (container_active | d(true) == true) else '0' }}" } - { name: onboot, value: "{{ '1' if (container_active | d(true) == true) else '0' }}" }
- name: set startup order and delay - name: set startup order and delay
lineinfile: lineinfile:
path: "/etc/pve/lxc/{{ container_id }}.conf" path: "{{ ('/etc/pve/lxc', container_id ~ '.conf') | path_join }}"
regexp: '^startup:.*$' regexp: '^startup:.*$'
line: "startup: {{ 'order=' ~ (container_order | d(role_dependency[host_primary_role] | d('0'))) ~ ((',up=' ~ container_startup_delay) if container_startup_delay is defined else '') }}" line: "startup: {{ 'order=' ~ (container_order | d(role_dependency_index | d('0'))) ~ ((',up=' ~ container_startup_delay) if container_startup_delay is defined else '') }}"
insertbefore: '^[^\#]' insertbefore: '^[^\#]'
firstmatch: yes firstmatch: yes
when: (container_order is defined) or (role_dependency[host_primary_role] is defined) or (container_startup_delay is defined) when: (container_order is defined) or (role_dependency_index is defined) or (container_startup_delay is defined)
- name: ensure that cpulimit is not set - name: ensure that cpulimit is not set
lineinfile: lineinfile:
path: "/etc/pve/lxc/{{ container_id }}.conf" path: "{{ ('/etc/pve/lxc', container_id ~ '.conf') | path_join }}"
regexp: '^cpulimit:.*$' regexp: '^cpulimit:.*$'
state: absent state: absent
@ -163,6 +177,7 @@
vmid: "{{ container_id }}" vmid: "{{ container_id }}"
proxmox_default_behavior: no_defaults proxmox_default_behavior: no_defaults
state: "{{ 'started' if (container_active | d(true) == true) else 'stopped' }}" state: "{{ 'started' if (container_active | d(true) == true) else 'stopped' }}"
unprivileged: yes
- name: end playbook for current host if container is set to inactive - name: end playbook for current host if container is set to inactive
@ -179,7 +194,7 @@
delay: 2 delay: 2
changed_when: no changed_when: no
delegate_to: 127.0.0.1 delegate_to: localhost
- name: preconfigure container - name: preconfigure container

@ -2,8 +2,8 @@ coredns_user: coredns
coredns_group: coredns coredns_group: coredns
coredns_conf_dir: /etc/coredns coredns_conf_dir: /etc/coredns
coredns_conf_file: "{{ coredns_conf_dir }}/coredns.conf" coredns_conf_file: "{{ (coredns_conf_dir, 'coredns.conf') | path_join }}"
coredns_tls_file: "{{ coredns_conf_dir }}/tls.conf" coredns_tls_file: "{{ (coredns_conf_dir, 'tls.conf') | path_join }}"
coredns_cert_file: "{{ coredns_conf_dir }}/ecc384.crt" coredns_cert_file: "{{ (coredns_conf_dir, 'ecc384.crt') | path_join }}"
coredns_key_file: "{{ coredns_conf_dir }}/ecc384.key" coredns_key_file: "{{ (coredns_conf_dir, 'ecc384.key') | path_join }}"

@ -0,0 +1,27 @@
- name: add default record
include_tasks:
file: add_record.yml
apply:
delegate_to: "{{ services.internal_ns }}"
vars:
record: {}
when: (records | d([]) | length) == 0
- name: process other items
include_tasks:
file: add_record.yml
apply:
delegate_to: "{{ services.internal_ns }}"
loop: "{{ records | d([]) }}"
loop_control:
loop_var: record
- name: restart coredns
service:
name: coredns
state: restarted
delegate_to: "{{ services.internal_ns }}"
when: (ns_instant | d(false) == false) and
((ns_records_changed | d(false) == true) or (ns_serial_changed | d(false) == true))

@ -10,21 +10,13 @@
when: record.zone is defined and record.zone is not string when: record.zone is defined and record.zone is not string
- name: check if record zone exists
fail:
msg: '"{{ record.zone }}" does not seem to be a valid zone'
when: (record.zone is defined) and
(record.zone != 'root') and
((int_zones is not defined) or (record.zone not in int_zones))
- name: construct record parameters - name: construct record parameters
set_fact: set_fact:
ns_zone: "{%- if (record.zone is defined) and (record.zone != 'root') -%}{{ record.zone }}\ ns_zone: "{%- if (record.zone is defined) and (record.zone != 'root') -%}{{ record.zone }}\
{%- else -%}{{ ns_tld | d(int_tld) }}\ {%- else -%}{{ ns_tld | d(int_tld) }}\
{%- endif -%}" {%- endif -%}"
ns_name: "{%- if record.name is defined -%}{{ record.name }}\ ns_name: "{%- if record.name is defined -%}{{ record.name }}\
{%- else -%}{{ inventory_hostname }}\ {%- else -%}{{ host_name }}\
{%- endif -%}" {%- endif -%}"
ns_type: "{%- if record.type is defined -%}{{ record.type | upper }}\ ns_type: "{%- if record.type is defined -%}{{ record.type | upper }}\
{%- else -%}A\ {%- else -%}A\
@ -33,6 +25,7 @@
{%- else -%}{{ ansible_host }}\ {%- else -%}{{ ansible_host }}\
{%- endif -%}" {%- endif -%}"
- name: set ns_quote - name: set ns_quote
set_fact: set_fact:
ns_quote: "{{ '\"' if ns_type == 'TXT' else '' }}" ns_quote: "{{ '\"' if ns_type == 'TXT' else '' }}"
@ -60,9 +53,9 @@
- name: slurp zone file - name: slurp zone file
slurp: slurp:
src: "{{ coredns_conf_dir ~ '/' ~ (ns_tld | d(int_tld)) ~ '.zone' }}" src: "{{ (coredns_conf_dir, ns_zone ~ '.zone') | path_join }}"
register: zf register: zf
changed_when: false changed_when: no
- name: enumerate stdout lines to check if an entry already exists - name: enumerate stdout lines to check if an entry already exists
@ -84,7 +77,7 @@
- name: replace the record - name: replace the record
lineinfile: lineinfile:
path: "{{ coredns_conf_dir ~ '/' ~ (ns_tld | d(int_tld)) ~ '.zone' }}" path: "{{ (coredns_conf_dir, ns_zone ~ '.zone') | path_join }}"
regexp: '^\s*{{ ns_name | regex_escape() }}\s+IN\s+{{ ns_type | regex_escape() }}\s+' regexp: '^\s*{{ ns_name | regex_escape() }}\s+IN\s+{{ ns_type | regex_escape() }}\s+'
line: "{{ ns_name }}\tIN\t{{ ns_type }}\t{{ ns_quote ~ ns_value ~ ns_quote }}" line: "{{ ns_name }}\tIN\t{{ ns_type }}\t{{ ns_quote ~ ns_value ~ ns_quote }}"
backrefs: yes backrefs: yes
@ -96,7 +89,7 @@
- name: add the record if it is missing - name: add the record if it is missing
lineinfile: lineinfile:
path: "{{ coredns_conf_dir ~ '/' ~ (ns_tld | d(int_tld)) ~ '.zone' }}" path: "{{ (coredns_conf_dir, ns_zone ~ '.zone') | path_join }}"
line: "{{ ns_name }}\tIN\t{{ ns_type }}\t{{ ns_quote ~ ns_value ~ ns_quote }}" line: "{{ ns_name }}\tIN\t{{ ns_type }}\t{{ ns_quote ~ ns_value ~ ns_quote }}"
when: not ns_exists when: not ns_exists
register: rr2 register: rr2
@ -109,6 +102,8 @@
- name: change serial - name: change serial
include_tasks: increase_serial.yml include_tasks: increase_serial.yml
vars:
zone: "{{ ns_zone }}"
when: ns_records_changed | d(false) == true when: ns_records_changed | d(false) == true

@ -1,21 +0,0 @@
- name: add default record
include_tasks: add_record.yml
vars:
record: {}
when: (ns_records | d([]) | length) == 0
- name: process other items
include_tasks: add_record.yml
loop: "{{ ns_records | d([]) }}"
loop_control:
loop_var: record
- name: restart coredns
service:
name: coredns
state: restarted
when: (ns_instant | d(false) == false) and
((ns_records_changed | d(false) == true) or
(ns_serial_changed | d(false) == true))

@ -1,13 +1,19 @@
- name: fail if zone is not defined
fail:
msg: zone is not defined
when: zone is not defined
- name: slurp zone file - name: slurp zone file
slurp: slurp:
src: "{{ coredns_conf_dir ~ '/' ~ (ns_tld | d(int_tld)) ~ '.zone' }}" src: "{{ (coredns_conf_dir, zone ~ '.zone') | path_join }}"
register: zf register: zf
changed_when: false changed_when: no
- name: get SOA serial value - name: get SOA serial value
set_fact: set_fact:
ns_old_serial: '{{ zf.content | b64decode | regex_search(''@\s+IN\s+SOA\s+\S+\s+\S+\s*\(\s*(\d+)'', ''\1'') | first }}' ns_old_serial: '{{ zf.content | b64decode | regex_search(''@\s+IN\s+SOA\s+\S+\s+\S+\s*\(\s*(\d+)'', ''\1'') | first | string }}'
- name: get current date - name: get current date
@ -36,7 +42,7 @@
- name: insert new serial - name: insert new serial
replace: replace:
path: "{{ coredns_conf_dir ~ '/' ~ (ns_tld | d(int_tld)) ~ '.zone' }}" path: "{{ (coredns_conf_dir, zone ~ '.zone') | path_join }}"
regexp: '(@\s+IN\s+SOA\s+\S+\s+\S+\s*\(\s*){{ ns_old_serial }}' regexp: '(@\s+IN\s+SOA\s+\S+\s+\S+\s*\(\s*){{ ns_old_serial }}'
replace: '\g<1>{{ ns_new_serial }}' replace: '\g<1>{{ ns_new_serial }}'
register: result register: result

@ -0,0 +1,13 @@
- name: set role information
set_fact:
role_dependency:
- {stage: 3, role: 'coredns'}
- {stage: 5, role: 'coredns', tasks_from: 'tls.yml'}
role_dependency_index: 0
role_hardware:
cores: 4
memory: 128
swap: 64
disk: 0.3

@ -1,93 +0,0 @@
- name: install coredns and dependencies
include_tasks: tasks/install_packages.yml
vars:
package:
- coredns
- alpine: coredns-openrc
- name: create user and group
include_tasks: tasks/create_user.yml
vars:
user:
name: "{{ coredns_user }}"
group: "{{ coredns_group }}"
- name: create config directory
file:
path: "{{ coredns_conf_dir }}"
state: directory
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
notify: restart coredns
- name: template corefile
template:
src: corefile.j2
dest: "{{ coredns_conf_file }}"
force: yes
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
mode: 0400
notify: restart coredns
- name: template empty tls file if missing
copy:
content: ''
dest: "{{ coredns_tls_file }}"
force: no
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
mode: 0400
notify: restart coredns
- name: template root zone if missing
template:
src: zone.j2
dest: "{{ coredns_conf_dir ~ '/' ~ (ns_tld | d(int_tld)) ~ '.zone' }}"
force: no
mode: 0400
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
notify: restart coredns
- name: edit service config
lineinfile:
path: /etc/conf.d/coredns
regexp: "^COREDNS_CONFIG="
line: "COREDNS_CONFIG={{ coredns_conf_file | quote }}"
notify: restart coredns
- name: template init script
template:
src: init.j2
dest: /etc/init.d/coredns
force: yes
mode: 0755
notify: restart coredns
- name: flush handlers
meta: flush_handlers
- name: add directories to backup plan
include_role:
name: backup
vars:
function: add
backup_items:
- "{{ coredns_conf_dir }}"
- name: enable and start coredns
service:
name: coredns
enabled: yes
state: started

@ -1,28 +0,0 @@
- name: deploy ecc384 cert
include_role:
name: ca
vars:
function: certs
ca_options:
mode: '0400'
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
concat_inter: true
preset: web
ocsp_must_staple: false
notify: restart coredns
ca_certs:
- type: ecc384
key: "{{ coredns_key_file }}"
cert: "{{ coredns_cert_file }}"
- name: template tls snippet file
template:
src: tls.j2
dest: "{{ coredns_tls_file }}"
force: yes
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
mode: 0400
notify: restart coredns

@ -1,13 +1,102 @@
- name: install coredns - name: build default tld list
include_tasks: install.yml set_fact:
when: function == 'install' coredns_tld_list: "{{ [ ns_tld | d(int_tld) ] }}"
when: coredns_tld_list is not defined
- name: install coredns tls enhancements - name: install coredns and dependencies
include_tasks: install_tls.yml include_tasks: tasks/install_packages.yml
when: function == 'install_tls' vars:
package:
- coredns
- alpine: coredns-openrc
- name: add records - name: create user and group
include_tasks: add_records.yml include_tasks: tasks/create_user.yml
when: function == 'add_records' vars:
user:
name: "{{ coredns_user }}"
group: "{{ coredns_group }}"
- name: create config directory
file:
path: "{{ coredns_conf_dir }}"
state: directory
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
notify: restart coredns
- name: template corefile
template:
src: corefile.j2
dest: "{{ coredns_conf_file }}"
force: yes
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
mode: 0400
notify: restart coredns
- name: template empty tls file if missing
copy:
content: ''
dest: "{{ coredns_tls_file }}"
force: no
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
mode: 0400
notify: restart coredns
- name: template all zones if missing
template:
src: zone.j2
dest: "{{ (coredns_conf_dir, zone ~ '.zone') | path_join }}"
force: no
mode: 0400
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
notify: restart coredns
loop: "{{ coredns_tld_list }}"
loop_control:
loop_var: zone
- name: edit service config
lineinfile:
path: /etc/conf.d/coredns
regexp: "^COREDNS_CONFIG="
line: "COREDNS_CONFIG={{ coredns_conf_file | quote }}"
notify: restart coredns
- name: template init script
template:
src: init.j2
dest: /etc/init.d/coredns
force: yes
mode: 0755
notify: restart coredns
- name: flush handlers
meta: flush_handlers
- name: add directories to backup plan
include_role:
name: backup
tasks_from: add.yml
vars:
backup_items:
- "{{ coredns_conf_dir }}"
- name: enable and start coredns
service:
name: coredns
enabled: yes
state: started

@ -0,0 +1,25 @@
- block:
- name: deploy ecc384 cert
include_role:
name: certs
vars:
certs:
cert: "{{ coredns_cert_file }}"
key: "{{ coredns_key_file }}"
ecc: yes
post_hook: service coredns restart
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
- name: template tls snippet file
template:
src: tls.j2
dest: "{{ coredns_tls_file }}"
force: yes
owner: "{{ coredns_user }}"
group: "{{ coredns_group }}"
mode: 0400
notify: restart coredns
when: host_tls

@ -1,6 +1,5 @@
(common) { (common) {
root {{ (coredns_conf_dir ~ '/') | quote }} root {{ (coredns_conf_dir ~ '/') | quote }}
file {{ ((ns_tld | d(int_tld)) ~ '.zone') | quote }}
any any
bufsize {{ ns_edns0_bufsize | d(1232) }} bufsize {{ ns_edns0_bufsize | d(1232) }}
@ -8,8 +7,11 @@
loadbalance loadbalance
} }
{{ ns_tld | d(int_tld) }} { {% for zone in coredns_tld_list %}
{{ zone }} {
import common import common
} file {{ (zone ~ '.zone') | quote }}
}
{% endfor %}
import {{ coredns_tls_file | quote }} import {{ coredns_tls_file | quote }}

@ -4,7 +4,7 @@ name="$SVCNAME"
directory="{{ coredns_conf_dir }}" directory="{{ coredns_conf_dir }}"
command="/usr/bin/coredns" command="/usr/bin/coredns"
command_args="-conf ${COREDNS_CONFIG} ${COREDNS_EXTRA_ARGS}" command_args="-conf ${COREDNS_CONFIG} ${COREDNS_EXTRA_ARGS}"
command_user="{{ coredns_user }}:{{ coredns_group }}" command_user="{{ coredns_user ~ ':' ~ coredns_group }}"
pidfile="/var/run/$SVCNAME.pid" pidfile="/var/run/$SVCNAME.pid"
command_background=true command_background=true
start_stop_daemon_args="--stdout-logger logger --stderr-logger logger" start_stop_daemon_args="--stdout-logger logger --stderr-logger logger"

@ -1,9 +1,13 @@
tls://{{ ns_tld | d(int_tld) }}:853 { {% for zone in coredns_tld_list %}
tls://{{ zone }}:853 {
import common import common
file {{ (zone ~ '.zone') | quote }}
tls {{ coredns_cert_file | quote }} {{ coredns_key_file | quote }} tls {{ coredns_cert_file | quote }} {{ coredns_key_file | quote }}
} }
https://{{ ns_tld | d(int_tld) }} { https://{{ zone }} {
import common import common
file {{ (zone ~ '.zone') | quote }}
tls {{ coredns_cert_file | quote }} {{ coredns_key_file | quote }} tls {{ coredns_cert_file | quote }} {{ coredns_key_file | quote }}
} }
{% endfor %}

@ -1,32 +1,13 @@
{%- set primary_ns = inventory_hostname -%} $ORIGIN {{ zone }}.
{%- if ns_server_group is defined -%}
{%- set primary_ns = hostvars[groups[ns_server_group][0]]['inventory_hostname'] -%}
{%- endif -%}
{%- set this_name = (ns_name | d(inventory_hostname)) -%}
{%- set this_primary_name = (hostvars[primary_ns]['ns_name'] | d(hostvars[primary_ns]['inventory_hostname'])) -%}
{%- set this_tld = (hostvars[primary_ns]['ns_tld'] | d(ns_tld) | d(int_tld)) -%}
$ORIGIN {{ this_tld }}.
$TTL {{ ns_ttl | d(300) }} $TTL {{ ns_ttl | d(300) }}
@ IN SOA {{ this_name ~ '.' ~ this_tld }}. {{ (ns_admin | replace('@', '.')) if ns_admin is defined else ('admin' ~ '.' ~ this_tld) }}. ( @ IN SOA {{ host_name ~ '.' ~ zone }}. {{ (ns_admin | replace('@', '.')) if ns_admin is defined else ('admin' ~ '.' ~ zone) }}. (
2021010101 2023010101
{{ ns_refresh | d(1200) }} {{ ns_refresh | d(1200) }}
{{ ns_retry | d(300) }} {{ ns_retry | d(300) }}
{{ ns_expire | d(1209600) }} {{ ns_expire | d(1209600) }}
{{ ns_neg_ttl | d(300) }} {{ ns_neg_ttl | d(300) }}
) )
{% if ns_server_group is defined -%} @ IN NS {{ host_name ~ '.' ~ zone }}.
{% for host in groups[ns_server_group] -%} {{ host_name }} IN A {{ ansible_host }}
@ IN NS {{ (hostvars[host]['ns_name'] | d(hostvars[host]['inventory_hostname'])) ~ '.' ~ this_tld }}.
{{ hostvars[host]['ns_name'] | d(hostvars[host]['inventory_hostname']) }} IN A {{ hostvars[host]['ansible_host'] }}
{% endfor -%}
{% else -%}
@ IN NS {{ this_primary_name ~ '.' ~ this_tld }}.
{{ this_primary_name }} IN A {{ ansible_host }}
{% endif -%}

@ -0,0 +1,7 @@
- name: set role information
set_fact:
role_dependency:
- { stage: 3, role: 'ns' }
role_dependency_index: 1
role_dependency_no_common: yes

@ -0,0 +1,3 @@
- name: add internal ns records
include_role:
name: ns

@ -1,16 +1,12 @@
- name: ns installation - name: add internal ns records with coredns
include_tasks: install.yml block:
when: function == 'install' - set_fact:
records_tmp: "{{ records | d([]) }}"
- include_role:
name: coredns
tasks_from: add.yml
vars:
records: "{{ records_tmp }}"
- block:
- name: add records
include_tasks: add_records.yml
when: services.internal_ns is defined when: services.internal_ns is defined
- debug:
msg: internal nameserver is not defined
when: services.internal_ns is not defined
when: function == 'add_records'

@ -0,0 +1,9 @@
- name: set role information
set_fact:
role_dependency:
- {stage: 1, role: 'common'}
- {stage: 1, role: 'proxmox'}
- {stage: 5, role: 'proxmox', tasks_from: 'rproxy'}
- {stage: 6, role: 'proxmox', tasks_from: 'mail'}
role_dependency_index: 0

@ -1,9 +1,10 @@
- name: install libsasl2-modules - block:
- name: install libsasl2-modules
package: package:
name: libsasl2-modules name: libsasl2-modules
- name: edit postfix config - name: edit postfix config
lineinfile: lineinfile:
path: /etc/postfix/main.cf path: /etc/postfix/main.cf
regexp: '^{{ item.name | regex_escape() }}([^\S\r\n]*)=([^\S\r\n]*)' regexp: '^{{ item.name | regex_escape() }}([^\S\r\n]*)=([^\S\r\n]*)'
@ -26,7 +27,7 @@
- { name: mydestination, value: "" } - { name: mydestination, value: "" }
- name: edit master.cf - name: edit master.cf
lineinfile: lineinfile:
path: /etc/postfix/master.cf path: /etc/postfix/master.cf
regexp: '^bounce([^\S\r\n]+)unix' regexp: '^bounce([^\S\r\n]+)unix'
@ -34,7 +35,7 @@
notify: restart postfix notify: restart postfix
- name: create postfix files - name: create postfix files
copy: copy:
dest: "/etc/postfix/{{ item.name }}" dest: "/etc/postfix/{{ item.name }}"
content: "{{ item.content }}" content: "{{ item.content }}"
@ -51,7 +52,7 @@
content: '/From:.*/ REPLACE From: {{ host_name }} <{{ host_name }}@{{ mail_server.tld }}>' content: '/From:.*/ REPLACE From: {{ host_name }} <{{ host_name }}@{{ mail_server.tld }}>'
- name: edit crontab mail config - name: edit crontab mail config
lineinfile: lineinfile:
path: /etc/crontab path: /etc/crontab
regexp: '^MAILTO=' regexp: '^MAILTO='
@ -59,7 +60,7 @@
insertafter: '^PATH=' insertafter: '^PATH='
- name: edit zed config file - name: edit zed config file
lineinfile: lineinfile:
path: /etc/zfs/zed.d/zed.rc path: /etc/zfs/zed.d/zed.rc
regexp: '^{{ item.name | upper | regex_escape() }}=' regexp: '^{{ item.name | upper | regex_escape() }}='
@ -70,3 +71,5 @@
- { name: zed_email_prog, value: mail } - { name: zed_email_prog, value: mail }
- { name: zed_email_opts, value: "-s '@SUBJECT@' @ADDRESS@ -r {{ mail_account.username ~ '@' ~ mail_server.tld }}" } - { name: zed_email_opts, value: "-s '@SUBJECT@' @ADDRESS@ -r {{ mail_account.username ~ '@' ~ mail_server.tld }}" }
- { name: zed_notify_verbose, value: 1 } - { name: zed_notify_verbose, value: 1 }
when: (mail_account is mapping) and (mail_server is mapping)

@ -3,16 +3,83 @@
proxmox_cfg: "{{ proxmox_default_config | d({}) | combine(proxmox_config | d({}), recursive=true) }}" proxmox_cfg: "{{ proxmox_default_config | d({}) | combine(proxmox_config | d({}), recursive=true) }}"
- name: proxmox installation - name: install extra proxmox packages
include_tasks: install.yml package:
when: function == 'install' name: "{{ item }}"
loop: "{{ proxmox_default_packages + (proxmox_packages | d([])) }}"
- name: proxmox reverse proxy configuration - block:
include_tasks: rproxy.yml - name: set cpu scheduler in cron
when: function == 'rproxy' cron:
name: set cpu scheduler
special_time: reboot
job: 'echo {{ proxmox_cfg.cpu_governor | quote }} | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor > /dev/null'
user: root
- name: proxmox mail configuration - block:
include_tasks: mail.yml - name: get current cpu scheduler types
when: (function == 'mail') and (mail_account is mapping) and (mail_server is mapping) shell:
cmd: cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
register: result
changed_when: no
- name: change cpu scheduler
shell:
cmd: 'echo {{ proxmox_cfg.cpu_governor | quote }} | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor'
when: (result.stdout_lines | unique | length > 1) or ((result.stdout_lines | unique)[0] != proxmox_cfg.cpu_governor)
rescue:
- name: report that cpu scheduler cannot be changed
debug:
msg: failed to change cpu scheduler
when: proxmox_cfg.cpu_governor is string
- name: disable enterprise repo
apt_repository:
repo: deb https://enterprise.proxmox.com/debian/pve bullseye pve-enterprise
filename: pve-enterprise.list
state: absent
update_cache: no
- name: enable community repo
apt_repository:
repo: deb http://download.proxmox.com/debian/pve bullseye pve-no-subscription
filename: pve-community.list
state: present
update_cache: no
- name: set datacenter configuration
lineinfile:
path: /etc/pve/datacenter.cfg
regexp: "^{{ item.key }}: "
line: "{{ item.key }}: {{ item.value }}"
mode: 0640
owner: root
group: www-data
create: yes
loop: "{{ proxmox_cfg.datacenter | dict2items }}"
- name: enable auto-reboot on kernel panic
copy:
dest: /etc/sysctl.d/90-auto-reboot.conf
content: "kernel.panic = 5\n"
mode: 0644
when: proxmox_cfg.auto_reboot | d(true) == true
- name: set max arc cache size for zfs
lineinfile:
path: /etc/modprobe.d/zfs.conf
regexp: "^options zfs zfs_arc_max="
line: "options zfs zfs_arc_max={{ proxmox_cfg.zfs_arc_max }}"
create: yes
mode: 0644
when: proxmox_cfg.zfs_arc_max is defined

@ -9,4 +9,3 @@
conf: conf:
http: http:
ssl_conf_command: [] ssl_conf_command: []

@ -13,12 +13,12 @@
mode: 0400 mode: 0400
regenerate: full_idempotence regenerate: full_idempotence
type: ed25519 type: ed25519
register: container_key register: host_ssh_key
- name: fail if public key is missing - name: fail if public key is missing
fail: fail:
msg: public key is missing msg: public key is missing
when: container_key.public_key is not defined when: host_ssh_key.public_key is not defined
delegate_to: localhost delegate_to: localhost

@ -11,6 +11,6 @@
- name: include roles for selected stage - name: include roles for selected stage
include_tasks: tasks/includes/role.yml include_tasks: tasks/includes/role.yml
loop: "{{ role_mapping | selectattr('stage', 'equalto', (this_stage | int)) | list }}" loop: "{{ role_deps | selectattr('stage', 'equalto', (this_stage | int)) | list }}"
loop_control: loop_control:
loop_var: this_role loop_var: this_role

@ -1,15 +1,31 @@
- name: determine host info - name: set host name
set_fact: set_fact:
host_name: "{{ actual_hostname | d(inventory_hostname) }}" host_name: "{{ inventory_hostname }}"
host_tld: "{%- if branch is defined -%}{{ branch }}.{%- endif -%}{{ tld if (use_external_tld | d(false) == true) else int_tld }}" when: host_name is not defined
host_tls: "{{ use_tls | d(true) }}"
host_protocol: "{{ 'https' if (use_tls | d(true)) else 'http' }}"
host_metrics: "{{ services.prometheus is defined and (use_metrics | d(true) == true) }}"
host_mail: "{{ mail_server.mta_hostname is defined and (use_mail | d(true) == true) }}"
host_backups: "{{ services.backup is defined and (use_backups | d(true) == true) }}"
- name: determine host fqdn and uri - name: set host tld
set_fact:
host_tld: "{{ ((host_branch ~ '.') if host_branch is defined else '') ~ (tld if (host_external_tld | d(false) == true) else int_tld) }}"
when: host_tld is not defined
- name: set host tls
set_fact:
host_tls: yes
when: host_tls is not defined
- name: set other host variables
set_fact:
host_protocol: "{{ 'https' if host_tls else 'http' }}"
host_metrics: "{{ services.prometheus is defined and (host_metrics | d(true) == true) }}"
host_mail: "{{ mail_server.mta_hostname is defined and (host_mail | d(true) == true) }}"
host_backups: "{{ services.backup is defined and (host_backups | d(true) == true) }}"
host_is_container: "{{ ('containers' in group_names) or (host_is_container | d(false) == true) }}"
- name: set host fqdn and uri
set_fact: set_fact:
host_fqdn: "{{ host_name ~ '.' ~ host_tld }}" host_fqdn: "{{ host_name ~ '.' ~ host_tld }}"
host_url: "{{ host_protocol }}://{{ host_name ~ '.' ~ host_tld }}" host_url: "{{ host_protocol }}://{{ host_name ~ '.' ~ host_tld }}"
@ -23,19 +39,20 @@
- name: select a cluster node - name: select a cluster node
include_tasks: tasks/select_node.yml include_tasks: tasks/select_node.yml
when: "'containers' in group_names" when: host_is_container
- name: set hardware information - name: set hardware information
set_fact: set_fact:
hardware: "{{ default_container_hardware | combine(role_hardware[host_primary_role] | d({})) | host_hardware: "{{ (default_container_hardware | combine(role_hardware | d({})) |
combine((container_hardware if 'containers' in group_names else host_hardware) | d({})) }}" combine(host_hardware | d({}))) if host_is_container
else (host_hardware | d({})) }}"
- name: clamp hardware cores to max node number - name: clamp hardware cores to max node number
set_fact: set_fact:
hardware: "{{ hardware | combine({'cores': ([hardware.cores, hostvars[selected_node]['max_cores'] | d(hardware.cores)] | min)}) }}" host_hardware: "{{ host_hardware | combine({'cores': ([host_hardware.cores, hostvars[selected_node]['max_cores'] | d(host_hardware.cores)] | min)}) }}"
when: "not ('containers' in group_names) and (selected_node is defined) and (hostvars[selected_node]['max_cores'] is defined)" when: "host_is_container and (selected_node is defined) and (hostvars[selected_node]['max_cores'] is defined)"
- block: - block:

Loading…
Cancel
Save