diff --git a/README.md b/README.md new file mode 100644 index 0000000..06eb231 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# traefik diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..6968198 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,17 @@ +path: /srv/{{ role_name }} + +traefik_override: {} +traefik_defaults: + expose_additional_ports: [] # 22 and 2022 will activate ssh + include_external_services: + config.yml.j2: + + + main_domain: "" + arpa_top_level: "" + http_port: "" + https_port: "" + + hetzner_api_token: "" + duckdns_api_token: "" + basic_auth_password: "" diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..1681791 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,9 @@ +- name: restart container + community.docker.docker_compose_v2: + project_src: "{{ path }}" + project_name: "{{ role_name }}" + recreate: "always" + services: ["traefik"] + state: "restarted" + when: not _traefik_already_restarted + diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..6243fd3 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + author: "Niklas Freese " + description: traefik + company: "Urmel Services" + license: AGPL-3.0-or-later + min_ansible_version: 2.1 + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..518a542 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,47 @@ +--- +- name: "set local variables from overrides and defaults" + set_fact: + traefik: "{{ (traefik_defaults | combine(traefik_override, recursive=True)) if traefik_override else traefik_defaults }}" + +- name: "Set arpa domain" + set_fact: + arpa_top_level: "{{ traefik.arpa_top_level | default(traefik.main_domain.split('.')[-2:] | join('.'), true) }}" + +- name: ensure that path exists + file: + path: "{{ path }}/config/file_provider" + state: directory + +- name: ensure that data exists + file: + path: "{{ path }}/data" + state: directory + +- name: template docker-compose + template: + src: compose.yml.j2 + dest: "{{ path }}/compose.yml" + +- name: template traefik.yml + template: + src: traefik.yml.j2 + dest: "{{ path }}/config/traefik.yml" + +- name: Populate templates + template: + src: "configs/{{ item.path }}" + dest: "{{ path }}/config/file_provider/{{ item.path | regex_replace('\\.j2$', '') }}" + with_filetree: "{{ role_path }}/templates/configs" + when: + - item.state == "file" + - item.path | regex_replace('\\.yml\\.j2$', '') | basename in traefik.include_external_services.keys() + +- name: ensure container is running + community.docker.docker_compose_v2: + project_src: "{{ path }}" + project_name: "{{ role_name }}" + state: "present" + pull: "always" + wait: true + services: ["traefik"] + register: _compose_result diff --git a/compose.yml b/templates/compose.yml.j2 similarity index 60% rename from compose.yml rename to templates/compose.yml.j2 index 884abe8..c803b9f 100644 --- a/compose.yml +++ b/templates/compose.yml.j2 @@ -1,5 +1,3 @@ -version: '3' - services: traefik: image: traefik:v3.6.7 @@ -10,33 +8,36 @@ services: networks: - exposed ports: - - 80:80 - - 443:443 - - 2022:2022 + - {{ traefik.http_port }}:{{ traefik.http_port }} + + - {{ traefik.https_port }}:{{ traefik.https_port }} + {% for port in traefik.expose_additional_ports %} + - "{{ port }}" + {% endfor %} + environment: - - HETZNER_API_TOKEN=${HETZNER_API_TOKEN} - - DUCKDNS_TOKEN=${DUCKDNS_TOKEN} - - AUTH_STRING=${AUTH_STRING} + - HETZNER_API_TOKEN={{ traefik.hetzner_api_token }} + {% if traefik.duckdns_api_token %} + - DUCKDNS_TOKEN={{ traefik.duckdns_api_token }} + {%- endif -%} + - AUTH_STRING={{ traefik.basic_auth_password }} - DUCKDNS_POLLING_INTERVAL=5 - DUCKDNS_PROPAGATION_TIMEOUT=120 volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - - ${PROJECT_PATH-$PWD}/traefik.yml:/traefik.yml:ro - - ${PROJECT_PATH-$PWD}/config:/config:ro - - traefik-certs:/certs + - ./config/traefik.yml:/traefik.yml:ro + - ./config/file_provider:/config:ro + - ./data/certs:/certs labels: traefik.enable: true traefik.http.routers.traefik-dashboard.entrypoints: https - traefik.http.routers.traefik-dashboard.rule: Host(`traefik.arpa.urmel.de`) + traefik.http.routers.traefik-dashboard.rule: Host(`traefik.arpa.{{ arpa_top_level }}`) traefik.http.routers.traefik-dashboard.tls: "true" traefik.http.routers.traefik-dashboard.tls.certresolver: hetzner traefik.http.routers.traefik-dashboard.service: api@internal - traefik.http.routers.traefik-dashboard.middlewares: local-only@file, traefik-auth@file - -volumes: - traefik-certs: + traefik.http.routers.traefik-dashboard.middlewares: local-only@file networks: exposed: @@ -45,4 +46,4 @@ networks: config: - subnet: 172.18.0.0/16 ip_range: 172.18.128.0/17 - gateway: 172.18.0.1 \ No newline at end of file + gateway: 172.18.0.1 diff --git a/config/config.yml b/templates/configs/config.yml.j2 similarity index 63% rename from config/config.yml rename to templates/configs/config.yml.j2 index 72a07bc..bd32659 100644 --- a/config/config.yml +++ b/templates/configs/config.yml.j2 @@ -1,51 +1,58 @@ -http: +http: routers: traefik-secure: service: noop@internal - rule: Host(`urmel.de`) + rule: Host(`{{ traefik.main_domain }}`) entryPoints: - https - http tls: certResolver: hetzner domains: - - main: "arpa.urmel.de" - sans: - - "*.arpa.urmel.de" - - main: "urmel.de" - sans: - - "*.urmel.de" + - main: "arpa.{{ arpa_top_level }}" + sans: + - "*.arpa.{{ arpa_top_level }}" + - main: "{{ traefik.main_domain }}" + sans: + - "*.{{ traefik.main_domain }}" + {% if traefik.duckdns_api_token -%} forward-duckdns: service: noop@internal rule: HostRegexp(`(.+\.)?urmel\.duckdns\.org`) priority: 1 middlewares: redirect-duckdns@file - tls: + tls: certResolver: duckdns domains: - main: "urmel.duckdns.org" - sans: + sans: - "*.urmel.duckdns.org" entryPoints: - https - http + {%- endif %} middlewares: traefik-auth: basicauth: - users: '{{ env "AUTH_STRING" }}' + users: '{% raw %}{{ env "AUTH_STRING" }}{% endraw %}' + + {% if traefik.duckdns_api_token %} redirect-duckdns: redirectRegex: regex: "^http(s?)://(.+\\.)?urmel\\.duckdns\\.org(:[0-9]+)?(/.*$|$)" replacement: "http${1}://${2}urmel.de${3}${4}" + {% endif %} + local-only: ipAllowList: sourceRange: - "10.0.0.0/8" - "172.16.0.0/12" - - "168.168.0.0/16" + - "192.168.0.0/16" + secure-only: ipAllowList: sourceRange: - "172.24.2.0/24" - - "10.99.0.4/24" \ No newline at end of file + - "10.99.0.4/24" diff --git a/config/external.yml b/templates/configs/homeassistant.yml.j2 similarity index 58% rename from config/external.yml rename to templates/configs/homeassistant.yml.j2 index ef09c92..e693665 100644 --- a/config/external.yml +++ b/templates/configs/homeassistant.yml.j2 @@ -2,7 +2,7 @@ http: routers: homeassistant: service: homeassistant - rule: Host(`home.urmel.de`) + rule: Host(`home.{{ traefik.main_domain }}`) priority: 10 entryPoints: - https @@ -15,11 +15,11 @@ http: homeassistant: loadbalancer: servers: - - url: "http://172.24.2.4:8123" + - url: "http://{{ traefik.include_external_services.homeassistant.url }}:8123" passHostHeader: true middlewares: homeassistant-headers: headers: customResponseHeaders: - X-Frame-Options: "ALLOW-FROM *.urmel.de" - Content-Security-Policy: "frame-ancestors *.urmel.de" + X-Frame-Options: "ALLOW-FROM *.{{ traefik.main_domain }}" + Content-Security-Policy: "frame-ancestors *.{{ traefik.main_domain }}" diff --git a/traefik.yml b/templates/traefik.yml.j2 similarity index 83% rename from traefik.yml rename to templates/traefik.yml.j2 index 2d73ec9..cee0b46 100644 --- a/traefik.yml +++ b/templates/traefik.yml.j2 @@ -4,7 +4,7 @@ api: entryPoints: http: asDefault: true - address: ":80" + address: ":{{ traefik.http_port }}" http: redirections: entryPoint: @@ -12,9 +12,11 @@ entryPoints: scheme: https https: asDefault: true - address: ":443" + address: ":{{ traefik.https_port }}" + {% if 2022 in traefik.expose_additional_ports -%} public_ssh: address: ":2022" + {% endif -%} serversTransport: insecureSkipVerify: true providers: @@ -35,6 +37,7 @@ certificatesResolvers: provider: "hetzner" resolvers: - "hydrogen.ns.hetzner.com" + {% if traefik.duckdns_api_token -%} duckdns: acme: email: nikl.free44@gmail.com @@ -48,4 +51,5 @@ certificatesResolvers: requireAllRNS: true resolvers: - "8.8.8.8:53" - - "1.1.1.1:53" \ No newline at end of file + - "1.1.1.1:53" + {%- endif %}