Compare commits

..

1 Commits

Author SHA1 Message Date
3c8fc29746 feat(roles/lego): Run hooks with bash -c
ref #13
2025-11-28 12:20:16 +01:00
55 changed files with 50 additions and 618 deletions

View File

@@ -1,21 +0,0 @@
---
# see https://docs.ansible.com/projects/lint/configuring/#ansible-lint-configuration
profile: moderate
exclude_paths:
- ".ansible/"
# Enable checking of loop variable prefixes in roles
loop_var_prefix: "^(__|{role}_)"
# Enforce variable names to follow pattern below, in addition to Ansible own
# requirements, like avoiding python identifiers. To disable add `var-naming`
# to skip_list.
var_naming_pattern: "^[a-z_][a-z0-9_]*$"
skip_list:
- "name[template]"
enable_list:
- galaxy
- fqcn
offline: true

2
.gitignore vendored
View File

@@ -1,2 +0,0 @@
.ansible/
results/

View File

@@ -5,8 +5,6 @@
This ansible collection provides various roles for installing This ansible collection provides various roles for installing
and configuring basic system utilities like gnupg, ssh etc and configuring basic system utilities like gnupg, ssh etc
- [`caddy`](roles/caddy/README.md): configures and runs caddy
- [`git`](roles/git/README.md): configures git on the target system - [`git`](roles/git/README.md): configures git on the target system
- [`gnupg`](roles/gnupg/README.md): configures gnupg on the target system - [`gnupg`](roles/gnupg/README.md): configures gnupg on the target system

View File

@@ -1,6 +1,6 @@
namespace: finallycoffee namespace: finallycoffee
name: base name: base
version: 0.4.1 version: 0.4.0
readme: README.md readme: README.md
authors: authors:
- transcaffeine <transcaffeine@finally.coffee> - transcaffeine <transcaffeine@finally.coffee>
@@ -8,9 +8,9 @@ description: >-2
Roles for base services which are core functionality like managing packages Roles for base services which are core functionality like managing packages
and ssh or common dependencies other services like databases and ssh or common dependencies other services like databases
dependencies: dependencies:
"community.docker": ">=4.7.0" "community.docker": "^4.7.0"
"community.general": ">=11.1.2" "community.general": "^11.1.2"
"containers.podman": ">=1.17.0" "containers.podman": "^1.17.0"
license_file: LICENSE.md license_file: LICENSE.md
build_ignore: build_ignore:
- '*.tar.gz' - '*.tar.gz'
@@ -24,7 +24,6 @@ tags:
- lego - lego
- minio - minio
- nginx - nginx
- caddy
- restic - restic
- user_management - user_management
- openldap - openldap

View File

@@ -1,7 +0,0 @@
---
- name: Install and configure caddy
hosts: "{{ caddy_hosts | default('caddy') }}"
become: "{{ caddy_become | default(false) }}"
gather_facts: "{{ caddy_gather_facts | default(false) }}"
roles:
- role: finallycoffee.base.caddy

View File

@@ -1,31 +0,0 @@
---
- name: Ensure reverse proxy configuration is created
hosts: "{{ target_hosts }}"
become: "{{ target_become | default(false) }}"
gather_facts: "{{ target_gather_facts | default(false) }}"
roles:
- role: finallycoffee.base.caddy_site
vars:
caddy_site_cert_basepath: >-2
{{ caddy_site_tls_store | default('/tls') }}/{{ caddy_site_name }}/certificates/{{ caddy_site_name }}
caddy_site_config: >-2
{{ caddy_site_config_override | default(caddy_site_default_config, true) }}
caddy_site_default_config: |+2
https://{{ caddy_site_name }} {
tls {{ caddy_site_cert_basepath}}.crt {{ caddy_site_cert_basepath }}.key
header {
Strict-Transport-Security "max-age=31536000"
}
encode zstd gzip
{% if caddy_reverse_proxy_template_block | default(true) -%}
reverse_proxy {{ caddy_reverse_proxy_backend_addr }} {
{{ caddy_reverse_proxy_extra_config | default('') | indent(6) }}
{%- if caddy_reverse_proxy_import_proxyheaders | default(true, true) %}
import proxyheaders
{%- endif +%}
}
{%- else -%}
{{- caddy_reverse_proxy_block | default('') | indent(4) }}
{%- endif +%}
}

View File

@@ -13,4 +13,4 @@
loop: "{{ docker_registries | default([], true) }}" loop: "{{ docker_registries | default([], true) }}"
loop_control: loop_control:
loop_var: "docker_registry" loop_var: "docker_registry"
label: "{{ docker_registry.username }}@{{ docker_registry.registry }}" label: "{{ docker_registry.username}}@{{ docker_registry.registry }}"

View File

