diff --git a/cmd/zed/context.go b/cmd/zed/context.go index bbb4991e..46ac3ffa 100644 --- a/cmd/zed/context.go +++ b/cmd/zed/context.go @@ -98,6 +98,18 @@ func contextListCmdFunc(cmd *cobra.Command, args []string) error { return nil } +// Reads the trusted certificate if it exists, returning byte form. +func getCertificate(cmd *cobra.Command) (certificate []byte, err error) { + cafile := cobrautil.MustGetString(cmd, "cafile") + if cafile != "" { + certificate, err = os.ReadFile(cafile) + if err != nil { + return nil, fmt.Errorf("Failed to read from CA Certificate File, %w") + } + } + return +} + func contextSetCmdFunc(cmd *cobra.Command, args []string) error { var name, endpoint, apiToken string err := stringz.Unpack(args, &name, &endpoint, &apiToken) @@ -107,11 +119,17 @@ func contextSetCmdFunc(cmd *cobra.Command, args []string) error { insecure := cobrautil.MustGetBool(cmd, "insecure") cfgStore, secretStore := defaultStorage() + + certificate, err := getCertificate(cmd) + if err != nil { + return err + } err = storage.PutToken(storage.Token{ - Name: name, - Endpoint: stringz.DefaultEmpty(endpoint, "grpc.authzed.com:443"), - APIToken: apiToken, - Insecure: &insecure, + Name: name, + Endpoint: stringz.DefaultEmpty(endpoint, "grpc.authzed.com:443"), + APIToken: apiToken, + Insecure: &insecure, + Certificate: certificate, }, secretStore) if err != nil { return err diff --git a/cmd/zed/main.go b/cmd/zed/main.go index e9aef516..c9da183a 100644 --- a/cmd/zed/main.go +++ b/cmd/zed/main.go @@ -51,9 +51,16 @@ func dialOptsFromFlags(cmd *cobra.Command, token storage.Token) []grpc.DialOptio grpc.WithChainUnaryInterceptor(interceptors...), } + if cobrautil.MustGetBool(cmd, "insecure") && cobrautil.MustGetString(cmd, "cafile") != "" { + panic("cafile flag cannot be combined with insecure") + } + if cobrautil.MustGetBool(cmd, "insecure") || (token.IsInsecure()) { opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) opts = append(opts, grpcutil.WithInsecureBearerToken(token.APIToken)) + } else if cobrautil.MustGetString(cmd, "cafile") != "" { + opts = append(opts, grpcutil.WithBearerToken(token.APIToken)) + opts = append(opts, grpcutil.WithCustomCerts(cobrautil.MustGetString(cmd, "cafile"), cobrautil.MustGetBool(cmd, "no-verify-ca"))) } else { opts = append(opts, grpcutil.WithBearerToken(token.APIToken)) opts = append(opts, grpcutil.WithSystemCerts(cobrautil.MustGetBool(cmd, "no-verify-ca"))) @@ -83,6 +90,7 @@ func main() { rootCmd.PersistentFlags().Bool("insecure", false, "connect over a plaintext connection") rootCmd.PersistentFlags().Bool("skip-version-check", false, "if true, no version check is performed against the server") rootCmd.PersistentFlags().Bool("no-verify-ca", false, "do not attempt to verify the server's certificate chain and host name") + rootCmd.PersistentFlags().String("cafile", "", "Use the contents of file as a CA Trust Bundle (PEM-formatted DER)") rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging") _ = rootCmd.PersistentFlags().MarkHidden("debug") // This cannot return its error. diff --git a/internal/storage/secrets.go b/internal/storage/secrets.go index 618cb90e..bcfe50e1 100644 --- a/internal/storage/secrets.go +++ b/internal/storage/secrets.go @@ -17,10 +17,11 @@ import ( var ErrTokenNotFound = errors.New("token does not exist") type Token struct { - Name string - Endpoint string - APIToken string - Insecure *bool + Name string + Endpoint string + APIToken string + Insecure *bool + Certificate []byte } func (t Token) IsInsecure() bool {