Added Ansible playbook for NextCloud installation

This commit is contained in:
mnemeth 2024-08-24 21:05:57 -07:00
commit 2c543ebde4
29 changed files with 8454 additions and 0 deletions

109
README.md Normal file
View File

@ -0,0 +1,109 @@
This is a Ansible playbook for configuring a stock Debian 12 (bookworm)
cloud image to install NextCloud under a LAMP stack.
NOTE! The stanzas reponsible for creating the /data filesystem on vdb in
roles/system/tasks/main.yml have been commented out for testing, remember
to uncomment them before you run this in production.
This was tested on a Debian-derived x86_64 system with 8GB RAM and 4 cores
under QEMU/KVM.
To test this playbook on a Debian-based OS using QEMU/KVM, do the following:
- Install the following packages if not already installed:
* ansible
* ansible-lint
* cloud-init
* cloud-guest-utils
* cloud-image-utils
* qemu-system-x86_64
* qemu-utils
* ssh
* ssh-askpass
* wget
* whois (for mkpasswd)
- Copy the file cloud-init.cfg from the data/ directory
NOTE! this is a YAML file, proper spacing is required!
The hashed password was created using
```
$ mkpasswd -m sha-512
```
- Create the seed image:
```
cloud-localds seed.iso cloud-init.cfg
```
- Download the latest debian 12 cloud image if you haven't done so already:
```
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
```
NOTE! Do *NOT* try to use the generic cloud image, it will *NOT* work!
- Copy the cloud image because cloud-init will change the image the first
time you run it under QEMU:
```
cp debian-12-generic-amd64.qcow2 test.qcow2
```
- The cloud image is only something like 2GB and installing anything
substantial on it will fail, so you need to resize it:
```
qemu-img resize test.qcow2 10G
```
- Start the VM:
```
qemu-system-x86_64 \
-name test \
-machine pc-q35-5.2,accel=kvm \
-cpu host \
-m 4096 \
-nographic \
-boot strict=on \
-drive file=test.qcow2,format=qcow2,media=disk \
-drive file=seed.iso,media=cdrom \
-nic user,ipv6=off,model=e1000,hostfwd=tcp:127.0.0.1:6666-:22,hostfwd=tcp:${SSH_HOST}:8080-:80
```
- After the VM has started and cloud-init has finished and you see a login
prompt, do the following in another terminal to run the ansible playbook:
```
export ANSIBLE_HOST_KEY_CHECKING=False
ansible-playbook site.yml --user installer --ask-pass -i hosts
```
when it asks you for the SSH password, type "password"
- If the installation is successful, it will print the MariaDB root and
nextcloud user's passwords. Be sure to copy these down, you will need
them when you configure NextCloud from your browser. The NextCloud
database is "nextcloud" and the user is "nextcloud".
- Once the VM is running, you can ssh to it using the following:
```
ssh-keygen -f "${HOME}/.ssh/known_hosts" -R "[127.0.0.1]:6666"
ssh installer@127.0.0.1 -p 6666
```
the password is "password"
After Ansible finishes installing NextCloud, you will be able to reach the
webserver running on the VM by going to localhost:8080 in your browser.
Follow the onscreen directions to finish configuring NextCloud using the
database passwords Ansible provides at the end of the installation.

11
data/cloud-init.cfg Normal file
View File

@ -0,0 +1,11 @@
#cloud-config
hostname: test
manage_etc_hosts: false
ssh_pwauth: true
disable_root: true
users:
- name: installer
hashed_passwd: $6$Ri2GEhfA1j/8eb1w$GFvPvQyFS0dwwzFMd3sB4XMZNCD0F.df4ugHJVaNYiD/QDgkcGo2Yh4LZ05lANkVrJyuWNMOTMA4RWnZ7M64c0
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
lock_passwd: false

View File

@ -0,0 +1,9 @@
Downloading the latest version of NextCloud over and over for testing can
be tiresome. You can download it once and cache it by doing the following:
The latest version of NextCloud can be found at
https://download.nextcloud.com/server/releases/latest.zip
Put it in roles/nextcloud/files/ and set "nextcloud_cached" to "true"
in vars/vars.yml.

10
doc/README.ldap Normal file
View File

@ -0,0 +1,10 @@
When doing an update/upgrade of NextCloud, you may encounter the following:
...
Exception: Database error when running migration 1027Date20230504122946 for app dav
Lost connection to LDAP server.
Update failed
Did you install the LDAP app? Is there a LDAP server that NextCloud is
supposed to communicate with?