@@ -1,54 +0,0 @@
---
- name: Configure shorewall for docker egress
hosts: "{{ docker_shorewall_hosts | default('docker:&shorewall') }}"
become: "{{ docker_shorewall_become | default(true, true) }}"
tasks:
- name: Add docker interface
ansible.builtin.lineinfile:
path: /etc/shorewall/interfaces
regex: "^dock"
line: |
dock docker0 bridge
- name: Add docker routing policy
ansible.builtin.blockinfile:
path: /etc/shorewall/policy
insertbefore: "^# THE FOLLOWING POLICY MUST BE LAST"
content: |
# Docker specific configuration
dock all ACCEPT
- name: Add docker zone
ansible.builtin.lineinfile:
path: /etc/shorewall/zones
regex: "^dock"
line: |
dock ipv4
- name: Add docker egress rules
ansible.builtin.blockinfile:
path: /etc/shorewall/rules
marker: "#{mark} ANSIBLE MANAGED BLOCK - DOCKER EGRESS"
content: |
#
# Docker egress configuration
#
ACCEPT dock all
- name: Add docker dns rules
ansible.builtin.blockinfile:
path: /etc/shorewall/rules
marker: "#{mark} ANSIBLE MANAGED BLOCK - DOCKER DNS"
content: |
#
# Docker dns configuration
#
DNS(ACCEPT) dock all
- name: Enable shorewall docker support
ansible.builtin.lineinfile:
path: /etc/shorewall/shorewall.conf
line: "DOCKER=Yes"
regex: "^DOCKER="
- name: Ensure shorewall reloaded
community.general.systemd_service:
service: "{{ item }}"
state: reloaded
loop:
- shorewall.service
- shorewall6.service

View File

@@ -6,7 +6,7 @@
pre_tasks: pre_tasks:
- name: Build target dns records - name: Build target dns records
ansible.builtin.set_fact: ansible.builtin.set_fact:
target_dns_records: "{{ target_dns_records + [_dns_record] }}" target_dns_records: "{{ target_dns_records + [ _dns_record ] }}"
vars: vars:
_dns_record: _dns_record:
type: "CNAME" type: "CNAME"

View File

@@ -1,7 +0,0 @@
---
- name: Install and configure network time protocol daemon
hosts: "{{ ntp_hosts | default('ntp') }}"
become: "{{ ntp_become | default(false) }}"
gather_facts: "{{ ntp_gather_facts | default(false) }}"
roles:
- role: finallycoffee.base.ntp

View File

@@ -1,7 +0,0 @@
---
- name: Configure wireguard interfaces with wg_quick
hosts: "{{ wg_quick_hosts | default('wg_quick') }}"
become: "{{ wg_quick_become | default(false) }}"
gather_facts: "{{ wg_quick_gather_facts | default(false) }}"
roles:
- role: finallycoffee.base.wg_quick

View File

