-
Notifications
You must be signed in to change notification settings - Fork 1
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
feat: auto detect tee env #29
Conversation
49480b3
to
e36bf5b
Compare
cmd/proxy-server/main.go
Outdated
} else { | ||
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")) |
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.
can client-attestation-type
also be detected? ideally we'd run a single server proxy instance that works for all client attestation types
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.
Yes, I've added this feat. Client attestation type is now determined via the measurements.json. During TLS handshake attestation the attester will check the OID of the attestee and then select the measurements it has in store for this OID, and tries to verify if they are a match. This works vice versa for the proxy-client, it attests servers based on measurement attestation types as well.
d4c4207
to
a1e15de
Compare
a1e15de
to
80df1e4
Compare
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 comment
The 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.
Checking if serverMeasurements are set is an alternative, as serverMeasurements will include the measurements and attestationTypes we approve.
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.
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"?
What is this check for? (just for my understanding)
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.
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 comment
The 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
I've verified this PR working on Azure, I've tested
|
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 |
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.
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 comment
The 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 comment
The 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)
I think the issue is that autodetect is not really an attestation type, I would probably distinguish between parsing attestation type string to a type from "resolving" auto to a type of attestation.
Resolving the attestation type would make much more sense in CreateAttestationIssuer
if you'd want to still fit it in the currently existing functions.
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 comment
The 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"?
What is this check for? (just for my understanding)
/dev/guest-tdx is going to be deprecated at some point. check for configfs-tsm as well or add it to the icebox for future |
} | ||
|
||
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 comment
The 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 comment
The 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.
@fnerdman non-none attestation type with no expected measurements is a valid configuration with the meaning to authenticate the attestation without authorizing it, whereas now it's impossible (with a single measurements flag). What is impossible exactly is the difference between expecting no attestation (error if provided) and expecting an attestation, but not any particular one (error if not provided or if invalid) — although we could probably change "no measurements" to mean "error if provided but not valid". I think the products handle this case correctly — since we will simply not be forwarding any attestation report.
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.
The same applies to the client, as the error really is introduced in the first check of CreateAttestationValidatorsFromFile
.
The alternative is to change the meaning of "none" attestation type and no measurements to "validate all provided attestations, no attestation — no problem"
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.
It's probably a bit late, but the one alternative that would work very well would be negotiation of attestation type rather than detection — if the client and server exchange allowed measurements, the parties could check if they can provide any of the allowed ones, much like in hashing and encryption algo negotiations.
Regardless, we should verify this doesn't break products before merging (not only that the change works in isolation)
@@ -45,8 +40,8 @@ var flags []cli.Flag = []cli.Flag{ | |||
}, | |||
&cli.StringFlag{ | |||
Name: "client-attestation-type", | |||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I think the default should still be none
— this is mostly still used to verify the server's attestation without providing client's own attestation. "auto" should be an option for the client to select if needed
} | ||
|
||
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 comment
The 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.
@fnerdman non-none attestation type with no expected measurements is a valid configuration with the meaning to authenticate the attestation without authorizing it, whereas now it's impossible (with a single measurements flag). What is impossible exactly is the difference between expecting no attestation (error if provided) and expecting an attestation, but not any particular one (error if not provided or if invalid) — although we could probably change "no measurements" to mean "error if provided but not valid". I think the products handle this case correctly — since we will simply not be forwarding any attestation report.
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.
The same applies to the client, as the error really is introduced in the first check of CreateAttestationValidatorsFromFile
.
The alternative is to change the meaning of "none" attestation type and no measurements to "validate all provided attestations, no attestation — no problem"
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 |
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.
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)
I think the issue is that autodetect is not really an attestation type, I would probably distinguish between parsing attestation type string to a type from "resolving" auto to a type of attestation.
Resolving the attestation type would make much more sense in CreateAttestationIssuer
if you'd want to still fit it in the currently existing functions.
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 comment
The 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
@@ -25,11 +25,6 @@ var flags []cli.Flag = []cli.Flag{ | |||
Value: "https://localhost:80", | |||
Usage: "address to proxy requests to", | |||
}, | |||
&cli.StringFlag{ |
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
I think this patch needs some clarification:
This patch implements two types of automatism in terms of figuring out the right attestation-type to choose.
What was the status quo before this patch?
What does this patch provide?
The attestation type of the locally generated quote is detected automatically by checking properties that are only available in these environments. If no such environment is detected the logic will default to not presenting an aTLS cert. This is the default behavior but we can "force" a specific attestation type via cmdline flag. I think auto is the right default flag for this type, as it fits "none" in all cases when no TEE is present, and selects the right TEE attestation type if a TEE is present.
The attestation type of the remote party can be detected via the OID parameter which is part of the remote party aTLS certificate. I don't think it makes sense to keep the attestation type flag since the remote party will advertise the type of attestation it is providing. We should now rather distinguish between these 3 use cases:
First, the remote attestation shall not be verified but be ignored. This is covered by leaving the measurements.json empty. No measurements provided will lead to attestation being ignored.
Second, only the non app specific parts of the remote attestations shall be verified (TCB level), the rest will be passed through as http headers. This is covered by specifying the validators that should be uses during the handshake attestation in the measurements file, but without describing app specific measurements (PCRs/MRTD/RTMRs).
Third, only the exact measurements provided in the measurements.json file should be used for verification. This is covered by specifying the exact measurements required in the json file. This patch will automatically handle different attestation types and use the correct validator based on the OID provided by the remote party.