Documentation
¶
Overview ¶
Package accessgate is the StackKits Access Gate (homelab identity target §7): the forward-auth decision every server runs in front of TinyAuth and, on the remote listener, in front of every route including endpoints with native authentication.
The listener class is fixed by the router, never by the request: the LAN router calls the gate at /v1/gate/lan from its forward-auth middleware, the remote router binds /v1/gate/remote to its only entrypoint. A request through the remote listener is never treated as home, forwarded headers do not change that, and TinyAuth's parent-domain cookie is never consulted there. The route table contract lives in internal/accessgatetable.
Index ¶
- Constants
- Variables
- type AgentEnvelopeVerifier
- type AgentRequest
- type AppPassVerifier
- type BrowserLauncher
- type BrowserSessionVerifier
- type Decision
- type DeviceProof
- type DeviceProofVerifier
- type Gate
- type LaunchRequest
- type Options
- type PinnedGatewayKeyMissing
- type Principal
- type RateLimit
- type Request
- type Route
- type Table
Constants ¶
const ( // LaunchPath is reserved on every gated host for the launch exchange. LaunchPath = "/.well-known/stackkit/launch" // SessionCookie is the gate's per-host browser session. SessionCookie = "__Host-stackkit-session" // LaunchStateCookie binds a launch to the browser the gate sent to the Hub. LaunchStateCookie = "__Host-stackkit-launch" )
Browser sessions and app passes (homelab identity target §6.3, §12). A browser carries no per-request proof: the gate sends a navigation without a session to the Hub with a fresh launch state, the Hub signs a 60 second launch proof with the device key, and the gate exchanges it at LaunchPath on the same host for a host-only session cookie recorded against the device. A third-party app sends its per-device app pass in HeaderAppPass on the routes the Owner opened to apps away.
const ( PathLAN = "/v1/gate/lan" PathRemote = "/v1/gate/remote" )
Gate paths. The LAN router's forward-auth middleware calls PathLAN; the remote router binds PathRemote to its only entrypoint.
const ( RoutesSchema = accessgatetable.RoutesSchema ListenerLAN = accessgatetable.ListenerLAN ListenerRemote = accessgatetable.ListenerRemote ExposureLocalOnly = accessgatetable.ExposureLocalOnly ExposureRemotePrivate = accessgatetable.ExposureRemotePrivate ExposurePublic = accessgatetable.ExposurePublic AuthenticationNone = accessgatetable.AuthenticationNone AuthenticationHuman = accessgatetable.AuthenticationHuman AuthenticationHumanDevice = accessgatetable.AuthenticationHumanDevice AuthenticationAgent = accessgatetable.AuthenticationAgent AuthenticationWorkload = accessgatetable.AuthenticationWorkload PrivilegeUser = accessgatetable.PrivilegeUser PrivilegeSignIn = accessgatetable.PrivilegeSignIn PrivilegeAdmin = accessgatetable.PrivilegeAdmin PrivilegeIdentity = accessgatetable.PrivilegeIdentity PrivilegeSecrets = accessgatetable.PrivilegeSecrets PrivilegeVault = accessgatetable.PrivilegeVault PrivilegeRecovery = accessgatetable.PrivilegeRecovery IngressForwardAuth = accessgatetable.IngressForwardAuth IngressNative = accessgatetable.IngressNative IngressNone = accessgatetable.IngressNone IdentityAdministrationPocketID = accessgatetable.IdentityAdministrationPocketID HeaderListener = accessgatetable.HeaderListener HeaderSubject = accessgatetable.HeaderSubject HeaderDevice = accessgatetable.HeaderDevice HeaderAuthorization = accessgatetable.HeaderAuthorization HeaderDPoP = accessgatetable.HeaderDPoP HeaderAgentEnvelope = accessgatetable.HeaderAgentEnvelope // HeaderAppPass carries an app pass; the router strips it before the backend. HeaderAppPass = accessgatetable.HeaderAppPass )
Variables ¶
var ( // LANBoundPrivileges stay on the LAN listener unless the Owner opts in. LANBoundPrivileges = accessgatetable.LANBoundPrivileges // ResponseHeaders is the exact authResponseHeaders list of both gate // middlewares. ResponseHeaders = accessgatetable.ResponseHeaders // ErrInvalidTable rejects a table the gate cannot enforce exactly. ErrInvalidTable = accessgatetable.ErrInvalidTable )
var ErrNoPinnedGatewayKey = errors.New("accessgate: no Gateway key is pinned at linking")
ErrNoPinnedGatewayKey is the agent class's fail-closed state: no linking step pins a Gateway key on this server yet.
Functions ¶
This section is empty.
Types ¶
type AgentEnvelopeVerifier ¶
type AgentEnvelopeVerifier interface {
VerifyAgentEnvelope(ctx context.Context, request AgentRequest) (Principal, error)
}
AgentEnvelopeVerifier verifies a Gateway-signed envelope (subject, agent instance, lease, body hash) against the Gateway key pinned at linking and maps it to the Homelab account it acts for (igti.12).
type AgentRequest ¶
AgentRequest is what the gate can bind an agent envelope to. Forward-auth carries no body, so the body hash is verified by the endpoint.
type AppPassVerifier ¶
type AppPassVerifier interface {
VerifyAppPass(ctx context.Context, host, pass string) (Principal, error)
}
AppPassVerifier verifies an app pass for one host.
type BrowserLauncher ¶
type BrowserLauncher interface {
// LaunchURL is the Hub page a browser without a session is sent to.
LaunchURL(target, state string) (string, error)
// CompleteLaunch admits a launch proof and returns the session value and
// its lifetime: four hours when the launch came through the LAN
// listener (Home), one hour otherwise.
CompleteLaunch(ctx context.Context, launch LaunchRequest) (string, time.Duration, error)
}
BrowserLauncher is the gate's seam to the Hub launch (internal/identity).
type BrowserSessionVerifier ¶
type BrowserSessionVerifier interface {
VerifyBrowserSession(ctx context.Context, host string, request *http.Request) (Principal, error)
}
BrowserSessionVerifier verifies the per-host __Host- session the gate sets after a Hub launch proof (homelab identity target §6.3, igti.10). Until it exists a browser carries no accepted remote session.
type Decision ¶
type Decision struct {
Allow bool
Status int
Reason string
Headers http.Header
Redirect string
Cookies []*http.Cookie
}
Decision is the gate's answer. Headers are set only on allow; Redirect and Cookies only on the browser launch answers (a 302 the router relays).
type DeviceProof ¶
DeviceProof is the per-request proof a kombify client carries, as the router observed the request: RouteOrigin is the https origin of the matched route from the gate's own route table, never a request header; Path is the request target in origin form (the verifier drops the query); Header holds "Authorization: DPoP <credential>" and the "DPoP" proof.
type DeviceProofVerifier ¶
type DeviceProofVerifier interface {
VerifyDeviceProof(ctx context.Context, proof DeviceProof) (Principal, error)
}
DeviceProofVerifier is the seam to the device verifier (internal/identity, reworked by igti.2). It returns the account, the device and the account's groups, or an error; the gate never inspects the credential itself.
type Gate ¶
type Gate struct {
// contains filtered or unexported fields
}
Gate decides per route from the compiler-owned table.
func (*Gate) ServeHTTP ¶
func (g *Gate) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP answers one forward-auth call. The router sends the original method, host and request URI as X-Forwarded-Method, X-Forwarded-Host and X-Forwarded-Uri derived from the request itself (trustForwardHeader is off on both gate middlewares); everything else about the request is read from its own headers.
type LaunchRequest ¶
type LaunchRequest struct {
Host string
Credential string
Proof string
State string
Target string
Home bool
}
LaunchRequest is one launch exchange as the gate observed it.
type Options ¶
type Options struct {
Devices DeviceProofVerifier
Browsers BrowserSessionVerifier
Launcher BrowserLauncher
AppPasses AppPassVerifier
Agents AgentEnvelopeVerifier
Now func() time.Time
}
Options are the gate's verifier seams. A nil seam denies its class.
type PinnedGatewayKeyMissing ¶
type PinnedGatewayKeyMissing struct{}
PinnedGatewayKeyMissing denies every agent request.
func (PinnedGatewayKeyMissing) VerifyAgentEnvelope ¶
func (PinnedGatewayKeyMissing) VerifyAgentEnvelope(context.Context, AgentRequest) (Principal, error)
VerifyAgentEnvelope always fails closed.
type Principal ¶
type Principal struct {
Subject string
Device string
Groups []string
GroupsKnown bool
ExpiresAt time.Time
}
Principal is a verified Homelab account, with its enrolled device where one was proven. Groups are the account's household groups when the verifier knows them (GroupsKnown); a device credential carries none.
type RateLimit ¶
type RateLimit = accessgatetable.RateLimit
The route table contract, re-exported for the gate's callers.
type Request ¶
type Request struct {
Listener string
Method string
Host string
URI string
Header http.Header
HTTP *http.Request
}
Request is one forward-auth call: the listener class the router bound, and the method, host and request URI the router derived from the request.
type Route ¶
type Route = accessgatetable.Route
The route table contract, re-exported for the gate's callers.
type Table ¶
type Table = accessgatetable.Table
The route table contract, re-exported for the gate's callers.
func ParseTable ¶
ParseTable decodes and validates a route table strictly.