Compare commits

...

15 Commits

Author SHA1 Message Date
2cce72a5fd update(nginx): bump version to 1.29.5 2026-02-04 21:02:42 +01:00
d4a86c667e meta: add ansible-lint configuration 2026-02-04 12:25:53 +01:00
b994778d66 meta: add .gitignore 2026-02-04 12:23:24 +01:00
5dac2b5175 chore: jinja2 spacing, variable naming 2026-02-04 12:20:38 +01:00
fb35a0acc6 fix(gnugpg): use FQMN, do not use yaml octal vars 2026-02-04 12:19:27 +01:00
afcea9439c fix: various ansible-lint failures (jinja2 spacing, yaml octal values) 2026-02-04 11:53:40 +01:00
d337407685 fix: ansible-lint yaml whitespace problems 2026-02-04 11:08:44 +01:00
a9c66729dd fix(galaxy): use correct NpmSpec in dependency requirements 2026-02-04 11:01:14 +01:00
f0f7c05564 fix(lego): capture exit code instead of std-out 2026-02-03 14:37:54 +01:00
91c793c3c4 fix(lego): Unset -e during nc command 2026-02-03 14:37:54 +01:00
d0ade80f19 fix(lego): do not trigger script abortion on wanted exit code != 0
(cherry picked from commit f2ee49abe4)
2026-02-03 14:18:59 +01:00
19e41c15ad meta: bump collection version to 0.4.1 2026-01-30 22:54:10 +01:00
b82fb86d83 fix(wg_quick): fix syntax error in Table definition 2026-01-25 14:58:51 +01:00
3c0f9efbb3 feat(wg_quick): allow specifying an optional PresharedKey 2026-01-25 13:57:27 +01:00
acf1e32eca feat(wg_quick): add ansible role and playbook 2026-01-10 21:58:41 +01:00
30 changed files with 203 additions and 46 deletions

View File

21
.config/ansible-lint.yml Normal file
View File

@@ -0,0 +1,21 @@
---
# 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 Normal file
View File

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

View File

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

View File

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

View File

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

7
playbooks/wg_quick.yml Normal file
View File

@@ -0,0 +1,7 @@
---
- 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

@@ -14,10 +14,9 @@ caddy_config: |+2
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

@@ -10,7 +10,7 @@
name: "{{ docker_fedora_repo_name }}"
description: "{{ docker_fedora_repo_description }}"
baseurl: "{{ docker_fedora_repo_url }}"
validate_certs: "{{ docker_fedora_repo_validate_certs }}"
validate_certs: "{{ docker_fedora_repo_validate_certs }}"
gpgkey: "{{ docker_fedora_repo_gpg_key }}"
gpgcheck: "{{ docker_fedora_repo_gpg_check }}"
state: "{{ docker_state }}"

View File

@@ -8,7 +8,7 @@
state: present
marker: "#{mark} ANSIBLE MANAGED BLOCK by finallycoffee.base.git"
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]
name = {{ git_config_user_name }}
email = {{ git_config_user_email }}
@@ -35,7 +35,7 @@
{% for alias in git_config_alias %}
{{ alias.name }} = {{ alias.command }}
{% endfor %}
{% for credentialset in git_config_credentials %}
[credential "{{ credentialset.remote_url }}"]
{% for entry in credentialset.config | dict2items %}

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
set -xeuo pipefail
LEGO_BINARY=$(/usr/bin/env which lego)
@@ -8,8 +8,11 @@ if [[ -n "${LEGO_HTTP_FALLBACK_PORT:-}" ]]; then
echo "nc not found (in PATH), exiting"
exit 1
fi
set +e
nc -z 127.0.0.1 $LEGO_HTTP_PORT;
if [[ $? -eq 0 ]]; then
nc_exit_code=$?;
set -e
if [[ $nc_exit_code -eq 0 ]]; then
LEGO_HTTP_PORT=$LEGO_HTTP_FALLBACK_PORT
fi
fi

View File

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

View File

@@ -23,7 +23,7 @@ minio_container_command:
- "--console-address"
- ":{{ minio_container_listen_port_console }}"
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_state == 'present') | ternary('started', 'absent') }}

View File

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

View File

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

View File

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

View File

@@ -62,4 +62,4 @@ openldap_default_database:
indices: "{{ openldap_default_database_indices }}"
openldap_default_database_olc_access: "{{ openldap_config_db_olc_access }}"
openldap_databases:
- "{{ openldap_default_database }}"
- "{{ openldap_default_database }}"

View File

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

View File

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

View File

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

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`"
- name: Ensure backup frequency adheres to systemd's OnCalender syntax
command:
ansible.builtin.command:
cmd: "systemd-analyze calendar {{ restic_policy.frequency }}"
register: systemd_calender_parse_res
failed_when: systemd_calender_parse_res.rc != 0
register: restic_systemd_calender_parse_result
failed_when: restic_systemd_calender_parse_result.rc != 0
changed_when: false
- name: Ensure restic is installed
@@ -42,13 +42,21 @@
state: present
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
template:
dest: "/etc/systemd/system/{{ restic_systemd_unit_naming_scheme }}.service"
src: restic.service.j2
owner: root
group: root
mode: 0640
mode: "0640"
notify:
- reload-systemd
- trigger-restic
@@ -59,7 +67,7 @@
src: restic.timer.j2
owner: root
group: root
mode: 0640
mode: "0640"
notify:
- reload-systemd

5
roles/wg_quick/README.md Normal file
View File

@@ -0,0 +1,5 @@
# `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

@@ -0,0 +1,20 @@
---
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

@@ -0,0 +1,7 @@
---
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

@@ -0,0 +1,25 @@
---
- 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

@@ -0,0 +1,27 @@
---
- 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

@@ -0,0 +1,32 @@
[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

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