| // 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" | 
 | ) | 
 |  | 
 | // ComponentConfig is the common configuration of a component. It's | 
 | // supposed to be instantiated within a Configuration struct of a component. | 
 | // | 
 | // It can be configured by flags (via RegisterFlags) or manually (eg. in tests). | 
 | type ComponentConfig 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 *ComponentConfig) 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 *ComponentConfig) 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)), | 
 | 	} | 
 | } | 
 |  | 
 | // GRPCServerOptionsPublic returns pre-built grpc.ServerOptions that this | 
 | // component should use to serve public gRPC. Any client will be allowed to | 
 | // connect, and it's up to the server implementation to authenticate incoming | 
 | // requests. | 
 | func (c *ComponentConfig) GRPCServerOptionsPublic() []grpc.ServerOption { | 
 | 	var certPath, keyPath string | 
 | 	if c.DevCerts { | 
 | 		// Use devcerts if requested. | 
 | 		certPath, keyPath, _ = 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") | 
 | 		} | 
 | 		keyPath = c.GRPCKeyPath | 
 | 		certPath = c.GRPCCertificatePath | 
 | 	} | 
 |  | 
 | 	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.RequestClientCert, | 
 | 	} | 
 | 	return []grpc.ServerOption{ | 
 | 		grpc.Creds(credentials.NewTLS(tlsConf)), | 
 | 	} | 
 | } |