Skip to content
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

credentials: create API for transport security level information #3214

Merged
merged 2 commits into from
Jan 7, 2020

Conversation

yihuazhang
Copy link
Contributor

@yihuazhang yihuazhang commented Nov 27, 2019

This PR does the following:

  1. Define a tri-state security level (SecurityNone, IntegrityOnly and PrivacyAndIntegrity) indicating the protection level of an established channel.
  2. Create ConnAuthInfo type struct that is a wrapper of credentials.AuthInfo and also contains the security level of an established channel. Existing TransportCredentials implementations are modified to return ConnAuthInfo upon finishing the handshake.
  3. Associate a security level to each credentials.PerRPCCredentials implementations (JWT, Oauth2).
  4. Enforce the security level check when attempting to send PerRPCCredentials on a channel. For backward-compatibility we sill preserve the original checking behavior (i.e., using RequreTransportSecurity()).

@yihuazhang yihuazhang force-pushed the security_level_negotiation branch from 4ebef55 to f12549f Compare November 27, 2019 01:05
@cesarghali cesarghali added no release notes Type: Feature New features or improvements in behavior labels Dec 4, 2019
@yihuazhang
Copy link
Contributor Author

Friendly ping :)

Copy link
Member

@dfawley dfawley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure travis is green. From the error it seems like gofmt wasn't run on grpclb_test. You can run vet.sh locally before pushing updates. (I'd recommend running via VET_SKIP_PROTO=1 vet.sh to skip some slower checks that shouldn't affect you.)

Copy link
Contributor

@jiangtaoli2016 jiangtaoli2016 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good. One question:

In clientconn.go, it checks if any PerRPCCredentials require transport security, and fails early.
Do we also want to check SecurityLevel there as well?

// EnableSecurityLevelCheck checks if security level examination should be enabled. It will be enabled
// only if 1) AuthInfo is an instance of ConnAuthInfo and 2) rpcCred implements
// MinimumSecurityLevel interface.
func EnableSecurityLevelCheck(authInfo AuthInfo, rpcCred PerRPCCredentials) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ShouldCheckSecurityLevel maybe a better name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Jiangtao, we decided not to do a centralized check, but let each PerRPCCredentials to implement its own check in GetRequestMetadata().

@yihuazhang yihuazhang force-pushed the security_level_negotiation branch from 815bfb3 to e501cd9 Compare December 13, 2019 19:20
@yihuazhang
Copy link
Contributor Author

@dfawley The PR is updated per our off-line discussion. Could you please take a look?

credentials/credentials.go Show resolved Hide resolved

// CommonAuthInfo contains authenticated information common to AuthInfo implementations.
type CommonAuthInfo struct {
Level SecurityLevel
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: SecurityLevel SecurityLevel. IMO "Level" by itself is not distinct enough.

@yihuazhang
Copy link
Contributor Author

@dfawley The comment is addressed. PTAL. Also, the failed tests do not seem to relate to my PR.

return "testAuthInfo"
}

func TestCheckSecurityLevel(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use table-style tests:

testCases := []struct{
  authLevel SecurityLevel
  testLevel SecurityLevel
  want      bool
}{{
   authLevel: PrivacyAndIntegrity,
   testLevel: PrivacyAndIntegrity,
   want: true
 }, {
   authLevel: IntegrityOnly,
   testLevel: PrivacyAndIntegrity,
   want: false
 }
....
}

for _, tc := range testCases {
  // make testAuthInfo, call CheckSecurityLevel, check result
}

This doesn't have to be exhaustive, but should check a few different cases.

credentials/tls.go Show resolved Hide resolved
credentials/credentials.go Show resolved Hide resolved
credentials/credentials.go Outdated Show resolved Hide resolved
}

// CommonInfo returns the pointer to CommonAuthInfo struct.
func (c *CommonAuthInfo) CommonInfo() *CommonAuthInfo {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake: s/CommonInfo/CommonAuthInfo/g please. CommonInfo() doesn't seem sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our offline discussion, the name is changed to commonAuthInfo()

credentials/tls.go Show resolved Hide resolved
@dfawley dfawley assigned yihuazhang and unassigned dfawley Dec 19, 2019
@yihuazhang
Copy link
Contributor Author

Thanks Doug for the comments and I am currently working on addressing them. I will let you know once it's ready.

@yihuazhang yihuazhang force-pushed the security_level_negotiation branch from 59d169c to 3a03528 Compare December 20, 2019 01:46
@yihuazhang
Copy link
Contributor Author

All comments are addressed, PTAL. @dfawley

credentials/tls.go Show resolved Hide resolved
credentials/tls.go Show resolved Hide resolved
}

