diff --git a/roles/homarr/tasks/main.yml b/roles/homarr/tasks/main.yml index f8dd3df..22bfe97 100644 --- a/roles/homarr/tasks/main.yml +++ b/roles/homarr/tasks/main.yml @@ -3,7 +3,22 @@ # tasks file for homarr # ===================================================================== -# 1. VORBEREITUNG: Pakete und Verzeichnisse VOR Container-Start +# 0. VALIDATION +# ===================================================================== + +- name: Validate encryption key + ansible.builtin.assert: + that: + - homarr_secret_encryption_key | length == 64 + - homarr_secret_encryption_key is match('^[a-f0-9]+$') + fail_msg: >- + homarr_secret_encryption_key must be a 64-character hex string. + Generate with: openssl rand -hex 32 + Provide via OpenBao, Ansible Vault or extra-vars. + success_msg: Encryption key validation passed + +# ===================================================================== +# 1. PREPARATION: packages and directories before container start # ===================================================================== - name: Ensure required packages are installed @@ -11,7 +26,6 @@ name: - sqlite3 - python3-docker - - python3-bcrypt state: present - name: Create docker compose directory @@ -37,7 +51,7 @@ register: db_exists # ===================================================================== -# 2. CONTAINER STARTEN +# 2. START CONTAINER # ===================================================================== - name: Create docker-compose file for homarr @@ -52,7 +66,7 @@ state: present # ===================================================================== -# 3. AUF DATENBANK WARTEN +# 3. WAIT FOR DATABASE # ===================================================================== - name: Wait for database to be created by Homarr @@ -63,71 +77,69 @@ when: not db_exists.stat.exists - name: Wait for database schema to be initialized - ansible.builtin.shell: | - i=0 - while [ $i -lt 30 ]; do - if sqlite3 "{{ homarr_db_dir }}" "SELECT name FROM sqlite_master WHERE type='table' AND name='board';" 2>/dev/null | grep -q board; then - exit 0 - fi - sleep 2 - i=$((i + 1)) - done - exit 1 + ansible.builtin.command: + cmd: sqlite3 "{{ homarr_db_dir }}" "SELECT name FROM sqlite_master WHERE type='table' AND name='board';" register: schema_check + until: schema_check.stdout == "board" + retries: 30 + delay: 2 changed_when: false when: not db_exists.stat.exists -- name: Ensure python3-bcrypt is installed - ansible.builtin.package: - name: python3-bcrypt - state: present +# ===================================================================== +# 4. GENERATE BCRYPT HASH (on controller, not on target) +# ===================================================================== - name: Generate bcrypt hash for admin password - ansible.builtin.shell: | - python3 -c " - import bcrypt - password = '{{ homarr_admin_password }}'.encode() - salt = bcrypt.gensalt(rounds=10) - hashed = bcrypt.hashpw(password, salt) - print(salt.decode()) - print(hashed.decode()) - " - register: bcrypt_output + ansible.builtin.shell: + cmd: python3 -c "import bcrypt, sys; print(bcrypt.hashpw(sys.stdin.read().encode(), bcrypt.gensalt(rounds=10)).decode())" + stdin: "{{ homarr_admin_password }}" + stdin_add_newline: false + delegate_to: localhost + become: false + register: bcrypt_result changed_when: false no_log: true +- name: Set bcrypt hash fact + ansible.builtin.set_fact: + homarr_bcrypt_hash: "{{ bcrypt_result.stdout }}" + no_log: true + # ===================================================================== -# 4. DATENBANK SEEDEN (nur wenn Onboarding noch nicht abgeschlossen) +# 5. SEED DATABASE (only if local admin user does not exist yet) # ===================================================================== -- name: Check if onboarding is already completed - ansible.builtin.shell: | - sqlite3 "{{ homarr_db_dir }}" "SELECT step FROM onboarding WHERE step='finish';" 2>/dev/null - register: onboarding_status +- name: Check if local admin user exists + ansible.builtin.command: + cmd: sqlite3 "{{ homarr_db_dir }}" "SELECT id FROM user WHERE id='user-local-admin';" + register: admin_exists changed_when: false failed_when: false - name: Seed Homarr database ansible.builtin.shell: | sqlite3 "{{ homarr_db_dir }}" << 'SEEDSQL' + BEGIN TRANSACTION; + -- SERVER SETTINGS INSERT OR REPLACE INTO serverSetting (setting_key, value) - VALUES - ('analytics', '{"json": {"enableGeneral": false, "enableWidgetData": false, "enableIntegrationData": false}}'), + VALUES + ('analytics', '{"json": {"enableGeneral": false, "enableWidgetData": false, "enableIntegrationData": false, "enableUserData": false}}'), ('culture', '{"json": {"defaultLocale": "de"}}'), ('crawling', '{"json": {"crawlingEnabled": false}}'), - ('board', '{"json": {"defaultBoardId": "board-default"}}'); + ('board', '{"json": {"homeBoardId": "board-default", "mobileHomeBoardId": "board-default", "enableStatusByDefault": true, "forceDisableStatus": false, "defaultBoardId": "board-default"}}'); - -- ONBOARDING ÜBERSPRINGEN + -- SKIP ONBOARDING UPDATE onboarding SET step = 'finish', previous_step = 'settings'; - -- ===================================================================== - -- GRUPPEN (müssen VOR groupMember existieren) - -- ===================================================================== + -- ================================================================= + -- GROUPS (must exist before groupMember) + -- ================================================================= - -- OIDC-ADMIN GRUPPE + -- OIDC admin group INSERT OR IGNORE INTO "group" (id, name, owner_id, position) - VALUES ('group-oidc-admins', '{{ oidc_admin_group | default("homarr-admins") }}', NULL, 0); + VALUES ('group-oidc-admins', '{{ homarr_oidc_admin_group }}', NULL, 0); INSERT OR IGNORE INTO groupPermission (group_id, permission) VALUES @@ -137,7 +149,7 @@ ('group-oidc-admins', 'integration-create'), ('group-oidc-admins', 'integration-full-access'); - -- CREDENTIALS-ADMIN GRUPPE + -- Credentials admin group INSERT OR IGNORE INTO "group" (id, name, owner_id, position) VALUES ('group-credentials-admin', 'credentials-admin', NULL, 1); @@ -149,31 +161,29 @@ ('group-credentials-admin', 'integration-create'), ('group-credentials-admin', 'integration-full-access'); - -- ===================================================================== - -- LOKALER ADMIN USER (Passwort wird via CLI gesetzt) - -- ===================================================================== + -- ================================================================= + -- LOCAL ADMIN USER + -- ================================================================= - INSERT OR IGNORE INTO user (id, name, email, password, salt, email_verified, provider) + INSERT OR IGNORE INTO user (id, name, email, password, email_verified, provider) VALUES ( 'user-local-admin', - '{{ homarr_admin_username | default("admin") }}', - '{{ homarr_admin_email | default("admin@digitalboard.ch") }}', - '{{ bcrypt_output.stdout_lines[1] }}', - '{{ bcrypt_output.stdout_lines[0] }}', + '{{ homarr_admin_username }}', + '{{ homarr_admin_email }}', + '{{ homarr_bcrypt_hash }}', 1, 'credentials' ); - -- ADMIN-USER DEN GRUPPEN ZUWEISEN (Gruppen existieren jetzt) - + -- Assign admin user to groups INSERT OR IGNORE INTO groupMember (group_id, user_id) VALUES ('group-credentials-admin', 'user-local-admin'), ('group-oidc-admins', 'user-local-admin'); - -- ===================================================================== + -- ================================================================= -- BOARD - -- ===================================================================== + -- ================================================================= INSERT OR IGNORE INTO board ( id, name, is_public, @@ -183,8 +193,8 @@ ) VALUES ( 'board-default', - '{{ default_board_name | default("Dashboard") }}', - {% if default_board_public | default(true) %}1{% else %}0{% endif %}, + '{{ homarr_default_board_name }}', + {% if homarr_default_board_public %}1{% else %}0{% endif %}, '#fa5252', '#fd7e14', 100, @@ -195,18 +205,18 @@ 0 ); - -- LAYOUTS + -- Layouts INSERT OR IGNORE INTO layout (id, name, board_id, column_count, breakpoint) VALUES ('layout-desktop', 'Desktop', 'board-default', 10, 0), ('layout-tablet', 'Tablet', 'board-default', 6, 768), ('layout-mobile', 'Mobile', 'board-default', 2, 480); - -- HOME BOARD FÜR ADMIN SETZEN (Board existiert jetzt) + -- Set home board for admin user (board exists now) UPDATE user SET home_board_id = 'board-default', mobile_home_board_id = 'board-default' WHERE id = 'user-local-admin'; - -- SEKTION + -- Section DELETE FROM section_layout WHERE section_id = 'section-apps'; DELETE FROM item_layout WHERE section_id = 'section-apps'; DELETE FROM section WHERE id = 'section-apps'; @@ -218,7 +228,7 @@ 'empty', 0, 0, - 'Anwendungen', + 'Applications', '{"json": {}}' ); @@ -228,15 +238,15 @@ ('section-apps', 'layout-tablet', NULL, 0, 0, 6, 4), ('section-apps', 'layout-mobile', NULL, 0, 0, 2, 6); - -- BOARD-BERECHTIGUNG + -- Board permissions INSERT OR IGNORE INTO boardGroupPermission (board_id, group_id, permission) VALUES ('board-default', 'group-oidc-admins', 'full-access'), ('board-default', 'group-credentials-admin', 'full-access'); - -- ===================================================================== + -- ================================================================= -- APPS - -- ===================================================================== + -- ================================================================= -- Nextcloud INSERT OR IGNORE INTO app (id, name, description, icon_url, href) @@ -312,85 +322,12 @@ ('item-mailman', 'section-apps', 'layout-desktop', 4, 0, 2, 1), ('item-mailman', 'section-apps', 'layout-tablet', 4, 0, 2, 1), ('item-mailman', 'section-apps', 'layout-mobile', 0, 1, 1, 1); + + COMMIT; SEEDSQL args: executable: /bin/bash register: seed_result changed_when: seed_result.rc == 0 - when: onboarding_status.stdout is not defined or 'finish' not in onboarding_status.stdout - -# ===================================================================== -# 5. RESTART, HEALTH-CHECK, DANN CLI -# ===================================================================== - -- name: Restart Homarr to apply database changes - ansible.builtin.shell: - cmd: docker compose restart - chdir: "{{ homarr_docker_compose_dir }}" - when: seed_result is changed - -- name: Wait for Homarr to be ready - ansible.builtin.shell: - cmd: docker compose exec -T {{ homarr_service_name }} wget -qO /dev/null "http://localhost:7575" 2>&1 - chdir: "{{ homarr_docker_compose_dir }}" - retries: 30 - delay: 5 - register: homarr_ready - until: homarr_ready.rc == 0 - changed_when: false - -- name: Display completion message - ansible.builtin.debug: - msg: | - ============================================================ - Homarr deployed! - URL: {{ homarr_base_url }} - Admin-User: {{ homarr_admin_username }} - Admin-Passwort: {{ homarr_admin_password }} - (Bitte sofort ändern!) - ============================================================ - when: not db_exists.stat.exists - -# ===================================================================== -# 6. ABSCHLUSS -# ===================================================================== - -- name: Display admin credentials - ansible.builtin.debug: - msg: | - ============================================ - LOKALER ADMIN PASSWORT (bitte sofort ändern!) - {{ admin_password_output.stdout }} - ============================================ - when: - - admin_password_output is defined - - admin_password_output.stdout is defined - - admin_password_output.stdout | length > 0 - -- name: Display completion message - ansible.builtin.debug: - msg: | - ============================================================ - Homarr wurde erfolgreich deployed! - ============================================================ - - URL: {{ homarr_base_url }} - - OIDC Konfiguration: - - Issuer: {{ oidc_issuer }} - - Client ID: {{ oidc_client_id }} - - Admin-Gruppe: {{ oidc_admin_group }} - - Nächste Schritte: - 1. Stelle sicher, dass in Keycloak die Gruppe "{{ oidc_admin_group }}" existiert - 2. Füge deinen User zur Gruppe "{{ oidc_admin_group }}" hinzu - 3. Öffne {{ homarr_base_url }} - 4. Du solltest automatisch via OIDC eingeloggt werden - - Das Standard-Board "{{ default_board_name }}" wurde erstellt mit: - - Nextcloud App - - Keycloak App - - Mailman App - - Weitere Apps kannst du direkt in der Homarr UI hinzufügen. - ============================================================ \ No newline at end of file + when: admin_exists.stdout == "" + notify: restart homarr \ No newline at end of file