Resident client¶
The Resident client is an optional local daemon. It keeps the identity and catalog current, assigns a loopback address to each visible resource, and regenerates native-client files when the catalog changes. Authorization is the same as for the explicit commands in the User guide.
Platforms: Linux x86-64 (the static musl binary from the release) and Windows
x86-64 (the .msi installer, supplied on request). Windows differences are in
Windows.
Access levels¶
access controls how an application reaches a local relay. resolver controls
how names are published. The two are set independently.
| Machine | Starting level |
|---|---|
| Personal workstation | basic; direct when a native client needs it |
| Managed desktop | basic under the managed user |
| Shared host | Linux: protected per-user basic/direct; other platforms: off |
| Server or non-interactive service | explicit commands or workload identity |
| Container | explicit commands |
| Level | What it publishes | Addressing requirement |
|---|---|---|
off |
no local listeners; use explicit commands | none |
basic |
one listener per resource on 127.0.0.1, each on its own port |
none |
direct |
one loopback address per resource; ports below 1024 are shifted by default | no low-port bind privilege with the default offset |
transparent |
a virtual interface and per-resource addresses | Linux: CAP_NET_ADMIN. Windows: the gdsgate-client-net service and Wintun |
auto |
the strongest level available; the default | none; status --explain shows the chosen level and why |
On Linux, local owner protection is enabled by default for all active levels.
The kernel admits resource connections only from sockets created by the
resident owner's OS UID. An ordinary up requests elevation once to install
the system service; sudo gdsgate up needs no second elevation. The default
low-port offset removes the bind restriction, while owner protection still
needs CAP_NET_ADMIN. See Local owner protection.
On the client host:
Expected result: up returns when the level is settled; status reports the
level in use. A catalogued but denied resource keeps its address, name and
status row; the gateway refuses the connection and reports the reason.
gdsgate down removes the listeners and names.
Privileged ports at direct¶
The default [client.direct].privileged_port_offset = 10000 moves only ports
1–1023: SSH 22 becomes 10022, HTTP 80 becomes 10080, and 443 becomes
10443. A database on 5432 stays on 5432. This changes the local listener;
the Connector still connects to the original resource port.
Set an explicit offset from 1023 to 64512, or 0 to preserve every protocol
port. With 0, low ports bind only when the host permits it. Windows has no
Unix low-port restriction; reservations and existing listeners can still refuse
a bind. status, connection commands and generated native-client files show
the actual local port. The offset does not apply to basic or transparent.
The transparent level¶
transparent routes traffic for the client's address pool through the daemon.
Use it for applications that cannot be pointed at a local proxy. It changes host
routing while the daemon runs, for every process on the host.
Capabilities¶
The daemon needs a virtual interface, an address pool, and permission to install
routes. Linux: CAP_NET_ADMIN. Windows: the Wintun adapter held by the
gdsgate-client-net service (see Windows). status --explain
names what is missing.
Requirements¶
The address pool must not overlap the host LAN or another virtual network.
Check basic first, then request transparent:
Expected result: ACCESS and SERVING report transparent, and a request to a
visible resource succeeds. On a missing capability or a pool conflict the daemon
refuses or falls back to a lower level, and status shows the level in use.
After down, the interface and routes are removed.
Tradeoffs¶
transparent reaches applications that ignore proxy variables and changes
host routing. On Linux, local owner protection restricts resource traffic to
the owner's UID. With protection disabled, or on other platforms, another
local account that can reach an endpoint can use the owner's cluster identity.
Connection identity¶
The daemon holds the user's identity and obtains short-lived access certificates
per resource. Authority still decides connect; request-aware lanes decide each
operation. A stale or revoked certificate shows as an ACCESS refusal.
Shared machines¶
On Linux, keep local owner protection enabled and give each resident separate
address pools and basic port ranges. Transparent residents also need distinct
interface names. Name publication changes the host resolver; coordinate it
separately. On platforms without owner protection, use off and explicit
commands for a shared machine. When an account is retired, run down and
remove its service, generated files and identity cache.
Local owner protection¶
[client].local_user_only defaults to enabled on Linux and disabled elsewhere.
Setting it to true on an unsupported platform refuses startup. On Linux it
requires the trusted filter command /usr/sbin/nft (or /sbin/nft) and
CAP_NET_ADMIN; install the host's kernel filter package before running up.
A failure to establish protection prevents publication. An endpoint that
cannot be protected remains unbound. There is no automatic fallback to an
unprotected listener.
At basic and direct, the filter covers each exact IPv4 address and TCP
port before the listener starts accepting. At transparent, it covers the
resource pool before the interface opens, including application domains.
Traffic arriving from another interface or forwarded from another network
namespace is denied, even if that namespace uses the same numeric UID. The
pool's reserved DNS address is excluded; it serves names, not resource sessions.
The private control socket has its own peer-credential checks.
status reports local OS owner UID <UID> only; JSON includes local_owner_uid
when enforcement is active. A clean stop closes listeners and the interface
before removing their protection. After a forced termination, restrictive
rules remain until the next start safely replaces that stopped process's
policy, or sudo gdsgate service uninstall client removes it. A live resident's
policy is never replaced by another startup.
The boundary is the socket creator's UID. Group membership grants no access. Processes under the owner's UID can use the resources; an owner can also pass an already authorized socket to another process. Root and processes able to change the kernel filter are outside this boundary. An administrator must retain the resident's tables when updating host firewall policy; flushing them removes enforcement.
Set local_user_only = false only when exposing the owner's resource relays to
other local accounts is intended. Separate configurations and high ports alone
do not provide an OS-user boundary.
Names¶
resolver |
Names |
|---|---|
off |
none |
hosts |
a marked block in the hosts file |
system |
the daemon serves the zone; the system resolver is pointed at it |
auto |
the strongest mode the host allows |
A resolver fallback does not change the access level.
Name resolution per access level¶
| Access | resolver = off |
hosts |
system |
auto |
|---|---|---|---|---|
off |
no names | managed hosts entries if allowed | explicit system resolver if configured | hosts or off |
basic |
no names | managed hosts entries | possible when the host routes queries to the daemon | system, hosts, or off |
direct |
no names | managed hosts entries | possible without transparent access | system, hosts, or off |
transparent |
no names | managed hosts entries | normally required for whole-host names | system or hosts |
system works at any level; transparent does not select it.
hosts¶
Requires write access to the hosts file.
Expected result: the name maps to the assigned local address. With a missing or
read-only hosts file, status shows names - or names ?; use
resolver = system or the loopback addresses. down removes only the block
this configuration wrote.
system¶
Requires the daemon's name server, a zone, and a system resolver that sends the zone's queries to it. Check both:
If the daemon answers but lookups fail, the system resolver still sends the zone
elsewhere. Restore the previous resolver settings or switch to hosts.
Verifying name resolution¶
status --explain shows the resolver mode, its state, and any fallback reason.
Look up one catalog name and one name outside the catalog; only the first
resolves to a managed address. After down, neither does.
An application's own name¶
A web resource may declare a native_name, the upstream application's own
host name. The Resident client intercepts it only when the user also lists the
whole name in [client].intercept_names.
Start the Resident client and open the name in a browser or HTTP client.
Expected result: the application receives its original host name, and audit
records an httpRequest. A name in only one of the two lists is not
intercepted. To stop interception, remove the name from intercept_names and
run down.
Certificate root for intercepted names¶
Install the deployment's certificate root in the browser or client trust store before using interception. A trust warning means the root is missing. Remove the root when the deployment is retired.
Limitations¶
Interception changes DNS and TLS for the listed names on the whole host. A name
absent from the catalog is not intercepted. Native names depend on the resolver
mode and are unavailable while it has fallen back to off.
Status output¶
status reports identity, catalog, names, listener, access and serving states:
ok passed, ? unknown (control plane unreachable), - not requested. Spacing
may differ between releases:
Start-up sequence¶
up validates settings, refuses to start over a running client, signs in when
needed, starts the daemon, settles the interface and names, fetches the catalog,
assigns addresses, generates native files, and reports the serving state. It
waits up to 10 seconds for the daemon's socket and 3 seconds for names and the
interface. In scripts, check status; do not rely on fixed sleeps.
Background execution¶
gdsgate --config "$CLIENT_CONFIG" up
gdsgate --config "$CLIENT_CONFIG" status
gdsgate --config "$CLIENT_CONFIG" down
up returns when the daemon reports its state. Expected result: SERVING ok.
If a client is already running or the identity is refused, up fails and the
running client is left unchanged; see status --explain.
The client journal¶
The journal records start-up decisions, fallbacks, listener changes and errors. It contains no private keys or provider tokens. With a malformed generated state file: stop the daemon, keep a copy of the file, check the configuration, start again.
--foreground¶
Use foreground mode to diagnose start-up or under a service manager:
The command stays attached while the daemon serves; start-up errors print to the terminal. Interrupting it shuts down normally and removes managed state.
On a terminal, output is also appended to ~/.gdsgate/gdsgated.log; its end
shows whether shutdown finished after the window was closed. Under a service
manager, output goes where the manager sends it.
Automatic authentication¶
With no usable identity, up runs the configured login. To use the browser:
A non-interactive service has no browser: use a workload identity. Do not put a user's token in a unit file.
Certificate prefetch¶
With warm_access on, the daemon requests access certificates during the
catalog pass, and status shows which resources are ready to connect. A refusal
is cached with its reason and not retried continuously. ACCESS ok means ready;
ACCESS step-up or ACCESS denied means refused. After a step-up or policy
change, the next catalog pass picks it up.
Address stability¶
Addresses stay the same while the client runs. An address freed by a removed resource is not reused immediately. On an address conflict, fix the pool and restart the client. Do not edit the state map by hand.
Resources removed from the catalog¶
When a resource leaves the catalog, the client immediately stops accepting new
connections to its address and removes its generated entries and DNS names.
The resource remains in status with listener state withdrawn during the
retention period. status --refresh reads the catalog again; it does not clear
records before their deadline. Use ls to see the current catalog and
status --explain to inspect recent withdrawals.
Open sessions end under their own revocation rules.
The age shown in status is time since withdrawal. Retention is
[client.transparent].reuse_grace_secs, capped at 86400 seconds (one day).
After that deadline, cleanup removes the record from endpoints.json, status
and diagnostics. Cleanup runs before catalog reads and at least once per minute
while waiting, including when identity or catalog access fails. A stopped client
cleans up when started again. Returning within retention restores the same address.
Address reuse¶
Addresses and basic ports published during a run remain reserved in memory until that run stops, even after the removed resource's record expires. These anonymous reservations contain no resource names and are not written to disk. After a restart, expired assignments are removed before new addresses are allocated; the freed addresses and ports can be used again. Active resources keep their addresses.
Generated files¶
Generated files are owner-only, written atomically, and marked; the daemon
replaces only its own content. They contain routing data and no credentials. SSH
host keys stay in the native client's known_hosts.
To inspect them:
Expected result: paths and resource states match the current catalog. After an interrupted update, stop the client and start it again.
SSH audit and recordings¶
SSH connections through a resource id, its published name or its Resident
address reach the same Connector and use the same audit and recording policy.
The # connected ... recording line is printed by the ProxyCommand for a
resource id. A connection directly to the Resident address has no terminal
message from that command.
Audit includes session.open when the session opens and session.close when
it ends. The recordings list shows closed sessions. After exiting SSH, check
both with the same cluster configuration:
gdsgate --config "$CLIENT_CONFIG" authority audit list --target demo-ssh --since 5m
gdsgate --config "$CLIENT_CONFIG" authority recordings list --target demo-ssh --since 5m
Expected result: the closed session has a matching session id in audit and, when recording is enabled, a recording entry. The absence of a terminal message does not indicate that recording is disabled.
Shutdown¶
Expected result: no Resident socket, managed listeners or managed names. down
is idempotent. After a crash, the next up removes stale state it owns and
reports what it could not remove. After a power loss, check the interface,
routes, hosts block and resolver before starting transparent again.
Safe uninstall¶
- End active sessions.
- If a service unit runs the Resident client, stop and disable it. Run
gdsgate downas the user and with the configuration that ranup. - Check that
status --explainshows no socket, listener, managed address or served name, and that the interface, routes, hosts block and resolver are back to their state before install. - Remove the generated fragments, endpoint map, socket directory and identity cache at the paths the configuration names.
- Check that the resource names no longer resolve and nothing listens on their addresses.
Access refusals¶
A refused resource keeps its local address, name and status row; the gateway
denies the connection. A refusal comes from identity, catalog visibility,
connect policy, a per-request action, an expired certificate, or a host
capability. The message names the operation, the cause and the next step.
Refusals in gdsgate status¶
| Value | Meaning |
|---|---|
ACCESS denied |
policy or identity refused the resource |
ACCESS step-up |
a one-time grant can allow it |
ACCESS ? |
the control plane is unreachable; the cached result is shown |
SERVING - |
no local listener was requested |
Step-up grants are single-use¶
A step-up grant belongs to one principal, resource and action. Prefetch does not consume it. The next matching operation consumes it; a second operation is denied.
Desktop notifications¶
If the notification program is unavailable, the refusal is still in status
and the journal.
Refusals inside an SSH connection¶
An SSH session can open while a later forward is denied. connect,
sshForwardLocal, sshForwardRemote and host-key failures are separate
decisions; the error names the channel that was refused.
Resolving access failures¶
Check in order: status --explain, identity freshness, catalog row, action
name, local listener, backend reachability. gdsgate --no-remediate disables
the interactive remediation prompt.
Reading gdsgate status¶
gdsgate --config "$CLIENT_CONFIG" status
gdsgate --config "$CLIENT_CONFIG" status --explain
gdsgate --config "$CLIENT_CONFIG" status --json
| Field | ok |
? |
- |
|---|---|---|---|
| identity | usable local identity | control-plane freshness unknown | not requested |
| catalog | current catalog | refresh uncertain | not requested |
| names | resolver serving | resolver state uncertain | names disabled |
| listener | local listener bound | bind status uncertain | no listener requested |
| access | policy/certificate usable | cached decision only | not warmed |
| serving | request can reach its relay | tunnel uncertain | no resource served |
Resources without an active connector¶
A catalog row can exist while no Connector serves it; status shows it as
declared but not serving. The administrator brings up the Connector that serves
the resource, or removes the row: see the
Admin guide.
Status details¶
--explain shows each problem with Fix first, followed by independent
Alternative actions, individual Cause fields and an optional Note.
It also lists observed local connections. --json carries the same problems
in diagnostics, with target, headline, cause, actions and note.
The first action is the recommended fix. Existing status fields remain available.
Resources are ordered by availability: ready first, then resources needing
attention, then unavailable resources. Unchecked access, failed checks, missing
local listeners and required user actions need attention. Policy denials,
missing Connectors and catalog withdrawals are unavailable. Within each group,
resource IDs are sorted without case sensitivity, with the exact ID breaking
ties. The table, --explain and JSON use the same order. Diagnostics for one
resource stay together; resolver problems appear first.
Terminal output colours ready resources green, problems yellow, policy denials
red and withdrawn resources grey. NO_COLOR=1 disables colour; JSON, pipes and
files contain no colour escapes. See the CLI reference
for an example and the JSON fields.
Windows¶
The client runs as the signed-in user. The gdsgate-client-net service, running
as SYSTEM, performs the two administrator operations: the NRPT rule that sends
the zone to the client's name server, and the Wintun adapter of transparent.
It serves one client at a time. It removes the rule and the adapter when the
client disconnects (including when it is killed) and when the service stops.
If the service stops, transparent addresses stop answering until the service
is started and the client restarted.
Installing¶
The installer gdsgate-<version>-x86_64-pc-windows-msvc.msi puts gdsgate.exe
in C:\Program Files\gdsgate, together with the Wintun library and the
pseudoconsole host of the agent sandbox. It adds the directory to the system
PATH and installs and starts gdsgate-client-net. From an elevated prompt:
Expected result: in a new prompt, gdsgate --version answers and
gdsgate doctor reports state.client_net and sandbox.console as OK. A
warning names the problem: the service is not running, or it cannot create an
adapter. A newer installer upgrades in place and keeps the service. Uninstalling
(Settings, or msiexec /x) removes the service and the files and keeps
C:\ProgramData\gdsgate.
What the service enables¶
| Level | Without the service | With the service | With the service and wintun.dll |
|---|---|---|---|
basic, direct |
works, no names | names through NRPT | names through NRPT |
transparent |
unavailable | unavailable | works, names through NRPT |
Under auto, the client picks the strongest level available.
Differences from Linux¶
resolver = systemgives names atdirectandbasictoo. Port 53 needs no privilege.resolver = hostswritesC:\Windows\System32\drivers\etc\hostsdirectly. It works only for a client started from an elevated prompt.- The environment file is
env.ps1. Load it with. ~\.gdsgate\env.ps1. - The control channel and the per-resource SSH agents are named pipes. The
generated
ssh_configpointsIdentityAgentat the pipe. WithSSH_AUTH_SOCKunset, the client forwards to the Windows OpenSSH agent (\\.\pipe\openssh-ssh-agent). - Notifications are Windows notifications, shown on behalf of Windows PowerShell.
Verifying on Windows¶
Expected result: status shows names system. The name resolves to the
resource's address, and the NRPT policy lists the zone with the client's name
server. After gdsgate down, Get-DnsClientNrptPolicy no longer lists the zone,
and Get-NetAdapter gdsgate0 finds nothing.
Runtime files and state¶
The client configuration, identity cache, resident socket, endpoint map and
generated fragments live at the paths the configuration names. Private keys are
mode 0600; generated files are owner-only. The endpoint map is regenerated as
needed.
Running under sudo¶
sudo gdsgate up resolves the original caller through SUDO_UID. It uses that
person's configuration, identity, state directory and control socket. On a host
with systemd it installs and starts gdsgate-client@<user>.service, running as
that person with CAP_NET_ADMIN and CAP_NET_BIND_SERVICE. It does not invoke
sudo a second time. A root login without an original caller uses root's own state.
As a service¶
On Linux with a reachable systemd manager, up installs, enables and starts a
service automatically. With the default owner protection, up uses
gdsgate-client@<user>.service and asks for elevation once;
sudo gdsgate up already has that elevation. With protection explicitly
disabled and a loopback level, up can use gdsgate-client.service in the
user's service directory. Explicit transparent still needs a system unit. The
system unit retains the user's identity and disables an earlier user unit so
two daemons do not claim the same socket.
For explicit installation and removal:
# Default protected system unit, preserving the original user's state:
sudo gdsgate --config "$CLIENT_CONFIG" service install client --start
sudo gdsgate service uninstall client
service install client without --start only installs and enables the unit.
The unit invokes this binary with up --foreground; foreground mode never
installs another unit. A repeated up reports a working resident. down stops
it and tears down its networking; uninstall also disables the unit, retaining
configuration and identity. Without systemd, up uses a detached process;
Linux owner protection still needs a privileged launcher, such as
sudo gdsgate up, and refuses startup without the required capability.
Service logs are in journalctl --user -u gdsgate-client.service or
journalctl -u gdsgate-client@<user>.service. On a terminal, up --foreground
continues to log to ~/.gdsgate/gdsgated.log.
Names under the system unit¶
For resolver = auto or system, installing a system unit grants its user the
resolver actions for DNS servers, domains and link teardown when the host has a
policy agent. The grant is removed by uninstall. The system unit has no general
permission to replace /etc/hosts. The resolver grant applies to all network
links the resolver authorizes; it is not restricted to the resident's interface.
On shared machines, review this host-wide permission before installation.
Use the system resolver with transparent
access, or configure host DNS routing separately. status --explain reports
whether names are being served; addresses can work even when names do not.
On Windows, up ensures the client network helper is installed and started,
requesting elevation when necessary. The resident stays in the signed-in user's
session; see Windows.
Completion¶
Generate shell completion (no connection to Authority):
Resource names complete from the local Resident state; with the daemon stopped the list is empty.
Settings¶
Keys used on this page: access, resolver, warm_access,
step_up_on_denial, the transparent address pool, and intercept_names. See
Configuration. After changing a setting, run
gdsgate doctor, then down and up to restart with the new settings.