-
Notifications
You must be signed in to change notification settings - Fork 14
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
Validate user provided certificates #834
Validate user provided certificates #834
Conversation
f514e7f
to
3d28d7c
Compare
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.
LGTM, only minor nits and a question
src/k8s/pkg/utils/pki/validate.go
Outdated
} | ||
} | ||
for _, ip := range check.IPSANs { | ||
if err := cert.VerifyHostname("[" + ip + "]"); err != 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.
Is this IPv6-safe?
I guess ip
cannot be [<ipv6>]
so that we would have double brackets, right?
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.
Good point. The ip address wouldn't include a port, so no need for square brackets. I haven't checked how VerifyHostname
handles double brackets, we could just do something like strings.Trim(ip, "[]").
However, we aren't using this check right now so we might as well remove 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.
Great work! Thanks a lot @petrutlucian94. Left some comments mostly for discussion. Feel free to ignore them if they don't seem relevant.
} | ||
} | ||
|
||
now := time.Now() |
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 it's better to have functionalities like time, passed like this to make it more testable:
type CertValidator struct {
nowFn func() time.Time
...
}
NewCertValidator(time.Now, ...)
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.
IMO this seems unnecessary and would complicate the caller code. We already have tests that check the NotBefore
and NotAfter
fields without having to pass a time
function.
@@ -118,6 +118,11 @@ func (c *ControlPlanePKI) CompleteCertificates() error { | |||
} | |||
c.CACert = cert | |||
c.CAKey = key | |||
} else { | |||
certCheck := pkiutil.CertCheck{CN: "kubernetes-ca", AllowSelfSigned: 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.
I would personally go with a dependency injection and interface. Something like:
type CertValidator interface {
Validate(certPEM, certKey string, opts... certvalidator.Option) error
}
type ControlPlanePKI struct {
certValidator CertValidator
...
}
func NewControlPlanePKI(certValidator CertValidator, ...) *ControlPlanePKI {
...
return &ControlPlanePKI{
certValidator: certValidator,
...
}
}
func (c *ControlPlanePKI) CompleteCertificates() error {
...
if err := c.certValidator.Validate(certPEM, certKey, certvalidator.WithAllowSelfSigned(), certvalidator.WithCanonicalName(cn)) {
return fmt.Errorf("failed to validate certificate: %w")
}
...
}
}
and in utils/pki/certvalidator/validator.go
:
package certvalidator
type Validator struct {
nowFn func() time.Time
...
}
type Option func(v *Validator)
func WithCanonicalName(name string) Option {
return func(v *Validator) {
v.cn = name
}
}
func (v *Validator) Validate(certPEM, certKey string, options ...Option) error {
for _, o in range options {
o(v)
}
...
}
I think it's more testable this way but I don't have any strong objections with the current implementation.
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 the suggestion but again I think this would complicate the implementation unnecessarily. We have just one concrete implementation and we don't need to stub it for testing purposes, in which case I don't see the point of using an interface and dependency injection.
We'll validate user provided certificates, checking fields such as: * Common Name * Organization * NotBefore, NotAfter * DNS Subject Alternative Name Also, we ensure that the certificates are signed by the specified CAs.
3d28d7c
to
4dc023f
Compare
4dc023f
to
ee1d3b1
Compare
We'll validate user provided certificates, checking fields such as:
Also, we ensure that the certificates are signed by the specified CAs.