Skip to content

Releases: albatroxxx/zanskar

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:34
e711c2e

Zanskar 1.4.0

A security release. In October the whole gateway was read end to end, by tools and by six
reviewers with a brief each, and every finding was fixed: eight serious ones in the session
path and at sign-in, thirty-three of medium weight, seventeen smaller ones, thirty-one plain
bugs, and the dead and duplicated code that let some of them hide. The published 1.3.0 image
also carries three Go advisories (denial of service in HTTP/2, HTTP range requests and TLS);
this release is built with the fixed Go 1.27.2.

Nothing here needs a new setting to keep working. Read Upgrading from 1.3.0 below before
you install: one migration rewrites data, and a few behaviours changed on purpose.

What was wrong and is now fixed

Sessions obey the policy that admitted them

  • A connect ticket for a desktop or PowerShell session could be presented to the terminal
    endpoint and open an SSH shell on a Windows host that also runs OpenSSH, with the RDP
    credential, when the policy allowed only RDP. Every session endpoint now checks that the
    ticket was issued for its protocol.
  • A ticket is now redeemed only by the sign-in that asked for it, within its thirty seconds,
    and the policies and the account's roles are checked again at that moment. A leaked ticket
    is useless without the cookie, signing out ends it, and an account made review-only in
    between starts nothing.
  • A clipboard-off policy used to drop only the first frame of a clipboard transfer; the text
    itself still reached the browser. The desktop bridge now drops the whole transfer, and guacd
    is told to disable copy and paste at the source.
  • The browser's clipboard goes to a desktop only when you press Ctrl+V or Cmd+V in it. Before,
    it was read every time the window regained focus, so a password copied for something else
    landed on the remote machine's clipboard.
  • An approval-gated policy no longer lends its clipboard, file, MFA and timeout settings to a
    session that a standing policy admitted. A selector tag with an empty value no longer
    matches every target that lacks the tag.
  • Only keystrokes keep a session from going idle; window resizes and keep-alive frames do not.
    A session window written as 00:00 to 00:00 means all day rather than never.

Taking access away ends live sessions

  • Disabling or locking an account, changing its roles, revoking its sign-ins, removing it from
    a group, and deleting or narrowing a policy now end the SSH, desktop, PowerShell and
    database sessions that access allowed. Before, an open session ran until its idle timeout,
    up to twenty-four hours, and the only way to stop it was to find and end each one by hand.
  • The person sees why: the session closes with its reason (policy revoked, ended by an
    administrator
    , gateway restarting), not as a lost connection, and the console no longer
    offers to switch to another autoscaling instance when the gateway itself ended the session.
  • Live shadowing of SSH and PowerShell sessions works. The auditor's watch page received the
    start and the end of a session and nothing in between; the bridge was never connected to it.

Sign-in

  • After single sign-on, the gateway could be made to redirect to another site through a
    crafted return path (a tab character passed the check and browsers strip it). The return
    path must now be a path on this site, and the sign-in page no longer shows text from the
    address bar, so a link cannot make the real page carry a scam message.
  • Wrong authenticator codes now count against the account like wrong passwords: five lock it
    for fifteen minutes. Before, a correct password reset the count, so codes could be guessed
    without limit. A wrong current password on the change-password form counts the same way,
    and adding an authenticator from a signed-in session asks for the password.
  • A temporary lock from five wrong passwords no longer throws that user out of the console.
    Anyone who knew a username could do that every fifteen minutes. An account an administrator
    disables or locks is still refused on its next request.
  • Completing a sign-in issues a new session token, so a cookie planted before sign-in never
    becomes a signed-in one. Changing your own password ends your other sessions.
  • The number of password checks running at once is capped (each costs 64 MiB), rate limiting
    groups IPv6 addresses by /64, and a flood of refused sign-ins is audited once, not per
    attempt, so it cannot grow the hash-chained log without bound.
  • Accounts created ahead of time for a directory or single sign-on user can sign in: the
    gateway adopts a local account with the same username and no password, and records the link.
    Before, auto_provision: false could never admit anyone.
  • An account provisioned at first sign-in is recorded as user.create with its roles. Group
    memberships a sign-in changes are recorded, and removal from a group ends the sessions that
    group allowed. Identity provider settings that affect access are recorded before and after
    each change.
  • LDAP group mappings written as a distinguished name are compared exactly, so a same-named
    group in another OU no longer matches. The console form accepts DNs and the guide recommends
    them.
  • Allowed email domains apply only to an address the provider marked verified. Entra ID never
    sends that mark, so a provider setting, trust email, accepts its addresses; the console
    says when to tick it.
  • Behind a proxy that terminates TLS, set ZANSKAR_PUBLIC_URL to the https address people use,
    and single sign-on builds the redirect URI the provider has registered. It could not before,
    which made OIDC unusable in that deployment.

Database sessions

  • The client container's network is internal: it reaches the relay and nothing else. Before,
    it had the gateway host's full network, and the official psql and mysql images carry
    shell escapes. The session user also no longer gets pgbouncer's admin console, which showed
    the upstream host and user and could pause the pool.
  • At most twenty database sessions run at once (ZANSKAR_DATABASE_MAX_SESSIONS; 0 lifts the
    cap), and a reaper removes client containers a crashed gateway left behind.
  • The docker commands the gateway runs inherit none of its secrets. The gateway drops its
    master key, database address and webhook secret from its own environment once read; keep
    the key in ZANSKAR_MASTER_KEY_FILE and it never appears in /proc/<pid>/environ either.

Audit

  • A client that disconnected right after a change could leave it unaudited: the audit write ran
    under the request's context and was cancelled with it. Audit writes now finish regardless.
  • Audit anchors, as ADR 0008 promised. The gateway signs the head of the log at start, every
    hour, and on zanskar audit anchor, under a key derived from the master key, and logs each
    anchor as a line to ship off the host. zanskar audit verify and the Events page check the
    anchors, the id sequence and any anchor you pass in, so deleting the newest events is noticed;
    before, only an edit in the middle of the chain was.
  • Every file transfer attempt is audited with its outcome; a failed upload went unrecorded and
    an interrupted download was recorded as a success. A recording that could not be saved is
    audited as recording.lost and never shown as a finished recording that then fails to play.
  • Host-key and certificate mismatches on autoscaling instances are audited when first seen,
    not on every poll (about 2,880 events a day per instance before). The SIEM webhook follows
    no redirect, so a signed batch is never re-sent elsewhere or over plain http.
  • Audit action names are constants, checked against the console's labels by a test, so a new
    action cannot ship without a label.

Targets and certificates

  • A probe never replaces a pinned RDP or WinRM certificate. A different certificate is held as
    pending, audited, and pinned only when an administrator accepts it on the target's page. A
    certificate mismatch at RDP connect time is audited like SSH and WinRM mismatches are.
  • Addresses that must never be targets are refused when a target is saved and again when a
    session dials it, for every protocol, database targets included: loopback and link-local
    addresses, the gateway's own address, and the metadata services of AWS (IPv4 and IPv6), EKS
    Pod Identity, Alibaba Cloud, Azure and Oracle Cloud.
  • Approval-gated access to an autoscaling group can be requested from the user portal. The
    group showed as allowed, connecting was refused, and there was no way to ask.
  • The name of a cloud-side autoscaling group is quoted in the paste-ready IAM script and the
    CloudFormation template, so a quote or a line break in it cannot change what the script does.
    A group stores its values trimmed, GovCloud and China role ARNs are accepted, Test access
    assumes the role afresh instead of reusing credentials cached for fifteen minutes, a failing
    group backs off exponentially, and groups sync side by side.
  • An uploaded TLS certificate that cannot serve (a clock behind its start date at boot, say) is
    kept, not deleted, and the Settings card says why it is not in use. Before, the certificate
    and its key were removed and, with HSTS, the console was unreachable. A failed regeneration
    leaves the old self-signed certificate serving, and that certificate renews while the gateway
    runs, within thirty days of its end.

