Skip to content

Commit

Permalink
Refactor options parsing for rest.VerifyCoordinator
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Weiße <[email protected]>
  • Loading branch information
daniel-weisse committed May 6, 2024
1 parent 046fdd2 commit b969719
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 75 deletions.
7 changes: 2 additions & 5 deletions cli/internal/cmd/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)
}
Expand Down
46 changes: 14 additions & 32 deletions cli/internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
9 changes: 3 additions & 6 deletions cli/internal/cmd/manifestGet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
9 changes: 3 additions & 6 deletions cli/internal/cmd/manifestSet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
9 changes: 3 additions & 6 deletions cli/internal/cmd/manifestVerify.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
9 changes: 3 additions & 6 deletions cli/internal/cmd/recover.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,20 @@ 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
}

// 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
}
Expand Down
43 changes: 29 additions & 14 deletions cli/internal/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
}
Expand All @@ -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)
}
}
Expand Down

0 comments on commit b969719

Please sign in to comment.