feat(nextcloud): Enhance integration with WAF, backups, mail, sync URLs
- WAF-safe SSL: Route through mitmproxy_inspector, auto-add routes - Scheduled backups: setup-backup-cron with hourly/daily/weekly support - Email/SMTP: setup-mail command for outbound notifications - CalDAV/CardDAV: connections command shows sync URLs for all clients - New RPCD methods: get_connections, setup_mail, setup_backup_cron - ACL updated with new method permissions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
00d92037b9
commit
0c55ef6ec1
@ -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 <host> [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
|
||||
|
||||
@ -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 <<EOF
|
||||
{
|
||||
"base_url": "$base_url",
|
||||
"caldav": "${base_url}/remote.php/dav/calendars/<username>/",
|
||||
"carddav": "${base_url}/remote.php/dav/addressbooks/users/<username>/contacts/",
|
||||
"webdav": "${base_url}/remote.php/dav/files/<username>/",
|
||||
"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
|
||||
;;
|
||||
|
||||
@ -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"]
|
||||
|
||||
@ -101,9 +101,13 @@ Commands:
|
||||
restore <name> Restore from backup
|
||||
list-backups List available backups
|
||||
|
||||
ssl-enable <domain> Register with HAProxy for SSL
|
||||
ssl-enable <domain> Register with HAProxy for SSL (via WAF)
|
||||
ssl-disable Remove HAProxy registration
|
||||
|
||||
setup-mail <args> 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 <smtp_host> [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 <username>"
|
||||
}
|
||||
|
||||
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/<username>/
|
||||
|
||||
CardDAV (Contacts):
|
||||
$base_url/remote.php/dav/addressbooks/users/<username>/contacts/
|
||||
|
||||
WebDAV (Files):
|
||||
$base_url/remote.php/dav/files/<username>/
|
||||
|
||||
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 ;;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user