-
Notifications
You must be signed in to change notification settings - Fork 3
Authentication and Security

On first start with no existing users, Sentinel displays a setup wizard at /setup. The wizard creates the initial admin account and marks setup as complete. Once complete, the /setup endpoint is permanently disabled.
If the container is started with SENTINEL_AUTH_ENABLED=false, all requests are treated as an authenticated admin with full permissions. This is intended for local/development use only.
| Property | Detail |
|---|---|
| Hashing | bcrypt (cost 12) |
| Minimum length | 8 characters |
| Maximum length | 72 bytes (bcrypt limit, enforced explicitly) |
| Complexity | At least one letter and one digit |
| Username | Case-sensitive |
| Rate limit | 5 failed attempts per IP in a 5-minute window |
| Account lockout | 10 consecutive failures locks the account for 30 minutes |
Passwords are stored as bcrypt hashes. The 72-byte limit is enforced before hashing to prevent bcrypt's silent truncation.
Login routes:
| Method | Path | Purpose |
|---|---|---|
GET |
/login |
Render login page |
POST |
/login |
Submit credentials |
POST |
/api/auth/logout |
End session |
Time-based one-time passwords using the standard TOTP algorithm (RFC 6238). Compatible with any TOTP app (Google Authenticator, Authy, 1Password, etc.).
- Navigate to Settings > Security and click Enable 2FA.
- Sentinel generates a secret and displays a QR code.
- Scan the QR code with your authenticator app.
- Enter a valid 6-digit code to confirm. 2FA is not active until this step completes.
- Save the 8 recovery codes shown. Each code is single-use. Recovery codes are stored as SHA-256 hashes.
When TOTP is enabled, the login flow becomes two-step:
- Submit username and password. On success, the server returns a pending TOTP token (valid for 5 minutes).
- Submit the 6-digit TOTP code (or a recovery code) with the pending token. On success, a session is created.
Recovery codes are consumed on use. If all recovery codes are exhausted, an admin can disable 2FA on the account.
Requires the account password for confirmation. Clears the TOTP secret and all recovery codes.
| Setting | Value |
|---|---|
| Issuer | Docker-Sentinel |
| Algorithm | SHA-1 (standard TOTP) |
| Digits | 6 |
| Period | 30 seconds |
| Recovery codes | 8, single-use, SHA-256 hashed |
FIDO2 passwordless authentication via hardware keys, biometrics, or platform authenticators. Opt-in, requires environment variables before use.
| Variable | Purpose | Example |
|---|---|---|
SENTINEL_WEBAUTHN_RPID |
Relying Party ID, must match the host serving the UI | sentinel.example.com |
SENTINEL_WEBAUTHN_ORIGINS |
Comma-separated allowed origins | https://sentinel.example.com |
SENTINEL_WEBAUTHN_DISPLAY_NAME |
Human-readable site name shown in authenticator prompts | Docker Sentinel |
When at least one passkey is registered, a Use passkey option appears on the login page. Multiple passkeys can be registered per account. Manage them under Settings > Security.
WebAuthn ceremonies (registration and assertion) have a 60-second TTL. If the user does not complete the ceremony within that window, it expires and must be restarted.
The availability endpoint (GET /api/auth/passkeys/available) is public and returns {"available": true} when passkeys are configured server-side.

