diff --git a/README.md b/README.md index 4d96263..abdc7c0 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ This ansible collection provides various roles for installing 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 - [`gnupg`](roles/gnupg/README.md): configures gnupg on the target system diff --git a/galaxy.yml b/galaxy.yml index bcb4628..e73f45e 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -24,6 +24,7 @@ tags: - lego - minio - nginx + - caddy - restic - user_management - openldap diff --git a/playbooks/caddy.yml b/playbooks/caddy.yml new file mode 100644 index 0000000..2e3fb1d --- /dev/null +++ b/playbooks/caddy.yml @@ -0,0 +1,7 @@ +--- +- 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 diff --git a/roles/caddy/README.md b/roles/caddy/README.md new file mode 100644 index 0000000..c25105b --- /dev/null +++ b/roles/caddy/README.md @@ -0,0 +1,10 @@ +# `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`. diff --git a/roles/caddy/defaults/main/config.yml b/roles/caddy/defaults/main/config.yml new file mode 100644 index 0000000..aef60d7 --- /dev/null +++ b/roles/caddy/defaults/main/config.yml @@ -0,0 +1,23 @@ +--- +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 + } diff --git a/roles/caddy/defaults/main/container.yml b/roles/caddy/defaults/main/container.yml new file mode 100644 index 0000000..eb41cab --- /dev/null +++ b/roles/caddy/defaults/main/container.yml @@ -0,0 +1,43 @@ +--- +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: ~ diff --git a/roles/caddy/defaults/main/main.yml b/roles/caddy/defaults/main/main.yml new file mode 100644 index 0000000..c3db1ed --- /dev/null +++ b/roles/caddy/defaults/main/main.yml @@ -0,0 +1,11 @@ +--- +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" diff --git a/roles/caddy/defaults/main/user.yml b/roles/caddy/defaults/main/user.yml new file mode 100644 index 0000000..b9de126 --- /dev/null +++ b/roles/caddy/defaults/main/user.yml @@ -0,0 +1,7 @@ +--- +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) }}" diff --git a/roles/caddy/meta/main.yml b/roles/caddy/meta/main.yml new file mode 100644 index 0000000..2c5862a --- /dev/null +++ b/roles/caddy/meta/main.yml @@ -0,0 +1,13 @@ +--- +allow_duplicates: true +dependencies: [] +galaxy_info: + role_name: caddy + description: Deploy caddy, a webserver + galaxy_tags: + - caddy + - zerossl + - http + - webserver + - docker + - podman diff --git a/roles/caddy/tasks/deploy-docker.yml b/roles/caddy/tasks/deploy-docker.yml new file mode 100644 index 0000000..da56c5e --- /dev/null +++ b/roles/caddy/tasks/deploy-docker.yml @@ -0,0 +1,26 @@ +--- +- 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 }}" diff --git a/roles/caddy/tasks/main.yml b/roles/caddy/tasks/main.yml new file mode 100644 index 0000000..2e5b1ea --- /dev/null +++ b/roles/caddy/tasks/main.yml @@ -0,0 +1,52 @@ +--- +- 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" diff --git a/roles/caddy/vars/main.yml b/roles/caddy/vars/main.yml new file mode 100644 index 0000000..2ddf440 --- /dev/null +++ b/roles/caddy/vars/main.yml @@ -0,0 +1,6 @@ +--- +caddy_states: + - "present" + - "absent" +caddy_deployment_methods: + - "docker"