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

Restrict cert auth by CIDR #4478

Merged
merged 13 commits into from
May 9, 2018
Merged

Restrict cert auth by CIDR #4478

merged 13 commits into from
May 9, 2018

Conversation

tyrannosaurus-becks
Copy link
Contributor

Related to #815, add ability to restrict cert auth by CIDR.

var boundCIDRList []string
if boundCIDRs, ok := boundCIDRListRaw.([]string); ok {
boundCIDRList = boundCIDRs
} else if boundCIDRListStr, ok := boundCIDRListRaw.(string); ok {
Copy link
Member

Choose a reason for hiding this comment

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

You shouldn't need to do this -- TypeCommaStringSlice will already split this for you.

Copy link
Member

@jefferai jefferai left a comment

Choose a reason for hiding this comment

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

I think this could go a step further, where not only do you restrict which CIDRs are allowed to auth, but you also have the ability to set which CIDRs are allowed to use resultant tokens. This would entail putting a settable field in logical.Auth and connecting the logic from the backend setting the field to the logic in handleLoginRequest to actually use that value on the token. But it would make this even better!

@tyrannosaurus-becks
Copy link
Contributor Author

@jefferai ah, thanks for the tip! Will do.

@jefferai jefferai added this to the 0.10.2 milestone Apr 30, 2018
Copy link
Contributor

@chrishoffman chrishoffman left a comment

Choose a reason for hiding this comment

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

Looking good! A few minor comments on parameter handling.

@@ -371,6 +382,33 @@ func (b *backend) checkForValidChain(chains [][]*x509.Certificate) bool {
return false
}

func (b *backend) checkCIDR(cert *CertEntry, req *logical.Request) error {

if len(cert.BoundCIDRs) <= 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Len is never going to be below zero. I think == makes more sense 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.

That's fine! A previous dev team I was on preferred it that way because it was "more defensive". Will update.

@@ -228,6 +234,23 @@ func (b *backend) pathCertWrite(ctx context.Context, req *logical.Request, d *fr
}
}

var parsedCIDRs []*sockaddr.SockAddrMarshaler
if boundCIDRListRaw, ok := d.GetOk("bound_cidrs"); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

This pattern is usually only required for paths that handle both create and update operations (have an existence check). I think you could simplify this by just iterating of the the list without checking if the parameter is set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

if boundCIDRListRaw, ok := d.GetOk("bound_cidrs"); ok {

var boundCIDRList []string
if boundCIDRs, ok := boundCIDRListRaw.([]string); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a zero value for TypeCommaStringSlice of nil slice and this cast checking is not required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🌮

@@ -69,6 +71,9 @@ type Auth struct {
// mappings groups for the group aliases in identity store. For all the
// matching groups, the entity ID of the user will be added.
GroupAliases []*Alias `json:"group_aliases" mapstructure:"group_aliases" structs:"group_aliases"`

// The set of CIDRs that this token can be used with
BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"`
Copy link
Contributor

Choose a reason for hiding this comment

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

This will also need to be added to logical/plugin/pb/backend.proto and subsequently to logical/plugin/translation.go.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for point that out! I will tackle this after #4501 is merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

Copy link
Member

Choose a reason for hiding this comment

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

Please don't forget about this :-)

Copy link
Member

Choose a reason for hiding this comment

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

Oh nm, you already did it.

}
for _, cidr := range cert.BoundCIDRs {
if cidr.Contains(remoteSockAddr) {
valid = true
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than setting the bool, you could just return nil 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.

✔️

for _, v := range d.Get("bound_cidrs").([]string) {
parsedCIDR, err := sockaddr.NewSockAddr(v)
if err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

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

Can we make this a logical.ErrorResponse to indicate the user that this is an input error as opposed to a 500?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

@@ -71,6 +72,10 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
return nil, nil
}

if err := b.checkCIDR(matched.Entry, req); err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

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

Can we make the error a logical.ErrorResponse to indicate the cause of the problem as opposed to an internal error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In that case, it will already be one because that's what the checkCIDR method will return if the check is unsuccessful.

@@ -152,6 +158,10 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, nil
}

if err := b.checkCIDR(cert, req); err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

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

Same 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.

That also will already be that error.

@@ -371,6 +382,33 @@ func (b *backend) checkForValidChain(chains [][]*x509.Certificate) bool {
return false
}

func (b *backend) checkCIDR(cert *CertEntry, req *logical.Request) error {
Copy link
Member

Choose a reason for hiding this comment

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

Considering that this function can be potentially used by all the backends that introduces bound_cidr option, we might want to pull this out as a helper, possibly in helper/cidrutil package? It would then also make sense to accept ([]*sockaddr.SockAddrMarshaler, *logical.Connection) as parameters instead of (*CertEntry, *logical.Request).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I can do that. I was thinking I'd do it in the next iteration because it's not yet reused in this PR, but I can just do it now too.

@v6
Copy link
Contributor

v6 commented May 3, 2018

// , Looking forward to this

if b.Logger().IsDebug() {
b.Logger().Debug(fmt.Sprintf("unable to parse %s as a cidr: %s", v, err))
}
return nil, logical.ErrPermissionDenied
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than a permission denied error here we should return an ErrInvalidRequest, probably with an error response with the parse error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

briankassouf
briankassouf previously approved these changes May 9, 2018
Copy link
Contributor

@briankassouf briankassouf left a comment

Choose a reason for hiding this comment

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

Just one additional comment, otherwise 🚢

vishalnayak
vishalnayak previously approved these changes May 9, 2018
if b.Logger().IsDebug() {
b.Logger().Debug(fmt.Sprintf("unable to parse %s as a cidr: %s", v, err))
}
return nil, logical.ErrInvalidRequest
Copy link
Contributor

Choose a reason for hiding this comment

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

One more thing, we should return a helpful error message to the user. So we should do something like:

return logical.ErrorResponse(fmt.Sprintf("unable to parse %s as a cidr", v)), logical.ErrInvalidRequest

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

@@ -228,6 +234,18 @@ func (b *backend) pathCertWrite(ctx context.Context, req *logical.Request, d *fr
}
}

var parsedCIDRs []*sockaddr.SockAddrMarshaler
for _, v := range d.Get("bound_cidrs").([]string) {
Copy link
Member

Choose a reason for hiding this comment

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

Using parseutil.ParseAddrs would be preferable here.

@@ -544,6 +551,15 @@ func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) {
return nil, err
}

var boundCIDRs []*sockaddr.SockAddrMarshaler
for _, cidr := range a.BoundCidrs {
Copy link
Member

Choose a reason for hiding this comment

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

Can use the parseutil function here too.

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 Jeff! Looks like these comments were after I merged. I'll go ahead and update that stuff, and PR it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants