Compare commits

...

7 Commits

Author SHA1 Message Date
uumas
59fa9c0b36 synapse: Update MAS configuration 2026-07-22 07:37:06 +03:00
uumas
df302cf6f5 Add headscale 2026-06-08 19:36:46 +03:00
uumas
e28a4097a0 container, service: Support reloading containers on template file change 2026-06-08 19:11:16 +03:00
uumas
3e84073f39 service: Support validating templated files 2026-06-08 17:30:07 +03:00
uumas
40357a17ad service: Support udp port mappings 2026-06-08 17:29:44 +03:00
uumas
284ad35406 windmill, caddy_socket_proxy: Add readmes 2026-06-08 15:09:15 +03:00
uumas
63272d0d2f service: Fix redis volume name 2026-06-05 18:24:06 +03:00
24 changed files with 322 additions and 21 deletions

View File

@@ -0,0 +1 @@
Sets up a caddy container and a systemd socket unit, forwarding traffic from it to target container

View File

@@ -14,3 +14,4 @@ container_auto_update: true
container_requires: [] container_requires: []
container_wants: [] container_wants: []
container_add_capabilities: [] container_add_capabilities: []
container_reload_method: none

View File

@@ -4,3 +4,9 @@
systemd_restart_units: "{{ systemd_restart_units + [container_name ~ '.service'] }}" # noqa: var-naming[no-role-prefix] systemd_restart_units: "{{ systemd_restart_units + [container_name ~ '.service'] }}" # noqa: var-naming[no-role-prefix]
changed_when: true changed_when: true
notify: Apply systemd unit restarts notify: Apply systemd unit restarts
- name: Reload container service {{ container_name }}
ansible.builtin.systemd_service:
name: "{{ container_name }}.service"
state: reloaded
when: "container_name ~ '.service' not in systemd_restart_units"

View File

@@ -212,3 +212,21 @@ argument_specs:
type: bool type: bool
required: false required: false
default: true default: true
container_reload_method:
description: |
How to reload configuration when systemctl reload is called
kill: Sends SIGHUP to the container
command: Runs the specified command inside the container
type: str
required: false
default: none
choices:
- kill
- command
- none
container_reload_command:
description: >-
Command to run inside container when systemctl reload is called.
Required if container_reload_method is command, ignored otherwise.
type: str
required: false

View File

@@ -69,6 +69,10 @@ _container_labels: >-
else {} else {}
}} }}
_container_reload_commands:
kill: podman kill --signal HUP {{ container_name }}
command: podman exec {{ container_name }} {{ container_reload_command }}
_container_quadlet_unit_options: | _container_quadlet_unit_options: |
[Unit] [Unit]
Description=Container {{ container_name }} Description=Container {{ container_name }}
@@ -90,8 +94,12 @@ _container_quadlet_auto_start_options: |
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
_container_quadlet_reload_options: |
[Service]
ExecReload={{ _container_reload_commands[container_reload_method] }}
_container_quadlet_options_incl_empty: _container_quadlet_options_incl_empty:
- "{{ 'AutoUpdate=registry' if container_auto_update else '' }}" - "{{ 'AutoUpdate=registry' if container_auto_update else '' }}"
- "{{ _container_quadlet_unit_options }}" - "{{ _container_quadlet_unit_options }}"
- "{{ _container_quadlet_auto_start_options if container_auto_start else '' }}" - "{{ _container_quadlet_auto_start_options if container_auto_start else '' }}"
- "{{ _container_quadlet_reload_options if container_reload_method != 'none' else '' }}"
_container_quadlet_options: "{{ _container_quadlet_options_incl_empty | select('!=', '') }}" _container_quadlet_options: "{{ _container_quadlet_options_incl_empty | select('!=', '') }}"

View File

@@ -0,0 +1 @@
Sets up a headscale container

View File

@@ -0,0 +1,4 @@
---
headscale_oidc_issuer_url: ""
headscale_oidc_client_id: ""
headscale_oidc_client_secret: ""