21
doc/README.upgrade Normal file
View File

@ -0,0 +1,21 @@
To upgrade NextCloud, ssh to the VM and do the following:
$ cd /data/www/vhosts.d/nextcloud
$ sudo -u www-data php ./occ maintenance:mode --on
$ mariadb --user nextcloud --database nextcloud -p < /home/installer/backup.sql
Enter password:
$ sudo -u www-data php ./occ maintenance:mode --off
$ mariadb --user nextcloud --database nextcloud -p
Enter password:
> describe oc_appconfig;
> alter table oc_appconfig add lazy tinyint(1) null default 0;
> alter table oc_appconfig add type int(11) not null default 2;
> describe oc_appconfig;
> drop table oc_open_local_editor;
> drop table oc_user_status;
> exit
NOTE! you MUST enable NextCloud's LDAP app before you go any further!
$ sudo -u www-data php ./occ upgrade

2
hosts Normal file
View File

@ -0,0 +1,2 @@
[servers]
test ansible_host=127.0.0.1 ansible_port=6666

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
$cfg['blowfish_secret'] = '{{ blowfish_random_generated }}';
$i = 0;
$i++;
$cfg['Servers'][$i]['auth_type'] = 'cookie';
$cfg['Servers'][$i]['host'] = 'localhost';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;
$cfg['UploadDir'] = '';
$cfg['SaveDir'] = '';

View File

@ -0,0 +1,24 @@
<VirtualHost *:80>
ServerAdmin "admin@{{ ansible_hostname }}"
DocumentRoot "{{ document_root }}/{{ package }}"
ServerName "{{ ansible_hostname }}"
ServerAlias "{{ ansible_hostname }}"
<Directory "{{ document_root }}/{{ package }}" >
Options FollowSymlinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory "{{ document_root }}/{{ package }}" >
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*) index.php [PT,L]
</Directory>
</VirtualHost>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
---
- name: Restart apache2
ansible.builtin.service:
name: apache2
state: restarted

View File

@ -0,0 +1,103 @@
---
- name: Install apache2
ansible.builtin.apt:
pkg:
- apache2
- name: Create apache2 directory
ansible.builtin.file:
path: "{{ document_root }}/{{ package }}"
state: directory
mode: "0755"
owner: www-data
group: www-data
- name: Create PHP cli config directory
ansible.builtin.file:
path: "/etc/php/{{ php_version }}/cli"
state: directory
owner: root
group: root
mode: "0755"
- name: Copy in cli php.ini file
ansible.builtin.template:
src: files/php-cli-ini.j2
dest: "/etc/php/{{ php_version }}/cli/php.ini"
owner: root
group: root
mode: "0644"
- name: Create PHP apache2 config directory
ansible.builtin.file:
path: "/etc/php/{{ php_version }}/apache2"
state: directory
owner: root
group: root
mode: "0755"
- name: Copy in apache2 php.ini file
ansible.builtin.template:
src: files/php-apache2-ini.j2
dest: "/etc/php/{{ php_version }}/apache2/php.ini"
owner: root
group: root
mode: "0644"
- name: Remove default apache2 website
ansible.builtin.file:
dest: /etc/apache2/sites-enabled/000-default.conf
state: absent
- name: Configure NextCloud site
ansible.builtin.template:
src: files/nextcloud.conf.j2
dest: /etc/apache2/sites-available/nextcloud.conf
owner: root
group: root
mode: "0644"
- name: Enable apache2 module mod_rewrite
community.general.apache2_module:
name: rewrite
state: present
- name: Enable apache2 module mod_headers
community.general.apache2_module:
name: headers
state: present
- name: Enable apache2 module mod_env
community.general.apache2_module:
name: env
state: present
- name: Enable apache2 module mod_dir
community.general.apache2_module:
name: dir
state: present
- name: Enable apache2 module mod_mime
community.general.apache2_module:
name: mime
state: present
- name: Enable apache2 module proxy_fcgi
community.general.apache2_module:
name: proxy_fcgi
state: present
- name: Enable apache2 module setenvif
community.general.apache2_module:
name: setenvif
state: present
- name: Configure apache2 - enable site
ansible.builtin.file:
src: /etc/apache2/sites-available/nextcloud.conf
dest: /etc/apache2/sites-enabled/nextcloud.conf
state: link
owner: root
group: root
notify: Restart apache2

