From b9697197b17a3f6880ffd6060f1cb34df536c720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= Date: Mon, 6 May 2024 15:31:33 +0200 Subject: [PATCH] Refactor options parsing for rest.VerifyCoordinator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Weiße --- cli/internal/cmd/certificate.go | 7 ++--- cli/internal/cmd/cmd.go | 46 +++++++++--------------------- cli/internal/cmd/manifestGet.go | 9 ++---- cli/internal/cmd/manifestSet.go | 9 ++---- cli/internal/cmd/manifestVerify.go | 9 ++---- cli/internal/cmd/recover.go | 9 ++---- cli/internal/rest/rest.go | 43 +++++++++++++++++++--------- 7 files changed, 57 insertions(+), 75 deletions(-) diff --git a/cli/internal/cmd/certificate.go b/cli/internal/cmd/certificate.go index cb47ea49..cdc8eafa 100644 --- a/cli/internal/cmd/certificate.go +++ b/cli/internal/cmd/certificate.go @@ -39,7 +39,7 @@ func runCertificate(saveCert func(io.Writer, *file.Handler, []*pem.Block) error, return func(cmd *cobra.Command, args []string) error { hostname := args[0] fs := afero.NewOsFs() - flags, err := parseRestFlags(cmd.Flags()) + verifyOpts, err := parseRestFlags(cmd.Flags()) if err != nil { return err } @@ -57,10 +57,7 @@ func runCertificate(saveCert func(io.Writer, *file.Handler, []*pem.Block) error, return fmt.Errorf("parsing root certificate from local cache: %w", err) } - certs, err := rest.VerifyCoordinator( - cmd.Context(), cmd.OutOrStdout(), hostname, - flags.eraConfig, flags.k8sNamespace, flags.nonce, flags.insecure, flags.acceptedTCBStatuses, flags.sgxQuotePath, - ) + certs, err := rest.VerifyCoordinator(cmd.Context(), cmd.OutOrStdout(), hostname, verifyOpts) if err != nil { return fmt.Errorf("retrieving certificate from Coordinator: %w", err) } diff --git a/cli/internal/cmd/cmd.go b/cli/internal/cmd/cmd.go index 757310b4..003feade 100644 --- a/cli/internal/cmd/cmd.go +++ b/cli/internal/cmd/cmd.go @@ -30,58 +30,40 @@ type poster interface { Post(ctx context.Context, path, contentType string, body io.Reader) ([]byte, error) } -// restFlags are command line flags used to configure the REST client to talk to the Coordinator. -type restFlags struct { - // k8sNamespace is the namespace of the MarbleRun installation. - // We use this to try to find the Coordinator when retrieving the era config. - k8sNamespace string - // eraConfig is the path to the era config file. - eraConfig string - // insecure is a flag to disable TLS verification. - insecure bool - // acceptedTCBStatuses is a list of TCB statuses that are accepted by the CLI. - // This can be used to allow connections to Coordinator instances running on outdated hardware or firmware. - acceptedTCBStatuses []string - // nonce is a user supplied nonce to be used in the attestation process. - nonce []byte - // sgxQuotePath is the path to save SGX quote file. - sgxQuotePath string -} - // parseRestFlags parses the command line flags used to configure the REST client. -func parseRestFlags(flags *pflag.FlagSet) (restFlags, error) { +func parseRestFlags(flags *pflag.FlagSet) (rest.VerifyCoordinatorOptions, error) { eraConfig, err := flags.GetString("era-config") if err != nil { - return restFlags{}, err + return rest.VerifyCoordinatorOptions{}, err } insecure, err := flags.GetBool("insecure") if err != nil { - return restFlags{}, err + return rest.VerifyCoordinatorOptions{}, err } acceptedTCBStatuses, err := flags.GetStringSlice("accepted-tcb-statuses") if err != nil { - return restFlags{}, err + return rest.VerifyCoordinatorOptions{}, err } k8snamespace, err := flags.GetString("namespace") if err != nil { - return restFlags{}, err + return rest.VerifyCoordinatorOptions{}, err } nonce, err := flags.GetString("nonce") if err != nil { - return restFlags{}, err + return rest.VerifyCoordinatorOptions{}, err } sgxQuotePath, err := flags.GetString("save-sgx-quote") if err != nil { - return restFlags{}, err + return rest.VerifyCoordinatorOptions{}, err } - return restFlags{ - k8sNamespace: k8snamespace, - eraConfig: eraConfig, - insecure: insecure, - acceptedTCBStatuses: acceptedTCBStatuses, - nonce: []byte(nonce), - sgxQuotePath: sgxQuotePath, + return rest.VerifyCoordinatorOptions{ + K8sNamespace: k8snamespace, + ConfigFilename: eraConfig, + Insecure: insecure, + AcceptedTCBStatuses: acceptedTCBStatuses, + Nonce: []byte(nonce), + SGXQuotePath: sgxQuotePath, }, nil } diff --git a/cli/internal/cmd/manifestGet.go b/cli/internal/cmd/manifestGet.go index ffa41295..da35866b 100644 --- a/cli/internal/cmd/manifestGet.go +++ b/cli/internal/cmd/manifestGet.go @@ -43,19 +43,16 @@ func runManifestGet(cmd *cobra.Command, args []string) error { hostname := args[0] fs := afero.NewOsFs() - restFlags, err := parseRestFlags(cmd.Flags()) + verifyOpts, err := parseRestFlags(cmd.Flags()) if err != nil { return err } - caCert, err := rest.VerifyCoordinator( - cmd.Context(), cmd.OutOrStdout(), hostname, - restFlags.eraConfig, restFlags.k8sNamespace, restFlags.nonce, restFlags.insecure, restFlags.acceptedTCBStatuses, restFlags.sgxQuotePath, - ) + caCert, err := rest.VerifyCoordinator(cmd.Context(), cmd.OutOrStdout(), hostname, verifyOpts) if err != nil { return err } - client, err := rest.NewClient(hostname, caCert, nil, restFlags.insecure) + client, err := rest.NewClient(hostname, caCert, nil, verifyOpts.Insecure) if err != nil { return err } diff --git a/cli/internal/cmd/manifestSet.go b/cli/internal/cmd/manifestSet.go index 2c3d07ad..2a25b4d0 100644 --- a/cli/internal/cmd/manifestSet.go +++ b/cli/internal/cmd/manifestSet.go @@ -43,20 +43,17 @@ func runManifestSet(cmd *cobra.Command, args []string) (retErr error) { return err } - restFlags, err := parseRestFlags(cmd.Flags()) + verifyOpts, err := parseRestFlags(cmd.Flags()) if err != nil { return err } - caCert, err := rest.VerifyCoordinator( - cmd.Context(), cmd.OutOrStdout(), hostname, - restFlags.eraConfig, restFlags.k8sNamespace, restFlags.nonce, restFlags.insecure, restFlags.acceptedTCBStatuses, restFlags.sgxQuotePath, - ) + caCert, err := rest.VerifyCoordinator(cmd.Context(), cmd.OutOrStdout(), hostname, verifyOpts) if err != nil { return err } - client, err := rest.NewClient(hostname, caCert, nil, restFlags.insecure) + client, err := rest.NewClient(hostname, caCert, nil, verifyOpts.Insecure) if err != nil { return err } diff --git a/cli/internal/cmd/manifestVerify.go b/cli/internal/cmd/manifestVerify.go index ff912dd2..1af71e23 100644 --- a/cli/internal/cmd/manifestVerify.go +++ b/cli/internal/cmd/manifestVerify.go @@ -43,19 +43,16 @@ func runManifestVerify(cmd *cobra.Command, args []string) error { return err } - restFlags, err := parseRestFlags(cmd.Flags()) + verifyOpts, err := parseRestFlags(cmd.Flags()) if err != nil { return err } - caCert, err := rest.VerifyCoordinator( - cmd.Context(), cmd.OutOrStdout(), hostname, - restFlags.eraConfig, restFlags.k8sNamespace, restFlags.nonce, restFlags.insecure, restFlags.acceptedTCBStatuses, restFlags.sgxQuotePath, - ) + caCert, err := rest.VerifyCoordinator(cmd.Context(), cmd.OutOrStdout(), hostname, verifyOpts) if err != nil { return err } - client, err := rest.NewClient(hostname, caCert, nil, restFlags.insecure) + client, err := rest.NewClient(hostname, caCert, nil, verifyOpts.Insecure) if err != nil { return err } diff --git a/cli/internal/cmd/recover.go b/cli/internal/cmd/recover.go index 3dc8c269..4fc9dd10 100644 --- a/cli/internal/cmd/recover.go +++ b/cli/internal/cmd/recover.go @@ -41,7 +41,7 @@ func runRecover(cmd *cobra.Command, args []string) error { return err } - flags, err := parseRestFlags(cmd.Flags()) + verifyOpts, err := parseRestFlags(cmd.Flags()) if err != nil { return err } @@ -49,15 +49,12 @@ func runRecover(cmd *cobra.Command, args []string) error { // A Coordinator in recovery mode will have a different certificate than what is cached // Only unsealing the Coordinator will allow it to use the original certificate again // Therefore we need to verify the Coordinator is running in the expected enclave instead - caCert, err := rest.VerifyCoordinator( - cmd.Context(), cmd.OutOrStdout(), hostname, - flags.eraConfig, flags.k8sNamespace, flags.nonce, flags.insecure, flags.acceptedTCBStatuses, flags.sgxQuotePath, - ) + caCert, err := rest.VerifyCoordinator(cmd.Context(), cmd.OutOrStdout(), hostname, verifyOpts) if err != nil { return err } - client, err := rest.NewClient(hostname, caCert, nil, flags.insecure) + client, err := rest.NewClient(hostname, caCert, nil, verifyOpts.Insecure) if err != nil { return err } diff --git a/cli/internal/rest/rest.go b/cli/internal/rest/rest.go index e87cb8e1..2ab9465f 100644 --- a/cli/internal/rest/rest.go +++ b/cli/internal/rest/rest.go @@ -48,6 +48,24 @@ const ( dataField = "data" ) +// VerifyCoordinatorOptions defines the options for verifying the connection to the MarbleRun Coordinator. +type VerifyCoordinatorOptions struct { + // ConfigFilename is the path to the era config file. + ConfigFilename string + // K8sNamespace is the namespace of the MarbleRun installation. + // We use this to try to find the Coordinator when retrieving the era config. + K8sNamespace string + // Insecure is a flag to disable TLS verification. + Insecure bool + // AcceptedTCBStatuses is a list of TCB statuses that are accepted by the CLI. + // This can be used to allow connections to Coordinator instances running on outdated hardware or firmware. + AcceptedTCBStatuses []string + // Nonce is a user supplied nonce to be used in the attestation process. + Nonce []byte + // SGXQuotePath is the path to save SGX quote file. + SGXQuotePath string +} + // Client is a REST client for the MarbleRun Coordinator. type Client struct { client *http.Client @@ -155,30 +173,27 @@ func (c *Client) do(req *http.Request) ([]byte, error) { } // VerifyCoordinator verifies the connection to the MarbleRun Coordinator. -func VerifyCoordinator( - ctx context.Context, out io.Writer, host, configFilename, k8sNamespace string, - nonce []byte, insecure bool, acceptedTCBStatuses []string, sgxQuotePath string, -) ([]*pem.Block, error) { +func VerifyCoordinator(ctx context.Context, out io.Writer, host string, opts VerifyCoordinatorOptions) ([]*pem.Block, error) { // skip verification if specified - if insecure { + if opts.Insecure { fmt.Fprintln(out, "Warning: skipping quote verification") certs, _, err := attestation.InsecureGetCertificate(ctx, host) return certs, err } - if configFilename == "" { - configFilename = eraDefaultConfig + if opts.ConfigFilename == "" { + opts.ConfigFilename = eraDefaultConfig // reuse existing config from current working directory if none specified // or try to get latest config from github if it does not exist - if _, err := os.Stat(configFilename); err == nil { + if _, err := os.Stat(opts.ConfigFilename); err == nil { fmt.Fprintln(out, "Reusing existing config file") - } else if err := fetchLatestCoordinatorConfiguration(ctx, out, k8sNamespace); err != nil { + } else if err := fetchLatestCoordinatorConfiguration(ctx, out, opts.K8sNamespace); err != nil { return nil, err } } - eraCfgRaw, err := os.ReadFile(configFilename) + eraCfgRaw, err := os.ReadFile(opts.ConfigFilename) if err != nil { return nil, fmt.Errorf("reading era config file: %w", err) } @@ -188,8 +203,8 @@ func VerifyCoordinator( return nil, fmt.Errorf("unmarshalling era config: %w", err) } - pemBlock, tcbStatus, rawQuote, err := attestation.GetCertificate(ctx, host, nonce, eraCfg) - validity, err := tcb.CheckStatus(tcbStatus, err, acceptedTCBStatuses) + pemBlock, tcbStatus, rawQuote, err := attestation.GetCertificate(ctx, host, opts.Nonce, eraCfg) + validity, err := tcb.CheckStatus(tcbStatus, err, opts.AcceptedTCBStatuses) if err != nil { return nil, err } @@ -201,8 +216,8 @@ func VerifyCoordinator( fmt.Fprintln(out, "Warning: TCB level invalid, but accepted by configuration:", tcbStatus) } - if sgxQuotePath != "" { - if err := os.WriteFile(sgxQuotePath, rawQuote, 0o644); err != nil { + if opts.SGXQuotePath != "" { + if err := os.WriteFile(opts.SGXQuotePath, rawQuote, 0o644); err != nil { return pemBlock, fmt.Errorf("error saving SGX quote to disk: %s", err) } }