View File

@@ -0,0 +1,85 @@
---
argument_specs:
main:
description: "Sets up a headscale container"
options:
headscale_domain:
description: Domain headscale should be available at
type: str
required: true
headscale_magicdns_base_domain:
description: Base domain for magic dns hostnames
type: str
required: true
headscale_policy:
description:
- Headscale policy configuration.
- See https://headscale.net/stable/ref/acls/
- This role's support for acls is currently quite limited. Waiting for headscale to release grants support.
type: dict
required: true
options:
groups:
description: >-
Groups of users. Formatted as dict where dict key is group name and
value is a list of users in the group.
type: dict
required: false
hosts:
description: Mapping of host names to ip addresses
type: dict
required: false
tagOwners:
description: Mapping of tags to lists of their owners
type: dict
required: false
acls:
description: List of acls
type: list
required: true
elements: dict
options:
action:
description: ACL action
type: str
required: true
choices:
- accept
- check
src:
description: >-
A list of sources, formatted as prefix:name:ports,
where prefix is the type of object, like tag: or group:,
name is the object name and ports is a comma separated list of ports or *
type: list
required: true
elements: str
dst:
description: A list of destinations. Same format as sources.
type: list
required: true
elements: str
proto:
description: Protocol
type: str
required: false
choices:
- tcp
- udp
- icmp
headscale_oidc_issuer_url:
description: OIDC issuer url. Leave unset to not use OIDC.
type: str
required: false
default: ""
headscale_oidc_client_id:
description: OIDC client id. Required if OIDC issuer is set.
type: str
required: false
default: ""
headscale_oidc_client_secret:
description: OIDC client secret. Required if OIDC issuer is set.
type: str
required: false
default: ""

View File

@@ -0,0 +1,36 @@
---
- name: Headscale service
ansible.builtin.import_role:
name: service
vars:
service_name: headscale
service_container_image: docker.io/headscale/headscale:stable
service_container_command: serve
service_container_mounts:
- type: volume
source: data
destination: /var/lib/headscale
- type: template
source: config.yaml.j2
destination: /etc/headscale/config.yaml
- type: template
source: policy.hujson.j2
destination: /etc/headscale/policy.hujson
template_validate_command: headscale policy check --file /etc/headscale/policy.hujson
service_container_http_port: 8080
service_domains:
- "{{ headscale_domain }}"
service_container_publish_ports:
- name: stun
container_port: 3478
protocol: udp
host_port: 3478
service_container_secrets: "{{ _headscale_secrets }}"
service_container_reload_method: kill
- name: Open port for stun
ansible.posix.firewalld:
service: stun
state: enabled
permanent: true
immediate: true

View File

@@ -0,0 +1,56 @@
---
# vim:ft=yaml
# {{ ansible_managed }}
listen_addr: 0.0.0.0:8080
server_url: https://{{ headscale_domain }}
noise:
private_key_path: /var/lib/headscale/noise_private.key
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
allocation: sequential
derp:
server:
enabled: true
region_id: 999
region_code: "headscale"
region_name: "Headscale embedded DERP"
verify_clients: true
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_private.key
urls: []
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
policy:
mode: file
path: /etc/headscale/policy.hujson
dns:
magic_dns: true
base_domain: {{ headscale_magicdns_base_domain }}
nameservers:
global:
- https://dns.quad9.net/dns-query
logtail:
enabled: false
{% if headscale_oidc_issuer_url | length > 0 %}
oidc:
only_start_if_oidc_is_available: false
issuer: {{ headscale_oidc_issuer_url }}
client_id: "{{ headscale_oidc_client_id }}"
client_secret_path: /run/secrets/oidc-client-secret
pkce:
enabled: true
{% endif %}

View File

@@ -0,0 +1,3 @@
// {{ ansible_managed }}
{{ headscale_policy | to_nice_json }}

View File

@@ -0,0 +1,6 @@
---
_headscale_oidc_secret:
name: oidc-client-secret
value: "{{ headscale_oidc_client_secret }}"
_headscale_secrets: "{{ [_headscale_oidc_secret] if headscale_oidc_issuer_url | length > 0 else [] }}"