View File

@ -0,0 +1,8 @@
---
- name: Generate passwords
ansible.builtin.set_fact:
blowfish_random_generated: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits length=32') }}"
mariadb_database_password: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits,ascii_uppercase length=12') }}"
mariadb_root_password: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits,ascii_uppercase length=12') }}"
cacheable: true

View File

@ -0,0 +1,84 @@
---
- name: Install python packages needed for Mariadb
ansible.builtin.apt:
pkg:
- python3-mysqldb
- name: Install Mariadb server
ansible.builtin.apt:
pkg:
- mariadb-server
- name: Restart Mariadb server
ansible.builtin.service:
name: mysql
state: restarted
- name: Set Mariadb root password
community.mysql.mysql_user:
name: "root"
password: "{{ mariadb_root_password }}"
state: present
- name: Update Mariadb root password
community.mysql.mysql_user:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
name: "root"
host: "{{ item }}"
password: "{{ mariadb_root_password }}"
with_items:
- 127.0.0.1
- ::1
- localhost
- name: Delete Mariadb anonymous user account for localhost
community.mysql.mysql_user:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
name: ""
host: localhost
state: absent
- name: Delete Mariadb anonymous user
community.mysql.mysql_user:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
name: ""
host: "{{ item }}"
state: absent
with_items:
- localhost
- "{{ ansible_nodename }}"
- name: Delete Mariadb hostname-based user
community.mysql.mysql_user:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
name: "root"
host: "{{ ansible_nodename }}"
state: absent
- name: Delete Mariadb test database
community.mysql.mysql_db:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
name: "test"
state: absent
- name: Create NextCloud database
community.mysql.mysql_db:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
name: "{{ package }}"
state: present
- name: Create NextCloud user
community.mysql.mysql_user:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
name: "{{ package }}"
password: "{{ mariadb_database_password }}"
priv: "{{ package }}.*:ALL"
state: present

View File

@ -0,0 +1,6 @@
---
- name: Restart apache2
ansible.builtin.service:
name: apache2
state: restarted

View File

@ -0,0 +1,44 @@
---
- name: Download latest NextCloud
ansible.builtin.get_url:
url: https://download.nextcloud.com/server/releases/latest.zip
dest: /tmp/latest.zip
mode: "0644"
owner: www-data
group: www-data
when: not nextcloud_cached
# XXX Downloading nextcloud takes an absurd amount of time once in a while.
# XXX You can get around this by having a local copy when debugging.
- name: Use local copy of latest NextCloud
ansible.builtin.copy:
src: files/latest.zip
dest: /tmp/latest.zip
mode: "0644"
when: nextcloud_cached
- name: Unzip latest NextCloud
ansible.builtin.unarchive:
src: /tmp/latest.zip
dest: "{{ document_root }}"
copy: false
group: www-data
owner: www-data
mode: "0755"
- name: Cleanup /tmp
ansible.builtin.file:
name: /tmp/latest.zip
state: absent
# XXX if you are running nextcloud in a VM and using portforwarding
# XXX to reach the VM's webserver from the host using something like
# XXX 'localhost:8080', you need to uncomment this.
# XXX NOTE! config.php will not exist until after you go to localhost:8080
# - name: Allow local remote servers
# ansible.builtin.lineinfile:
# path: /data/www/vhosts.d/nextcloud/config/config.php
# insertbefore: '^\);'
# line: " 'allow_local_remote_servers' => true,"

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
$cfg['blowfish_secret'] = '{{ blowfish_random_generated }}';
$i = 0;
$i++;
$cfg['Servers'][$i]['auth_type'] = 'cookie';
$cfg['Servers'][$i]['host'] = 'localhost';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;
$cfg['UploadDir'] = '';
$cfg['SaveDir'] = '';

View File

@ -0,0 +1,2 @@
<?php
phpinfo();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
---
- name: Reload apache2
ansible.builtin.service:
name: apache2
state: reloaded

46
roles/php/tasks/main.yml Normal file
View File

