diff --git a/group_vars/all/08_ports.yml b/group_vars/all/08_ports.yml index c5fc88be..f205481a 100644 --- a/group_vars/all/08_ports.yml +++ b/group_vars/all/08_ports.yml @@ -13,6 +13,7 @@ ports: pgadmin: 4185 phpldapadmin: 4186 fusiondirectory: 4187 + gitea: 4188 ldap: ldap: 389 http: diff --git a/group_vars/all/12_iam.yml b/group_vars/all/12_iam.yml index 00288b8f..cc2f0be3 100644 --- a/group_vars/all/12_iam.yml +++ b/group_vars/all/12_iam.yml @@ -57,13 +57,16 @@ ldap: application_roles: "ou=application_roles,{{_ldap_dn_base}}" attributes: # Attribut to identify the user - user_id: "{{ _ldap_user_id }}" + user_id: "{{ _ldap_user_id }}" + mail: "mail" + name: "cn" # Password to access dn.bind bind_credential: "{{applications.ldap.credentials.administrator_database_password}}" server: domain: "{{applications.ldap.hostname if applications.ldap.network.docker | bool else domains.ldap}}" # Mapping for public or locale access port: "{{_ldap_server_port}}" uri: "{% if applications.ldap.network.docker | bool %}ldap://{{ applications.ldap.hostname }}{% else %}ldaps://{{ domains.ldap }}{% endif %}:{{ _ldap_server_port }}" + security: "" #TLS, SSL - Leave empty for none network: local: "{{applications.ldap.network.docker}}" # Uses the application configuration to define if local network should be available or not user_objects: diff --git a/roles/docker-bigbluebutton/templates/env.j2 b/roles/docker-bigbluebutton/templates/env.j2 index 91f0345b..94101f86 100644 --- a/roles/docker-bigbluebutton/templates/env.j2 +++ b/roles/docker-bigbluebutton/templates/env.j2 @@ -158,7 +158,7 @@ OFFICE365_HD= # It is useful for cases when Greenlight is deployed behind a Network Load Balancer or proxy OAUTH2_REDIRECT= -{% if applications[application_id].features.ldap | bool %} +{% if applications | is_feature_enabled('ldap',application_id) %} # LDAP Login Provider (optional) # # You can enable LDAP authentication by providing values for the variables below. diff --git a/roles/docker-espocrm/templates/env.j2 b/roles/docker-espocrm/templates/env.j2 index a43aa74d..03b172f4 100644 --- a/roles/docker-espocrm/templates/env.j2 +++ b/roles/docker-espocrm/templates/env.j2 @@ -61,12 +61,12 @@ ESPOCRM_CONFIG_OUTBOUND_EMAIL_FROM_ADDRESS={{ users['no-reply'].email }} # LDAP settings (optional) # Applied only if the feature flag is true # ------------------------------------------------ -{% if applications[application_id].features.ldap | bool %} +{% if applications | is_feature_enabled('ldap',application_id) %} ESPOCRM_CONFIG_AUTHENTICATION_METHOD=Ldap ESPOCRM_CONFIG_LDAP_HOST={{ ldap.server.domain }} ESPOCRM_CONFIG_LDAP_PORT={{ ldap.server.port }} # ESPOCRM_CONFIG_LDAP_SECURITY: "", SSL or TLS -ESPOCRM_CONFIG_LDAP_SECURITY= +ESPOCRM_CONFIG_LDAP_SECURITY={{ ldap.server.security }} ESPOCRM_CONFIG_LDAP_USERNAME={{ ldap.dn.administrator }} ESPOCRM_CONFIG_LDAP_PASSWORD={{ ldap.bind_credential }} ESPOCRM_CONFIG_LDAP_BASE_DN={{ ldap.dn.users }} diff --git a/roles/docker-funkwhale/templates/env.j2 b/roles/docker-funkwhale/templates/env.j2 index 331b2a32..6a42d856 100644 --- a/roles/docker-funkwhale/templates/env.j2 +++ b/roles/docker-funkwhale/templates/env.j2 @@ -100,7 +100,7 @@ DJANGO_SETTINGS_MODULE=config.settings.production # Generate one using `openssl rand -base64 45`, for example DJANGO_SECRET_KEY={{applications[application_id].credentials.django_secret}} -{% if applications[application_id].features.ldap | bool %} +{% if applications | is_feature_enabled('ldap',application_id) %} # LDAP settings # Use the following options to allow authentication on your Funkwhale instance # using a LDAP directory. diff --git a/roles/docker-gitea/meta/schema.yml b/roles/docker-gitea/meta/schema.yml index 8b137891..2d9a34dc 100644 --- a/roles/docker-gitea/meta/schema.yml +++ b/roles/docker-gitea/meta/schema.yml @@ -1 +1,5 @@ - +credentials: + oauth2_proxy_cookie_secret: + description: "Secret used to encrypt cookies for the OAuth2 proxy (hex-encoded, 16 bytes)" + algorithm: "sha256" + validation: "^[a-f0-9]{32}$" \ No newline at end of file diff --git a/roles/docker-gitea/tasks/main.yml b/roles/docker-gitea/tasks/main.yml index 0443f5d8..5caa2e0a 100644 --- a/roles/docker-gitea/tasks/main.yml +++ b/roles/docker-gitea/tasks/main.yml @@ -11,3 +11,44 @@ http_port: "{{ ports.localhost.http[application_id] }}" - include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml" + +- name: Wait for Gitea HTTP endpoint + wait_for: + host: "127.0.0.1" + port: "{{ ports.localhost.http[application_id] }}" + delay: 5 + timeout: 300 + +- name: "Run DB migrations inside Gitea container" + shell: | + docker-compose -f "{{ docker_compose.directories.instance }}/docker-compose.yml" \ + exec -T --user git application \ + /app/gitea/gitea migrate + args: + chdir: "{{ docker_compose.directories.instance }}" + register: migrate + changed_when: "'migrations completed' in migrate.stdout" + +- name: "Create initial admin user" + shell: | + docker-compose -f "{{ docker_compose.directories.instance }}/docker-compose.yml" \ + exec -T --user git application \ + /app/gitea/gitea admin user create \ + --admin \ + --username "{{ users.administrator.username }}" \ + --password "{{ users.administrator.password }}" \ + --email "{{ users.administrator.email }}" \ + -c /data/gitea/conf/app.ini + args: + chdir: "{{ docker_compose.directories.instance }}" + register: create_admin + changed_when: "'has been successfully created' in create_admin.stdout" + failed_when: create_admin.rc != 0 and 'user already exists' not in create_admin.stderr + +- name: Execute OIDC Routine + include_tasks: oidc.yml + vars: + action: add + register: oidc_add + ignore_errors: true + when: applications | is_feature_enabled('oidc', application_id) \ No newline at end of file diff --git a/roles/docker-gitea/tasks/oidc.yml b/roles/docker-gitea/tasks/oidc.yml new file mode 100644 index 00000000..cce5107b --- /dev/null +++ b/roles/docker-gitea/tasks/oidc.yml @@ -0,0 +1,63 @@ +- name: "Wait until Gitea setup and migrations are ready" + uri: + url: "http://127.0.0.1:{{ ports.localhost.http[application_id] }}/api/v1/version" + method: GET + status_code: 200 + return_content: no + register: gitea_ready + until: gitea_ready.status == 200 + retries: 20 + delay: 5 + +- name: "Add Keycloak OIDC Provider" + shell: | + docker-compose -f "{{ docker_compose.directories.instance }}/docker-compose.yml" \ + exec -T --user git application \ + gitea admin auth add-oauth \ + --provider openidConnect \ + --name "{{ oidc.button_text }}" \ + --key "{{ oidc.client.id }}" \ + --secret "{{ oidc.client.secret }}" \ + --auto-discover-url "{{ oidc.client.discovery_document }}" \ + --scopes "openid profile email" + args: + chdir: "{{ docker_compose.directories.instance }}" + register: oidc_manage + failed_when: oidc_manage.rc != 0 and "login source already exists" not in oidc_manage.stderr + +- name: "Lookup existing Keycloak auth source ID" + shell: | + docker-compose -f "{{ docker_compose.directories.instance }}/docker-compose.yml" \ + exec -T --user git application \ + /app/gitea/gitea admin auth list \ + | tail -n +2 \ + | grep -F "{{ oidc.button_text }}" \ + | awk '{print $1; exit}' + args: + chdir: "{{ docker_compose.directories.instance }}" + register: oidc_source_id_raw + failed_when: + - oidc_source_id_raw.rc != 0 + - oidc_source_id_raw.stdout == "" + changed_when: false + +- name: "Set Keycloak source ID fact" + set_fact: + oidc_source_id: "{{ oidc_source_id_raw.stdout }}" + +- name: "Update Keycloak OIDC Provider" + shell: | + docker-compose -f "{{ docker_compose.directories.instance }}/docker-compose.yml" \ + exec -T --user git application \ + gitea admin auth update-oauth \ + --id {{ oidc_source_id }}\ + --provider openidConnect \ + --name "{{ oidc.button_text }}" \ + --key "{{ oidc.client.id }}" \ + --secret "{{ oidc.client.secret }}" \ + --auto-discover-url "{{ oidc.client.discovery_document }}" \ + --scopes "openid profile email" + args: + chdir: "{{ docker_compose.directories.instance }}" + register: oidc_manage + failed_when: oidc_manage.rc != 0 \ No newline at end of file diff --git a/roles/docker-gitea/templates/env.j2 b/roles/docker-gitea/templates/env.j2 index 412d3026..a39c62ab 100644 --- a/roles/docker-gitea/templates/env.j2 +++ b/roles/docker-gitea/templates/env.j2 @@ -14,6 +14,7 @@ DOMAIN={{domains | get_domain(application_id)}} SSH_DOMAIN={{domains | get_domain(application_id)}} RUN_MODE="{{ 'dev' if (CYMAIS_ENVIRONMENT | lower) == 'development' else 'prod' }}" ROOT_URL="{{ web_protocol }}://{{domains | get_domain(application_id)}}/" +APP_NAME="{{ applications[application_id].title }}" # Mail Configuration # @see https://docs.gitea.com/next/installation/install-with-docker#managing-deployments-with-environment-variables @@ -30,4 +31,47 @@ GITEA__mailer__PASSWD={{ users['no-reply'].mailu_token }} # @see https://github.com/go-gitea/gitea/issues/17619 GITEA__REPOSITORY__ENABLE_PUSH_CREATE_USER={{ applications[application_id].configuration.repository.enable_push_create_user | lower }} GITEA__REPOSITORY__DEFAULT_PRIVATE={{ applications[application_id].configuration.repository.default_private | lower }} -GITEA__REPOSITORY__DEFAULT_PUSH_CREATE_PRIVATE={{ applications[application_id].configuration.repository.default_push_create_private | lower }} \ No newline at end of file +GITEA__REPOSITORY__DEFAULT_PUSH_CREATE_PRIVATE={{ applications[application_id].configuration.repository.default_push_create_private | lower }} + +GITEA__security__INSTALL_LOCK=true # Locks the installation page + +{% if applications | is_feature_enabled('oidc',application_id) %} + +GITEA__openid__ENABLE_OPENID_SIGNUP=true +GITEA__openid__ENABLE_OPENID_SIGNUP=true + +{% endif %} + +{% if applications | is_feature_enabled('ldap',application_id) %} + +# ------------------------------------------------ +# LDAP Authentication (via BindDN) +# ------------------------------------------------ +GITEA__auth__LDAP__ENABLED={{ applications | is_feature_enabled('ldap',application_id) | string | lower }} +GITEA__auth__LDAP__HOST={{ ldap.server.domain }} +GITEA__auth__LDAP__PORT={{ ldap.server.port }} +# security protocol: "", "SSL" or "TLS" +GITEA__auth__LDAP__SECURITY={{ ldap.server.security | trim or "unencrypted" }} +GITEA__auth__LDAP__BIND_DN={{ ldap.dn.administrator }} +GITEA__auth__LDAP__BIND_PASSWORD={{ ldap.bind_credential }} +GITEA__auth__LDAP__USER_SEARCH_BASE={{ ldap.dn.users }} +GITEA__auth__LDAP__USER_FILTER={{ ldap.filters.user_filter }} +# map LDAP attributes to Gitea fields +GITEA__auth__LDAP__ATTRIBUTE_USERNAME={{ ldap.attributes.user_id }} +GITEA__auth__LDAP__ATTRIBUTE_FULL_NAME={{ ldap.attributes.name }} +GITEA__auth__LDAP__ATTRIBUTE_MAIL={{ ldap.attributes.mail }} + +# ------------------------------------------------ +# Periodic sync for external LDAP users +# ------------------------------------------------ +GITEA__cron__SYNC_EXTERNAL_USERS_ENABLED=true +# default: sync daily at midnight +GITEA__cron__SYNC_EXTERNAL_USERS_CRON=0 0 * * * +{% endif %} + +# ------------------------------------------------ +# Disable user self-registration +# ------------------------------------------------ +# After this only admins can create accounts +GITEA__service__DISABLE_REGISTRATION=false + diff --git a/roles/docker-gitea/vars/configuration.yml b/roles/docker-gitea/vars/configuration.yml index df1185e1..cc88d325 100644 --- a/roles/docker-gitea/vars/configuration.yml +++ b/roles/docker-gitea/vars/configuration.yml @@ -1,3 +1,4 @@ +title: "CyMaIS Code Hub" images: gitea: "gitea/gitea:latest" configuration: @@ -7,9 +8,18 @@ configuration: default_push_create_private: True # Default private when creating a new repository with push-to-create. features: matomo: true - css: true + css: false portfolio_iframe: true central_database: true + ldap: false # Deactivated because OIDC is implemented + oauth2: false # Deactivated. Use OIDC instead. + oidc: true +oauth2_proxy: + application: "application" + port: "80" + acl: + blacklist: + - "/user/login" csp: flags: script-src-elem: diff --git a/roles/docker-keycloak/templates/import/realm.json.j2 b/roles/docker-keycloak/templates/import/realm.json.j2 index 24929abd..a6eb089d 100644 --- a/roles/docker-keycloak/templates/import/realm.json.j2 +++ b/roles/docker-keycloak/templates/import/realm.json.j2 @@ -1944,7 +1944,7 @@ "true" ], "ldap.full.name.attribute": [ - "cn" + "{{ ldap.attributes.name }}" ] } }, diff --git a/roles/docker-mailu/templates/env.j2 b/roles/docker-mailu/templates/env.j2 index 9a7d71b6..28fef70d 100644 --- a/roles/docker-mailu/templates/env.j2 +++ b/roles/docker-mailu/templates/env.j2 @@ -165,7 +165,7 @@ AUTH_REQUIRE_TOKENS=True # @see https://github.com/heviat/Mailu-OIDC/tree/master # Enable OpenID Connect. Possible values: True, False -OIDC_ENABLED={{ applications[application_id].features.oidc | string | capitalize }} +OIDC_ENABLED={{ applications | is_feature_enabled('oidc',application_id) | string | capitalize }} # OpenID Connect provider configuration URL OIDC_PROVIDER_INFO_URL={{oidc.client.issuer_url}} diff --git a/roles/docker-mastodon/templates/env.j2 b/roles/docker-mastodon/templates/env.j2 index 093773db..3b9f2929 100644 --- a/roles/docker-mastodon/templates/env.j2 +++ b/roles/docker-mastodon/templates/env.j2 @@ -59,7 +59,7 @@ SMTP_FROM_ADDRESS=Mastodon <{{ users['no-reply'].email }}> # @see https://github.com/mastodon/mastodon/pull/16221 # @see https://stackoverflow.com/questions/72081776/how-mastodon-configured-login-using-sso -OIDC_ENABLED={{ applications[application_id].features.oidc | string | lower }} +OIDC_ENABLED={{ applications | is_feature_enabled('oidc',application_id) | string | lower }} OIDC_DISPLAY_NAME="{{oidc.button_text}}" OIDC_ISSUER={{oidc.client.issuer_url}} OIDC_DISCOVERY=true diff --git a/roles/docker-nextcloud/templates/config/oidc.config.php.j2 b/roles/docker-nextcloud/templates/config/oidc.config.php.j2 index bbe6b90b..b00cd569 100644 --- a/roles/docker-nextcloud/templates/config/oidc.config.php.j2 +++ b/roles/docker-nextcloud/templates/config/oidc.config.php.j2 @@ -146,7 +146,7 @@ return array ( // // The `id` attribute in `oidc_login_attributes` must return the // "Internal Username" (see expert settings in LDAP integration) - 'oidc_login_proxy_ldap' => {{applications[application_id].features.ldap | string | lower}}, + 'oidc_login_proxy_ldap' => {{ applications | is_feature_enabled('ldap',application_id) | string | lower }}, // Disable creation of users new to Nextcloud from OIDC login. // A user may be known to the IdP but not (yet) known to Nextcloud. diff --git a/roles/docker-openproject/vars/configuration.yml b/roles/docker-openproject/vars/configuration.yml index de6afc84..ad8a53cb 100644 --- a/roles/docker-openproject/vars/configuration.yml +++ b/roles/docker-openproject/vars/configuration.yml @@ -4,9 +4,9 @@ oauth2_proxy: port: "80" acl: whitelist: - - "/users/me" # Necessary for Nextcloud Plugin to work - - "/api/" # Necessary for Nextcloud Plugin to work - - "/oauth/token" # Necessary for Nextcloud Plugin to work + - "/users/me" # Necessary for Nextcloud Plugin to work + - "/api/" # Necessary for Nextcloud Plugin to work + - "/oauth/token" # Necessary for Nextcloud Plugin to work ldap: filters: administrators: True # Set true to filter administrators diff --git a/roles/docker-pixelfed/templates/env.j2 b/roles/docker-pixelfed/templates/env.j2 index 0279ff4e..354c82f8 100644 --- a/roles/docker-pixelfed/templates/env.j2 +++ b/roles/docker-pixelfed/templates/env.j2 @@ -140,7 +140,7 @@ ENABLE_CONFIG_CACHE=true ################################### # @see https://github.com/pixelfed/pixelfed/commit/b3c27815788e4b47e7eb3fca727d817512cf26c2#diff-66e408190a301e81b5f1c079463487c54a6452c4944dc5ae80770f50101283ff -PF_OIDC_ENABLED={{ applications[application_id].features.oidc | string | lower }} +PF_OIDC_ENABLED={{ applications | is_feature_enabled('oidc',application_id) | string | lower }} PF_OIDC_AUTHORIZE_URL="{{oidc.client.authorize_url}}" PF_OIDC_TOKEN_URL="{{oidc.client.token_url}}" PF_OIDC_PROFILE_URL="{{ oidc.client.user_info_url }}" diff --git a/roles/docker-taiga/templates/docker-compose.yml.j2 b/roles/docker-taiga/templates/docker-compose.yml.j2 index 37040149..bca30e84 100644 --- a/roles/docker-taiga/templates/docker-compose.yml.j2 +++ b/roles/docker-taiga/templates/docker-compose.yml.j2 @@ -9,7 +9,7 @@ services: - media-data:/taiga-back/media # - ./config.py:/taiga-back/settings/config.py -{% if applications[application_id].features.oidc and applications[application_id].oidc.flavor == 'taigaio' %} +{% if applications | is_feature_enabled('oidc',application_id) and applications[application_id].oidc.flavor == 'taigaio' %} - {{ docker_compose.directories.config }}taiga-local.py:/taiga-back/settings/local.py:ro @@ -22,7 +22,7 @@ services: condition: service_started taiga-async-rabbitmq: condition: service_started -{% if applications[application_id].features.oidc and applications[application_id].oidc.flavor == 'taigaio' %} +{% if applications | is_feature_enabled('oidc',application_id) and applications[application_id].oidc.flavor == 'taigaio' %} command: > /bin/sh -c " @@ -42,7 +42,7 @@ services: - media-data:/taiga-back/media # - ./config.py:/taiga-back/settings/config.py -{% if applications[application_id].features.oidc and applications[application_id].oidc.flavor == 'taigaio' %} +{% if applications | is_feature_enabled('oidc',application_id) and applications[application_id].oidc.flavor == 'taigaio' %} {% for item in settings_files %} - {{ docker_compose.directories.config }}taiga-{{ item }}.py:/taiga-back/settings/{{ item }}.py:ro @@ -57,7 +57,7 @@ services: condition: service_started taiga-async-rabbitmq: condition: service_started -{% if applications[application_id].features.oidc and applications[application_id].oidc.flavor == 'taigaio' %} +{% if applications | is_feature_enabled('oidc',application_id) and applications[application_id].oidc.flavor == 'taigaio' %} command: > /bin/sh -c " diff --git a/roles/docker-taiga/templates/env.j2 b/roles/docker-taiga/templates/env.j2 index eb739b8e..4f7eafa3 100644 --- a/roles/docker-taiga/templates/env.j2 +++ b/roles/docker-taiga/templates/env.j2 @@ -47,7 +47,7 @@ MAX_AGE = 360 # Taiga's Telemetry - Variable to enable or disable the anonymous telemetry ENABLE_TELEMETRY = True -{% if applications[application_id].features.oidc %} +{% if applications | is_feature_enabled('oidc',application_id) %} {% if applications[application_id].oidc.flavor == 'taigaio' %}