OpenID Connect integration for single sign-on with external identity providers (Keycloak, Authentik, Authelia, Google, etc.).
OIDC is configured from the web UI at Settings > Security > OIDC, or via the API.
| Setting | Description |
|---|---|
| Issuer URL | OIDC discovery endpoint (e.g. https://auth.example.com/realms/main) |
| Client ID | OAuth2 client identifier |
| Client Secret | OAuth2 client secret |
| Redirect URL | Callback URL, typically https://sentinel.example.com/api/auth/oidc/callback
|
| Auto-create users | Create local accounts from OIDC claims on first login |
| Default role | Role assigned to auto-created users (admin, operator, or viewer) |
- User clicks Sign in with SSO on the login page.
- Sentinel generates a CSRF state token, an OIDC nonce, and a PKCE code verifier (S256). All three are stored in HttpOnly cookies (
oidc_state,oidc_nonce,oidc_pkce). - Browser redirects to the identity provider's authorization endpoint with the state, nonce, and PKCE code challenge.
- After authentication, the provider redirects back with an authorization code.
- Sentinel exchanges the code for an ID token using the PKCE verifier, then validates the nonce claim with constant-time comparison.
- Claims are extracted (
preferred_username,email,name). If the username matches an existing local account, a session is created. - If no match exists and auto-create is enabled, a new user is created with the configured default role.
OIDC users are assigned a random password (they authenticate via the identity provider, not via the Sentinel login form).
The nonce and PKCE protections are automatic - no additional configuration is required. They guard against replay attacks and authorization code interception respectively.
openid, profile, email
| Method | Path | Purpose |
|---|---|---|
GET |
/api/auth/oidc/available |
Check if OIDC is configured (public) |
GET |
/api/auth/oidc/login |
Initiate OIDC flow (redirects to IdP) |
GET |
/api/auth/oidc/callback |
Handle IdP redirect |
GET |
/api/settings/oidc |
Read OIDC settings (admin) |
POST |
/api/settings/oidc |
Save OIDC settings (admin) |
Three built-in roles with ten granular permissions. Roles are assigned per user at creation or via Settings > Users.
| Role | Description |
|---|---|
| Admin | Full access to all features including user management and settings |
| Operator | Container operations (view, update, approve, rollback, manage) and read-only settings |
| Viewer | Read-only access to containers, settings, logs, and history |
| Permission | Admin | Operator | Viewer | Description |
|---|---|---|---|---|
containers.view |
Yes | Yes | Yes | Read container list, detail, queue, stats, SSE stream |
containers.update |
Yes | Yes | Trigger scans, pull updates, switch registry source | |
containers.approve |
Yes | Yes | Approve, reject, or ignore pending updates | |
containers.rollback |
Yes | Yes | Roll back containers or services | |
containers.manage |
Yes | Yes | Start, stop, restart, set per-container policy | |
settings.view |
Yes | Yes | Yes | Read all settings, hooks, dependencies, cluster info |
settings.modify |
Yes | Write settings, manage notifications/registries/cluster | ||
users.manage |
Yes | Create, delete, and configure user accounts | ||
logs.view |
Yes | Yes | Yes | Read activity logs |
history.view |
Yes | Yes | Yes | Read update history |
| Property | Detail |
|---|---|
| Token format | 64-character lowercase hex (32 random bytes) |
| Generation | crypto/rand |
| Storage | BoltDB |
| Default expiry | 720 hours (30 days) |
| Expiry override |
SENTINEL_SESSION_EXPIRY (e.g. 168h) |
| Cookie name | sentinel_session |
| Cookie flags | HttpOnly, SameSite=Lax |
| Secure flag | Set when SENTINEL_COOKIE_SECURE=true
|
Sessions are listed and individually revocable from the Account page or via the API. Expired sessions are cleaned up periodically.
Session rotation: each successful login creates a new session token. Old sessions are not reused.
Logout is available only via POST /logout. The GET /logout route is intentionally not registered - logout must be a CSRF-protected state-changing request. The CSRF double-submit check applies, so the X-CSRF-Token header (or csrf_token form field) is required.
Long-lived bearer tokens for automation and scripting.
| Property | Detail |
|---|---|
| Format |
stk_ prefix + base64url-encoded 32 random bytes |
| Storage | SHA-256 hash only; plaintext shown once at creation |
| Transport |
Authorization: Bearer stk_... header |
| CSRF | Exempt (not cookie-based) |
| Scope | Supports optional expiry and permission scoping |
| Last used | Tracked automatically on each request |
API tokens can be restricted to a subset of the creating user's permissions. The effective permissions are the intersection of the user's role permissions and the token's declared scope. Tokens with no explicit scope inherit all permissions from the user's role.
# List containers using an API token
curl -H "Authorization: Bearer stk_abc123..." \
https://sentinel.example.com/api/containersTokens with no expiry remain valid until deleted. Manage tokens from Settings > Security or via the API.
The webhook endpoint (POST /api/webhook) uses its own secret-based authentication, separate from session or token auth.
The secret must be passed via the X-Webhook-Secret header. Query-string authentication (?secret=...) is not supported - proxies and access logs record URLs verbatim, which would leak the secret into log aggregators and browser history.
curl -X POST http://your-server:8080/api/webhook \
-H "X-Webhook-Secret: your_secret" \
-H "Content-Type: application/json" \
-d '{"push_data":{"tag":"latest"},"repository":{"repo_name":"myuser/myapp"}}'The secret is compared using constant-time comparison to prevent timing attacks. Generate or rotate the secret from Settings > Webhooks in the UI, or via POST /api/settings/webhook-secret.
Sentinel uses the double-submit cookie pattern for cross-site request forgery protection.
| Component | Detail |
|---|---|
| Cookie name | sentinel_csrf |
| Cookie flags | Not HttpOnly (JavaScript must read it), SameSite=Lax |
| Header | X-CSRF-Token |
| Form field |
csrf_token (fallback for HTML form submissions) |
| Applies to | POST, PUT, DELETE, PATCH requests with session cookies |
| Exempt | Bearer token requests, requests when auth is disabled |
| Comparison | Constant-time (crypto/subtle) |
On each mutating request, the server compares the sentinel_csrf cookie value against the X-CSRF-Token header (or csrf_token form field) using constant-time comparison. A mismatch returns HTTP 403.
Login attempts are rate-limited per IP address.
| Parameter | Value |
|---|---|
| Window | 5 minutes |
| Max attempts per window | 5 |
| Account lockout threshold | 10 consecutive failures |
| Account lockout duration | 30 minutes |
| Cleanup interval | 1 hour |
After 5 failed attempts from an IP within a 5-minute window, further attempts are rejected. After 10 consecutive failures on a specific account, that account is locked for 30 minutes regardless of IP.
Successful login resets both the IP rate limit counter and the account failure counter.
| Mode | Configuration |
|---|---|
| Reverse proxy (recommended) | Run Sentinel on HTTP behind a proxy; no extra config needed |
| Bring your own certificate |
SENTINEL_TLS_CERT=/path/cert.pem and SENTINEL_TLS_KEY=/path/key.pem
|
| Auto self-signed |
SENTINEL_TLS_AUTO=true, generates and stores a cert on first start |
When using built-in TLS, also set SENTINEL_COOKIE_SECURE=true.
nginx with WebSocket and SSE support:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off; # required for SSE
proxy_cache off;
proxy_read_timeout 86400s; # keep SSE connection open
}Caddy:
sentinel.example.com {
reverse_proxy localhost:8080
}Caddy handles TLS automatically via Let's Encrypt. No additional Sentinel config is required.
| Cookie | HttpOnly | SameSite | Secure | Purpose |
|---|---|---|---|---|
sentinel_session |
Yes | Lax | Configurable (SENTINEL_COOKIE_SECURE) |
Session authentication |
sentinel_csrf |
No | Lax | Configurable | CSRF double-submit value |
oidc_state |
Yes | Lax | Configurable | OIDC CSRF state (temporary, cleared after callback) |
oidc_nonce |
Yes | Lax | Configurable | OIDC nonce for replay protection (temporary, cleared after callback) |
oidc_pkce |
Yes | Lax | Configurable | PKCE code verifier for code interception protection (temporary, cleared after callback) |
Recent dependency bumps addressing known vulnerabilities:
- grpc v1.79.3 - fixes connection handling edge cases in the gRPC transport layer used by cluster communication.
- go-jose v4.1.4 - fixes JWT parsing issues in the JOSE library used by the OIDC provider.
Keep Sentinel updated to the latest release to receive these and future security patches.
| Variable | Default | Description |
|---|---|---|
SENTINEL_AUTH_ENABLED |
true (DB default) |
Override auth on/off; false grants full admin to all requests |
SENTINEL_SESSION_EXPIRY |
720h |
Session lifetime |
SENTINEL_COOKIE_SECURE |
true |
Set Secure flag on cookies |
SENTINEL_TLS_CERT |
Path to TLS certificate PEM | |
SENTINEL_TLS_KEY |
Path to TLS private key PEM | |
SENTINEL_TLS_AUTO |
false |
Auto-generate self-signed certificate |
SENTINEL_WEBAUTHN_RPID |
WebAuthn Relying Party ID | |
SENTINEL_WEBAUTHN_ORIGINS |
Comma-separated WebAuthn origins | |
SENTINEL_WEBAUTHN_DISPLAY_NAME |
Docker-Sentinel |
WebAuthn display name |
Getting Started
Using Sentinel
Multi-Host
Security
Reference