View File

@@ -46,11 +46,6 @@ passwords:
algorithm: argon2id algorithm: argon2id
minimum_complexity: 3 minimum_complexity: 3
clients:
- client_id: 0000000000000000000SYNAPSE
client_auth_method: client_secret_basic
client_secret: "{{ matrix_authentication_service_client_secret }}"
matrix: matrix:
homeserver: {{ matrix_authentication_service_homeserver_name }} homeserver: {{ matrix_authentication_service_homeserver_name }}
secret: "{{ matrix_authentication_service_homeserver_secret }}" secret: "{{ matrix_authentication_service_homeserver_secret }}"

View File

@@ -18,6 +18,8 @@ service_container_devices: []
service_container_secrets: [] service_container_secrets: []
service_container_env: {} service_container_env: {}
service_container_add_capabilities: [] service_container_add_capabilities: []
service_container_reload_method: none
service_container_reload_command: ""
service_database_type: none service_database_type: none
service_database_additional_networks: [] service_database_additional_networks: []

View File

@@ -113,6 +113,14 @@ argument_specs:
description: Container port to publish description: Container port to publish
type: int type: int
required: true required: true
protocol:
description: Protocol of the port
type: str
required: false
default: tcp
choices:
- tcp
- udp
type: type:
description: Whether to publish as a port or socket description: Whether to publish as a port or socket
type: str type: str
@@ -207,6 +215,14 @@ argument_specs:
elements: str elements: str
required: false required: false
default: [] default: []
template_validate_command:
description: >-
Command used to verify templated file validity.
Will be run in a temporary container using the same container image as the main service.
File will be available in the same place as in the service container (destination).
Only applicable if mount type is template
type: str
required: false
service_container_devices: service_container_devices:
description: List of devices to be added inside the service main container. description: List of devices to be added inside the service main container.
type: list type: list
@@ -282,6 +298,26 @@ argument_specs:
default: [] default: []
elements: str elements: str
service_container_reload_method:
description: |
How to reload configuration when systemctl reload is called
kill: Sends SIGHUP to the container
command: Runs the specified command inside the container
type: str
required: false
default: none
choices:
- kill
- command
- none
service_container_reload_command:
description: >-
Command to run inside container when systemctl reload is called.
Required if container_reload_method is command, ignored otherwise.
type: str
required: false
default: ""
service_database_type: service_database_type:
description: description:
- Database type to set up. - Database type to set up.
@@ -495,6 +531,14 @@ argument_specs:
description: Container port to publish description: Container port to publish
type: int type: int
required: true required: true
protocol:
description: Protocol of the port
type: str
required: false
default: tcp
choices:
- tcp
- udp
type: type:
description: Whether to publish as a port or socket description: Whether to publish as a port or socket
type: str type: str
@@ -529,6 +573,25 @@ argument_specs:
type: list type: list
required: false required: false
elements: str elements: str
reload_method:
description: |
How to reload configuration when systemctl reload is called
kill: Sends SIGHUP to the container
command: Runs the specified command inside the container
type: str
required: false
default: none
choices:
- kill
- command
- none
reload_command:
description: >-
Command to run inside container when systemctl reload is called.
Required if container_reload_method is command, ignored otherwise.
type: str
required: false
default: ""
secrets: secrets:
description: description:
- > - >

View File

@@ -16,6 +16,8 @@
container_secrets: "{{ _service_additional_container_secrets }}" container_secrets: "{{ _service_additional_container_secrets }}"
container_env: "{{ _service_additional_container.env }}" container_env: "{{ _service_additional_container.env }}"
container_add_capabilities: "{{ _service_additional_container.add_capabilities }}" container_add_capabilities: "{{ _service_additional_container.add_capabilities }}"
container_reload_method: "{{ _service_additional_container.reload_method }}"
container_reload_command: "{{ _service_additional_container.reload_command }}"
container_requires: "{{ _service_additional_container_requires }}" container_requires: "{{ _service_additional_container_requires }}"
container_wants: "{{ _service_additional_container_wants }}" container_wants: "{{ _service_additional_container_wants }}"
container_auto_update: "{{ service_auto_update }}" container_auto_update: "{{ service_auto_update }}"