// CommonAuthInfo returns the pointer to CommonAuthInfo struct.
func (c *CommonAuthInfo) commonAuthInfo() *CommonAuthInfo {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, bad suggestion on my part. I think it will make sense to allow users to access this in an AuthInfo, if present. GetCommonAuthInfo should be fine. It seems the accessor-style name is unavoidable here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries. Renaming is done in the next commit.

case PrivacyAndIntegrity:
return "PrivacyAndIntegrity"
}
return ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fmt.Sprintf("invalid SecurityLevel: %v", int(s))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the next commit.

credentials/credentials.go Show resolved Hide resolved
@@ -50,6 +51,49 @@ type PerRPCCredentials interface {
RequireTransportSecurity() bool
}

// SecurityLevel defines the protection level on an established channel.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throughout: please don't use "channel". gRPC-Go does not use that term. You can use "connection" for an individual connection or "ClientConn" for a grpc.ClientConn connected to potentially many backends.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the next commit.

@yihuazhang yihuazhang changed the title Enforce security level negotiation between call credentials and channel Enforce security level negotiation between call credentials and connection Dec 20, 2019
@yihuazhang
Copy link
Contributor Author

All comments are addressed (again :) PTAL.

@dfawley
Copy link
Member

dfawley commented Dec 20, 2019

FYI: travis is red:

+misspell -error .
credentials/credentials.go:189:114: "compatability" is a misspelling of "compatibility"

Copy link
Member

@dfawley dfawley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more small things, then I think this should be good to go.

@@ -127,6 +169,8 @@ type Bundle interface {
type RequestInfo struct {
// The method passed to Invoke or NewStream for this RPC. (For proto methods, this has the format "/some.Service/Method")
Method string
// AuthInfo contains the information resulted from a security handshake (TransportCredentials.ClientHandshake, TransportCredentials.ServerHandshake)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/resulted//.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -42,6 +42,9 @@ func (ts TokenSource) GetRequestMetadata(ctx context.Context, uri ...string) (ma
if err != nil {
return nil, err
}
if !credentials.CheckSecurityLevel(ctx, credentials.PrivacyAndIntegrity) {
return nil, fmt.Errorf("connection is not secure enough to transfer TokenSource PerRPCCredentials which requires PrivacyAndIntegrity")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm.. ideally this would show more information, namely the SecurityLevel of the connection.

CheckSecurityLevel could return an error instead of bool to facilitate this.

Maybe something like fmt.Errorf("requires SecurityLevel %v; connection has %v", requiredLevel, hasLevel)? And this can wrap: fmt.Errorf("unable to transfer TokenSource PerRPCCredentials: %v", err)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. CheckSecurityLevel now returns error instead of bool, and more complete error messages are printed in oauth.go.

credentials/credentials.go Show resolved Hide resolved
}
ri, _ := RequestInfoFromContext(ctx)
if ri.AuthInfo == nil {
return errors.New("RequestInfo does not contain AuthInfo struct")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only real way to get here is if ctx does not contain a RequestInfo (i.e. it was called with the wrong context). I would say "unable to obtain SecurityLevel from context".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@yihuazhang yihuazhang force-pushed the security_level_negotiation branch from bef9360 to bf6cafe Compare December 21, 2019 00:02
@yihuazhang
Copy link
Contributor Author

Thanks much @dfawley for detailed reviews!

@yihuazhang
Copy link
Contributor Author

@dfawley Do you have any other comments on the PR?

Copy link
Member

@dfawley dfawley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add "experimental" comments on SecurityLevel and CommonAuthInfo, otherwise LGTM.

@dfawley dfawley changed the title Enforce security level negotiation between call credentials and connection credentials: create API for transport security level information Jan 7, 2020
@dfawley dfawley merged commit 4346c59 into grpc:master Jan 7, 2020
@dfawley dfawley added this to the 1.27 Release milestone Jan 7, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Type: Feature New features or improvements in behavior
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants