Skip to content

access

View on pkg.go.dev

import "github.com/thesimonho/warden/access"

Package access defines the credential passthrough model for Warden containers. An Item groups one or more Credential entries that share host access with a container. Each credential declares how to detect and read a value on the host (Source), an optional transformation (Transform), and how to deliver the result into the container (Injection).

Resolution is stateless — Warden never stores credential values, only the recipes that describe how to obtain and inject them.

Built-in access item IDs. These are stable identifiers stored in the database and referenced by frontends.

const (
BuiltInIDGit = "git"
BuiltInIDSSH = "ssh"
BuiltInIDGPG = "gpg"
)

ContainerGPGAgentPath is the fixed path where the GPG agent socket appears inside the container. Placed at the default gpg socket location (~/.gnupg/S.gpg-agent) so gpg finds it automatically without needing env var overrides or extra configuration.

const ContainerGPGAgentPath = constants.ContainerHomeDir + "/.gnupg/S.gpg-agent"

ContainerGPGPubringPath is where the host’s public keyring is mounted inside the container. GPG needs the public keyring to know which keys exist — the agent socket alone only handles private key operations (signing/decryption).

const ContainerGPGPubringPath = constants.ContainerHomeDir + "/.gnupg/pubring.kbx"

ContainerGPGTrustDBPath is where the host’s trust database is mounted. Without it, GPG shows “[unknown]” trust level for all keys, which can cause signing failures with strict trust settings.

const ContainerGPGTrustDBPath = constants.ContainerHomeDir + "/.gnupg/trustdb.gpg"

ContainerGitConfigHostPath is the container path where the host’s gitconfig is mounted. Used by the entrypoint to set up `git config —global include.path` and by the TransformGitInclude transform to produce content-bearing injections.

const ContainerGitConfigHostPath = constants.ContainerHomeDir + "/.gitconfig.host"

ContainerGitIncludeDir is the directory inside the container where git include files from the host are mounted. The TransformGitInclude transform rewrites include paths to point here.

const ContainerGitIncludeDir = constants.ContainerHomeDir + "/.gitconfig.d"

ContainerSSHAgentPath is the fixed path where the SSH agent socket appears inside the container. The entrypoint’s socat process creates this socket and forwards connections to the host via the TCP bridge. Placed under the warden user’s home directory so the entrypoint can create it without root permissions.

const ContainerSSHAgentPath = constants.ContainerHomeDir + "/.ssh/agent.sock"

ProbeTimeout is the maximum time to wait when verifying a host socket or named pipe has a live listener. Kept short since these are local.

const ProbeTimeout = 500 * time.Millisecond

func ContainerGitIncludePath(basename string) string

ContainerGitIncludePath returns the container path for a git include file, placed under ContainerGitIncludeDir. Uses the file’s basename; when disambiguation is needed, the caller appends a suffix.

func IsBuiltInID(id string) bool

IsBuiltInID reports whether the given ID belongs to a built-in access item.

func ParseGitIncludePaths(content string) []string

ParseGitIncludePaths extracts all include/includeIf path values from a gitconfig file’s content. Returns paths as they appear in the file (may use ~/, be relative, etc.). Handles both quoted and unquoted values.

func ResolveIncludePath(path string, configDir string) string

ResolveIncludePath resolves a gitconfig include path to an absolute host path. Handles tilde expansion and relative path resolution (relative to the directory containing the gitconfig file).

func RewriteGitIncludePaths(content string, pathMap map[string]string) string

RewriteGitIncludePaths replaces include/includeIf path values in gitconfig content using the provided mapping from original path to new path. Only paths present in the map are rewritten; all other content (comments, section headers, conditions) is preserved exactly.

Credential is the atomic unit of the access system. It pairs one or more host Source entries (tried in order, first detected wins) with an optional Transform and one or more container Injection targets.

type Credential struct {
// Label is a human-readable name for this credential (e.g. "SSH Agent Socket").
Label string `json:"label"`
// Sources are tried in order; the first detected value is used.
Sources []Source `json:"sources"`
// Transform is an optional processing step applied to the resolved value.
Transform *Transform `json:"transform,omitempty"`
// Injections are the container-side delivery targets.
Injections []Injection `json:"injections"`
}

CredentialStatus reports whether a single credential’s sources are available on the host, without reading their values.

type CredentialStatus struct {
// Label is the credential's human-readable name.
Label string `json:"label"`
// Available is true when at least one source was detected.
Available bool `json:"available"`
// SourceMatched describes which source was detected (empty when unavailable).
SourceMatched string `json:"sourceMatched,omitempty"`
}

DetectionResult reports which credentials within an access item are available on the current host.

type DetectionResult struct {
// ID is the access item identifier.
ID string `json:"id"`
// Label is the access item display name.
Label string `json:"label"`
// Available is true when at least one credential was detected.
Available bool `json:"available"`
// Credentials contains per-credential detection results.
Credentials []CredentialStatus `json:"credentials"`
}

