Repository navigation
Releases: albatroxxx/zanskar
Release list
v1.4.0
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 as00:00to00:00means 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: falsecould never admit anyone. - An account provisioned at first sign-in is recorded as
user.createwith 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_URLto 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 officialpsqlandmysqlimages 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
dockercommands 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 inZANSKAR_MASTER_KEY_FILEand it never appears in/proc/<pid>/environeither.
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 onzanskar audit anchor, under a key derived from the master key, and logs each
anchor as a line to ship off the host.zanskar audit verifyand 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 asrecording.lostand 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 --forceon an install that keeps its key inZANSKAR_MASTER_KEY_FILEkeeps it
there. Before, it minted a new inline key and the service could not open anything.zanskar restorerun 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-masterwrites 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...
v1.3.0
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.helplists 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=offin 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_cursoris 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 stepsContainer 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.comBuild 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.0Each .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
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-spoolunless
ZANSKAR_RECORDINGS_SPOOL_DIRsays 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 /connectis documented as what it is:ticket,expires_atand
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 stepsContainer 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.comBuild 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.2Each .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
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_FILEnames a file holding the key, so it is not in
/etc/zanskar/envbeside every other setting. The file must be readable by its owner only,
or the gateway refuses to start.docs/deploy.mdhas the four commands to move an
existing key into one.zanskar key rotate-masterrewrites the key file in place, keeping its owner and mode.
Master key rotation cannot strand the key any more
rotate-master -env-fileused 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
-generatethe 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.deband.rpmcarry the same binary. - SLSA level 3 build provenance for every file (
zanskar.intoto.jsonl): which workflow, at
which commit, built it. Check it withslsa-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/compress1.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 stepsContainer 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.comBuild 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.1Each .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
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-verifierall pass. slsa-verifierrejects 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 stepsContainer 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.comBuild 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.1Each .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
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 initnow asks for it,
-tls-hostsandZANSKAR_TLS_HOSTSset 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
tolatest, andgo install github.com/albatroxxx/zanskar/cmd/zanskar@latestis documented
as the one-line path, with its caveats (no web console, no version string). - The install guide installs
cosignbefore the verification step: it is in none of the
Ubuntu, Debian or RHEL repositories, so a fresh host answeredcosign: 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 initnow defaults to a gateway-managed certificate on
0.0.0.0:443with 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-proxykeeps 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_SERVICEso 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-fullwith 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_cacredential generates the authority; the console prints the exact
TrustedUserCAKeyscommands 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 rotateandzanskar key rotate-masterfor 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/auditlinks 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 actualmysql:8,mariadb:11andpostgres:16client 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...
v1.2.0-rc.2
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 initnow asks for it,
-tls-hostsandZANSKAR_TLS_HOSTSset 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
tolatest, andgo install github.com/albatroxxx/zanskar/cmd/zanskar@latestis documented
as the one-line path, with its caveats (no web console, no version string). - HTTPS out of the box.
zanskar initnow defaults to a gateway-managed certificate on
0.0.0.0:443with 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-proxykeeps 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_SERVICEso 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-fullwith 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_cacredential generates the authority; the console prints the exact
TrustedUserCAKeyscommands 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 rotateandzanskar key rotate-masterfor 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...
v1.2.0-rc.1
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 initnow defaults to a gateway-managed certificate on
0.0.0.0:443with 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-proxykeeps 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_SERVICEso 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-fullwith 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_cacredential generates the authority; the console prints the exact
TrustedUserCAKeyscommands 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 rotateandzanskar key rotate-masterfor 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' ownmysqlandmariadbclient 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 stepsContainer 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.comFull 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...
v1.0.0
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 restorefor the SQLite database and recordings.
Operations
zanskar initwrites 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 stepsContainer 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.comFull guide: docs/deploy.md.