Documentation
¶
Overview ¶
Package control is the listener for a daemon's gRPC admin (control-plane) API: the port an operator's CLI talks to, as opposed to the one the daemon's users talk to.
An admin API can reconfigure, drain or stop the daemon, so the question "who may reach it" has only two acceptable answers, and this package accepts no other:
- a unix socket, whose file mode is its access control: it is left at 0600, owned by the daemon's user, and nobody else can connect;
- TCP with mutual TLS: the client must present a certificate signed by a CA the daemon names, and the daemon presents one signed by a CA the client names.
There is no plaintext TCP, and loopback is not an exception: any local user can connect to 127.0.0.1, so loopback without client certificates is an admin API open to every account on the machine.
Use ¶
cfg := control.Config{Listen: "unix:///run/mydaemon/admin.sock"}
if err := cfg.Check(); err != nil { ... } // at config load, no side effects
lis, opts, err := control.Listen(cfg)
gs := grpc.NewServer(opts...)
adminpb.RegisterAdminServer(gs, impl)
go gs.Serve(lis)
and in a handler, for the audit line:
log.Printf("admin: %s called Drain", control.Caller(ctx))
which prints "uid=1000" for a unix caller and "cn=ops-laptop" for an mTLS one.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Caller ¶
Caller says who made the RPC whose context this is, for an audit line: "cn=<subject CN>" for an mTLS client (the whole subject if its CN is empty), "uid=<uid>" for a unix client — read from the kernel when the connection was accepted (SO_PEERCRED on Linux, LOCAL_PEERCRED on darwin and FreeBSD) — or "unix" where the platform cannot say (Windows, and the BSDs without LOCAL_PEERCRED). It returns "unknown" when the context carries neither.
func Dial ¶
func Dial(c ClientConfig, opts ...grpc.DialOption) (*grpc.ClientConn, error)
Dial returns a client connection to an admin API. As with grpc.NewClient, nothing is connected until the first RPC; what fails here is a configuration that could never work, and unreadable files.
For a unix target, each connection is refused unless the process listening on the socket runs as this process's uid, as root, or as ClientConfig.ServerUID: the RPC then fails with an error naming the uid found.
func Listen ¶
Listen binds the admin listener and returns it with the grpc.ServerOptions (transport credentials) to pass to grpc.NewServer. It runs Check first.
For unix, the socket is bound inside a fresh 0700 directory next to the requested path, chmod'ed to 0600 there, and only then hard-linked into place, so it is never reachable by another user, not even for the instant between bind and chmod — see bindUnix. The process umask is not touched: it is process-wide, and changing it around bind would silently change the mode of every file other goroutines create meanwhile. A socket file already at the path is removed only if nothing answers on it; a live one is an error naming the path, and anything that is not a socket is never removed. Closing the listener removes the socket file, if it is still the one this call created.
For TCP, the server presents TLSCertFile/TLSKeyFile, requires and verifies a client certificate against ClientCAFile, and accepts TLS 1.2 at the least (TLS 1.3 is negotiated whenever the client offers it).
Types ¶
type ClientConfig ¶
type ClientConfig struct {
// Target has the syntax of Config.Listen.
Target string
// CertFile and KeyFile are the client's certificate and key, and
// ServerCAFile the CA that signed the server's certificate. All three are
// required for TCP and forbidden for unix.
CertFile, KeyFile, ServerCAFile string
// ServerName is the name to verify the server's certificate against. It
// defaults to the host in Target.
ServerName string
// ServerUID is, for a unix Target, a uid the server may run as besides
// this process's own uid and root's, which are always accepted. Dial
// checks who is listening on the socket (the kernel's peer credentials)
// and refuses any other uid: a socket in a directory others can write may
// have been bound by an impostor, which would otherwise receive the CLI's
// commands. Forbidden for TCP, where the server's certificate is the
// check. On a platform without peer credentials (Windows, and the BSDs
// other than FreeBSD) the uid cannot be read and is not checked.
ServerUID *uint32
}
ClientConfig says how to reach an admin API: for tests, CLIs, and the consumers' own tests.
type Config ¶
type Config struct {
// Listen is "unix:///abs/path.sock" or "host:port".
Listen string
// TLSCertFile and TLSKeyFile are the server's certificate and key, and
// ClientCAFile the CA bundle client certificates must chain to. All three
// are required together for TCP and forbidden for unix: a socket's
// permissions are its access control.
TLSCertFile, TLSKeyFile, ClientCAFile string
}
Config says where the admin API listens.