func Detect(item Item, env EnvResolver) DetectionResult

Detect checks whether each credential’s sources are available on the host without reading their values. This is a lightweight availability check for the UI.

If env is nil, a default ProcessEnvResolver is used.

EnvResolver abstracts environment variable lookup so callers can provide a combined process + shell environment. This allows Warden to resolve credentials from shell config files (.bashrc, .zshrc, .profile) even when launched from a desktop entry that doesn’t inherit the user’s shell environment.

type EnvResolver interface {
// LookupEnv returns the value of the named environment variable.
LookupEnv(key string) (string, bool)
// ExpandEnv replaces ${var} and $var references in the string
// using the resolver's environment.
ExpandEnv(s string) string
// Environ returns the full environment as a []string slice
// (KEY=VALUE format), suitable for use as exec.Cmd.Env.
Environ() []string
}

Injection describes how a resolved credential is delivered into the container.

type Injection struct {
// Type is the kind of container injection.
Type InjectionType `json:"type"`
// Key is the env var name or container path for the injection target.
Key string `json:"key"`
// Value is a static override for the resolved value. When set, this
// is used instead of the source-resolved value. Useful when the
// injection needs a fixed container-side path (e.g. SSH_AUTH_SOCK
// env var pointing to the container socket path).
Value string `json:"value,omitempty"`
// ReadOnly applies to mount injections — when true the mount is read-only.
ReadOnly bool `json:"readOnly,omitempty"`
}

InjectionType describes how a resolved credential is delivered into the container.

type InjectionType string

const (
// InjectionEnvVar sets an environment variable inside the container.
InjectionEnvVar InjectionType = "env"
// InjectionMountFile bind-mounts a host file into the container.
InjectionMountFile InjectionType = "mount_file"
// InjectionMountSocket signals that a Unix domain socket should be
// forwarded into the container. The service layer bridges the host
// socket via a TCP proxy; socat in the container recreates it.
InjectionMountSocket InjectionType = "mount_socket"
)

Item is a named group of credentials that share host access with containers. Items can be built-in (shipped with Warden, not deletable) or user-created.

type Item struct {
// ID is a stable identifier. Built-in items use well-known IDs
// (e.g. "git", "ssh"); user items get generated UUIDs.
ID string `json:"id"`
// Label is the human-readable display name (e.g. "Git Config").
Label string `json:"label"`
// Description explains what this access item provides.
Description string `json:"description"`
// Method is the delivery strategy (only "transport" for now).
Method Method `json:"method"`
// Credentials are the individual credential entries in this group.
Credentials []Credential `json:"credentials"`
// BuiltIn is true for items that ship with Warden.
BuiltIn bool `json:"builtIn"`
}

func BuiltInGPG() Item

BuiltInGPG returns the built-in GPG access item. It forwards the host’s gpg-agent socket and mounts the public keyring so git commit signing (-S) works inside the container without copying private keys. The public keyring is needed because GPG must know which keys exist before it can ask the agent to perform signing operations.

func BuiltInGit() Item

BuiltInGit returns the built-in Git access item. It mounts the host’s .gitconfig (read-only) so git commands inside the container use the host user’s identity and settings.

func BuiltInItemByID(id string) *Item

BuiltInItemByID returns a built-in access item by ID, or nil if not found.

func BuiltInItems() []Item

BuiltInItems returns all built-in access items.

func BuiltInSSH() Item

BuiltInSSH returns the built-in SSH access item. It mounts the host’s SSH config (filtered to strip IdentitiesOnly), known_hosts, and optionally forwards the SSH agent socket.

Method identifies the strategy used to deliver credentials into a container. Only MethodTransport is implemented; the interface exists so a proxy-based method can be added later without changing callers.

type Method string

const (
// MethodTransport extracts a credential on the host and injects it
// directly into the container (env var, bind mount, or socket bridge).
MethodTransport Method = "transport"
)

ProcessEnvResolver delegates directly to the os package. It is the default resolver used when no shell environment is available, and is the test-friendly implementation (works with t.Setenv).

type ProcessEnvResolver struct{}

func (ProcessEnvResolver) Environ() []string

Environ delegates to os.Environ.

func (ProcessEnvResolver) ExpandEnv(s string) string

ExpandEnv delegates to os.ExpandEnv.

func (ProcessEnvResolver) LookupEnv(key string) (string, bool)

LookupEnv delegates to os.LookupEnv.

Refresher is implemented by EnvResolver implementations that support refreshing their cached environment (e.g. by re-spawning the user’s login shell). The service layer type-asserts against this interface before Test and container-create operations.

type Refresher interface {
Refresh() error
}

ResolvedCredential holds the resolution output for a single credential.

type ResolvedCredential struct {
// Label is the credential's human-readable name.
Label string `json:"label"`
// Resolved is true when a source was detected and all injections
// were produced.
Resolved bool `json:"resolved"`
// SourceMatched describes which source was matched (empty when unresolved).
SourceMatched string `json:"sourceMatched,omitempty"`
// Injections are the resolved container-side deliveries.
Injections []ResolvedInjection `json:"injections,omitempty"`
// Error is set when resolution failed (distinct from "not detected").
Error string `json:"error,omitempty"`
}

