Commit Graph
155 Commits
Author SHA1 Message Date
Jarek Krochmalski 909e9934d3 compose generated from a container now captures network config and resource limits 2026-08-27 07:34:37 +02:00
Jarek Krochmalski fa1987add6 KeePassXC as a secret provider - pull secrets from a .kdbx via keepassxc-cli, bulk or inline refs 2026-08-27 07:34:37 +02:00
Jarek Krochmalski d5d979aa5d hide tls certificate fields for backends that never use tls 2026-08-25 05:42:29 +02:00
Jarek Krochmalski 730ad8ff82 fix the container backups tab crashing when a container has a mount 2026-08-23 19:00:40 +02:00
Jarek Krochmalski 49d71c01b6 generate a compose file from any container - save as new or append to existing stack 2026-08-23 12:08:30 +02:00
Jarek Krochmalski 9a7ef9c12c MQTT notification channel - publish events to a broker 2026-08-23 11:00:36 +02:00
Jarek Krochmalski 4af255ebc9 clarify stack env-var api docs (git vs internal storage, secret masking) 2026-08-23 10:21:00 +02:00
Jarek Krochmalski 4ba349113d branch selector on git stack, with per-stack branch override 2026-08-23 10:21:00 +02:00
Jarek Krochmalski 534ecf4564 fix switching an Infisical provider back to a static token 2026-08-23 10:21:00 +02:00
Jarek Krochmalski 114f21385a sort IPv6 subnets and addresses numerically instead of randomly 2026-08-23 10:21:00 +02:00
Jarek Krochmalski 70491423dc Pushover notifications can target specific devices 2026-08-23 10:20:59 +02:00
Jarek Krochmalski 7692758467 TLS backup repositories with a private CA and optional mutual TLS 2026-08-23 10:20:59 +02:00
Jarek Krochmalski f6ffadbacb stack and container icons - pick an icon per stack or per container (survives recreation), plus automatic app logos on containers 2026-08-23 10:20:59 +02:00
Jarek Krochmalski b165e4608a branch selector on git stack, with per-stack branch override 2026-08-22 17:11:57 +02:00
Jarek Krochmalski 68821f4d92 branch selector on git stack, with per-stack branch override 2026-08-22 16:22:18 +02:00
Bartosz S. 28bb291c32 feat: add branch selection to Git Stack modal with branches API endpoint (#1343)
* feat: add branch selection to Git Stack modal with branches API endpoint

- Add branch dropdown in create mode (fetches branches from new API endpoint)
- Add branch field in edit mode for existing Git stacks
- Remove redundant branch badge next to repository URL in edit mode
- Add POST /api/git/branches endpoint to list available branches
- Export buildGitEnv, buildRepoUrl, execGit from git.ts for reuse

* cla: recheck with correct author email

* fix: harden git branches endpoint and fix credential resolution

* feat(git-stacks): per-stack branch override

Add a per-stack 'branch' column to git_stacks so each stack can deploy
from a branch different from its repository's default.

- Migration 0012 (drizzle + drizzle-pg): add nullable 'branch' column
- resolveStackBranch() in git.ts: effective branch = per-stack override,
  else repository default; wired into sync/deploy/test paths
- db.ts: persist/read branch; GitStackData.branch is string | null
- API routes (preview-env, stacks create/update): honor explicit branch
  override with partial-update semantics (null/empty clears the override)
- UI: GitStackModal shows a 'Repository default' sentinel + per-stack
  selector; GitSourceBadge displays effective branch with a '(per-stack)'
  marker; stacks page passes through the per-stack branch
- tests/git-stack-branch.test.ts: unit coverage for resolveStackBranch
  and effective-branch display semantics

* feat(git-branches): security hardening per PR #1343 maintainer review

Adopts the three security recommendations from the maintainer review on
the git-branch-stack feature:

1. SSRF guard (assertSafeBranchTarget)
   New src/lib/server/git-branch-lookup.ts rejects repository URLs that
   point at private/loopback/link-local addresses before any git
   subprocess is spawned. Covers IPv4 (127/8, 169.254/16, 10/8,
   172.16/12, 192.168/16, 100.64/10, 0/8), IPv6 (::1, fe80::/10,
   fc00::/7, ::), IPv4-mapped IPv6 (::ffff:a.b.c.d), and a 25-entry
   hostname denylist (localhost, metadata.google.internal,
   host.docker.internal, etc.). Raw-URL parse only — no DNS resolution,
   avoiding TOCTOU/rebinding. Runs on BOTH the url and repositoryId
   paths (a stored repo's URL could also point internal).

2. Credential-exfiltration guard (assertCredentialUrlPair)
   A raw url paired with a stored credentialId is only allowed when the
   credential is plausibly for that host. SSH and none credentials are
   always allowed (no secret to leak). Password credentials: allowed
   when the username equals the URL host, or when the credential name
   (or its hyphen/underscore-separated segments) equals the host or its
   first label (e.g. "github-pat" → github.com). Inference-based — no
   new DB column. The repositoryId path is not subject to this guard
   (the pairing is the user's own stored config).

3. Timeout on git subprocesses
   execGit now accepts a timeoutMs parameter (default GIT_TIMEOUT_MS =
   20s, env-overridable). On timeout the process is killed with SIGKILL
   and the caller receives { timedOut: true, code: 124 }. Applied to all
   execGit call sites (clone, pull, fetch, ls-remote, etc.) so a dead
   or black-holed remote cannot stall a request indefinitely.

OpenAPI: @openapi JSDoc block added to the branches endpoint; spec
regenerated via the upstream generator (359/359 coverage, all 6 gates
pass). /api/git/branches POST entry added to openapi.generated.json.

Svelte5: fix-svelte-optional-params.sh applied to 7 .svelte files
(x?: T → x: T = undefined) to work around esbuild's rejection of
TS-only optional-parameter syntax in lang="ts" script tags.

Tests: 59 tests in tests/git-branches.test.ts covering SSRF (blocked
IPs, boundary cases), credential affinity (allowed/rejected cases),
endpoint pipeline ordering, and parseRepoHost edge cases.

Rebased onto upstream/main (a485c85) — zero conflicts; GitStackModal.svelte
had not drifted since the branch was created.

* fix(git): close SSRF/RCE/exfil gaps across all git URL+credential endpoints (PR #1343 re-review)

1. SSRF: replace hand-rolled assertSafeBranchTarget with shared URL()-based
   isSafeNotificationUrl — blocks decimal/octal/hex/short-form IPv4 and
   v4-mapped IPv6 encodings (all six maintainer bypass payloads now blocked).
   ALL schemes (http/https/ssh/git/scp-like) now judge the CANONICALIZED
   host through that single check: non-http(s) schemes can't be passed to
   URL() as-is, so the parsed host (port-stripped, IPv6 bracketed only
   when it contains a colon) is run through isSafeNotificationUrl under
   an https:// scheme. Closes the ssh/git encoded-IP bypass (decimal
   2130706433, hex 0x7f000001, short-form 127.1 over ssh://, git:// and
   scp-like all resolve to 127.0.0.1 and are now blocked).
2. Credential affinity: drop the dead second rule — with rule (i) being
   username === host, the single-label variant was identical and unreachable.
3. listRemoteBranches + testRepositoryConfig call assertSafeRepoUrl explicitly
   so the ext::/file:: transport denylist holds on every path into a git
   subprocess, not only via buildRepoUrl.
4. preview-env: assertSafeRepoTarget (SSRF+RCE) + repoFilePath containment for
   composePath/envFilePath (path traversal) + assertCredentialHostMatch
   (exfil).
5. repositories/test: assertSafeRepoTarget + assertCredentialHostMatch.

Tests: 134 across 3 files (git-branches, preview-env-security,
repositories-test-security) — lock the six http(s) encoding bypasses, the
four ssh/git encoded-IP payloads at unit and endpoint level, the subdomain
attack, single-label username, path traversal, and the endpoint pipelines.

Verified safe (no change): backup/destinations/test, registries/test,
environments/test, git/repositories CRUD, git/stacks, git/repositories/[id]/test.

* fix(git): address branch stack review feedback

- scope remote branch lookup timeout to ls-remote only
- require git:edit permission for preview-env
- clarify SSRF policy while preserving private LAN Git hosts
- support free-text branch selection for new repositories
- share production branch resolution logic with tests and UI
- add testable preview-env request pipeline
2026-08-22 10:20:43 +02:00
Jarek Krochmalski e23936b4df azure key vault as a secret provider (bulk pull or inline azurekv:// references) 2026-08-22 09:09:41 +02:00
Jarek Krochmalski a760680b8d detect newer version tags for pinned images and show them as a badge with release notes 2026-08-22 09:09:41 +02:00
jarek a485c8580b detect newer version tags for pinned images and show them as a badge with release notes 2026-08-20 08:28:26 +02:00
jarek b56e1ef7f9 Compose validate - a preflight linter & validator for compose files 2026-08-20 08:28:26 +02:00
jarek f50c3e52f3 registry auth survives an http to https token realm redirect behind a TLS proxy 2026-08-20 08:28:26 +02:00
jarek 99b1672be5 detect newer version tags for pinned images and show them as a badge with release notes 2026-08-19 14:26:45 +02:00
jarek 29795c074c Bitwarden Secrets Manager as a secret provider via an operator-installed bws client 2026-08-19 14:26:45 +02:00
jarek 2584e52852 Compose validate - a preflight linter & validator for compose files 2026-08-19 14:26:45 +02:00
jarek adabaffb23 API: validating a stack's env vars no longer drops the environment id 2026-08-18 10:05:37 +02:00
jarek 9b6b6a9d3e Infisical: the project ID is optional when using a service token - it's taken from the token 2026-08-18 10:05:36 +02:00
jarek 088c7dc343 OpenAPI spec at /api/docs with a Scalar viewer, opt-in via FEAT_API_DOCS 2026-08-18 10:05:36 +02:00
jarek 1ad16a7c2a Compose validate - a preflight linter & validator for compose files 2026-08-18 10:05:36 +02:00
Sergei Nesterkov 7a007bd623 feat(notifications): add Zabbix history.push provider (#1419)
* feat(notifications): add Zabbix history.push provider

* test(notifications): add Zabbix provider tests
2026-08-17 15:24:47 +02:00
jarek da26f7f764 OpenAPI spec at /api/docs with a Scalar viewer, opt-in via FEAT_API_DOCS 2026-08-17 09:50:34 +02:00
jarek ba8670af66 detect newer version tags for pinned images and show them as a badge with release notes 2026-08-17 09:50:34 +02:00
jarek 95fd26656b Bitwarden Secrets Manager as a secret provider via an operator-installed bws client 2026-08-17 09:50:34 +02:00
StefanSa 593659f652 Add Bitwarden Secrets Manager provider via external bws (#1416)
* add external bws Bitwarden secrets provider

* add self-hosted Bitwarden server support
2026-08-17 08:15:36 +02:00
jarek 0a08c94287 optional indentation guides in the compose/YAML editor 2026-08-16 08:15:30 +02:00
jarek 19548772df reject duplicate secret provider name with a 400 2026-08-14 15:59:50 +02:00
jarek 5fef45e003 validate secret provider config before save 2026-08-14 14:01:36 +02:00
jarek 344ad4aadc external vault providers - live probing secrets when editing stack 2026-08-14 10:40:02 +02:00
jarek a38e973155 grouped container logs now show one merged timeline sorted by time 2026-08-14 09:35:37 +02:00
jarek 32a86129d0 stack secrets from external providers (1Password, HashiCorp Vault, Infisical and Doppler) 2026-08-14 09:35:36 +02:00
jarek 959a426507 internal cleanups and build config 2026-08-13 10:20:30 +02:00
jarek 3c75bd4919 backup engine internals - restic timeout and stack-dir capture 2026-08-13 10:20:30 +02:00
jarek 610e46dc19 read-side backup snapshot endpoints use job polling 2026-08-13 10:20:30 +02:00
jarek f1a3311c90 start, stop or restart on Hawser updates its files on the remote host, not only on redeploy 2026-08-13 10:20:29 +02:00
jarek cc0d50913a updating a compose file over the API now saves it, not just deploys it 2026-08-13 10:20:29 +02:00
jarek 16ee5d82b9 reopening the MFA setup screen while MFA is enabled no longer wipes the secret and backup codes 2026-08-13 10:20:29 +02:00
jarek ce93e92f6e support caddy-docker-proxy labels 2026-08-13 10:20:29 +02:00
jarek c91f38eddf upload a Docker image .tar through the UI (docker load) for air-gapped hosts 2026-08-13 10:20:29 +02:00
jarek f43736d94a stack secrets from external providers (1Password, HashiCorp Vault, Infisical and Doppler) 2026-08-13 10:20:29 +02:00
1ad36a83fb feat: Add pluggable secret providers (1Password, Infisical, HashiCorp Vault) (#1360)
* Add pluggable secret provider foundation

Introduces a provider-agnostic secret backend abstraction so stacks can
pull deploy-time secrets from 1Password, Infisical or HashiCorp Vault.

Providers implement the SecretProvider contract in secretproviders/shared.ts
and advertise which of the two resolution modes they support:

  - reference interpolation: a variable's value is an inline reference in
    the backend's own syntax (e.g. 1Password's op://vault/item/field)
  - bulk pull: every secret under a provider-defined selector, fetched as
    a flat key/value map

Adding a backend means dropping a file in secretproviders/ and registering
it in the index; no caller changes.

Four providers ship here: 1Password service account (references + bulk),
1Password Connect (references), Infisical (bulk) and HashiCorp Vault KV v2
(bulk). Provider config is persisted as an encrypted JSON blob in the new
secret_providers table, mirroring the existing notification_settings
type+config pattern, and stack_sources gains a nullable secret_provider_id.

Decrypted tokens stay in the provider modules for the duration of a call
and are never written to disk or returned to the client.

Co-authored-by: GooRoo <sergey.olendarenko@gmail.com>

* Add secret provider API routes and permissions

CRUD plus two test-connection endpoints under /api/secret-providers. The
unsaved variant validates a config posted from the provider form; the
stored variant tests a persisted provider by id.

Responses only ever carry provider summaries (id, type, name, timestamps).
The decrypted config never leaves the server.

Adds a "secrets" permission resource with view/create/edit/delete actions,
wired through the permission type, the role editor and the seeded roles,
and a secret_provider audit entity type so provider changes are logged.
Config rotations are audited as a boolean rather than a value.

* Resolve provider secrets at stack deploy time

Stacks can bind a secret provider; when one is bound, its secrets are
resolved and merged into the vars handed to docker compose.

Bulk pull is triggered by a selector variable: OP_ENVIRONMENT_ID, kept for
compatibility with the original 1Password integration, or the generic
DOCKHAND_SECRET_SELECTOR for any bulk-capable provider. The selector is
consumed by the provider and stripped before the vars reach the stack.
Values the bound provider recognises as inline references are resolved in
place, and references found only in the stack's .env file are promoted into
the secret set so they are never written back as literals.

Resolution is applied on deploy, on up, on recreate and on service update.
Explicitly configured secrets always win over bulk-pulled ones. A missing
or unsupported provider warns and leaves values untouched rather than
failing the deploy; a provider that errors mid-resolution fails it.

* Add secret provider settings UI and stack binding

A Secrets tab under settings lists configured providers and drives the
create/edit modal, which switches its fields on the selected provider type
and can test a connection before saving.

Both stack modals gain a provider selector so a stack can be bound to a
provider or left unbound. When a provider is bound, the bulk selector
variables are no longer reported as unused compose variables, since they
are consumed by the provider rather than the compose file.

---------

Co-authored-by: GooRoo <sergey.olendarenko@gmail.com>
Co-authored-by: Jarek Krochmalski <jotka@users.noreply.github.com>
2026-08-13 10:08:02 +02:00
jarek 905c4a004d persist restic cache and backup restore fixes 2026-08-09 13:46:03 +02:00