diff --git a/roles/service/defaults/main.yaml b/roles/service/defaults/main.yaml index 55beca8..d12b6c0 100644 --- a/roles/service/defaults/main.yaml +++ b/roles/service/defaults/main.yaml @@ -28,6 +28,9 @@ service_database_secret_target: "{{ service_database_type }}" service_postgres_image: docker.io/pgautoupgrade/pgautoupgrade service_postgres_tag: alpine service_redis: false +service_tailscale: false +service_tailscale_login_server: "" +service_tailscale_apikey: "" service_additional_containers: [] diff --git a/roles/service/meta/argument_specs.yaml b/roles/service/meta/argument_specs.yaml index 9569e14..a3d0687 100644 --- a/roles/service/meta/argument_specs.yaml +++ b/roles/service/meta/argument_specs.yaml @@ -377,6 +377,23 @@ argument_specs: type: bool required: false default: false + service_tailscale: + description: >- + Whether to install tailscale in a container and have the service container use its + network. This will make the container accessible over tailscale. + type: bool + required: false + default: false + service_tailscale_login_server: + description: Tailscale login server. Required if tailscale is enabled. + type: str + required: false + default: "" + service_tailscale_apikey: + description: Tailscale API key. Required if tailscale is enabled. + type: str + required: false + default: "" service_additional_containers: description: @@ -592,6 +609,13 @@ argument_specs: type: str required: false default: "" + tailscale: + description: >- + Whether to install tailscale in a container and this container use its + network. This will make the container accessible over tailscale. + type: bool + required: false + default: false secrets: description: - > diff --git a/roles/service/tasks/additional.yaml b/roles/service/tasks/additional.yaml index fad27b4..b5151c5 100644 --- a/roles/service/tasks/additional.yaml +++ b/roles/service/tasks/additional.yaml @@ -1,5 +1,5 @@ --- -- name: Additional containers for {{ service_name }} +- name: Additional container {{ _service_additional_container.name }} ansible.builtin.include_role: name: container vars: @@ -11,7 +11,11 @@ container_mounts: "{{ _service_additional_container_mounts }}" container_devices: "{{ _service_additional_container.devices }}" container_publish_ports: "{{ _service_additional_container_publish_ports }}" - container_networks: "{{ _service_additional_container_networks }}" + container_networks: >- + {{ + _service_additional_container_networks if not _service_additional_container.tailscale + else [{'name': _service_additional_container.name ~ '-tailscale.container'}] + }} container_hostname: "{{ _service_additional_container.name | regex_replace('^' ~ service_name ~ '-', '') }}" container_secrets: "{{ _service_additional_container_secrets }}" container_env: "{{ _service_additional_container.env }}" @@ -21,13 +25,23 @@ container_requires: "{{ _service_additional_container_requires }}" container_wants: "{{ _service_additional_container_wants }}" container_auto_update: "{{ service_auto_update }}" - loop: "{{ _service_additional_containers }}" - loop_control: - loop_var: _service_additional_container - index_var: _service_additional_container_index -- name: Socat sockets for additional containers of {{ service_name }} - ansible.builtin.include_tasks: additional_socat.yaml - loop: "{{ _service_additional_containers }}" +- name: Socat for socket published ports of {{ _service_additional_container.name }} + ansible.builtin.include_role: + name: socat + loop: "{{ _service_additional_container_publish_socket_ports }}" loop_control: - loop_var: _service_additional_container + loop_var: publish_port + vars: + socat_service_name: "{{ service_name }}-{{ publish_port.name }}" + socat_target_container: "{{ _service_additional_container.name }}" + socat_target_http_port: "{{ publish_port.container_port }}" + socat_auto_update: "{{ service_auto_update }}" + +- name: Tailscale for {{ _service_additional_container.name }} + ansible.builtin.include_tasks: tailscale.yaml + when: _service_additional_container.tailscale + vars: + _service_tailscale_container_name: "{{ _service_additional_container.name }}" + _service_tailscale_networks: "{{ _service_additional_container_networks }}" + _service_tailscale_hostname: "{{ _service_additional_container.name | regex_replace('^' ~ service_name ~ '-', '') }}" diff --git a/roles/service/tasks/additional_socat.yaml b/roles/service/tasks/additional_socat.yaml deleted file mode 100644 index c29ec58..0000000 --- a/roles/service/tasks/additional_socat.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -- name: Socat for socket published ports of {{ service_name }} - ansible.builtin.include_role: - name: socat - loop: "{{ _service_additional_container_publish_socket_ports }}" - loop_control: - loop_var: publish_port - vars: - socat_service_name: "{{ service_name }}-{{ publish_port.name }}" - socat_target_container: "{{ _service_additional_container.name }}" - socat_target_http_port: "{{ publish_port.container_port }}" - socat_auto_update: "{{ service_auto_update }}" diff --git a/roles/service/tasks/main.yaml b/roles/service/tasks/main.yaml index 59177f0..637d0c9 100644 --- a/roles/service/tasks/main.yaml +++ b/roles/service/tasks/main.yaml @@ -5,14 +5,21 @@ - name: Database for {{ service_name }} ansible.builtin.include_tasks: database.yaml when: _service_setup_database - - name: Redis for {{ service_name }} ansible.builtin.include_tasks: redis.yaml when: service_redis - name: Secrets for {{ service_name }} ansible.builtin.include_tasks: secrets.yaml - when: _service_container_secrets | length > 0 + when: _service_container_secrets | length > 0 or service_tailscale + +- name: Tailscale for {{ service_name }} + ansible.builtin.include_tasks: tailscale.yaml + when: service_tailscale + vars: + _service_tailscale_container_name: "{{ service_name }}" + _service_tailscale_networks: "{{ _service_networks }}" + _service_tailscale_hostname: "{{ service_name }}" - name: Template mounts for {{ service_name }} ansible.builtin.include_tasks: host_mounts.yaml @@ -20,7 +27,10 @@ - name: Additional containers for {{ service_name }} ansible.builtin.include_tasks: additional.yaml - when: _service_additional_containers | length > 0 + loop: "{{ _service_additional_containers }}" + loop_control: + loop_var: _service_additional_container + index_var: _service_additional_container_index - name: Native socket for {{ service_name }} ansible.builtin.include_role: diff --git a/roles/service/tasks/tailscale.yaml b/roles/service/tasks/tailscale.yaml new file mode 100644 index 0000000..7bddc2b --- /dev/null +++ b/roles/service/tasks/tailscale.yaml @@ -0,0 +1,60 @@ +--- +- name: Create tailscale preauthkey secret for {{ _service_tailscale_container_name }} + when: + - not ansible_check_mode + - "_service_tailscale_container_name ~ '-tailscale-preauthkey' not in service_podman_secrets" + block: + - name: Get preauthkey from tailscale API + ansible.builtin.uri: + url: "{{ service_tailscale_login_server }}/api/v1/preauthkey" + headers: + Authorization: Bearer {{ service_tailscale_apikey }} + method: POST + body: + expiration: >- + {{ + now(utc=true).replace( + day=now(utc=true).day if now(utc=true).hour != 23 else now(utc=true).day + 1, + hour=now(utc=true).hour + 1 if now(utc=true).hour != 23 else 0 + ).isoformat() + }}Z + aclTags: + - tag:container + - "tag:{{ _service_tailscale_container_name }}" + body_format: json + register: _service_tailscale_preauthkey + - name: Create preauthkey secret + containers.podman.podman_secret: + name: "{{ _service_tailscale_container_name }}-tailscale-preauthkey" + data: "{{ _service_tailscale_preauthkey.json.preAuthKey.key }}" + no_log: true + +- name: Tailscale container for {{ _service_tailscale_container_name }} + ansible.builtin.import_role: + name: container + vars: + container_name: "{{ _service_tailscale_container_name }}-tailscale" + container_image: docker.io/tailscale/tailscale:latest + container_networks: "{{ _service_tailscale_networks }}" + container_hostname: "{{ _service_tailscale_hostname }}" + container_add_capabilities: + - NET_ADMIN + container_devices: + - source: /dev/net/tun + container_mounts: + - type: volume + source: "{{ _service_tailscale_container_name }}-tailscale" + destination: /var/lib/tailscale + container_auto_update: "{{ service_auto_update }}" + container_secrets: + - name: "{{ _service_tailscale_container_name }}-tailscale-preauthkey" + target: preauthkey + container_env: + TS_STATE_DIR: /var/lib/tailscale + TS_AUTHKEY: file:/run/secrets/preauthkey + TS_EXTRA_ARGS: --login-server={{ service_tailscale_login_server }} + TS_HOSTNAME: "{{ _service_tailscale_container_name }}" + TS_AUTH_ONCE: "true" + TS_NO_LOGS_NO_SUPPORT: "true" + TS_USERSPACE: "false" + TS_ACCEPT_DNS: "true" diff --git a/roles/service/vars/main/additional.yaml b/roles/service/vars/main/additional.yaml index 826900d..0da6845 100644 --- a/roles/service/vars/main/additional.yaml +++ b/roles/service/vars/main/additional.yaml @@ -1,6 +1,7 @@ --- _service_additional_container_defaults: image: "{{ service_container_image }}" + tailscale: false requires: [] wants: [] diff --git a/roles/service/vars/main/general.yaml b/roles/service/vars/main/general.yaml index 4843d38..e2b3f6a 100644 --- a/roles/service/vars/main/general.yaml +++ b/roles/service/vars/main/general.yaml @@ -1,5 +1,5 @@ --- -_service_container_networks: >- +_service_networks: >- {{ [{ 'name': service_name, @@ -21,6 +21,10 @@ _service_container_networks: >- ) ) | map('community.general.dict_kv', 'name') }} +_service_container_networks: >- + {{ + [{'name': service_name ~ '-tailscale.container'}] if service_tailscale else _service_networks + }} _service_static_ip: "{{ service_container_ip | length > 0 }}" _service_container_requires: >-