Documentation
¶
Overview ¶
Package cockpit is the loopback daemon's Cockpit surface: the request guard every Cockpit route sits behind, the API mux, and the mounts that attach both to the dashboard listener (spec/features/cockpit).
Index ¶
- Constants
- func CanonicalHost(listenAddress string) string
- func Guard(canonical string, next http.Handler) http.Handler
- func Gzip(data []byte) []byte
- func ServePayload(writer http.ResponseWriter, request *http.Request, payload Payload)
- func ServePrivatePayload(writer http.ResponseWriter, request *http.Request, payload Payload)
- type Capability
- type LoginCode
- type MachineRoute
- type MetadataHandler
- type Options
- type OwnerHandler
- type Payload
- type Principal
- type SSHRoute
- type Server
- func (server *Server) HandleMetadata(name string, capability Capability, handler MetadataHandler)
- func (server *Server) HandleOwner(method, path string, capability Capability, handler OwnerHandler)
- func (server *Server) IsOwner(request *http.Request) bool
- func (server *Server) LocalReader(request *http.Request) bool
- func (server *Server) LoginCodeHandler() http.Handler
- func (server *Server) MintLoginCode() (LoginCode, error)
- func (server *Server) Mounts() map[string]http.Handler
- func (server *Server) MountsWith(others map[string]http.Handler) map[string]http.Handler
- func (server *Server) SetMachineRoutes(source func() []MachineRoute)
- type SessionStore
Constants ¶
const ( PagePrefix = web.MountPath APIPrefix = "/api/v1/cockpit/" )
PagePrefix and APIPrefix are the two subtrees Cockpit owns on the loopback listener (cockpit#req:cockpit-mount).
const ( PrincipalAnonymousLocal = "anonymous-local" PrincipalOwner = "owner" )
The two principals a request resolves to.
const ( LoginPath = PagePrefix + "session/login" LogoutPath = PagePrefix + "session/logout" )
LoginPath and LogoutPath are the two session routes under PagePrefix (cockpit#req:owner-session). The login code travels in LoginPath's "code" query parameter.
const CheckedAtHeader = "X-Wb-Cockpit-Checked-At"
CheckedAtHeader is the response header in which a metadata route says when the daemon last found what it serves to be current, as an RFC 3339 time. It is not part of the body, so a body that did not change keeps its ETag and is answered 304 while the header moves on: a client reads the freshness of what it holds from it.
const LoginCodeRPCPath = "/wb.cockpit.v1/login-code"
LoginCodeRPCPath is the owner-channel route that mints a login code. The daemon registers it on the mux its unix socket serves, behind the owner token, and never on the loopback listener. The daemon's file bridge dispatches into that same mux but forwards only the DaemonService procedures it lists, so it refuses this path.
Variables ¶
This section is empty.
Functions ¶
func CanonicalHost ¶
CanonicalHost picks the one loopback name Cockpit's session cookie is scoped to from the daemon's listen address: "::1" when the listener is the IPv6 loopback address, otherwise "127.0.0.1" (including a listener named "localhost", or an address that does not parse).
func Guard ¶
Guard refuses a request whose Host header does not name a loopback host with status 421, before next runs: that is what stops a page that rebinds DNS to the loopback address. The port is not checked beyond being a number, so an SSH forward to another local port works.
canonical is the loopback name the daemon's listener has (see CanonicalHost). A GET or HEAD for a page on any other loopback name is redirected to the same path on http://<canonical>:<port>, using the port the browser reached; any other method there is refused with 421 so it is never replayed to another origin. An API request on an alias is served.
func Gzip ¶ added in v0.175.0
Gzip compresses data at the best level. It is the compressor a Payload is built with in production: the bytes are computed once, when the body is stored, so the level costs nothing per request.
func ServePayload ¶ added in v0.175.0
func ServePayload(writer http.ResponseWriter, request *http.Request, payload Payload)
ServePayload answers a GET with payload: gzip when the request accepts it, identity otherwise, each with its own strong ETag, `Vary: Origin, Accept-Encoding` and `Cache-Control: no-cache` (a client may keep the body but revalidates on every use). If-None-Match accepts either ETag form, and `*`, and a match is answered 304 with no body.
func ServePrivatePayload ¶ added in v0.175.0
func ServePrivatePayload(writer http.ResponseWriter, request *http.Request, payload Payload)
ServePrivatePayload is ServePayload for a body no client and no intermediary may keep: `Cache-Control: no-store`. The ETags are still sent and still honoured, so a reader that holds the body in memory is answered 304.
Types ¶
type Capability ¶
type Capability string
Capability is one permission a Cockpit route or action requires (cockpit#req:capability-vocabulary).
const ( CapabilityFleetRead Capability = "fleet.read" CapabilityMachineRead Capability = "machine.read" CapabilityRepoRead Capability = "repo.read" CapabilityWorktreeRead Capability = "worktree.read" CapabilityBranchRead Capability = "branch.read" CapabilityPRRead Capability = "pr.read" CapabilityAgentRead Capability = "agent.read" CapabilityRepoContentRead Capability = "repo.content.read" )
The capability vocabulary. A Feature that defines an action adds the action's capability.
type LoginCode ¶
LoginCode is a freshly minted login code and the moment it stops being exchangeable.
type MachineRoute ¶ added in v0.175.0
MachineRoute is how the owner reaches one machine over SSH: the id of its machine entry in the fleet document and the host, optional login and wb command of the local configuration (session_move.targets.<machine>.ssh). It names hosts and users, which are not metadata (cockpit#req:anonymous-local-reads-metadata-only): it is sent to an owner session and to nobody else.
type MetadataHandler ¶
type MetadataHandler func(http.ResponseWriter, *http.Request, Principal)
MetadataHandler serves one metadata route for a resolved principal.
type Options ¶
type Options struct {
// CanonicalHost is the loopback name the listener has (see CanonicalHost).
CanonicalHost string
// Config is wb.yaml's cockpit: section, after defaults.
Config wbconfig.CockpitConfig
// Now is the clock login codes and sessions expire against; nil means
// time.Now.
Now func() time.Time
// Random is the source of login codes and session identifiers; nil means
// crypto/rand.
Random io.Reader
// Sessions holds the owner sessions; nil means a fresh in-memory store.
Sessions SessionStore
}
Options is what the daemon hands Cockpit at startup.
type OwnerHandler ¶
type OwnerHandler func(http.ResponseWriter, *http.Request)
OwnerHandler serves one owner route for a request with an owner session.
type Payload ¶ added in v0.175.0
type Payload struct {
// contains filtered or unexported fields
}
Payload is a JSON body prepared once for every request that will read it: the identity bytes, their gzip encoding and the strong ETags of the two (cockpit-views#req:compressed-responses). Preparing it is the only place the compressor runs, so serving never compresses.
func NewPayload ¶ added in v0.175.0
NewPayload prepares identity, compressing it once with compress.
type Principal ¶
type Principal struct {
Name string `json:"principal"`
Capabilities []Capability `json:"capabilities"`
}
Principal is who a request acts as and what it may do.
func (Principal) Has ¶
func (principal Principal) Has(capability Capability) bool
Has reports whether the principal holds capability.
type SSHRoute ¶ added in v0.175.0
type SSHRoute struct {
Host string `json:"host"`
User string `json:"user,omitempty"`
WBPath string `json:"wb_path"`
}
SSHRoute is the SSH part of a MachineRoute.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is Cockpit's state for one daemon run: its login codes, its owner sessions and its routes.
func (*Server) HandleMetadata ¶
func (server *Server) HandleMetadata(name string, capability Capability, handler MetadataHandler)
HandleMetadata registers the metadata GET route APIPrefix+name, which requires capability. A metadata route is the only kind the hosted origin may read (cockpit#req:cross-origin-allowance), so its handler must return nothing beyond the metadata field set, and registering one with a capability that is not a metadata capability panics. Routes are registered before Mounts is called.
func (*Server) HandleOwner ¶
func (server *Server) HandleOwner(method, path string, capability Capability, handler OwnerHandler)
HandleOwner registers an owner route — one that returns content or changes state — at path, under PagePrefix or APIPrefix, for one method. It requires an owner session holding capability; an empty capability requires the session alone. A route whose method is not GET changes state, and also requires the canonical origin and a JSON content type (cockpit#req:owner-routes). No owner route ever receives the cross-origin allowance. Routes are registered before Mounts is called.
func (*Server) IsOwner ¶ added in v0.175.0
IsOwner reports whether request acts as the owner principal, for a route the daemon serves on the same listener outside Cockpit's mounts and so outside Guard (cockpit#req:daemon-log-is-owner-only). It applies what the mounts apply to an owner read: the Host header names a loopback host (cockpit#req:host-header-check), the request comes from the Cockpit page or carries no Origin at all, never from the hosted or a foreign origin, and it carries a live owner session (cockpit#req:owner-session). Nothing else makes a request the owner's: there is no anonymous fallback here.
func (*Server) LocalReader ¶ added in v0.175.0
LocalReader reports whether request was made on this machine by the Cockpit page itself or by a client that names no origin: the Host header names a loopback host (cockpit#req:host-header-check) and the Origin is the canonical one or absent, never the hosted origin and never a foreign one. It is the one classification of "a reader on this machine", for a caller that must tell such a reader from the hosted page, which reads the same metadata routes (cockpit-views#req:remote-exporter-transports counts only the former as demand).
func (*Server) LoginCodeHandler ¶
LoginCodeHandler serves LoginCodeRPCPath. The caller mounts it behind the owner token; it does no authentication of its own.
func (*Server) MintLoginCode ¶
MintLoginCode issues a single-use login code. Only the owner channel may reach it.
func (*Server) Mounts ¶
Mounts returns Cockpit's two subtrees, each behind Guard with the canonical host, in the shape dashboard.Options.Mounts takes. They are mounted whether or not wb.yaml has a hub: section. Taking the mounts freezes the route registries: a registration after it panics.
func (*Server) MountsWith ¶
MountsWith returns Cockpit's mounts added to others (the hub's, which is nil without a hub: section). The others are copied, never modified.
func (*Server) SetMachineRoutes ¶ added in v0.175.0
func (server *Server) SetMachineRoutes(source func() []MachineRoute)
SetMachineRoutes names the source of the owner-only machine_routes of the session response. It is set before Mounts is called.
type SessionStore ¶
type SessionStore interface {
// Create starts a session at now and returns its identifier.
Create(now time.Time) (string, error)
// Valid reports whether id names a session that has not ended or expired
// at now.
Valid(id string, now time.Time) bool
// End ends the session id names, if any.
End(id string)
}
SessionStore holds owner sessions. The daemon's store is in memory, so every session ends when the daemon restarts; the interface is the seam a test replaces. An identifier is a secret: an implementation never logs it.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package fleet is Cockpit's fleet read model: one versioned document of this machine's repositories, worktrees, branches, pull requests and agents, and of every other machine's published snapshot, kept current by a background snapshotter and served without a request ever running Git (cockpit#req:fleet-read-model, cockpit#req:no-fleet-scan-on-the-request-path).
|
Package fleet is Cockpit's fleet read model: one versioned document of this machine's repositories, worktrees, branches, pull requests and agents, and of every other machine's published snapshot, kept current by a background snapshotter and served without a request ever running Git (cockpit#req:fleet-read-model, cockpit#req:no-fleet-scan-on-the-request-path). |
|
Package machinemetrics samples this machine's CPU, load, memory and disk into an in-memory ring buffer for the Cockpit's machine-metrics route (cockpit-views#req:metrics-sampler).
|
Package machinemetrics samples this machine's CPU, load, memory and disk into an in-memory ring buffer for the Cockpit's machine-metrics route (cockpit-views#req:metrics-sampler). |