-
Notifications
You must be signed in to change notification settings - Fork 2
feat: auto detect tee env #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,11 +25,6 @@ var flags []cli.Flag = []cli.Flag{ | |
Value: "https://localhost:80", | ||
Usage: "address to proxy requests to", | ||
}, | ||
&cli.StringFlag{ | ||
Name: "server-attestation-type", | ||
Value: string(proxy.AttestationAzureTDX), | ||
Usage: "type of attestation to expect and verify (" + proxy.AvailableAttestationTypes + ")", | ||
}, | ||
&cli.StringFlag{ | ||
Name: "server-measurements", | ||
Usage: "optional path to JSON measurements enforced on the server", | ||
|
@@ -45,8 +40,8 @@ var flags []cli.Flag = []cli.Flag{ | |
}, | ||
&cli.StringFlag{ | ||
Name: "client-attestation-type", | ||
fnerdman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Value: string(proxy.AttestationNone), | ||
Usage: "type of attestation to present (" + proxy.AvailableAttestationTypes + ")", | ||
Value: "auto", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the default should still be |
||
Usage: "type of attestation to present (" + proxy.AvailableAttestationTypes + "). If not set, automatically detected.", | ||
}, | ||
&cli.BoolFlag{ | ||
Name: "log-json", | ||
|
@@ -96,32 +91,27 @@ func runClient(cCtx *cli.Context) error { | |
Version: common.Version, | ||
}) | ||
|
||
if cCtx.String("server-attestation-type") != "none" && verifyTLS { | ||
log.Error("invalid combination of --verify-tls and --server-attestation-type passed (only 'none' is allowed)") | ||
return errors.New("invalid combination of --verify-tls and --server-attestation-type passed (only 'none' is allowed)") | ||
if serverMeasurements != "" && verifyTLS { | ||
log.Error("invalid combination of --verify-tls and --server-measurements passed (cannot add server measurements and verify default TLS at the same time)") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't check for server-attestation-type anymore since the type was removed as it is automatically selected based on OID during handshake. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about the differences and purpose of the checks before and now after. But would using the auto detect type here help instead and check if it is not "none"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so as a client, you are building up a connection to a server. That server will either provide a ca signed TLS cert, or a self signed aTLS cert. This check is just to make sure what you are doing, and prevent a configuration where you accidentally set verifyTLS but actually you wanted to verify measurements. Taking this example, if this check wouldn't be there, the client would verify a signed TLS cert and not the TEE attestation, thus undermining security. It's just a clear separation for security purposes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar comment as elsewhere — no expected measurement is not the same as not expected attestation |
||
return errors.New("invalid combination of --verify-tls and --server-measurements passed (cannot add server measurements and verify default TLS at the same time)") | ||
} | ||
|
||
clientAttestationType, err := proxy.ParseAttestationType(cCtx.String("client-attestation-type")) | ||
// Auto-detect client attestation type if not specified | ||
clientAttestationType, err := proxy.ParseAttestationType(log, cCtx.String("client-attestation-type")) | ||
if err != nil { | ||
log.With("attestation-type", cCtx.String("client-attestation-type")).Error("invalid client-attestation-type passed, see --help") | ||
return err | ||
} | ||
|
||
serverAttestationType, err := proxy.ParseAttestationType(cCtx.String("server-attestation-type")) | ||
if err != nil { | ||
log.With("attestation-type", cCtx.String("server-attestation-type")).Error("invalid server-attestation-type passed, see --help") | ||
return err | ||
} | ||
|
||
issuer, err := proxy.CreateAttestationIssuer(log, clientAttestationType) | ||
if err != nil { | ||
log.Error("could not create attestation issuer", "err", err) | ||
return err | ||
} | ||
|
||
validators, err := proxy.CreateAttestationValidators(log, serverAttestationType, serverMeasurements) | ||
validators, err := proxy.CreateAttestationValidatorsFromFile(log, serverMeasurements) | ||
if err != nil { | ||
log.Error("could not create attestation validators", "err", err) | ||
log.Error("could not create attestation validators from file", "err", err) | ||
return err | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,8 +40,8 @@ var flags []cli.Flag = []cli.Flag{ | |
&cli.StringFlag{ | ||
Name: "server-attestation-type", | ||
EnvVars: []string{"SERVER_ATTESTATION_TYPE"}, | ||
Value: string(proxy.AttestationAzureTDX), | ||
Usage: "type of attestation to present (" + proxy.AvailableAttestationTypes + ")", | ||
Value: "auto", | ||
Usage: "type of attestation to present (" + proxy.AvailableAttestationTypes + "). If not set, automatically detected.", | ||
}, | ||
&cli.StringFlag{ | ||
Name: "tls-certificate-path", | ||
|
@@ -53,12 +53,6 @@ var flags []cli.Flag = []cli.Flag{ | |
EnvVars: []string{"TLS_PRIVATE_KEY_PATH"}, | ||
Usage: "Path to private key file for the certificate. Only valid with --tls-certificate-path", | ||
}, | ||
&cli.StringFlag{ | ||
Name: "client-attestation-type", | ||
EnvVars: []string{"CLIENT_ATTESTATION_TYPE"}, | ||
Value: string(proxy.AttestationNone), | ||
Usage: "type of attestation to expect and verify (" + proxy.AvailableAttestationTypes + ")", | ||
}, | ||
&cli.StringFlag{ | ||
Name: "client-measurements", | ||
EnvVars: []string{"CLIENT_MEASUREMENTS"}, | ||
|
@@ -132,21 +126,16 @@ func runServer(cCtx *cli.Context) error { | |
return errors.New("not all of --tls-certificate-path and --tls-private-key-path specified") | ||
} | ||
|
||
serverAttestationType, err := proxy.ParseAttestationType(serverAttestationTypeFlag) | ||
// Auto-detect server attestation type if not specified | ||
serverAttestationType, err := proxy.ParseAttestationType(log, cCtx.String("server-attestation-type")) | ||
if err != nil { | ||
log.With("attestation-type", cCtx.String("server-attestation-type")).Error("invalid server-attestation-type passed, see --help") | ||
return err | ||
} | ||
|
||
clientAttestationType, err := proxy.ParseAttestationType(cCtx.String("client-attestation-type")) | ||
if err != nil { | ||
log.With("attestation-type", cCtx.String("client-attestation-type")).Error("invalid client-attestation-type passed, see --help") | ||
return err | ||
} | ||
|
||
validators, err := proxy.CreateAttestationValidators(log, clientAttestationType, clientMeasurements) | ||
validators, err := proxy.CreateAttestationValidatorsFromFile(log, clientMeasurements) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wait, this logic seems incorrect. doesn't this need attestation types to be present to actually verify the client attestation? this should not be loaded from a file, but instead from the client TCP connection! the job of this proxy is NOT to gatekeep, it's to check the attestation and then pass it on for later services to reject or accept this measurement! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the issue is I think confusion of "none" attestation type with no expected measurements. What I'd suggest is to re-introduce the client attestation type flag, and the configuration of "auto" client attestation type with no measurements retains the previous meaning — validate any submitted attestation without expecting any particular one. |
||
if err != nil { | ||
log.Error("could not create attestation validators", "err", err) | ||
log.Error("could not create attestation validators from file", "err", err) | ||
return err | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,15 +23,39 @@ import ( | |
type AttestationType string | ||
|
||
const ( | ||
AttestationAuto AttestationType = "auto" | ||
AttestationNone AttestationType = "none" | ||
AttestationAzureTDX AttestationType = "azure-tdx" | ||
AttestationDCAPTDX AttestationType = "dcap-tdx" | ||
) | ||
|
||
const AvailableAttestationTypes string = "none, azure-tdx, dcap-tdx" | ||
const AvailableAttestationTypes string = "auto, none, azure-tdx, dcap-tdx" | ||
|
||
// DetectAttestationType determines the attestation type based on environment | ||
func DetectAttestationType() AttestationType { | ||
// Check for TDX device files - these indicate DCAP TDX | ||
_, tdxErr1 := os.Stat("/dev/tdx-guest") | ||
_, tdxErr2 := os.Stat("/dev/tdx_guest") | ||
if tdxErr1 == nil || tdxErr2 == nil { | ||
return AttestationDCAPTDX | ||
} | ||
|
||
// Try Azure TDX attestation - if it works, we're in Azure TDX | ||
issuer := azure_tdx.NewIssuer(nil) // nil logger for detection | ||
_, err := issuer.Issue(context.Background(), []byte("test"), []byte("test")) | ||
if err == nil { | ||
return AttestationAzureTDX | ||
} | ||
MoeMahhouk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return AttestationNone | ||
} | ||
|
||
func ParseAttestationType(attestationType string) (AttestationType, error) { | ||
func ParseAttestationType(log *slog.Logger, attestationType string) (AttestationType, error) { | ||
switch attestationType { | ||
case string(AttestationAuto): | ||
detectedType := DetectAttestationType() | ||
log.With("detected_attestation", detectedType).Info("Auto-detected attestation type") | ||
return detectedType, nil | ||
Comment on lines
+53
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: instead of changing the function signature to add logging for the extra new switch case, it could have been handled outside after calling the function and keep the signature as it was before for compatibility and avoid changing it everywhere it was called before. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer to forward the log var than adding logic for extracting this log emit of a subcondition that might not be hit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think "autodetect" should be done here, since it doesn't really make sense to do in some cases (you can't detect someone else's attestation type, while you can parse it just fine from a string) |
||
case string(AttestationNone): | ||
return AttestationNone, nil | ||
case string(AttestationAzureTDX): | ||
|
@@ -56,8 +80,8 @@ func CreateAttestationIssuer(log *slog.Logger, attestationType AttestationType) | |
} | ||
} | ||
|
||
func CreateAttestationValidators(log *slog.Logger, attestationType AttestationType, jsonMeasurementsPath string) ([]atls.Validator, error) { | ||
if attestationType == AttestationNone { | ||
func CreateAttestationValidatorsFromFile(log *slog.Logger, jsonMeasurementsPath string) ([]atls.Validator, error) { | ||
if jsonMeasurementsPath == "" { | ||
return nil, nil | ||
} | ||
|
||
|
@@ -72,26 +96,42 @@ func CreateAttestationValidators(log *slog.Logger, attestationType AttestationTy | |
return nil, err | ||
} | ||
|
||
switch attestationType { | ||
case AttestationAzureTDX: | ||
validators := []atls.Validator{} | ||
for _, measurement := range parsedMeasurements { | ||
// Group validators by attestation type | ||
validatorsByType := make(map[AttestationType][]atls.Validator) | ||
|
||
for _, measurement := range parsedMeasurements { | ||
attestationType, err := ParseAttestationType(log, measurement.AttestationType) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid attestation type %s in measurements file", measurement.AttestationType) | ||
} | ||
|
||
switch attestationType { | ||
case AttestationAzureTDX: | ||
attConfig := config.DefaultForAzureTDX() | ||
attConfig.SetMeasurements(measurement.Measurements) | ||
validators = append(validators, azure_tdx.NewValidator(attConfig, AttestationLogger{Log: log})) | ||
} | ||
return []atls.Validator{NewMultiValidator(validators)}, nil | ||
case AttestationDCAPTDX: | ||
validators := []atls.Validator{} | ||
for _, measurement := range parsedMeasurements { | ||
validatorsByType[attestationType] = append( | ||
validatorsByType[attestationType], | ||
azure_tdx.NewValidator(attConfig, AttestationLogger{Log: log}), | ||
) | ||
case AttestationDCAPTDX: | ||
attConfig := &config.QEMUTDX{Measurements: measurements.DefaultsFor(cloudprovider.QEMU, variant.QEMUTDX{})} | ||
attConfig.SetMeasurements(measurement.Measurements) | ||
validators = append(validators, dcap_tdx.NewValidator(attConfig, AttestationLogger{Log: log})) | ||
validatorsByType[attestationType] = append( | ||
validatorsByType[attestationType], | ||
dcap_tdx.NewValidator(attConfig, AttestationLogger{Log: log}), | ||
) | ||
default: | ||
return nil, fmt.Errorf("unsupported attestation type %s in measurements file", measurement.AttestationType) | ||
} | ||
return []atls.Validator{NewMultiValidator(validators)}, nil | ||
default: | ||
return nil, errors.New("invalid attestation-type passed in") | ||
} | ||
|
||
// Create a MultiValidator for each attestation type | ||
var validators []atls.Validator | ||
for _, typeValidators := range validatorsByType { | ||
validators = append(validators, NewMultiValidator(typeValidators)) | ||
} | ||
|
||
return validators, nil | ||
} | ||
|
||
func ExtractMeasurementsFromExtension(ext *pkix.Extension, v variant.Variant) (map[uint32][]byte, error) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could always simply deprecate the flag instead of removing it right away — the behavior should be the same and the flag could be ignored until we are confident we can safely remove it