Security
The shape of a norsk-ctl deployment, security-wise: one public-facing TLS proxy, one local-only daemon API, and per-instance ingest ports that bind directly to the host. This page walks each surface and the cert sources you’ll pick between.
Attack surface, summarised
Section titled “Attack surface, summarised”| Surface | Default exposure | Hardening |
|---|---|---|
Proxy front door (:443) | Public | TLS-mandatory; HTTP Basic Auth or OAuth2; HTTP/2 + TLSv1.3 only |
HTTP → HTTPS redirect (:80) | Public unless --no-http-redirect | Drops anything that isn’t a redirect or certbot HTTP-01 challenge |
Daemon API (:8333) | Loopback only | Firewall it. Do not expose. The CLI and the MCP stdio bridge live on the same host |
| Studio + Media internal ports | Bridge-internal (docker); media on the host network in hybrid | Not advertised externally; Studio routed by container name on norsk-net |
| Ingest ports (SRT, RTMP, …) | Public — published per-instance | Each pipeline decides; open only the ports your instances actually need |
TLS — mandatory, four cert sources
Section titled “TLS — mandatory, four cert sources”The insecure-HTTP mode was removed in #280. Every install picks a cert source at norsk-ctl init time (the interactive wizard offers all four; the installer flags map straight through).
| Source | When to pick it | What it gives you |
|---|---|---|
mkcert | Local development on your laptop | Auto-installs the mkcert root CA into your system trust store. Browsers on the same machine trust the resulting cert without warning. Local dev only — anything else won’t trust the CA |
self-signed | Remote box with no DNS name (cloud VM, on-prem IP-only) | openssl mints a cert whose SAN covers the configured publicHost / --ip. Browsers show a one-time warning, then it’s real TLS |
user | You have your own cert and key (corporate CA, mkcert-on-laptop scp’d over) | Pass --cert-path and --key-path. norsk-ctl reads them at boot — renewal is on you |
certbot | You have a public DNS name pointing at the box | Auto-issues a Let’s Encrypt cert via certbot’s HTTP-01 challenge on port 80. Renews on the cert’s normal cron |
See Server install — overview for the decision flowchart, and Reverse Proxy for the cipher details.
certSource lives in config.yaml after init and influences the daemon’s renewal behavior for the sources it manages.
Authentication at the proxy
Section titled “Authentication at the proxy”Two paths:
- HTTP Basic Auth (default). Users live in nginx’s htpasswd file, managed via
norsk-ctl user set/delete. Changes hot-reload nginx — no dropped connections. - OAuth2 via
oauth2-proxy. Enabled at init by passing OAuth2 client credentials. Unauthenticated browsers are redirected through the IdP first.
The default admin user is whatever you set at install time. Rotate with norsk-ctl user set <name>; pass NORSK_ADMIN_PASSWORD in the environment to keep passwords off the command line and out of shell history.
Daemon API is loopback-only
Section titled “Daemon API is loopback-only”:8333 is intentionally unauthenticated. The CLI, the web UI (via the proxy), and the local MCP stdio bridge all live on the same host. Do not expose :8333 to the public internet — at the cloud firewall, restrict it to localhost or block it entirely from the inbound rules.
The /mcp endpoint is special-cased: the nginx proxy will only forward /mcp requests originating on the loopback interface, so the local stdio bridge works but external traffic to https://<host>/mcp is blocked.
License file handling
Section titled “License file handling”The license is per product: it’s supplied at registration time (norsk-ctl product add --license-file <path>) and stored on the product registration — there is no daemon-global license in config.yaml. The referenced file is mounted into the media container at launch. Treat it like any other credential — file permissions, backups, and rotation are on you. ~/.norsk-ctl/ is the conventional location.
For marketplace-licensed products (product add --marketplace-provider <aws|gcp>), the provider’s metering applies — no license file on disk.
Credential storage
Section titled “Credential storage”| Credential | Where | Notes |
|---|---|---|
| Proxy users (htpasswd) | nginx htpasswd file under the proxy’s working dir | Hot-reloaded on user set/delete. Bcrypt-hashed |
| OAuth2 client secret | config.yaml | File perms matter — chmod 600 |
| TLS private key | keyPath | File perms matter |
| Norsk license file | product registration (product add --license-file) | File perms matter |
| Instance / launch state | ~/.norsk-ctl/norsk-ctl.db | SQLite; daemon-managed |
Production recommendations
Section titled “Production recommendations”- Get a DNS name. Real TLS via certbot is the cleanest user experience and avoids per-device trust workflows.
- Restrict
:8333. Even on a single-tenant VM, an inbound rule that allows only loopback removes a whole class of mistakes. - Use OAuth2 over basic auth if you have an IdP. Basic auth is fine for a small ops team, but OAuth2 inherits your existing user lifecycle.
- Rotate the admin password. Set a strong one at install, change it with
norsk-ctl user set <admin>periodically. - Backup
~/.norsk-ctl/. Both the config and the state DB. Without it, instance metadata and launch configs are lost. See Backup & Upgrade. - Pin image versions. Studio/Media versions come from the product template’s compose pins (with a per-launch image override available in the launch form). Pinned versions give you reproducibility.
See also
Section titled “See also”- Reverse Proxy — TLS, auth, and the URL shape
- Ports — what each port is for, what to firewall
- Cloud Deployment — security notes for cloud VMs