diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fac1c60 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 1.0.0 + +* Initial Release \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f999d41 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Ansible Collection - enbewe.nextcloud +Collecion to deploy Nextcloud for file storage as well as CalDav/CardDav Server. + +## Playbooks +### enbewe.nextcloud.deployment +Deploys the role `enbewe.nextcloud.nextcloud` to all hosts in the group `nextcloud`. + +## Roles +### enbewe.nextcloud.nextcloud +Deploys [Nextcloud](https://nextcloud.com/) through a podman container. +The deployment consists of a separate postgres database container, a redis container, +an application container as well as a container for the cron service. + +The configuration of the instance is automatically adjusted to work with +a OpenID provider. + +## Modules +### enbewe.nextcloud.nextcloud_app +Manages the apps installed in the instance. \ No newline at end of file diff --git a/galaxy.yml b/galaxy.yml new file mode 100644 index 0000000..25661f3 --- /dev/null +++ b/galaxy.yml @@ -0,0 +1,67 @@ +--- +### REQUIRED +# The namespace of the collection. This can be a company/brand/organization or product namespace under which all +# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with +# underscores or numbers and cannot contain consecutive underscores +namespace: 'enbewe' + +# The name of the collection. Has the same character restrictions as 'namespace' +name: 'nextcloud' + +# The version of the collection. Must be compatible with semantic versioning +version: '1.0.0' + +# The path to the Markdown (.md) readme file. This path is relative to the root of the collection +readme: 'README.md' + +# A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) +# @nicks:irc/im.site#channel' +authors: + - 'Nis Wechselberg ' + + +### OPTIONAL but strongly recommended +# A short summary description of the collection +description: 'Deployment tools for nextcloud' + +# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only +# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' +license: + - 'MIT' + +# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character +# requirements as 'namespace' and 'name' +tags: + - 'linux' + +# Collections that this collection requires to be installed for it to be usable. The key of the dict is the +# collection label 'namespace.name'. The value is a version range +# L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version +# range specifiers can be set and are separated by ',' +dependencies: + containers.podman: '1.13.0' + +# The URL of the originating SCM repository +repository: 'https://git.enbewe.de/Coding/ansible-collection-nextcloud' + +# The URL to any online docs +# documentation: http://docs.example.com + +# The URL to the homepage of the collection/project +# homepage: http://example.com + +# The URL to the collection issue tracker +# issues: http://example.com/issue/tracker + +# A list of file glob-like patterns used to filter any files or directories that should not be included in the build +# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This +# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', +# and '.git' are always filtered. Mutually exclusive with 'manifest' +# build_ignore: [] + +# A dict controlling use of manifest directives used in building the collection artifact. The key 'directives' is a +# list of MANIFEST.in style +# L(directives,https://packaging.python.org/en/latest/guides/using-manifest-in/#manifest-in-commands). The key +# 'omit_default_directives' is a boolean that controls whether the default directives are used. Mutually exclusive +# with 'build_ignore' +# manifest: null diff --git a/meta/runtime.yml b/meta/runtime.yml new file mode 100644 index 0000000..d6c6f44 --- /dev/null +++ b/meta/runtime.yml @@ -0,0 +1,52 @@ +--- +# Collections must specify a minimum required ansible version to upload +# to galaxy +requires_ansible: '>=2.17.0' + +# Content that Ansible needs to load from another location or that has +# been deprecated/removed +# plugin_routing: +# action: +# redirected_plugin_name: +# redirect: ns.col.new_location +# deprecated_plugin_name: +# deprecation: +# removal_version: "4.0.0" +# warning_text: | +# See the porting guide on how to update your playbook to +# use ns.col.another_plugin instead. +# removed_plugin_name: +# tombstone: +# removal_version: "2.0.0" +# warning_text: | +# See the porting guide on how to update your playbook to +# use ns.col.another_plugin instead. +# become: +# cache: +# callback: +# cliconf: +# connection: +# doc_fragments: +# filter: +# httpapi: +# inventory: +# lookup: +# module_utils: +# modules: +# netconf: +# shell: +# strategy: +# terminal: +# test: +# vars: + +# Python import statements that Ansible needs to load from another location +# import_redirection: +# ansible_collections.ns.col.plugins.module_utils.old_location: +# redirect: ansible_collections.ns.col.plugins.module_utils.new_location + +# Groups of actions/modules that take a common set of options +# action_groups: +# group_name: +# - module1 +# - module2 diff --git a/playbooks/deployment.yml b/playbooks/deployment.yml new file mode 100644 index 0000000..d36a072 --- /dev/null +++ b/playbooks/deployment.yml @@ -0,0 +1,5 @@ +--- +- name: 'Deploy collection roles to matching host groups' + hosts: 'nextcloud' + roles: + - 'enbewe.nextcloud.nextcloud' diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 0000000..269a4c2 --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,31 @@ +# Collections Plugins Directory + +This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that +is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that +would contain module utils and modules respectively. + +Here is an example directory of the majority of plugins currently supported by Ansible: + +``` +└── plugins + ├── action + ├── become + ├── cache + ├── callback + ├── cliconf + ├── connection + ├── filter + ├── httpapi + ├── inventory + ├── lookup + ├── module_utils + ├── modules + ├── netconf + ├── shell + ├── strategy + ├── terminal + ├── test + └── vars +``` + +A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible-core/2.17/plugins/plugins.html). diff --git a/plugins/modules/nextcloud_app.py b/plugins/modules/nextcloud_app.py new file mode 100644 index 0000000..0304cde --- /dev/null +++ b/plugins/modules/nextcloud_app.py @@ -0,0 +1,279 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Nis Wechselberg +# MIT License (see https://spdx.org/licenses/MIT.html) +# Based upon code by s3lph +# MIT License (see https://spdx.org/licenses/MIT.html) + +from __future__ import (absolute_import, division, print_function) + +DOCUMENTATION = r''' +--- +module: nextcloud_app + +short_description: Enable or disable Nextcloud apps. + +version_added: "1.0.0" + +description: Enable or disable Nextcloud apps via C(occ app:) in the running container. + +options: + name: + description: Name of the app or apps to install/remove/enable/disable. + required: true + type: list + elements: str + state: + description: State the app or apps should be in. + required: false + default: enabled + type: str + choices: + - enabled + - disabled + - absent + force: + description: Install and enable apps that are not compatible with the current Nextcloud version. + required: false + default: false + type: bool + engine: + description: Engine to use for the OCI runtime. + required: false + default: podman + type: str + container_user: + description: The user to run the occ command as while in the container. + required: false + default: www-data + type: str + container_name: + description: The name of the container to run the command in. + required: false + default: nextcloud-app + type: str + +author: + - Nis Wechselberg +''' + +EXAMPLES = r''' +- name: Install and enable photos app + enbewe.nextcloud.nextcloud_app: + name: photos + +- name: Install and enable PIM apps + enbewe.nextcloud.nextcloud_app: + name: + - contacts + - calendar + - mail + +- name: Disable resource hungry dashboard + enbewe.nextcloud.nextcloud_app: + name: dashboard + state: disabled +''' + +RETURN = r''' +''' + +import json +import subprocess +from ansible.module_utils.basic import AnsibleModule + +def check_nextcloud_status(module, result): + ''' + Gather nextcloud status through calling `occ status`. + ''' + sc = subprocess.run([module.params['engine'], 'exec', + '--user', module.params['container_user'], + module.params['container_name'], + 'php', 'occ', 'status', '--output=json'], + capture_output=True, encoding='utf-8', check=False) + if sc.returncode != 0: + result['stdout'] = sc.stdout + result['stderr'] = sc.stderr + module.fail_json( + msg='occ status returned non-zero exit code. Run with -vvv to view the output', + **result) + + status = json.loads(sc.stdout) + if not status['installed']: + module.fail_json( + msg='Nextcloud installation has not been completed, so occ app is not available.', + **result) + +def get_app_status(module, result): + ''' + Gather state of installed apps in nextcloud. + ''' + # Gather Nextcloud app list + ac = subprocess.run([module.params['engine'], 'exec', + '--user', module.params['container_user'], + module.params['container_name'], + 'php', 'occ', 'app:list', '--output=json'], + capture_output=True, encoding='utf-8', check=False) + if ac.returncode != 0: + result['stdout'] = ac.stdout + result['stderr'] = ac.stderr + module.fail_json( + msg='occ app:list returned non-zero exit code. Run with -vvv to view the output', + **result) + + return json.loads(ac.stdout) + +def remove_app(module, result, app): + ''' + Remove the given app from the installation. + ''' + result['changed'] = True + if not module.check_mode: + c = subprocess.run([module.params['engine'], 'exec', + '--user', module.params['container_user'], + module.params['container_name'], + 'php', 'occ', 'app:remove', '--keep-data', app], + capture_output=True, encoding='utf-8', check=False) + if c.returncode != 0: + result['stdout'] = c.stdout + result['stderr'] = c.stderr + module.fail_json( + msg='occ app:remove returned non-zero exit code. Run with -vvv to view the output', + **result) + +def install_app(module, result, app): + ''' + Install the given app to the installation. + ''' + state = module.params['state'] + result['changed'] = True + if not module.check_mode: + cmdline = [module.params['engine'], 'exec', + '--user', module.params['container_user'], + module.params['container_name'], + 'php', 'occ', 'app:install'] + if state == 'disabled': + cmdline.append('--keep-disabled') + if module.params['force']: + cmdline.append('--force') + cmdline.append(app) + c = subprocess.run(cmdline, + capture_output=True, encoding='utf-8', check=False) + if c.returncode != 0: + result['stdout'] = c.stdout + result['stderr'] = c.stderr + module.fail_json( + msg='occ app:install returned non-zero exit code. Run with -vvv to view the output', + **result) + +def enable_app(module, result, app): + ''' + Enable the (already installed) app. + ''' + result['changed'] = True + if not module.check_mode: + cmdline = [module.params['engine'], 'exec', + '--user', module.params['container_user'], + module.params['container_name'], + 'php', 'occ', 'app:enable'] + if module.params['force']: + cmdline.append('--force') + cmdline.append(app) + c = subprocess.run(cmdline, + capture_output=True, encoding='utf-8', check=False) + if c.returncode != 0: + result['stdout'] = c.stdout + result['stderr'] = c.stderr + module.fail_json( + msg='occ app:enable returned non-zero exit code. Run with -vvv to view the output', + **result) + +def disable_app(module, result, app): + ''' + Disable the given app without removing it. + ''' + result['changed'] = True + if not module.check_mode: + c = subprocess.run([module.params['engine'], 'exec', + '--user', module.params['container_user'], + module.params['container_name'], + 'php', 'occ', 'app:disable', app], + capture_output=True, encoding='utf-8', check=False) + if c.returncode != 0: + result['stdout'] = c.stdout + result['stderr'] = c.stderr + module.fail_json( + msg='occ app:disable returned non-zero exit code. Run with -vvv to view the output', + **result) + +def apply_app_status(module, result, app_status): + ''' + Apply the status from the configuration to the applications in nextcloud. + ''' + state = module.params['state'] + apps = module.params['name'] + if isinstance(apps, str): + apps = [apps] + + # Apply app configuration changes + for app in apps: + if state == 'absent' and (app in app_status['enabled'] or app in app_status['disabled']): + remove_app(module, result, app) + elif (state in ['enabled', 'disabled'] + and app not in app_status['enabled'] + and app not in app_status['disabled']): + install_app(module, result, app) + elif state == 'enabled' and app in app_status['disabled']: + enable_app(module, result, app) + elif state == 'disabled' and app in app_status['enabled']: + disable_app(module, result, app) + +def run_module(): + ''' + Run the ansible module code. + ''' + # define available arguments/parameters a user can pass to the module + module_args = { + 'name': { 'required': True, 'type': 'list' }, + 'state': { 'required': False, 'default': 'enabled', 'type': 'str' }, + 'force': { 'required': False, 'default': False, 'type': 'bool' }, + 'engine': { 'required': False, 'default': 'podman', 'type': 'str' }, + 'container_user': { 'required': False, 'default': 'www-data', 'type': 'str' }, + 'container_name': { 'required': False, 'default': 'nextcloud-app', 'type': 'str' }, + } + + # seed the result dict in the object + # we primarily care about changed and state + # changed is if this module effectively modified the target + # state will include any data that you want your module to pass back + # for consumption, for example, in a subsequent task + result = { 'changed': False } + + # the AnsibleModule object will be our abstraction working with Ansible + # this includes instantiation, a couple of common attr would be the + # args/params passed to the execution, as well as if the module + # supports check mode + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + if module.params['state'] not in ['enabled', 'disabled', 'absent']: + module.fail_json(msg='state must be one of enabled, disabled or absent', **result) + + check_nextcloud_status(module, result) + app_status = get_app_status(module, result) + apply_app_status(module, result, app_status) + + module.exit_json(**result) + + +def main(): + ''' + The main method called for the module. + ''' + run_module() + +if __name__ == '__main__': + main() diff --git a/roles/nextcloud/defaults/main.yml b/roles/nextcloud/defaults/main.yml new file mode 100644 index 0000000..863c102 --- /dev/null +++ b/roles/nextcloud/defaults/main.yml @@ -0,0 +1,20 @@ +--- +nextcloud_podman_network: 'nextcloud' + +nextcloud_db_container_name: nextcloud-db +nextcloud_db_image_name: 'docker.io/library/postgres' +nextcloud_db_image_tag: '16' +nextcloud_db_volume: 'nextcloud-db' + +nextcloud_redis_container_name: nextcloud-redis +nextcloud_redis_image_name: 'docker.io/library/redis' +nextcloud_redis_image_tag: 'alpine' + +nextcloud_cron_container_name: 'nextcloud-cron' + +nextcloud_app_listen_port: '127.0.0.1:8085' +nextcloud_app_container_name: 'nextcloud-app' +nextcloud_app_image_name: 'docker.io/library/nextcloud' +nextcloud_app_image_tag: '29-apache' +nextcloud_app_volume: 'nextcloud-app' +nextcloud_app_environments: [] diff --git a/roles/nextcloud/handlers/main.yml b/roles/nextcloud/handlers/main.yml new file mode 100644 index 0000000..f235b7d --- /dev/null +++ b/roles/nextcloud/handlers/main.yml @@ -0,0 +1,34 @@ +--- +- name: 'Restart nextcloud-db container' + become: true + ansible.builtin.service: + name: 'container-nextcloud-db' + state: 'restarted' + daemon_reload: true + notify: + - 'Restart nextcloud-app container' + - 'Restart nextcloud-cron container' + +- name: 'Restart nextcloud-redis container' + become: true + ansible.builtin.service: + name: 'container-nextcloud-redis' + state: 'restarted' + daemon_reload: true + notify: + - 'Restart nextcloud-app container' + - 'Restart nextcloud-cron container' + +- name: 'Restart nextcloud-app container' + become: true + ansible.builtin.service: + name: 'container-nextcloud-app' + state: 'restarted' + daemon_reload: true + +- name: 'Restart nextcloud-cron container' + become: true + ansible.builtin.service: + name: 'container-nextcloud-cron' + state: 'restarted' + daemon_reload: true diff --git a/roles/nextcloud/tasks/main.yml b/roles/nextcloud/tasks/main.yml new file mode 100644 index 0000000..d760026 --- /dev/null +++ b/roles/nextcloud/tasks/main.yml @@ -0,0 +1,93 @@ +--- +- name: 'Ensure needed software is installed' + become: true + ansible.builtin.apt: + name: 'podman' + state: 'present' + update_cache: true + cache_valid_time: 3600 + +- name: 'Prepare network' + become: true + containers.podman.podman_network: + name: '{{ nextcloud_podman_network }}' + state: 'present' + +- name: 'Prepare nextcloud config files location' + become: true + ansible.builtin.file: + path: '/etc/nextcloud' + state: 'directory' + owner: 'root' + group: 'root' + mode: 'u=rwx,g=rx,o=rx' + +- name: 'Create config files for nextcloud' + become: true + ansible.builtin.template: + src: '{{ item.src }}' + dest: '{{ item.dest }}' + owner: 33 + group: 33 + mode: '{{ item.mode }}' + notify: 'Restart nextcloud-app container' + loop: + - src: 'nextcloud/oidc.config.php.j2' + dest: '/etc/nextcloud/oidc.config.php' + mode: 'u=rw,g=r,o=r' + - src: 'nextcloud/ansible.config.php.j2' + dest: '/etc/nextcloud/ansible.config.php' + mode: 'u=rw,g=r,o=r' + - src: 'nextcloud/copy_config.sh.j2' + dest: '/etc/nextcloud/copy_config.sh' + mode: 'u=rwx,g=rx,o=rx' + +- name: 'Create service files' + become: true + ansible.builtin.template: + src: '{{ item.src }}' + dest: '{{ item.dest }}' + owner: 'root' + group: 'root' + mode: 'u=rw,g=r,o=r' + notify: '{{ item.notify }}' + loop: + - src: 'systemd/container-nextcloud-db.service.j2' + dest: '/etc/systemd/system/container-nextcloud-db.service' + notify: 'Restart nextcloud-db container' + - src: 'systemd/container-nextcloud-redis.service.j2' + dest: '/etc/systemd/system/container-nextcloud-redis.service' + notify: 'Restart nextcloud-redis container' + - src: 'systemd/container-nextcloud-cron.service.j2' + dest: '/etc/systemd/system/container-nextcloud-cron.service' + notify: 'Restart nextcloud-cron container' + - src: 'systemd/container-nextcloud-app.service.j2' + dest: '/etc/systemd/system/container-nextcloud-app.service' + notify: 'Restart nextcloud-app container' + +- name: 'Start and enable services' + become: true + ansible.builtin.systemd: + name: '{{ item }}' + state: 'started' + daemon_reload: true + enabled: true + loop: + - 'container-nextcloud-db.service' + - 'container-nextcloud-redis.service' + - 'container-nextcloud-cron.service' + - 'container-nextcloud-app.service' + +- name: 'Configure apps' + become: true + enbewe.nextcloud.nextcloud_app: + state: '{{ item.key }}' + name: '{{ item.value }}' + force: true + container_name: '{{ nextcloud_app_container_name }}' + retries: 30 + delay: 10 + notify: + - 'Restart nextcloud-cron container' + - 'Restart nextcloud-app container' + loop: '{{ nextcloud_apps | dict2items }}' diff --git a/roles/nextcloud/templates/nextcloud/ansible.config.php.j2 b/roles/nextcloud/templates/nextcloud/ansible.config.php.j2 new file mode 100644 index 0000000..bfd7602 --- /dev/null +++ b/roles/nextcloud/templates/nextcloud/ansible.config.php.j2 @@ -0,0 +1,8 @@ + {{ nextcloud_oidc_allow_user_change_display_name | default('false') }}, + 'skeletondirectory' => '', + 'templatedirectory' => '', + 'hide_login_form' => {{ nextcloud_oidc_hide_password_form | default('false') }}, + 'maintenance_window_start' => {{ nextcloud_maintenance_window_start | default(2) }}, +); \ No newline at end of file diff --git a/roles/nextcloud/templates/nextcloud/copy_config.sh.j2 b/roles/nextcloud/templates/nextcloud/copy_config.sh.j2 new file mode 100644 index 0000000..13d4d04 --- /dev/null +++ b/roles/nextcloud/templates/nextcloud/copy_config.sh.j2 @@ -0,0 +1,3 @@ +#!/bin/bash + +cp /docker-entrypoint-hooks.d/before-starting/*.config.php /var/www/html/config/ diff --git a/roles/nextcloud/templates/nextcloud/oidc.config.php.j2 b/roles/nextcloud/templates/nextcloud/oidc.config.php.j2 new file mode 100644 index 0000000..774071c --- /dev/null +++ b/roles/nextcloud/templates/nextcloud/oidc.config.php.j2 @@ -0,0 +1,123 @@ + {{ nextcloud_oidc_allow_user_change_display_name | default('false') }}, + 'lost_password_link' => '{{ nextcloud_oidc_lost_password_link | default('disabled') }}', + + // URL of provider. All other URLs are auto-discovered from .well-known + 'oidc_login_provider_url' => '{{ nextcloud_oidc_provider_url }}', + + // Client ID and secret registered with the provider + 'oidc_login_client_id' => '{{ nextcloud_oidc_client_id }}', + 'oidc_login_client_secret' => '{{ nextcloud_oidc_client_secret }}', + + // Automatically redirect the login page to the provider + 'oidc_login_auto_redirect' => {{ nextcloud_oidc_auto_redirect | default('false') }}, + + // Redirect to this page after logging out the user + 'oidc_login_logout_url' => '{{ nextcloud_oidc_logout_url | default('') }}', + + // If set to true the user will be redirected to the logout endpoint of the OIDC provider after logout + // in Nextcloud. After successfull logout the OIDC provider will redirect back to 'oidc_login_logout_url' (MUST be set). + 'oidc_login_end_session_redirect' => {{ nextcloud_oidc_end_session_redirect | default('false') }}, + + // Login button text + 'oidc_login_button_text' => '{{ nextcloud_oidc_button_text | default('Log in with Open ID') }}', + + // Hide the NextCloud password change form. + 'oidc_login_hide_password_form' => {{ nextcloud_oidc_hide_password_form | default('false') }}, + + // Use ID Token instead of UserInfo + 'oidc_login_use_id_token' => {{ nextcloud_oidc_use_id_token | default('false') }}, + + // Attribute map for OIDC response. Available keys are: + // * id: Unique identifier for username + // * name: Full name + // If set to null, existing display name won't be overwritten + // * mail: Email address + // If set to null, existing email address won't be overwritten + // * quota: Nextcloud storage quota + // * home: Home directory location. A symlink or external storage to this location is used + // * ldap_uid: LDAP uid to search for when running in proxy mode + // * groups: Array or space separated string of Nextcloud groups for the user. + // Note that the name here corresponds to the GID of the group and not the display name + // In the admin panel, the GID may be obtained from the URL when editing a group + // * login_filter: Array or space separated string. If 'oidc_login_filter_allowed_values' is + // set, it is checked against these values. + // * photoURL: The URL of the user avatar. The nextcloud server will download the picture + // at user login. This may lead to security issues. Use with care. + // This will only be effective if oidc_login_update_avatar is enabled. + // * is_admin: If this value is truthy, the user is added to the admin group (optional) + 'oidc_login_attributes' => array ( + 'id' => 'sub', + 'name' => 'name', + 'mail' => 'email', + 'groups' => 'groups', + 'is_admin' => 'groups_cloud_admin', + ), + + // Allow only users in configured value(s) to access Nextcloud. In case the user + // is not assigned to this value (read from oidc_login_attributes) the login + // will not be allowed for this user. + // + // Must be specified as an array of values (e.g. roles) that are allowed to + // access Nextcloud. e.g. 'oidc_login_filter_allowed_values' => array('role1', 'role2') + 'oidc_login_filter_allowed_values' => null, + + // Set OpenID Connect scope + 'oidc_login_scope' => '{{ nextcloud_oidc_scope | default('openid profile') }}', + + // Disable creation of users new to Nextcloud from OIDC login. + // A user may be known to the IdP but not (yet) known to Nextcloud. + // This setting controls what to do in this case. + // - 'true' (default): if the user authenticates to the IdP but is not known to Nextcloud, + // then they will be returned to the login screen and not allowed entry; + // - 'false': if the user authenticates but is not yet known to Nextcloud, + // then the user will be automatically created; note that with this setting, + // you will be allowing (or relying on) a third-party (the IdP) to create new users + 'oidc_login_disable_registration' => {{ nextcloud_oidc_disable_registration | default('true') }}, + + // For development, you may disable TLS verification. Default value is `true` + // which should be kept in production + 'oidc_login_tls_verify' => {{ nextcloud_oidc_tls_verify | default('true') }}, + + // If you get your groups from the oidc_login_attributes, you might want + // to create them if they are not already existing, Default is `false`. + 'oidc_create_groups' => {{ nextcloud_oidc_create_groups | default('false') }}, + + // Enable use of WebDAV via OIDC bearer token. + 'oidc_login_webdav_enabled' => false, + + // Enable authentication with user/password for DAV clients that do not + // support token authentication (e.g. DAVx⁵) + 'oidc_login_password_authentication' => false, + + // The time in seconds used to cache public keys from provider. + // The default value is 1 day. + 'oidc_login_public_key_caching_time' => 86400, + + // The minimum time in seconds to wait between requests to the jwks_uri endpoint. + // Avoids that the provider will be DoSed when someone requests with unknown kids. + // The default is 10 seconds. + 'oidc_login_min_time_between_jwks_requests' => 10, + + // The time in seconds used to cache the OIDC well-known configuration from the provider. + // The default value is 1 day. + 'oidc_login_well_known_caching_time' => 86400, + + // If true, nextcloud will download user avatars on login. + // This may lead to security issues as the server does not control + // which URLs will be requested. Use with care. + 'oidc_login_update_avatar' => false, + + // If true, the default Nextcloud proxy won't be used to make internals OIDC call. + // The default is false. + 'oidc_login_skip_proxy' => false, + + // Code challenge method for PKCE flow. + // Possible values are: + // - 'S256' + // - 'plain' + // The default value is empty, which won't apply the PKCE flow. + 'oidc_login_code_challenge_method' => '', +); \ No newline at end of file diff --git a/roles/nextcloud/templates/systemd/container-nextcloud-app.service.j2 b/roles/nextcloud/templates/systemd/container-nextcloud-app.service.j2 new file mode 100644 index 0000000..acdaec4 --- /dev/null +++ b/roles/nextcloud/templates/systemd/container-nextcloud-app.service.j2 @@ -0,0 +1,49 @@ +[Unit] +Description=Podman container-nextcloud-app.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +After=container-nextcloud-db.service +After=container-nextcloud-redis.service +RequiresMountsFor=%t/containers + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +Type=notify +NotifyAccess=all + +ExecStartPre=/bin/rm -f %t/%n.ctr-id + +ExecStart=/usr/bin/podman run \ + --cidfile=%t/%n.ctr-id \ + --cgroups=no-conmon \ + --rm \ + --sdnotify=conmon \ + --replace \ + --network={{ nextcloud_podman_network }} \ + --detach \ + --tty \ + --publish {{ nextcloud_app_listen_port }}:80 \ + --volume {{ nextcloud_app_volume }}:/var/www/html:Z \ + --volume /etc/nextcloud/oidc.config.php:/docker-entrypoint-hooks.d/before-starting/oidc.config.php:Z \ + --volume /etc/nextcloud/ansible.config.php:/docker-entrypoint-hooks.d/before-starting/ansible.config.php:Z \ + --volume /etc/nextcloud/copy_config.sh:/docker-entrypoint-hooks.d/before-starting/copy_config.sh:Z \ +{% for environment in nextcloud_app_environments %} + --env {{ environment.key }}={{ environment.value }} \ +{% endfor %} + --name={{ nextcloud_app_container_name }} \ + {{ nextcloud_app_image_name }}:{{ nextcloud_app_image_tag }} + +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id + +ExecStopPost=/usr/bin/podman rm \ + --force \ + --ignore \ + --cidfile=%t/%n.ctr-id + +[Install] +WantedBy=default.target diff --git a/roles/nextcloud/templates/systemd/container-nextcloud-cron.service.j2 b/roles/nextcloud/templates/systemd/container-nextcloud-cron.service.j2 new file mode 100644 index 0000000..18afc79 --- /dev/null +++ b/roles/nextcloud/templates/systemd/container-nextcloud-cron.service.j2 @@ -0,0 +1,45 @@ +[Unit] +Description=Podman container-nextcloud-cron.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +After=container-nextcloud-db.service +After=container-nextcloud-redis.service +RequiresMountsFor=%t/containers + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +Type=notify +NotifyAccess=all + +ExecStartPre=/bin/rm -f %t/%n.ctr-id + +ExecStart=/usr/bin/podman run \ + --cidfile=%t/%n.ctr-id \ + --cgroups=no-conmon \ + --rm \ + --sdnotify=conmon \ + --replace \ + --network={{ nextcloud_podman_network }} \ + --detach \ + --tty \ + --volume {{ nextcloud_app_volume }}:/var/www/html:Z \ + --volume /etc/nextcloud/oidc.config.php:/docker-entrypoint-hooks.d/before-starting/oidc.config.php:Z \ + --volume /etc/nextcloud/copy_oidc_config.sh:/docker-entrypoint-hooks.d/before-starting/copy_oidc_config.sh:Z \ + --name={{ nextcloud_cron_container_name }} \ + --entrypoint=/cron.sh \ + {{ nextcloud_app_image_name }}:{{ nextcloud_app_image_tag }} + +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id + +ExecStopPost=/usr/bin/podman rm \ + --force \ + --ignore \ + --cidfile=%t/%n.ctr-id + +[Install] +WantedBy=default.target diff --git a/roles/nextcloud/templates/systemd/container-nextcloud-db.service.j2 b/roles/nextcloud/templates/systemd/container-nextcloud-db.service.j2 new file mode 100644 index 0000000..db47b01 --- /dev/null +++ b/roles/nextcloud/templates/systemd/container-nextcloud-db.service.j2 @@ -0,0 +1,42 @@ +[Unit] +Description=Podman container-nextcloud-db.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=%t/containers + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +Type=notify +NotifyAccess=all + +ExecStartPre=/bin/rm -f %t/%n.ctr-id + +ExecStart=/usr/bin/podman run \ + --cidfile=%t/%n.ctr-id \ + --cgroups=no-conmon \ + --rm \ + --sdnotify=conmon \ + --replace \ + --detach \ + --tty \ + --network={{ nextcloud_podman_network }} \ + --volume {{ nextcloud_db_volume }}:/var/lib/postgresql/data/:Z \ + --env POSTGRES_USER={{ nextcloud_db_user }} \ + --env POSTGRES_PASSWORD={{ nextcloud_db_password }} \ + --env POSTGRES_DB={{ nextcloud_db_database }} \ + --name={{ nextcloud_db_container_name }} \ + {{ nextcloud_db_image_name }}:{{ nextcloud_db_image_tag }} + +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id + +ExecStopPost=/usr/bin/podman rm \ + --force --ignore \ + --cidfile=%t/%n.ctr-id + +[Install] +WantedBy=default.target diff --git a/roles/nextcloud/templates/systemd/container-nextcloud-redis.service.j2 b/roles/nextcloud/templates/systemd/container-nextcloud-redis.service.j2 new file mode 100644 index 0000000..4ed157e --- /dev/null +++ b/roles/nextcloud/templates/systemd/container-nextcloud-redis.service.j2 @@ -0,0 +1,39 @@ +[Unit] +Description=Podman container-nextcloud-redis.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=%t/containers + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +Type=notify +NotifyAccess=all + +ExecStartPre=/bin/rm -f %t/%n.ctr-id + +ExecStart=/usr/bin/podman run \ + --cidfile=%t/%n.ctr-id \ + --cgroups=no-conmon \ + --rm \ + --sdnotify=conmon \ + --replace \ + --detach \ + --tty \ + --network={{ nextcloud_podman_network }} \ + --name={{ nextcloud_redis_container_name }} \ + --volume nextcloud-redis:/data:Z \ + {{ nextcloud_redis_image_name }}:{{ nextcloud_redis_image_tag }} + +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id + +ExecStopPost=/usr/bin/podman rm \ + --force --ignore \ + --cidfile=%t/%n.ctr-id + +[Install] +WantedBy=default.target