7.2 KiB
Synology Snapshot Admin POC
This guide enables external access to the Python snapshot admin service on Synology without exposing the raw service port to the internet.
Recommended topology
- Keep the Python service bound to loopback only:
python tools/run_snapshot_admin_server_v1.py \
--host 127.0.0.1 \
--port 8787 \
--db outputs/snapshot_admin/snapshot_admin.db \
--seed GatherTradingData.json
-
Put Synology DSM reverse proxy in front of it:
- Source:
https://<public-host>:443 - Destination:
http://127.0.0.1:8787 - Keep the service port closed from direct WAN access.
- Source:
-
Add browser authentication with the built-in Basic Auth gate:
- Set
SNAPSHOT_ADMIN_AUTH_USER - Set
SNAPSHOT_ADMIN_AUTH_PASSWORD - Or pass
--auth-userand--auth-passwordon the wrapper command
- Set
-
Verify from the NAS:
curl -i http://127.0.0.1:8787/api/state
curl -u "$SNAPSHOT_ADMIN_AUTH_USER:$SNAPSHOT_ADMIN_AUTH_PASSWORD" http://127.0.0.1:8787/api/state
- Verify from outside the NAS:
- Open
https://<public-host>/ - The browser should prompt for Basic Auth
https://<public-host>/tablesshould render after login
- Open
DSM Checklist
Use these exact values for the first POC.
-
DSM app path
Control PanelLogin PortalAdvancedReverse Proxy
-
Create reverse proxy rule
- Description:
snapshot-admin - Source protocol:
HTTPS - Source hostname: your public DNS name, for example
admin.example.com - Source port:
443 - Source path:
/ - Destination protocol:
HTTP - Destination hostname:
127.0.0.1 - Destination port:
8787
- Description:
-
Certificate
- Attach a valid TLS certificate for the public hostname
- Prefer a Synology-managed or imported certificate that matches
admin.example.com
-
Firewall
- Allow inbound
443/TCPonly for the reverse proxy endpoint - Do not expose
8787/TCPon WAN - If the NAS must be reachable only from a VPN or office IP range, allowlist those ranges and block the rest
- Allow inbound
-
Service start policy
- Start the Python service on boot or via DSM Task Scheduler
- Keep it bound to
127.0.0.1unless you intentionally use direct bind mode - If you use direct bind mode, keep
--allow-remoteand Basic Auth enabled together - For Gitea Actions runner verification, register
act_runnerwith a dedicated host label (self-hosted:host,snapshot-admin-host:host) if you want to avoid Docker job containers and theCleaning up containerlog line - Preferred launcher script:
tools/run_snapshot_admin_synology.sh - Gitea CI deploy path: trigger
.gitea/workflows/snapshot_admin_deploy.ymlworkflow_dispatchand let the host runner call the launcher script - Runner bootstrap:
tools/re_register_act_runner_synology.sh - Runner daemon start:
tools/start_act_runner_synology.sh
-
Runner re-registration
- Use this when you want to switch an existing runner from Docker mode to host mode:
cd /volume1/projects/data_feed
REG_TOKEN="<runner-registration-token>" \
GITEA_URL="http://192.168.123.100:8418" \
bash tools/re_register_act_runner_synology.sh
- Expected effect:
- removes the existing
.runnerregistration file - registers
self-hosted:host,snapshot-admin-host:host - writes an updated
config.yaml
- removes the existing
- If the old runner remains listed in Gitea, remove it from the repository runner page and re-run the command above
- Runner start
- After re-registration, start the daemon:
bash tools/start_act_runner_synology.sh
- Expected effect:
- launches
act_runner daemonusing the existing config - records
runner.pidandrunner.logunder the runner directory
- launches
DSM Task Scheduler
Create two scheduled tasks in Control Panel > Task Scheduler.
- Boot task
- Task name:
snapshot-admin-start - User:
rootor a dedicated service account with access to the project folder - Event:
Boot-up - Command:
- Task name:
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh start
- Healthcheck task
- Task name:
snapshot-admin-healthcheck - User: same as boot task
- Event:
Scheduled Task - Repeat: every 5 minutes
- Command:
- Task name:
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh healthcheck
- Manual restart task
- Task name:
snapshot-admin-restart - User: same as boot task
- Event:
Scheduled Task - Repeat: manual only, or keep disabled until needed
- Command:
- Task name:
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh restart
Direct bind mode
Direct binding to 0.0.0.0 is allowed only when both auth values are configured:
python tools/run_snapshot_admin_server_v1.py \
--host 0.0.0.0 \
--port 8787 \
--allow-remote \
--auth-user "$SNAPSHOT_ADMIN_AUTH_USER" \
--auth-password "$SNAPSHOT_ADMIN_AUTH_PASSWORD"
Use this only if you have a separate firewall or VPN rule in place. The default POC path is still loopback + reverse proxy.
Validation
Run the unit/web checks before and after deployment:
python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q
python tools/validate_snapshot_admin_web_v1.py
The auth gate is part of the service now, so public exposure without credentials is rejected by the server itself.
Curl checklist
Use this as the POC run sheet.
- Local service check:
curl -i http://127.0.0.1:8787/api/state
Expected:
200 OK- JSON payload contains
version.app
- Reverse proxy auth challenge:
curl -i https://<public-host>/api/state
Expected:
401 UnauthorizedWWW-Authenticate: Basic
- Reverse proxy authenticated access:
curl -u '<user>:<password>' https://<public-host>/api/state
Expected:
200 OK- JSON payload contains the same
version.app
- UI rendering:
curl -I https://<public-host>/
curl -I https://<public-host>/tables
Expected:
200 OKafter auth- HTML response, not a redirect to the raw port
- Restart persistence:
bash tools/run_snapshot_admin_synology.sh restart
bash tools/run_snapshot_admin_synology.sh healthcheck
Expected:
healthcheck ok- The proxy URL continues to answer after the service restarts
Live verification
Use this sequence on the actual Synology box after the reverse proxy rule is in place:
- Start the service and confirm the local health endpoint:
curl -i http://127.0.0.1:8787/api/state
- Confirm the auth gate:
curl -i https://<public-host>/api/state
Expected result:
401 Unauthorizedwhen no credentials are provided200 OKwhen valid Basic Auth credentials are supplied
-
Confirm the browser surface:
- Open
https://<public-host>/ - Sign in with the Basic Auth credentials
- Open
https://<public-host>/tables - Confirm rows render from the three SQLite sources
- Open
-
Confirm the deployment survives a process restart:
- Restart the Python service or the task that launches it
- Re-run
curl -i http://127.0.0.1:8787/api/state - Re-open the browser URL and confirm login still works
-
Archive evidence:
- Save the
curloutputs - Save a screenshot of
/and/tables - Record the DSM reverse proxy rule values and certificate name
- Save the