@ -0,0 +1,46 @@
---
- name: Install required PHP packages
ansible.builtin.apt:
pkg:
- php{{ php_version }}
- php{{ php_version }}-bcmath
- php{{ php_version }}-bz2
- php{{ php_version }}-cgi
- php{{ php_version }}-cli
- php{{ php_version }}-common
- php{{ php_version }}-curl
- php{{ php_version }}-fpm
- php{{ php_version }}-gd
- php{{ php_version }}-ldap
- php{{ php_version }}-mbstring
- php{{ php_version }}-mysql
- php{{ php_version }}-opcache
- php{{ php_version }}-readline
- php{{ php_version }}-xml
- php{{ php_version }}-zip
state: present
- name: Install libapache2-mod-php
ansible.builtin.apt:
pkg:
- libapache2-mod-php{{ php_version }}
state: present
- name: Copy cli php.ini file
ansible.builtin.template:
src: "files/php-cli-ini.j2"
dest: "/etc/php/{{ php_version }}/cli/php.ini"
owner: root
group: root
mode: "0644"
notify: Reload apache2
- name: Setup PHP info page for debugging
ansible.builtin.template:
src: "files/info.php.j2"
dest: "{{ document_root }}/{{ package }}/info.php"
owner: root
group: root
mode: "0644"
notify: Reload apache2

View File

@ -0,0 +1,7 @@
---
- name: Print passwords
ansible.builtin.debug:
msg:
- "Mariadb Root Password is: {{ mariadb_root_password }}"
- "Mariadb {{ package }} Database Password is: {{ mariadb_database_password }}"

View File

@ -0,0 +1,6 @@
---
- name: Restart sshd
ansible.builtin.service:
name: sshd
state: restarted

11
roles/sshd/tasks/main.yml Normal file
View File

@ -0,0 +1,11 @@
---
- name: Configure sshd for password auth
ansible.builtin.lineinfile:
path: "/etc/ssh/sshd_config"
regex: "^(#)?{{ item.key }}"
line: "{{ item.key }} {{ item.value }}"
state: present
loop:
- {key: "PasswordAuthentication", value: "yes"}
notify: Restart sshd

View File

@ -0,0 +1,86 @@
---
- name: Disable IPv6
ansible.posix.sysctl:
name: "{{ item }}"
value: 1
state: present
reload: true
with_items:
- net.ipv6.conf.all.disable_ipv6
- net.ipv6.conf.default.disable_ipv6
- net.ipv6.conf.lo.disable_ipv6
- name: Allow reading kernel messages for non-root users
ansible.posix.sysctl:
name: "{{ item }}"
value: 0
state: present
reload: true
with_items:
- kernel.dmesg_restrict
- name: Disable ufw (firewall)
ansible.builtin.apt:
name: ufw
state: absent
- name: Disable apparmor
ansible.builtin.systemd:
name: apparmor
enabled: false
- name: Create /data folder
ansible.builtin.file:
path: /data
state: directory
owner: root
group: root
mode: "0755"
- name: Wait 5 minutes for fstrim to finish
ansible.builtin.pause:
minutes: 5
when: not debugging
- name: Unmount mnt
ansible.posix.mount:
path: /mnt
state: unmounted
- name: Disable mount point mnt
ansible.posix.mount:
path: /mnt
state: absent
- name: Create fstab entry for vdb
ansible.posix.mount:
path: /data
src: /dev/vdb
fstype: ext4
opts: defaults
state: present
when: not debugging
- name: Mount /data
ansible.posix.mount:
path: /data
src: /dev/vdb
fstype: ext4
opts: defaults
state: mounted
when: not debugging
- name: Update cache to find necessary utilities
ansible.builtin.apt:
update_cache: true
- name: Install necessary utilities
ansible.builtin.apt:
pkg:
- ca-certificates
- cron
- curl
- unzip
- zip
state: present

16
site.yml Normal file
View File

@ -0,0 +1,16 @@
---
- name: Install NextCloud on a Debian 12 (Bookworm) cloud image.
hosts: servers
become: true
vars_files:
- vars/vars.yml
roles:
- system
- sshd
- generate_passwords
- apache2
- php
- mariadb
- nextcloud
- show_passwords

7
test_roles.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
export ANSIBLE_HOST_KEY_CHECKING=False
ansible-playbook site.yml --user installer --ask-pass -i hosts

14
vars/vars.yml Normal file
View File

@ -0,0 +1,14 @@
---
package: nextcloud
app_root: "{{ package }}"
debugging: true
document_root: /data/www/vhosts.d
http_host: "{{ ansible_hostname }}"
http_port: 80
https_port: 443
nextcloud_cached: false
php_version: 8.2
server_name: "{{ ansible_hostname }}"
server_addr: "{{ ansible_default_ipv4.address }}"