ResolvedInjection is a single resolved delivery into the container.

type ResolvedInjection struct {
// Type is the injection kind (env, mount_file, mount_socket).
Type InjectionType `json:"type"`
// Key is the env var name or container path.
Key string `json:"key"`
// Value is the resolved content (env var value, host file path,
// or host socket path).
Value string `json:"value"`
// ReadOnly applies to mount injections.
ReadOnly bool `json:"readOnly,omitempty"`
// Content, when non-empty, holds file content to mount instead of
// using Value as a host path. The service layer writes this to a
// temp file before creating the bind mount. Only used with
// InjectionMountFile when a transform rewrites file content
// (e.g. git include path rewriting).
Content string `json:"content,omitempty"`
}

ResolvedItem holds the full resolution output for an access item.

type ResolvedItem struct {
// ID is the access item identifier.
ID string `json:"id"`
// Label is the access item display name.
Label string `json:"label"`
// Credentials contains per-credential resolution results.
Credentials []ResolvedCredential `json:"credentials"`
}

func Resolve(item Item, env EnvResolver) (*ResolvedItem, error)

Resolve resolves all credentials in the given access item, returning the resolved injections for each credential. Credentials whose sources are not detected on the host are skipped (partial resolution is normal). An error is returned only for hard failures like an invalid transform configuration.

If env is nil, a default ProcessEnvResolver is used (backward compatible with direct os.LookupEnv behavior).

ShellEnvResolver captures the user’s full shell environment by spawning their login shell. This allows Warden to see env vars defined in .bashrc, .zshrc, .profile, etc. even when launched from a desktop entry that doesn’t inherit the shell environment.

Process env always takes precedence over the shell cache — if a variable is set in both, the process value wins.

type ShellEnvResolver struct {
// contains filtered or unexported fields
}

func NewShellEnvResolver() *ShellEnvResolver

NewShellEnvResolver creates a resolver that will spawn the user’s login shell to capture environment variables. Call [Load] (typically in a background goroutine) to populate the cache eagerly at startup.

func (r *ShellEnvResolver) Environ() []string

Environ returns the full combined environment as a []string slice (KEY=VALUE format). Shell cache is the base; process env is overlaid so process values always win on conflicts.

func (r *ShellEnvResolver) ExpandEnv(s string) string

ExpandEnv replaces ${var} and $var in the string using the combined process + shell environment.

func (r *ShellEnvResolver) Load() error

Load spawns the user’s login shell and caches the resulting environment. Safe to call from a goroutine. If the shell fails or times out, the resolver degrades gracefully to process-only env.

func (r *ShellEnvResolver) LookupEnv(key string) (string, bool)

LookupEnv checks the process environment first, then falls back to the cached shell environment. Process env takes precedence because explicitly set variables are the most intentional.

func (r *ShellEnvResolver) Refresh() error

Refresh re-spawns the login shell to pick up any env changes since the last load. Skips the spawn if the cache was loaded less than 30 seconds ago (cooldown).

Source describes how to detect and read a credential value on the host. Detection is implicit: an env var must be set, a file or socket must exist, and a command must exit 0.

type Source struct {
// Type is the kind of host source.
Type SourceType `json:"type"`
// Value is the env var name, file path, socket path, or command string.
Value string `json:"value"`
}

SourceType describes where a credential value lives on the host.

type SourceType string

const (
// SourceEnvVar reads a value from a host environment variable.
SourceEnvVar SourceType = "env"
// SourceFilePath reads a file from the host filesystem.
SourceFilePath SourceType = "file"
// SourceSocketPath references a Unix domain socket on the host.
SourceSocketPath SourceType = "socket"
// SourceCommand runs a host command and captures stdout.
SourceCommand SourceType = "command"
// SourceNamedPipe references a Windows named pipe on the host
// (e.g. \\.\pipe\openssh-ssh-agent). Detection dials the pipe
// to verify it has a listener.
SourceNamedPipe SourceType = "named_pipe"
)

Transform describes an optional processing step between source resolution and injection. Only used by built-in access items.

type Transform struct {
// Type identifies the transformation.
Type TransformType `json:"type"`
// Params holds type-specific configuration (e.g. "pattern" for strip_lines).
Params map[string]string `json:"params,omitempty"`
}

TransformType identifies a built-in transformation applied between source resolution and container injection. Transforms are internal — they are not exposed in the user-facing UI.

type TransformType string

const (
// TransformStripLines removes lines matching a case-insensitive
// pattern. Params: "pattern" (regex).
TransformStripLines TransformType = "strip_lines"
// TransformGitInclude writes an include directive rather than
// mounting over the container's .gitconfig.
TransformGitInclude TransformType = "git_include"
)

Generated by gomarkdoc