blob: dee4703d7eef3ff65492fd78af37b6d35f8b7029 [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
Serge Bazanskia5baa872022-09-15 18:49:35 +020020// ComponentConfig is the common configuration of a component. It's
21// supposed to be instantiated within a Configuration struct of a component.
22//
23// It can be configured by flags (via RegisterFlags) or manually (eg. in tests).
24type ComponentConfig struct {
Serge Bazanskibee272f2022-09-13 13:52:42 +020025 // GRPCKeyPath is the filesystem path of the x509 key used to serve internal
26 // gRPC traffic.
27 GRPCKeyPath string
28 // GRPCCertificatePath is the filesystem path of the x509 certificate used to
29 // serve internal gRPC traffic.
30 GRPCCertificatePath string
31 // GRPCCAPath is the filesystem path of of the x509 CA certificate used to
32 // verify incoming connections on internal gRPC traffic.
33 GRPCCAPath string
34 // GRPCListenAddress is the address on which the component should server
35 // internal gRPC traffic.
36 GRPCListenAddress string
37
38 // DevCerts, if enabled, automatically generates development CA and component
39 // certificates/keys at DevCertsPath, uses these to serve traffic.
40 DevCerts bool
41 // DevCertsPath sets the prefix in which DevCerts are generated. All components
42 // should have the same path set so that they reuse the CA certificate.
43 DevCertsPath string
44
45 // ComponentName is the name of this component, which should be [a-z0-9+]. It's
46 // used to prefix all flags set by the Configuration.
47 ComponentName string
48}
49
50// RegisterFlags registers the component configuration to be provided by flags.
51// This must be called exactly once before then calling flags.Parse().
Serge Bazanskia5baa872022-09-15 18:49:35 +020052func (c *ComponentConfig) RegisterFlags(componentName string) {
Serge Bazanskibee272f2022-09-13 13:52:42 +020053 flag.StringVar(&c.GRPCKeyPath, componentName+"_grpc_key_path", "", "Path to gRPC server/client key for "+componentName)
54 flag.StringVar(&c.GRPCCertificatePath, componentName+"_grpc_certificate_path", "", "Path to gRPC server/client certificate for "+componentName)
55 flag.StringVar(&c.GRPCCAPath, componentName+"_grpc_ca_certificate_path", "", "Path to gRPC CA certificate for "+componentName)
56 flag.StringVar(&c.GRPCListenAddress, componentName+"_grpc_listen_address", ":4242", "Address to listen at for gRPC connections for "+componentName)
57
58 flag.BoolVar(&c.DevCerts, componentName+"_dev_certs", false, "Use developer certificates (autogenerated) for "+componentName)
59 flag.StringVar(&c.DevCertsPath, componentName+"_dev_certs_path", filepath.Join(xdg.ConfigHome, "monogon-dev-certs"), "Path for storing developer certificates")
60
61 c.ComponentName = componentName
62}
63
64// GRPCServerOptions returns pre-built grpc.ServerOptions that this component
65// should use to serve internal gRPC.
Serge Bazanskia5baa872022-09-15 18:49:35 +020066func (c *ComponentConfig) GRPCServerOptions() []grpc.ServerOption {
Serge Bazanskibee272f2022-09-13 13:52:42 +020067 var certPath, keyPath, caPath string
68 if c.DevCerts {
69 // Use devcerts if requested.
70 certPath, keyPath, caPath = c.GetDevCerts()
71 } else {
72 // Otherwise, use data from flags.
73 if c.GRPCKeyPath == "" {
74 klog.Exitf("-grpc_key_path must be set")
75 }
76 if c.GRPCCertificatePath == "" {
77 klog.Exitf("-grpc_certificate_path must be set")
78 }
79 if c.GRPCCAPath == "" {
80 klog.Exitf("-grpc_ca_certificate_path must be set")
81 }
82 keyPath = c.GRPCKeyPath
83 certPath = c.GRPCCertificatePath
84 caPath = c.GRPCCAPath
85 }
86
87 ca, err := os.ReadFile(caPath)
88 if err != nil {
89 klog.Exitf("Could not read GRPC CA: %v", err)
90 }
91 certPool := x509.NewCertPool()
92 if !certPool.AppendCertsFromPEM(ca) {
93 klog.Exitf("Could not load GRPC CA: %v", err)
94 }
95
96 pair, err := tls.LoadX509KeyPair(certPath, keyPath)
97 if err != nil {
98 klog.Exitf("Could not load GRPC TLS keypair: %v", err)
99 }
100 tlsConf := &tls.Config{
101 Certificates: []tls.Certificate{pair},
102 ClientAuth: tls.RequireAndVerifyClientCert,
103 RootCAs: certPool,
104 }
105 return []grpc.ServerOption{
106 grpc.Creds(credentials.NewTLS(tlsConf)),
107 }
108}
Serge Bazanski4abeb132022-10-11 11:32:19 +0200109
110// GRPCServerOptionsPublic returns pre-built grpc.ServerOptions that this
111// component should use to serve public gRPC. Any client will be allowed to
112// connect, and it's up to the server implementation to authenticate incoming
113// requests.
114func (c *ComponentConfig) GRPCServerOptionsPublic() []grpc.ServerOption {
115 var certPath, keyPath string
116 if c.DevCerts {
117 // Use devcerts if requested.
118 certPath, keyPath, _ = c.GetDevCerts()
119 } else {
120 // Otherwise, use data from flags.
121 if c.GRPCKeyPath == "" {
122 klog.Exitf("-grpc_key_path must be set")
123 }
124 if c.GRPCCertificatePath == "" {
125 klog.Exitf("-grpc_certificate_path must be set")
126 }
127 keyPath = c.GRPCKeyPath
128 certPath = c.GRPCCertificatePath
129 }
130
131 pair, err := tls.LoadX509KeyPair(certPath, keyPath)
132 if err != nil {
133 klog.Exitf("Could not load GRPC TLS keypair: %v", err)
134 }
135 tlsConf := &tls.Config{
136 Certificates: []tls.Certificate{pair},
137 ClientAuth: tls.RequestClientCert,
138 }
139 return []grpc.ServerOption{
140 grpc.Creds(credentials.NewTLS(tlsConf)),
141 }
142}