View File

@@ -23,8 +23,18 @@
src: "{{ item[0].source }}" src: "{{ item[0].source }}"
dest: "{{ item[1] }}" dest: "{{ item[1] }}"
mode: "{{ item[0].mode | default('0644') }}" mode: "{{ item[0].mode | default('0644') }}"
notify: Restart container service {{ service_name }} validate: "{{ validate if item[0].template_validate_command is defined else omit }}"
notify: >-
{{ 'Reload' if service_container_reload_method != 'none' else 'Restart' }}
container service {{ service_name }}
loop: "{{ _service_all_template_mounts | zip(_service_all_template_mount_host_files) }}" loop: "{{ _service_all_template_mounts | zip(_service_all_template_mount_host_files) }}"
vars:
validate: >-
podman run --rm
-v %s:{{ item[0].destination }}:ro
--entrypoint {{ item[0].template_validate_command.split(' ', 1)[0] }}
{{ service_container_image }}
{{ item[0].template_validate_command.split(' ', 1)[1] }}
- name: Copy files for copy mounts - name: Copy files for copy mounts
ansible.builtin.copy: ansible.builtin.copy:

View File

@@ -48,6 +48,8 @@
container_secrets: "{{ _service_container_secrets }}" container_secrets: "{{ _service_container_secrets }}"
container_env: "{{ service_container_env }}" container_env: "{{ service_container_env }}"
container_add_capabilities: "{{ service_container_add_capabilities }}" container_add_capabilities: "{{ service_container_add_capabilities }}"
container_reload_method: "{{ service_container_reload_method }}"
container_reload_command: "{{ service_container_reload_command }}"
container_requires: "{{ _service_container_requires }}" container_requires: "{{ _service_container_requires }}"
container_wants: "{{ _service_container_wants }}" container_wants: "{{ _service_container_wants }}"
container_auto_update: "{{ service_auto_update }}" container_auto_update: "{{ service_auto_update }}"

View File

@@ -10,7 +10,7 @@
ip: "{{ service_container_ip | ansible.utils.ipmath(2) if _service_static_ip else '' }}" ip: "{{ service_container_ip | ansible.utils.ipmath(2) if _service_static_ip else '' }}"
container_mounts: container_mounts:
- type: volume - type: volume
source: redis source: "{{ service_name }}-redis"
destination: /data destination: /data
container_hostname: redis container_hostname: redis
container_auto_update: "{{ service_auto_update }}" container_auto_update: "{{ service_auto_update }}"

View File

