diff --git a/group_vars/all/00_general.yml b/group_vars/all/00_general.yml index 3df78cf4..799088a6 100644 --- a/group_vars/all/00_general.yml +++ b/group_vars/all/00_general.yml @@ -51,7 +51,7 @@ certbot_webroot_path: "/var/lib/letsencrypt/" # Path used by certbot_cert_path: "/etc/letsencrypt/live" # Path containing active certificate symlinks for domains ## Docker Role Specific Parameters -docker_restart_policy: "unless-stopped" +docker_restart_policy: "unless-stopped" # helper -_applications_nextcloud_oidc_flavor: "{{ applications.nextcloud.oidc.flavor | default('oidc_login' if applications.nextcloud.features.ldap | default(true) else 'sociallogin') }}" \ No newline at end of file +_applications_nextcloud_oidc_flavor: "{{ applications.nextcloud.oidc.flavor | default('oidc_login' if applications.nextcloud.features.ldap | default(true) else 'sociallogin') }}" \ No newline at end of file diff --git a/roles/docker-moodle/Administration.md b/roles/docker-moodle/Administration.md index 9f7748c9..cd71c384 100644 --- a/roles/docker-moodle/Administration.md +++ b/roles/docker-moodle/Administration.md @@ -1,6 +1,36 @@ # Administration -# Radical Erase of Setup +## Moodle Docker Directory Path + +Moodle lives in: ``cd /opt/docker/moodle`` + +## Upgrade + +```bash +docker exec --user daemon moodle php /opt/bitnami/moodle/admin/cli/upgrade.php --non-interactive +``` + +## Delete Cache + +To clean the cache execute: + +```bash +docker exec --user daemon moodle php /opt/bitnami/moodle/admin/cli/purge_caches.php +docker exec --user root moodle rm -rf \ + /bitnami/moodledata/cache/* \ + /bitnami/moodledata/localcache/* \ + /bitnami/moodledata/temp/* \ + /bitnami/moodledata/sessions/* +docker restart moodle +``` + +## CLI + +A detailled Guid how to use the CLI in moodle you will find [here](https://docs.moodle.org/500/de/Administration_%C3%BCber_Kommandozeile). + +## General Administration Tasks + +### Radical Erase of Setup To manually erase the full moodle setup inkluding all data execute: **CLI:** @@ -24,5 +54,5 @@ DROP DATABASE IF EXISTS moodle; to delete all data in the database related to this role. -# Virgin Setup +### Virgin Setup After the installation you can rerun this role to create a fresh setup of Moodle. \ No newline at end of file diff --git a/roles/docker-moodle/meta/main.yml b/roles/docker-moodle/meta/main.yml index 6804e62e..c7620073 100644 --- a/roles/docker-moodle/meta/main.yml +++ b/roles/docker-moodle/meta/main.yml @@ -19,4 +19,5 @@ galaxy_info: documentation: "https://s.veen.world/cymais" logo: class: "fa-solid fa-graduation-cap" -dependencies: [] \ No newline at end of file + run_after: + - "docker-keycloak" \ No newline at end of file diff --git a/roles/docker-moodle/tasks/main.yml b/roles/docker-moodle/tasks/main.yml index 044899c9..c9bfe24e 100644 --- a/roles/docker-moodle/tasks/main.yml +++ b/roles/docker-moodle/tasks/main.yml @@ -12,6 +12,26 @@ - include_tasks: "{{ playbook_dir }}/roles/docker-compose/tasks/create-files.yml" +- name: Wait until the Moodle container is healthy + shell: docker inspect --format '{% raw %}{{.State.Health.Status}}{% endraw %}' {{ container_name }} + register: health_check + until: health_check.stdout.strip() == "healthy" + retries: 120 + delay: 5 + +- name: "Include ownership settings tasks for moodle" + include_tasks: ownership.yml + - name: "Configure OIDC login for Moodle if enabled" include_tasks: oidc.yml when: applications | is_feature_enabled('oidc',application_id) + +- name: Run Moodle system check + command: > + docker exec --user {{ bitnami_user }} {{ container_name }} + php /opt/bitnami/moodle/admin/cli/checks.php + register: moodle_checks + changed_when: false + failed_when: > + moodle_checks.rc != 0 or + "OK: All" not in moodle_checks.stdout diff --git a/roles/docker-moodle/tasks/oidc.yml b/roles/docker-moodle/tasks/oidc.yml index 872da595..b8555463 100644 --- a/roles/docker-moodle/tasks/oidc.yml +++ b/roles/docker-moodle/tasks/oidc.yml @@ -1,24 +1,57 @@ --- -- name: "Upgrade Moodle to apply OIDC plugin" - command: "docker exec {{ container_name }} php admin/cli/upgrade.php --non-interactive" + +- name: Check if OIDC plugin is present in container + command: > + docker exec --user root {{ container_name }} test -d {{ bitnami_oidc_plugin_dir }} + register: oidc_plugin_check + ignore_errors: true + changed_when: false + +- name: Fail if plugin not present to avoid broken auth + fail: + msg: "OIDC plugin not present – skipping configuration" + when: oidc_plugin_check.rc != 0 + +#- name: "Upgrade Moodle to apply OIDC plugin" +# command: "docker exec --user {{ bitnami_user }} {{ container_name }} php /opt/bitnami/moodle/admin/cli/upgrade.php --non-interactive" +# +#- name: Clear Moodle cache +# command: > +# docker exec --user {{ bitnami_user }} {{ container_name }} php /opt/bitnami/moodle/admin/cli/purge_caches.php - name: "Set Moodle OIDC configuration via CLI" loop: - - { name: "issuerurl", value: "{{ oidc.client.issuer_url }}" } - - { name: "clientid", value: "{{ oidc.client.id }}" } - - { name: "clientsecret", value: "{{ oidc.client.secret }}" } - - { name: "authmethod", value: "oidc" } - - { name: "loginflow", value: "authorization_code" } - - { name: "idpname", value: "Keycloak" } - - { name: "scopes", value: "openid profile email" } - - { name: "authenticationendpoint", value: "{{ oidc.client.authorize_url }}" } - - { name: "tokenendpoint", value: "{{ oidc.client.token_url }}" } - - { name: "userinfoendpoint", value: "{{ oidc.client.user_info_url }}" } + - { name: "idptype", value: 3 } + - { name: "clientauthmethod", value: 1 } + - { name: "clientid", value: "{{ oidc.client.id }}" } + - { name: "clientsecret", value: "{{ oidc.client.secret }}" } + - { name: "opname", value: "{{oidc.button_text}}" } + - { name: "oidcscope", value: "openid profile email" } + - { name: "authendpoint", value: "{{ oidc.client.authorize_url }}" } + - { name: "tokenendpoint", value: "{{ oidc.client.token_url }}" } + - { name: "bindingusernameclaim", value: "{{ oidc.attributes.username }}" } + - { name: "single_sign_off", value: 1 } # Logs the user out from the IDP + - { name: "logouturi", value: "{{ oidc.client.logout_url }}" } + - { name: "icon", value: "moodle:t/lock" } + - { name: "field_map_firstname", value: "{{ oidc.attributes.given_name }}" } + - { name: "field_map_lastname", value: "{{ oidc.attributes.family_name }}" } + #- { name: "showloginform", value: 0 } # Deactivate if OIDC is active + - { name: "alternateloginurl", value: "{{ web_protocol }}://{{ domains | get_domain(application_id) }}/auth/oidc/" } loop_control: label: "{{ item.name }}" command: > - docker exec {{ container_name }} php admin/cli/cfg.php --component=auth_oidc + docker exec --user {{ bitnami_user }} {{ container_name }} php /opt/bitnami/moodle/admin/cli/cfg.php --component=auth_oidc --name={{ item.name }} --set="{{ item.value }}" - name: "Enable OIDC login" - command: "docker exec {{ container_name }} php admin/cli/cfg.php --name=auth --set=oidc" + command: "docker exec --user {{ bitnami_user }} {{ container_name }} php /opt/bitnami/moodle/admin/cli/cfg.php --name=auth --set=oidc" + +- name: Set auth = 'oidc' for all users except guest + shell: > + docker exec {{ database_instance }} mariadb -u {{ database_username }} -p{{ database_password }} + -e "UPDATE moodle.mdl_user SET auth = 'oidc' WHERE username != 'guest';" + args: + executable: /bin/bash + +#- name: Prevent Account Creation +# command: docker exec --user {{ bitnami_user }} {{ container_name }} php /opt/bitnami/moodle/admin/cli/cfg.php --name=authpreventaccountcreation --set=1 \ No newline at end of file diff --git a/roles/docker-moodle/tasks/ownership.yml b/roles/docker-moodle/tasks/ownership.yml new file mode 100644 index 00000000..a41d701d --- /dev/null +++ b/roles/docker-moodle/tasks/ownership.yml @@ -0,0 +1,19 @@ +# This file sets the correct ownership rights for Moodle volumes + +- name: Set ownership and permissions on Moodle directories + vars: + moodle_dirs: + - "{{ bitnami_code_dir }}" + - "{{ bitnami_data_dir }}" + block: + - name: Ensure ownership is correct + command: "docker exec --user root {{ container_name }} chown -R {{ bitnami_user_group }} {{ item }}" + loop: "{{ moodle_dirs }}" + + - name: Set directory permissions (770) + command: "docker exec --user root {{ container_name }} find {{ item }} -type d -exec chmod 770 {} \\;" + loop: "{{ moodle_dirs }}" + + - name: Set file permissions (660) + command: "docker exec --user root {{ container_name }} find {{ item }} -type f -exec chmod 660 {} \\;" + loop: "{{ moodle_dirs }}" diff --git a/roles/docker-moodle/templates/Dockerfile.j2 b/roles/docker-moodle/templates/Dockerfile.j2 index c6e81985..7c8adc38 100644 --- a/roles/docker-moodle/templates/Dockerfile.j2 +++ b/roles/docker-moodle/templates/Dockerfile.j2 @@ -1,14 +1,16 @@ FROM bitnami/moodle:{{ applications[application_id].version }} -{% if applications | is_feature_enabled('oidc',application_id) %} -# Install git (required to clone the OIDC plugin) -USER root -RUN install_packages git unzip - -# Clone the Microsoft OIDC plugin into Moodle's auth directory -RUN git clone https://github.com/microsoft/moodle-auth_oidc.git \ - /opt/bitnami/moodle/auth/oidc && \ - chown -R www-data:www-data /opt/bitnami/moodle/auth/oidc - -USER 1001 -{% endif %} \ No newline at end of file +{% if applications | is_feature_enabled('oidc', application_id) %} +RUN install_packages unzip curl jq \ + && VERSION=$(curl -s https://api.github.com/repos/microsoft/moodle-auth_oidc/tags \ + | jq -r '.[].name' \ + | grep v{{ applications[application_id].version }} \ + | sort -Vr \ + | head -n1) \ + && echo "Using version $VERSION" \ + && curl -L -o /tmp/oidc.zip https://github.com/microsoft/moodle-auth_oidc/archive/refs/tags/${VERSION}.zip \ + && unzip /tmp/oidc.zip -d /tmp \ + && mv /tmp/moodle-auth_oidc-* {{ bitnami_oidc_plugin_dir }} \ + && chown -R {{ bitnami_user_group }} {{ bitnami_oidc_plugin_dir }} \ + && rm -rf /tmp/oidc.zip +{% endif %} diff --git a/roles/docker-moodle/templates/docker-compose.yml.j2 b/roles/docker-moodle/templates/docker-compose.yml.j2 index 6c9f0596..3f5ee2f9 100644 --- a/roles/docker-moodle/templates/docker-compose.yml.j2 +++ b/roles/docker-moodle/templates/docker-compose.yml.j2 @@ -11,8 +11,8 @@ services: - 127.0.0.1:{{ports.localhost.http[application_id]}}:8080 {% include 'roles/docker-compose/templates/services/base.yml.j2' %} volumes: - - 'moodle:/bitnami/moodle' - - 'data:/bitnami/moodledata' + - 'code:{{ bitnami_code_link }}' + - 'data:{{ bitnami_data_dir }}' healthcheck: test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1'"] interval: 30s @@ -22,7 +22,7 @@ services: {% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/compose/volumes.yml.j2' %} - moodle: + code: data: {% include 'templates/docker/compose/networks.yml.j2' %} diff --git a/roles/docker-moodle/templates/env.j2 b/roles/docker-moodle/templates/env.j2 index 957d7632..bc9aafb5 100644 --- a/roles/docker-moodle/templates/env.j2 +++ b/roles/docker-moodle/templates/env.j2 @@ -1,10 +1,7 @@ -MOODLE_DATABASE_HOST={{database_host}} -MOODLE_DATABASE_PORT_NUMBER={{database_port}} -MOODLE_DATABASE_USER={{database_username}} -MOODLE_DATABASE_NAME={{database_name}} -MOODLE_DATABASE_PASSWORD={{database_password}} -ALLOW_EMPTY_PASSWORD=no + +# General MOODLE_SITE_NAME="{{applications[application_id].site_titel}}" +MOODLE_HOST="{{ domains | get_domain(application_id) }}" MOODLE_SSLPROXY=yes MOODLE_REVERSE_PROXY=yes MOODLE_USERNAME={{applications[application_id].users.administrator.username}} @@ -12,8 +9,15 @@ MOODLE_PASSWORD={{applications[application_id].credentials.user_password}} MOODLE_EMAIL={{applications[application_id].users.administrator.email}} BITNAMI_DEBUG={% if enable_debug | bool %}true{% else %}false{% endif %} +# Database +MOODLE_DATABASE_HOST={{database_host}} +MOODLE_DATABASE_PORT_NUMBER={{database_port}} +MOODLE_DATABASE_USER={{database_username}} +MOODLE_DATABASE_NAME={{database_name}} +MOODLE_DATABASE_PASSWORD={{database_password}} -MOODLE_HOST={{ system_email.host }} +# SMTP +MOODLE_SMTP_HOST={{ system_email.host }} MOODLE_SMTP_PORT_NUMBER={{ system_email.port }} MOODLE_SMTP_USER={{ users['no-reply'].email }} MOODLE_SMTP_PASSWORD={{ users['no-reply'].mailu_token }} diff --git a/roles/docker-moodle/vars/configuration.yml b/roles/docker-moodle/vars/configuration.yml index 9bbac8fe..0e8ebc5e 100644 --- a/roles/docker-moodle/vars/configuration.yml +++ b/roles/docker-moodle/vars/configuration.yml @@ -3,20 +3,23 @@ users: administrator: username: "{{users.administrator.username}}" email: "{{users.administrator.email}}" -version: "latest" +version: "4.5" # Latest LTS - Necessary for OIDC features: matomo: true css: false portfolio_iframe: false central_database: true - oidc: false + oidc: true csp: flags: script-src-elem: unsafe-inline: true unsafe-eval: true + script-src: + unsafe-eval: true style-src: unsafe-inline: true + unsafe-eval: true whitelist: font-src: - "data:" diff --git a/roles/docker-moodle/vars/main.yml b/roles/docker-moodle/vars/main.yml index d8f4d145..de401bdc 100644 --- a/roles/docker-moodle/vars/main.yml +++ b/roles/docker-moodle/vars/main.yml @@ -1,4 +1,10 @@ --- -application_id: "moodle" -database_type: "mariadb" -container_name: "{{ application_id }}" \ No newline at end of file +application_id: "moodle" +database_type: "mariadb" +container_name: "{{ application_id }}" +bitnami_code_link: "/bitnami/moodle" +bitnami_code_dir: "/opt{{bitnami_code_link}}" +bitnami_data_dir: "/bitnami/moodledata" +bitnami_oidc_plugin_dir: "{{ bitnami_code_dir }}/auth/oidc" +bitnami_user: "daemon" +bitnami_user_group: "{{ bitnami_user }}:{{ bitnami_user }}" \ No newline at end of file