Operations

  • zanskar init --force on an install that keeps its key in ZANSKAR_MASTER_KEY_FILE keeps it
    there. Before, it minted a new inline key and the service could not open anything.
  • zanskar restore run as root gives the database and recordings to the owner of the files
    they replace, so the service's own user can open them.
  • zanskar key rotate-master writes the new key only where the service reads the old one; any
    other file is refused, and it re-signs the audit anchors under the ne...
Read more

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 07:51
48786e0

Zanskar 1.3.0

Administrators can now work from a command line inside the console. It runs Zanskar
commands only, opens with a fresh code from your authenticator, and records every line in the
audit log. Authenticator codes also become single-use, and the API documentation now covers
every route the gateway serves.

Changes

A command line in the admin console

  • The terminal-window icon at the top right of every admin page, or Ctrl+`, opens it at the
    bottom of the page. help lists the 34 commands: sessions, users, targets, policies, access
    requests, events, logs, settings, autoscaling groups and a restart.
  • It is not a shell. Pipes, redirection, variables and anything not in the list are refused,
    no command takes a password or key, and backup, restore, key rotation and audit repair stay
    on the host.
  • Only administrators get it. Each command is the same API request a console page makes,
    through the same permission checks, so it can do nothing the console cannot.
  • It opens with a code from your authenticator (a recovery code works too), and asks again
    after 15 minutes without a command. Five wrong codes close it for 15 minutes without
    signing you out. A sign-in that proved no code (an identity-provider account, or MFA not
    required) does not open it.
  • Commands that end, disable, revoke, change or restart something say what will happen and
    ask you to type a word, usually the name of what you are changing.
  • Every line is an audit event, including lines that only read. The Events page shows it as
    ran "sessions --live" on the command line, with the API routes it called in the details.
  • ZANSKAR_CONSOLE_CLI=off in the environment file turns it off. It is deliberately not a
    console setting, so a stolen sign-in cannot turn it back on.
  • The reference of every command is in docs/console-cli.md; the design
    is ADR 0027.

Each authenticator code works once

  • Before, a code that had been accepted was accepted again for the rest of its window, about
    a minute and a half with the allowance for clock drift. Someone who saw an administrator
    type a code, over a shoulder or on a screen share, and also knew the password, could
    reuse it.
  • Now the gateway remembers the last code accepted for each user and refuses that code and
    any earlier one, at sign-in, at enrolment and at the command line. The next code works as
    usual.
  • A reused code is recorded as one: failed the second factor (a code already used). Seeing
    that for someone who did not retry may mean someone else read their code.

The API documentation covers every route

  • Fourteen routes that were missing are documented, among them file transfer, the WinRM,
    database and shadow WebSockets, identity providers on the sign-in page and the audit
    filters. A test now fails if a route is added without documentation.
  • Entries that disagreed with the server are corrected: group membership updates,
    autoscaling group responses (and that their list is not paged), ending a session, the audit
    verification result, the session list's filters, the terminal WebSocket's messages and the
    identity provider types. next_cursor is absent on the last page, not null.

Dependencies

  • The session player in the console no longer pulls in a serializer with published
    advisories. The affected code was never part of the built console.

Upgrading from 1.2.2

Install the new package, or set ZANSKAR_VERSION=1.3.0, and restart. Two migrations run at
start and add one column each, to sign-in sessions and to authenticators. No data is
rewritten.

The command line is on for administrators straight away. To keep it off, set
ZANSKAR_CONSOLE_CLI=off in /etc/zanskar/env before restarting.

One behaviour change: an authenticator code works once. A person signing in will not
notice. A script that signs in with a code and then sends the same code again, for example
to confirm enrolment and sign in within the same 30 seconds, must wait for the next code.

API changes: new routes under /api/v1/admin/cli for the console command line
(administrators only). A reused code is answered like a wrong one: 401 invalid_code at
sign-in. No other request or response changed; the documentation corrections describe what
the server already did.

Install

Packages (recommended for a single server) — Debian/Ubuntu and RHEL/Fedora/SUSE, amd64 and arm64:

sudo apt install ./zanskar_1.3.0_linux_amd64.deb     # or: sudo rpm -i zanskar_1.3.0_linux_amd64.rpm
sudo zanskar init                                       # writes /etc/zanskar/env and prints the next steps

Container image — ghcr.io/albatroxxx/zanskar:1.3.0 (linux/amd64, linux/arm64). Single-node stack with TLS: deploy/docker-compose.yml with ZANSKAR_VERSION=1.3.0.

Verify — checksums and the image are signed with Sigstore (keyless, GitHub Actions identity).
cosign is not in the Ubuntu, Debian or RHEL repositories; install it from
its own release (cosign_<ver>_amd64.deb,
cosign-<ver>-1.x86_64.rpm) or see Sigstore's installation page.

sha256sum -c SHA256SUMS --ignore-missing
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.3.0$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
cosign verify ghcr.io/albatroxxx/zanskar:1.3.0 \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.3.0$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Build provenance and SBOM — zanskar.intoto.jsonl is SLSA level 3 provenance for every file
in SHA256SUMS: which workflow, at which commit, built it. Check a download against it with
slsa-verifier:

slsa-verifier verify-artifact zanskar_1.3.0_linux_amd64.deb \
  --provenance-path zanskar.intoto.jsonl \
  --source-uri github.com/albatroxxx/zanskar --source-tag v1.3.0

Each .tar.gz has an SPDX software bill of materials beside it (….tar.gz.sbom.json) listing
the Go modules compiled into the binary, with versions. The .deb and .rpm for an
architecture carry the same binary, so its archive's SBOM describes them too.

Full guide: docs/deploy.md.

Changelog

  • dd12f7f build(deps): Bump anchore/sbom-action/download-syft (#151)
  • d1f8eb4 build(deps): Bump the go-deps group with 3 updates (#147)
  • e53be4f build(deps-dev): Bump the web-deps group in /web with 3 updates (#146)
  • 31b2606 feat: console command line (ADR 0027), and the Silver badge docs (#148)
  • 3193de8 fix: single-use authenticator codes; command line docs, screenshots and full API spec (1.3.0 prep) (#152)
  • 48786e0 release: 1.3.0 notes, chart and site version, roadmap (#153)

v1.2.2

Choose a tag to compare

@github-actions github-actions released this 04 Oct 14:46
f859778

Zanskar 1.2.2

A fix release on top of 1.2.1. Moving a target no longer looks like an attack, WinRM
sessions on Windows autoscaling groups can fail over again, recordings waiting to upload to
S3 cannot be swapped by another user on the machine, and the console reads in plain words
throughout. Behind it, four in five lines of the gateway are now exercised by tests.

Changes

Moving a target starts its trust afresh

  • Saving a target with a new address now clears its trusted SSH host key and its pinned RDP
    and WinRM certificates, as the API documentation always said it would. A new address may
    be a different machine, so probe it and trust it again.
  • Before, the old key stayed trusted. The next connection was refused as a host key
    mismatch
    , audited like an attack, when an administrator had only moved the target.
  • The console warns under the address field before you save, and the audit event says the
    trust was reset.

WinRM sessions on autoscaling instances can fail over again

  • A WinRM session never told the browser its session id, so when its instance was lost the
    move to another instance dialog did not appear. SSH sessions were not affected.

Recordings waiting to upload to S3 are kept private

  • With S3 storage, a recording in progress waits in /tmp/zanskar-spool unless
    ZANSKAR_RECORDINGS_SPOOL_DIR says otherwise. On a machine shared with other users, one of
    them could create that folder first and replace a recording before it was uploaded.
  • The gateway now refuses a spool folder it does not own, or that others can write to, and
    says which setting to change. The package's service and the containers were never
    exposed: they have a private /tmp.

The console in plain words

  • Access requests read as sentences in the Events page: approved bob's request for SSH
    access to web-1
    , not approved access request d2091e4b.
  • An event whose user has been deleted says deleted user, not an id fragment.
  • Protocols read as names everywhere: SSH, RDP, VNC, WinRM and database (it used to read
    DATABASE and WINRM).
  • Dates in an event's details read as dates, not as raw timestamps.

Smaller fixes

  • The file panel beside terminal and desktop sessions works from the keyboard: folders and
    files are buttons you can Tab to.
  • Database names no longer wrap at a hyphen when the endpoint is long, and table borders
    stay straight beside the Edit and Delete buttons.
  • Confirming an authenticator that is already confirmed answers already enrolled (409),
    not an internal error (500). Nothing could be replaced either way.
  • The Helm chart's migration job has memory and CPU limits.

The API documentation matches the server

  • Six update operations were documented as PATCH but are PUT (users, groups, identity
    providers, targets, access policies, autoscaling groups), and four documented operations
    did not exist. The specification now matches, and a test keeps it that way.
  • The response of POST /connect is documented as what it is: ticket, expires_at and
    ws_path.

Tested more thoroughly

  • 80% of the gateway's own code is exercised by tests, up from about two-thirds at 1.2.1
    (counted the same way): whole
    terminal, desktop, WinRM and database sessions; who may open, watch or review them;
    approvals; backup and restore; key rotation; first start with HTTPS. Database sessions run
    for real in Docker in CI. Several of the fixes above were found by these tests.
  • The web console has a test runner of its own.

Upgrading from 1.2.1

Install the new package, or set ZANSKAR_VERSION=1.2.2, and restart. No migration runs and
no setting needs to change.

One behaviour change: saving a target with a different address now clears its trusted
host key and certificates. Probe it and trust it again before users connect. Renaming a
target, or changing anything other than its address, keeps them.

If you run the gateway by hand (not as the package's service or a container) with S3
recording storage on a machine other people use, set ZANSKAR_RECORDINGS_SPOOL_DIR to a
private directory, for example /var/lib/zanskar/spool. Otherwise a recording is refused
when the shared default is not safe, with a message saying so.

API changes: none to requests. Confirming an already-confirmed authenticator now returns
409 already_enrolled instead of 500. A client written from the old specification that sent
PATCH for one of the six updates was already getting a 405; send PUT with the whole object.

Install

Packages (recommended for a single server) — Debian/Ubuntu and RHEL/Fedora/SUSE, amd64 and arm64:

sudo apt install ./zanskar_1.2.2_linux_amd64.deb     # or: sudo rpm -i zanskar_1.2.2_linux_amd64.rpm
sudo zanskar init                                       # writes /etc/zanskar/env and prints the next steps

Container image — ghcr.io/albatroxxx/zanskar:1.2.2 (linux/amd64, linux/arm64). Single-node stack with TLS: deploy/docker-compose.yml with ZANSKAR_VERSION=1.2.2.

Verify — checksums and the image are signed with Sigstore (keyless, GitHub Actions identity).
cosign is not in the Ubuntu, Debian or RHEL repositories; install it from
its own release (cosign_<ver>_amd64.deb,
cosign-<ver>-1.x86_64.rpm) or see Sigstore's installation page.

sha256sum -c SHA256SUMS --ignore-missing
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.2$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
cosign verify ghcr.io/albatroxxx/zanskar:1.2.2 \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.2$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Build provenance and SBOM — zanskar.intoto.jsonl is SLSA level 3 provenance for every file
in SHA256SUMS: which workflow, at which commit, built it. Check a download against it with
slsa-verifier:

slsa-verifier verify-artifact zanskar_1.2.2_linux_amd64.deb \
  --provenance-path zanskar.intoto.jsonl \
  --source-uri github.com/albatroxxx/zanskar --source-tag v1.2.2

Each .tar.gz has an SPDX software bill of materials beside it (….tar.gz.sbom.json) listing
the Go modules compiled into the binary, with versions. The .deb and .rpm for an
architecture carry the same binary, so its archive's SBOM describes them too.

Full guide: docs/deploy.md.

Changelog

  • 744346e ci(pages): deploy the site when a release is published (#117)
  • 0369ff8 ci: measure coverage over the gateway's own code, not the hack/ test tools (#134)
  • 3db6c9f docs(adr): 0018 records that an admin may approve their own request (#128)
  • 41cb5e0 docs(api): ConnectTicket is what POST /connect actually returns (#133)
  • 54da6e1 docs(site): 1.2.1 screenshots; no version on the Install button (#118)
  • 884ded8 docs(site): no Zanskar version or roadmap phase in page text (#121)
  • f0218b6 fix(connect): WinRM sessions tell the browser their id; WinRM tested end to end (#138)
  • 4a50a36 fix(target): a new address clears the trusted host key and pins (#129)
  • 725e78c fix: SonarCloud findings: spool folder, keyboard file panels, and triage of all 141 (#122)
  • c50fc4d fix: four console findings from the 1.2.1 screenshot shoot (#120)
  • f859778 release: 1.2.2 — notes, site, chart, screenshots, and the last plain-words fixes (#145)
  • 2381a5c test(access, policy, audit): approvals, policy changes and SIEM export (#127)
  • 7e2fc30 test(cli): backup, restore, audit, key, admin and init as operators run them (#125)
  • 4b54aeb test(cli): exit codes, keygen, migrate and init's questions (#137)
  • efa4079 test(connect): desktop sessions, and the RDP pin checked at the gateway (#132)
  • 1646e6d test(connect): session access, tickets and file transfer; coverage measured suite-wide (#124)
  • 56f6da8 test(connect): the user's target list, watching a desktop, and failover (#136)
  • baf555b test(console): a test runner for the console, run in CI (#123)
  • ed1bf48 test(credential): no secret ever reaches the audit log (#130)
  • cde76bd test(dbgw): a real database session end to end, in Docker (#135)
  • fac50cc test(idp, group, users): weak IdP configs, empty member lists, MFA reset (#139)
  • 407f0b4 test(serve): the whole gateway starts, answers and stops in a test (#131)
  • 5bb1b5c test(session, auth): who reads recordings and the log, and how sign-in refuses (#126)

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 30 Sep 18:49
71c70ec

Zanskar 1.2.1

A security and supply-chain release on top of 1.2.0. Database connections are verified by
default, the master key can live in a file of its own, and every release file now comes
with a software bill of materials, signed build provenance and a build anyone can repeat.

Changes

Database TLS is verified by default (ADR 0025)

  • A new database target checks the database's certificate (verify-full) unless you
    choose otherwise, and so needs the CA bundle that signs it. For Amazon RDS that is your
    region's bundle, https://truststore.pki.rds.amazonaws.com/<region>/<region>-bundle.pem.
  • Encrypted, not verified and Encrypted if offered, not verified are still there, as an
    explicit choice, and such targets carry a warning in the console. Without verification,
    whoever can intercept the connection can pose as the database and receive its password.
  • The console says in words how each database target is protected, instead of showing the
    mode's name.

The master key can live in its own file (ADR 0025)

  • ZANSKAR_MASTER_KEY_FILE names a file holding the key, so it is not in
    /etc/zanskar/env beside every other setting. The file must be readable by its owner only,
    or the gateway refuses to start. docs/deploy.md has the four commands to move an
    existing key into one.
  • zanskar key rotate-master rewrites the key file in place, keeping its owner and mode.

Master key rotation cannot strand the key any more

  • rotate-master -env-file used to rewrap the database first and write the file second.
    Run as a user that could not write /etc/zanskar, it failed after the rewrap, and with
    -generate the new key had been printed nowhere, so every stored secret was lost. It now
    checks that the file can be replaced before touching anything, and if the final write
    still fails it prints the new key rather than dropping it.

Release files you can check three ways

  • An SPDX software bill of materials beside each .tar.gz (….tar.gz.sbom.json), listing
    the Go modules compiled in, with versions. The .deb and .rpm carry the same binary.
  • SLSA level 3 build provenance for every file (zanskar.intoto.jsonl): which workflow, at
    which commit, built it. Check it with slsa-verifier; the commands are below.
  • Reproducible builds: the Go toolchain is pinned, and archives and packages no longer
    record who built them, where or when, so rebuilding the tag gives the same bytes.

Also

  • klauspost/compress 1.18.7 (GO-2026-5841; not reachable from Zanskar's code).
  • Audit events read changed their password rather than change user alice.

Upgrading from 1.2.0

Install the new package, or set ZANSKAR_VERSION=1.2.1, and restart. Migration 0025 runs at
start: it writes prefer, the value those targets already used, into any database target
stored without a TLS mode, so no existing connection changes. Tighten each one to
Verified when you have its CA bundle.

API change: creating a database target without tls_mode now means verify-full, which
requires tls_ca; a request with neither gets a 400 that says what to send. Pass
"tls_mode": "require" (or "prefer") to keep the old behaviour deliberately.

ZANSKAR_MASTER_KEY in the environment file keeps working unchanged; the key file is an
option.

Install

Packages (recommended for a single server) — Debian/Ubuntu and RHEL/Fedora/SUSE, amd64 and arm64:

sudo apt install ./zanskar_1.2.1_linux_amd64.deb     # or: sudo rpm -i zanskar_1.2.1_linux_amd64.rpm
sudo zanskar init                                       # writes /etc/zanskar/env and prints the next steps

Container image — ghcr.io/albatroxxx/zanskar:1.2.1 (linux/amd64, linux/arm64). Single-node stack with TLS: deploy/docker-compose.yml with ZANSKAR_VERSION=1.2.1.

Verify — checksums and the image are signed with Sigstore (keyless, GitHub Actions identity).
cosign is not in the Ubuntu, Debian or RHEL repositories; install it from
its own release (cosign_<ver>_amd64.deb,
cosign-<ver>-1.x86_64.rpm) or see Sigstore's installation page.

sha256sum -c SHA256SUMS --ignore-missing
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.1$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
cosign verify ghcr.io/albatroxxx/zanskar:1.2.1 \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.1$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Build provenance and SBOM — zanskar.intoto.jsonl is SLSA level 3 provenance for every file
in SHA256SUMS: which workflow, at which commit, built it. Check a download against it with
slsa-verifier:

slsa-verifier verify-artifact zanskar_1.2.1_linux_amd64.deb \
  --provenance-path zanskar.intoto.jsonl \
  --source-uri github.com/albatroxxx/zanskar --source-tag v1.2.1

Each .tar.gz has an SPDX software bill of materials beside it (….tar.gz.sbom.json) listing
the Go modules compiled into the binary, with versions. The .deb and .rpm for an
architecture carry the same binary, so its archive's SBOM describes them too.

Full guide: docs/deploy.md.

Changelog

  • b009ca7 build(release): the same tag rebuilds to the same bytes on any machine (#113)
  • d3a5149 ci: OSV-Scanner and Trivy scans of the container image (#115)
  • e38c767 feat(security): verified database TLS by default; the master key in its own file (#114)
  • 71c70ec release: 1.2.1 on the site, chart and API spec (#116)

v1.2.1-rc.1

v1.2.1-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 30 Sep 18:52
f8bbaa9

Zanskar 1.2.1-rc.1

Superseded by 1.2.1. Install that instead.

This candidate was a check of the new release pipeline on a real tag. It is kept here as the record of how 1.2.1 was reached.

What it proved: the first release with an SPDX SBOM per archive and SLSA level 3 build provenance (zanskar.intoto.jsonl).

  • cosign, the checksums and slsa-verifier all pass.
  • slsa-verifier rejects a tampered file and a wrong source repository.

What it exposed: rebuilding the tag on another machine did not reproduce CI's bytes. The code was not the cause:

  • Go patch version: 1.27.0 locally, 1.27.1 in CI.
  • File owners and times recorded in the .tar.gz.
  • The build host's name recorded in the .rpm.

#113 fixed all three. The 1.2.1 rebuild then matched CI byte for byte on every binary, archive and package.

What it lacks: 1.2.1's verified database TLS default, the master key file and the key-rotation fix (#114).


Install

Packages (recommended for a single server) — Debian/Ubuntu and RHEL/Fedora/SUSE, amd64 and arm64:

sudo apt install ./zanskar_1.2.1-rc.1_linux_amd64.deb     # or: sudo rpm -i zanskar_1.2.1-rc.1_linux_amd64.rpm
sudo zanskar init                                       # writes /etc/zanskar/env and prints the next steps

Container image — ghcr.io/albatroxxx/zanskar:1.2.1-rc.1 (linux/amd64, linux/arm64). Single-node stack with TLS: deploy/docker-compose.yml with ZANSKAR_VERSION=1.2.1-rc.1.

Verify — checksums and the image are signed with Sigstore (keyless, GitHub Actions identity).
cosign is not in the Ubuntu, Debian or RHEL repositories; install it from
its own release (cosign_<ver>_amd64.deb,
cosign-<ver>-1.x86_64.rpm) or see Sigstore's installation page.

sha256sum -c SHA256SUMS --ignore-missing
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.1-rc.1$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
cosign verify ghcr.io/albatroxxx/zanskar:1.2.1-rc.1 \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.1-rc.1$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Build provenance and SBOM — zanskar.intoto.jsonl is SLSA level 3 provenance for every file
in SHA256SUMS: which workflow, at which commit, built it. Check a download against it with
slsa-verifier:

slsa-verifier verify-artifact zanskar_1.2.1-rc.1_linux_amd64.deb \
  --provenance-path zanskar.intoto.jsonl \
  --source-uri github.com/albatroxxx/zanskar --source-tag v1.2.1-rc.1

Each .tar.gz has an SPDX software bill of materials beside it (….tar.gz.sbom.json) listing
the Go modules compiled into the binary, with versions. The .deb and .rpm for an
architecture carry the same binary, so its archive's SBOM describes them too.

Full guide: docs/deploy.md.

Changelog

  • ab42db1 docs(site): drop the go install section from the install page (#108)
  • fffad8e docs(site): one copy box per zanskar init variant (#107)
  • 1c1d932 docs(site): what the 1.2.0 test taught, written the way it now works (#106)
  • f8bbaa9 feat(release): SPDX SBOM per archive and SLSA level 3 provenance (#111)
  • cbe308e fix(deps): clear both Scorecard vulnerability findings (#110)

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 10:02
4255e6a

Zanskar 1.2.0

The second release of Zanskar, an agentless access gateway for the machines and managed
databases you already run: SSH, RDP, VNC and WinRM sessions in the browser, brokered
PostgreSQL, MySQL and MariaDB sessions, vaulted credentials, access policies, just-in-time
approvals, session recording and a tamper-evident audit log. One Go binary with the web UI
embedded, nothing installed on the targets.

1.2.0 is what the 1.0 manual QA round asked for: an install that serves HTTPS out of the box,
MySQL and MariaDB alongside PostgreSQL, keyless SSH through a certificate authority as the
recommended Linux path, guided AWS enrolment, a console that owns its runtime settings,
recording storage and logs, and onboarding that hands new users a one-time password. It
remains a single-instance release: one gateway, embedded SQLite or PostgreSQL, and a
guacd sidecar for RDP and VNC.

Highlights

Install and TLS

  • Name the address people will actually use. The certificate generated at first start covers
    what the machine can work out: localhost, its host name, its interface addresses. A cloud
    instance's public address is translated upstream and is on none of them, so it was missing, which
    meant a name-mismatch warning and, less obviously, a plain-HTTP listener that refused to redirect
    rather than send a browser to a name the certificate cannot serve. zanskar init now asks for it,
    -tls-hosts and ZANSKAR_TLS_HOSTS set it, adding one and restarting regenerates the self-signed
    certificate, and the refusal page says which address it was asked for and how to fix it.
  • The installation instructions point at the newest release rather than a pinned version:
    the package commands resolve it from the releases redirect, the container image tag defaults
    to latest, and go install github.com/albatroxxx/zanskar/cmd/zanskar@latest is documented
    as the one-line path, with its caveats (no web console, no version string).
  • The install guide installs cosign before the verification step: it is in none of the
    Ubuntu, Debian or RHEL repositories, so a fresh host answered cosign: command not found.
    Downloads go to /tmp/zanskar, because apt cannot read a package from a 0750 home directory
    and says so with a notice that reads like a fault.
  • HTTPS out of the box. zanskar init now defaults to a gateway-managed certificate on
    0.0.0.0:443 with plain HTTP on port 80 redirected (ZANSKAR_TLS_MODE=managed,
    ZANSKAR_HTTP_REDIRECT_ADDR=:80). A self-signed certificate is generated at first start;
    upload the real one on the Settings page and it serves on the next connection, no restart.
    zanskar init -behind-proxy keeps the loopback-behind-a-proxy shape. Existing installs
    keep their behaviour: the mode is derived from the variables they already set.
  • The packaged unit grants CAP_NET_BIND_SERVICE so the unprivileged service can bind 443
    and 80.

Databases

  • MySQL and MariaDB sessions, including Amazon RDS, through the gateway's own relay
    sidecar (zanskar dbproxy, the gateway image run beside each session). As with
    PostgreSQL, the user's client container never holds the database password.
  • A Databases page separate from Hosts, kind-first forms, a database name and a TLS mode
    per target (disable, prefer, require, verify-full with an optional CA bundle),
    honoured by both sidecars.
  • A tri-state credential slot: vaulted, user-supplied at connect time, or none.

SSH without stored keys

  • The certificate authority is the recommended way to reach Linux targets. Creating an
    ssh_ca credential generates the authority; the console prints the exact
    TrustedUserCAKeys commands for each target. Every session gets a certificate for one
    login user, valid for minutes, with a key id that names the Zanskar user in the target's
    auth log. A per-authority allowlist limits the login users it will issue for.
  • Two-phase rotation of the authority key: prepare the next key, install both public
    keys, test each bound target with a real certificate login, cut over, retire. No session
    breaks on a host that has not learned the new key.

Autoscaling groups on AWS

  • The gateway knows its own principal: detected from its instance profile or IRSA and
    shown on the Autoscaling page. Enrolment previews the IAM documents before the group is
    saved: a trust policy with the real principal (never a placeholder), the permissions
    policy, an AWS CLI script and a CloudFormation template that create the role.
  • Test access assumes the role with the ExternalId and describes the group before
    saving, and names the failing stage: trust policy, permissions, or no such group.
  • Windows autoscaling groups now serve sessions (ADR 0024). A group could already be
    enrolled with RDP and WinRM capabilities and would sync and report healthy instances, but
    every connection was refused. Each instance is now pinned to the RDP and WinRM certificate
    it first presented; a certificate that changes under us marks that instance unhealthy and is
    audited, rather than being trusted quietly. Windows instances are expected to come from an
    image that already carries the HTTPS WinRM listener, since nothing configures a machine that
    may be gone within the hour.
  • A WinRM credential that authenticates and is then refused a shell now gets a plain answer:
    Zanskar opens a WinRS shell, which WinRM grants to local administrators only, so membership
    of Remote Management Users is not enough. It used to be an opaque 409.

Access control

  • Per-target protocol rules inside one policy.
  • Just-in-time access: one open request per target, editable grant duration, extensions of
    an active grant, a nav badge and expiry notices.
  • Recording retention per policy and per target, fixed on each recording when its session
    starts; the sweeper honours it.

Operations from the console

  • Settings: typed runtime settings applied live (login banner, MFA requirement, guacd
    address, log level, relay image) with their source shown; install-time settings listed
    read-only.
  • Restart required banner when the environment file changed since start (variable names
    only), and a restart with a drain of live sessions.
  • Recording storage: an S3-compatible bucket configured, probed and switched from the
    console, with an audited move of existing local recordings.
  • Logs: the gateway's recent log lines from an in-memory ring, filtered and downloadable,
    without shell access.
  • zanskar key status, zanskar key rotate and zanskar key rotate-master for the key
    ring and the master key.

Onboarding and identity

  • Creating a local user without a password generates a one-time password shown once; the
    user replaces it at first sign-in, before the second factor.
  • Auditor-only accounts are review-only and cannot reach targets.

Audit and recordings

  • Download recordings and command transcripts, each download audited.
  • Targets and autoscaling groups are retired, not erased, so session history keeps its names;
    deletion is refused while a policy or a live session still references them.
  • Names instead of ids throughout the audit log and session lists.

Console

  • Events and Recordings live inside the admin console, below Logs. An admin no longer
    leaves for the audit portal to read the log or play a recording, and the portal switch no
    longer offers it; the audit portal is for auditor-only accounts. Old /audit links take an
    admin to the same page in the console.
  • Audit event details read as names: Authenticator enrolled, not user.mfa.confirm, and
    User admin, not user admin 9ddd1ecc. The action code and the full id stay on hover, and the
    Action filter shows the same labels while still matching the stored code.
  • The logo goes home: the logo and name at the top of the sidebar return to the first page
    of the portal you are in, or to sign-in when the session has expired.
  • Modal action rows stay put, credential bindings apply on Save, a key-file picker for SSH
    keys, recording player transport controls, clipboard sync with RDP and VNC, a pulsing
    recording indicator.
  • Six things the manual QA pass found: rows inside a dialog no longer inherit the closing row's
    right alignment (and a textarea outside a field wrapper no longer renders 170px wide); the
    user portal opens on a tab that has something in it; the database chip is no longer the same
    colour as a warning; a terminal whose session has ended stops its counters, drops the
    Recording dot and says why it ended; log and audit times show seconds; and the database
    drawer writes engine names the way the table beside it does.

Security posture

OpenSSF Scorecard and Best Practices (passing) badges, CodeQL, gosec, govulncheck, gitleaks,
Trivy and Go native fuzz targets on every change; local git hooks for secrets and
vulnerabilities; all GitHub Actions and base images pinned by digest; branch protection on
main with eight required checks; private vulnerability reporting enabled. Threat model:
docs/threat-model.md. Report vulnerabilities privately:
https://github.com/albatroxxx/zanskar/security/advisories/new.

Known limitations

  • Single instance only; running more than one replica breaks cross-pod admin terminate and
    auditor shadowing (Phase 4).
  • The MySQL and MariaDB relay was verified with unit tests, the Go MySQL client, and
    sessions through the actual mysql:8, mariadb:11 and postgres:16 client containers on
    the 1.2.0 staging gateway. Report anything a particular client version does differently.
  • Autoscaling instances are pinned on first sight, without an administrator approving a
    fingerprint, because the instance did not exist a minute ago (ADR 0024). For SSH the host key
    is cross-checked against the serial console where AWS publishes it; for the Windows
    certificates there is no such cross-check.
  • No SAM...
Read more

v1.2.0-rc.2

v1.2.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 30 Sep 07:39
bd96e98

Zanskar 1.2.0-rc.2

Release candidate for the 1.2.0 staging test. Not for production: latest does not move, and the container image is tagged 1.2.0-rc.2 only. The notes below are the 1.2.0 notes.

What changed since rc.1, all of it found by testing rc.1 on a staging gateway with a real Active Directory domain, a domain-joined Windows host, an RDS database and two autoscaling groups:

  • Saving an access policy from the console failed outright, on every policy. The API rejected the two fields the form has been sending since 1.2.0 gained per-target rules and retention. This was the release blocker and is why rc.1 must not become 1.2.0.
  • Windows autoscaling groups could be enrolled and would report healthy instances, and then refuse every session. They work now, with each instance pinned to the certificates it first presented (ADR 0024).
  • A WinRM credential that authenticates and is then refused a shell said nothing useful. It now names the cause: Zanskar opens a WinRS shell, which Windows grants to local administrators only.
  • Six console fixes from the same pass: alignment inside dialogs, the portal's opening tab, the database button's colour, a terminal that kept counting after its session ended, seconds on log and audit times, and engine names that disagreed between a table and its drawer.
  • Installation points at the newest release rather than a pinned version, and documents go install …/cmd/zanskar@latest.
  • The guide gained the Active Directory walkthrough it never had, and the autoscaling section is rewritten for Windows.
  • Reaching a fresh install by its public address: the certificate now covers the address you name at install time, and plain HTTP redirects to it instead of refusing. Previously only the private address worked.

The second release of Zanskar, an agentless access gateway for the machines and managed
databases you already run: SSH, RDP, VNC and WinRM sessions in the browser, brokered
PostgreSQL, MySQL and MariaDB sessions, vaulted credentials, access policies, just-in-time
approvals, session recording and a tamper-evident audit log. One Go binary with the web UI
embedded, nothing installed on the targets.

1.2.0 is what the 1.0 manual QA round asked for: an install that serves HTTPS out of the box,
MySQL and MariaDB alongside PostgreSQL, keyless SSH through a certificate authority as the
recommended Linux path, guided AWS enrolment, a console that owns its runtime settings,
recording storage and logs, and onboarding that hands new users a one-time password. It
remains a single-instance release: one gateway, embedded SQLite or PostgreSQL, and a
guacd sidecar for RDP and VNC.

Highlights

Install and TLS

  • Name the address people will actually use. The certificate generated at first start covers
    what the machine can work out: localhost, its host name, its interface addresses. A cloud
    instance's public address is translated upstream and is on none of them, so it was missing, which
    meant a name-mismatch warning and, less obviously, a plain-HTTP listener that refused to redirect
    rather than send a browser to a name the certificate cannot serve. zanskar init now asks for it,
    -tls-hosts and ZANSKAR_TLS_HOSTS set it, adding one and restarting regenerates the self-signed
    certificate, and the refusal page says which address it was asked for and how to fix it.
  • The installation instructions point at the newest release rather than a pinned version:
    the package commands resolve it from the releases redirect, the container image tag defaults
    to latest, and go install github.com/albatroxxx/zanskar/cmd/zanskar@latest is documented
    as the one-line path, with its caveats (no web console, no version string).
  • HTTPS out of the box. zanskar init now defaults to a gateway-managed certificate on
    0.0.0.0:443 with plain HTTP on port 80 redirected (ZANSKAR_TLS_MODE=managed,
    ZANSKAR_HTTP_REDIRECT_ADDR=:80). A self-signed certificate is generated at first start;
    upload the real one on the Settings page and it serves on the next connection, no restart.
    zanskar init -behind-proxy keeps the loopback-behind-a-proxy shape. Existing installs
    keep their behaviour: the mode is derived from the variables they already set.
  • The packaged unit grants CAP_NET_BIND_SERVICE so the unprivileged service can bind 443
    and 80.

Databases

  • MySQL and MariaDB sessions, including Amazon RDS, through the gateway's own relay
    sidecar (zanskar dbproxy, the gateway image run beside each session). As with
    PostgreSQL, the user's client container never holds the database password.
  • A Databases page separate from Hosts, kind-first forms, a database name and a TLS mode
    per target (disable, prefer, require, verify-full with an optional CA bundle),
    honoured by both sidecars.
  • A tri-state credential slot: vaulted, user-supplied at connect time, or none.

SSH without stored keys

  • The certificate authority is the recommended way to reach Linux targets. Creating an
    ssh_ca credential generates the authority; the console prints the exact
    TrustedUserCAKeys commands for each target. Every session gets a certificate for one
    login user, valid for minutes, with a key id that names the Zanskar user in the target's
    auth log. A per-authority allowlist limits the login users it will issue for.
  • Two-phase rotation of the authority key: prepare the next key, install both public
    keys, test each bound target with a real certificate login, cut over, retire. No session
    breaks on a host that has not learned the new key.

Autoscaling groups on AWS

  • The gateway knows its own principal: detected from its instance profile or IRSA and
    shown on the Autoscaling page. Enrolment previews the IAM documents before the group is
    saved: a trust policy with the real principal (never a placeholder), the permissions
    policy, an AWS CLI script and a CloudFormation template that create the role.
  • Test access assumes the role with the ExternalId and describes the group before
    saving, and names the failing stage: trust policy, permissions, or no such group.
  • Windows autoscaling groups now serve sessions (ADR 0024). A group could already be
    enrolled with RDP and WinRM capabilities and would sync and report healthy instances, but
    every connection was refused. Each instance is now pinned to the RDP and WinRM certificate
    it first presented; a certificate that changes under us marks that instance unhealthy and is
    audited, rather than being trusted quietly. Windows instances are expected to come from an
    image that already carries the HTTPS WinRM listener, since nothing configures a machine that
    may be gone within the hour.
  • A WinRM credential that authenticates and is then refused a shell now gets a plain answer:
    Zanskar opens a WinRS shell, which WinRM grants to local administrators only, so membership
    of Remote Management Users is not enough. It used to be an opaque 409.

Access control

  • Per-target protocol rules inside one policy.
  • Just-in-time access: one open request per target, editable grant duration, extensions of
    an active grant, a nav badge and expiry notices.
  • Recording retention per policy and per target, fixed on each recording when its session
    starts; the sweeper honours it.

Operations from the console

  • Settings: typed runtime settings applied live (login banner, MFA requirement, guacd
    address, log level, relay image) with their source shown; install-time settings listed
    read-only.
  • Restart required banner when the environment file changed since start (variable names
    only), and a restart with a drain of live sessions.
  • Recording storage: an S3-compatible bucket configured, probed and switched from the
    console, with an audited move of existing local recordings.
  • Logs: the gateway's recent log lines from an in-memory ring, filtered and downloadable,
    without shell access.
  • zanskar key status, zanskar key rotate and zanskar key rotate-master for the key
    ring and the master key.

Onboarding and identity

  • Creating a local user without a password generates a one-time password shown once; the
    user replaces it at first sign-in, before the second factor.
  • Auditor-only accounts are review-only and cannot reach targets.

Audit and recordings

  • Download recordings and command transcripts, each download audited.
  • Targets and autoscaling groups are retired, not erased, so session history keeps its names;
    deletion is refused while a policy or a live session still references them.
  • Names instead of ids throughout the audit log and session lists.

Console

  • Modal action rows stay put, credential bindings apply on Save, a key-file picker for SSH
    keys, recording player transport controls, clipboard sync with RDP and VNC, a pulsing
    recording indicator.

Console

  • Six things the manual QA pass found: rows inside a dialog no longer inherit the closing row's
    right alignment (and a textarea outside a field wrapper no longer renders 170px wide); the
    user portal opens on a tab that has something in it; the database chip is no longer the same
    colour as a warning; a terminal whose session has ended stops its counters, drops the
    Recording dot and says why it ended; log and audit times show seconds; and the database
    drawer writes engine names the way the table beside it does.

Security posture

OpenSSF Scorecard and Best Practices (passing) badges, CodeQL, gosec, govulncheck, gitleaks,
Trivy and Go native fuzz targets on every change; local git hooks for secrets and
vulnerabilities; all GitHub Actions and base images pinned by digest; branch protection on
main with eight required checks; private vulnerability reporting enabled. Threat model:
docs/threat-model.md. Report vulnerabilities privately:
https://github.com/albatroxxx/zanskar/security/advisories/new.

Known limitations

  • Single instance only; run...
Read more

v1.2.0-rc.1

v1.2.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 30 Sep 10:05

Zanskar 1.2.0-rc.1

Release candidate for the 1.2.0 staging test. Same code as the 1.2.0 release will carry unless the test finds something; the notes below are the 1.2.0 notes. Not for production: latest does not move, and the container image is tagged 1.2.0-rc.1 only.

The second release of Zanskar, an agentless access gateway for the machines and managed
databases you already run: SSH, RDP, VNC and WinRM sessions in the browser, brokered
PostgreSQL, MySQL and MariaDB sessions, vaulted credentials, access policies, just-in-time
approvals, session recording and a tamper-evident audit log. One Go binary with the web UI
embedded, nothing installed on the targets.

1.2.0 is what the 1.0 manual QA round asked for: an install that serves HTTPS out of the box,
MySQL and MariaDB alongside PostgreSQL, keyless SSH through a certificate authority as the
recommended Linux path, guided AWS enrolment, a console that owns its runtime settings,
recording storage and logs, and onboarding that hands new users a one-time password. It
remains a single-instance release: one gateway, embedded SQLite or PostgreSQL, and a
guacd sidecar for RDP and VNC.

Highlights

Install and TLS

  • HTTPS out of the box. zanskar init now defaults to a gateway-managed certificate on
    0.0.0.0:443 with plain HTTP on port 80 redirected (ZANSKAR_TLS_MODE=managed,
    ZANSKAR_HTTP_REDIRECT_ADDR=:80). A self-signed certificate is generated at first start;
    upload the real one on the Settings page and it serves on the next connection, no restart.
    zanskar init -behind-proxy keeps the loopback-behind-a-proxy shape. Existing installs
    keep their behaviour: the mode is derived from the variables they already set.
  • The packaged unit grants CAP_NET_BIND_SERVICE so the unprivileged service can bind 443
    and 80.

Databases

  • MySQL and MariaDB sessions, including Amazon RDS, through the gateway's own relay
    sidecar (zanskar dbproxy, the gateway image run beside each session). As with
    PostgreSQL, the user's client container never holds the database password.
  • A Databases page separate from Hosts, kind-first forms, a database name and a TLS mode
    per target (disable, prefer, require, verify-full with an optional CA bundle),
    honoured by both sidecars.
  • A tri-state credential slot: vaulted, user-supplied at connect time, or none.

SSH without stored keys

  • The certificate authority is the recommended way to reach Linux targets. Creating an
    ssh_ca credential generates the authority; the console prints the exact
    TrustedUserCAKeys commands for each target. Every session gets a certificate for one
    login user, valid for minutes, with a key id that names the Zanskar user in the target's
    auth log. A per-authority allowlist limits the login users it will issue for.
  • Two-phase rotation of the authority key: prepare the next key, install both public
    keys, test each bound target with a real certificate login, cut over, retire. No session
    breaks on a host that has not learned the new key.

Autoscaling groups on AWS

  • The gateway knows its own principal: detected from its instance profile or IRSA and
    shown on the Autoscaling page. Enrolment previews the IAM documents before the group is
    saved: a trust policy with the real principal (never a placeholder), the permissions
    policy, an AWS CLI script and a CloudFormation template that create the role.
  • Test access assumes the role with the ExternalId and describes the group before
    saving, and names the failing stage: trust policy, permissions, or no such group.

Access control

  • Per-target protocol rules inside one policy.
  • Just-in-time access: one open request per target, editable grant duration, extensions of
    an active grant, a nav badge and expiry notices.
  • Recording retention per policy and per target, fixed on each recording when its session
    starts; the sweeper honours it.

Operations from the console

  • Settings: typed runtime settings applied live (login banner, MFA requirement, guacd
    address, log level, relay image) with their source shown; install-time settings listed
    read-only.
  • Restart required banner when the environment file changed since start (variable names
    only), and a restart with a drain of live sessions.
  • Recording storage: an S3-compatible bucket configured, probed and switched from the
    console, with an audited move of existing local recordings.
  • Logs: the gateway's recent log lines from an in-memory ring, filtered and downloadable,
    without shell access.
  • zanskar key status, zanskar key rotate and zanskar key rotate-master for the key
    ring and the master key.

Onboarding and identity

  • Creating a local user without a password generates a one-time password shown once; the
    user replaces it at first sign-in, before the second factor.
  • Auditor-only accounts are review-only and cannot reach targets.

Audit and recordings

  • Download recordings and command transcripts, each download audited.
  • Targets and autoscaling groups are retired, not erased, so session history keeps its names;
    deletion is refused while a policy or a live session still references them.
  • Names instead of ids throughout the audit log and session lists.

Console

  • Modal action rows stay put, credential bindings apply on Save, a key-file picker for SSH
    keys, recording player transport controls, clipboard sync with RDP and VNC, a pulsing
    recording indicator.

Security posture

OpenSSF Scorecard and Best Practices (passing) badges, CodeQL, gosec, govulncheck, gitleaks,
Trivy and Go native fuzz targets on every change; local git hooks for secrets and
vulnerabilities; all GitHub Actions and base images pinned by digest; branch protection on
main with eight required checks; private vulnerability reporting enabled. Threat model:
docs/threat-model.md. Report vulnerabilities privately:
https://github.com/albatroxxx/zanskar/security/advisories/new.

Known limitations

  • Single instance only; running more than one replica breaks cross-pod admin terminate and
    auditor shadowing (Phase 4).
  • The MySQL and MariaDB relay was verified with unit tests and the Go MySQL client; a run
    with the sessions' own mysql and mariadb client containers is part of the 1.2.0
    staging test. Report anything a particular client version does differently.
  • No SAML, SCIM or WebAuthn yet (Phase 4). Passwords for Windows and database targets are not
    rotated by Zanskar; the systems that own those accounts do that better.

Upgrading from 1.0.0

Install the new package or set ZANSKAR_VERSION=1.2.0 and restart. Migrations 0017 to 0023
run at start (new columns and tables; no data is rewritten). An existing install keeps its TLS
behaviour: with ZANSKAR_TLS_CERT set the mode is file, otherwise proxy, exactly as
before; only a fresh zanskar init defaults to managed TLS on 443. The MySQL and MariaDB
relay image defaults to ghcr.io/albatroxxx/zanskar:1.2.0 and can be pointed at a mirror on
the Settings page. ZANSKAR_AWS_GATEWAY_PRINCIPAL can be removed on EC2 and EKS gateways,
which now detect it.


Install

Packages (recommended for a single server) — Debian/Ubuntu and RHEL/Fedora/SUSE, amd64 and arm64:

sudo apt install ./zanskar_1.2.0-rc.1_linux_amd64.deb     # or: sudo rpm -i zanskar_1.2.0-rc.1_linux_amd64.rpm
sudo zanskar init                                       # writes /etc/zanskar/env and prints the next steps

Container image — ghcr.io/albatroxxx/zanskar:1.2.0-rc.1 (linux/amd64, linux/arm64). Single-node stack with TLS: deploy/docker-compose.yml with ZANSKAR_VERSION=1.2.0-rc.1.

Verify — checksums and the image are signed with Sigstore (keyless, GitHub Actions identity):

sha256sum -c SHA256SUMS --ignore-missing
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.0-rc.1$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
cosign verify ghcr.io/albatroxxx/zanskar:1.2.0-rc.1 \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.2.0-rc.1$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Full guide: docs/deploy.md.

Changelog

  • 643ae2d Add Search Console verification file (#51)
  • 74d99a9 build(ci): bump github/codeql-action to v4.38.2 in every workflow at once (#79)
  • 39dcc8f build(deps): Bump goreleaser/goreleaser-action from 6.4.0 to 7.2.3 (#55)
  • 7cc26ed build(deps): Bump node from 25-alpine to 26-alpine in /deploy (#56)
  • 387fb52 build(deps): Bump the go-deps group with 10 updates (#61)
  • 666cd4f build(deps-dev): Bump the web-deps group in /web with 3 updates (#57)
  • c7b636e docs(release): 1.2.0 notes, READMEs and site covering everything since 1.0.0
  • 77c71db docs(roadmap): tick the frontend scaffold box (#92)
  • 42c79b3 docs(site): readiness check per init mode; explain the TLS wrong-version error (#52)
  • 8f9e549 feat(access): one open request per target, editable grant, extensions, badge and notices (#71)
  • 7f10701 feat(api,web): names instead of ids in every session, request and audit view (#68)
  • da2a872 feat(audit): download recordings and command transcripts, audited (#76)
  • 04ccab2 feat(auth): local-user onboarding with a one-time password replaced at first sign-i...
Read more

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 08:48
90dc908

Zanskar 1.0.0

The first stable release of Zanskar, an agentless access gateway for the machines you
already run: SSH, RDP, VNC and WinRM sessions in the browser, plus brokered access to managed
databases, with vaulted credentials, access policies, just-in-time approvals, session
recording and a tamper-evident audit log. One Go binary with the web UI embedded, nothing
installed on the targets.

1.0 is a single-instance release: one gateway, embedded SQLite, and a guacd sidecar
for RDP and VNC. Multi-replica HA and the Helm chart are preview-only until Phase 4 lands.

Highlights

Sessions

  • SSH terminal in the browser over WebSocket, with SFTP upload/download in a side panel
    when the policy allows file transfer.
  • RDP and VNC desktops through guacd, with clipboard policy and a files panel.
  • Windows command line over WinRM, TLS-pinned to the host's listener certificate.
  • Managed/PaaS database access (PostgreSQL first) through a per-session client container
    that talks to a credential-holding proxy sidecar: the user never sees the database
    credential, not even from a shell escape.
  • Every session is recorded (asciicast for terminals, Guacamole-format for desktops), admins can
    watch live, terminate, or replay with command markers; auditors can shadow.

Access control

  • Users, groups and roles (admin, auditor, user); local accounts with Argon2id passwords and
    mandatory TOTP, or OIDC and LDAP/Active Directory sign-in with audited group-to-role
    mapping.
  • Access policies bind subjects to targets with protocol, time window, idle and maximum
    session limits, clipboard and file-transfer switches.
  • Just-in-time access (PIM): policies can require approval; users request time-bound
    access with a reason, admins approve, deny or revoke, grants expire automatically and every
    step is audited.
  • Host-key and certificate pinning: trust on first use at enrollment, admin approval, any
    later change blocks connections until re-approved.

Credentials and secrets

  • Vaulted passwords, SSH keys and an SSH certificate-authority mode; envelope encryption
    (AES-256-GCM data keys wrapped by a master key) with key versioning and rotation.
  • The master key is never written to the database or included in backups.

Autoscaling groups

  • AWS Auto Scaling groups enrolled through a cross-account IAM role: healthy-pool tracking,
    EC2 Instance Connect credentials, and a failover prompt when the active instance goes away.

Audit and compliance

  • Append-only, hash-chained audit log with zanskar audit verify; SIEM export.
  • Recording retention by age and size with an audited hourly sweep.
  • zanskar backup / zanskar restore for the SQLite database and recordings.

Operations

  • zanskar init writes the configuration interactively or from flags; a hardened systemd
    unit ships in the deb/rpm packages.
  • Docker Compose single-node stack with Caddy TLS pulls the signed multi-arch image from
    ghcr.io/albatroxxx/zanskar.
  • Every release artifact is checksummed and signed with Sigstore (keyless, GitHub Actions
    identity); the verify commands are below.

Security posture

OpenSSF Scorecard and Best Practices (passing) badges, CodeQL, gosec, govulncheck, gitleaks and
Trivy on every change, all GitHub Actions and base images pinned by digest, branch protection
on main, private vulnerability reporting enabled. Threat model: docs/threat-model.md.
Report vulnerabilities privately: https://github.com/albatroxxx/zanskar/security/advisories/new.

Known limitations

  • Single instance only; running more than one replica breaks cross-pod admin terminate and
    auditor shadowing (Phase 4).
  • Database access supports PostgreSQL; MySQL via ProxySQL is next.
  • No SAML, SCIM, WebAuthn or credential auto-rotation yet (Phase 4).

Upgrading from a release candidate

1.0.0-rc.1 and 1.0.0-rc.2 share the schema with 1.0.0. Install the new package or set
ZANSKAR_VERSION=1.0.0 and restart; migrations are a no-op.


Install

Packages (recommended for a single server) — Debian/Ubuntu and RHEL/Fedora/SUSE, amd64 and arm64:

sudo apt install ./zanskar_1.0.0_linux_amd64.deb     # or: sudo rpm -i zanskar_1.0.0_linux_amd64.rpm
sudo zanskar init                                       # writes /etc/zanskar/env and prints the next steps

Container image — ghcr.io/albatroxxx/zanskar:1.0.0 (linux/amd64, linux/arm64). Single-node stack with TLS: deploy/docker-compose.yml with ZANSKAR_VERSION=1.0.0.

Verify — checksums and the image are signed with Sigstore (keyless, GitHub Actions identity):

sha256sum -c SHA256SUMS --ignore-missing
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.0.0$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS
cosign verify ghcr.io/albatroxxx/zanskar:1.0.0 \
  --certificate-identity-regexp '^https://github.com/albatroxxx/zanskar/.github/workflows/release.yml@refs/tags/v1.0.0$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Full guide: docs/deploy.md.

Changelog

  • 90dc908 release: 1.0.0 highlights, private file modes, TLS warning wording, chart 1.0.0 (#47)