@@ -18,6 +18,8 @@ _service_additional_container_same_image_defaults:
devices: "{{ service_container_devices }}" devices: "{{ service_container_devices }}"
env: "{{ service_container_env }}" env: "{{ service_container_env }}"
add_capabilities: "{{ service_container_add_capabilities }}" add_capabilities: "{{ service_container_add_capabilities }}"
reload_method: "{{ service_container_reload_method }}"
reload_command: "{{ service_container_reload_command }}"
_service_additional_container_different_image_defaults: _service_additional_container_different_image_defaults:
user: "" user: ""
@@ -28,6 +30,8 @@ _service_additional_container_different_image_defaults:
publish_ports: [] publish_ports: []
env: {} env: {}
add_capabilities: [] add_capabilities: []
reload_method: none
reload_command: ""
secrets: [] secrets: []
_service_additional_same_image_containers: >- _service_additional_same_image_containers: >-
@@ -152,6 +156,7 @@ _service_additional_container_publish_ports: >-
_service_additional_container_publish_port_ports | map(attribute='host_port'), _service_additional_container_publish_port_ports | map(attribute='host_port'),
_service_additional_container_publish_port_ports | map(attribute='container_port') _service_additional_container_publish_port_ports | map(attribute='container_port')
) | map('join', ':') ) | map('join', ':')
| zip(_service_additional_container_publish_port_ports | map(attribute='protocol')) | map('join', '/')
}} }}
_service_additional_volume_mounts: "{{ _service_additional_container.mounts | selectattr('type', '==', 'volume') }}" _service_additional_volume_mounts: "{{ _service_additional_container.mounts | selectattr('type', '==', 'volume') }}"
@@ -176,7 +181,7 @@ _service_additional_container_template_mounts: >-
([{'readonly': true}] * _service_additional_template_mounts | length) | ([{'readonly': true}] * _service_additional_template_mounts | length) |
zip( zip(
_service_additional_template_mounts | _service_additional_template_mounts |
community.general.remove_keys(['mode']), community.general.remove_keys(['mode', 'template_validate_command']),
_service_additional_template_mounts | _service_additional_template_mounts |
map(attribute='source') | map(attribute='source') |
map('regex_replace', '\.j2$', '') | map('regex_replace', '\.j2$', '') |

View File

@@ -30,7 +30,7 @@ _service_container_template_mounts: >-
([{'readonly': true}] * _service_template_mounts | length) | ([{'readonly': true}] * _service_template_mounts | length) |
zip( zip(
_service_template_mounts | _service_template_mounts |
community.general.remove_keys(['mode']), community.general.remove_keys(['mode', 'template_validate_command']),
_service_template_mounts | _service_template_mounts |
map(attribute='source') | map(attribute='source') |
map('regex_replace', '\.j2$', '') | map('regex_replace', '\.j2$', '') |

View File

@@ -1,7 +1,7 @@
--- ---
_service_container_publish_ports_with_defaults: >- _service_container_publish_ports_with_defaults: >-
{{ {{
([{ 'type': 'port', 'host_address': '0.0.0.0' }] * service_container_publish_ports | length) ([{ 'type': 'port', 'host_address': '0.0.0.0', 'protocol': 'tcp' }] * service_container_publish_ports | length)
| zip(service_container_publish_ports) | zip(service_container_publish_ports)
| map('combine') | map('combine')
}} }}
@@ -13,9 +13,10 @@ _service_container_publish_port_ports: >-
_service_container_publish_ports: >- _service_container_publish_ports: >-
{{ {{
_service_container_publish_port_ports | map(attribute='host_address') | _service_container_publish_port_ports | map(attribute='host_address')
zip( | zip(
_service_container_publish_port_ports | map(attribute='host_port'), _service_container_publish_port_ports | map(attribute='host_port'),
_service_container_publish_port_ports | map(attribute='container_port') _service_container_publish_port_ports | map(attribute='container_port')
) | map('join', ':') ) | map('join', ':')
| zip(_service_container_publish_port_ports | map(attribute='protocol')) | map('join', '/')
}} }}

View File

@@ -106,12 +106,7 @@ auto_accept_invites: {{ synapse_auto_accept_invites }}
auto_join_rooms: {{ synapse_auto_join_rooms }} auto_join_rooms: {{ synapse_auto_join_rooms }}
autocreate_auto_join_rooms: false autocreate_auto_join_rooms: false
experimental_features: matrix_authentication_service:
msc3861: enabled: true
enabled: true endpoint: http://matrix-authentication-service:8080/
issuer: http://matrix-authentication-service:8080/ secret: "{{ service_podman_secrets['synapse-mas-homeserver-secret'] }}"
client_id: 0000000000000000000SYNAPSE
client_auth_method: client_secret_basic
client_secret: "{{ service_podman_secrets['synapse-mas-client-secret'] }}"
admin_token: "{{ service_podman_secrets['synapse-mas-homeserver-secret'] }}"
account_management_url: "https://{{ synapse_mas_domain }}/account"

1
roles/windmill/README.md Normal file
View File

@@ -0,0 +1 @@
Installs windmill with worker in podman in podman