| // Package component implements reusable bits for cloud service components. Each |
| // component is currently defined as being a standalone Go binary with its own |
| // internal gRPC listener. Subsequent listeners (eg. public gRPC or HTTP) can be |
| // defined by users of this library. |
| package component |
| |
| import ( |
| "crypto/tls" |
| "crypto/x509" |
| "flag" |
| "os" |
| "path/filepath" |
| |
| "github.com/adrg/xdg" |
| "google.golang.org/grpc" |
| "google.golang.org/grpc/credentials" |
| "k8s.io/klog/v2" |
| ) |
| |
| // Configuration is the common configuration of a component. |
| type Configuration struct { |
| // GRPCKeyPath is the filesystem path of the x509 key used to serve internal |
| // gRPC traffic. |
| GRPCKeyPath string |
| // GRPCCertificatePath is the filesystem path of the x509 certificate used to |
| // serve internal gRPC traffic. |
| GRPCCertificatePath string |
| // GRPCCAPath is the filesystem path of of the x509 CA certificate used to |
| // verify incoming connections on internal gRPC traffic. |
| GRPCCAPath string |
| // GRPCListenAddress is the address on which the component should server |
| // internal gRPC traffic. |
| GRPCListenAddress string |
| |
| // DevCerts, if enabled, automatically generates development CA and component |
| // certificates/keys at DevCertsPath, uses these to serve traffic. |
| DevCerts bool |
| // DevCertsPath sets the prefix in which DevCerts are generated. All components |
| // should have the same path set so that they reuse the CA certificate. |
| DevCertsPath string |
| |
| // ComponentName is the name of this component, which should be [a-z0-9+]. It's |
| // used to prefix all flags set by the Configuration. |
| ComponentName string |
| } |
| |
| // RegisterFlags registers the component configuration to be provided by flags. |
| // This must be called exactly once before then calling flags.Parse(). |
| func (c *Configuration) RegisterFlags(componentName string) { |
| flag.StringVar(&c.GRPCKeyPath, componentName+"_grpc_key_path", "", "Path to gRPC server/client key for "+componentName) |
| flag.StringVar(&c.GRPCCertificatePath, componentName+"_grpc_certificate_path", "", "Path to gRPC server/client certificate for "+componentName) |
| flag.StringVar(&c.GRPCCAPath, componentName+"_grpc_ca_certificate_path", "", "Path to gRPC CA certificate for "+componentName) |
| flag.StringVar(&c.GRPCListenAddress, componentName+"_grpc_listen_address", ":4242", "Address to listen at for gRPC connections for "+componentName) |
| |
| flag.BoolVar(&c.DevCerts, componentName+"_dev_certs", false, "Use developer certificates (autogenerated) for "+componentName) |
| flag.StringVar(&c.DevCertsPath, componentName+"_dev_certs_path", filepath.Join(xdg.ConfigHome, "monogon-dev-certs"), "Path for storing developer certificates") |
| |
| c.ComponentName = componentName |
| } |
| |
| // GRPCServerOptions returns pre-built grpc.ServerOptions that this component |
| // should use to serve internal gRPC. |
| func (c *Configuration) GRPCServerOptions() []grpc.ServerOption { |
| var certPath, keyPath, caPath string |
| if c.DevCerts { |
| // Use devcerts if requested. |
| certPath, keyPath, caPath = c.GetDevCerts() |
| } else { |
| // Otherwise, use data from flags. |
| if c.GRPCKeyPath == "" { |
| klog.Exitf("-grpc_key_path must be set") |
| } |
| if c.GRPCCertificatePath == "" { |
| klog.Exitf("-grpc_certificate_path must be set") |
| } |
| if c.GRPCCAPath == "" { |
| klog.Exitf("-grpc_ca_certificate_path must be set") |
| } |
| keyPath = c.GRPCKeyPath |
| certPath = c.GRPCCertificatePath |
| caPath = c.GRPCCAPath |
| } |
| |
| ca, err := os.ReadFile(caPath) |
| if err != nil { |
| klog.Exitf("Could not read GRPC CA: %v", err) |
| } |
| certPool := x509.NewCertPool() |
| if !certPool.AppendCertsFromPEM(ca) { |
| klog.Exitf("Could not load GRPC CA: %v", err) |
| } |
| |
| pair, err := tls.LoadX509KeyPair(certPath, keyPath) |
| if err != nil { |
| klog.Exitf("Could not load GRPC TLS keypair: %v", err) |
| } |
| tlsConf := &tls.Config{ |
| Certificates: []tls.Certificate{pair}, |
| ClientAuth: tls.RequireAndVerifyClientCert, |
| RootCAs: certPool, |
| } |
| return []grpc.ServerOption{ |
| grpc.Creds(credentials.NewTLS(tlsConf)), |
| } |
| } |