diff --git a/README.md b/README.md index 1074bd7..682597e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ansible-hms-docker -Ansible Playbook to setup an automated Home Media Server stack running on Docker across a variety of platforms with support for GPUs, SSL, DDNS, and more. +Ansible Playbook to setup an automated Home Media Server stack running on Docker across a variety of platforms with support for GPUs, SSL, SSO, DDNS, and more. ## Getting Started @@ -9,6 +9,7 @@ Ansible Playbook to setup an automated Home Media Server stack running on Docker - [Configuration](#configuration) - [Connecting the containers](#connecting-the-containers) - [Only generate config files](#only-generate-config-files) +- [Using Authentik](#using-authentik) ## Container List @@ -26,6 +27,7 @@ Ansible Playbook to setup an automated Home Media Server stack running on Docker - Requestrr: chat client for requests - Watchtower: automatic container updates (if enabled) - Cloudflare-ddns: dynamic dns (if enabled) +- Authentik: SSO ## Other Features @@ -36,6 +38,7 @@ Ansible Playbook to setup an automated Home Media Server stack running on Docker - Dynamic DNS updates - Wildcard SSL certificate generation - Support for multiple network shares +- Single Sign-On with Authentik ## Supported Platforms @@ -54,7 +57,7 @@ Ansible Playbook to setup an automated Home Media Server stack running on Docker - `root` or `sudo` access - Supported Platform - 4 CPU Cores -- Minimum 4GB RAM +- Minimum 4GB RAM (2GB additional if using Authentik) - Minimum 8GB free disk space - Nvidia GPU drivers already installed (if using Nvidia GPU acceleration) - Python 3.8 (Recommended, minimum Python 3.6) @@ -145,9 +148,11 @@ It is recommended to read and follow this guide entirely as there is a lot of co ## Configuration of the `vars/default.yml` file +This is personal preference, but you may want to copy this `vars/default.yml` file to a `vars/custom.yml` file and then update the `vars_files` line in the `hms-docker.yml` file to point to your new custom file. + - Settings to configure: - - `plex_claim_token` : (optional) your Plex claim code from https://plex.tv/claim + - `plex_claim_token` : (optional) your Plex claim code from [Plex's website](https://plex.tv/claim) - `hms_docker_domain` : the local domain name of the server to be used for proxy rules and SSL certificates (e.g. `home.local`) - `transmission_vpn_user` : the username of the VPN user - `transmission_vpn_pass` : the password of the VPN user @@ -186,8 +191,9 @@ It is recommended to read and follow this guide entirely as there is a lot of co - `cloudflare_ddns_subdomain` : the subdomain record (e.g. `overseerr` would be created as `overseerr.example.com`) (default: `overseerr`) - `cloudflare_ddns_proxied` : `'true'` or `'false'` to enable/disable proxying the traffic through Cloudflare (default: `'true'`) -- Optional settings to configure: - - If you with to use a more advanced configuration, you can run this command to replace the standard config with the default advanced config: +### Optional settings to configure + +- If you with to use a more advanced configuration, you can run this command to replace the standard config with the default advanced config: ```bash cp roles/hmsdocker/defaults/main.yml vars/default.yml @@ -211,7 +217,9 @@ If you do not already have a "wildcard" DNS record setup for the domain you used You can also create individual A records for each container listed in the table below. -If the above DNS requirements are met, you can then access the containers by using the following URLs (substituting `{{ domain }}` for the domain you used): +If the above DNS requirements are met, you can then access the containers by using the following URLs (substituting `{{ domain }}` for the domain you used). + +You can also change the subdomain of each application within the advanced `hms_docker_container_map` setting. Plex: `https://plex.{{ domain }}` @@ -235,27 +243,30 @@ Traefik: `https://traefik.{{ domain }}` NZBGet: `https://nzbget.{{ domain }}` +Authentik: `https://authentik.{{ domain }}` + ## Connecting the Containers When connecting Prowlarr to Sonarr and Radarr and etc, you can use the name of the container (e.g. `prowlarr` or `radarr`) and then defining the container port to connect to (e.g. `prowlarr:9696` or `radarr:7878`). If you choose to expose the container ports on the host (by setting `container_expose_ports: yes` in the `vars/default.yml` file), see below for which ports are mapped to which container on the host. -| Service Name | Container Name | Host Port (if enabled) | Container Port | Accessible via Traefik | -| ------------------ | -------------------- | ---------------------- | -------------- | ---------------------- | -| Plex | `plex` | `32400` | `32400` | ☑ | -| Sonarr | `sonarr` | `8989` | `8989` | ☑ | -| Radarr | `radarr` | `7878` | `7878` | ☑ | -| Prowlarr | `prowlarr` | `9696` | `9696` | ☑ | -| Overseerr | `Overseerr` | `5055` | `5055` | ☑ | -| Requestrr | `Requestrr` | `4545` | `4545` | ☑ | -| Transmission | `transmission` | `9091` | `9091` | ☑ | -| Transmission Proxy | `transmission-proxy` | `8081` | `8080` | ☐ | -| Portainer | `portainer` | `9000` | `9000` | ☑ | -| Bazarr | `bazarr` | `6767` | `6767` | ☑ | -| Tautulli | `tautulli` | `8181` | `8181` | ☑ | -| Traefik | `traefik` | `8080` | `8080` | ☑ | -| NZBGet | `nzbget` | `6789` | `6789` | ☑ | +| Service Name | Container Name | Host Port (if enabled) | Container Port | Accessible via Traefik | +| ------------------ | -------------------- | ---------------------- | ----------------- | ---------------------- | +| Plex | `plex` | `32400` | `32400` | ☑ | +| Sonarr | `sonarr` | `8989` | `8989` | ☑ | +| Radarr | `radarr` | `7878` | `7878` | ☑ | +| Prowlarr | `prowlarr` | `9696` | `9696` | ☑ | +| Overseerr | `Overseerr` | `5055` | `5055` | ☑ | +| Requestrr | `Requestrr` | `4545` | `4545` | ☑ | +| Transmission | `transmission` | `9091` | `9091` | ☑ | +| Transmission Proxy | `transmission-proxy` | `8081` | `8080` | ☐ | +| Portainer | `portainer` | `9000` | `9000` | ☑ | +| Bazarr | `bazarr` | `6767` | `6767` | ☑ | +| Tautulli | `tautulli` | `8181` | `8181` | ☑ | +| Traefik | `traefik` | `8080` | `8080` | ☑ | +| NZBGet | `nzbget` | `6789` | `6789` | ☑ | +| Authentik | `authentic-server` | `9000` and `9443` | `9000` and `9443` | ☑ | ## Only generate config files @@ -266,3 +277,77 @@ ansible-playbook -i inventory --connection local generate-configs.yml ``` By default, it will output these configs into `/opt/hms-docker/` + +## Using Authentik + +In order to use Authentik, you must be using the [advanced configuration outlined above](#optional-settings-to-configure). + +This Authentik installation is based on the [single application](https://goauthentik.io/docs/providers/proxy/forward_auth#single-application) proxy provider configuration. + +There are no authentik proxy containers defined in the `docker-compose.yml` file. This is because authentik will auto-detect the Docker socket and be able to start/stop its own proxy containers by using the configurations below. + +Authentik is able to be controlled on a per-container basis, but requires a bit of configuration as outlined below: + +1. Within the advanced variable settings (as outlined in the [optional settings setup](#optional-settings-to-configure)), enable the authentik container and enable authentik for the containers you want using the `hms_docker_container_map` variable + +2. Run the playbook as normal + +3. Once all containers are started, go to `https://authentik.{{ domain }}/if/flow/initial-setup/` to create the initial user and password to continue Authentik setup + +4. **Configure an Application Provider within Authentik** + + a. Login + + b. Go to the Admin panel + + c. Expand `Applications` on the left + + d. Click `Providers` + + e. Create a new `Proxy Provider` + + f. Give it the same name as the application (such as Sonarr) + + g. Select `Forward auth (single application)` + + h. Set the `External host` to the URL of the application (such as `sonarr.{{ domain }}`) + + i. Click finish + +5. **Configure an Application** + + a. Do a-c again + + b. Click `Applications` + + c. Create a new Application with its name, slug (lowercase name for it), and select the provider created for the application + + d. Click `Create` and now you should have Authentik in front of the application! + +6. **Configure an Application Outpost** + + a. Do a-c above + + b. Click `Outposts` + + c. Give it a name, the `type` is `Proxy` and integration should be the `Local Docker connection` + + d. Select the application to associate it to + + e. IMPORTANT: copy the correct configuration generated in: `{{ hms_docker_apps_path }}/authentik/outposts/authentik-{{ container_name }}-output.yml` (so by default: `/opt/hms-docker/authentik/...`) + + * If a configuration does not exist for the container you want, ensure you've enabled authentik and have enabled authentik for that specific container + + f. Replace the configuration in the authentik webpage with this generated configuration, otherwise stuff will not work correctly. + + g. Once you click create, it will automatically create a new authentik-proxy container that will handle authentication. + + h. Note: it will take some time to setup the proxy, be patient + +7. **Troubleshooting** + + a. Using the Traefik and Portainer dashboards help a LOT during the troubleshooting process + + b. If you're getting a `404 not found` error, this is likely due to the `authentik-proxy` containers not working, running, or not being configured correctly. If you just configured a new application output, wait a couple more minutes. + + c. If you're getting a `500` server error, this is possibly due to having duplicate Traefik routes for the same host rules diff --git a/hms-docker.yml b/hms-docker.yml index 128fd3f..b307d16 100644 --- a/hms-docker.yml +++ b/hms-docker.yml @@ -8,6 +8,7 @@ vars_files: - vars/default.yml + - vars/._computed.yml roles: - docker diff --git a/roles/hmsdocker/defaults/main.yml b/roles/hmsdocker/defaults/main.yml index b1c0e4d..5d32dbc 100644 --- a/roles/hmsdocker/defaults/main.yml +++ b/roles/hmsdocker/defaults/main.yml @@ -345,62 +345,92 @@ hms_docker_compose_container_state: present hms_docker_container_map: traefik: enabled: yes + proxy_host_rule: traefik directory: yes traefik: yes + authentik: no sonarr: enabled: yes + proxy_host_rule: sonarr directory: yes traefik: yes + authentik: no expose_to_public: no radarr: enabled: yes + proxy_host_rule: radarr directory: yes traefik: yes + authentik: no expose_to_public: no bazarr: enabled: yes + proxy_host_rule: bazarr directory: yes traefik: yes + authentik: no expose_to_public: no transmission: enabled: yes + proxy_host_rule: transmission directory: yes traefik: yes + authentik: no expose_to_public: no portainer: enabled: yes + proxy_host_rule: portainer directory: yes traefik: yes + authentik: no expose_to_public: no overseerr: enabled: yes + proxy_host_rule: overseerr directory: yes traefik: yes + authentik: no expose_to_public: no prowlarr: enabled: yes + proxy_host_rule: prowlarr directory: yes traefik: yes + authentik: no expose_to_public: no requestrr: enabled: yes + proxy_host_rule: requestrr directory: yes traefik: yes + authentik: no expose_to_public: no plex: enabled: yes + proxy_host_rule: plex directory: yes traefik: yes + authentik: no expose_to_public: no tautulli: enabled: yes + proxy_host_rule: tautulli directory: yes traefik: yes + authentik: no expose_to_public: no nzbget: enabled: yes + proxy_host_rule: nzbget directory: yes traefik: yes + authentik: no + authentik: + enabled: no + proxy_host_rule: authentik + directory: yes + traefik: yes + authentik: no expose_to_public: no plex_transcode_folder: "{{ hms_docker_apps_path }}/plex/transcode_temp" # default: "{{ hms_docker_apps_path }}/plex/transcode_temp" @@ -410,6 +440,17 @@ plex_transcode_folder: "{{ hms_docker_apps_path }}/plex/transcode_temp" # defaul +####################################################################### +### Authentik settings +# This OR the option in the container map will enable or disable the Authentik container +authentik_enabled: no +authentik_host: "http://authentik-server:9000" # leave this as default if you're using the default hms-docker configuration (really only used if you have a separate authentik server) default: "http://authentik-server:9000" +authentik_external_host: 'https://{{ hms_docker_container_map["authentik"]["proxy_host_rule"] }}.{{ hms_docker_domain }}' # This needs to match the host rule that routes traffic to the Authentik container +### End of authentik settings +####################################################################### + + + ####################################################################### ### Advanced Ansible settings # Overrides the family to 'redhat' if running Alma Linux diff --git a/roles/hmsdocker/tasks/authelia.yml b/roles/hmsdocker/tasks/authelia.yml new file mode 100644 index 0000000..9e8234e --- /dev/null +++ b/roles/hmsdocker/tasks/authelia.yml @@ -0,0 +1,10 @@ +--- +- name: Ensure Authelia config + template: + src: authelia.j2 + dest: "{{ hms_docker_apps_path }}/authelia/configuration.yml" + mode: 0600 + owner: root + group: root + backup: yes + force: no diff --git a/roles/hmsdocker/tasks/authentik.yml b/roles/hmsdocker/tasks/authentik.yml new file mode 100644 index 0000000..ba120ad --- /dev/null +++ b/roles/hmsdocker/tasks/authentik.yml @@ -0,0 +1,26 @@ +--- +- name: Ensure authentik env + template: + src: authentik_env.j2 + dest: "{{ hms_docker_data_path }}/.authentik.env" + mode: 0600 + owner: root + group: root + backup: yes + force: no + +- name: Ensure Outposts directory + file: + path: "{{ hms_docker_apps_path }}/authentik/outposts" + state: directory + mode: 0755 + owner: root + group: root + +- name: Ensure authentik Outpost configs + template: + src: authentik_outpost.j2 + dest: "{{ hms_docker_apps_path }}/authentik/outposts/authentik-{{ item.key }}.outpost.yml" + mode: 0644 + with_dict: "{{ hms_docker_container_map }}" + when: item.value.enabled and (item.value.authentik is defined and item.value.authentik) diff --git a/roles/hmsdocker/tasks/main.yml b/roles/hmsdocker/tasks/main.yml index e54ec34..cca40a3 100644 --- a/roles/hmsdocker/tasks/main.yml +++ b/roles/hmsdocker/tasks/main.yml @@ -43,6 +43,12 @@ import_tasks: "traefik.yml" when: hms_docker_container_map.traefik.enabled is defined and hms_docker_container_map.traefik.enabled +- name: Ensure Authentik + import_tasks: "authentik.yml" + when: ((hms_docker_container_map.authentik.enabled is defined and hms_docker_container_map.authentik.enabled) or + (authentik_enabled)) and + (traefik_ssl_enabled) + - name: Ensure docker-compose.yml file. template: src: docker-compose.yml.j2 diff --git a/roles/hmsdocker/templates/authelia.j2 b/roles/hmsdocker/templates/authelia.j2 new file mode 100644 index 0000000..3603f17 --- /dev/null +++ b/roles/hmsdocker/templates/authelia.j2 @@ -0,0 +1,860 @@ +# yamllint disable rule:comments-indentation +--- +############################################################################### +# Authelia Configuration # +############################################################################### + +## Note: the container by default expects to find this file at /config/configuration.yml. + +## Certificates directory specifies where Authelia will load trusted certificates (public portion) from in addition to +## the system certificates store. +## They should be in base64 format, and have one of the following extensions: *.cer, *.crt, *.pem. +# certificates_directory: /config/certificates/ + +## The theme to display: light, dark, grey, auto. +theme: light + +## The secret used to generate JWT tokens when validating user identity by email confirmation. JWT Secret can also be +## set using a secret: https://www.authelia.com/docs/configuration/secrets.html +jwt_secret: {{ lookup('password', '/dev/null length=32 chars=ascii_letters') }} + +## Default redirection URL +## +## If user tries to authenticate without any referer, Authelia does not know where to redirect the user to at the end +## of the authentication process. This parameter allows you to specify the default redirection URL Authelia will use +## in such a case. +## +## Note: this parameter is optional. If not provided, user won't be redirected upon successful authentication. +default_redirection_url: https://home.{{ hms_docker_domain }}/ + +## Set the default 2FA method for new users and for when a user has a preferred method configured that has been +## disabled. This setting must be a method that is enabled. +## Options are totp, webauthn, mobile_push. +default_2fa_method: "" + +## +## Server Configuration +## +server: + + ## The address to listen on. + host: 0.0.0.0 + + ## The port to listen on. + port: 9091 + + ## Set the single level path Authelia listens on. + ## Must be alphanumeric chars and should not contain any slashes. + path: "" + + ## Set the path on disk to Authelia assets. + ## Useful to allow overriding of specific static assets. + # asset_path: /config/assets/ + + ## Buffers usually should be configured to be the same value. + ## Explanation at https://www.authelia.com/docs/configuration/server.html + ## Read buffer size adjusts the server's max incoming request size in bytes. + ## Write buffer size does the same for outgoing responses. + read_buffer_size: 4096 + write_buffer_size: 4096 + + ## Enables the pprof endpoint. + enable_pprof: false + + ## Enables the expvars endpoint. + enable_expvars: false + + ## Disables writing the health check vars to /app/.healthcheck.env which makes healthcheck.sh return exit code 0. + ## This is disabled by default if either /app/.healthcheck.env or /app/healthcheck.sh do not exist. + disable_healthcheck: false + + ## Authelia by default doesn't accept TLS communication on the server port. This section overrides this behaviour. + tls: + ## The path to the DER base64/PEM format private key. + key: "" + + ## The path to the DER base64/PEM format public certificate. + certificate: "" + + ## The list of certificates for client authentication. + client_certificates: [] + + ## Server headers configuration/customization. + headers: + + ## The CSP Template. Read the docs. + csp_template: "" + +## +## Log Configuration +## +log: + ## Level of verbosity for logs: info, debug, trace. + level: debug + + ## Format the logs are written as: json, text. + # format: json + + ## File path where the logs will be written. If not set logs are written to stdout. + # file_path: /config/authelia.log + + ## Whether to also log to stdout when a log_file_path is defined. + # keep_stdout: false + +## +## TOTP Configuration +## +## Parameters used for TOTP generation. +totp: + ## Disable TOTP. + disable: false + + ## The issuer name displayed in the Authenticator application of your choice. + issuer: authelia.com + + ## The TOTP algorithm to use. + ## It is CRITICAL you read the documentation before changing this option: + ## https://www.authelia.com/docs/configuration/one-time-password.html#algorithm + algorithm: sha1 + + ## The number of digits a user has to input. Must either be 6 or 8. + ## Changing this option only affects newly generated TOTP configurations. + ## It is CRITICAL you read the documentation before changing this option: + ## https://www.authelia.com/docs/configuration/one-time-password.html#digits + digits: 6 + + ## The period in seconds a one-time password is valid for. + ## Changing this option only affects newly generated TOTP configurations. + period: 30 + + ## The skew controls number of one-time passwords either side of the current one that are valid. + ## Warning: before changing skew read the docs link below. + skew: 1 + ## See: https://www.authelia.com/docs/configuration/one-time-password.html#input-validation to read the documentation. + + ## The size of the generated shared secrets. Default is 32 and is sufficient in most use cases, minimum is 20. + secret_size: 32 + +## +## WebAuthn Configuration +## +## Parameters used for WebAuthn. +webauthn: + ## Disable Webauthn. + disable: false + + ## Adjust the interaction timeout for Webauthn dialogues. + timeout: 60s + + ## The display name the browser should show the user for when using Webauthn to login/register. + display_name: Authelia + + ## Conveyance preference controls if we collect the attestation statement including the AAGUID from the device. + ## Options are none, indirect, direct. + attestation_conveyance_preference: indirect + + ## User verification controls if the user must make a gesture or action to confirm they are present. + ## Options are required, preferred, discouraged. + user_verification: preferred + +## +## Duo Push API Configuration +## +## Parameters used to contact the Duo API. Those are generated when you protect an application of type +## "Partner Auth API" in the management panel. +duo_api: + disable: false + hostname: api-123456789.{{ hms_docker_domain }} + integration_key: ABCDEF + ## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + secret_key: 1234567890abcdefghifjkl + enable_self_enrollment: false + +## +## NTP Configuration +## +## This is used to validate the servers time is accurate enough to validate TOTP. +ntp: + ## NTP server address. + address: "time.cloudflare.com:123" + + ## NTP version. + version: 4 + + ## Maximum allowed time offset between the host and the NTP server. + max_desync: 3s + + ## Disables the NTP check on startup entirely. This means Authelia will not contact a remote service at all if you + ## set this to true, and can operate in a truly offline mode. + disable_startup_check: false + + ## The default of false will prevent startup only if we can contact the NTP server and the time is out of sync with + ## the NTP server more than the configured max_desync. If you set this to true, an error will be logged but startup + ## will continue regardless of results. + disable_failure: false + +## +## Authentication Backend Provider Configuration +## +## Used for verifying user passwords and retrieve information such as email address and groups users belong to. +## +## The available providers are: `file`, `ldap`. You must use only one of these providers. +authentication_backend: + ## Disable both the HTML element and the API for reset password functionality. + disable_reset_password: false + + ## Password Reset Options. + password_reset: + + ## External reset password url that redirects the user to an external reset portal. This disables the internal reset + ## functionality. + custom_url: "" + + ## The amount of time to wait before we refresh data from the authentication backend. Uses duration notation. + ## To disable this feature set it to 'disable', this will slightly reduce security because for Authelia, users will + ## always belong to groups they belonged to at the time of login even if they have been removed from them in LDAP. + ## To force update on every request you can set this to '0' or 'always', this will increase processor demand. + ## See the below documentation for more information. + ## Duration Notation docs: https://www.authelia.com/docs/configuration/index.html#duration-notation-format + ## Refresh Interval docs: https://www.authelia.com/docs/configuration/authentication/ldap.html#refresh-interval + refresh_interval: 5m + + ## + ## LDAP (Authentication Provider) + ## + ## This is the recommended Authentication Provider in production + ## because it allows Authelia to offload the stateful operations + ## onto the LDAP service. + # ldap: + ## The LDAP implementation, this affects elements like the attribute utilised for resetting a password. + ## Acceptable options are as follows: + ## - 'activedirectory' - For Microsoft Active Directory. + ## - 'custom' - For custom specifications of attributes and filters. + ## This currently defaults to 'custom' to maintain existing behaviour. + ## + ## Depending on the option here certain other values in this section have a default value, notably all of the + ## attribute mappings have a default value that this config overrides, you can read more about these default values + ## at https://www.authelia.com/docs/configuration/authentication/ldap.html#defaults + # implementation: custom + + ## The url to the ldap server. Format: ://
[:]. + ## Scheme can be ldap or ldaps in the format (port optional). + # url: ldap://127.0.0.1 + + ## The dial timeout for LDAP. + # timeout: 5s + + ## Use StartTLS with the LDAP connection. + # start_tls: false + + # tls: + ## Server Name for certificate validation (in case it's not set correctly in the URL). + # server_name: ldap.{{ hms_docker_domain }} + + ## Skip verifying the server certificate (to allow a self-signed certificate). + ## In preference to setting this we strongly recommend you add the public portion of the certificate to the + ## certificates directory which is defined by the `certificates_directory` option at the top of the config. + # skip_verify: false + + ## Minimum TLS version for either Secure LDAP or LDAP StartTLS. + # minimum_version: TLS1.2 + + ## The distinguished name of the container searched for objects in the directory information tree. + ## See also: additional_users_dn, additional_groups_dn. + # base_dn: dc=example,dc=com + + ## The attribute holding the username of the user. This attribute is used to populate the username in the session + ## information. It was introduced due to #561 to handle case insensitive search queries. For you information, + ## Microsoft Active Directory usually uses 'sAMAccountName' and OpenLDAP usually uses 'uid'. Beware that this + ## attribute holds the unique identifiers for the users binding the user and the configuration stored in database. + ## Therefore only single value attributes are allowed and the value must never be changed once attributed to a user + ## otherwise it would break the configuration for that user. Technically, non-unique attributes like 'mail' can also + ## be used but we don't recommend using them, we instead advise to use the attributes mentioned above + ## (sAMAccountName and uid) to follow https://www.ietf.org/rfc/rfc2307.txt. + # username_attribute: uid + + ## The additional_users_dn is prefixed to base_dn and delimited by a comma when searching for users. + ## i.e. with this set to OU=Users and base_dn set to DC=a,DC=com; OU=Users,DC=a,DC=com is searched for users. + # additional_users_dn: ou=users + + ## The users filter used in search queries to find the user profile based on input filled in login form. + ## Various placeholders are available in the user filter which you can read about in the documentation which can + ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#users-filter-replacements + ## + ## Recommended settings are as follows: + ## - Microsoft Active Directory: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) + ## - OpenLDAP: + ## - (&({username_attribute}={input})(objectClass=person)) + ## - (&({username_attribute}={input})(objectClass=inetOrgPerson)) + ## + ## To allow sign in both with username and email, one can use a filter like + ## (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person)) + # users_filter: (&({username_attribute}={input})(objectClass=person)) + + ## The additional_groups_dn is prefixed to base_dn and delimited by a comma when searching for groups. + ## i.e. with this set to OU=Groups and base_dn set to DC=a,DC=com; OU=Groups,DC=a,DC=com is searched for groups. + # additional_groups_dn: ou=groups + + ## The groups filter used in search queries to find the groups based on relevant authenticated user. + ## Various placeholders are available in the groups filter which you can read about in the documentation which can + ## be found at: https://www.authelia.com/docs/configuration/authentication/ldap.html#groups-filter-replacements + ## + ## If your groups use the `groupOfUniqueNames` structure use this instead: + ## (&(uniqueMember={dn})(objectClass=groupOfUniqueNames)) + # groups_filter: (&(member={dn})(objectClass=groupOfNames)) + + ## The attribute holding the name of the group. + # group_name_attribute: cn + + ## The attribute holding the mail address of the user. If multiple email addresses are defined for a user, only the + ## first one returned by the LDAP server is used. + # mail_attribute: mail + + ## The attribute holding the display name of the user. This will be used to greet an authenticated user. + # display_name_attribute: displayName + + ## Follow referrals returned by the server. + ## This is especially useful for environments where read-only servers exist. Only implemented for write operations. + # permit_referrals: false + + ## The username and password of the admin user. + # user: cn=admin,dc=example,dc=com + ## Password can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + # password: password + + ## + ## File (Authentication Provider) + ## + ## With this backend, the users database is stored in a file which is updated when users reset their passwords. + ## Therefore, this backend is meant to be used in a dev environment and not in production since it prevents Authelia + ## to be scaled to more than one instance. The options under 'password' have sane defaults, and as it has security + ## implications it is highly recommended you leave the default values. Before considering changing these settings + ## please read the docs page below: + ## https://www.authelia.com/docs/configuration/authentication/file.html#password-hash-algorithm-tuning + ## + ## Important: Kubernetes (or HA) users must read https://www.authelia.com/docs/features/statelessness.html + ## + file: + path: /config/users_database.yml + password: + algorithm: argon2id + iterations: 1 + key_length: 32 + salt_length: 16 + memory: 1024 + parallelism: 8 + +## +## Password Policy Configuration. +## +password_policy: + + ## The standard policy allows you to tune individual settings manually. + standard: + enabled: false + + ## Require a minimum length for passwords. + min_length: 8 + + ## Require a maximum length for passwords. + max_length: 0 + + ## Require uppercase characters. + require_uppercase: true + + ## Require lowercase characters. + require_lowercase: true + + ## Require numeric characters. + require_number: true + + ## Require special characters. + require_special: true + + ## zxcvbn is a well known and used password strength algorithm. It does not have tunable settings. + zxcvbn: + enabled: false + + ## Configures the minimum score allowed. + min_score: 3 + +## +## Access Control Configuration +## +## Access control is a list of rules defining the authorizations applied for one resource to users or group of users. +## +## If 'access_control' is not defined, ACL rules are disabled and the 'bypass' rule is applied, i.e., access is allowed +## to anyone. Otherwise restrictions follow the rules defined. +## +## Note: One can use the wildcard * to match any subdomain. +## It must stand at the beginning of the pattern. (example: *.mydomain.com) +## +## Note: You must put patterns containing wildcards between simple quotes for the YAML to be syntactically correct. +## +## Definition: A 'rule' is an object with the following keys: 'domain', 'subject', 'policy' and 'resources'. +## +## - 'domain' defines which domain or set of domains the rule applies to. +## +## - 'subject' defines the subject to apply authorizations to. This parameter is optional and matching any user if not +## provided. If provided, the parameter represents either a user or a group. It should be of the form +## 'user:' or 'group:'. +## +## - 'policy' is the policy to apply to resources. It must be either 'bypass', 'one_factor', 'two_factor' or 'deny'. +## +## - 'resources' is a list of regular expressions that matches a set of resources to apply the policy to. This parameter +## is optional and matches any resource if not provided. +## +## Note: the order of the rules is important. The first policy matching (domain, resource, subject) applies. +access_control: + ## Default policy can either be 'bypass', 'one_factor', 'two_factor' or 'deny'. It is the policy applied to any + ## resource if there is no policy to be applied to the user. + default_policy: deny + + networks: + - name: internal + networks: + - {{ hms_docker_network_subnet }} + + rules: + ## Rules applied to everyone + - domain: 'public.{{ hms_docker_domain }}' + policy: bypass + + ## Domain Regex examples. Generally we recommend just using a standard domain. + # - domain_regex: '^(?P\w+)\.example\.com$' + # policy: one_factor + # - domain_regex: '^(?P\w+)\.example\.com$' + # policy: one_factor + # - domain_regex: + # - '^appgroup-.*\.example\.com$' + # - '^appgroup2-.*\.example\.com$' + # policy: one_factor + # - domain_regex: '^.*\.example\.com$' + # policy: two_factor + + - domain: 'secure.{{ hms_docker_domain }}' + policy: one_factor + ## Network based rule, if not provided any network matches. + networks: + - internal + + - domain: + - 'secure.{{ hms_docker_domain }}' + - 'private.{{ hms_docker_domain }}' + policy: two_factor + + - domain: 'singlefactor.{{ hms_docker_domain }}' + policy: one_factor + + ## Rules applied to 'admins' group + - domain: 'mx2.mail.{{ hms_docker_domain }}' + subject: 'group:admins' + policy: deny + + - domain: '*.{{ hms_docker_domain }}' + subject: + - 'group:admins' + - 'group:moderators' + policy: two_factor + + ## Rules applied to 'dev' group + - domain: 'dev.{{ hms_docker_domain }}' + resources: + - '^/groups/dev/.*$' + subject: 'group:dev' + policy: two_factor + + ## Rules applied to user 'john' + - domain: 'dev.{{ hms_docker_domain }}' + resources: + - '^/users/john/.*$' + subject: 'user:john' + policy: two_factor + + ## Rules applied to user 'harry' + - domain: 'dev.{{ hms_docker_domain }}' + resources: + - '^/users/harry/.*$' + subject: 'user:harry' + policy: two_factor + + ## Rules applied to user 'bob' + - domain: '*.mail.{{ hms_docker_domain }}' + subject: 'user:bob' + policy: two_factor + - domain: 'dev.{{ hms_docker_domain }}' + resources: + - '^/users/bob/.*$' + subject: 'user:bob' + policy: two_factor + +## +## Session Provider Configuration +## +## The session cookies identify the user once logged in. +## The available providers are: `memory`, `redis`. Memory is the provider unless redis is defined. +session: + ## The name of the session cookie. + name: authelia_session + + ## The domain to protect. + ## Note: the authenticator must also be in that domain. + ## If empty, the cookie is restricted to the subdomain of the issuer. + domain: {{ hms_docker_domain }} + + ## Sets the Cookie SameSite value. Possible options are none, lax, or strict. + ## Please read https://www.authelia.com/docs/configuration/session/#same_site + same_site: lax + + ## The secret to encrypt the session data. This is only used with Redis / Redis Sentinel. + ## Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + secret: insecure_session_secret + + ## The value for expiration, inactivity, and remember_me_duration are in seconds or the duration notation format. + ## See: https://www.authelia.com/docs/configuration/index.html#duration-notation-format + ## All three of these values affect the cookie/session validity period. Longer periods are considered less secure + ## because a stolen cookie will last longer giving attackers more time to spy or attack. + + ## The time before the cookie expires and the session is destroyed if remember me IS NOT selected. + expiration: 1h + + ## The inactivity time before the session is reset. If expiration is set to 1h, and this is set to 5m, if the user + ## does not select the remember me option their session will get destroyed after 1h, or after 5m since the last time + ## Authelia detected user activity. + inactivity: 5m + + ## The time before the cookie expires and the session is destroyed if remember me IS selected. + ## Value of -1 disables remember me. + remember_me_duration: 1M + + ## + ## Redis Provider + ## + ## Important: Kubernetes (or HA) users must read https://www.authelia.com/docs/features/statelessness.html + ## + # redis: + # host: 127.0.0.1 + # port: 6379 + ## Use a unix socket instead + # host: /var/run/redis/redis.sock + + ## Username used for redis authentication. This is optional and a new feature in redis 6.0. + # username: authelia + + ## Password can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + # password: authelia + + ## This is the Redis DB Index https://redis.io/commands/select (sometimes referred to as database number, DB, etc). + # database_index: 0 + + ## The maximum number of concurrent active connections to Redis. + # maximum_active_connections: 8 + + ## The target number of idle connections to have open ready for work. Useful when opening connections is slow. + # minimum_idle_connections: 0 + + ## The Redis TLS configuration. If defined will require a TLS connection to the Redis instance(s). + # tls: + ## Server Name for certificate validation (in case you are using the IP or non-FQDN in the host option). + # server_name: myredis.{{ hms_docker_domain }} + + ## Skip verifying the server certificate (to allow a self-signed certificate). + ## In preference to setting this we strongly recommend you add the public portion of the certificate to the + ## certificates directory which is defined by the `certificates_directory` option at the top of the config. + # skip_verify: false + + ## Minimum TLS version for the connection. + # minimum_version: TLS1.2 + + ## The Redis HA configuration options. + ## This provides specific options to Redis Sentinel, sentinel_name must be defined (Master Name). + # high_availability: + ## Sentinel Name / Master Name. + # sentinel_name: mysentinel + + ## Specific username for Redis Sentinel. The node username and password is configured above. + # sentinel_username: sentinel_specific_user + + ## Specific password for Redis Sentinel. The node username and password is configured above. + # sentinel_password: sentinel_specific_pass + + ## The additional nodes to pre-seed the redis provider with (for sentinel). + ## If the host in the above section is defined, it will be combined with this list to connect to sentinel. + ## For high availability to be used you must have either defined; the host above or at least one node below. + # nodes: + # - host: sentinel-node1 + # port: 6379 + # - host: sentinel-node2 + # port: 6379 + + ## Choose the host with the lowest latency. + # route_by_latency: false + + ## Choose the host randomly. + # route_randomly: false + +## +## Regulation Configuration +## +## This mechanism prevents attackers from brute forcing the first factor. It bans the user if too many attempts are made +## in a short period of time. +regulation: + ## The number of failed login attempts before user is banned. Set it to 0 to disable regulation. + max_retries: 3 + + ## The time range during which the user can attempt login before being banned. The user is banned if the + ## authentication failed 'max_retries' times in a 'find_time' seconds window. Find Time accepts duration notation. + ## See: https://www.authelia.com/docs/configuration/index.html#duration-notation-format + find_time: 2m + + ## The length of time before a banned user can login again. Ban Time accepts duration notation. + ## See: https://www.authelia.com/docs/configuration/index.html#duration-notation-format + ban_time: 5m + +## +## Storage Provider Configuration +## +## The available providers are: `local`, `mysql`, `postgres`. You must use one and only one of these providers. +storage: + ## The encryption key that is used to encrypt sensitive information in the database. Must be a string with a minimum + ## length of 20. Please see the docs if you configure this with an undesirable key and need to change it. + encryption_key: {{ lookup('password', '/dev/null length=32 chars=ascii_letters') }} + + ## + ## Local (Storage Provider) + ## + ## This stores the data in a SQLite3 Database. + ## This is only recommended for lightweight non-stateful installations. + ## + ## Important: Kubernetes (or HA) users must read https://www.authelia.com/docs/features/statelessness.html + ## + local: + path: /config/db.sqlite3 + + ## + ## MySQL / MariaDB (Storage Provider) + ## + # mysql: + # host: 127.0.0.1 + # port: 3306 + # database: authelia + # username: authelia + ## Password can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + # password: mypassword + # timeout: 5s + + ## + ## PostgreSQL (Storage Provider) + ## + # postgres: + # host: 127.0.0.1 + # port: 5432 + # database: authelia + # schema: public + # username: authelia + # ## Password can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + # password: mypassword + # timeout: 5s + # ssl: + # mode: disable + # root_certificate: disable + # certificate: disable + # key: disable + +## +## Notification Provider +## +## Notifications are sent to users when they require a password reset, a Webauthn registration or a TOTP registration. +## The available providers are: filesystem, smtp. You must use only one of these providers. +notifier: + ## You can disable the notifier startup check by setting this to true. + disable_startup_check: false + + ## + ## File System (Notification Provider) + ## + ## Important: Kubernetes (or HA) users must read https://www.authelia.com/docs/features/statelessness.html + ## + filesystem: + filename: /config/notification.txt + + ## + ## SMTP (Notification Provider) + ## + ## Use a SMTP server for sending notifications. Authelia uses the PLAIN or LOGIN methods to authenticate. + ## [Security] By default Authelia will: + ## - force all SMTP connections over TLS including unauthenticated connections + ## - use the disable_require_tls boolean value to disable this requirement + ## (only works for unauthenticated connections) + ## - validate the SMTP server x509 certificate during the TLS handshake against the hosts trusted certificates + ## (configure in tls section) + # smtp: + ## The SMTP host to connect to. + # host: 127.0.0.1 + + ## The port to connect to the SMTP host on. + # port: 1025 + + ## The connection timeout. + # timeout: 5s + + ## The username used for SMTP authentication. + # username: test + + ## The password used for SMTP authentication. + ## Can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + # password: password + + ## The sender is used to is used for the MAIL FROM command and the FROM header. + ## If this is not defined and the username is an email, we use the username as this value. This can either be just + ## an email address or the RFC5322 'Name ' format. + # sender: "Authelia " + + ## HELO/EHLO Identifier. Some SMTP Servers may reject the default of localhost. + # identifier: localhost + + ## Subject configuration of the emails sent. {title} is replaced by the text from the notifier. + # subject: "[Authelia] {title}" + + ## This address is used during the startup check to verify the email configuration is correct. + ## It's not important what it is except if your email server only allows local delivery. + # startup_check_address: test@authelia.com + + ## By default we require some form of TLS. This disables this check though is not advised. + # disable_require_tls: false + + ## Disables sending HTML formatted emails. + # disable_html_emails: false + + # tls: + ## Server Name for certificate validation (in case you are using the IP or non-FQDN in the host option). + # server_name: smtp.{{ hms_docker_domain }} + + ## Skip verifying the server certificate (to allow a self-signed certificate). + ## In preference to setting this we strongly recommend you add the public portion of the certificate to the + ## certificates directory which is defined by the `certificates_directory` option at the top of the config. + # skip_verify: false + + ## Minimum TLS version for either StartTLS or SMTPS. + # minimum_version: TLS1.2 + +## +## Identity Providers +## +# identity_providers: + + ## + ## OpenID Connect (Identity Provider) + ## + ## It's recommended you read the documentation before configuration of this section: + ## https://www.authelia.com/docs/configuration/identity-providers/oidc.html + # oidc: + ## The hmac_secret is used to sign OAuth2 tokens (authorization code, access tokens and refresh tokens). + ## HMAC Secret can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html + # hmac_secret: this_is_a_secret_abc123abc123abc + + ## The issuer_private_key is used to sign the JWT forged by OpenID Connect. + ## Issuer Private Key can also be set using a secret: https://docs.authelia.com/configuration/secrets.html + # issuer_private_key: | + # --- KEY START + # --- KEY END + + ## The lifespans configure the expiration for these token types. + # access_token_lifespan: 1h + # authorize_code_lifespan: 1m + # id_token_lifespan: 1h + # refresh_token_lifespan: 90m + + ## Enables additional debug messages. + # enable_client_debug_messages: false + + ## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it below 8 for + ## security reasons. + # minimum_parameter_entropy: 8 + + ## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it set to 'never' + ## for security reasons. + # enforce_pkce: public_clients_only + + ## Cross-Origin Resource Sharing (CORS) settings. + # cors: + ## List of endpoints in addition to the metadata endpoints to permit cross-origin requests on. + # endpoints: + # - authorization + # - token + # - revocation + # - introspection + # - userinfo + + ## List of allowed origins. + ## Any origin with https is permitted unless this option is configured or the + ## allowed_origins_from_client_redirect_uris option is enabled. + # allowed_origins: + # - https://{{ hms_docker_domain }} + + ## Automatically adds the origin portion of all redirect URI's on all clients to the list of allowed_origins, + ## provided they have the scheme http or https and do not have the hostname of localhost. + # allowed_origins_from_client_redirect_uris: false + + ## Clients is a list of known clients and their configuration. + # clients: + # - + ## The ID is the OpenID Connect ClientID which is used to link an application to a configuration. + # id: myapp + + ## The description to show to users when they end up on the consent screen. Defaults to the ID above. + # description: My Application + + ## The client secret is a shared secret between Authelia and the consumer of this client. + # secret: this_is_a_secret + + ## Sector Identifiers are occasionally used to generate pairwise subject identifiers. In most cases this is not + ## necessary. Read the documentation for more information. + ## The subject identifier must be the host component of a URL, which is a domain name with an optional port. + # sector_identifier: {{ hms_docker_domain }} + + ## Sets the client to public. This should typically not be set, please see the documentation for usage. + # public: false + + ## The policy to require for this client; one_factor or two_factor. + # authorization_policy: two_factor + + ## By default users cannot remember pre-configured consents. Setting this value to a period of time using a + ## duration notation will enable users to remember consent for this client. The time configured is the amount + ## of time the pre-configured consent is valid for granting new authorizations to the user. + # pre_configured_consent_duration: + + ## Audience this client is allowed to request. + # audience: [] + + ## Scopes this client is allowed to request. + # scopes: + # - openid + # - groups + # - email + # - profile + + ## Redirect URI's specifies a list of valid case-sensitive callbacks for this client. + # redirect_uris: + # - https://oidc.{{ hms_docker_domain }}:8080/oauth2/callback + + ## Grant Types configures which grants this client can obtain. + ## It's not recommended to define this unless you know what you're doing. + # grant_types: + # - refresh_token + # - authorization_code + + ## Response Types configures which responses this client can be sent. + ## It's not recommended to define this unless you know what you're doing. + # response_types: + # - code + + ## Response Modes configures which response modes this client supports. + # response_modes: + # - form_post + # - query + # - fragment + + ## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256. + # userinfo_signing_algorithm: none +... diff --git a/roles/hmsdocker/templates/authentik_env.j2 b/roles/hmsdocker/templates/authentik_env.j2 new file mode 100644 index 0000000..09a2d96 --- /dev/null +++ b/roles/hmsdocker/templates/authentik_env.j2 @@ -0,0 +1,5 @@ +PG_PASS={{ lookup('password', '/dev/null length=40 chars=ascii_letters') }} +PG_USER=authentik +PG_DB=authentik + +AUTHENTIK_SECRET_KEY={{ lookup('password', '/dev/null length=50 chars=ascii_letters') }} diff --git a/roles/hmsdocker/templates/authentik_outpost.j2 b/roles/hmsdocker/templates/authentik_outpost.j2 new file mode 100644 index 0000000..639ec04 --- /dev/null +++ b/roles/hmsdocker/templates/authentik_outpost.j2 @@ -0,0 +1,22 @@ +log_level: info +docker_labels: + traefik.enable: "true" + traefik.http.services.authentik-proxy-{{ project_name }}-{{ item.key }}-service.loadbalancer.server.port: "9000" + traefik.http.routers.authentik-proxy-{{ project_name }}-{{ item.key }}-router.rule: Host(`{{ item.key }}.{{ hms_docker_domain }}`) && PathPrefix(`/outpost.goauthentik.io/`) + traefik.http.middlewares.authentik-proxy-{{ project_name }}-{{ item.key }}-router.forwardauth.address: http://authentik-proxy-{{ project_name }}-{{ item.key }}:9000/outpost.goauthentik.io/auth/traefik + traefik.http.middlewares.authentik-proxy-{{ project_name }}-{{ item.key }}-router.forwardauth.trustForwardHeader: "true" + traefik.http.middlewares.authentik-proxy-{{ project_name }}-{{ item.key }}-router.forwardauth.authResponseHeaders: X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version +authentik_host: {{ authentik_host }} +authentik_host_browser: {{ authentik_external_host }} +docker_network: {{ project_name }}_traefik_net +container_image: null +docker_map_ports: false +kubernetes_replicas: 1 +kubernetes_namespace: default +object_naming_template: authentik-proxy-{{ project_name }}-{{ item.key }} +authentik_host_insecure: false +kubernetes_service_type: ClusterIP +kubernetes_image_pull_secrets: [] +kubernetes_disabled_components: [] +kubernetes_ingress_annotations: {} +kubernetes_ingress_secret_name: authentik-outpost-tls diff --git a/roles/hmsdocker/templates/docker-compose.yml.j2 b/roles/hmsdocker/templates/docker-compose.yml.j2 index 3d795f8..1a7793f 100644 --- a/roles/hmsdocker/templates/docker-compose.yml.j2 +++ b/roles/hmsdocker/templates/docker-compose.yml.j2 @@ -1,9 +1,9 @@ version: '3' services: -{% if hms_docker_container_map['portainer']['enabled'] %} +{% if container_enabled_portainer %} # Portainer container, webgui for docker portainer: - image: portainer/portainer:latest + image: portainer/portainer-ce:latest container_name: portainer command: -H unix:///var/run/docker.sock restart: {{ container_restart_policy }} @@ -13,20 +13,23 @@ services: max-size: 10m networks: - "traefik_net" -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_portainer %} ports: - 9000:9000 {% endif %} volumes: - /var/run/docker.sock:/var/run/docker.sock - {{ hms_docker_apps_path }}/portainer/config:/data -{% if hms_docker_container_map['portainer']['traefik'] %} +{% if traefik_enabled_portainer %} labels: - traefik.enable=true - traefik.http.services.portainer-{{ project_name }}.loadbalancer.server.port=9000 -{% if not hms_docker_container_map['portainer']['expose_to_public'] %} - - "traefik.http.routers.portainer-{{ project_name }}.middlewares=internal-ipwhitelist" + - traefik.http.routers.portainer-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['portainer']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_portainer %} + - traefik.http.routers.portainer-{{ project_name }}.middlewares=internal-ipwhitelist {% endif %} +{% if authentik_enabled_portainer %} + - traefik.http.routers.portainer-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-portainer-router@docker {% endif %} {% endif %} @@ -47,7 +50,7 @@ services: - /var/run/docker.sock:/var/run/docker.sock {% endif %} -{% if hms_docker_container_map['traefik']['enabled'] %} +{% if container_enabled_traefik %} # Traefik container, loadbalancer/reverse proxy/ssl traefik: image: traefik:v2.6.1 @@ -76,17 +79,150 @@ services: - /var/run/docker.sock:/var/run/docker.sock - {{ hms_docker_apps_path }}/traefik/config/traefik.yml:/etc/traefik/traefik.yml - {{ hms_docker_apps_path }}/traefik/config/certs/:/certs/ -{% if hms_docker_container_map['traefik']['traefik'] %} +{% if traefik_enabled_traefik %} labels: - traefik.enable=true + - traefik.http.routers.traefik-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['traefik']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) - traefik.http.services.traefik-{{ project_name }}.loadbalancer.server.port=8080 - "traefik.http.middlewares.internal-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, {{ traefik_subnet_allow_list }}" - "traefik.http.middlewares.external-ipwhitelist.ipwhitelist.sourcerange=0.0.0.0/0" - "traefik.http.routers.traefik-{{ project_name }}.middlewares=internal-ipwhitelist" +{% if authentik_enabled_traefik %} + - traefik.http.routers.traefik-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-traefik-router@docker +{% endif %} +{% endif %} +{% endif %} + +{% if authentik_enabled_globally %} + # Authentik container, authentication/authorization + authentik-postgresql: + container_name: authentik-postgresql + image: postgres:12-alpine + restart: {{ container_restart_policy }} + networks: + - authentik_net + logging: + driver: json-file + options: + max-size: 10m + healthcheck: + test: ["CMD", "pg_isready"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s + volumes: + - authentik_database:/var/lib/postgresql/data + env_file: + - .authentik.env + environment: + - POSTGRES_PASSWORD=${PG_PASS:?database password required} + - POSTGRES_USER=${PG_USER:-authentik} + - POSTGRES_DB=${PG_DB:-authentik} + + authentik-redis: + container_name: authentik-redis + image: redis:alpine + restart: {{ container_restart_policy }} + networks: + - authentik_net + logging: + driver: json-file + options: + max-size: 10m + healthcheck: + test: ["CMD-SHELL", "redis-cli ping | grep PONG"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 3s + + authentik-server: + container_name: authentik-server + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.5.2} + restart: {{ container_restart_policy }} + networks: + - authentik_net + - traefik_net + logging: + driver: json-file + options: + max-size: 10m + command: server + env_file: + - .authentik.env + environment: + AUTHENTIK_REDIS__HOST: authentik-redis + AUTHENTIK_POSTGRESQL__HOST: authentik-postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + # AUTHENTIK_ERROR_REPORTING__ENABLED: "true" + # WORKERS: 2 + volumes: + - {{ hms_docker_apps_path }}/authentik/media:/media + - {{ hms_docker_apps_path }}/authentik/custom-templates:/templates + - authentik_geoip:/geoip +{% if expose_ports_enabled_authentik %} + ports: + - "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9001}:9000" + - "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443" +{% endif %} +{% if traefik_enabled_authentik %} + labels: + - traefik.enable=true + - traefik.http.services.authentik-server-{{ project_name }}.loadbalancer.server.port=9000 + - traefik.http.routers.authentik-server-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['authentik']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_authentik %} + - "traefik.http.routers.authentik-server-{{ project_name }}.middlewares=internal-ipwhitelist" +{% endif %} +{% if authentik_enabled_authentik %} + - traefik.http.routers.authentik-server-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-authentik-server-router@docker {% endif %} {% endif %} -{% if hms_docker_container_map['nzbget']['enabled'] %} + authentik-worker: + container_name: authentik-worker + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.5.2} + restart: {{ container_restart_policy }} + networks: + - authentik_net + command: worker + env_file: + - .authentik.env + environment: + AUTHENTIK_REDIS__HOST: authentik-redis + AUTHENTIK_POSTGRESQL__HOST: authentik-postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + # AUTHENTIK_ERROR_REPORTING__ENABLED: "true" + # This is optional, and can be removed. If you remove this, the following will happen + # - The permissions for the /media folders aren't fixed, so make sure they are 1000:1000 + # - The docker socket can't be accessed anymore + user: root + volumes: + - {{ hms_docker_apps_path }}/authentik/media:/media + - {{ hms_docker_apps_path }}/authentik/certs:/certs + - /var/run/docker.sock:/var/run/docker.sock + - {{ hms_docker_apps_path }}/authentik/custom-templates:/templates + - authentik_geoip:/geoip + + authentik-geoipupdate: + container_name: authentik-geoipupdate + image: "maxmindinc/geoipupdate:latest" + networks: + - authentik_net + volumes: + - "authentik_geoip:/usr/share/GeoIP" + env_file: + - .authentik.env + environment: + GEOIPUPDATE_EDITION_IDS: "GeoLite2-City" + GEOIPUPDATE_FREQUENCY: "8" +{% endif %} + +{% if container_enabled_nzbget %} # NZBGet container nzbget: image: lscr.io/linuxserver/nzbget:latest @@ -108,21 +244,25 @@ services: {% if nzbget_enable_downloads_mount %} - {{ hms_docker_apps_path }}/nzbget/downloads:/data/usenet {% endif %} -{% if container_expose_ports %} +{% if expose_ports_enabled_nzbget %} ports: - 6789:6789 {% endif %} -{% if hms_docker_container_map['nzbget']['traefik'] %} +{% if traefik_enabled_nzbget %} labels: - traefik.enable=true - traefik.http.services.nzbget-{{ project_name }}.loadbalancer.server.port=6789 -{% if not hms_docker_container_map['nzbget']['expose_to_public'] %} + - traefik.http.routers.nzbget-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['nzbget']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if expose_public_enabled_nzbget %} - "traefik.http.routers.nzbget-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_nzbget %} + - traefik.http.routers.nzbget-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-nzbget-router@docker +{% endif %} {% endif %} {% endif %} -{% if hms_docker_container_map['transmission']['enabled'] and transmission_vpn_provider is defined and transmission_vpn_user is defined and transmission_vpn_pass is defined %} +{% if container_enabled_transmission %} # Transmission container, torrent client/VPN transmission: image: haugene/transmission-openvpn:latest @@ -136,7 +276,7 @@ services: - "download_net" cap_add: - NET_ADMIN -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_transmission %} ports: - 9091:9091 - 8888:8888 @@ -192,24 +332,25 @@ services: - transmission environment: - TZ={{ container_timezone }} -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_transmission %} ports: - 8081:8080 {% endif %} -{% if hms_docker_container_map['transmission']['traefik'] %} +{% if traefik_enabled_transmission %} labels: - traefik.enable=true - # This is an "override" rule since the traefik config, by default, will use the container name, but this container is the proxy - # for the transmission container web UI, so we have traefik point to this one instead when trying to access the transmission web UI - - traefik.http.routers.proxy-{{ project_name }}.rule=Host(`transmission.{{ hms_docker_domain }}`) + - traefik.http.routers.proxy-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['transmission']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) - traefik.http.services.proxy-{{ project_name }}.loadbalancer.server.port=8080 -{% if not hms_docker_container_map['transmission']['expose_to_public'] %} +{% if not expose_public_enabled_transmission %} - "traefik.http.routers.proxy-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_transmission %} + - traefik.http.routers.transmission-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-transmission-router@docker +{% endif %} {% endif %} {% endif %} -{% if hms_docker_container_map['requestrr']['enabled'] %} +{% if container_enabled_requestrr %} # Requestrr container, chat client for requests requestrr: image: darkalfx/requestrr:latest @@ -228,21 +369,25 @@ services: - TZ={{ container_timezone }} volumes: - {{ hms_docker_apps_path }}/requestrr/config:/root/config -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_requestrr %} ports: - 4545:4545 {% endif %} -{% if hms_docker_container_map['requestrr']['traefik'] %} +{% if traefik_enabled_requestrr %} labels: - traefik.enable=true - traefik.http.services.requestrr-{{ project_name }}.loadbalancer.server.port=4545 -{% if not hms_docker_container_map['requestrr']['expose_to_public'] %} + - traefik.http.routers.requestrr-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['requestrr']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_requestrr %} - "traefik.http.routers.requestrr-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_requestrr %} + - traefik.http.routers.requestrr-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-requestrr-router@docker +{% endif %} {% endif %} {% endif %} -{% if hms_docker_container_map['prowlarr']['enabled'] %} +{% if container_enabled_prowlarr %} # Prowlarr container, torrent indexer prowlarr: image: linuxserver/prowlarr:develop @@ -261,21 +406,25 @@ services: - TZ={{ container_timezone }} volumes: - {{ hms_docker_apps_path }}/prowlarr/config:/config -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_prowlarr %} ports: - 9696:9696 {% endif %} -{% if hms_docker_container_map['prowlarr']['traefik'] %} +{% if traefik_enabled_prowlarr %} labels: - traefik.enable=true - traefik.http.services.prowlarr-{{ project_name }}.loadbalancer.server.port=9696 -{% if not hms_docker_container_map['prowlarr']['expose_to_public'] %} + - traefik.http.routers.prowlarr-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['prowlarr']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_prowlarr %} - "traefik.http.routers.prowlarr-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_prowlarr %} + - traefik.http.routers.prowlarr-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-prowlarr-router@docker +{% endif %} {% endif %} {% endif %} -{% if hms_docker_container_map['sonarr']['enabled'] %} +{% if container_enabled_sonarr %} # Sonarr container, tv show indexer sonarr: image: linuxserver/sonarr:latest @@ -292,13 +441,17 @@ services: - PUID={{ container_uid }} - PGID={{ container_gid }} - TZ={{ container_timezone }} -{% if hms_docker_container_map['sonarr']['traefik'] %} +{% if traefik_enabled_sonarr %} labels: - traefik.enable=true - traefik.http.services.sonarr-{{ project_name }}.loadbalancer.server.port=8989 -{% if not hms_docker_container_map['sonarr']['expose_to_public'] %} + - traefik.http.routers.sonarr-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['sonarr']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_sonarr %} - "traefik.http.routers.sonarr-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_sonarr %} + - traefik.http.routers.sonarr-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-sonarr-router@docker +{% endif %} {% endif %} volumes: - {{ hms_docker_apps_path }}/sonarr/config:/config @@ -315,13 +468,13 @@ services: - {{ path.local_mount_path }}/{{ hms_docker_library_folder_name }}:/data/media_additional/{{ path.name | map('regex_replace', regex, replace) | list | join }} {% endfor %} {% endif %} -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_sonarr %} ports: - 8989:8989 {% endif %} {% endif %} -{% if hms_docker_container_map['radarr']['enabled'] %} +{% if container_enabled_radarr %} # Radarr container, movie indexer radarr: image: linuxserver/radarr:latest @@ -338,13 +491,17 @@ services: - PUID={{ container_uid }} - PGID={{ container_gid }} - TZ={{ container_timezone }} -{% if hms_docker_container_map['radarr']['traefik'] %} +{% if traefik_enabled_radarr %} labels: - traefik.enable=true - traefik.http.services.radarr-{{ project_name }}.loadbalancer.server.port=7878 -{% if not hms_docker_container_map['radarr']['expose_to_public'] %} + - traefik.http.routers.radarr-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['radarr']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_radarr %} - "traefik.http.routers.radarr-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_radarr %} + - traefik.http.routers.radarr-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-radarr-router@docker +{% endif %} {% endif %} volumes: - {{ hms_docker_apps_path }}/radarr/config:/config @@ -361,13 +518,13 @@ services: - {{ path.local_mount_path }}/{{ hms_docker_library_folder_name }}:/data/media_additional/{{ path.name | map('regex_replace', regex, replace) | list | join }} {% endfor %} {% endif %} -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_radarr %} ports: - 7878:7878 {% endif %} {% endif %} -{% if hms_docker_container_map['bazarr']['enabled'] %} +{% if container_enabled_bazarr %} # Bazarr container, subtitle indexer bazarr: image: linuxserver/bazarr:latest @@ -379,7 +536,7 @@ services: max-size: 10m networks: - "traefik_net" -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_bazarr %} ports: - 6767:6767 {% endif %} @@ -395,17 +552,21 @@ services: - {{ path.local_mount_path }}/{{ hms_docker_library_folder_name }}:/data/media_additional/{{ path.name | map('regex_replace', regex, replace) | list | join }} {% endfor %} {% endif %} -{% if hms_docker_container_map['bazarr']['traefik'] %} +{% if traefik_enabled_bazarr %} labels: - traefik.enable=true - traefik.http.services.bazarr-{{ project_name }}.loadbalancer.server.port=6767 -{% if not hms_docker_container_map['bazarr']['expose_to_public'] %} + - traefik.http.routers.bazarr-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['bazarr']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_bazarr %} - "traefik.http.routers.bazarr-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_bazarr %} + - traefik.http.routers.bazarr-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-bazarr-router@docker +{% endif %} {% endif %} {% endif %} -{% if hms_docker_container_map['overseerr']['enabled'] %} +{% if container_enabled_overseerr %} # Overseer container, request platform overseerr: image: linuxserver/overseerr:latest @@ -423,21 +584,25 @@ services: - PUID={{ container_uid }} - PGID={{ container_gid }} - TZ={{ container_timezone }} -{% if hms_docker_container_map['overseerr']['traefik'] %} +{% if traefik_enabled_overseerr %} labels: - traefik.enable=true - traefik.http.services.overseerr-{{ project_name }}.loadbalancer.server.port=5055 + - traefik.http.routers.overseerr-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['overseerr']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) - "traefik.http.routers.overseerr-{{ project_name }}.middlewares=external-ipwhitelist" +{% if authentik_enabled_overseerr %} + - traefik.http.routers.overseerr-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-overseerr-router@docker +{% endif %} {% endif %} volumes: - {{ hms_docker_apps_path }}/overseerr/config:/config -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% if expose_ports_enabled_overseerr %} ports: - 5055:5055 {% endif %} {% endif %} -{% if hms_docker_container_map['plex']['enabled'] %} +{% if container_enabled_plex %} # Plex container, media server plex: image: lscr.io/linuxserver/plex:latest @@ -487,17 +652,21 @@ services: - {{ path.local_mount_path }}/{{ hms_docker_library_folder_name }}:/data/media_additional/{{ path.name | map('regex_replace', regex, replace) | list | join }} {% endfor %} {% endif %} -{% if hms_docker_container_map['plex']['traefik'] %} +{% if traefik_enabled_plex %} labels: - traefik.enable=true - traefik.http.services.plex-{{ project_name }}.loadbalancer.server.port=32400 -{% if not hms_docker_container_map['plex']['expose_to_public'] %} + - traefik.http.routers.plex-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['plex']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_plex %} - "traefik.http.routers.plex-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_plex %} + - traefik.http.routers.plex-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-plex-router@docker +{% endif %} {% endif %} {% endif %} -{% if hms_docker_container_map['tautulli']['enabled'] %} +{% if container_enabled_tautulli %} # Tautulli container, analytics tautulli: image: tautulli/tautulli:latest @@ -510,19 +679,25 @@ services: networks: - "media_net" - "traefik_net" + - "authentik_net" environment: - PUID={{ container_uid }} - PGID={{ container_gid }} - TZ={{ container_timezone }} -{% if hms_docker_container_map['tautulli']['traefik'] %} +{% if traefik_enabled_tautulli %} labels: - traefik.enable=true - traefik.http.services.tautulli-{{ project_name }}.loadbalancer.server.port=8181 -{% if not hms_docker_container_map['tautulli']['expose_to_public'] %} + - traefik.http.routers.tautulli-{{ project_name }}.rule=Host(`{{ hms_docker_container_map['tautulli']['proxy_host_rule'] }}.{{ hms_docker_domain }}`) +{% if not expose_public_enabled_tautulli %} - "traefik.http.routers.tautulli-{{ project_name }}.middlewares=internal-ipwhitelist" {% endif %} +{% if authentik_enabled_tautulli %} + - traefik.http.routers.tautulli-{{ project_name }}.middlewares=authentik-proxy-{{ project_name }}-tautulli-router@docker +{% endif %} {% endif %} -{% if container_expose_ports or not hms_docker_container_map['traefik']['enabled'] %} +{% endif %} +{% if expose_ports_enabled_tautulli %} ports: - 8181:8181 {% endif %} @@ -563,5 +738,18 @@ networks: driver: bridge attachable: false "traefik_net": + driver: bridge + attachable: true +{% if authentik_enabled_globally %} + "authentik_net": driver: bridge attachable: false +{% endif %} + +{% if authentik_enabled_globally %} +volumes: + authentik_database: + driver: local + authentik_geoip: + driver: local +{% endif %} diff --git a/vars/._computed.yml b/vars/._computed.yml new file mode 100644 index 0000000..079dab4 --- /dev/null +++ b/vars/._computed.yml @@ -0,0 +1,87 @@ + +####################################################################### +# Computed variables +# DO NOT MODIFY unless you want stuff to break +# These are used to generate the `docker-compose` template, this helps keep the amount of in-line logic in the template to a minimum and keeps it clean(ish) + +# Container computed variables +container_enabled_portainer: "{{ True if (hms_docker_container_map['portainer']['enabled'] is defined and hms_docker_container_map['portainer']['enabled']) else False }}" +container_enabled_traefik: "{{ True if (hms_docker_container_map['traefik']['enabled'] is defined and hms_docker_container_map['traefik']['enabled']) else False }}" +container_enabled_authentik: "{{ True if (hms_docker_container_map['authentik']['enabled'] is defined and hms_docker_container_map['authentik']['enabled']) else False }}" +container_enabled_sonarr: "{{ True if (hms_docker_container_map['sonarr']['enabled'] is defined and hms_docker_container_map['sonarr']['enabled']) else False }}" +container_enabled_radarr: "{{ True if (hms_docker_container_map['radarr']['enabled'] is defined and hms_docker_container_map['radarr']['enabled']) else False }}" +container_enabled_bazarr: "{{ True if (hms_docker_container_map['bazarr']['enabled'] is defined and hms_docker_container_map['bazarr']['enabled']) else False }}" +container_enabled_transmission: "{{ True if ((hms_docker_container_map['transmission']['enabled'] is defined and hms_docker_container_map['transmission']['enabled']) and (transmission_vpn_provider is defined and transmission_vpn_user is defined and transmission_vpn_pass is defined)) else False }}" +container_enabled_overseerr: "{{ True if (hms_docker_container_map['overseerr']['enabled'] is defined and hms_docker_container_map['overseerr']['enabled']) else False }}" +container_enabled_prowlarr: "{{ True if (hms_docker_container_map['prowlarr']['enabled'] is defined and hms_docker_container_map['prowlarr']['enabled']) else False }}" +container_enabled_requestrr: "{{ True if (hms_docker_container_map['requestrr']['enabled'] is defined and hms_docker_container_map['requestrr']['enabled']) else False }}" +container_enabled_plex: "{{ True if (hms_docker_container_map['plex']['enabled'] is defined and hms_docker_container_map['plex']['enabled']) else False }}" +container_enabled_tautulli: "{{ True if (hms_docker_container_map['tautulli']['enabled'] is defined and hms_docker_container_map['tautulli']['enabled']) else False }}" +container_enabled_nzbget: "{{ True if (hms_docker_container_map['nzbget']['enabled'] is defined and hms_docker_container_map['nzbget']['enabled']) else False }}" + + +# Traefik computed variables +traefik_enabled_traefik: "{{ True if (hms_docker_container_map['traefik']['traefik'] is defined and hms_docker_container_map['traefik']['traefik']) else False }}" +traefik_enabled_portainer: "{{ True if (hms_docker_container_map['portainer']['traefik'] is defined and hms_docker_container_map['portainer']['traefik']) else False }}" +traefik_enabled_authentik: "{{ True if (hms_docker_container_map['authentik']['traefik'] is defined and hms_docker_container_map['authentik']['traefik']) else False }}" +traefik_enabled_sonarr: "{{ True if (hms_docker_container_map['sonarr']['traefik'] is defined and hms_docker_container_map['sonarr']['traefik']) else False }}" +traefik_enabled_radarr: "{{ True if (hms_docker_container_map['radarr']['traefik'] is defined and hms_docker_container_map['radarr']['traefik']) else False }}" +traefik_enabled_bazarr: "{{ True if (hms_docker_container_map['bazarr']['traefik'] is defined and hms_docker_container_map['bazarr']['traefik']) else False }}" +traefik_enabled_transmission: "{{ True if (hms_docker_container_map['transmission']['traefik'] is defined and hms_docker_container_map['transmission']['traefik']) else False }}" +traefik_enabled_overseerr: "{{ True if (hms_docker_container_map['overseerr']['traefik'] is defined and hms_docker_container_map['overseerr']['traefik']) else False }}" +traefik_enabled_prowlarr: "{{ True if (hms_docker_container_map['prowlarr']['traefik'] is defined and hms_docker_container_map['prowlarr']['traefik']) else False }}" +traefik_enabled_requestrr: "{{ True if (hms_docker_container_map['requestrr']['traefik'] is defined and hms_docker_container_map['requestrr']['traefik']) else False }}" +traefik_enabled_plex: "{{ True if (hms_docker_container_map['plex']['traefik'] is defined and hms_docker_container_map['plex']['traefik']) else False }}" +traefik_enabled_tautulli: "{{ True if (hms_docker_container_map['tautulli']['traefik'] is defined and hms_docker_container_map['tautulli']['traefik']) else False }}" +traefik_enabled_nzbget: "{{ True if (hms_docker_container_map['nzbget']['traefik'] is defined and hms_docker_container_map['nzbget']['traefik']) else False }}" + + +# Public container exposure computed variables +expose_public_enabled_portainer: "{{ True if (hms_docker_container_map['portainer']['expose_to_public'] is defined and hms_docker_container_map['portainer']['expose_to_public']) else False }}" +expose_public_enabled_authentik: "{{ True if (hms_docker_container_map['authentik']['expose_to_public'] is defined and hms_docker_container_map['authentik']['expose_to_public']) else False }}" +expose_public_enabled_sonarr: "{{ True if (hms_docker_container_map['sonarr']['expose_to_public'] is defined and hms_docker_container_map['sonarr']['expose_to_public']) else False }}" +expose_public_enabled_radarr: "{{ True if (hms_docker_container_map['radarr']['expose_to_public'] is defined and hms_docker_container_map['radarr']['expose_to_public']) else False }}" +expose_public_enabled_bazarr: "{{ True if (hms_docker_container_map['bazarr']['expose_to_public'] is defined and hms_docker_container_map['bazarr']['expose_to_public']) else False }}" +expose_public_enabled_transmission: "{{ True if (hms_docker_container_map['transmission']['expose_to_public'] is defined and hms_docker_container_map['transmission']['expose_to_public']) else False }}" +expose_public_enabled_overseerr: "{{ True if (hms_docker_container_map['overseerr']['expose_to_public'] is defined and hms_docker_container_map['overseerr']['expose_to_public']) else False }}" +expose_public_enabled_prowlarr: "{{ True if (hms_docker_container_map['prowlarr']['expose_to_public'] is defined and hms_docker_container_map['prowlarr']['expose_to_public']) else False }}" +expose_public_enabled_requestrr: "{{ True if (hms_docker_container_map['requestrr']['expose_to_public'] is defined and hms_docker_container_map['requestrr']['expose_to_public']) else False }}" +expose_public_enabled_plex: "{{ True if (hms_docker_container_map['plex']['expose_to_public'] is defined and hms_docker_container_map['plex']['expose_to_public']) else False }}" +expose_public_enabled_tautulli: "{{ True if (hms_docker_container_map['tautulli']['expose_to_public'] is defined and hms_docker_container_map['tautulli']['expose_to_public']) else False }}" +expose_public_enabled_nzbget: "{{ True if (hms_docker_container_map['nzbget']['expose_to_public'] is defined and hms_docker_container_map['nzbget']['expose_to_public']) else False }}" + + +# Container host port mapping computed variables +expose_ports_enabled_portainer: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_portainer) else False }}" +expose_ports_enabled_authentik: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_authentik) else False }}" +expose_ports_enabled_sonarr: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_sonarr) else False }}" +expose_ports_enabled_radarr: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_radarr) else False }}" +expose_ports_enabled_bazarr: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_bazarr) else False }}" +expose_ports_enabled_transmission: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_transmission) else False }}" +expose_ports_enabled_overseerr: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_overseerr) else False }}" +expose_ports_enabled_prowlarr: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_prowlarr) else False }}" +expose_ports_enabled_requestrr: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_requestrr) else False }}" +expose_ports_enabled_plex: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_plex) else False }}" +expose_ports_enabled_tautulli: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_tautulli) else False }}" +expose_ports_enabled_nzbget: "{{ True if (container_expose_ports or not container_enabled_traefik or not traefik_enabled_nzbget) else False }}" + + +# Authentik computed variables +authentik_enabled_globally: "{{ True if (container_enabled_authentik or authentik_enabled) else False }}" + +authentik_enabled_traefik: "{{ True if ((hms_docker_container_map['traefik']['authentik'] is defined and hms_docker_container_map['traefik']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_sonarr: "{{ True if ((hms_docker_container_map['sonarr']['authentik'] is defined and hms_docker_container_map['sonarr']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_radarr: "{{ True if ((hms_docker_container_map['radarr']['authentik'] is defined and hms_docker_container_map['radarr']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_bazarr: "{{ True if ((hms_docker_container_map['bazarr']['authentik'] is defined and hms_docker_container_map['bazarr']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_transmission: "{{ True if ((hms_docker_container_map['transmission']['authentik'] is defined and hms_docker_container_map['transmission']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_portainer: "{{ True if ((hms_docker_container_map['portainer']['authentik'] is defined and hms_docker_container_map['portainer']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_overseerr: "{{ True if ((hms_docker_container_map['overseerr']['authentik'] is defined and hms_docker_container_map['overseerr']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_prowlarr: "{{ True if ((hms_docker_container_map['prowlarr']['authentik'] is defined and hms_docker_container_map['prowlarr']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_requestrr: "{{ True if ((hms_docker_container_map['requestrr']['authentik'] is defined and hms_docker_container_map['requestrr']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_plex: "{{ True if ((hms_docker_container_map['plex']['authentik'] is defined and hms_docker_container_map['plex']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_tautulli: "{{ True if ((hms_docker_container_map['tautulli']['authentik'] is defined and hms_docker_container_map['tautulli']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_nzbget: "{{ True if ((hms_docker_container_map['nzbget']['authentik'] is defined and hms_docker_container_map['nzbget']['authentik']) and authentik_enabled_globally) else False }}" +authentik_enabled_authentik: "{{ True if ((hms_docker_container_map['authentik']['authentik'] is defined and hms_docker_container_map['authentik']['authentik']) and authentik_enabled_globally) else False }}" + + +#######################################################################