From 278dd3cebee24d27b2c0849c33266e740fb0a3b7 Mon Sep 17 00:00:00 2001 From: il Date: Sat, 2 May 2026 16:42:30 +0900 Subject: [PATCH] feat(nextcloud): release nextcloud deployment note: - use nextcloud for groupware - consider replacing vikunja and opencloud --- ansible/inventory/group_vars/all.yaml | 9 + ansible/playbooks/app/site.yaml | 8 + ansible/roles/app/handlers/main.yaml | 12 ++ .../app/tasks/services/set_nextcloud.yaml | 176 ++++++++++++++++++ .../infra/tasks/services/set_postgresql.yaml | 1 + config/secrets/secrets.yaml | 15 +- .../nextcloud/config/background.config.php.j2 | 4 + .../app/nextcloud/config/cache.config.php.j2 | 12 ++ .../app/nextcloud/config/domain.config.php.j2 | 9 + .../config/local_remote.config.php.j2 | 4 + .../nextcloud/config/user_oidc.config.php.j2 | 9 + .../containers/app/nextcloud/ini/opcache.ini | 14 ++ .../containers/app/nextcloud/ini/upload.ini | 6 + .../app/nextcloud/nextcloud.container.j2 | 36 ++++ .../nextcloud/systemd/nextcloud-cron.service | 8 + .../nextcloud/systemd/nextcloud-cron.timer | 10 + .../auth/authelia/config/authelia.yaml.j2 | 22 +++ .../common/caddy/etc/app/Caddyfile.j2 | 6 + .../common/caddy/etc/auth/Caddyfile.j2 | 9 + docs/services/app/nextcloud.md | 88 +++++++++ docs/specifications/environments.md | 8 +- 21 files changed, 460 insertions(+), 6 deletions(-) create mode 100644 ansible/roles/app/tasks/services/set_nextcloud.yaml create mode 100644 config/services/containers/app/nextcloud/config/background.config.php.j2 create mode 100644 config/services/containers/app/nextcloud/config/cache.config.php.j2 create mode 100644 config/services/containers/app/nextcloud/config/domain.config.php.j2 create mode 100644 config/services/containers/app/nextcloud/config/local_remote.config.php.j2 create mode 100644 config/services/containers/app/nextcloud/config/user_oidc.config.php.j2 create mode 100644 config/services/containers/app/nextcloud/ini/opcache.ini create mode 100644 config/services/containers/app/nextcloud/ini/upload.ini create mode 100644 config/services/containers/app/nextcloud/nextcloud.container.j2 create mode 100644 config/services/containers/app/nextcloud/systemd/nextcloud-cron.service create mode 100644 config/services/containers/app/nextcloud/systemd/nextcloud-cron.timer create mode 100644 docs/services/app/nextcloud.md diff --git a/ansible/inventory/group_vars/all.yaml b/ansible/inventory/group_vars/all.yaml index 3d9fe7f..b7c4ef3 100644 --- a/ansible/inventory/group_vars/all.yaml +++ b/ansible/inventory/group_vars/all.yaml @@ -148,6 +148,14 @@ services: http: "3010" redis: "6381" manticore: "9308" + nextcloud: + domain: + public: "nextcloud" + internal: "nextcloud.app" + ports: + http: "8002" + redis: "6382" + subuid: "100032" version: packages: @@ -184,3 +192,4 @@ version: opencloud: "4.0.6" manticore: "25.0.0" affine: "0.26.3" + nextcloud: "33.0.3" diff --git a/ansible/playbooks/app/site.yaml b/ansible/playbooks/app/site.yaml index 2f6819f..44f4b5e 100644 --- a/ansible/playbooks/app/site.yaml +++ b/ansible/playbooks/app/site.yaml @@ -225,6 +225,14 @@ tags: ["site", "affine"] tags: ["site", "affine"] + - name: Set nextcloud + ansible.builtin.include_role: + name: "app" + tasks_from: "services/set_nextcloud" + apply: + tags: ["site", "nextcloud"] + tags: ["site", "nextcloud"] + - name: Flush handlers right now ansible.builtin.meta: "flush_handlers" diff --git a/ansible/roles/app/handlers/main.yaml b/ansible/roles/app/handlers/main.yaml index 9788635..093d61f 100644 --- a/ansible/roles/app/handlers/main.yaml +++ b/ansible/roles/app/handlers/main.yaml @@ -99,3 +99,15 @@ changed_when: false listen: "notification_restart_affine" ignore_errors: true # noqa: ignore-errors + +- name: Restart nextcloud + ansible.builtin.systemd: + name: "nextcloud.service" + state: "restarted" + enabled: true + daemon_reload: true + scope: "user" + when: is_nextcloud_init.stat.exists + changed_when: false + listen: "notification_restart_nextcloud" + ignore_errors: true # noqa: ignore-errors diff --git a/ansible/roles/app/tasks/services/set_nextcloud.yaml b/ansible/roles/app/tasks/services/set_nextcloud.yaml new file mode 100644 index 0000000..224f76f --- /dev/null +++ b/ansible/roles/app/tasks/services/set_nextcloud.yaml @@ -0,0 +1,176 @@ +--- +- name: Set redis service name + ansible.builtin.set_fact: + redis_service: "nextcloud" + +- name: Create redis_nextcloud directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ services['redis']['subuid'] }}" + group: "svadmins" + mode: "0770" + loop: + - "containers/redis" + - "containers/redis/{{ redis_service }}" + - "containers/redis/{{ redis_service }}/data" + become: true + +- name: Deploy redis config file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.conf.j2" + dest: "{{ node['home_path'] }}/containers/redis/{{ redis_service }}/redis.conf" + owner: "{{ ansible_user }}" + group: "svadmins" + mode: "0644" + register: "is_redis_conf" + +- name: Deploy redis container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/redis_{{ redis_service }}.container" + owner: "{{ ansible_user }}" + group: "svadmins" + mode: "0644" + register: "is_redis_containerfile" + +- name: Enable (Restart) redis service + ansible.builtin.systemd: + name: "redis_{{ redis_service }}.service" + state: "restarted" + enabled: true + daemon_reload: true + scope: "user" + when: is_redis_conf.changed or is_redis_containerfile.changed # noqa: no-handler + +- name: Create nextcloud directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ services['nextcloud']['subuid'] }}" + group: "svadmins" + mode: "0770" + loop: + - "data/containers/nextcloud" + - "data/containers/nextcloud/html" + - "containers/nextcloud" + - "containers/nextcloud/ssl" + - "containers/nextcloud/ini" + become: true + +- name: Check data directory empty + ansible.builtin.stat: + path: "{{ node['home_path'] }}/data/containers/nextcloud/.init" + register: "is_nextcloud_init" + +- name: Deploy root certificate + ansible.builtin.copy: + content: | + {{ hostvars['console']['ca']['root']['crt'] }} + dest: "{{ node['home_path'] }}/containers/nextcloud/ssl/{{ root_cert_filename }}" + owner: "{{ services['nextcloud']['subuid'] }}" + group: "svadmins" + mode: "0440" + become: true + notify: "notification_restart_nextcloud" + no_log: true + +- name: Initialize nextcloud + when: not is_nextcloud_init.stat.exists + block: + - name: Execute init command (Including pulling image) + containers.podman.podman_container: + name: "nextcloud_init" + image: "docker.io/library/nextcloud:{{ version['containers']['nextcloud'] }}" + command: "/bin/true" + state: "started" + rm: true + detach: false + env: + NEXTCLOUD_UPDATE: "1" + NEXTCLOUD_ADMIN_USER: "admin-local" + NEXTCLOUD_ADMIN_PASSWORD: "{{ hostvars['console']['nextcloud']['admin-local']['password'] }}" + POSTGRES_HOST: "{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}" + POSTGRES_DB: "nextcloud_db" + POSTGRES_USER: "nextcloud" + POSTGRES_PASSWORD: "{{ hostvars['console']['postgresql']['password']['nextcloud'] }}" + PGSSLMODE: "verify-full" + PGSSLROOTCERT: "/etc/ssl/nextcloud/{{ root_cert_filename }}" + PGSSLCERTMODE: "disable" + REDIS_HOST: "host.containers.internal" + REDIS_HOST_PORT: "{{ services['nextcloud']['ports']['redis'] }}" + volume: + - "{{ node['home_path'] }}/containers/nextcloud/ssl:/etc/ssl/nextcloud:ro" + - "{{ node['home_path'] }}/data/containers/nextcloud/html:/var/www/html:rw" + no_log: true + + - name: Create .init file + ansible.builtin.file: + path: "{{ node['home_path'] }}/data/containers/nextcloud/.init" + state: "touch" + mode: "0644" + owner: "{{ ansible_user }}" + group: "svadmins" + +- name: Deploy config files + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/nextcloud/config/{{ item }}.j2" + dest: "{{ node['home_path'] }}/data/containers/nextcloud/html/config/{{ item }}" + owner: "{{ services['nextcloud']['subuid'] }}" + group: "svadmins" + mode: "0640" + loop: + - "background.config.php" + - "cache.config.php" + - "domain.config.php" + - "local_remote.config.php" + - "user_oidc.config.php" + become: true + notify: "notification_restart_nextcloud" + +- name: Deploy opcache.ini file + ansible.builtin.copy: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/nextcloud/ini/{{ item }}" + dest: "{{ node['home_path'] }}/containers/nextcloud/ini/{{ item }}" + group: "svadmins" + mode: "0644" + loop: + - "opcache.ini" + - "upload.ini" + notify: "notification_restart_nextcloud" + +- name: Deploy nextcloud.container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/nextcloud/nextcloud.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/nextcloud.container" + owner: "{{ ansible_user }}" + group: "svadmins" + mode: "0644" + notify: "notification_restart_nextcloud" + +- name: Deploy nextcloud-cron service + ansible.builtin.copy: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/nextcloud/systemd/{{ item }}" + dest: "{{ node['home_path'] }}/.config/systemd/user/{{ item }}" + owner: "{{ ansible_user }}" + group: "svadmins" + mode: "0644" + loop: + - "nextcloud-cron.service" + - "nextcloud-cron.timer" + +- name: Enable nextcloud.service + ansible.builtin.systemd: + name: "nextcloud.service" + state: "started" + enabled: true + daemon_reload: true + scope: "user" + +- name: Enable nextcloud-cron.timer + ansible.builtin.systemd: + name: "nextcloud-cron.timer" + state: "started" + enabled: true + daemon_reload: true + scope: "user" diff --git a/ansible/roles/infra/tasks/services/set_postgresql.yaml b/ansible/roles/infra/tasks/services/set_postgresql.yaml index 09dae46..5b497af 100644 --- a/ansible/roles/infra/tasks/services/set_postgresql.yaml +++ b/ansible/roles/infra/tasks/services/set_postgresql.yaml @@ -11,6 +11,7 @@ - "paperless" - "vikunja" - "affine" + - "nextcloud" - name: Create postgresql directory ansible.builtin.file: diff --git a/config/secrets/secrets.yaml b/config/secrets/secrets.yaml index dd7b5ae..a96de09 100644 --- a/config/secrets/secrets.yaml +++ b/config/secrets/secrets.yaml @@ -119,6 +119,7 @@ postgresql: paperless: ENC[AES256_GCM,data:6VBrBbjVoam7SkZCSvoBTdrfkUoDghdGTiBmFLul04X/okXOHeC5zusJffY=,iv:iZumcJ3TWwZD77FzYx8THwCqC+EbnXUBrEKuPh3zgV8=,tag:u2m8SppAdxZ/duNdpuS3oQ==,type:str] vikunja: ENC[AES256_GCM,data:/+wQdoFPTBG2elI9kZbAVWrHZ0DhMaYr4dc+2z9QNdb3TcDS2PEia0JuSAg=,iv:MViZTyUD8YqMmxSTWCQpJ30f/KQdQGOzPlRHHsQ8lAw=,tag:zov3POno139dkMxFDpj2gg==,type:str] affine: ENC[AES256_GCM,data:XPXrcszsV06YqCJZ7CDqc4rCwqqNlbtLCFYfLAQ8jamLtft8L2UVrMA4WZo=,iv:vrWdBeckxB9tmEE628j4jhU+hSpE6TXYMGt0hh1Cg84=,tag:hlWwWUGht8NqWTZREMsa1Q==,type:str] + nextcloud: ENC[AES256_GCM,data:ROsximNuWYMTZktmLJPx7W1Qol/uT+APgwoCtFO/6ZYYc3KxKvlk344eqEc=,iv:4d+MrfIHjJKAcwhvZ3g4go66uZcieuL7lngKErJd+fg=,tag:QbWOtxeCbiu62GyrE2atXg==,type:str] #ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] # # @@ -255,6 +256,16 @@ affine: #ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] # # +#ENC[AES256_GCM,data:PZS7EbvMHqHGorNUGAWj4dk1,iv:vOE+djRAvBTMM51kHi6kG5Arw3uPXlJt1d/BpcEaD0c=,tag:AuoCHLQz42CYvVVdKFWu1Q==,type:comment] +nextcloud: + admin-local: + password: ENC[AES256_GCM,data:mIwF5A09oqYbdK3bOKid9A896Q5J5Q6Ax+vDNqEJFGNdzd/mJ4oQS6rva+s=,iv:QroUMST2wnEJzk6DySe9tPZaWuqdxzJZ0+oi6mW6x00=,tag:3UTzjupK7+omrI3Hvyr8bA==,type:str] + oidc: + secret: ENC[AES256_GCM,data:Sr4KkKkYdkU0UWdpfUF7PyiGoerjBiw+sOFcENyLxw0FRXGG0Y8gv5uGb4Q=,iv:LbGsNM3+iY7bWFQe88TepVKUdiRQWZ+K7Ubn6ze6lV4=,tag:SbcfIAMW9ZprgahOFU4IQQ==,type:str] + hash: ENC[AES256_GCM,data:CkstbIYQmi72QhsbJZN0lQedgCn7TmGpYcYj0n+NvJIoTlol8G9N/88cwGbVoGK9nEISv54FL94cEJFppnMIuj0BHrhasrZsyI2/Lj52YLWdwNJWNQ+iYt+Ifp/1kI0zqmdoajzZ5DS2w/1evCBC1+JdfTRlpVXmSsHUIPIHelBRj90=,iv:vwvT5TTkF4woxXOvrRRqmrdLXf19s47NIDtdT+zLp0U=,tag:KC0MS0DTH6j3zIHOjCFOSA==,type:str] +#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] +# +# #ENC[AES256_GCM,data:T4Wtn49AAxPd2QUFTR+q,iv:bH5goGWBDqumAat9dUv2OwfCUJUpuVqncTMqMBZUXhI=,tag:G+W6hHA+yftQ+4RJpXrxHg==,type:comment] switch: password: ENC[AES256_GCM,data:qu0f9L7A0eFq/UCpaRs=,iv:W8LLOp3MSfd/+EfNEZNf91K8GgI5eUfVPoWTRES2C0Y=,tag:Q5FlAOfwqwJwPvd7k6i+0g==,type:str] @@ -284,7 +295,7 @@ sops: UmliaFNxVTBqRkI1QWJpWGpTRWxETW8KEY/8AfU73UOzCGhny1cNnd5dCNv7bHXt k+uyWPPi+enFkVaceSwMFrA66uaWWrwAj11sXEB7yzvGFPrnAGezjQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-04-06T14:32:22Z" - mac: ENC[AES256_GCM,data:OFiSsBBAzOUoOwnAwhaplQQ8k2kUo+Avzk475BpaiOJoaB2c0wsJ3siP15tcLMrav4Qw8boZFo64v+rjdMoNI/MRo1EOYWNr1ZRMqHzwmQeaiMH2QcfoRZ0oLqrn5ekQztuPR9ULjDYZb63AwVGmzseUf4R5lGXgdgN5tjU/pH4=,iv:hqzDwryMuJ7JnkBazzDSznw05m7k61Sk61aPgO3JtpU=,tag:Lhhlgwy+YuQ1S0hkbsjecg==,type:str] + lastmodified: "2026-05-02T04:55:25Z" + mac: ENC[AES256_GCM,data:4U/SGYS9eNRgRvUEvZh9E0JSctkZzSpdoUYEAbnOVyU+5u8NcG9lbMUAB4kFXb9kHVGBUI5wMwnzg102g96q1IYw5m/k4lrpePceGVNAxxKpWTnkLROhJlL3Z/Bylgq2mj7PVDcGCGEB0xPDgN+ffa7ldCxIikYmSKktISguwYU=,iv:zqS9iJ54FIaNQhnfOl4YY9QcaZLbPekTxlY1AEp3m/s=,tag:TckIRAKRVyxf/UD+jejNng==,type:str] unencrypted_suffix: _unencrypted version: 3.12.1 diff --git a/config/services/containers/app/nextcloud/config/background.config.php.j2 b/config/services/containers/app/nextcloud/config/background.config.php.j2 new file mode 100644 index 0000000..aef8e6c --- /dev/null +++ b/config/services/containers/app/nextcloud/config/background.config.php.j2 @@ -0,0 +1,4 @@ + 18, +]; diff --git a/config/services/containers/app/nextcloud/config/cache.config.php.j2 b/config/services/containers/app/nextcloud/config/cache.config.php.j2 new file mode 100644 index 0000000..2560080 --- /dev/null +++ b/config/services/containers/app/nextcloud/config/cache.config.php.j2 @@ -0,0 +1,12 @@ + '\OC\Memcache\APCu', + 'memcache.distributed' => '\OC\Memcache\Redis', + 'memcache.locking' => '\OC\Memcache\Redis', + 'redis' => [ + 'host' => 'host.containers.internal', + 'port' => {{ services['nextcloud']['ports']['redis'] }}, + 'timeout' => 1.5, + 'dbindex' => 0, + ], +]; diff --git a/config/services/containers/app/nextcloud/config/domain.config.php.j2 b/config/services/containers/app/nextcloud/config/domain.config.php.j2 new file mode 100644 index 0000000..1baa803 --- /dev/null +++ b/config/services/containers/app/nextcloud/config/domain.config.php.j2 @@ -0,0 +1,9 @@ + [ + '{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}', + ], + 'overwritehost' => '{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}', + 'overwriteprotocol' => 'https', + 'overwrite.cli.url' => 'https://{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}', +]; diff --git a/config/services/containers/app/nextcloud/config/local_remote.config.php.j2 b/config/services/containers/app/nextcloud/config/local_remote.config.php.j2 new file mode 100644 index 0000000..00e9a33 --- /dev/null +++ b/config/services/containers/app/nextcloud/config/local_remote.config.php.j2 @@ -0,0 +1,4 @@ + true, +]; diff --git a/config/services/containers/app/nextcloud/config/user_oidc.config.php.j2 b/config/services/containers/app/nextcloud/config/user_oidc.config.php.j2 new file mode 100644 index 0000000..80abdf1 --- /dev/null +++ b/config/services/containers/app/nextcloud/config/user_oidc.config.php.j2 @@ -0,0 +1,9 @@ + [ + 'default_token_endpoint_auth_method' => 'client_secret_post', + 'auto_provision' => true, + 'soft_auto_provision' => true, + 'disable_account_creation' => false, + ], +]; diff --git a/config/services/containers/app/nextcloud/ini/opcache.ini b/config/services/containers/app/nextcloud/ini/opcache.ini new file mode 100644 index 0000000..58dfc68 --- /dev/null +++ b/config/services/containers/app/nextcloud/ini/opcache.ini @@ -0,0 +1,14 @@ +; /usr/local/etc/php/conf.d/opcache-recommended.ini +; OPcache tuning +opcache.enable=1 +opcache.enable_cli=1 +opcache.memory_consumption=512 +opcache.interned_strings_buffer=32 +opcache.max_accelerated_files=20000 +opcache.validate_timestamps=0 +opcache.save_comments=1 +opcache.revalidate_freq=60 +opcache.fast_shutdown=1 + +; APCu CLI activate +apc.enable_cli=1 \ No newline at end of file diff --git a/config/services/containers/app/nextcloud/ini/upload.ini b/config/services/containers/app/nextcloud/ini/upload.ini new file mode 100644 index 0000000..d4c2130 --- /dev/null +++ b/config/services/containers/app/nextcloud/ini/upload.ini @@ -0,0 +1,6 @@ +; /usr/local/etc/php/conf.d/nextcloud-upload.ini +upload_max_filesize=16G +post_max_size=16G +memory_limit=1024M +max_execution_time=3600 +max_input_time=3600 \ No newline at end of file diff --git a/config/services/containers/app/nextcloud/nextcloud.container.j2 b/config/services/containers/app/nextcloud/nextcloud.container.j2 new file mode 100644 index 0000000..80e4204 --- /dev/null +++ b/config/services/containers/app/nextcloud/nextcloud.container.j2 @@ -0,0 +1,36 @@ +[Quadlet] +DefaultDependencies=false + +[Unit] +Description=Nextcloud + +[Container] +Image=docker.io/library/nextcloud:{{ version['containers']['nextcloud'] }} +ContainerName=nextcloud +HostName=nextcloud + +PublishPort={{ services['nextcloud']['ports']['http'] }}:80 + +Volume=%h/containers/nextcloud/ssl:/etc/ssl/nextcloud:ro +Volume=%h/containers/nextcloud/ini/opcache.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini:ro +Volume=%h/containers/nextcloud/ini/upload.ini:/usr/local/etc/php/conf.d/upload.ini:ro +Volume=%h/data/containers/nextcloud/html:/var/www/html:rw + +# General +Environment="TZ=Asia/Seoul" +# PostgreSQL +Environment="PGSSLMODE=verify-full" +Environment="PGSSLROOTCERT=/etc/ssl/nextcloud/{{ root_cert_filename }}" +## libpq in Nextcloud automatically tries to use a client certificate for mTLS. Therefore, when only TLS is required, then disable the option explicitly. +Environment="PGSSLCERTMODE=disable" +# Redis +Environment="REDIS_HOST=host.containers.internal" +Environment="REDIS_HOST_PORT={{ services['nextcloud']['ports']['redis'] }}" + +[Service] +Restart=always +RestartSec=10s +TimeoutStopSec=120 + +[Install] +WantedBy=default.target diff --git a/config/services/containers/app/nextcloud/systemd/nextcloud-cron.service b/config/services/containers/app/nextcloud/systemd/nextcloud-cron.service new file mode 100644 index 0000000..abcd1fa --- /dev/null +++ b/config/services/containers/app/nextcloud/systemd/nextcloud-cron.service @@ -0,0 +1,8 @@ +[Unit] +Description=Nextcloud cron.php +Requires=nextcloud.service +After=nextcloud.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/podman exec -u www-data nextcloud php -f /var/www/html/cron.php diff --git a/config/services/containers/app/nextcloud/systemd/nextcloud-cron.timer b/config/services/containers/app/nextcloud/systemd/nextcloud-cron.timer new file mode 100644 index 0000000..676fc4f --- /dev/null +++ b/config/services/containers/app/nextcloud/systemd/nextcloud-cron.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Run Nextcloud cron every 5 minutes + +[Timer] +OnBootSec=5min +OnUnitActiveSec=5min +Unit=nextcloud-cron.service + +[Install] +WantedBy=timers.target diff --git a/config/services/containers/auth/authelia/config/authelia.yaml.j2 b/config/services/containers/auth/authelia/config/authelia.yaml.j2 index f378de7..bab02f4 100644 --- a/config/services/containers/auth/authelia/config/authelia.yaml.j2 +++ b/config/services/containers/auth/authelia/config/authelia.yaml.j2 @@ -365,3 +365,25 @@ identity_providers: access_token_signed_response_alg: 'none' userinfo_signed_response_alg: 'none' token_endpoint_auth_method: 'client_secret_post' + # https://www.authelia.com/integration/openid-connect/clients/nextcloud/#openid-connect-user-backend-app + - client_id: 'nextcloud' + client_name: 'Nextcloud' + client_secret: '{{ hostvars['console']['nextcloud']['oidc']['hash'] }}' + public: false + authorization_policy: 'one_factor' + require_pkce: true + pkce_challenge_method: 'S256' + redirect_uris: + - 'https://{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}/apps/user_oidc/code' + scopes: + - 'openid' + - 'profile' + - 'email' + - 'groups' + response_types: + - 'code' + grant_types: + - 'authorization_code' + access_token_signed_response_alg: 'none' + userinfo_signed_response_alg: 'none' + token_endpoint_auth_method: 'client_secret_post' diff --git a/config/services/containers/common/caddy/etc/app/Caddyfile.j2 b/config/services/containers/common/caddy/etc/app/Caddyfile.j2 index da5699b..686aca4 100644 --- a/config/services/containers/common/caddy/etc/app/Caddyfile.j2 +++ b/config/services/containers/common/caddy/etc/app/Caddyfile.j2 @@ -77,3 +77,9 @@ header_up Host {http.request.header.X-Forwarded-Host} } } +{{ services['nextcloud']['domain']['internal'] }}.{{ domain['internal'] }} { + import private_tls + reverse_proxy host.containers.internal:{{ services['nextcloud']['ports']['http'] }} { + header_up Host {http.request.header.X-Forwarded-Host} + } +} diff --git a/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 b/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 index 250be10..433d0d5 100644 --- a/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 +++ b/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 @@ -136,6 +136,15 @@ } } } +{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }} { + import crowdsec_log + route { + crowdsec + reverse_proxy https://{{services['nextcloud']['domain']['internal'] }}.{{ domain['internal'] }} { + header_up Host {http.reverse_proxy.upstream.host} + } + } +} # Internal domain {{ node['name'] }}.{{ domain['internal'] }} { diff --git a/docs/services/app/nextcloud.md b/docs/services/app/nextcloud.md new file mode 100644 index 0000000..b039f5a --- /dev/null +++ b/docs/services/app/nextcloud.md @@ -0,0 +1,88 @@ +# Nextcloud + +## Prerequisite + +### Create database + +- Create the password with `openssl rand -base64 32` + - Save this value in secrets.yaml in `postgresql.password.nextcloud` + - Access infra server to create nextcloud_db with `podman exec -it postgresql psql -U postgres` + +```SQL +CREATE USER nextcloud WITH PASSWORD 'postgresql.password.nextcloud'; +CREATE DATABASE nextcloud_db; +ALTER DATABASE nextcloud_db OWNER TO nextcloud; +``` + +### Create oidc secret and hash + +- Create the secret with `openssl rand -base64 32` +- access to auth vm + - `podman exec -it authelia sh` + - `authelia crypto hash generate pbkdf2 --password 'nextcloud.oidc.secret'` +- Save this value in secrets.yaml in `nextcloud.oidc.secret` and `nextcloud.oidc.hash` + +### Create admin password + +- Create the secret with `openssl rand -base64 32` +- Save this value in secrets.yaml in `nextcloud.admin-local.password` + +### Add postgresql dump backup list + +- [set_postgresql.yaml](../../../ansible/roles/infra/tasks/services/set_postgresql.yaml) + +```yaml +- name: Set connected services list + ansible.builtin.set_fact: + connected_services: + - ... + - "nextcloud" +``` + +## Configuration + +### Access + +- https://nextcloud.ilnmors.com + - login with admin-local + +### Disable and enable apps + +- Profile: Apps: Your apps: Disable + - Photo + - dashboard + +- Profile: Apps: Search + - OpenID Connect user backend + - Calendar + - Contacts + - Deck + - Tasks + - Mail + - Nextcloud Office + +### Configuration + +```bash +podman exec -u www-data nextcloud php occ user_oidc:provider Authelia \ + --clientid="nextcloud" \ + --clientsecret="nextcloud.oidc.secret" \ + --discoveryuri="https://authelia.ilnmors.com/.well-known/openid-configuration" \ + --scope="openid profile email groups" \ + --unique-uid=0 \ + --mapping-uid="preferred_username" \ + --mapping-display-name="name" \ + --mapping-email="email" \ + --mapping-groups="groups" \ + --group-whitelist-regex="/^users$/" \ + --group-provisioning=1 + +podman exec -u www-data nextcloud php occ db:add-missing-indices +podman exec -u www-data nextcloud php occ db:add-missing-columns +podman exec -u www-data nextcloud php occ db:add-missing-primary-keys +``` + +### Account configuration + +- Profile: Accounts: + - allocate admin group for admin users diff --git a/docs/specifications/environments.md b/docs/specifications/environments.md index bf6a61c..50c35f4 100644 --- a/docs/specifications/environments.md +++ b/docs/specifications/environments.md @@ -11,8 +11,8 @@ - [x] Terminal - [x] Step-CLI - [x] Ansible - - Git - - Kopia + - [x] Git + - [x] Kopia - [x] cloud-image-utils ## vmm \(Hypervisor\) @@ -119,10 +119,10 @@ - [x] Immich - [x] Actual budget - [x] Paperless-ngx - - [x] vikunja - When affine is verified to substitute kanban board and etc, then disable this service. + - [x] vikunja - [x] OpenCloud - [x] affine \(Notion substitution\) - - [ ] Radicale + - [x] Nextcloud \(Use nextcloud as CalDAV and CardDav, kanban and todo\) - [ ] Collabora office - WriteFreely - MediaCMS