Phantom v2: dual-mode architecture, security hardening, deployment management
- Dual-mode operation: standalone + c2itall integrated (env var detection)
- SSH keys moved to ~/.ssh/c2deploy_ph-{id} with per-deployment known_hosts
- Ansible output streaming with filtered console + full log capture
- Deployment management menu: discover, SSH, teardown existing deployments
- Cert setup script (setup-cert.sh) deployed to servers for post-DNS LE certs
- Matrix hardening: unique secrets, SSRF protection, rate limits, nginx security headers
- Base hardening: fail2ban systemd backend (Debian 12), SSH limits, nginx jails
- Add-matrix-user helper script deployed to all Matrix servers
- .env support for standalone credential storage
- Config key rename: deploy_id → deployment_id (with backward compat)
- Provider cleanup playbooks for teardown
- Test suite with 50 tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
---
|
||||
# Nextcloud database, app, and service configuration
|
||||
|
||||
- name: Generate MariaDB password for Nextcloud
|
||||
command: openssl rand -base64 24
|
||||
register: nc_db_password_gen
|
||||
args:
|
||||
creates: /var/www/nextcloud/.db_password
|
||||
|
||||
- name: Save DB password
|
||||
copy:
|
||||
content: "{{ nc_db_password_gen.stdout }}"
|
||||
dest: /var/www/nextcloud/.db_password
|
||||
owner: www-data
|
||||
group: www-data
|
||||
mode: "0600"
|
||||
when: nc_db_password_gen.changed
|
||||
|
||||
- name: Read saved DB password
|
||||
slurp:
|
||||
src: /var/www/nextcloud/.db_password
|
||||
register: nc_db_password_file
|
||||
|
||||
- name: Set DB password fact
|
||||
set_fact:
|
||||
nc_db_pass: "{{ (nc_db_password_file.content | b64decode).strip() }}"
|
||||
|
||||
- name: Create Nextcloud MariaDB database
|
||||
mysql_db:
|
||||
name: nextcloud
|
||||
state: present
|
||||
encoding: utf8mb4
|
||||
collation: utf8mb4_general_ci
|
||||
login_unix_socket: /var/run/mysqld/mysqld.sock
|
||||
|
||||
- name: Create Nextcloud MariaDB user
|
||||
mysql_user:
|
||||
name: nextcloud
|
||||
password: "{{ nc_db_pass }}"
|
||||
priv: "nextcloud.*:ALL"
|
||||
host: localhost
|
||||
state: present
|
||||
login_unix_socket: /var/run/mysqld/mysqld.sock
|
||||
|
||||
- name: Generate admin password
|
||||
command: openssl rand -base64 16
|
||||
register: nc_admin_password_gen
|
||||
args:
|
||||
creates: /var/www/nextcloud/.admin_password
|
||||
|
||||
- name: Save admin password
|
||||
copy:
|
||||
content: "{{ nc_admin_password_gen.stdout }}"
|
||||
dest: /var/www/nextcloud/.admin_password
|
||||
owner: www-data
|
||||
group: www-data
|
||||
mode: "0600"
|
||||
when: nc_admin_password_gen.changed
|
||||
|
||||
- name: Read saved admin password
|
||||
slurp:
|
||||
src: /var/www/nextcloud/.admin_password
|
||||
register: nc_admin_password_file
|
||||
|
||||
- name: Configure PHP-FPM pool for Nextcloud
|
||||
copy:
|
||||
dest: /etc/php/8.2/fpm/pool.d/nextcloud.conf
|
||||
content: |
|
||||
[nextcloud]
|
||||
user = www-data
|
||||
group = www-data
|
||||
listen = /run/php/php8.2-fpm-nextcloud.sock
|
||||
listen.owner = www-data
|
||||
listen.group = www-data
|
||||
pm = dynamic
|
||||
pm.max_children = 16
|
||||
pm.start_servers = 4
|
||||
pm.min_spare_servers = 2
|
||||
pm.max_spare_servers = 8
|
||||
pm.max_requests = 500
|
||||
env[HOSTNAME] = $HOSTNAME
|
||||
env[TMP] = /tmp
|
||||
env[TMPDIR] = /tmp
|
||||
env[TEMP] = /tmp
|
||||
php_value[upload_max_filesize] = 10G
|
||||
php_value[post_max_size] = 10G
|
||||
php_value[memory_limit] = 512M
|
||||
php_value[max_execution_time] = 3600
|
||||
php_value[max_input_time] = 3600
|
||||
php_value[opcache.enable] = 1
|
||||
php_value[opcache.memory_consumption] = 128
|
||||
php_value[opcache.interned_strings_buffer] = 16
|
||||
php_value[opcache.max_accelerated_files] = 10000
|
||||
php_value[opcache.revalidate_freq] = 1
|
||||
mode: "0644"
|
||||
notify: restart php-fpm
|
||||
|
||||
- name: Flush handlers to restart PHP-FPM
|
||||
meta: flush_handlers
|
||||
|
||||
- name: Install Nextcloud via occ
|
||||
become_user: www-data
|
||||
command: >
|
||||
php /var/www/nextcloud/occ maintenance:install
|
||||
--database mysql
|
||||
--database-name nextcloud
|
||||
--database-user nextcloud
|
||||
--database-pass "{{ nc_db_pass }}"
|
||||
--admin-user "{{ nextcloud_admin }}"
|
||||
--admin-pass "{{ (nc_admin_password_file.content | b64decode).strip() }}"
|
||||
--data-dir /var/www/nextcloud/data
|
||||
args:
|
||||
creates: /var/www/nextcloud/config/config.php
|
||||
|
||||
- name: Set trusted domain
|
||||
become_user: www-data
|
||||
command: "php /var/www/nextcloud/occ config:system:set trusted_domains 0 --value={{ nextcloud_domain }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Set overwrite.cli.url
|
||||
become_user: www-data
|
||||
command: "php /var/www/nextcloud/occ config:system:set overwrite.cli.url --value=https://{{ nextcloud_domain }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Configure Redis caching
|
||||
become_user: www-data
|
||||
command: "php /var/www/nextcloud/occ config:system:set {{ item.key }} --value={{ item.value }} {{ item.type | default('') }}"
|
||||
loop:
|
||||
- { key: "memcache.local", value: "\\OC\\Memcache\\APCu" }
|
||||
- { key: "memcache.distributed", value: "\\OC\\Memcache\\Redis" }
|
||||
- { key: "memcache.locking", value: "\\OC\\Memcache\\Redis" }
|
||||
- { key: "redis host", value: "localhost" }
|
||||
- { key: "redis port", value: "6379", type: "--type=integer" }
|
||||
changed_when: true
|
||||
|
||||
- name: Set default phone region
|
||||
become_user: www-data
|
||||
command: "php /var/www/nextcloud/occ config:system:set default_phone_region --value=US"
|
||||
changed_when: true
|
||||
|
||||
- name: Deploy Nextcloud cron systemd timer
|
||||
copy:
|
||||
dest: /etc/systemd/system/nextcloud-cron.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Nextcloud cron.php
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=www-data
|
||||
ExecStart=/usr/bin/php /var/www/nextcloud/cron.php
|
||||
Type=oneshot
|
||||
mode: "0644"
|
||||
|
||||
- name: Deploy Nextcloud cron timer
|
||||
copy:
|
||||
dest: /etc/systemd/system/nextcloud-cron.timer
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Run Nextcloud cron.php every 5 minutes
|
||||
|
||||
[Timer]
|
||||
OnBootSec=5min
|
||||
OnUnitActiveSec=5min
|
||||
Unit=nextcloud-cron.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
mode: "0644"
|
||||
|
||||
- name: Enable Nextcloud cron timer
|
||||
systemd:
|
||||
name: nextcloud-cron.timer
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
|
||||
- name: Display admin credentials
|
||||
debug:
|
||||
msg: "Nextcloud admin credentials saved to /var/www/nextcloud/.admin_password — access at https://{{ nextcloud_domain }}"
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
# Nextcloud package and dependency installation
|
||||
|
||||
- name: Install Nextcloud dependencies
|
||||
apt:
|
||||
name:
|
||||
- php8.2-fpm
|
||||
- php8.2-xml
|
||||
- php8.2-mbstring
|
||||
- php8.2-gd
|
||||
- php8.2-curl
|
||||
- php8.2-zip
|
||||
- php8.2-intl
|
||||
- php8.2-mysql
|
||||
- php8.2-redis
|
||||
- php8.2-apcu
|
||||
- php8.2-imagick
|
||||
- php8.2-bcmath
|
||||
- php8.2-gmp
|
||||
- mariadb-server
|
||||
- redis-server
|
||||
- nginx
|
||||
- certbot
|
||||
- python3-certbot-nginx
|
||||
- unzip
|
||||
- curl
|
||||
- ca-certificates
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Enable and start MariaDB
|
||||
service:
|
||||
name: mariadb
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Enable and start Redis
|
||||
service:
|
||||
name: redis-server
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Enable and start PHP-FPM
|
||||
service:
|
||||
name: php8.2-fpm
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Get latest Nextcloud version
|
||||
uri:
|
||||
url: https://download.nextcloud.com/server/releases/latest.tar.bz2.sha256
|
||||
return_content: true
|
||||
register: nc_checksum_response
|
||||
|
||||
- name: Parse Nextcloud checksum
|
||||
set_fact:
|
||||
nc_checksum: "{{ nc_checksum_response.content.split()[0] }}"
|
||||
|
||||
- name: Download Nextcloud tarball
|
||||
get_url:
|
||||
url: https://download.nextcloud.com/server/releases/latest.tar.bz2
|
||||
dest: /tmp/nextcloud.tar.bz2
|
||||
checksum: "sha256:{{ nc_checksum }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Extract Nextcloud
|
||||
unarchive:
|
||||
src: /tmp/nextcloud.tar.bz2
|
||||
dest: /var/www/
|
||||
remote_src: true
|
||||
creates: /var/www/nextcloud/index.php
|
||||
|
||||
- name: Set Nextcloud ownership
|
||||
file:
|
||||
path: /var/www/nextcloud
|
||||
state: directory
|
||||
owner: www-data
|
||||
group: www-data
|
||||
recurse: true
|
||||
|
||||
- name: Create Nextcloud data directory
|
||||
file:
|
||||
path: /var/www/nextcloud/data
|
||||
state: directory
|
||||
owner: www-data
|
||||
group: www-data
|
||||
mode: "0770"
|
||||
|
||||
- name: Allow HTTP/HTTPS through UFW
|
||||
ufw:
|
||||
rule: allow
|
||||
port: "{{ item }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- "80"
|
||||
- "443"
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
# Nginx reverse proxy for Nextcloud
|
||||
|
||||
- name: Create certbot webroot
|
||||
file:
|
||||
path: /var/www/certbot
|
||||
state: directory
|
||||
owner: www-data
|
||||
group: www-data
|
||||
mode: "0755"
|
||||
|
||||
- name: Deploy HTTP-only nginx config (for certbot)
|
||||
copy:
|
||||
dest: /etc/nginx/sites-available/nextcloud
|
||||
content: |
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ nextcloud_domain }};
|
||||
location /.well-known/acme-challenge/ { root /var/www/certbot; }
|
||||
location / { return 200 'phantom setup in progress'; add_header Content-Type text/plain; }
|
||||
}
|
||||
mode: "0644"
|
||||
notify: reload nginx
|
||||
|
||||
- name: Enable Nextcloud nginx site
|
||||
file:
|
||||
src: /etc/nginx/sites-available/nextcloud
|
||||
dest: /etc/nginx/sites-enabled/nextcloud
|
||||
state: link
|
||||
notify: reload nginx
|
||||
|
||||
- name: Flush handlers to apply HTTP config
|
||||
meta: flush_handlers
|
||||
|
||||
- name: Setup TLS (certbot with self-signed fallback)
|
||||
include_tasks: ../../common/tls_setup.yml
|
||||
vars:
|
||||
_tls_domain: "{{ nextcloud_domain }}"
|
||||
|
||||
- name: Deploy SSL nginx config
|
||||
copy:
|
||||
dest: /etc/nginx/sites-available/nextcloud
|
||||
content: |
|
||||
upstream php-handler {
|
||||
server unix:/run/php/php8.2-fpm-nextcloud.sock;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ nextcloud_domain }};
|
||||
location /.well-known/acme-challenge/ { root /var/www/certbot; }
|
||||
location / { return 301 https://$host$request_uri; }
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name {{ nextcloud_domain }};
|
||||
|
||||
ssl_certificate {{ _ssl_cert }};
|
||||
ssl_certificate_key {{ _ssl_key }};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
server_tokens off;
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-Frame-Options SAMEORIGIN always;
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
add_header Referrer-Policy no-referrer always;
|
||||
|
||||
client_max_body_size 10G;
|
||||
client_body_timeout 3600s;
|
||||
fastcgi_buffers 64 4K;
|
||||
|
||||
root /var/www/nextcloud;
|
||||
|
||||
location = /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; }
|
||||
location = /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; }
|
||||
location = /.well-known/webfinger { return 301 $scheme://$host/index.php/.well-known/webfinger; }
|
||||
location = /.well-known/nodeinfo { return 301 $scheme://$host/index.php/.well-known/nodeinfo; }
|
||||
|
||||
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\/.+|ocs-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;
|
||||
fastcgi_param modHeadersAvailable true;
|
||||
fastcgi_param front_controller_active true;
|
||||
fastcgi_pass php-handler;
|
||||
fastcgi_intercept_errors on;
|
||||
fastcgi_request_buffering off;
|
||||
fastcgi_read_timeout 3600;
|
||||
}
|
||||
|
||||
location ~ ^\/(?:updater|ocs-provider)(?:$|\/) { try_files $uri/ =404; index index.php; }
|
||||
location ~ \.(?:css|js|woff2?|svg|gif|map)$ { try_files $uri /index.php$request_uri; add_header Cache-Control "public, max-age=15778463"; access_log off; }
|
||||
location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ { try_files $uri /index.php$request_uri; access_log off; }
|
||||
}
|
||||
mode: "0644"
|
||||
notify: reload nginx
|
||||
Reference in New Issue
Block a user