diff --git a/library/occ b/library/occ new file mode 100644 index 0000000..0cc0ee8 --- /dev/null +++ b/library/occ @@ -0,0 +1,36 @@ +#!/bin/bash +# wrapper for conditional setting of nextcloud occ variables +# derived from ansible-openwrt + +# parameters are command, key, value +source ${1} +logger occ $(cat ${1}) + +unquoted_key="$(echo $key | sed -e s/\'//g)" +unquoted_value="$(echo $value | sed -e s/\'//g)" + +# Hardcoded +cd /opt/nextcloud + +# test if we need to apply a change +case "$command" in + 'config:app:set') + [ "$(sudo -u www-data php occ config:app:get $unquoted_key)" = "$unquoted_value" ] + changed=$? + ;; + 'config:system:set') + [ "$(sudo -u www-data php occ config:system:get $unquoted_key)" = "$unquoted_value" ] + changed=$? + ;; +esac + +if [ $changed -eq 0 ] +then + echo -n '{"changed": false}' +else + if [ -z "${_ansible_check_mode}" -o "${_ansible_check_mode}" = "False" ] + then + logger occ $(sudo -u www-data php occ "${command}" ${unquoted_key} --value="${unquoted_value}") + fi + echo -n '{"changed": true, "msg": "executed sudo -u www:data php occ '${command}' '${unquoted_key}' --value='${value}'"}' +fi diff --git a/nextcloud.yaml b/nextcloud.yaml new file mode 100644 index 0000000..d3ffa99 --- /dev/null +++ b/nextcloud.yaml @@ -0,0 +1,25 @@ +--- +- hosts: 'cloud' + gather_facts: false + tasks: + - import_role: name='lxc_guest' + vars: + vm_name: '{{ inventory_hostname }}' + vm_size: '4G' + vg_name: '{{ hostvars[ansible_lxc_host]["vg_name"] }}' + delegate_to: '{{ ansible_lxc_host }}' + - set_fact: ansible_connection='ssh_lxc' + - setup: + - import_role: name='ssh_server' + - set_fact: ansible_connection='ssh' + +- hosts: 'cloud' + roles: + - role: 'dns_record' + - role: 'reverse_proxy' + hostname: 'cloud' + - role: 'nextcloud' + +- hosts: 'status' + roles: + - role: 'icinga2-monitoring' diff --git a/roles/ldap/tasks/3_provision_tree.yaml b/roles/ldap/tasks/3_provision_tree.yaml index c0c1eec..3224965 100644 --- a/roles/ldap/tasks/3_provision_tree.yaml +++ b/roles/ldap/tasks/3_provision_tree.yaml @@ -135,6 +135,7 @@ loop: - 'TestServer' - 'projects' + - 'nextcloud' #- name: templating ACLs # template: diff --git a/roles/nextcloud/defaults/main.yaml b/roles/nextcloud/defaults/main.yaml new file mode 100644 index 0000000..d5bce76 --- /dev/null +++ b/roles/nextcloud/defaults/main.yaml @@ -0,0 +1,5 @@ +--- +server_fqdn: '{{ ansible_hostname }}.{{ domain }}' +ldap_server: 'ldap1.dmz.{{ domain }}' +ldap_basedn: 'dc={{ domain.replace(".", ",dc=") }}' +... diff --git a/roles/nextcloud/files/ldap.conf b/roles/nextcloud/files/ldap.conf new file mode 100644 index 0000000..545c25f --- /dev/null +++ b/roles/nextcloud/files/ldap.conf @@ -0,0 +1,22 @@ +# +# LDAP Defaults +# + +# See ldap.conf(5) for details +# This file should be world readable but not world writable. + +#BASE dc=example,dc=com +#URI ldap://ldap.example.com ldap://ldap-master.example.com:666 + +#SIZELIMIT 12 +#TIMELIMIT 15 +#DEREF never + +# TLS certificates (needed for GnuTLS) +TLS_CACERT /etc/ldap/root_ca.crt +#TLS_CERT /etc/ldap/ldap.crt +#TLS_KEY /etc/ldap/ldap.key + +# TLSv1.3 Only +TLS_CIPHER_SUITE SECURE:-VERS-ALL:+VERS-TLS1.3 + diff --git a/roles/nextcloud/meta/main.yaml b/roles/nextcloud/meta/main.yaml new file mode 100644 index 0000000..f6688f0 --- /dev/null +++ b/roles/nextcloud/meta/main.yaml @@ -0,0 +1,4 @@ +--- +dependencies: + - role: nginx + diff --git a/roles/nextcloud/tasks/main.yaml b/roles/nextcloud/tasks/main.yaml new file mode 100644 index 0000000..6d1dbf0 --- /dev/null +++ b/roles/nextcloud/tasks/main.yaml @@ -0,0 +1,195 @@ +- name: 'install requirements' + apt: + pkg: + - 'sudo' + - 'bzip2' + - 'php7.3-fpm' + - 'php7.3-common' + - 'php7.3-xml' + - 'php7.3-gd' + - 'php7.3-json' + - 'php7.3-mbstring' + - 'php7.3-zip' + - 'php7.3-pgsql' + - 'php7.3-ldap' + - 'php7.3-curl' + - 'php7.3-intl' + - 'php7.3-bz2' + #- 'php7.3.-imagick' + #- 'ffmpeg' + - 'postgresql' + - 'postgresql-contrib' + - 'python3-psycopg2' + - 'ca-certificates' + state: 'present' + update_cache: true + cache_valid_time: 3600 + tags: + - 'packages' + +- block: + - name: 'create nextcloud DB' + postgresql_db: + name: 'nextcloud' + - name: 'create nextcloud DB user' + postgresql_user: + name: 'www-data' + db: 'nextcloud' + priv: 'ALL' + become: true + become_method: 'su' + become_user: 'postgres' + +- name: 'download latest nextcloud' + get_url: + url: 'https://download.nextcloud.com/server/releases/nextcloud-18.0.3.tar.bz2' + dest: '/opt/nextcloud.tar.bz2' + register: 'new_download' + tags: + - 'packages' + +- name: 'unpack nextcloud' + unarchive: + src: '/opt/nextcloud.tar.bz2' + dest: '/opt' + owner: 'www-data' + group: 'www-data' + copy: no + when: new_download.changed + tags: + - 'packages' + +- name: 'create nextcloud data folder' + file: + path: '/opt/nextcloud_data' + owner: 'www-data' + group: 'www-data' + state: 'directory' + +- name: 'create nginx configuration' + template: + src: 'nextcloud.conf.j2' + dest: '/etc/nginx/locations/{{ server_fqdn }}/nextcloud.conf' + notify: 'restart nginx' + +- import_tasks: 'occ.yaml' + vars: + occ_args: '--no-warnings status --output json' + ignore_changes: true +- set_fact: + installed: '{{ occ_out.installed }}' + +- block: + - name: 'create random root password' + gen_passwd: length=20 + register: 'password' + - set_fact: + initial_root_password: '{{ new_passwd.passwd }}' + - name: 'store root password plaintext' + copy: + content: '{{ initial_root_password }}' + dest: '/etc/nextcloud.secret' + - fail: + msg: >- + Warning! First Install and `initial_root_password` not provided. + Random password generated and stored in /etc/nextcloud.secret. + **WIPE AS SOON AS POSSIBLE** + failed_when: false + when: (initial_root_password is not defined) and (not installed) + +- name: 'install nextcloud' + include_tasks: 'occ.yaml' + vars: + occ_args: >- + maintenance:install + --database 'pgsql' + --database-name 'nextcloud' + --database-host '/var/run/postgresql' + --database-user 'www-data' + --database-pass '' + --admin-pass '{{ initial_root_password }}' + --data-dir '/opt/nextcloud_data' + --no-interaction + nojson: true + when: not installed + +- name: 'set trusted_domains' + occ: + command: 'config:system:set' + key: 'trusted_domains {{ idx }}' + value: '{{ item }}' + loop: + - 'localhost' + - '{{ server_fqdn }}' + loop_control: + index_var: idx + +- name: 'update tls ca' + copy: + content: '{{ tls_root_ca }}' + dest: '/etc/ldap/root_ca.crt' + tags: + - 'tls_int' + +- name: 'configure ldap client' + copy: + src: 'ldap.conf' + dest: '/etc/ldap/ldap.conf' + +- name: 'enable user_ldap' + occ: + command: 'config:app:set' + key: 'user_ldap enabled' + value: 'yes' + tags: + - 'service_password' + +- name: 'configure user_ldap' + occ: + command: 'config:app:set' + key: 'user_ldap s01{{ item.key }}' + value: '{{ item.value }}' + loop: '{{ ldap_settings|dict2items }}' + vars: + ldap_settings: + has_memberof_filter_support: '1' + ldap_host: '{{ ldap_server }}' + ldap_port: '389' + ldap_dn: 'cn={{ ansible_hostname }},ou=Server,{{ ldap_basedn }}' + ldap_base: 'ou=People,{{ ldap_basedn }}' + ldap_base_users: 'ou=People,{{ ldap_basedn }}' + ldap_base_groups: 'ou=Groups,{{ ldap_basedn }}' + ldap_login_filter: '(&(cn=%uid)(authorizedService=nextcloud))' + ldap_user_filter: '(authorizedService=nextcloud)' + ldap_attributes_for_user_search: 'cn' + ldap_attributes_for_group_search: 'cn' + ldap_email_attr: 'mail' + ldap_tls: '1' + ldap_experienced_admin: '1' + ldap_configuration_active: '1' + +- name: 'generate nextcloud ldap password' + gen_passwd: 'length=32' + register: 'new_passwd' + tags: + - 'service_password' + +- name: 'set nextcloud ldap password in ldap' + delegate_to: 'localhost' + ldap_passwd: + dn: 'cn={{ ansible_hostname }},ou=Server,{{ ldap_basedn }}' + passwd: '{{ new_passwd.passwd }}' + server_uri: 'ldap://{{ ldap_server }}' + start_tls: true + bind_dn: '{{ ldap_admin_dn }}' + bind_pw: '{{ ldap_admin_pw }}' + tags: + - 'service_password' + +- import_tasks: 'occ.yaml' + vars: + occ_args: 'ldap:set-config s01 ldapAgentPassword {{ new_passwd.passwd }}' + nojson: true + tags: + - 'service_password' +... diff --git a/roles/nextcloud/tasks/occ.yaml b/roles/nextcloud/tasks/occ.yaml new file mode 100644 index 0000000..75fca90 --- /dev/null +++ b/roles/nextcloud/tasks/occ.yaml @@ -0,0 +1,13 @@ +--- +- command: 'php occ {{ occ_args }}' + become: true + become_user: 'www-data' + args: + chdir: '/opt/nextcloud' + register: occ_out_raw + changed_when: occ_out_raw.changed and (not ignore_changes|default(false)) + +- set_fact: + occ_out: '{{ occ_out_raw.stdout | from_json }}' + when: nojson is not defined +... diff --git a/roles/nextcloud/templates/ldap.conf.j2 b/roles/nextcloud/templates/ldap.conf.j2 new file mode 100644 index 0000000..7378fea --- /dev/null +++ b/roles/nextcloud/templates/ldap.conf.j2 @@ -0,0 +1,22 @@ +# +# LDAP Defaults +# + +# See ldap.conf(5) for details +# This file should be world readable but not world writable. + +#BASE dc=example,dc=com +#URI ldap://ldap.example.com ldap://ldap-master.example.com:666 + +#SIZELIMIT 12 +#TIMELIMIT 15 +#DEREF never + +# TLS certificates (needed for GnuTLS) +TLS_CACERT /etc/ldap/ca.crt +#TLS_CERT /etc/ldap/ldap.crt +#TLS_KEY /etc/ldap/ldap.key + +# TLSv1.3 Only +TLS_CIPHER_SUITE SECURE:-VERS-ALL:+VERS-TLS1.3 + diff --git a/roles/nextcloud/templates/nextcloud.conf.j2 b/roles/nextcloud/templates/nextcloud.conf.j2 new file mode 100644 index 0000000..6f942f1 --- /dev/null +++ b/roles/nextcloud/templates/nextcloud.conf.j2 @@ -0,0 +1,90 @@ +add_header Referrer-Policy "no-referrer" always; +add_header X-Content-Type-Options "nosniff" always; +add_header X-Download-Options "noopen" always; +add_header X-Frame-Options "SAMEORIGIN" always; +add_header X-Permitted-Cross-Domain-Policies "none" always; +add_header X-Robots-Tag "none" always; +add_header X-XSS-Protection "1; mode=block" always; + +fastcgi_hide_header X-Powered-By; + +root /opt/nextcloud; + +location = /robots.txt { + allow all; + log_not_found off; + access_log off; +} + +location = /.well-known/carddav { + return 301 $scheme://$host:$server_port/remote.php/dav; +} +location = /.well-known/caldav { + return 301 $scheme://$host:$server_port/remote.php/dav; +} + +# set max upload size +client_max_body_size 512M; +fastcgi_buffers 64 4K; + +# Enable gzip but do not remove ETag headers +gzip on; +gzip_vary on; +gzip_comp_level 4; +gzip_min_length 256; +gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; +gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; + +location / { + rewrite ^ /index.php; +} +location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ { + deny all; +} +location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) { + deny all; +} + +location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) { + fastcgi_split_path_info ^(.+?\.php)(\/.*|)$; + set $path_info $fastcgi_path_info; + try_files $fastcgi_script_name =404; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $path_info; + fastcgi_param HTTPS on; + # Avoid sending the security headers twice + fastcgi_param modHeadersAvailable true; + # Enable pretty urls + fastcgi_param front_controller_active true; + fastcgi_pass unix:/var/run/php/php7.3-fpm.sock; + fastcgi_intercept_errors on; + fastcgi_request_buffering off; +} + +location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) { + try_files $uri/ =404; + index index.php; +} + +# Adding the cache control header for js, css and map files +# Make sure it is BELOW the PHP block +location ~ \.(?:css|js|woff2?|svg|gif|map)$ { + try_files $uri /index.php$request_uri; + add_header Cache-Control "public, max-age=15778463"; + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Download-Options "noopen" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "none" always; + add_header X-XSS-Protection "1; mode=block" always; + + access_log off; +} + +location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ { + try_files $uri /index.php$request_uri; + + access_log off; +} \ No newline at end of file