-
Notifications
You must be signed in to change notification settings - Fork 1
security
- Use HTTPS for anything internet-facing
- Session tokens are configurable (default 7-day remember-me / 8-hour standard); API keys default to never-expire — set a per-key expiry, or rotate manually if compromised
- Enrollment PINs are single-use, expire after 10 minutes
- Device tokens are 256-bit random secrets
- Passwords stored as PBKDF2-HMAC-SHA256 (600 000 iterations, OWASP 2023 minimum); legacy SHA-256 hashes auto-upgraded on next login
- Webhook URLs (legacy single + multi-destination) stored server-side only, redacted from backup exports
- CMDB vault uses AES-GCM with PBKDF2-derived keys; passphrase never persisted server-side
- Custom commands run as root - use the per-device command allowlist for untrusted operators
- Viewer role users cannot queue commands, change config, or access API keys
-
apikeys.json,tokens.json, andusers.jsonare owned by the CGI user mode700- protect your server - Agent state files (
/var/lib/remotepower/mode0700) useO_NOFOLLOWon every read/write to defeat symlink attacks from local non-root users -
Session tokens are hashed at rest —
tokens.jsonis keyed by the SHA-256 of the bearer token, never the token itself, so a leaked file yields no usable session - Agents verify the server's TLS certificate (
CERT_REQUIRED+ hostname check); an internal CA can be trusted viaRP_CA_BUNDLEin addition to the system store, never instead of it. The agent→satellite relay hop can also run over HTTPS
Each release is scanned with an external toolchain in addition to the code-level review and CI guardrails. v4.7.0 — the homelab/fleet software integrations + containerized-agent release — was independently penetration-tested with wapiti, nikto, nuclei, bandit and OWASP ZAP and passed clean (no findings). The new outbound integration subsystem reuses the same connect-time SSRF guard (loopback / link-local / cloud-metadata refused, peer IP re-validated, no redirects) as every other outbound feature, with credentials redacted from API responses and the raw URL admin-only.
v4.4.0 was independently scanned with wapiti,
nikto, nuclei,
bandit and OWASP ZAP
and passed clean (no exploitable findings) — see
security-review-4.4.0, the security-themed release
(authorized scanning, passkeys, SAML, the audit hash-chain, and the CRITICAL
admin-gate escalation fix). The most recent full review,
security-review-4.6.0, re-audited the server and
agent (SSRF parity, resolved-role checks, agent credential-storage hardening).
v4.8.0 was likewise scanned with wapiti, nikto, nuclei, bandit and OWASP ZAP and
passed clean. The strict Content-Security-Policy
(default-src 'self', no unsafe-inline), full security-header set (HSTS,
X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy,
COOP/CORP), same-origin enforcement on state-changing requests, and the SSRF-safe
fetch path (peer-IP revalidation + no-redirect) were all verified live.
- Session tokens hashed at rest (above).
- OIDC token-exchange failures log only the HTTP status + OAuth error code, never the IdP response body (which can echo a client secret).
- Webhook-host classification is anchored to the apex / a real subdomain, so look-alike hosts (
discord.com.attacker.tld) aren't trusted. - AWS cloud-import validates the region against the AWS region shape, fetches through the anti-rebinding, no-redirect opener, and refuses any EC2 response carrying a DTD / entity declarations before parsing (XXE / entity-expansion hardening).
- Ansible runs trust host keys on first use (
accept-new+ a per-runknown_hosts) instead of disabling host-key checking.
Defense-in-depth note: agent self-update enforces a signature only when an operator pins a release public key and enables require-signed-updates (opt-in). On its own the server-supplied SHA-256 is an integrity check, not an authenticity one — so a server compromise could push a binary. Enable signed updates on internet-exposed or multi-tenant deployments. Devices managed over RouterOS/MikroTik default to the vendor self-signed cert (TLS verification is a per-device opt-in,
routeros.verify); enable it where the device has a trusted cert.
All data in /var/lib/remotepower/ (owned by www-data, mode 700):
| File | Contents |
|---|---|
users.json |
Admin accounts + bcrypt hashes + roles |
devices.json |
Enrolled devices, MAC, group, notes, cached sysinfo + journal |
tokens.json |
Active browser sessions (7-day TTL), keyed by SHA-256 of the token |
apikeys.json |
Named API keys (values stored here) |
pins.json |
Pending enrollment PINs |
commands.json |
Pending command queue per device |
config.json |
Webhook URL, WoL settings, monitor targets, patch threshold |
history.json |
Command log (last 200 entries) |
schedule.json |
Scheduled jobs (one-shot + recurring cron) |
uptime.json |
Online/offline state changes per device |
monitor_history.json |
Check results per monitor target (last 50) |
cmd_output.json |
Custom command output per device (last 100) |
metrics.json |
CPU/RAM/disk snapshots per device (last 1440) |
cmd_library.json |
Saved command snippets |
longpoll.json |
Pending long-poll output slots (transient) |
Backup:
sudo tar czf remotepower-backup-$(date +%F).tar.gz /var/lib/remotepower/
# Or via dashboard: Settings → Export backupRemotePower has been audited end-to-end across multiple releases — the server
(api.py, helper modules, nginx config), the agent (remotepower-agent), and
the extended subsystems (WebTerm handshake, CMDB vault, LDAP, TOTP, API keys, AI
provider, Proxmox/OPNsense/RouterOS integrations, SSRF-guarded outbound calls,
backup/restore, host-config, and the RBAC scope model). The full reviews live in
docs/security-review-*.md; each release-over-release pass is
summarised in the latest, security-review-4.6.0.
The codebase is also scanned with a combined SAST + DAST pipeline (Bandit;
OWASP ZAP, Nikto, Nuclei, Wapiti, WhatWeb) — the most recent full run reported
no exploitable findings (see Security testing below). Summary of the
defences in place (kept current):
-
Session tokens are 256-bit, generated with
secrets.token_urlsafe(32), carried in theX-TokenHTTP header. Not cookies — this means cross-site requests cannot forge state-changing API calls without a CORS preflight that the server never permits. - Passwords are PBKDF2-HMAC-SHA256 at 600 000 iterations (OWASP 2023 minimum) with per-account salts. Legacy SHA-256 hashes are auto-upgraded on next login.
- Login rate limiting uses an exponential backoff ladder (10 s → 1 min → 5 min → 30 min → 2 h) and a dummy-verify on missing users to prevent timing-based account enumeration.
- TOTP secrets are 160-bit (RFC 4226); a code window of ±1 step accommodates clock skew. TOTP failures count against the login rate limit.
-
Passkeys (WebAuthn, v4.2) offer phishing-resistant, passwordless sign-in
via the vetted
py_webauthnlibrary; a cloned-authenticator sign-count regression is refused, only the public key is stored, and a passkey satisfies the MFA-required policy. -
SAML 2.0 SSO (v4.2) delegates signature / audience / validity checks to
pysaml2+xmlsec1and addsInResponseTo+ one-time-use replay protection; the SP holds no private key. - Account guardrails (v4.2): optionally enforce MFA (TOTP or passkey) per role, cap concurrent sessions per user, set a default API-key expiry, and read a graded security-posture self-check on the Audit page. The audit log is hash-chained (tamper-evident) with a one-click integrity verify.
-
API keys are 320-bit, compared with
hmac.compare_digest, shown to the operator only at creation, support per-key expiry, capped at 50 per server. -
LDAP binds use
CERT_REQUIREDTLS verification by default; opt-out exists for self-signed CAs. -
Authorization: Beareris accepted alongsideX-Tokenas of v3.2.0 (was previously only/api/metrics). The token verification path is identical — same TTL, same role lookup, same admin gate.X-Tokentakes priority when both headers are present, so a strayAuthorizationheader injected by a transparent proxy can't override the dashboard's session token. Bearer was generalised for the bundled MCP server, which sends it per RFC 6750. Operator note:Authorizationheaders tend to be logged by more middleware than the non-standardX-Token— if you suspect Bearer-bearing requests have been logged upstream, rotate the affected API keys.
- The
X-Tokenheader scheme is CSRF-safe by construction: custom headers force a CORS preflight, and RemotePower serves no permissiveAccess-Control-Allow-Origin. Browsers cannot forge cross-origin state-changing requests. - Defence-in-depth: every state-changing request (
POST/PUT/PATCH/DELETE) passes an Origin/Referer same-origin check before route dispatch. CLI and agent clients (which send no Origin) are unaffected; evil-site form posts get a 403.
- All user-derived content is escaped via
escHtml/escAttrbeforeinnerHTMLassignment. - AI assistant output is rendered through an escape-first Markdown renderer: the entire response is HTML-escaped, then transforms operate only on safe ground. Code fences are extracted and re-inserted without further interpretation.
- Toast notifications escape their message string.
- Outbound webhook URLs are validated for
http/httpsscheme. - Per-event toggles, CVE severity filters, maintenance-window suppression, and per-device "unmonitored" gating all apply uniformly to legacy single-URL and multi-destination configurations.
- Optional
webhook_block_localconfig flag refuses POSTs to loopback / link-local / unspecified IPs (covers cloud metadata services at 169.254.169.254). RFC1918 private networks are deliberately permitted — homelab Gotify / ntfy on the LAN is legitimate. - DNS-rebinding protected. The webhook sender, the audit→SIEM forwarder, the OIDC discovery / token-exchange fetches, and (since v3.9.0) the HTTP uptime monitor re-validate the actual peer IP at connect time, not just the address resolved during the pre-flight check — so a hostname that resolves to a permitted address for the check but an internal/metadata address for the real request is caught and refused. TLS verification (server name + certificate chain) is unaffected; the audit forwarder pins the verified TLS context for the connection it validated. (Introduced v3.8.0.)
-
HTTP monitor SSRF (v3.9.0). The uptime monitor's
http/httpscheck now validates its target through the shared per-IP classifier instead of a literal string-prefix blocklist (which missed IPv6[::1], integer/octal/hex-encoded IPv4, and DNS rebinding) and fetches through the connect-time SSRF guard above. Cloud-metadata / link-local is always blocked; loopback only whenallow_internal_monitorsis set; RFC1918 LAN stays allowed by design. See the latestdocs/security-review-*.md.
- AES-GCM with PBKDF2-HMAC-SHA256 600 000 iterations, 256-bit salts, 96-bit nonces, canary blob for key verification without leaking ciphertext.
- The vault passphrase is never persisted server-side. The derived key is
returned to the browser, sent back on every operation in the
X-RP-Vault-Keyheader, and used to encrypt/decrypt in a single request scope. - Vault keys do not appear in audit logs, debug logs, or backup exports.
- Runs as root by design (needs
dpkg/pacman/systemctl), but every subprocess invocation uses the argv-list form — no shell injection — except the deliberateexec:command path, which is the agent's product feature. - TLS verification is mandatory:
CERT_REQUIRED+check_hostname=True;http_post()rejects non-HTTPS URLs at the function head. - Self-updates are SHA-256 verified with
hmac.compare_digestand applied atomically viamkstemp+shutil.move. -
Opt-in mandatory signed updates (v3.8.0): create the marker file
/etc/remotepower/require-signed-updatesand the agent fails closed — it refuses any self-update unless a release public key is pinned and the download carries a valid signature. Without the marker the default is fail-open (an unsigned update is allowed when no key is configured), so this flips the posture for hosts that demand signed updates. - Agent state files live in
/var/lib/remotepower/(mode0700) withO_NOFOLLOWon every read and write. A/tmp/fallback exists for non-root deploys and uses the same anti-symlink hardening. - Enrollment credentials are written with
O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOWat mode0600atomically — no race between create and chmod. - Server-pushed log-watch paths are passed through a deny list (
/etc/shadow,/root/.ssh/,/proc/,/sys/,/dev/, etc.) withrealpath()resolution so symlinks cannot bypass.
- Backup exports redact every secret field: webhook URLs, Pushover tokens, SMTP passwords, LDAP bind passwords, Proxmox API tokens, AI provider keys. The redacted backup can be safely shared with support.
- Strict security headers:
X-Frame-Options: DENY,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policydenying geolocation/camera/microphone,frame-ancestors 'none'in CSP. - Methods restricted to
GET POST DELETE PATCHat the location block. - Request body capped at 64 KB.
- Static
.jsonand.tmpfiles denied (defence against accidental data-dir exposure). - The
/cgi-bin/path is denied as a static location (defence in depth — the Python backend lives there but is only ever executed via/api/through fcgiwrap, so its URL should never resolve to a static file). - HSTS commented out — uncomment after HTTPS is fully tested.
Authentication (session token / API key, rate-limited login, optional 2FA) is
the primary control on every API call. If the instance is reachable from the
public internet, consider also restricting /api/ by source IP — or
fronting it with a VPN / SSO proxy. The static dashboard shell can stay public;
/api/ is the sensitive surface. Keep an allowlist in an include file and pull
it into each /api/ location:
# /etc/nginx/snippets/rp-allowlist.conf
allow 203.0.113.7; # admin IP
allow 10.0.0.0/24; # LAN / VPN range
deny all;Caveat: agents POST to /api/heartbeat (and enroll / download), so the
allowlist must include every agent's source IP (your LAN/VPN) or they stop
reporting. If agents roam on arbitrary public IPs, don't blanket-allowlist
/api/ — rely on the built-in per-device token auth and put the admin surface
behind a VPN/SSO instead. Never IP-restrict /api/csp-report (you'd lose CSP
reports from real browsers). The shipped server/conf/remotepower.conf carries
this guidance inline.
RemotePower is reviewed and scanned on an ongoing basis:
-
Manual security reviews of the server and agent every few releases
(see the
docs/security-review-*.mdfiles; latest: security-review-4.6.0). - SAST — Bandit static analysis of the Python codebase.
- DAST — OWASP ZAP full active scan, Nikto, Nuclei, Wapiti and WhatWeb against a running instance.
The most recent full SAST + DAST run reported no exploitable findings — only informational results and tool false positives (e.g. a metadata-SSRF probe against a path that simply returns a 404, and benign timestamp/internal-IP disclosures inherent to a fleet dashboard). The few static-analysis nits it did surface (e.g. non-cryptographic fingerprint hashes) were annotated or fixed. v4.8.0 was independently tested with wapiti, nikto, nuclei, bandit and OWASP ZAP and passed clean.
If you find a security issue, please report it privately rather than opening a public issue.
In scope:
- Anonymous internet attackers reaching the dashboard
- Cross-site attackers attempting CSRF / XSS / framing
- Local non-root users on a managed host attempting privilege escalation via the agent
- Compromised or malicious server attempting silent exfiltration via agent-side controlled inputs (log-watch paths, etc.)
- MITM attackers attempting to inject into agent ↔ server traffic
- Cred-stuffing / brute-force against login and admin-password re-prompt
Out of scope:
- A fully compromised server with root access — by design, the operator has shell-level control over every agent. Root-on-server = root-on-agents. We defend against silent-exfil pivots, not against intentional misuse.
- A fully compromised admin session — once an attacker has a valid
X-Token, they are the admin. We log everything to the audit log so the operator can see what happened, and the WebTerm flow requires admin-password re-prompt to raise the bar for that specific privileged action.
Recommended for production deployments beyond the secure defaults:
- Run behind HTTPS with a valid certificate (Let's Encrypt is fine).
- If internet-facing, restrict
/api/by source IP (allowlist include) or front it with a VPN/SSO — see "Internet-facing access control" above. Include your agents' source IPs in the allowlist. - Uncomment the
Strict-Transport-Securityheader inserver/conf/remotepower.confonce HTTPS is verified. - Enable the
limit_reqrate-limit zones at the nginx level — the config ships with commented-out examples. - Change the default admin password on first login (a banner reminds you).
- Enable TOTP 2FA for every admin account: Settings → Security → TOTP.
- Rotate or expire any API keys you no longer use.
- If your deployment must never POST webhooks to internal IPs, enable
webhook_block_localin the config. - Configure a daily backup destination (built-in scheduled backup, Settings → Backup) and verify the redacted export can be restored.
- Review the audit log on a schedule —
/api/audit-logor Settings → Audit Log. - If using LDAP/AD, set
ldap_tls_verify: trueand provide a trusted CA; only set tofalsefor known-self-signed internal directories. - If using the CMDB vault, set a passphrase that meets the complexity gate (≥ 12 characters, 2 of 4 character classes) and store it separately — RemotePower cannot recover it.
RemotePower · README · CHANGELOG · remotepower.tvipper.com — generated from docs/, do not edit pages here directly.
Getting started
- Install
- Admin guide
- Deployment map
- Docker / Compose
- HTTPS / TLS
- Self-signed TLS
- Upgrading
- Troubleshooting
Agents & devices
Monitoring & health
Security
Integrations & automation
- Homelab integrations
- OPNsense
- Scripts
- Custom scripts
- MCP server
- Webhooks
- Terraform / IaC
- AI assistant
- RAG
Reference
- Architecture
- CMDB
- Feature inventory
- REST API
- Swagger / OpenAPI
- Fleet management
- Scaling
- Satellites
- Keyboard shortcuts
Release notes