blob: 4353bdfcd38884f0532ecd70eb711bdfe31550e2 [file] [log] [blame]
Serge Bazanskibee272f2022-09-13 13:52:42 +02001// Package component implements reusable bits for cloud service components. Each
2// component is currently defined as being a standalone Go binary with its own
3// internal gRPC listener. Subsequent listeners (eg. public gRPC or HTTP) can be
4// defined by users of this library.
5package component
6
7import (
8 "crypto/tls"
9 "crypto/x509"
10 "flag"
11 "os"
12 "path/filepath"
13
14 "github.com/adrg/xdg"
15 "google.golang.org/grpc"
16 "google.golang.org/grpc/credentials"
17 "k8s.io/klog/v2"
18)
19
20// Configuration is the common configuration of a component.
21type Configuration struct {
22 // GRPCKeyPath is the filesystem path of the x509 key used to serve internal
23 // gRPC traffic.
24 GRPCKeyPath string
25 // GRPCCertificatePath is the filesystem path of the x509 certificate used to
26 // serve internal gRPC traffic.
27 GRPCCertificatePath string
28 // GRPCCAPath is the filesystem path of of the x509 CA certificate used to
29 // verify incoming connections on internal gRPC traffic.
30 GRPCCAPath string
31 // GRPCListenAddress is the address on which the component should server
32 // internal gRPC traffic.
33 GRPCListenAddress string
34
35 // DevCerts, if enabled, automatically generates development CA and component
36 // certificates/keys at DevCertsPath, uses these to serve traffic.
37 DevCerts bool
38 // DevCertsPath sets the prefix in which DevCerts are generated. All components
39 // should have the same path set so that they reuse the CA certificate.
40 DevCertsPath string
41
42 // ComponentName is the name of this component, which should be [a-z0-9+]. It's
43 // used to prefix all flags set by the Configuration.
44 ComponentName string
45}
46
47// RegisterFlags registers the component configuration to be provided by flags.
48// This must be called exactly once before then calling flags.Parse().
49func (c *Configuration) RegisterFlags(componentName string) {
50 flag.StringVar(&c.GRPCKeyPath, componentName+"_grpc_key_path", "", "Path to gRPC server/client key for "+componentName)
51 flag.StringVar(&c.GRPCCertificatePath, componentName+"_grpc_certificate_path", "", "Path to gRPC server/client certificate for "+componentName)
52 flag.StringVar(&c.GRPCCAPath, componentName+"_grpc_ca_certificate_path", "", "Path to gRPC CA certificate for "+componentName)
53 flag.StringVar(&c.GRPCListenAddress, componentName+"_grpc_listen_address", ":4242", "Address to listen at for gRPC connections for "+componentName)
54
55 flag.BoolVar(&c.DevCerts, componentName+"_dev_certs", false, "Use developer certificates (autogenerated) for "+componentName)
56 flag.StringVar(&c.DevCertsPath, componentName+"_dev_certs_path", filepath.Join(xdg.ConfigHome, "monogon-dev-certs"), "Path for storing developer certificates")
57
58 c.ComponentName = componentName
59}
60
61// GRPCServerOptions returns pre-built grpc.ServerOptions that this component
62// should use to serve internal gRPC.
63func (c *Configuration) GRPCServerOptions() []grpc.ServerOption {
64 var certPath, keyPath, caPath string
65 if c.DevCerts {
66 // Use devcerts if requested.
67 certPath, keyPath, caPath = c.GetDevCerts()
68 } else {
69 // Otherwise, use data from flags.
70 if c.GRPCKeyPath == "" {
71 klog.Exitf("-grpc_key_path must be set")
72 }
73 if c.GRPCCertificatePath == "" {
74 klog.Exitf("-grpc_certificate_path must be set")
75 }
76 if c.GRPCCAPath == "" {
77 klog.Exitf("-grpc_ca_certificate_path must be set")
78 }
79 keyPath = c.GRPCKeyPath
80 certPath = c.GRPCCertificatePath
81 caPath = c.GRPCCAPath
82 }
83
84 ca, err := os.ReadFile(caPath)
85 if err != nil {
86 klog.Exitf("Could not read GRPC CA: %v", err)
87 }
88 certPool := x509.NewCertPool()
89 if !certPool.AppendCertsFromPEM(ca) {
90 klog.Exitf("Could not load GRPC CA: %v", err)
91 }
92
93 pair, err := tls.LoadX509KeyPair(certPath, keyPath)
94 if err != nil {
95 klog.Exitf("Could not load GRPC TLS keypair: %v", err)
96 }
97 tlsConf := &tls.Config{
98 Certificates: []tls.Certificate{pair},
99 ClientAuth: tls.RequireAndVerifyClientCert,
100 RootCAs: certPool,
101 }
102 return []grpc.ServerOption{
103 grpc.Creds(credentials.NewTLS(tlsConf)),
104 }
105}