Compare commits
26 Commits
0deed89c3f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8bd645a80 | ||
|
|
6d2d305fd0 | ||
|
|
90ade1e766 | ||
|
|
f2840d79a7 | ||
|
|
217b79b225 | ||
|
|
37066850a0 | ||
|
|
7617edfdde | ||
|
|
e4c8a2343a | ||
|
|
9b40f06804 | ||
|
|
273da948b5 | ||
|
|
7e0538ae20 | ||
|
|
1c9649e8d6 | ||
|
|
648da9266b | ||
|
|
8af49bcc3e | ||
|
|
cb0817fc54 | ||
|
|
c0753aeaa2 | ||
|
|
83569c59ee | ||
|
|
6c340c5111 | ||
|
|
3cd66c54a7 | ||
|
|
d9f8733c39 | ||
|
|
6234c0c459 | ||
|
|
cebe1eb957 | ||
|
|
8e27769f5b | ||
|
|
9856bc2087 | ||
|
|
3d4f2773a8 | ||
|
|
0db60e2d60 |
2
roles/automatic_updates/README.md
Normal file
2
roles/automatic_updates/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
This role enables automatic package updates.
|
||||||
|
It currently supports Debian and Ubuntu.
|
||||||
9
roles/automatic_updates/meta/main.yaml
Normal file
9
roles/automatic_updates/meta/main.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.compatcheck
|
||||||
|
vars:
|
||||||
|
compatcheck_supported_distributions:
|
||||||
|
- name: debian
|
||||||
|
version_min: 11
|
||||||
|
- name: ubuntu
|
||||||
|
version_min: 22
|
||||||
@@ -1,14 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure host distribution is supported
|
|
||||||
ansible.builtin.import_role:
|
|
||||||
name: compatcheck
|
|
||||||
vars:
|
|
||||||
compatcheck_supported_distributions:
|
|
||||||
- name: debian
|
|
||||||
version_min: 11
|
|
||||||
- name: ubuntu
|
|
||||||
version_min: 24
|
|
||||||
|
|
||||||
- name: Install unatteded-upgrades
|
- name: Install unatteded-upgrades
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name: unattended-upgrades
|
name: unattended-upgrades
|
||||||
|
|||||||
1
roles/borgmatic/README.md
Normal file
1
roles/borgmatic/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Installs borgmatic
|
||||||
22
roles/borgmatic/meta/argument_specs.yaml
Normal file
22
roles/borgmatic/meta/argument_specs.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Borgmatic
|
||||||
|
description:
|
||||||
|
- Installs borgmatic
|
||||||
|
options:
|
||||||
|
borgmatic_targets:
|
||||||
|
description: List of backup targets
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
|
elements: dict
|
||||||
|
options:
|
||||||
|
host:
|
||||||
|
description: Target hostname
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
directories:
|
||||||
|
description: Directories on the host where backup repos will be created under
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
|
elements: str
|
||||||
9
roles/borgmatic/meta/main.yaml
Normal file
9
roles/borgmatic/meta/main.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.compatcheck
|
||||||
|
vars:
|
||||||
|
compatcheck_supported_distributions:
|
||||||
|
- name: debian
|
||||||
|
version_min: 11
|
||||||
|
- name: ubuntu
|
||||||
|
version_min: 22
|
||||||
47
roles/borgmatic/tasks/main.yaml
Normal file
47
roles/borgmatic/tasks/main.yaml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
- name: Install borgmatic
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: borgmatic
|
||||||
|
register: _borgmatic_install
|
||||||
|
|
||||||
|
- name: Disable borgmatic global timer
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: borgmatic.timer
|
||||||
|
state: stopped
|
||||||
|
enabled: false
|
||||||
|
when: "not (ansible_check_mode and _borgmatic_install.changed)"
|
||||||
|
|
||||||
|
- name: Add systemd drop-in service for borgmatic
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: borgmatic@.service.j2
|
||||||
|
dest: /etc/systemd/system/borgmatic@.service
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Create borgmatic configurations directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/borgmatic.d
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Generate ssh key for borg
|
||||||
|
community.crypto.openssh_keypair:
|
||||||
|
type: ed25519
|
||||||
|
path: "{{ ansible_user_dir }}/.ssh/id_ed25519_borg"
|
||||||
|
comment: "{{ ansible_user_id }}@{{ ansible_fqdn }} borg"
|
||||||
|
register: _borgmatic_key
|
||||||
|
|
||||||
|
- name: Setup backup targets
|
||||||
|
ansible.builtin.include_tasks:
|
||||||
|
file: target.yaml
|
||||||
|
apply:
|
||||||
|
delegate_to: "{{ target.host }}"
|
||||||
|
become: false
|
||||||
|
loop: "{{ borgmatic_targets }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: target
|
||||||
|
|
||||||
|
- name: Add borg target ssh host keys to known hosts
|
||||||
|
ansible.builtin.known_hosts:
|
||||||
|
name: "{{ item }}"
|
||||||
|
key: "{{ item }} ssh-ed25519 {{ hostvars[item].ansible_ssh_host_key_ed25519_public }}"
|
||||||
|
loop: "{{ borgmatic_targets | map(attribute='host') }}"
|
||||||
28
roles/borgmatic/tasks/target.yaml
Normal file
28
roles/borgmatic/tasks/target.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
- name: Gather facts
|
||||||
|
ansible.builtin.setup:
|
||||||
|
delegate_facts: true
|
||||||
|
ignore_unreachable: true
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
- name: Add ssh key to authorized_keys
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: "{{ hostvars[target.host].ansible_user_id }}"
|
||||||
|
key: >-
|
||||||
|
{{
|
||||||
|
_borgmatic_key.public_key + ' ' + _borgmatic_key.comment
|
||||||
|
if not (ansible_check_mode and _borgmatic_key.changed)
|
||||||
|
else 'ssh-ed25519 AAAA'
|
||||||
|
}}
|
||||||
|
key_options: >-
|
||||||
|
command="borg
|
||||||
|
serve{% for directory in target.directories %}
|
||||||
|
--restrict-to-path
|
||||||
|
{{ hostvars[target.host].ansible_user_dir }}/{{ directory }}/{{ ansible_fqdn }}{%- endfor -%}",restrict
|
||||||
|
|
||||||
|
- name: Create backup directories
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ hostvars[target.host].ansible_user_dir }}/{{ item }}/{{ ansible_fqdn }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0700"
|
||||||
|
loop: "{{ target.directories }}"
|
||||||
63
roles/borgmatic/templates/borgmatic@.service.j2
Normal file
63
roles/borgmatic/templates/borgmatic@.service.j2
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=borgmatic backup %i
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
Documentation=https://torsion.org/borgmatic/
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RuntimeDirectory=borgmatic
|
||||||
|
StateDirectory=borgmatic
|
||||||
|
|
||||||
|
# Security settings for systemd running as root, optional but recommended to improve security. You
|
||||||
|
# can disable individual settings if they cause problems for your use case. For more details, see
|
||||||
|
# the systemd manual: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
|
||||||
|
LockPersonality=true
|
||||||
|
# Certain borgmatic features like Healthchecks integration need MemoryDenyWriteExecute to be off.
|
||||||
|
# But you can try setting it to "yes" for improved security if you don't use those features.
|
||||||
|
MemoryDenyWriteExecute=no
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectClock=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectHostname=yes
|
||||||
|
ProtectKernelLogs=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
SystemCallFilter=@system-service
|
||||||
|
SystemCallErrorNumber=EPERM
|
||||||
|
# To restrict write access further, change "ProtectSystem" to "strict" and
|
||||||
|
# uncomment "ReadWritePaths", "TemporaryFileSystem", "BindPaths" and
|
||||||
|
# "BindReadOnlyPaths". Then add any local repository paths to the list of
|
||||||
|
# "ReadWritePaths". This leaves most of the filesystem read-only to borgmatic.
|
||||||
|
ProtectSystem=full
|
||||||
|
# ReadWritePaths=-/mnt/my_backup_drive
|
||||||
|
# This will mount a tmpfs on top of /root and pass through needed paths
|
||||||
|
# TemporaryFileSystem=/root:ro
|
||||||
|
# BindPaths=-/root/.cache/borg -/root/.config/borg -/root/.borgmatic
|
||||||
|
# BindReadOnlyPaths=-/root/.ssh
|
||||||
|
|
||||||
|
# May interfere with running external programs within borgmatic hooks.
|
||||||
|
CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_NET_RAW
|
||||||
|
|
||||||
|
# Lower CPU and I/O priority.
|
||||||
|
Nice=19
|
||||||
|
CPUSchedulingPolicy=batch
|
||||||
|
IOSchedulingClass=best-effort
|
||||||
|
IOSchedulingPriority=7
|
||||||
|
IOWeight=100
|
||||||
|
|
||||||
|
Restart=no
|
||||||
|
# Prevent rate limiting of borgmatic log events. If you are using an older version of systemd that
|
||||||
|
# doesn't support this (pre-240 or so), you may have to remove this option.
|
||||||
|
LogRateLimitIntervalSec=0
|
||||||
|
|
||||||
|
ExecStart=systemd-inhibit --who="borgmatic" --what="sleep:shutdown" --why="Prevent interrupting scheduled backup" /usr/bin/borgmatic -c "/etc/borgmatic.d/%I.yaml" --verbosity -2 --syslog-verbosity 1
|
||||||
1
roles/borgmatic_config/README.md
Normal file
1
roles/borgmatic_config/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Creates a bormatic configuration in /etc/borgmatic.d/ and creates the repos
|
||||||
8
roles/borgmatic_config/defaults/main.yaml
Normal file
8
roles/borgmatic_config/defaults/main.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
borgmatic_config_backup_frequency:
|
||||||
|
unit: h
|
||||||
|
amount: 1
|
||||||
|
|
||||||
|
borgmatic_config_keep_backups_months: 6
|
||||||
|
|
||||||
|
borgmatic_config_targets: "{{ borgmatic_targets }}"
|
||||||
13
roles/borgmatic_config/handlers/main.yaml
Normal file
13
roles/borgmatic_config/handlers/main.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
- name: Initialize borgmatic
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: borgmatic init --encryption repokey
|
||||||
|
register: _borgmatic_init_out
|
||||||
|
changed_when: _borgmatic_init_out.stdout | length > 0
|
||||||
|
|
||||||
|
- name: Restart borgmatic timer {{ borgmatic_config_name }}
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: "borgmatic@{{ borgmatic_config_name }}.timer"
|
||||||
|
state: restarted
|
||||||
|
daemon_reload: true
|
||||||
|
ignore_errors: "{{ ansible_check_mode }}"
|
||||||
65
roles/borgmatic_config/meta/argument_specs.yaml
Normal file
65
roles/borgmatic_config/meta/argument_specs.yaml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Borgmatic config
|
||||||
|
description:
|
||||||
|
- Creates a bormatic configuration in /etc/borgmatic.d/ and creates the repos
|
||||||
|
options:
|
||||||
|
borgmatic_config_name:
|
||||||
|
description:
|
||||||
|
- Name of the borgmatic config.
|
||||||
|
- Must be unique within the (source) host.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
borgmatic_config_directories:
|
||||||
|
description: Directories to backup
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
|
elements: str
|
||||||
|
borgmatic_config_encryption_passphrase:
|
||||||
|
description: Passphrase for borg repo encryption
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
borgmatic_config_targets:
|
||||||
|
description:
|
||||||
|
- List of backup targets for this config.
|
||||||
|
- All backup targets and directories must be listed in borgmatic_targets.
|
||||||
|
- Defaults to all defined in borgmatic_targets.
|
||||||
|
type: list
|
||||||
|
required: false
|
||||||
|
elements: dict
|
||||||
|
options:
|
||||||
|
host:
|
||||||
|
description: Target hostname
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
directories:
|
||||||
|
description: Directories on the host where backup repos will be created under
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
|
elements: str
|
||||||
|
borgmatic_config_backup_frequency:
|
||||||
|
description: How often to take backups. Defaults to once per hour.
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default:
|
||||||
|
unit: h
|
||||||
|
amount: 1
|
||||||
|
options:
|
||||||
|
unit:
|
||||||
|
description: Time unit
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
choices:
|
||||||
|
- min
|
||||||
|
- h
|
||||||
|
- d
|
||||||
|
amount:
|
||||||
|
description: Every how many time units to take backpus
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
borgmatic_config_keep_backups_months:
|
||||||
|
description: How many months to keep backups for
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: 6
|
||||||
3
roles/borgmatic_config/meta/main.yaml
Normal file
3
roles/borgmatic_config/meta/main.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- borgmatic
|
||||||
36
roles/borgmatic_config/tasks/main.yaml
Normal file
36
roles/borgmatic_config/tasks/main.yaml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
- name: Fail if hosts or directories listed in borgmatic_config_targets not in borgmatic_targets.
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: All backup targets and directories must be listed in borgmatic_targets.
|
||||||
|
when: >-
|
||||||
|
borgmatic_config_targets
|
||||||
|
| items2dict(key_name='host', value_name='directories')
|
||||||
|
| ansible.builtin.combine(
|
||||||
|
borgmatic_targets | items2dict(key_name='host', value_name='directories'), list_merge='prepend_rp'
|
||||||
|
)
|
||||||
|
| dict2items(key_name='host', value_name='directories')
|
||||||
|
| difference(borgmatic_targets)
|
||||||
|
| length != 0
|
||||||
|
|
||||||
|
- name: Add borgmatic configuration for {{ borgmatic_config_name }}
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: borgmatic.yaml.j2
|
||||||
|
dest: /etc/borgmatic.d/{{ borgmatic_config_name }}.yaml
|
||||||
|
mode: "0600"
|
||||||
|
no_log: true
|
||||||
|
notify: Initialize borgmatic
|
||||||
|
|
||||||
|
- name: Add systemd timer for borgmatic {{ borgmatic_config_name }}
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: borgmatic@.timer.j2
|
||||||
|
dest: /etc/systemd/system/borgmatic@{{ borgmatic_config_name }}.timer
|
||||||
|
mode: "0644"
|
||||||
|
register: _borgmatic_config_systemd_timer
|
||||||
|
notify: Restart borgmatic timer {{ borgmatic_config_name }}
|
||||||
|
|
||||||
|
- name: Enable systemd timer for borgmatic {{ borgmatic_config_name }}
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
name: borgmatic@{{ borgmatic_config_name }}.timer
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
when: "not (ansible_check_mode and _borgmatic_config_systemd_timer.changed)"
|
||||||
35
roles/borgmatic_config/templates/borgmatic.yaml.j2
Normal file
35
roles/borgmatic_config/templates/borgmatic.yaml.j2
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
# vim:ft=yaml
|
||||||
|
|
||||||
|
source_directories:
|
||||||
|
{{ borgmatic_config_directories | to_nice_yaml }}
|
||||||
|
repositories:
|
||||||
|
{% for target in borgmatic_config_targets %}
|
||||||
|
{% for directory in target.directories %}
|
||||||
|
- path: "ssh://{{ hostvars[target.host].ansible_user_id }}@{{ target.host }}/{{ directory }}/{{ ansible_fqdn }}/{{ borgmatic_config_name }}"
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
working_directory: "~"
|
||||||
|
|
||||||
|
one_file_system: true
|
||||||
|
exclude_patterns:
|
||||||
|
- /var/cache
|
||||||
|
exclude_caches: true
|
||||||
|
exclude_if_present:
|
||||||
|
- .nobackup
|
||||||
|
keep_exclude_tags: true
|
||||||
|
|
||||||
|
source_directories_must_exist: true
|
||||||
|
|
||||||
|
compression: zstd
|
||||||
|
|
||||||
|
encryption_passphrase: "{{ borgmatic_config_encryption_passphrase }}"
|
||||||
|
ssh_command: ssh -i ~/.ssh/id_ed25519_borg
|
||||||
|
|
||||||
|
|
||||||
|
keep_within: 48H
|
||||||
|
keep_hourly: 168
|
||||||
|
keep_daily: 30
|
||||||
|
keep_weekly: 26
|
||||||
|
keep_monthly: {{ (borgmatic_config_keep_backups_months / 2) | round(0, 'ceil') | int }}
|
||||||
|
keep_yearly: {{ (borgmatic_config_keep_backups_months / 12) | round(0, 'ceil') | int }}
|
||||||
21
roles/borgmatic_config/templates/borgmatic@.timer.j2
Normal file
21
roles/borgmatic_config/templates/borgmatic@.timer.j2
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Run borgmatic backup
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
{% if borgmatic_config_backup_frequency.unit == "min" %}
|
||||||
|
OnCalendar=*:0/{{ borgmatic_config_backup_frequency.amount }}
|
||||||
|
{% elif borgmatic_config_backup_frequency.unit == "h" %}
|
||||||
|
OnCalendar=0/{{ borgmatic_config_backup_frequency.amount }}:30
|
||||||
|
{% elif borgmatic_config_backup_frequency.unit == "d" %}
|
||||||
|
OnCalendar=*-1/{{ borgmatic_config_backup_frequency.amount }} 22:00
|
||||||
|
{% else %}
|
||||||
|
{{ dafuq }}
|
||||||
|
{% endif %}
|
||||||
|
Persistent=true
|
||||||
|
RandomizedDelaySec={{ 10 * borgmatic_config_backup_frequency.amount }}{{ _borgmatic_config_previous_time_unit[borgmatic_config_backup_frequency.unit] }}
|
||||||
|
FixedRandomDelay=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
5
roles/borgmatic_config/vars/main.yaml
Normal file
5
roles/borgmatic_config/vars/main.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
_borgmatic_config_previous_time_unit:
|
||||||
|
min: s
|
||||||
|
h: min
|
||||||
|
d: h
|
||||||
10
roles/caddy/meta/main.yaml
Normal file
10
roles/caddy/meta/main.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.compatcheck
|
||||||
|
vars:
|
||||||
|
compatcheck_supported_distributions:
|
||||||
|
- name: debian
|
||||||
|
version_min: 12
|
||||||
|
- name: ubuntu
|
||||||
|
version_min: 22
|
||||||
|
- role: uumas.general.firewalld
|
||||||
@@ -1,14 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure host distribution is supported
|
|
||||||
ansible.builtin.import_role:
|
|
||||||
name: compatcheck
|
|
||||||
vars:
|
|
||||||
compatcheck_supported_distributions:
|
|
||||||
- name: debian
|
|
||||||
version_min: 11
|
|
||||||
- name: ubuntu
|
|
||||||
version_min: 22
|
|
||||||
|
|
||||||
- name: Ensure legacy caddy apt repository not present
|
- name: Ensure legacy caddy apt repository not present
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /etc/apt/sources.list.d/caddy-stable.list
|
path: /etc/apt/sources.list.d/caddy-stable.list
|
||||||
@@ -51,3 +41,14 @@
|
|||||||
validate: 'caddy validate --config %s --adapter caddyfile'
|
validate: 'caddy validate --config %s --adapter caddyfile'
|
||||||
backup: true
|
backup: true
|
||||||
notify: Reload caddy
|
notify: Reload caddy
|
||||||
|
|
||||||
|
- name: Open ports for caddy
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
service: "{{ item }}"
|
||||||
|
state: enabled
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
loop:
|
||||||
|
- http
|
||||||
|
- https
|
||||||
|
- http3
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ argument_specs:
|
|||||||
- ubuntu
|
- ubuntu
|
||||||
- fedora
|
- fedora
|
||||||
- archlinux
|
- archlinux
|
||||||
|
- macosx
|
||||||
version_min:
|
version_min:
|
||||||
description: Earliest supported major version. Allows any version if not specified.
|
description: Earliest supported major version. Allows any version if not specified.
|
||||||
type: int
|
type: int
|
||||||
@@ -31,7 +32,9 @@ argument_specs:
|
|||||||
type: int
|
type: int
|
||||||
required: false
|
required: false
|
||||||
package_managers:
|
package_managers:
|
||||||
description: List of supported package managers. Defaults to apt for debian and ubuntu, dnf for fedora, pacman for archlinux
|
description: >-
|
||||||
|
List of supported package managers. Defaults to apt for debian and ubuntu,
|
||||||
|
dnf for fedora, pacman for archlinux, homebrew for macosx
|
||||||
type: list
|
type: list
|
||||||
required: false
|
required: false
|
||||||
elements: str
|
elements: str
|
||||||
@@ -40,3 +43,4 @@ argument_specs:
|
|||||||
- dnf
|
- dnf
|
||||||
- pacman
|
- pacman
|
||||||
- atomic_container
|
- atomic_container
|
||||||
|
- homebrew
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ _compatcheck_default_package_managers:
|
|||||||
ubuntu: apt
|
ubuntu: apt
|
||||||
fedora: dnf
|
fedora: dnf
|
||||||
archlinux: pacman
|
archlinux: pacman
|
||||||
|
macosx: homebrew
|
||||||
_compatcheck_default_package_manager: "{{ _compatcheck_default_package_managers[ansible_distribution | lower] }}"
|
_compatcheck_default_package_manager: "{{ _compatcheck_default_package_managers[ansible_distribution | lower] }}"
|
||||||
|
|||||||
12
roles/example/meta/main.yaml
Normal file
12
roles/example/meta/main.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.compatcheck
|
||||||
|
vars:
|
||||||
|
compatcheck_supported_distributions:
|
||||||
|
- name: debian
|
||||||
|
version_min: 8
|
||||||
|
- name: archlinux
|
||||||
|
- name: ubuntu
|
||||||
|
version_min: 16
|
||||||
|
- name: fedora
|
||||||
|
version_min: 29
|
||||||
@@ -1,17 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure host distribution is supported
|
|
||||||
ansible.builtin.import_role:
|
|
||||||
name: compatcheck
|
|
||||||
vars:
|
|
||||||
compatcheck_supported_distributions:
|
|
||||||
- name: debian
|
|
||||||
version_min: 8
|
|
||||||
- name: archlinux
|
|
||||||
- name: ubuntu
|
|
||||||
version_min: 16
|
|
||||||
- name: fedora
|
|
||||||
version_min: 29
|
|
||||||
|
|
||||||
- name: Ping
|
- name: Ping
|
||||||
ansible.builtin.ping:
|
ansible.builtin.ping:
|
||||||
when: example_ping
|
when: example_ping
|
||||||
|
|||||||
1
roles/firewalld/README.md
Normal file
1
roles/firewalld/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Installs firewalld
|
||||||
5
roles/firewalld/meta/argument_specs.yaml
Normal file
5
roles/firewalld/meta/argument_specs.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
description: Installs firewalld
|
||||||
|
options: {}
|
||||||
9
roles/firewalld/meta/main.yaml
Normal file
9
roles/firewalld/meta/main.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.compatcheck
|
||||||
|
vars:
|
||||||
|
compatcheck_supported_distributions:
|
||||||
|
- name: debian
|
||||||
|
version_min: 12
|
||||||
|
- name: ubuntu
|
||||||
|
version_min: 22
|
||||||
4
roles/firewalld/tasks/main.yaml
Normal file
4
roles/firewalld/tasks/main.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
- name: Install firewalld
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: firewalld
|
||||||
11
roles/locale/meta/main.yaml
Normal file
11
roles/locale/meta/main.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.compatcheck
|
||||||
|
vars:
|
||||||
|
compatcheck_supported_distributions:
|
||||||
|
- name: debian
|
||||||
|
version_min: 10
|
||||||
|
- name: ubuntu
|
||||||
|
version_min: 24
|
||||||
|
- name: fedora
|
||||||
|
version_min: 39
|
||||||
@@ -1,16 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure host distribution is supported
|
|
||||||
ansible.builtin.import_role:
|
|
||||||
name: compatcheck
|
|
||||||
vars:
|
|
||||||
compatcheck_supported_distributions:
|
|
||||||
- name: debian
|
|
||||||
version_min: 10
|
|
||||||
- name: ubuntu
|
|
||||||
version_min: 24
|
|
||||||
- name: fedora
|
|
||||||
version_min: 39
|
|
||||||
|
|
||||||
- name: Include variables for os family {{ ansible_os_family }}
|
- name: Include variables for os family {{ ansible_os_family }}
|
||||||
ansible.builtin.include_vars: "{{ ansible_os_family }}.yaml"
|
ansible.builtin.include_vars: "{{ ansible_os_family }}.yaml"
|
||||||
|
|
||||||
|
|||||||
2
roles/prometheus_node_exporter/defaults/main.yaml
Normal file
2
roles/prometheus_node_exporter/defaults/main.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
prometheus_node_exporter_local_network: ""
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Restart prometheus-node-exporter
|
- name: Restart prometheus-node-exporter
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd_service:
|
||||||
name: prometheus-node-exporter
|
name: prometheus-node-exporter
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ argument_specs:
|
|||||||
main:
|
main:
|
||||||
description: Installs and configures prometheus node exporter to listen on local ipv4 address
|
description: Installs and configures prometheus node exporter to listen on local ipv4 address
|
||||||
options:
|
options:
|
||||||
local_network:
|
prometheus_node_exporter_local_network:
|
||||||
description: The local ipv4 network block, listen address is taken from this block
|
description: >-
|
||||||
|
The local ipv4 network block, listen address is taken from this block.
|
||||||
|
If empty, listens on 0.0.0.0
|
||||||
type: str
|
type: str
|
||||||
required: true
|
required: false
|
||||||
|
default: ""
|
||||||
|
|||||||
3
roles/prometheus_node_exporter/meta/main.yaml
Normal file
3
roles/prometheus_node_exporter/meta/main.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.firewalld
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure host distribution is supported
|
- name: Compatibility check
|
||||||
ansible.builtin.import_role:
|
ansible.builtin.import_role:
|
||||||
name: compatcheck
|
name: uumas.general.compatcheck
|
||||||
vars:
|
vars:
|
||||||
compatcheck_supported_distributions:
|
compatcheck_supported_distributions:
|
||||||
- name: debian
|
- name: debian
|
||||||
version_min: 11
|
version_min: 11
|
||||||
- name: ubuntu
|
- name: ubuntu
|
||||||
version_min: 24
|
version_min: 22
|
||||||
|
|
||||||
- name: Install prometheus node exporter
|
- name: Install prometheus node exporter
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name: prometheus-node-exporter
|
name: prometheus-node-exporter
|
||||||
|
install_recommends: false
|
||||||
|
|
||||||
- name: Set prometheus options in /etc/default/prometheus-node-exporter
|
- name: Set prometheus options in /etc/default/prometheus-node-exporter
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ARGS="--web.listen-address {{ (ansible_all_ipv4_addresses | ansible.utils.ipaddr(local_network))[0] }}:9100 --collector.logind --collector.systemd --collector.processes"
|
ARGS="--web.listen-address {{ (ansible_all_ipv4_addresses | ansible.utils.ipaddr(prometheus_node_exporter_local_network))[0] if prometheus_node_exporter_local_network | length > 0 else '0.0.0.0' }}:9100 --collector.logind --collector.systemd --collector.processes"
|
||||||
|
|||||||
11
roles/users/meta/main.yaml
Normal file
11
roles/users/meta/main.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- role: uumas.general.compatcheck
|
||||||
|
vars:
|
||||||
|
compatcheck_supported_distributions:
|
||||||
|
- name: debian
|
||||||
|
version_min: 11
|
||||||
|
- name: ubuntu
|
||||||
|
version_min: 22
|
||||||
|
- name: fedora
|
||||||
|
version_min: 39
|
||||||
@@ -1,16 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure host distribution is supported
|
|
||||||
ansible.builtin.import_role:
|
|
||||||
name: compatcheck
|
|
||||||
vars:
|
|
||||||
compatcheck_supported_distributions:
|
|
||||||
- name: debian
|
|
||||||
version_min: 11
|
|
||||||
- name: ubuntu
|
|
||||||
version_min: 24
|
|
||||||
- name: fedora
|
|
||||||
version_min: 39
|
|
||||||
|
|
||||||
- name: Ensure sudo is installed
|
- name: Ensure sudo is installed
|
||||||
ansible.builtin.package:
|
ansible.builtin.package:
|
||||||
name: sudo
|
name: sudo
|
||||||
@@ -38,7 +26,8 @@
|
|||||||
- name: Set ssh authorized keys for users
|
- name: Set ssh authorized keys for users
|
||||||
ansible.posix.authorized_key:
|
ansible.posix.authorized_key:
|
||||||
user: "{{ item.name }}"
|
user: "{{ item.name }}"
|
||||||
key: "{{ item.ssh_pubkey }}"
|
key: "{{ item.ssh_pubkeys | default([item.ssh_pubkey]) | join('\n') }}"
|
||||||
|
exclusive: true
|
||||||
when: item.state | default('present') == 'present'
|
when: item.state | default('present') == 'present'
|
||||||
loop: "{{ users }}"
|
loop: "{{ users }}"
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,18 @@ vhost_basicauth_users: {}
|
|||||||
vhost_proxy_target_netproto: tcp
|
vhost_proxy_target_netproto: tcp
|
||||||
vhost_proxy_target_protocol: http
|
vhost_proxy_target_protocol: http
|
||||||
vhost_proxy_target_host: localhost
|
vhost_proxy_target_host: localhost
|
||||||
|
vhost_proxy_headers: {}
|
||||||
vhost_proxy_delete_headers: []
|
vhost_proxy_delete_headers: []
|
||||||
|
vhost_proxy_pass_host_header: true
|
||||||
|
vhost_proxy_auth_socket: ""
|
||||||
|
vhost_proxy_auth_uri: ""
|
||||||
|
vhost_proxy_auth_unauthorized_redir: ""
|
||||||
|
|
||||||
vhost_redirect_type: temporary
|
vhost_redirect_type: temporary
|
||||||
vhost_redirect_preserve_path: false
|
vhost_redirect_preserve_path: false
|
||||||
vhost_redirect_preserve_query: "{{ vhost_redirect_preserve_path }}"
|
vhost_redirect_preserve_query: "{{ vhost_redirect_preserve_path }}"
|
||||||
|
|
||||||
vhost_respond_content_type: plain
|
vhost_respond_content_type: plain
|
||||||
|
vhost_respond_status: 200
|
||||||
|
|
||||||
|
vhost_matchers: []
|
||||||
|
|||||||
@@ -103,12 +103,43 @@ argument_specs:
|
|||||||
- Only applicable if vhost_type is reverse_proxy and vhost_proxy_target_netproto is unix.
|
- Only applicable if vhost_type is reverse_proxy and vhost_proxy_target_netproto is unix.
|
||||||
type: str
|
type: str
|
||||||
required: false
|
required: false
|
||||||
|
vhost_proxy_headers:
|
||||||
|
description: Dict of request headers and their values to set for proxied requests
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: {}
|
||||||
vhost_proxy_delete_headers:
|
vhost_proxy_delete_headers:
|
||||||
description: List of headers to delete from proxied requests
|
description: List of headers to delete from proxied requests
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
required: false
|
required: false
|
||||||
default: []
|
default: []
|
||||||
|
vhost_proxy_pass_host_header:
|
||||||
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
vhost_proxy_auth_socket:
|
||||||
|
description: >-
|
||||||
|
Unix socket path to forward requests to for auhtentication, before
|
||||||
|
proxying them
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
vhost_proxy_auth_uri:
|
||||||
|
description: >-
|
||||||
|
The authentication endpoint of the auth host. Required if
|
||||||
|
proxy_auth_socket is defined. Does nothing otherwise.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
vhost_proxy_auth_unauthorized_redir:
|
||||||
|
description: >-
|
||||||
|
Where to redirect requests if authentication service returns 401
|
||||||
|
unathorized. If not set, returns responses as is.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
vhost_redirect_target:
|
vhost_redirect_target:
|
||||||
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
||||||
@@ -145,6 +176,199 @@ argument_specs:
|
|||||||
choices:
|
choices:
|
||||||
- plain
|
- plain
|
||||||
- json
|
- json
|
||||||
|
vhost_respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: 200
|
||||||
|
|
||||||
|
vhost_matchers:
|
||||||
|
description: >
|
||||||
|
List of matchers to handle differently from the default for vhost.
|
||||||
|
A matcher matches if all of its conditions match
|
||||||
|
type: list
|
||||||
|
elements: dict
|
||||||
|
required: false
|
||||||
|
default: []
|
||||||
|
options:
|
||||||
|
name:
|
||||||
|
description: Name of the matcher used to reference it
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
|
||||||
|
match_methods:
|
||||||
|
description: HTTP methods to match against. Matching one method is enough.
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
choices:
|
||||||
|
- GET
|
||||||
|
- HEAD
|
||||||
|
- OPTIONS
|
||||||
|
- TRACE
|
||||||
|
- PUT
|
||||||
|
- DELETE
|
||||||
|
- POST
|
||||||
|
- PATCH
|
||||||
|
- CONNECT
|
||||||
|
required: false
|
||||||
|
default: []
|
||||||
|
match_headers:
|
||||||
|
description: >-
|
||||||
|
Headers to match against.
|
||||||
|
If the value begins with ^ and end with $, the value is matched as regex.
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: {}
|
||||||
|
|
||||||
|
type:
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_type }}"
|
||||||
|
choices:
|
||||||
|
- reverse_proxy
|
||||||
|
- redirect
|
||||||
|
- respond
|
||||||
|
headers:
|
||||||
|
description: Dict of response headers and their values
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_headers }}"
|
||||||
|
delete_headers:
|
||||||
|
description: List of response headers to delete
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_delete_headers }}"
|
||||||
|
|
||||||
|
basicauth:
|
||||||
|
description: Whether to require basic auth for the location
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_basicauth }}"
|
||||||
|
basicauth_users:
|
||||||
|
description: A dict of basic auth users and their password hashes. Required if basicauth is true
|
||||||
|
type: dict
|
||||||
|
default: "{{ vhost_basicauth_users }}"
|
||||||
|
|
||||||
|
proxy_target_netproto:
|
||||||
|
description:
|
||||||
|
- Network protocol to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_netproto }}"
|
||||||
|
choices:
|
||||||
|
- tcp
|
||||||
|
- unix
|
||||||
|
proxy_target_protocol:
|
||||||
|
description:
|
||||||
|
- Transport protocol (scheme) to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_protocol }}"
|
||||||
|
choices:
|
||||||
|
- http
|
||||||
|
- https
|
||||||
|
proxy_target_host:
|
||||||
|
description: Host where to proxy requests to. Only applicable if type is reverse_proxy
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_host }}"
|
||||||
|
proxy_target_port:
|
||||||
|
description: Port where to proxy requests to. Only applicable if type is reverse_proxy.
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_port if vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'tcp' else 0 }}"
|
||||||
|
proxy_target_socket:
|
||||||
|
description:
|
||||||
|
- Unix socket path to proxy requests to.
|
||||||
|
- Only applicable if type is reverse_proxy and proxy_target_netproto is unix.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_socket if vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'unix' else '' }}"
|
||||||
|
proxy_headers:
|
||||||
|
description: Dict of request headers and their values to set for proxied requests
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_headers }}"
|
||||||
|
proxy_delete_headers:
|
||||||
|
description: List of request headers to delete from proxied requests
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_delete_headers }}"
|
||||||
|
proxy_pass_host_header:
|
||||||
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_pass_host_header }}"
|
||||||
|
proxy_auth_socket:
|
||||||
|
description: >-
|
||||||
|
Unix socket path to forward requests to for auhtentication, before
|
||||||
|
proxying them
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_auth_socket }}"
|
||||||
|
proxy_auth_uri:
|
||||||
|
description: >-
|
||||||
|
The authentication endpoint of the auth host. Required if
|
||||||
|
proxy_auth_socket is defined. Does nothing otherwise.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_auth_uri }}"
|
||||||
|
proxy_auth_unauthorized_redir:
|
||||||
|
description: >-
|
||||||
|
Where to redirect requests if authentication service returns 401
|
||||||
|
unathorized. If not set, returns responses as is.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_auth_unauthorized_redir }}"
|
||||||
|
|
||||||
|
redirect_target:
|
||||||
|
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_target if vhost_type == 'redirect' else '' }}"
|
||||||
|
redirect_preserve_path:
|
||||||
|
description: Whether to keep the original request path
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_preserve_path }}"
|
||||||
|
redirect_preserve_query:
|
||||||
|
description: Whether to keep the original request query string
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_preserve_query }}"
|
||||||
|
redirect_type:
|
||||||
|
description: Only applicable if vhost_type is redirect
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_type }}"
|
||||||
|
choices:
|
||||||
|
- temporary
|
||||||
|
- permanent
|
||||||
|
|
||||||
|
respond_content:
|
||||||
|
description: >-
|
||||||
|
Content to respond with.
|
||||||
|
Json content can be set as yaml as long as respond_content_type is set to json.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
||||||
|
respond_content_type:
|
||||||
|
description: Type of the respond content
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_content_type }}"
|
||||||
|
choices:
|
||||||
|
- plain
|
||||||
|
- json
|
||||||
|
respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_status }}"
|
||||||
|
|
||||||
vhost_locations:
|
vhost_locations:
|
||||||
description: List of locations to handle differently from the default for vhost
|
description: List of locations to handle differently from the default for vhost
|
||||||
@@ -154,7 +378,9 @@ argument_specs:
|
|||||||
elements: dict
|
elements: dict
|
||||||
options:
|
options:
|
||||||
path:
|
path:
|
||||||
description: Path to match. Only supports full paths for now.
|
description: >-
|
||||||
|
Path to match.
|
||||||
|
If the value begins with ^ and end with $, the value is matched as regex.
|
||||||
type: str
|
type: str
|
||||||
required: true
|
required: true
|
||||||
type:
|
type:
|
||||||
@@ -224,12 +450,43 @@ argument_specs:
|
|||||||
type: str
|
type: str
|
||||||
required: false
|
required: false
|
||||||
default: "{{ vhost_proxy_target_socket if vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'unix' else '' }}"
|
default: "{{ vhost_proxy_target_socket if vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'unix' else '' }}"
|
||||||
|
proxy_headers:
|
||||||
|
description: Dict of request headers and their values to set for proxied requests
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_headers }}"
|
||||||
proxy_delete_headers:
|
proxy_delete_headers:
|
||||||
description: List of request headers to delete from proxied requests
|
description: List of request headers to delete from proxied requests
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
required: false
|
required: false
|
||||||
default: "{{ vhost_proxy_delete_headers }}"
|
default: "{{ vhost_proxy_delete_headers }}"
|
||||||
|
proxy_pass_host_header:
|
||||||
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_pass_host_header }}"
|
||||||
|
proxy_auth_socket:
|
||||||
|
description: >-
|
||||||
|
Unix socket path to forward requests to for auhtentication, before
|
||||||
|
proxying them
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_auth_socket }}"
|
||||||
|
proxy_auth_uri:
|
||||||
|
description: >-
|
||||||
|
The authentication endpoint of the auth host. Required if
|
||||||
|
proxy_auth_socket is defined. Does nothing otherwise.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_auth_uri }}"
|
||||||
|
proxy_auth_unauthorized_redir:
|
||||||
|
description: >-
|
||||||
|
Where to redirect requests if authentication service returns 401
|
||||||
|
unathorized. If not set, returns responses as is.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_auth_unauthorized_redir }}"
|
||||||
|
|
||||||
redirect_target:
|
redirect_target:
|
||||||
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
||||||
@@ -256,7 +513,9 @@ argument_specs:
|
|||||||
- permanent
|
- permanent
|
||||||
|
|
||||||
respond_content:
|
respond_content:
|
||||||
description: Content to respond with. Json content can be set as yaml as long as respond_content_type is set to json
|
description: >-
|
||||||
|
Content to respond with.
|
||||||
|
Json content can be set as yaml as long as respond_content_type is set to json.
|
||||||
type: str
|
type: str
|
||||||
required: false
|
required: false
|
||||||
default: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
default: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
||||||
@@ -268,3 +527,156 @@ argument_specs:
|
|||||||
choices:
|
choices:
|
||||||
- plain
|
- plain
|
||||||
- json
|
- json
|
||||||
|
respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_status }}"
|
||||||
|
|
||||||
|
matchers:
|
||||||
|
description: >
|
||||||
|
List of matchers to handle differently from the default for vhost.
|
||||||
|
A matcher matches if all of its conditions match.
|
||||||
|
Options without a specified default will default to location's corresponding option.
|
||||||
|
type: list
|
||||||
|
elements: dict
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_matchers }}"
|
||||||
|
options:
|
||||||
|
name:
|
||||||
|
description: Name of the matcher used to reference it
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
|
||||||
|
match_methods:
|
||||||
|
description: HTTP methods to match against. Matching one method is enough.
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
choices:
|
||||||
|
- GET
|
||||||
|
- HEAD
|
||||||
|
- OPTIONS
|
||||||
|
- TRACE
|
||||||
|
- PUT
|
||||||
|
- DELETE
|
||||||
|
- POST
|
||||||
|
- PATCH
|
||||||
|
- CONNECT
|
||||||
|
required: false
|
||||||
|
default: []
|
||||||
|
match_headers:
|
||||||
|
description: >-
|
||||||
|
Headers to match against.
|
||||||
|
If the value begins with ^ and end with $, the value is matched as regex.
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: {}
|
||||||
|
|
||||||
|
type:
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- reverse_proxy
|
||||||
|
- redirect
|
||||||
|
- respond
|
||||||
|
headers:
|
||||||
|
description: Dict of response headers and their values
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
delete_headers:
|
||||||
|
description: List of response headers to delete
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
|
||||||
|
basicauth:
|
||||||
|
description: Whether to require basic auth for the location
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
basicauth_users:
|
||||||
|
description: A dict of basic auth users and their password hashes. Required if basicauth is true
|
||||||
|
type: dict
|
||||||
|
|
||||||
|
proxy_target_netproto:
|
||||||
|
description:
|
||||||
|
- Network protocol to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- tcp
|
||||||
|
- unix
|
||||||
|
proxy_target_protocol:
|
||||||
|
description:
|
||||||
|
- Transport protocol (scheme) to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- http
|
||||||
|
- https
|
||||||
|
proxy_target_host:
|
||||||
|
description: Host where to proxy requests to. Only applicable if type is reverse_proxy
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
proxy_target_port:
|
||||||
|
description: Port where to proxy requests to. Only applicable if type is reverse_proxy.
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
proxy_target_socket:
|
||||||
|
description:
|
||||||
|
- Unix socket path to proxy requests to.
|
||||||
|
- Only applicable if type is reverse_proxy and proxy_target_netproto is unix.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
proxy_headers:
|
||||||
|
description: Dict of request headers and their values to set for proxied requests
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
proxy_delete_headers:
|
||||||
|
description: List of request headers to delete from proxied requests
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
proxy_pass_host_header:
|
||||||
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
|
||||||
|
redirect_target:
|
||||||
|
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
redirect_preserve_path:
|
||||||
|
description: Whether to keep the original request path
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
redirect_preserve_query:
|
||||||
|
description: Whether to keep the original request query string
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
redirect_type:
|
||||||
|
description: Only applicable if vhost_type is redirect
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- temporary
|
||||||
|
- permanent
|
||||||
|
|
||||||
|
respond_content:
|
||||||
|
description: >-
|
||||||
|
Content to respond with.
|
||||||
|
Json content can be set as yaml as long as respond_content_type is set to json.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
respond_content_type:
|
||||||
|
description: Type of the respond content
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- plain
|
||||||
|
- json
|
||||||
|
respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
|||||||
@@ -3,56 +3,7 @@
|
|||||||
ansible.builtin.blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: /etc/caddy/Caddyfile
|
path: /etc/caddy/Caddyfile
|
||||||
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ vhost_id }}"
|
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ vhost_id }}"
|
||||||
# yamllint disable rule:line-length
|
block: "{{ lookup('ansible.builtin.template', 'Caddyfile_block.j2') }}"
|
||||||
block: |
|
|
||||||
{{ vhost_domains | join(' ') }} {
|
|
||||||
{% for location in _vhost_locations_complete %}
|
|
||||||
handle {{ location.path }} {
|
|
||||||
{% for header in location.delete_headers %}
|
|
||||||
header -{{ header }}
|
|
||||||
{% endfor %}
|
|
||||||
{% for header in location.headers | dict2items %}
|
|
||||||
header {{ header.key }} `{{ header.value }}`
|
|
||||||
{% endfor %}
|
|
||||||
{% if location.basicauth %}
|
|
||||||
basicauth {
|
|
||||||
{% for user in location.basicauth_users | dict2items %}
|
|
||||||
{{ user.key }} {{ user.value }}
|
|
||||||
{% endfor %}
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
{% if location.type == 'reverse_proxy' %}
|
|
||||||
reverse_proxy {
|
|
||||||
{% if location.proxy_target_netproto == 'tcp' %}
|
|
||||||
to tcp/{{ location.proxy_target_host }}:{{ location.proxy_target_port }}
|
|
||||||
{% else %}
|
|
||||||
to unix/{{ location.proxy_target_socket }}
|
|
||||||
{% endif %}
|
|
||||||
{% if location.proxy_target_protocol == 'https' %}
|
|
||||||
transport http {
|
|
||||||
tls
|
|
||||||
{% if location.proxy_target_host == 'localhost' %}
|
|
||||||
tls_insecure_skip_verify
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% for header in location.proxy_delete_headers %}
|
|
||||||
request_header -{{ header }}
|
|
||||||
{% endfor %}
|
|
||||||
{% elif location.type == 'redirect' %}
|
|
||||||
redir * {{ location.redirect_target }}{{ '{path}' if location.redirect_preserve_path }}{{ '?{query}' if location.redirect_preserve_query }} {{ location.redirect_type }}
|
|
||||||
{% elif location.type == 'respond' %}
|
|
||||||
{% if location.respond_content_type == 'json' %}
|
|
||||||
respond `{{ location.respond_content | to_json }}`
|
|
||||||
{% else %}
|
|
||||||
respond `{{ location.respond_content }}`
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
{% endfor %}
|
|
||||||
}
|
|
||||||
# yamllint enable rule:line-length
|
|
||||||
validate: 'caddy validate --config %s --adapter caddyfile'
|
validate: 'caddy validate --config %s --adapter caddyfile'
|
||||||
backup: true
|
backup: true
|
||||||
state: "{{ vhost_state }}"
|
state: "{{ vhost_state }}"
|
||||||
|
|||||||
85
roles/vhost/templates/Caddyfile_block.j2
Normal file
85
roles/vhost/templates/Caddyfile_block.j2
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#jinja2: lstrip_blocks: True
|
||||||
|
{{ vhost_domains | join(' ') }} {
|
||||||
|
{% for location in _vhost_locations_complete %}
|
||||||
|
{% if location.path != '' %}
|
||||||
|
@{{ location.path }} path{{ '_regexp' if location.path.startswith('^') and location.path.endswith('$') else '' }} {{ location.path }}
|
||||||
|
handle @{{ location.path }} {
|
||||||
|
{% else %}
|
||||||
|
handle {
|
||||||
|
{% endif %}
|
||||||
|
{% for matcher in location.matchers %}
|
||||||
|
{% if matcher.name != '' %}
|
||||||
|
@{{ matcher.name }} {
|
||||||
|
{% if matcher.match_methods | length > 0 %}
|
||||||
|
method {{ matcher.match_methods | join(' ') }}
|
||||||
|
{% endif %}
|
||||||
|
{% for header in matcher.match_headers | dict2items %}
|
||||||
|
header{{ '_regexp' if header.value.startswith('^') and header.value.endswith('$') else '' }} {{ header.key }} {{ header.value }}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
handle{{ ' @' ~ matcher.name if matcher.name != '' else '' }} {
|
||||||
|
{% for header in matcher.delete_headers %}
|
||||||
|
header -{{ header }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for header in matcher.headers | dict2items %}
|
||||||
|
header {{ header.key }} `{{ header.value }}`
|
||||||
|
{% endfor %}
|
||||||
|
{% if matcher.basicauth %}
|
||||||
|
basicauth {
|
||||||
|
{% for user in matcher.basicauth_users | dict2items %}
|
||||||
|
{{ user.key }} {{ user.value }}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if matcher.type == 'reverse_proxy' %}
|
||||||
|
{% if matcher.proxy_auth_socket | length > 0 %}
|
||||||
|
forward_auth {
|
||||||
|
to unix//{{ matcher.proxy_auth_socket }}
|
||||||
|
uri {{ matcher.proxy_auth_uri }}
|
||||||
|
{% if matcher.proxy_auth_unauthorized_redir | length > 0 %}
|
||||||
|
@unauthorized status 401
|
||||||
|
handle_response @unauthorized {
|
||||||
|
redir * {{ matcher.proxy_auth_unauthorized_redir }}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
reverse_proxy {
|
||||||
|
{% if matcher.proxy_target_netproto == 'tcp' %}
|
||||||
|
to tcp/{{ matcher.proxy_target_host }}:{{ matcher.proxy_target_port }}
|
||||||
|
{% else %}
|
||||||
|
to unix/{{ matcher.proxy_target_socket }}
|
||||||
|
{% endif %}
|
||||||
|
{% if matcher.proxy_target_protocol == 'https' %}
|
||||||
|
transport http {
|
||||||
|
tls
|
||||||
|
{% if matcher.proxy_target_host == 'localhost' %}
|
||||||
|
tls_insecure_skip_verify
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% for header in matcher.proxy_delete_headers %}
|
||||||
|
header_up -{{ header }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for header in matcher.proxy_headers | dict2items %}
|
||||||
|
header_up {{ header.key }} `{{ header.value }}`
|
||||||
|
{% endfor %}
|
||||||
|
{% if (not matcher.proxy_pass_host_header) and ('host' not in matcher.proxy_headers | map('lower')) %}
|
||||||
|
header_up Host {upstream_hostport}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% elif matcher.type == 'redirect' %}
|
||||||
|
redir * {{ matcher.redirect_target }}{{ '{path}' if matcher.redirect_preserve_path }}{{ '?{query}' if matcher.redirect_preserve_query }} {{ matcher.redirect_type }}
|
||||||
|
{% elif matcher.type == 'respond' %}
|
||||||
|
{% if matcher.respond_content_type == 'json' %}
|
||||||
|
respond `{{ matcher.respond_content | to_json }}`
|
||||||
|
{% else %}
|
||||||
|
respond `{{ matcher.respond_content }}` {{ matcher.respond_status }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
@@ -1,4 +1,15 @@
|
|||||||
---
|
---
|
||||||
|
_vhost_matcher_defaults:
|
||||||
|
match_headers: {}
|
||||||
|
match_method: []
|
||||||
|
_vhost_matchers: >-
|
||||||
|
{{
|
||||||
|
vhost_matchers
|
||||||
|
| map('combine', _vhost_matcher_defaults)
|
||||||
|
| zip(vhost_matchers)
|
||||||
|
| map('combine')
|
||||||
|
}}
|
||||||
|
|
||||||
_vhost_location_defaults:
|
_vhost_location_defaults:
|
||||||
type: "{{ vhost_type }}"
|
type: "{{ vhost_type }}"
|
||||||
headers: "{{ vhost_headers }}"
|
headers: "{{ vhost_headers }}"
|
||||||
@@ -14,7 +25,12 @@ _vhost_location_defaults:
|
|||||||
vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'tcp' else '' }}"
|
vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'tcp' else '' }}"
|
||||||
proxy_target_socket: "{{ vhost_proxy_target_socket if
|
proxy_target_socket: "{{ vhost_proxy_target_socket if
|
||||||
vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'unix' else '' }}"
|
vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'unix' else '' }}"
|
||||||
|
proxy_headers: "{{ vhost_proxy_headers }}"
|
||||||
proxy_delete_headers: "{{ vhost_proxy_delete_headers }}"
|
proxy_delete_headers: "{{ vhost_proxy_delete_headers }}"
|
||||||
|
proxy_pass_host_header: "{{ vhost_proxy_pass_host_header }}"
|
||||||
|
proxy_auth_socket: "{{ vhost_proxy_auth_socket }}"
|
||||||
|
proxy_auth_uri: "{{ vhost_proxy_auth_uri }}"
|
||||||
|
proxy_auth_unauthorized_redir: "{{ vhost_proxy_auth_unauthorized_redir }}"
|
||||||
|
|
||||||
redirect_target: "{{ vhost_redirect_target if vhost_type == 'redirect' else '' }}"
|
redirect_target: "{{ vhost_redirect_target if vhost_type == 'redirect' else '' }}"
|
||||||
redirect_preserve_path: "{{ vhost_redirect_preserve_path }}"
|
redirect_preserve_path: "{{ vhost_redirect_preserve_path }}"
|
||||||
@@ -23,12 +39,36 @@ _vhost_location_defaults:
|
|||||||
|
|
||||||
respond_content: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
respond_content: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
||||||
respond_content_type: "{{ vhost_respond_content_type }}"
|
respond_content_type: "{{ vhost_respond_content_type }}"
|
||||||
|
respond_status: "{{ vhost_respond_status }}"
|
||||||
|
|
||||||
|
matchers: "{{ _vhost_matchers }}"
|
||||||
|
|
||||||
_vhost_locations: "{{ vhost_locations + [{'path': ''}] }}"
|
_vhost_locations: "{{ vhost_locations + [{'path': ''}] }}"
|
||||||
|
|
||||||
_vhost_locations_complete: "{{
|
_vhost_locations_withdefaults: >-
|
||||||
|
{{
|
||||||
_vhost_locations
|
_vhost_locations
|
||||||
| map('combine', _vhost_location_defaults)
|
| map('combine', _vhost_location_defaults)
|
||||||
| zip(_vhost_locations)
|
| zip(
|
||||||
|
_vhost_locations
|
||||||
|
)
|
||||||
| map('combine')
|
| map('combine')
|
||||||
}}"
|
| map('combine', {'matchers': [{'name': ''}]}, list_merge='append')
|
||||||
|
}}
|
||||||
|
|
||||||
|
_vhost_locations_complete: >-
|
||||||
|
{{
|
||||||
|
_vhost_locations_withdefaults |
|
||||||
|
sort(attribute='path') |
|
||||||
|
zip(
|
||||||
|
_vhost_locations_withdefaults |
|
||||||
|
subelements('matchers') |
|
||||||
|
map('combine') |
|
||||||
|
groupby('path') |
|
||||||
|
map('last') |
|
||||||
|
map('community.general.remove_keys', ['matchers', 'path']) |
|
||||||
|
map('community.general.dict_kv', 'matchers')
|
||||||
|
) |
|
||||||
|
map('combine') |
|
||||||
|
reverse
|
||||||
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user