@@ -1,10 +0,0 @@
# `finallycoffee.base.caddy` ansible role
Deploy a (pre-)configure [caddy v2](https://caddyserver.com) web
server / proxy using ansible.
## Configuration
To change the default configuration of reading all files from
`/etc/caddy/sites.d/` (see `caddy_dynamic_config_dir`), specify
your desired configuration in `caddy_config`.

View File

@@ -1,22 +0,0 @@
---
caddy_config: |+2
{
auto_https disable_redirects
}
(proxyheaders) {
header_up X-Forwarded-Ssl on
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
# header_up X-Forwarded-Port {port}
header_up X-Forwarded-Proto {scheme}
header_up X-Url-Scheme {scheme}
header_up X-Forwarded-Host {host}
}
# Import all configurations
import {{ caddy_dynamic_configs_dir }}/*/Caddyfile
:80 {
redir / https://{host}{uri} 301
}

View File

@@ -1,43 +0,0 @@
---
caddy_container_image_registry: "docker.io"
caddy_container_image_namespace: "library"
caddy_container_image_repository: "caddy"
caddy_container_image_name: >-2
{{ [
caddy_container_image_registry | default([], true),
caddy_container_image_namespace | default([], true),
caddy_container_image_repository
] | flatten | join('/') }}
caddy_container_image_tag: ~
caddy_container_image: >-2
{{ [
caddy_container_image_name,
caddy_container_image_tag | default(caddy_version, true)
] | join(':') }}
caddy_container_image_source: "pull"
caddy_container_image_force_source: >-2
{{ caddy_container_image_tag | ansible.builtin.type_debug != 'NoneType' }}
caddy_container_image_state: "{{ caddy_state }}"
caddy_container_name: "caddy"
caddy_container_env: ~
caddy_container_ports: ~
caddy_container_user: ~
caddy_container_labels: ~
caddy_container_volumes: ~
caddy_container_config_dir: "/etc/caddy"
caddy_container_default_volumes:
- "{{ caddy_config_dir }}:{{ caddy_container_config_dir }}:ro"
- "{{ caddy_dynamic_configs_dir }}:{{ caddy_dynamic_configs_dir }}:ro"
- "{{ caddy_config_internal_dir }}:/config:rw"
- "{{ caddy_state_dir }}:/data:rw"
caddy_container_all_volumes: >-2
{{ caddy_container_default_volumes | default([], true)
+ caddy_container_volumes | default([], true) }}
caddy_container_state: >-2
{{ (caddy_state == 'present') | ternary('started', 'absent') }}
caddy_container_restart_policy: "on-failure"
caddy_container_networks: ~
caddy_container_network_mode: ~
caddy_container_etc_hosts: ~

View File

@@ -1,11 +0,0 @@
---
caddy_user: "caddy"
caddy_version: "2.10.2"
caddy_config_file: "/etc/caddy/Caddyfile"
caddy_config_dir: "{{ caddy_config_file | ansible.builtin.dirname }}"
caddy_config_internal_dir: "{{ caddy_config_dir }}/config"
caddy_dynamic_configs_dir: "{{ caddy_config_dir }}/sites.d"
caddy_state_dir: "/var/lib/caddy"
caddy_state: "present"
caddy_deployment_method: "docker"

View File

@@ -1,7 +0,0 @@
---
caddy_user_state: "{{ caddy_state }}"
caddy_user_system: true
caddy_user_create_home: false
caddy_run_uid: "{{ caddy_user_info.uid | default(caddy_user) }}"
caddy_run_gid: "{{ caddy_user_info.group | default(caddy_user) }}"

View File

@@ -1,13 +0,0 @@
---
allow_duplicates: true
dependencies: []
galaxy_info:
role_name: caddy
description: Deploy caddy, a webserver
galaxy_tags:
- caddy
- zerossl
- http
- webserver
- docker
- podman

View File

@@ -1,26 +0,0 @@
---
- name: Ensure container image '{{ caddy_container_image }}' is {{ caddy_container_image_state }}
community.docker.docker_image:
name: "{{ caddy_container_image }}"
state: "{{ caddy_container_image_state }}"
source: "{{ caddy_container_image_source }}"
force_source: "{{ caddy_container_image_force_source }}"
register: caddy_container_image_info
until: caddy_container_image_info is success
retries: 10
delay: 3
- name: Ensure container '{{ caddy_container_name }}' is {{ caddy_container_state }}
community.docker.docker_container:
name: "{{ caddy_container_name }}"
image: "{{ caddy_container_image }}"
state: "{{ caddy_container_state }}"
env: "{{ caddy_container_env | default(omit, true) }}"
user: "{{ caddy_container_user | default(omit, true) }}"
ports: "{{ caddy_container_ports | default(omit, true) }}"
labels: "{{ caddy_container_labels | default(omit, true) }}"
volumes: "{{ caddy_container_all_volumes }}"
networks: "{{ caddy_container_networks | default(omit, true) }}"
etc_hosts: "{{ caddy_container_etc_hosts | default(omit, true) }}"
network_mode: "{{ caddy_container_network_mode | default(omit, true) }}"
restart_policy: "{{ caddy_container_restart_policy }}"

View File

@@ -1,52 +0,0 @@
---
- name: Ensure state '{{ caddy_state }}' is valid
ansible.builtin.fail:
msg: >-2
Unsupported caddy_state '{{ caddy_state }}'.
Supported states are {{ caddy_states | join(', ') }}.
when: caddy_state not in caddy_states
- name: Ensure deployment method '{{ caddy_deployment_method }}' is valid
ansible.builtin.fail:
msg: >-2
Unsupported caddy_deployment_method '{{ caddy_deployment_method }}'.
Supported deployment methods are {{ caddy_deployment_methods | join(', ') }}.
when: caddy_deployment_method not in caddy_deployment_methods
- name: Ensure caddy user '{{ caddy_user }}' is {{ caddy_user_state }}
ansible.builtin.user:
name: "{{ caddy_user }}"
state: "{{ caddy_user_state }}"
system: "{{ caddy_user_system }}"
create_home: "{{ caddy_user_create_home }}"
register: "caddy_user_info"
- name: Ensure base directories are present
ansible.builtin.file:
path: "{{ dir.name }}"
state: "directory"
owner: "{{ dir.owner | default(caddy_run_uid) }}"
group: "{{ dir.group | default(caddy_run_uid) }}"
mode: "{{ dir.mode | default('0750') }}"
when: caddy_state == 'present'
loop:
- name: "{{ caddy_config_dir }}"
- name: "{{ caddy_dynamic_configs_dir }}"
- name: "{{ caddy_config_internal_dir }}"
- name: "{{ caddy_state_dir }}"
loop_control:
loop_var: "dir"
label: "{{ dir.name }}"
- name: Ensure caddy configuration is up to date
ansible.builtin.copy:
dest: "{{ caddy_config_file }}"
content: "{{ caddy_config }}"
owner: "{{ caddy_run_uid }}"
group: "{{ caddy_run_gid }}"
mode: "0640"
when: caddy_state == 'present'
- name: Ensure caddy is deployed using {{ caddy_deployment_method }}
ansible.builtin.include_tasks:
file: "deploy-{{ caddy_deployment_method }}.yml"

View File

@@ -1,6 +0,0 @@
---
caddy_states:
- "present"
- "absent"
caddy_deployment_methods:
- "docker"

View File

@@ -1,7 +0,0 @@
# `finallycoffee.base.caddy_site` ansible role
Provision a single site configuration in caddy.
Set `caddy_site_name` as a unique
site identifier (needs to be a valid filename) and `caddy_site_config`
to contain the actual `Caddyfile` contents.

View File

@@ -1,13 +0,0 @@
---
caddy_site_name: ~
caddy_site_config: ~
caddy_site_state: "present"
caddy_site_configs: "/etc/caddy/sites.d"
caddy_site_config_dir: >-2
{{ caddy_site_configs }}/{{ caddy_site_name }}
caddy_site_config_file: >-2
{{ caddy_site_config_dir }}/Caddyfile
caddy_site_owner: "caddy"
caddy_site_group: "caddy"

View File

@@ -1,11 +0,0 @@
---
allow_duplicates: true
dependencies: []
galaxy_info:
role_name: caddy_site
description: Deploy a sites' configuration in caddy
galaxy_tags:
- caddy
- zerossl
- http
- webserver

View File

@@ -1,26 +0,0 @@
---
- name: Fail if required variables are not populated
ansible.builtin.fail:
msg: "Either `caddy_site_name` or `caddy_site_config` is not provided"
when: >-2
(caddy_site_name | ansible.builtin.type_debug == 'NoneType')
or
(caddy_site_config | ansible.builtin.type_debug == 'NoneType')
- name: Ensure directory for caddy site config '{{ caddy_site_name }}' is {{ caddy_site_state }}
ansible.builtin.file:
path: "{{ caddy_site_config_dir }}"
state: >-2
{{ (caddy_site_state == 'present') | ternary('directory', 'absent') }}
owner: "{{ caddy_site_owner }}"
group: "{{ caddy_site_group }}"
mode: "0750"
- name: Ensure caddy site configuration is templated
ansible.builtin.copy:
dest: "{{ caddy_site_config_file }}"
content: "{{ caddy_site_config }}"
owner: "{{ caddy_site_owner }}"
group: "{{ caddy_site_group }}"
mode: "0640"
when: caddy_site_state == 'present'

View File

@@ -8,7 +8,7 @@
state: present state: present
marker: "#{mark} ANSIBLE MANAGED BLOCK by finallycoffee.base.git" marker: "#{mark} ANSIBLE MANAGED BLOCK by finallycoffee.base.git"
block: |+2 block: |+2
{% if git_config_user_name | default(false, true) and git_config_user_email | default(false, true) %} {% if git_config_user_name|default(false, true) and git_config_user_email|default(false, true) %}
[user] [user]
name = {{ git_config_user_name }} name = {{ git_config_user_name }}
email = {{ git_config_user_email }} email = {{ git_config_user_email }}

View File

@@ -14,7 +14,7 @@ gpg_config_emit_version: false
gpg_config_comments: false gpg_config_comments: false
gpg_config_ignore_time_conflict: false gpg_config_ignore_time_conflict: false
gpg_config_allow_freeform_uid: true gpg_config_allow_freeform_uid: true
gpg_config_keyid_format: "0xlong" gpg_config_keyid_format: 0xlong
gpg_config_with_fingerprint: true gpg_config_with_fingerprint: true
gpg_config_keyserver: hkps://keys.openpgp.org gpg_config_keyserver: hkps://keys.openpgp.org

View File

@@ -1,54 +1,55 @@
--- ---
- name: Ensure gnupg is installed (RedHat*) - name: Ensure gnupg is installed (RedHat*)
ansible.builtin.package: package:
name: gnupg2 name: gnupg2
state: latest state: latest
become: true become: true
when: ansible_os_family == "RedHat" when: ansible_os_family == "RedHat"
- name: Ensure gnupg is installed (Arch) - name: Ensure gnupg is installed (Arch)
ansible.builtin.package: package:
name: gnupg name: gnupg
state: latest state: latest
become: true become: true
when: ansible_os_family == "Archlinux" when: ansible_os_family == "Archlinux"
- name: Ensure ~/.gnupg folder exists with correct permissions - name: Ensure ~/.gnupg folder exists with correct permissions
ansible.builtin.file: file:
path: "{{ gpg_config_folder }}" path: "{{ gpg_config_folder }}"
state: directory state: directory
mode: "0700" mode: 0700
- name: Ensure gpg.conf is templated - name: Ensure gpg.conf is templated
ansible.builtin.template: template:
src: gpg.conf.j2 src: gpg.conf.j2
dest: "{{ gpg_config_file }}" dest: "{{ gpg_config_file }}"
- name: Configure gpg-agent.conf (agent configuration) - name: Configure gpg-agent.conf (agent configuration)
ansible.builtin.template: template:
src: gpg-agent.conf.j2 src: gpg-agent.conf.j2
dest: "{{ gpg_agent_config_file }}" dest: "{{ gpg_agent_config_file }}"
- name: Configure scdaemon.conf (smartcard daemon) - name: Configure scdaemon.conf (smartcard daemon)
ansible.builtin.template: template:
src: scdaemon.conf.j2 src: scdaemon.conf.j2
dest: "{{ gpg_scdaemon_config_file }}" dest: "{{ gpg_scdaemon_config_file }}"
- name: Configure sshcontrol (in order for gpg-agent to act as ssh-agent) - name: Configure sshcontrol (in order for gpg-agent to act as ssh-agent)
ansible.builtin.template: template:
src: sshcontrol.j2 src: sshcontrol.j2
dest: "{{ gpg_agent_sshcontrol_file }}" dest: "{{ gpg_agent_sshcontrol_file }}"
when: gpg_agent_config_enable_ssh_support when: gpg_agent_config_enable_ssh_support
- name: Copy gnupg_agent script, which makes gpg-agent responsible for ssh-auth - name: Copy gnupg_agent script, which makes gpg-agent responsible for ssh-auth
ansible.builtin.copy: copy:
src: gpg-configure-ssh-auth-socket.sh src: gpg-configure-ssh-auth-socket.sh
dest: "{{ gpg_configure_agent_script }}" dest: "{{ gpg_configure_agent_script }}"
mode: "0700" mode: 0700
when: gpg_agent_config_enable_ssh_support when: gpg_agent_config_enable_ssh_support
- name: Ensure gnupg_agent script is included in bashrc - name: Ensure gnupg_agent script is included in bashrc
ansible.builtin.lineinfile: lineinfile:
path: "~/.bashrc" path: "~/.bashrc"
line: "source {{ gpg_configure_agent_script }}" line: "source {{ gpg_configure_agent_script }}"
state: present state: present

View File

@@ -1,6 +1,6 @@
--- ---
lego_user: "lego" lego_user: "lego"
lego_version: "4.31.0" lego_version: "4.28.1"
lego_instance: default lego_instance: default
lego_base_path: "/opt/lego" lego_base_path: "/opt/lego"
lego_cert_user: "acme-{{ lego_instance }}" lego_cert_user: "acme-{{ lego_instance }}"

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -xeuo pipefail set -euo pipefail
LEGO_BINARY=$(/usr/bin/env which lego) LEGO_BINARY=$(/usr/bin/env which lego)
@@ -8,17 +8,14 @@ if [[ -n "${LEGO_HTTP_FALLBACK_PORT:-}" ]]; then
echo "nc not found (in PATH), exiting" echo "nc not found (in PATH), exiting"
exit 1 exit 1
fi fi
set +e
nc -z 127.0.0.1 $LEGO_HTTP_PORT; nc -z 127.0.0.1 $LEGO_HTTP_PORT;
nc_exit_code=$?; if [[ $? -eq 0 ]]; then
set -e
if [[ $nc_exit_code -eq 0 ]]; then
LEGO_HTTP_PORT=$LEGO_HTTP_FALLBACK_PORT LEGO_HTTP_PORT=$LEGO_HTTP_FALLBACK_PORT
fi fi
fi fi
if [[ -n "${LEGO_PRE_RENEWAL_HOOK:-}" ]]; then if [[ -n "${LEGO_PRE_RENEWAL_HOOK:-}" ]]; then
$LEGO_PRE_RENEWAL_HOOK /usr/bin/env bash -c "$LEGO_PRE_RENEWAL_HOOK"
fi fi
LEGO_COMMAND_ARGS_EXPANDED=$(bash -c "echo $LEGO_COMMAND_ARGS") # This is a bit icky LEGO_COMMAND_ARGS_EXPANDED=$(bash -c "echo $LEGO_COMMAND_ARGS") # This is a bit icky
@@ -34,5 +31,5 @@ find "$LEGO_CERT_STORE_PATH/certificates" -type f | xargs -I{} -n 1 chmod "$LEGO
find "$LEGO_CERT_STORE_PATH/certificates" -type f | xargs -I{} -n 1 chown "${LEGO_CERT_USER}:${LEGO_CERT_GROUP}" "{}" find "$LEGO_CERT_STORE_PATH/certificates" -type f | xargs -I{} -n 1 chown "${LEGO_CERT_USER}:${LEGO_CERT_GROUP}" "{}"
if [[ -n "${LEGO_POST_RENEWAL_HOOK:-}" ]]; then if [[ -n "${LEGO_POST_RENEWAL_HOOK:-}" ]]; then
$LEGO_POST_RENEWAL_HOOK /usr/bin/env bash -c "$LEGO_POST_RENEWAL_HOOK"
fi fi

View File

@@ -1,11 +1,11 @@
--- ---
lego_domain_command_args: >-2 lego_domain_command_args: >-
{% for domain in lego_cert_domains %} {% for domain in lego_cert_domains %}
--domains={{ domain }} --domains={{ domain }}
{%- endfor %} {%- endfor %}
lego_config_command_args: >-2 lego_config_command_args: >-
{% for key in lego_full_command_config %} {% for key in lego_full_command_config %}
--{{ key | replace("_", "-") }} --{{ key | replace("_", "-") }}
{%- if lego_full_command_config[key] != None and lego_full_command_config[key] != '' -%} {%- if lego_full_command_config[key] != None and lego_full_command_config[key] != '' -%}

View File

@@ -23,7 +23,7 @@ minio_container_command:
- "--console-address" - "--console-address"
- ":{{ minio_container_listen_port_console }}" - ":{{ minio_container_listen_port_console }}"
minio_container_restart_policy: "unless-stopped" minio_container_restart_policy: "unless-stopped"
minio_container_image_force_source: "{{ (minio_container_image_tag == 'latest') | bool }}" minio_container_image_force_source: "{{ (minio_container_image_tag == 'latest')|bool }}"
minio_container_state: >-2 minio_container_state: >-2
{{ (minio_state == 'present') | ternary('started', 'absent') }} {{ (minio_state == 'present') | ternary('started', 'absent') }}

View File

@@ -3,15 +3,15 @@
ansible.builtin.file: ansible.builtin.file:
path: "{{ minio_data_path }}" path: "{{ minio_data_path }}"
state: directory state: directory
user: "{{ minio_user | default(omit, True) }}" user: "{{ minio_user|default(omit, True) }}"
group: "{{ minio_user | default(omit, True) }}" group: "{{ minio_user|default(omit, True) }}"
when: minio_manage_host_filesystem when: minio_manage_host_filesystem
- name: Ensure container image '{{ minio_container_image }}' is {{ minio_state }} - name: Ensure container image '{{ minio_container_image }}' is {{ minio_state }}
community.docker.docker_image: community.docker.docker_image:
name: "{{ minio_container_image }}" name: "{{ minio_container_image }}"
state: "{{ minio_state }}" state: "{{ minio_state }}"
source: "pull" source: pull
force_source: "{{ minio_container_image_force_source }}" force_source: "{{ minio_container_image_force_source }}"
- name: Ensure container '{{ minio_container_name }}' is {{ minio_container_state }} - name: Ensure container '{{ minio_container_name }}' is {{ minio_container_state }}
@@ -23,7 +23,7 @@
labels: "{{ minio_container_labels }}" labels: "{{ minio_container_labels }}"
networks: "{{ minio_container_networks }}" networks: "{{ minio_container_networks }}"
ports: "{{ minio_container_ports }}" ports: "{{ minio_container_ports }}"
user: "{{ minio_user | default(omit, True) }}" user: "{{ minio_user|default(omit, True) }}"
command: "{{ minio_container_command }}" command: "{{ minio_container_command }}"
restart_policy: "{{ minio_container_restart_policy }}" restart_policy: "{{ minio_container_restart_policy }}"
state: "{{ minio_container_state }}" state: "{{ minio_container_state }}"

View File

@@ -1,5 +1,5 @@
--- ---
nginx_version: "1.29.5" nginx_version: "1.29.3"
nginx_flavour: alpine nginx_flavour: alpine
nginx_base_path: /opt/nginx nginx_base_path: /opt/nginx
nginx_config_file: "{{ nginx_base_path }}/nginx.conf" nginx_config_file: "{{ nginx_base_path }}/nginx.conf"
@@ -34,3 +34,4 @@ nginx_container_state: >-2
nginx_container_restart_policy: "unless-stopped" nginx_container_restart_policy: "unless-stopped"
nginx_container_volumes: nginx_container_volumes:
- "{{ nginx_config_file }}:/etc/nginx/conf.d/nginx.conf:ro" - "{{ nginx_config_file }}:/etc/nginx/conf.d/nginx.conf:ro"

View File

@@ -30,7 +30,7 @@
ansible.builtin.copy: ansible.builtin.copy:
dest: "{{ nginx_config_file }}" dest: "{{ nginx_config_file }}"
content: "{{ nginx_config }}" content: "{{ nginx_config }}"
mode: "0640" mode: 0640
notify: notify:
- restart-nginx - restart-nginx
when: nginx_state == 'present' when: nginx_state == 'present'

View File

@@ -1,4 +0,0 @@
# `finallycoffee.base.ntp`
Install `ntp`, configure a timezone by sym-linking `/etc/localtime`
and enable the systemd service.

View File

@@ -1,14 +0,0 @@
---
ntp_state: present
ntp_package_name: "ntp"
ntp_timezone: "Europe/Paris"
ntp_systemd_service_name: "ntpd.service"
ntp_systemd_service_state: >-2
{{ (ntp_state == 'present') | ternary('started', 'stopped') }}
ntp_systemd_service_enabled: >-2
{{ (ntp_state == 'present') }}
ntp_etc_localtime_file: "/etc/localtime"
ntp_usr_share_zoneinfo_path: "/usr/share/zoneinfo"

View File

@@ -1,8 +0,0 @@
---
allow_duplicates: true
dependencies: []
galaxy_info:
role_name: ntp
description: Install network time protocol daemon
galaxy_tags:
- ntp

View File

@@ -1,28 +0,0 @@
---
- name: Check if 'ntp_state' is valid
ansible.builtin.fail:
msg: >-2
Invalid state '{{ ntp_state }}'! Valid
states are {{ ntp_states | join(', ') }}.
when: ntp_state not in ntp_states
- name: Ensure system package is {{ ntp_state }}
ansible.builtin.package:
name: "{{ ntp_package_name }}"
state: "{{ ntp_state }}"
- name: Ensure /etc/localtime is symlinked
ansible.builtin.file:
src: "{{ ntp_usr_share_zoneinfo_path }}/{{ ntp_timezone }}"
dest: "{{ ntp_etc_localtime_file }}"
state: "{{ (ntp_state == 'present') | ternary('link', 'absent') }}"
- name: Ensure ntp systemd service is configured
ansible.builtin.systemd:
name: "{{ ntp_systemd_service_name }}"
enabled: "{{ ntp_systemd_service_enabled }}"
- name: Ensure ntp systemd service is {{ ntp_systemd_service_state }}
ansible.builtin.systemd:
name: "{{ ntp_systemd_service_name }}"
state: "{{ ntp_systemd_service_state }}"

View File

@@ -1,4 +0,0 @@
---
ntp_states:
- "present"
- "absent"

View File

@@ -2,7 +2,7 @@
- name: Ensure config attributes are configured - name: Ensure config attributes are configured
community.general.ldap_attrs: community.general.ldap_attrs:
dn: "{{ openldap_config_dn }}" dn: "{{ openldap_config_dn }}"
attributes: "{{ {entry.key: entry.value} }}" attributes: "{{ { entry.key : entry.value } }}"
state: exact state: exact
server_uri: "{{ openldap_socket_url }}" server_uri: "{{ openldap_socket_url }}"
loop: "{{ openldap_config_attributes | dict2items }}" loop: "{{ openldap_config_attributes | dict2items }}"
@@ -13,7 +13,7 @@
- name: Ensure config db attributes are configured - name: Ensure config db attributes are configured
community.general.ldap_attrs: community.general.ldap_attrs:
dn: "{{ openldap_config_db_dn }}" dn: "{{ openldap_config_db_dn }}"
attributes: "{{ {entry.key: entry.value} }}" attributes: "{{ { entry.key : entry.value } }}"
state: exact state: exact
server_uri: "{{ openldap_socket_url }}" server_uri: "{{ openldap_socket_url }}"
loop: "{{ openldap_config_db_attributes | dict2items }}" loop: "{{ openldap_config_db_attributes | dict2items }}"

View File

@@ -22,3 +22,4 @@
{{ openldap_container_restart_policy | default(omit, true) }} {{ openldap_container_restart_policy | default(omit, true) }}
healthcheck: "{{ openldap_container_healthcheck | default(omit, true) }}" healthcheck: "{{ openldap_container_healthcheck | default(omit, true) }}"
state: "{{ openldap_container_state }}" state: "{{ openldap_container_state }}"

View File

@@ -2,7 +2,7 @@
- name: Ensure additional schemas are mapped to container - name: Ensure additional schemas are mapped to container
ansible.builtin.set_fact: ansible.builtin.set_fact:
openldap_init_container_volumes: >-2 openldap_init_container_volumes: >-2
{{ openldap_init_container_volumes + [schema_mount] }} {{ openldap_init_container_volumes + [ schema_mount ] }}
vars: vars:
schema_file: "{{ openldap_schema_path }}/{{ schema.name }}.ldif" schema_file: "{{ openldap_schema_path }}/{{ schema.name }}.ldif"
schema_mount: >-2 schema_mount: >-2

View File

@@ -41,7 +41,7 @@
community.docker.docker_container_exec: community.docker.docker_container_exec:
container: "{{ powerdns_tsig_key_container_name }}" container: "{{ powerdns_tsig_key_container_name }}"
command: "pdnsutil list-tsig-keys" command: "pdnsutil list-tsig-keys"
delegate_to: "{{ powerdns_tsig_key_hostname | default(inventory_hostname) }}" delegate_to: "{{ powerdns_tsig_key_hostname }}"
register: powerdns_tsig_key_powerdns_info register: powerdns_tsig_key_powerdns_info
changed_when: false changed_when: false
check_mode: false check_mode: false
@@ -54,7 +54,7 @@
when: >- when: >-
(powerdns_tsig_key_name ~ '. ' ~ powerdns_tsig_key_algo ~ '. ') (powerdns_tsig_key_name ~ '. ' ~ powerdns_tsig_key_algo ~ '. ')
not in powerdns_tsig_key_powerdns_info.stdout not in powerdns_tsig_key_powerdns_info.stdout
delegate_to: "{{ powerdns_tsig_key_hostname | default(inventory_hostname) }}" delegate_to: "{{ powerdns_tsig_key_hostname }}"
register: powerdns_tsig_key_powerdns_generated_tsig_key register: powerdns_tsig_key_powerdns_generated_tsig_key
throttle: 1 throttle: 1
become: true become: true
@@ -67,7 +67,7 @@
when: >- when: >-
(powerdns_tsig_key_name ~ '. ' ~ powerdns_tsig_key_algo ~ '. ') (powerdns_tsig_key_name ~ '. ' ~ powerdns_tsig_key_algo ~ '. ')
not in powerdns_tsig_key_powerdns_info.stdout not in powerdns_tsig_key_powerdns_info.stdout
delegate_to: "{{ powerdns_tsig_key_hostname | default(inventory_hostname) }}" delegate_to: "{{ powerdns_tsig_key_hostname }}"
throttle: 1 throttle: 1
become: true become: true

View File

@@ -18,10 +18,10 @@
msg: "`restic_backup_stdin_command` was set but no filename for the resulting output was supplied in `restic_backup_stdin_command_filename`" msg: "`restic_backup_stdin_command` was set but no filename for the resulting output was supplied in `restic_backup_stdin_command_filename`"
- name: Ensure backup frequency adheres to systemd's OnCalender syntax - name: Ensure backup frequency adheres to systemd's OnCalender syntax
ansible.builtin.command: command:
cmd: "systemd-analyze calendar {{ restic_policy.frequency }}" cmd: "systemd-analyze calendar {{ restic_policy.frequency }}"
register: restic_systemd_calender_parse_result register: systemd_calender_parse_res
failed_when: restic_systemd_calender_parse_result.rc != 0 failed_when: systemd_calender_parse_res.rc != 0
changed_when: false changed_when: false
- name: Ensure restic is installed - name: Ensure restic is installed
@@ -42,21 +42,13 @@
state: present state: present
when: ansible_os_family not in ['RedHat', 'Debian'] when: ansible_os_family not in ['RedHat', 'Debian']
- name: Ensure backup script is templated
ansible.builtin.copy:
src: restic-backup-directories.sh
dest: /opt/restic-backup-directories.sh
owner: root
group: root
mode: "0755"
- name: Ensure systemd service file for '{{ restic_job_name }}' is templated - name: Ensure systemd service file for '{{ restic_job_name }}' is templated
template: template:
dest: "/etc/systemd/system/{{ restic_systemd_unit_naming_scheme }}.service" dest: "/etc/systemd/system/{{ restic_systemd_unit_naming_scheme }}.service"
src: restic.service.j2 src: restic.service.j2
owner: root owner: root
group: root group: root
mode: "0640" mode: 0640
notify: notify:
- reload-systemd - reload-systemd
- trigger-restic - trigger-restic
@@ -67,7 +59,7 @@
src: restic.timer.j2 src: restic.timer.j2
owner: root owner: root
group: root group: root
mode: "0640" mode: 0640
notify: notify:
- reload-systemd - reload-systemd

View File

@@ -4,9 +4,6 @@ Description=Run {{ restic_timer_description | default(restic_job_name) }}
[Timer] [Timer]
OnCalendar={{ restic_policy.frequency }} OnCalendar={{ restic_policy.frequency }}
Unit={{ restic_systemd_unit_naming_scheme }}.service Unit={{ restic_systemd_unit_naming_scheme }}.service
{% if restic_systemd_timer_randomized_delay_sec %}
RandomizedDelaySec={{ restic_systemd_timer_randomized_delay_sec }}
{% endif %}
[Install] [Install]
WantedBy=timers.target WantedBy=timers.target

View File

@@ -1,5 +0,0 @@
# `finallycoffee.base.wg_quick` ansible role
Configure a wireguard interface using `wg_quick`. This role writes
the configuration files and activates the interface using the systemd
template service abstractions.

View File

@@ -1,20 +0,0 @@
---
wg_quick_interface_name: ~
wg_quick_interface_address: ~
wg_quick_interface_listen_port: ~
wg_quick_interface_private_key: ~
wg_quick_interface_private_key_file: ~
wg_quick_interface_peer_endpoint: ~
wg_quick_interface_peer_public_key: ~
wg_quick_interface_peer_allowed_ips: ~
wg_quick_interfaces:
- name: "{{ wg_quck_interface_name }}"
address: "{{ wg_quick_interface_address }}"
listen_port: "{{ wg_quick_interface_listen_port }}"
private_key: "{{ wg_quick_interface_private_key }}"
private_key_file: "{{ wg_quick_interface_private_key_file }}"
peers:
- endpoint: "{{ wg_quick_interface_peer_endpoint }}"
public_key: "{{ wg_quick_interface_peer_public_key }}"
allowed_ips: "{{ wg_quick_interface_peer_allowed_ips }}"

View File

@@ -1,7 +0,0 @@
---
wg_quick_state: "present"
wg_quick_package_name: "wireguard-tools"
wg_quick_system_packages:
- "{{ wg_quick_package_name }}"
wg_quick_configuration_dir: "/etc/wireguard"

View File

@@ -1,25 +0,0 @@
---
- name: Ensure wg-quick configuration for interface '{{ wg_quick_iface.name }}' is up to date
ansible.builtin.template:
src: "wg-quick.conf.j2"
dest: "{{ wg_quick_configuration_dir }}/{{ wg_quick_iface.name }}.conf"
when: wg_quick_iface.state | default(wg_quick_state) == 'present'
- name: Ensure systemd service is enabled
ansible.builtin.systemd_service:
name: "wg-quick@{{ wg_quick_iface.name }}.service"
enabled: true
when: wg_quick_iface.state | default(wg_quick_state) == 'present'
- name: Ensure systemd service is {{ wg_quick_iface.state | default(wg_quick_state) }}
ansible.builtin.systemd_service:
name: "wg-quick@{{ wg_quick_iface.name }}.service"
state: >-2
{{ (wg_quick_iface.state | default(wg_quick_state) == 'present')
| ternary('started', 'absent') }}
- name: Ensure wg-quick configuration for interface '{{ wg_quick_iface.name }}' is absent
ansible.builtin.file:
path: "{{ wg_quick_configuration_dir }}/{{ wg_quick_face.name }}.conf"
state: "absent"
when: wg_quick_iface.state | default(wg_quick_state) == 'absent'

View File

@@ -1,27 +0,0 @@
---
- name: Ensure wg_quick_state is valid
ansible.builtin.fail:
msg: >-2
Invalid state '{{ wg_quick_state }}'. Valid
states are {{ wg_quick_states | join(', ') }}.
when: wg_quick_state not in wg_quick_states
- name: Ensure system packages are available
ansible.builtin.package:
name: "{{ wg_quick_system_packages }}"
state: "present"
when: wg_quick_state == 'present'
- name: Ensure configuration folder is present
ansible.builtin.file:
name: "{{ wg_quick_configuration_dir }}"
state: "directory"
when: wg_quick_state == 'present'
- name: Ensure connections are in the configured state
ansible.builtin.include_tasks:
file: "configure-interface.yml"
loop: "{{ wg_quick_interfaces }}"
loop_control:
loop_var: "wg_quick_iface"
label: "{{ wg_quick_iface.name }}"

View File

@@ -1,32 +0,0 @@
[Interface]
Address = {{ wg_quick_iface.address | join(', ') }}
ListenPort = {{ wg_quick_iface.listen_port }}
{% if wg_quick_iface.private_key %}
PrivateKey = {{ wg_quick_iface.private_key }}
{% elif wg_quick_iface.private_key_file %}
PrivateKeyFile = {{ wg_quick_iface.private_key_file }}
{% endif %}
{% if wg_quick_iface.table is defined %}
Table = {{ wg_quick_iface.table | ternary('on', 'off') }}
{% endif %}
{% if wg_quick_iface.post_up %}
PostUp = /bin/bash -c "{{ wg_quick_iface.post_up | join('; ') }}"
{% endif %}
{% if wg_quick_iface.pre_down %}
PreDown = /bin/bash -c "{{ wg_quick_iface.pre_down | join('; ') }}"
{% endif %}
{% for _peer in wg_quick_iface.peers %}
[Peer]
Endpoint = {{ _peer.endpoint }}
PublicKey = {{ _peer.public_key }}
AllowedIPs = {{ _peer.allowed_ips | join(', ') }}
{% if _peer.persistent_keepalive %}
PersistentKeepalive = {{ _peer.persistent_keepalive }}
{% endif %}
{% if 'psk' in _peer %}
PresharedKey = {{ _peer.psk }}
{% endif %}
{% endfor %}

View File

@@ -1,4 +0,0 @@
---
wg_quick_states:
- "present"
- "absent"