diff --git a/playbooks/xsrv/host_vars/my.example.org/my.example.org.yml b/playbooks/xsrv/host_vars/my.example.org/my.example.org.yml index 1ecc0bef3..e63ebc7a1 100644 --- a/playbooks/xsrv/host_vars/my.example.org/my.example.org.yml +++ b/playbooks/xsrv/host_vars/my.example.org/my.example.org.yml @@ -2,6 +2,7 @@ ### configuration variables for my.example.org ### vault_* variables are stored encrypted in my.example.org.vault.yml + # administrator/sudo account username/password ansible_user: "{{ vault_ansible_user }}" ansible_become_pass: "{{ vault_ansible_become_pass }}" @@ -13,6 +14,7 @@ xsrv_admin_password: "{{ vault_xsrv_admin_password }}" ##### NEXTCLOUD ##### # see roles/nextcloud/defaults/main.yml +nextcloud_fqdn: "cloud.CHANGEME.org" nextcloud_user: "{{ xsrv_admin_username }}" nextcloud_password: "{{ xsrv_admin_password }}" diff --git a/roles/nextcloud/README.md b/roles/nextcloud/README.md index 6a3c1c114..0273fd150 100644 --- a/roles/nextcloud/README.md +++ b/roles/nextcloud/README.md @@ -19,6 +19,14 @@ Default installed applications include: Nextcloud is an alternative to services such as Dropbox, Google Drive/Agenda... See the [comparison page](https://nextcloud.com/compare/). +[![](https://i.imgur.com/kQyXV9S.png)](https://i.imgur.com/nCXJMus.png) +[![](https://i.imgur.com/lXroRsI.png)](https://i.imgur.com/XlDrlS4.png) +[![](https://i.imgur.com/cCg6HgB.png)](https://i.imgur.com/iuWdvKG.png) +[![](https://i.imgur.com/URs7XH5.png)](https://i.imgur.com/V6CR3we.png) +[![](https://i.imgur.com/0ALCk1W.png)](https://i.imgur.com/qRYPBdU.png) +[![](https://i.imgur.com/PPVIb6V.png)](https://i.imgur.com/1YaT357.png) +[![](https://i.imgur.com/Co3DHUr.png)](https://i.imgur.com/Tu1lVHo.png) +[![](https://i.imgur.com/TJTvqtd.png)](https://i.imgur.com/ztI0rJz.png) Requirements ------------ @@ -35,7 +43,7 @@ See [defaults/main.yml](defaults/main.yml) Dependencies ------------ -The [`lamp`](https://gitlab.com/nodiscc/ansible-xsrv-lamp) role +The [`lamp`](../lamp/README.md) role Example Playbook @@ -47,6 +55,13 @@ Example Playbook - common - lamp - nextcloud + vars: + nextcloud_fqdn: "cloud.CHANGEME.org" + nextcloud_user: "CHANGEME" + +# ansible-vault edit host_vars/my.example.org/my.example.org.vault.yml +vault_nextcloud_password: "CHANGEME" +vault_nextcloud_db_password: "CHANGEME" ``` @@ -78,7 +93,7 @@ Other: ### Backups -See the included [rsnapshot configuration](templates/etc_rsnapshot.d_nextcloud.conf.j2) for the [backup](https://gitlab.com/nodiscc/ansible-xsrv-backup) role. +See the included [rsnapshot configuration](templates/etc_rsnapshot.d_nextcloud.conf.j2) for the [backup](../backup/README.md) role. To restore a backup: @@ -94,10 +109,10 @@ rm -rv /var/nextcloud/data # Reinstall nextcloud by running the playbook/nextcloud role, then # Restore the database -mysql -u root -p nextcloud < /var/backups/xsrv/daily.0/localhost/var/backups/mysql/nextcloud/nextcloud.sql +mysql -u root -p nextcloud < /var/backups/rsnapshot/daily.0/localhost/var/backups/mysql/nextcloud/nextcloud.sql # Restore the data directory -rsync -avP --delete /var/backups/xsrv/daily.0/localhost/var/nextcloud/data /var/nextcloud/ +rsync -avP --delete /var/backups/rsnapshot/daily.0/localhost/var/nextcloud/data /var/nextcloud/ # Rescan files sudo -u www-data /usr/bin/php /var/www/my.example.org/nextcloud/occ files:scan diff --git a/roles/nextcloud/defaults/main.yml b/roles/nextcloud/defaults/main.yml index b25c1dade..b47c4cdd7 100644 --- a/roles/nextcloud/defaults/main.yml +++ b/roles/nextcloud/defaults/main.yml @@ -3,11 +3,15 @@ ##### NEXTCLOUD ##### # Nextcloud admin username/password -# nextcloud_user: "CHANGEME" -# nextcloud_password: "CHANGEME" +nextcloud_user: "{{ vault_nextcloud_user }}" # CHANGEME +nextcloud_password: "{{ vault_nextcloud_password }}" # CHANGEME # Fully Qualified Domain Name for the nextcloud instance -nextcloud_fqdn: "{{ inventory_hostname }}" +# nextcloud_fqdn: "cloud.CHANGEME.org" +# Nextcloud installation directory (must be under a valid documentroot) +nextcloud_install_dir: "/var/www/{{ nextcloud_fqdn }}" +# full public URL of your tt-rss installation (update this if you changed the install location to a subdirectory) +nextcloud_full_url: "https://{{ nextcloud_fqdn }}/" # nextcloud data storage directory nextcloud_data_dir: "/var/nextcloud/data" @@ -17,16 +21,14 @@ nextcloud_db_name: "nextcloud" nextcloud_db_user: "nextcloud" nextcloud_db_table_prefix: "oc_" nextcloud_db_host: "localhost" -nextcloud_db_password: "{{ lookup('password', 'secrets/' + inventory_hostname + '.nextcloud_db_password length=18') }}" +nextcloud_db_password: "{{ vault_nextcloud_db_password }}" # CHANGEME -# Nextcloud installation directory (must be under a valid documentroot) -nextcloud_install_dir: "/var/www/{{ nextcloud_fqdn }}/nextcloud" # Nextcloud and applications versions # https://github.com/nextcloud/server/releases.atom, remove leading v nextcloud_version: '17.0.2' # https://github.com/nextcloud/calendar/releases.atom -nextcloud_calendar_version: 'v2.0.0' +nextcloud_calendar_version: 'v2.0.2' # https://github.com/nextcloud/contacts/releases.atom nextcloud_contacts_version: 'v3.1.7' # https://github.com/nextcloud/tasks/releases.atom diff --git a/roles/nextcloud/tasks/apache.yml b/roles/nextcloud/tasks/apache.yml new file mode 100644 index 000000000..9207510b7 --- /dev/null +++ b/roles/nextcloud/tasks/apache.yml @@ -0,0 +1,25 @@ +--- + +- name: enable apache2 modules + command: a2enmod {{ item }} + with_items: + - 'rewrite' + - 'headers' + - 'env' + - 'dir' + - 'mime' + args: + creates: "/etc/apache2/mods-enabled/{{ item }}.load" + notify: reload apache + +- name: copy apache2 virtualhost configuration + template: + src: etc_apache2_sites-available_nextcloud.conf.j2 + dest: /etc/apache2/sites-available/nextcloud.conf + notify: reload apache + +- name: enable apache2 virtualhost + command: a2ensite nextcloud + args: + creates: "/etc/apache2/sites-enabled/nextcloud.conf" + notify: restart apache diff --git a/roles/nextcloud/tasks/main.yml b/roles/nextcloud/tasks/main.yml index 35ffa348d..f30bddf0c 100644 --- a/roles/nextcloud/tasks/main.yml +++ b/roles/nextcloud/tasks/main.yml @@ -4,3 +4,8 @@ become: yes tags: - nextcloud + +- include: apache.yml + become: yes + tags: + - nextcloud diff --git a/roles/nextcloud/tasks/nextcloud.yml b/roles/nextcloud/tasks/nextcloud.yml index 29316f996..316ccf3d7 100644 --- a/roles/nextcloud/tasks/nextcloud.yml +++ b/roles/nextcloud/tasks/nextcloud.yml @@ -2,7 +2,7 @@ ##### PACKAGES ##### -- name: install required packages for nextcloud +- name: install required packages apt: state: present update_cache: yes @@ -19,32 +19,13 @@ - php-mbstring - php-xml - php-zip + - php-bz2 + - php-smbclient + - php-imap + - php-apcu + - ffmpeg - acl - notify: restart apache - - -##### DOWNLOADS ##### - -- name: download nextcloud zip - get_url: - url: "https://download.nextcloud.com/server/releases/nextcloud-{{ nextcloud_version }}.zip" - dest: "/root/nextcloud-{{ nextcloud_version }}.zip" - -- name: download nextcloud applications - get_url: - url: "{{ item.url }}" - dest: "{{ item.dest }}" - with_items: - - { url: 'https://github.com/nextcloud/calendar/releases/download/{{ nextcloud_calendar_version }}/calendar.tar.gz', - dest: '/root/nextcloud-calendar-{{ nextcloud_calendar_version }}.tar.gz'} - - { url: 'https://github.com/nextcloud/contacts/releases/download/{{ nextcloud_contacts_version }}/contacts.tar.gz', - dest: '/root/nextcloud-contacts-{{ nextcloud_contacts_version }}.tar.gz'} - - { url: 'https://github.com/nextcloud/tasks/releases/download/{{ nextcloud_tasks_version }}/tasks.tar.gz', - dest: '/root/nextcloud-tasks-{{ nextcloud_tasks_version }}.tar.gz'} - - { url: 'https://github.com/owncloud/music/releases/download/v{{ nextcloud_music_version }}/music.zip', - dest: '/root/music.zip'} - - { url: 'https://github.com/nextcloud/notes/releases/download/{{ nextcloud_notes_version }}/notes.tar.gz', - dest: '/root/notes.tar.gz'} + notify: reload apache ##### DATABASE ##### @@ -64,31 +45,7 @@ state: present priv: "{{ nextcloud_db_name }}.*:ALL" login_password: "{{ mariadb_root_password }}" - - -##### APACHE ##### - -- name: enable apache2 modules env, dir, mime - command: a2enmod {{ item }} - with_items: - - 'env' - - 'dir' - - 'mime' - args: - creates: "/etc/apache2/mods-enabled/{{ item }}.load" - notify: restart apache - -- name: setup nextcloud content-security-policy - template: - src: etc_apache2_conf-available_nextcloud-csp.conf.j2 - dest: /etc/apache2/conf-available/nextcloud-csp.conf - notify: restart apache - -- name: enable content security policy config - command: a2enconf nextcloud-csp - args: - creates: "/etc/apache2/conf-enabled/nextcloud-csp.conf" - notify: restart apache + no_log: True ###### FAIL2BAN ########## @@ -120,86 +77,117 @@ src: etc_rsnapshot.d_nextcloud.conf.j2 dest: /etc/rsnapshot.d/nextcloud.conf -##### FILES IN WEBROOT ##### -- name: extract nextcloud zip - unarchive: - src: "/root/nextcloud-{{ nextcloud_version }}.zip" - dest: "{{ nextcloud_install_dir | dirname }}" - copy: no - owner: www-data - group: www-data - creates: "{{ nextcloud_install_dir }}/occ" +##### NEXTCLOUD INSTALLATION ##### -- name: extract nextcloud applications - unarchive: - src: "{{ item.archive }}" - dest: "{{ item.dest }}" - copy: no - owner: www-data - group: www-data - with_items: - - { archive: '/root/nextcloud-calendar-{{ nextcloud_calendar_version }}.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} - - { archive: '/root/nextcloud-contacts-{{ nextcloud_contacts_version }}.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} - - { archive: '/root/nextcloud-tasks-{{ nextcloud_tasks_version }}.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} # Not idempotent - - { archive: '/root/music.zip', dest: '{{ nextcloud_install_dir }}/apps/'} - - { archive: '/root/notes.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} +- name: check nextcloud if installation directory exists + stat: + path: "{{ nextcloud_install_dir }}" + register: nextcloud_dir + +- name: abort on unsupported conditions + fail: + msg: "ERROR: unsupported conditions: nextcloud_dir.stat.exists: {{ nextcloud_dir.stat.exists }}, but ansible_local.nextcloud.installed.version says otherwise" + when: '((ansible_local.nextcloud.installed.version is defined) and (not nextcloud_dir.stat.exists)) or + ((ansible_local.nextcloud.installed.version is undefined) and (nextcloud_dir.stat.exists))' + +- name: check if initial installation should be performed + set_fact: + nextcloud_action: initial + when: '(ansible_local.nextcloud.installed.version is undefined) and (not nextcloud_dir.stat.exists)' + +- name: check if upgrade should be performed + set_fact: + nextcloud_action: upgrade + when: '(ansible_local.nextcloud.installed.version is defined) and (ansible_local.nextcloud.installed.version < nextcloud_version) and (nextcloud_dir.stat.exists)' +#- debug: +# msg: "action: {{ nextcloud_action }}, nextcloud_dir.stat.exists: {{ nextcloud_dir.stat.exists }}, nextcloud_version: {{ nextcloud_version }}" -###### FILE PERMISSIONS ###### +- name: download nextcloud zip + get_url: + url: "https://download.nextcloud.com/server/releases/nextcloud-{{ nextcloud_version }}.zip" + dest: "/root/nextcloud-{{ nextcloud_version }}.zip" + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action == 'upgrade' -- name: create nextcloud data directory +- name: remove nextcloud zip extraction directory file: - path: "/var/nextcloud/data" - state: directory - owner: www-data - group: www-data - mode: 0750 + path: /root/nextcloud-unpack + state: absent + when: + - nextcloud_action is defined + - nextcloud_action == 'upgrade' -# Not idempotent -- name: set nextcloud files ownership to root:www-data +- name: create nextcloud zip extraction directory file: - path: "{{ nextcloud_install_dir }}" + path: /root/nextcloud-unpack state: directory - owner: root - group: www-data - recurse: yes + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' -# Not idempotent -- name: ensure files are mode 0640 and directories mode 0750 - file: - path: "{{ nextcloud_install_dir }}" - mode: "u=rwX,g=rX" - recurse: yes +- name: extract nextcloud zip + unarchive: + src: "/root/nextcloud-{{ nextcloud_version }}.zip" + dest: "/root/nextcloud-unpack" + remote_src: yes + owner: root + group: root + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' -# Not idempotent -- name: give nextcloud write access to required directories +- name: give write permissions to the webserver file: path: "{{ item }}" - state: directory - owner: www-data group: www-data - recurse: yes + mode: "g+w" + recurse: no with_items: - - "{{ nextcloud_install_dir }}/apps" - - "{{ nextcloud_install_dir }}/config" - - "{{ nextcloud_install_dir }}/data" - - "{{ nextcloud_install_dir }}/theme" - - "{{ nextcloud_install_dir }}/updater" - - "{{ nextcloud_install_dir }}/assets" - - "{{ nextcloud_data_dir }}" - -- name: set execute permission for occ command line tool + - "/root/nextcloud-unpack/nextcloud/apps" + - "/root/nextcloud-unpack/nextcloud/config" + - "/root/nextcloud-unpack/nextcloud/themes" + #- "/root/nextcloud-unpack/nextcloud/updater" + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' + +- name: give exexecute permissions to the webserver on occ command-line tool file: - path: "{{ nextcloud_install_dir }}/occ" - state: file - mode: 0750 + path: "/root/nextcloud-unpack/nextcloud/occ" + mode: g+X + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' +- name: create/set permissions on nextcloud data directory + file: + path: "/var/nextcloud/data" + state: directory + owner: root + group: www-data + mode: u=rwX,g=rwX + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' -##### SETUP ##### - -- name: nextcloud first setup - become: True +- name: move old nextcloud installation to temporary dir + shell: mv '{{ nextcloud_install_dir }}' '{{ nextcloud_install_dir }}.old' + args: + removes: '{{ nextcloud_install_dir }}' + when: + - nextcloud_action is defined + - nextcloud_action ==' upgrade' + +- name: move nextcloud extraction directory to install directory + shell: mv /root/nextcloud-unpack/nextcloud '{{ nextcloud_install_dir }}' + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' + +- name: run first setup (this can take a while) become_user: www-data command: /usr/bin/php occ \ maintenance:install \ @@ -217,15 +205,11 @@ chdir: "{{ nextcloud_install_dir }}" failed_when: nextcloud_first_setup.rc !=0 and 'Command "maintenance:install" is not defined.' not in nextcloud_first_setup.stderr changed_when: nextcloud_first_setup.rc == 0 + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' -# trashbin_retention_obligation: -# auto: keep for 30 days, then delete IF space needed -# D, auto: keep at least D+ days, then delete IF space needed -# auto, D: max D days, but delete if space needed -# D1, D2: keep lat east D1 days and max D2 days -# disabled: auto clean disabled - -# TODO OPTIMIZE: not idempotent, this task will return changed every time (ignored with noqa 301, occ doesn't report indication of changes) +# TODO: not idempotent, this task will return changed every time (ignored with noqa 301, occ doesn't report indication of changes) - name: set nextcloud domain config.php values become: yes become_user: www-data @@ -234,7 +218,7 @@ chdir: "{{ nextcloud_install_dir }}/" with_items: - { key: "trusted_domains 1", value: "{{ nextcloud_fqdn }}" } - - { key: "overwrite.cli.url", value: "https://{{ nextcloud_fqdn }}" } + - { key: "overwrite.cli.url", value: "{{ nextcloud_full_url }}" } - { key: "datadir", value: "{{ nextcloud_data_dir }}" } - { key: "dbtype", value: "mysql" } - { key: "dbname", value: "{{ nextcloud_db_name }}" } @@ -248,6 +232,16 @@ - { key: "loglevel", value: "2" } - { key: "trashbin_retention_obligation", value: "auto" } no_log: True + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' + +# trashbin_retention_obligation: +# auto: keep for 30 days, then delete IF space needed +# D, auto: keep at least D+ days, then delete IF space needed +# auto, D: max D days, but delete if space needed +# D1, D2: keep lat east D1 days and max D2 days +# disabled: auto clean disabled - name: run nextcloud upgrade command become: True @@ -256,9 +250,55 @@ args: chdir: "{{ nextcloud_install_dir }}" register: nextcloud_upgrade - changed_when: not 'Nextcloud is already latest version' in nextcloud_upgrade.stdout + changed_when: not 'Nextcloud is already at the latest version' in nextcloud_upgrade.stdout + when: + - nextcloud_action is defined + - nextcloud_action == 'initial' or nextcloud_action ==' upgrade' + +- name: create ansible facts.d directory + file: + path: /etc/ansible/facts.d + state: directory + mode: 0755 + +- name: create nextcloud installed fact file + template: + src: etc_ansible_facts.d_nextcloud.fact.j2 + dest: /etc/ansible/facts.d/nexcloud.fact + mode: 0644 + +###### NEXTCLOUD APPLICATIONS ###### + +- name: download nextcloud applications + get_url: + url: "{{ item.url }}" + dest: "{{ item.dest }}" + with_items: + - { url: 'https://github.com/nextcloud/calendar/releases/download/{{ nextcloud_calendar_version }}/calendar.tar.gz', + dest: '/root/nextcloud-calendar-{{ nextcloud_calendar_version }}.tar.gz'} + - { url: 'https://github.com/nextcloud/contacts/releases/download/{{ nextcloud_contacts_version }}/contacts.tar.gz', + dest: '/root/nextcloud-contacts-{{ nextcloud_contacts_version }}.tar.gz'} + - { url: 'https://github.com/nextcloud/tasks/releases/download/{{ nextcloud_tasks_version }}/tasks.tar.gz', + dest: '/root/nextcloud-tasks-{{ nextcloud_tasks_version }}.tar.gz'} + - { url: 'https://github.com/owncloud/music/releases/download/v{{ nextcloud_music_version }}/music.zip', + dest: '/root/music.zip'} + - { url: 'https://github.com/nextcloud/notes/releases/download/{{ nextcloud_notes_version }}/notes.tar.gz', + dest: '/root/notes.tar.gz'} + +- name: extract nextcloud applications + unarchive: + src: "{{ item.archive }}" + dest: "{{ item.dest }}" + copy: no + owner: www-data + group: www-data + with_items: + - { archive: '/root/nextcloud-calendar-{{ nextcloud_calendar_version }}.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} + - { archive: '/root/nextcloud-contacts-{{ nextcloud_contacts_version }}.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} + - { archive: '/root/nextcloud-tasks-{{ nextcloud_tasks_version }}.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} # Not idempotent + - { archive: '/root/music.zip', dest: '{{ nextcloud_install_dir }}/apps/'} + - { archive: '/root/notes.tar.gz', dest: '{{ nextcloud_install_dir }}/apps/'} -# Not idempotent - https://github.com/nextcloud/server/issues/17789 - name: enable/disable nextcloud applications become: yes become_user: www-data @@ -267,4 +307,7 @@ chdir: "{{ nextcloud_install_dir }}" with_items: "{{ nextcloud_apps }}" register: nextcloud_app_enable_disable - changed_when: not 'No such app enabled' in nextcloud_app_enable_disable.stdout + changed_when: + - not 'No such app enabled' in nextcloud_app_enable_disable.stdout + - not 'already enabled' in nextcloud_app_enable_disable.stdout + diff --git a/roles/nextcloud/templates/etc_ansible_facts.d_nextcloud.fact.j2 b/roles/nextcloud/templates/etc_ansible_facts.d_nextcloud.fact.j2 new file mode 100644 index 000000000..47637f5c4 --- /dev/null +++ b/roles/nextcloud/templates/etc_ansible_facts.d_nextcloud.fact.j2 @@ -0,0 +1,2 @@ +[installed] +version={{ nextcloud_version }} diff --git a/roles/nextcloud/templates/etc_apache2_conf-available_nextcloud-csp.conf.j2 b/roles/nextcloud/templates/etc_apache2_conf-available_nextcloud-csp.conf.j2 deleted file mode 100644 index 78715cadc..000000000 --- a/roles/nextcloud/templates/etc_apache2_conf-available_nextcloud-csp.conf.j2 +++ /dev/null @@ -1,4 +0,0 @@ - - # Unset system-wide Content-Security-Policy header since Nextcloud provides its own CSP - Header always unset Content-Security-Policy - \ No newline at end of file diff --git a/roles/nextcloud/templates/etc_apache2_sites-available_nextcloud.conf.j2 b/roles/nextcloud/templates/etc_apache2_sites-available_nextcloud.conf.j2 new file mode 100644 index 000000000..0f2c4aba1 --- /dev/null +++ b/roles/nextcloud/templates/etc_apache2_sites-available_nextcloud.conf.j2 @@ -0,0 +1,23 @@ + + ServerName {{ nextcloud_fqdn }} + # Redirect all HTTP requests to HTTPS + RewriteEngine On + RewriteCond %{HTTPS} off + RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} + + + + DocumentRoot {{ nextcloud_install_dir }} + ServerName {{ nextcloud_fqdn }} + + + Require all granted + AllowOverride All + Options FollowSymLinks MultiViews + + + Dav off + + + + \ No newline at end of file