Documentation
¶
Overview ¶
Package hostagent is the forwarding agent that stands on each app host of a multi-host Docker install where Pando's own container stands on one host (O-45, design 06 §4, notes-multi-host-docker-issue-72.md).
The agent decides nothing. Pando's proxy authenticates the request, authorizes it, mints the assertion and strips every X-Pando-* header and pando_* cookie (R-023, R-053, R-173) before it opens a connection here. The agent's two checks are the ones that keep it from being a way around that: a connection must present Pando's client certificate, and it is carried only to a container on one of the Pando-managed app networks the agent was joined to on its own host.
This package is shared by the agent (cmd/pando host-agent) and the runtime adapter that dials it. It imports nothing from core.
Index ¶
- Constants
- func AnswerPing(conn *tls.Conn, liveness Liveness, member Member)
- func ClientTLS(as Authorities, client tls.Certificate, host string) *tls.Config
- func Contacted(err error) bool
- func Dial(ctx context.Context, addr string, tlsConfig *tls.Config, name string, port int) (net.Conn, error)
- func DialRuntime(ctx context.Context, addr string, tlsConfig *tls.Config) (net.Conn, error)
- func DueForRenewal(notAfter, now time.Time) bool
- func Fingerprint256(der []byte) string
- func IsControl(cert *x509.Certificate) bool
- func NewAuthority() ([]byte, error)
- func Permitted(addr netip.Addr, pool netip.Prefix, own []netip.Prefix) error
- func Ping(ctx context.Context, addr string, tlsConfig *tls.Config, from string) ([]byte, error)
- func PinnedClientTLS(as Authorities, client tls.Certificate, host, pin string) *tls.Config
- func ProbeSender(cert *x509.Certificate) (from string, ok bool)
- func RenewingServerTLS(cert *RenewingCert, authoritiesPEM []byte) (*tls.Config, error)
- func ServerName(host string) string
- func ServerTLS(certPEM, keyPEM, authoritiesPEM []byte) (*tls.Config, error)
- func SystemInterfaces() ([]netip.Prefix, error)
- type Agent
- type Authorities
- func (as Authorities) Fingerprint() string
- func (as Authorities) InMemory(cert tls.Certificate) *RenewingCert
- func (as Authorities) IssueClient() (tls.Certificate, error)
- func (as Authorities) IssueCollector() (tls.Certificate, error)
- func (as Authorities) IssueHost(name string, roles []string, pub crypto.PublicKey) ([]byte, error)
- func (as Authorities) IssueHostAt(now time.Time, name string, roles []string, pub crypto.PublicKey) ([]byte, error)
- func (as Authorities) IssueHostClient(hostID string) (tls.Certificate, error)
- func (as Authorities) IssueHostClientAt(now time.Time, hostID string) (tls.Certificate, error)
- func (as Authorities) IssueServer(host string) (certPEM, keyPEM []byte, err error)
- func (as Authorities) Pool() *x509.CertPool
- func (as Authorities) PoolPEM() []byte
- func (as Authorities) Reissue(leaf *x509.Certificate, now time.Time) (tls.Certificate, error)
- type Authority
- type ContainerAPI
- type DockerFencer
- type Fence
- type Liveness
- type Member
- type PingError
- type RenewingCert
- func (r *RenewingCert) Current() tls.Certificate
- func (r *RenewingCert) Due() bool
- func (r *RenewingCert) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error)
- func (r *RenewingCert) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error)
- func (r *RenewingCert) Refresh(ctx context.Context) (bool, error)
- func (r *RenewingCert) Run(ctx context.Context, interval time.Duration)
- func (r *RenewingCert) Set(cert tls.Certificate)
Constants ¶
const ClientCommonName = "pando-proxy"
ClientCommonName is the subject every certificate Pando's proxy presents to an agent carries. An agent refuses any other, so a certificate the same authority issued to an agent (a server certificate) cannot be used to open a connection through another agent.
const CollectorUnit = "collector"
CollectorUnit is the organizational unit of the leader's collector's certificate, the one probe certificate that names no host.
const DefaultPort = 7443
DefaultPort is where an agent listens, and the one port an app host publishes (R-026 concerns app workloads; this port reaches none without Pando's certificate).
const HostCommonName = "pando-host"
HostCommonName is the subject of the certificate a host presents when it checks another (IssueHost). An agent takes a ping from it and nothing else: a host's certificate opens no connection to an app, so a host that was broken into cannot use it to reach another host's apps around Pando's proxy (R-023).
const PingProtocol = "pando-ping/1"
PingProtocol is the ALPN protocol a ping asks for.
const RenewBefore = 30 * 24 * time.Hour
RenewBefore is how long before it expires a certificate is issued again.
const RoleControl = "control"
RoleControl is the organizational unit a certificate issued for a control host carries: the cluster's own name for the role (R-414).
Variables ¶
This section is empty.
Functions ¶
func AnswerPing ¶ added in v0.17.0
AnswerPing answers the one ping sent on conn, a connection whose handshake negotiated PingProtocol and verified the client's certificate against the cluster authority: a control host's cluster listener hands it such connections (R-421). Anything but a ping is refused, and so is a ping from a host member does not take: one removed from the cluster.
func ClientTLS ¶
func ClientTLS(as Authorities, client tls.Certificate, host string) *tls.Config
ClientTLS is the dialer's TLS configuration: Pando's client certificate, and the agent's verified against the authorities and the host's name.
The client certificate is issued again from the authorities before it expires (sharedRenewal), so a process that runs past leafValidity keeps connecting (R-421): every configuration made for one certificate shares its renewal.
func Contacted ¶ added in v0.17.0
Contacted reports whether err, from Ping, still came from the machine: true for nil, and for a PingError with Contact.
func Dial ¶
func Dial(ctx context.Context, addr string, tlsConfig *tls.Config, name string, port int) (net.Conn, error)
Dial opens a connection through the agent at addr to the container name on port, as the client tlsConfig (ClientTLS) authenticates. The connection it returns is a stream to the workload.
func DialRuntime ¶ added in v0.17.0
DialRuntime opens a stream through the agent at addr to its host's container runtime API, as the client tlsConfig authenticates: a control host's (IsControl). A Docker client dials one per connection it keeps.
func DueForRenewal ¶ added in v0.17.0
DueForRenewal reports whether a certificate valid until notAfter is due to be issued again at now.
func Fingerprint256 ¶ added in v0.17.0
Fingerprint256 is a certificate's fingerprint as the cluster records it: the SHA-256 of its DER, hex (cluster_hosts.cert_sha256).
func IsControl ¶ added in v0.17.0
func IsControl(cert *x509.Certificate) bool
IsControl reports whether a verified client certificate was issued for a control host: Pando's proxy's, carrying RoleControl. Only a holder of the authority's key — a control host — can issue one (R-416).
func NewAuthority ¶
NewAuthority generates an authority and returns it as PEM: the certificate followed by its private key. That text is what the adapter's agent_ca credential holds, sealed like every adapter credential (R-190).
func Permitted ¶
Permitted reports whether the agent may forward to addr.
Only to a container on an app network on this host: addr must be inside the app network range, and on a network the agent itself is joined to — which on a multi-host install the adapter does for app networks and nothing else. Within such a network, never to the network's first address (Docker gives it to the bridge, which is the host itself), its network or broadcast address, or the agent's own address.
func Ping ¶ added in v0.17.0
Ping checks the agent at addr, as the client tlsConfig authenticates, and returns the view it answered with. from is the checking host's ID, or "" for the leader's collector. An error is a *PingError, which says whether the machine answered at all (Contacted).
func PinnedClientTLS ¶ added in v0.17.0
func PinnedClientTLS(as Authorities, client tls.Certificate, host, pin string) *tls.Config
PinnedClientTLS is ClientTLS that also requires the agent's certificate to be the one its host joined the cluster with (cluster_hosts.cert_sha256), so a machine given another certificate under the host's name is not taken for it. An empty pin is ClientTLS. pin may name several fingerprints, separated by commas: while a host's certificate is renewed, the one it holds and the one it was just issued are both its own (state.Host.Pins).
func ProbeSender ¶ added in v0.17.0
func ProbeSender(cert *x509.Certificate) (from string, ok bool)
ProbeSender is who a verified probe certificate was issued to: a host's ID, or "" for the leader's collector. ok is false for any other certificate, which sends no ping.
func RenewingServerTLS ¶ added in v0.17.0
func RenewingServerTLS(cert *RenewingCert, authoritiesPEM []byte) (*tls.Config, error)
RenewingServerTLS is ServerTLS serving cert, which is renewed while the agent runs (R-421): each handshake presents the certificate held then. A nil cert leaves Certificates to the caller.
func ServerName ¶
ServerName is the name an agent's certificate is issued for, and what the dialer verifies, for the host named host in the adapter's configuration.
func ServerTLS ¶
ServerTLS is the agent's TLS configuration: its own certificate, and a client certificate required, issued by one of the authorities, for client authentication, and naming Pando's proxy.
func SystemInterfaces ¶
SystemInterfaces is every IPv4 address of this process's network interfaces, with its prefix.
Types ¶
type Agent ¶
type Agent struct {
// TLS must require and verify Pando's client certificate (ServerTLS).
TLS *tls.Config
// Pool is the range the app networks on this host take their addresses
// from: the Docker adapter's network_pool. A target outside it is
// refused, which is what keeps the agent off the network it is published
// on and off anything else on the host.
Pool netip.Prefix
// Resolve looks a container name up. Nil is the system resolver, which in
// a container on user-defined networks is Docker's.
Resolve func(ctx context.Context, name string) ([]netip.Addr, error)
// Interfaces are the agent's own addresses with their prefix lengths: the
// networks it is joined to. Nil reads them from the system each time.
Interfaces func() ([]netip.Prefix, error)
// Dial connects to the permitted address. Nil is a TCP dial.
Dial func(ctx context.Context, addr string) (net.Conn, error)
// RuntimeSocket is the host's own container runtime socket, rootless on
// a joined host. Set, the agent also carries runtime API calls to it, for
// a control host's certificate only (R-416, runtime.go). Empty carries
// none: the agents this runtime starts itself are given no socket.
RuntimeSocket string
// DialRuntime connects to the runtime. Nil dials RuntimeSocket.
DialRuntime func(ctx context.Context) (net.Conn, error)
Logger *zap.Logger
// Liveness answers other hosts' pings (ping.go, R-421). Nil refuses them.
Liveness Liveness
// Fence, when set, refuses runtime calls that create or start a
// container while this host is fencing (runtime.go, R-421).
Fence Fence
// Member reports whether a host is in the cluster now; a ping from one
// that is not, such as a host removed from it, or sent with a revoked
// certificate, is refused (R-415). Nil takes any host the authority
// issued a probe certificate to.
Member Member
}
Agent accepts Pando's proxy and carries each connection to one workload.
type Authorities ¶
type Authorities []Authority
Authorities is the configured set, newest first. The first issues; every one is trusted, which is how the authority is replaced without a moment in which an agent and the proxy disagree: add the new one first, wait for the agents to be re-created, then remove the old one.
func ParseAuthorities ¶
func ParseAuthorities(data []byte) (Authorities, error)
ParseAuthorities reads one or more authorities from PEM, each a certificate followed by its key.
func (Authorities) Fingerprint ¶
func (as Authorities) Fingerprint() string
Fingerprint names the set, so an agent issued under a different one is known to need re-creating.
func (Authorities) InMemory ¶ added in v0.17.0
func (as Authorities) InMemory(cert tls.Certificate) *RenewingCert
InMemory renews cert from the authorities: the same subject, names and usages, for a new key. For a certificate only a holder of the authority's key issues, and which nothing records: Pando's proxy's, the leader's collector's, a control host's probe and listener certificates.
func (Authorities) IssueClient ¶
func (as Authorities) IssueClient() (tls.Certificate, error)
IssueClient issues the certificate Pando's proxy presents to agents. Held in memory only: every replica issues its own from the authority at start.
It carries RoleControl: only a control host holds an authority's key, so only a control host can issue one, and an agent carries runtime calls for no other certificate (R-416).
func (Authorities) IssueCollector ¶ added in v0.17.0
func (as Authorities) IssueCollector() (tls.Certificate, error)
IssueCollector issues the certificate the leader's collector pings hosts with, to hear what each has reached: it names no host, so its pings are recorded as no host's.
func (Authorities) IssueHost ¶ added in v0.17.0
IssueHost issues a joined host's certificate for its key pub, as DER: the one its agent serves with, and the one it presents to other hosts (R-416, notes-cluster.md). It names the host as ServerName does, so a dialer verifies it by name, and carries the host's roles as organizational units. It is never Pando's proxy's: its subject is not ClientCommonName, which an agent requires of every connection, so a host's own certificate opens nothing through another host's agent.
func (Authorities) IssueHostAt ¶ added in v0.17.0
func (as Authorities) IssueHostAt(now time.Time, name string, roles []string, pub crypto.PublicKey) ([]byte, error)
IssueHostAt is IssueHost as of now: how a host's certificate is renewed (R-421: a certificate left to expire would cut every host off at once).
func (Authorities) IssueHostClient ¶ added in v0.17.0
func (as Authorities) IssueHostClient(hostID string) (tls.Certificate, error)
IssueHostClient issues the certificate the host hostID presents to other hosts when it checks them: client authentication only, carrying the host's ID, which is what a ping it sends is taken to be from (R-421).
func (Authorities) IssueHostClientAt ¶ added in v0.17.0
func (as Authorities) IssueHostClientAt(now time.Time, hostID string) (tls.Certificate, error)
IssueHostClientAt is IssueHostClient as of now.
func (Authorities) IssueServer ¶
func (as Authorities) IssueServer(host string) (certPEM, keyPEM []byte, err error)
IssueServer issues an agent's certificate for the host named host, as PEM. Server authentication only: it cannot be presented to another agent as Pando's.
func (Authorities) Pool ¶
func (as Authorities) Pool() *x509.CertPool
Pool is every authority's certificate, for verifying either side.
func (Authorities) PoolPEM ¶
func (as Authorities) PoolPEM() []byte
PoolPEM is Pool as PEM, which is what an agent is given: certificates only, never a key.
func (Authorities) Reissue ¶ added in v0.17.0
func (as Authorities) Reissue(leaf *x509.Certificate, now time.Time) (tls.Certificate, error)
Reissue issues a certificate like leaf, its subject, names and extended key usages, for a new key, as of now.
type Authority ¶
type Authority struct {
Cert *x509.Certificate
Key crypto.Signer
}
Authority is one install certificate authority for agents: its certificate and its key.
type ContainerAPI ¶ added in v0.17.0
type ContainerAPI interface {
ContainerList(ctx context.Context, options client.ContainerListOptions) (client.ContainerListResult, error)
ContainerStop(ctx context.Context, containerID string, options client.ContainerStopOptions) (client.ContainerStopResult, error)
}
ContainerAPI is the slice of the Docker client a fence uses.
type DockerFencer ¶ added in v0.17.0
type DockerFencer struct {
Docker ContainerAPI
}
DockerFencer stops every running app container on this host, through the host's own container runtime, when the host is cut off from most of its cluster (R-421). It only stops: nothing here starts a container, and the Docker runtime creates app containers with no restart policy, so a fenced host never restarts its apps by itself. Pando starts them again, from the control plane, once the host is reached.
Only containers Pando made for an app: the edge and Pando's own containers carry no app label and are left running.
func (DockerFencer) Fence ¶ added in v0.17.0
func (f DockerFencer) Fence(ctx context.Context) (int, error)
Fence stops the running app containers, all at once, and returns how many it stopped. It tries every one before reporting the first failure, so one container that will not stop does not keep the rest running.
type Fence ¶ added in v0.17.0
type Fence interface {
Fencing() bool
}
Fence is whether this host is fencing (R-421): it decided it is cut off from most of the cluster, and has not reached it again since, whether or not its apps are stopped yet. core's liveness.Monitor is one. From the decision on, through the stop, the agent refuses every runtime call that would create or start a container, so the control plane cannot undo the fence through it, nor start a container the stop has already passed.
type Liveness ¶ added in v0.17.0
Liveness answers pings: core's liveness.Monitor. from is the host that sent the ping, or "" for the leader's collector.
type Member ¶ added in v0.17.0
Member reports whether a ping from hostID, sent with the certificate whose SHA-256 fingerprint is certSHA256, is answered: false for a host removed from the cluster, and for any certificate revoked with it (R-415).
type PingError ¶ added in v0.17.0
PingError is a ping that got no view back. Contact says whether the machine at the address answered anyway (R-421): a connection refused or reset, a TLS alert, a certificate, a refusal or any other reply all come from a machine that is up and on the network, whatever its Pando process is doing. Only silence — a timeout, no route — is not contact. A host whose Pando process is down, restarting or waiting for its database is not cut off, and must never be taken for one that is: its apps may be running.
type RenewingCert ¶ added in v0.17.0
type RenewingCert struct {
// Renew issues the next certificate, as of now.
Renew func(ctx context.Context, now time.Time) (tls.Certificate, error)
// Now is the clock; nil is time.Now.
Now func() time.Time
// OnHandshake renews a certificate found due on a handshake before it is
// presented: for a renewal made in memory from an authority, which is
// quick. A renewal that asks another host is made by Run instead, off
// the handshake.
OnHandshake bool
// OnError is told of a renewal that failed; the certificate held is kept.
OnError func(error)
// contains filtered or unexported fields
}
RenewingCert is a certificate a running process presents, issued again before it expires.
func NewRenewingCert ¶ added in v0.17.0
func NewRenewingCert(cert tls.Certificate, renew func(ctx context.Context, now time.Time) (tls.Certificate, error)) *RenewingCert
NewRenewingCert is a RenewingCert holding cert.
func (*RenewingCert) Current ¶ added in v0.17.0
func (r *RenewingCert) Current() tls.Certificate
Current is the certificate held now.
func (*RenewingCert) Due ¶ added in v0.17.0
func (r *RenewingCert) Due() bool
Due reports whether the certificate held is due for renewal.
func (*RenewingCert) GetCertificate ¶ added in v0.17.0
func (r *RenewingCert) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error)
GetCertificate is tls.Config.GetCertificate.
func (*RenewingCert) GetClientCertificate ¶ added in v0.17.0
func (r *RenewingCert) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error)
GetClientCertificate is tls.Config.GetClientCertificate.
func (*RenewingCert) Refresh ¶ added in v0.17.0
func (r *RenewingCert) Refresh(ctx context.Context) (bool, error)
Refresh renews the certificate when it is due, and reports whether it did. A failed renewal keeps the certificate held.
func (*RenewingCert) Run ¶ added in v0.17.0
func (r *RenewingCert) Run(ctx context.Context, interval time.Duration)
Run renews the certificate whenever it is due, looking every interval, until ctx ends.
func (*RenewingCert) Set ¶ added in v0.17.0
func (r *RenewingCert) Set(cert tls.Certificate)
Set replaces the certificate held.