-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Conversation
var boundCIDRList []string | ||
if boundCIDRs, ok := boundCIDRListRaw.([]string); ok { | ||
boundCIDRList = boundCIDRs | ||
} else if boundCIDRListStr, ok := boundCIDRListRaw.(string); ok { |
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.
You shouldn't need to do this -- TypeCommaStringSlice will already split this for you.
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 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!
@jefferai ah, thanks for the tip! Will do. |
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.
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 { |
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.
Len is never going to be below zero. I think ==
makes more sense here.
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.
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 { |
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.
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.
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.
👍
if boundCIDRListRaw, ok := d.GetOk("bound_cidrs"); ok { | ||
|
||
var boundCIDRList []string | ||
if boundCIDRs, ok := boundCIDRListRaw.([]string); ok { |
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.
There is a zero value for TypeCommaStringSlice of nil slice and this cast checking is not required.
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.
🌮
@@ -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"` |
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.
This will also need to be added to logical/plugin/pb/backend.proto
and subsequently to logical/plugin/translation.go
.
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.
Thanks for point that out! I will tackle this after #4501 is merged.
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.
✔️
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.
Please don't forget about this :-)
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.
Oh nm, you already did it.
} | ||
for _, cidr := range cert.BoundCIDRs { | ||
if cidr.Contains(remoteSockAddr) { | ||
valid = true |
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.
Rather than setting the bool, you could just return nil
here
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.
✔️
for _, v := range d.Get("bound_cidrs").([]string) { | ||
parsedCIDR, err := sockaddr.NewSockAddr(v) | ||
if err != nil { | ||
return nil, err |
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 we make this a logical.ErrorResponse
to indicate the user that this is an input error as opposed to a 500?
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.
✔️
@@ -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 |
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 we make the error a logical.ErrorResponse
to indicate the cause of the problem as opposed to an internal 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.
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 |
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.
Same here.
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.
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 { |
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.
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)
.
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.
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.
// , 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 |
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.
Rather than a permission denied error here we should return an ErrInvalidRequest
, probably with an error response with the parse 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.
✔️
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.
Just one additional comment, otherwise 🚢
if b.Logger().IsDebug() { | ||
b.Logger().Debug(fmt.Sprintf("unable to parse %s as a cidr: %s", v, err)) | ||
} | ||
return nil, logical.ErrInvalidRequest |
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.
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
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.
✔️
@@ -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) { |
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.
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 { |
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 use the parseutil function here too.
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.
Hi Jeff! Looks like these comments were after I merged. I'll go ahead and update that stuff, and PR it.
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.
Related to #815, add ability to restrict cert auth by CIDR.