diff --git a/.claude/HISTORY.md b/.claude/HISTORY.md index 228ca23a..bb3d214b 100644 --- a/.claude/HISTORY.md +++ b/.claude/HISTORY.md @@ -4044,3 +4044,26 @@ git checkout HEAD -- index.html - `./secubox-tools/pre-deploy-lint.sh luci-app-system-hub` - `./secubox-tools/pre-deploy-lint.sh --all` - `./secubox-tools/quick-deploy.sh --app system-hub` (lint runs automatically) + +59. **Nextcloud Integration Enhancements (2026-03-01)** + - **WAF-Safe SSL Routing:** + - `ssl-enable` now routes through `mitmproxy_inspector` backend + - Automatically adds route to `/srv/mitmproxy/haproxy-routes.json` + - Traffic flow: Client → HAProxy → mitmproxy (WAF) → Nextcloud + - Prevents WAF bypass vulnerability + - **Scheduled Backups:** + - `nextcloudctl setup-backup-cron` creates cron jobs + - Supports hourly, daily, weekly schedules (from UCI config) + - Automatic cleanup of old backups (configurable retention) + - **Email/SMTP Integration:** + - `nextcloudctl setup-mail [port] [user] [pass] [from]` + - Supports Gmail, local mailserver, Mailcow configurations + - Auto-configures TLS for ports 587/465 + - **CalDAV/CardDAV Connection Info:** + - `nextcloudctl connections` shows all sync URLs + - iOS, Android (DAVx5), Thunderbird instructions + - Desktop client and mobile app links + - **New RPCD Methods:** + - `get_connections` - Returns all sync URLs + - `setup_mail` - Configure SMTP via LuCI + - `setup_backup_cron` - Enable scheduled backups via LuCI diff --git a/package/secubox/luci-app-nextcloud/root/usr/libexec/rpcd/luci.nextcloud b/package/secubox/luci-app-nextcloud/root/usr/libexec/rpcd/luci.nextcloud index d4f6d92e..37d0c5ba 100755 --- a/package/secubox/luci-app-nextcloud/root/usr/libexec/rpcd/luci.nextcloud +++ b/package/secubox/luci-app-nextcloud/root/usr/libexec/rpcd/luci.nextcloud @@ -438,6 +438,82 @@ delete_backup() { fi } +# Get connection URLs (CalDAV, CardDAV, WebDAV) +get_connections() { + local http_port=$(uci_get main.http_port) + local ssl_enabled=$(uci_get ssl.enabled) + local ssl_domain=$(uci_get ssl.domain) + + # Get LAN IP + local lan_ip + lan_ip=$(uci -q get network.lan.ipaddr || echo "192.168.255.1") + + local base_url="http://${lan_ip}:${http_port:-8080}" + if [ "$ssl_enabled" = "1" ] && [ -n "$ssl_domain" ]; then + base_url="https://${ssl_domain}" + fi + + cat </", + "carddav": "${base_url}/remote.php/dav/addressbooks/users//contacts/", + "webdav": "${base_url}/remote.php/dav/files//", + "ios_server": "$base_url", + "ios_path": "/remote.php/dav", + "davx5_url": "${base_url}/remote.php/dav", + "desktop_url": "$base_url", + "ios_app": "https://apps.apple.com/app/nextcloud/id1125420102", + "android_app": "https://play.google.com/store/apps/details?id=com.nextcloud.client" +} +EOF +} + +# Setup email/SMTP +setup_mail() { + local input + read -r input + + local smtp_host=$(echo "$input" | jsonfilter -e '@.smtp_host' 2>/dev/null) + local smtp_port=$(echo "$input" | jsonfilter -e '@.smtp_port' 2>/dev/null) + local smtp_user=$(echo "$input" | jsonfilter -e '@.smtp_user' 2>/dev/null) + local smtp_pass=$(echo "$input" | jsonfilter -e '@.smtp_pass' 2>/dev/null) + local smtp_from=$(echo "$input" | jsonfilter -e '@.smtp_from' 2>/dev/null) + + if [ -z "$smtp_host" ]; then + echo '{"success": false, "error": "SMTP host is required"}' + return + fi + + if ! lxc_running; then + echo '{"success": false, "error": "Nextcloud container not running"}' + return + fi + + # Build command arguments + local args="$smtp_host ${smtp_port:-587}" + [ -n "$smtp_user" ] && args="$args $smtp_user" + [ -n "$smtp_pass" ] && args="$args \"$smtp_pass\"" + [ -n "$smtp_from" ] && args="$args $smtp_from" + + if nextcloudctl setup-mail $args >/tmp/nextcloud-mail.log 2>&1; then + echo '{"success": true, "message": "SMTP configured successfully"}' + else + local err=$(tail -1 /tmp/nextcloud-mail.log 2>/dev/null || echo "Unknown error") + echo "{\"success\": false, \"error\": \"$err\"}" + fi +} + +# Setup backup cron job +setup_backup_cron() { + if nextcloudctl setup-backup-cron >/tmp/nextcloud-cron.log 2>&1; then + echo '{"success": true, "message": "Backup cron job configured"}' + else + local err=$(tail -1 /tmp/nextcloud-cron.log 2>/dev/null || echo "Unknown error") + echo "{\"success\": false, \"error\": \"$err\"}" + fi +} + # RPCD list method list_methods() { cat <<'EOF' @@ -461,7 +537,10 @@ list_methods() { "logs": {}, "list_users": {}, "reset_password": {"uid": "string", "password": "string"}, - "get_storage": {} + "get_storage": {}, + "get_connections": {}, + "setup_mail": {"smtp_host": "string", "smtp_port": "number", "smtp_user": "string", "smtp_pass": "string", "smtp_from": "string"}, + "setup_backup_cron": {} } EOF } @@ -493,6 +572,9 @@ case "$1" in list_users) list_users ;; reset_password) reset_password ;; get_storage) get_storage ;; + get_connections) get_connections ;; + setup_mail) setup_mail ;; + setup_backup_cron) setup_backup_cron ;; *) echo '{"error": "Unknown method"}' ;; esac ;; diff --git a/package/secubox/luci-app-nextcloud/root/usr/share/rpcd/acl.d/luci-app-nextcloud.json b/package/secubox/luci-app-nextcloud/root/usr/share/rpcd/acl.d/luci-app-nextcloud.json index 92f762a5..f950cf97 100644 --- a/package/secubox/luci-app-nextcloud/root/usr/share/rpcd/acl.d/luci-app-nextcloud.json +++ b/package/secubox/luci-app-nextcloud/root/usr/share/rpcd/acl.d/luci-app-nextcloud.json @@ -3,7 +3,7 @@ "description": "Grant access to Nextcloud LXC app", "read": { "ubus": { - "luci.nextcloud": ["status", "get_config", "list_backups", "logs", "list_users", "get_storage"] + "luci.nextcloud": ["status", "get_config", "list_backups", "logs", "list_users", "get_storage", "get_connections"] }, "uci": ["nextcloud"] }, @@ -23,7 +23,9 @@ "ssl_enable", "ssl_disable", "occ", - "reset_password" + "reset_password", + "setup_mail", + "setup_backup_cron" ] }, "uci": ["nextcloud"] diff --git a/package/secubox/secubox-app-nextcloud/files/usr/sbin/nextcloudctl b/package/secubox/secubox-app-nextcloud/files/usr/sbin/nextcloudctl index 357a2707..bfac0dd8 100644 --- a/package/secubox/secubox-app-nextcloud/files/usr/sbin/nextcloudctl +++ b/package/secubox/secubox-app-nextcloud/files/usr/sbin/nextcloudctl @@ -101,9 +101,13 @@ Commands: restore Restore from backup list-backups List available backups - ssl-enable Register with HAProxy for SSL + ssl-enable Register with HAProxy for SSL (via WAF) ssl-disable Remove HAProxy registration + setup-mail Configure outbound email (SMTP) + setup-backup-cron Setup scheduled backup cron job + connections Show CalDAV/CardDAV/WebDAV URLs + service-run Start service (used by init) service-stop Stop service (used by init) @@ -976,7 +980,7 @@ cmd_ssl_enable() { return 1 fi - log_info "Enabling SSL for $domain via HAProxy..." + log_info "Enabling SSL for $domain via HAProxy + WAF..." # Check if HAProxy is available if [ ! -x /usr/sbin/haproxyctl ]; then @@ -984,45 +988,83 @@ cmd_ssl_enable() { return 1 fi - # Add vhost to HAProxy - local vhost_name="nextcloud_${domain//\./_}" + # Use haproxyctl vhost add - routes through mitmproxy_inspector by default + log_info "Adding vhost via haproxyctl..." + if ! /usr/sbin/haproxyctl vhost add "$domain" --acme; then + log_error "Failed to add HAProxy vhost" + return 1 + fi - uci add haproxy vhost - uci set haproxy.@vhost[-1].name="$vhost_name" - uci set haproxy.@vhost[-1].domain="$domain" - uci set haproxy.@vhost[-1].backend="nextcloud_backend" - uci set haproxy.@vhost[-1].ssl='1' - uci set haproxy.@vhost[-1].acme='1' - uci set haproxy.@vhost[-1].enabled='1' + # Add route to mitmproxy for WAF inspection + # This ensures traffic is inspected before reaching Nextcloud + local routes_file="/srv/mitmproxy/haproxy-routes.json" + local routes_file_in="/srv/mitmproxy-in/haproxy-routes.json" - # Add backend - uci add haproxy backend - uci set haproxy.@backend[-1].name="nextcloud_backend" - uci set haproxy.@backend[-1].mode='http' + if [ -f "$routes_file" ]; then + log_info "Adding mitmproxy route for WAF inspection..." + # Use jsonfilter to check if route exists, then add if not + local existing + existing=$(jsonfilter -i "$routes_file" -e "@[\"$domain\"]" 2>/dev/null || echo "") - # Add server - uci add haproxy server - uci set haproxy.@server[-1].backend="nextcloud_backend" - uci set haproxy.@server[-1].address="127.0.0.1" - uci set haproxy.@server[-1].port="$http_port" + if [ -z "$existing" ]; then + # Add route: domain -> [127.0.0.1, port] + local tmpfile="/tmp/routes-$$.json" + # Read existing routes and add new one + if command -v python3 >/dev/null 2>&1; then + python3 -c " +import json +with open('$routes_file', 'r') as f: + routes = json.load(f) +routes['$domain'] = ['127.0.0.1', $http_port] +with open('$tmpfile', 'w') as f: + json.dump(routes, f, indent=2) +" + mv "$tmpfile" "$routes_file" + # Also update the in-container copy + [ -f "$routes_file_in" ] && cp "$routes_file" "$routes_file_in" + log_info "Added WAF route: $domain -> 127.0.0.1:$http_port" + else + log_info "Python3 not available, manual route config needed" + log_info "Add to $routes_file: \"$domain\": [\"127.0.0.1\", $http_port]" + fi + else + log_info "WAF route already exists for $domain" + fi + else + log_info "mitmproxy routes file not found, creating..." + mkdir -p "$(dirname "$routes_file")" + echo "{\"$domain\": [\"127.0.0.1\", $http_port]}" > "$routes_file" + [ -d "$(dirname "$routes_file_in")" ] && cp "$routes_file" "$routes_file_in" + fi - uci commit haproxy - - # Update Nextcloud config + # Update Nextcloud UCI config uci_set ssl.enabled '1' uci_set ssl.domain "$domain" - # Reload HAProxy + # Restart mitmproxy to load new routes + if [ -x /etc/init.d/mitmproxy ]; then + log_info "Reloading mitmproxy..." + /etc/init.d/mitmproxy restart 2>/dev/null || true + fi + + # Regenerate HAProxy config and reload + log_info "Regenerating HAProxy config..." + /usr/sbin/haproxyctl generate 2>/dev/null || true /etc/init.d/haproxy reload 2>/dev/null || true # Add trusted domain in Nextcloud if lxc_running; then + log_info "Configuring Nextcloud trusted domains..." lxc_occ config:system:set trusted_domains 1 --value="$domain" lxc_occ config:system:set overwriteprotocol --value="https" + lxc_occ config:system:set overwrite.cli.url --value="https://$domain" fi - log_info "SSL enabled for $domain" + log_info "" + log_info "SSL enabled for $domain (with WAF inspection)" log_info "Access Nextcloud at: https://$domain" + log_info "" + log_info "Traffic flow: Client -> HAProxy -> mitmproxy (WAF) -> Nextcloud" } cmd_ssl_disable() { @@ -1037,6 +1079,165 @@ cmd_ssl_disable() { log_info "SSL disabled" } +cmd_setup_mail() { + require_root + load_config + + local smtp_host="${1:-}" + local smtp_port="${2:-587}" + local smtp_user="${3:-}" + local smtp_pass="${4:-}" + local smtp_from="${5:-}" + + if [ -z "$smtp_host" ]; then + cat << 'EOF' +Usage: nextcloudctl setup-mail [port] [user] [password] [from_address] + +Examples: + # Gmail SMTP + nextcloudctl setup-mail smtp.gmail.com 587 user@gmail.com "app-password" user@gmail.com + + # Local mailserver (secubox-app-mailserver) + nextcloudctl setup-mail 127.0.0.1 25 + + # Mailcow/Mailinabox + nextcloudctl setup-mail mail.example.com 587 noreply@example.com "password" noreply@example.com + +Note: For Gmail, use an App Password (not your regular password). +EOF + return 1 + fi + + if ! lxc_running; then + log_error "Nextcloud container not running" + return 1 + fi + + log_info "Configuring SMTP mail settings..." + + # Set mail mode to SMTP + lxc_occ config:system:set mail_smtpmode --value="smtp" + lxc_occ config:system:set mail_smtphost --value="$smtp_host" + lxc_occ config:system:set mail_smtpport --value="$smtp_port" + + # Enable TLS for standard ports + if [ "$smtp_port" = "587" ] || [ "$smtp_port" = "465" ]; then + lxc_occ config:system:set mail_smtpsecure --value="tls" + fi + + # Set authentication if provided + if [ -n "$smtp_user" ]; then + lxc_occ config:system:set mail_smtpauth --value="1" + lxc_occ config:system:set mail_smtpauthtype --value="LOGIN" + lxc_occ config:system:set mail_smtpname --value="$smtp_user" + fi + + if [ -n "$smtp_pass" ]; then + lxc_occ config:system:set mail_smtppassword --value="$smtp_pass" + fi + + # Set from address + if [ -n "$smtp_from" ]; then + lxc_occ config:system:set mail_from_address --value="${smtp_from%@*}" + lxc_occ config:system:set mail_domain --value="${smtp_from#*@}" + elif [ -n "$smtp_user" ]; then + lxc_occ config:system:set mail_from_address --value="${smtp_user%@*}" + lxc_occ config:system:set mail_domain --value="${smtp_user#*@}" + fi + + log_info "SMTP configured: $smtp_host:$smtp_port" + log_info "" + log_info "Test with: nextcloudctl occ notification:test-push " +} + +cmd_setup_backup_cron() { + require_root + load_config + + local schedule="$(uci_get backup.schedule)" || schedule="daily" + local cron_file="/etc/cron.d/nextcloud-backup" + + log_info "Setting up scheduled backups: $schedule" + + case "$schedule" in + hourly) + echo "0 * * * * root /usr/sbin/nextcloudctl backup auto-\$(date +\\%Y\\%m\\%d-\\%H) >/dev/null 2>&1" > "$cron_file" + ;; + daily) + echo "0 3 * * * root /usr/sbin/nextcloudctl backup auto-\$(date +\\%Y\\%m\\%d) >/dev/null 2>&1" > "$cron_file" + ;; + weekly) + echo "0 3 * * 0 root /usr/sbin/nextcloudctl backup auto-\$(date +\\%Y\\%m\\%d) >/dev/null 2>&1" > "$cron_file" + ;; + disabled|none) + rm -f "$cron_file" + log_info "Scheduled backups disabled" + return 0 + ;; + *) + log_error "Unknown schedule: $schedule (use: hourly, daily, weekly, disabled)" + return 1 + ;; + esac + + chmod 644 "$cron_file" + + # Restart cron if running + /etc/init.d/cron restart 2>/dev/null || true + + log_info "Backup cron job created: $cron_file" + log_info "Backups will run $schedule and keep last $(uci_get backup.keep) copies" +} + +cmd_connections() { + load_config + + local lan_ip + lan_ip=$(uci -q get network.lan.ipaddr || echo "192.168.1.1") + + local base_url="http://${lan_ip}:${http_port}" + if [ "$ssl_enabled" = "1" ] && [ -n "$ssl_domain" ]; then + base_url="https://${ssl_domain}" + fi + + cat << EOF +Nextcloud Connection URLs +========================= + +Web Interface: + $base_url + +CalDAV (Calendar): + $base_url/remote.php/dav/calendars// + +CardDAV (Contacts): + $base_url/remote.php/dav/addressbooks/users//contacts/ + +WebDAV (Files): + $base_url/remote.php/dav/files// + +iOS/macOS Calendar & Contacts: + Server: $base_url + Path: /remote.php/dav + +Android (DAVx5): + Base URL: $base_url/remote.php/dav + Login: Your Nextcloud username and password + +Thunderbird (TbSync + Provider for CalDAV & CardDAV): + Server URL: $base_url + +Desktop Sync Client: + Download from: https://nextcloud.com/install/#install-clients + Server: $base_url + +Mobile Apps: + iOS: https://apps.apple.com/app/nextcloud/id1125420102 + Android: https://play.google.com/store/apps/details?id=com.nextcloud.client + +EOF +} + cmd_service_run() { require_root load_config @@ -1067,6 +1268,9 @@ case "${1:-}" in list-backups) shift; cmd_list_backups "$@" ;; ssl-enable) shift; cmd_ssl_enable "$@" ;; ssl-disable) shift; cmd_ssl_disable "$@" ;; + setup-mail) shift; cmd_setup_mail "$@" ;; + setup-backup-cron) shift; cmd_setup_backup_cron "$@" ;; + connections) shift; cmd_connections "$@" ;; service-run) shift; cmd_service_run "$@" ;; service-stop) shift; cmd_service_stop "$@" ;; *) usage ;;