-
Notifications
You must be signed in to change notification settings - Fork 40k
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
Add SNI support to the apiserver #35109
Conversation
@k8s-bot gci gke e2e test this |
needs tests that actually start a TLS server and check that a default-cert+sni-certs config actually serves the sni cert to one of the sni hostnames, and falls back to the default cert for all other hostnames |
return nil, err | ||
} | ||
if len(nkc.Names) > 0 { | ||
for _, name := range nkc.Names { |
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.
explicitly specified names should trump auto-populated names
for example, if I have the following certs:
cert1: CN=bar.com
cert2: CN=foo.com, dnsSAN=bar.com
and I specify these flags:
--tls-sni-cert-key=cert1.pem,key1.pem,bar.com --tls-sni-cert-key=cert2.pem,key2.pem
I should end up with
"bar.com": cert1
"foo.com": cert2
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 we should give auto-generation precedence, but enforce that the first cert given trumps any later one. That matches your example. WDYT?
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.
explicitly given names should always outweigh auto-detected ones
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.
done
"domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+ | ||
"segments. If no domain patterns are provided, the common names of the certificate are "+ | ||
"used instead. For multiple key/certificate pairs, use the --tls-sni-key-cert multiple times. "+ | ||
"Examples: \"example.key,example.crt\" or \"*.foo.com,foo.com:foo.key,foo.crt\".") |
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.
maybe put the variable args at the end, and the cert first (all the go stdlib functions do cert,key)? (e.g. cert,key[,name,...]
)
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.
maybe with a colon?
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, cert:key[:name,...]
?
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.
key,cert:name,name,name
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.
put cert first for consistency, unsure about the delimiters
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 fine with either order. Where do we have cert,key? I my head the key exists first, then the cert ;)
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.
server.ListenAndServeTLS(cert,key)
tls.X509KeyPair(certbytes,keybytes)
tls.LoadX509KeyPair(cert,key)
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 not user facing. But cert/key is fine as well.
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.
changed it to: cert,key:name,name,name
return "namedKeyCert" | ||
} | ||
|
||
func (a *NamedKeyCertArray) String() 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.
this is for display only, not for re-parsing, 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.
It's only used for the default output right now: []
.
@@ -200,7 +206,8 @@ func (s *GenericAPIServer) Run() { | |||
// Can't use SSLv3 because of POODLE and BEAST | |||
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher | |||
// Can't use TLSv1.1 because of RC4 cipher usage | |||
MinVersion: tls.VersionTLS12, | |||
MinVersion: tls.VersionTLS12, | |||
NameToCertificate: namedCerts, |
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.
does this need to include the names from the default cert as well?
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.
when I did this in OpenShift, I had to use a custom GetCertificate
function instead of NameToCertificate
:
// Set SNI certificate func
// Do not use NameToCertificate, since that requires certificates be included in the server's tlsConfig.Certificates list,
// which we do not control when running with http.Server#ListenAndServeTLS
GetCertificate: cmdutil.GetCertificateFunc(extraCerts),
can you verify this is no longer the case?
edit: looks like this may have been fixed in go1.5
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 default GetCertificate 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.
there was a default GetCertificate in go1.4 as well, which behaved differently, and required jumping through hoops to get the right cert selected. The current implementation looks like it does what we want, but I do want tests to make sure the combination of ListenAndServeTLS(defaultCert,defaultKey) and the NameToCertificate map we're passing in selects the right cert.
I don't see a clean way to shut down a server started with ListenAndServeTLS |
I'm ok adding a channel, but I doubt it will bring everything down cleanly. We had a lot of trouble with that in the distant past. |
@deads2k former go versions (<= 1.3) had races when closing the |
Jenkins Kubemark GCE e2e failed for commit ce9830c9e1292ba79eb4a01ab0bd1fb46d2665cc. Full PR test history. The magic incantation to run this job again is |
Jenkins GCI GCE e2e failed for commit ce9830c9e1292ba79eb4a01ab0bd1fb46d2665cc. Full PR test history. The magic incantation to run this job again is |
LGTM, squash in "address comments" commit |
@@ -105,6 +106,6 @@ func Run(serverOptions *genericoptions.ServerRunOptions) error { | |||
if err := s.InstallAPIGroup(&apiGroupInfo); err != nil { | |||
return fmt.Errorf("Error in installing API: %v", err) | |||
} | |||
s.PrepareRun().Run() | |||
s.PrepareRun().Run(wait.NeverStop) |
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 one we want to stop.
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.
added a close channel everywhere where the example apiserver is used.
InsecureServingInfo *ServingInfo | ||
|
||
// numerical ports, set after listening | ||
effectiveSecurePort, effectiveInsecurePort int |
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 reading through, this looks weird. I'm assuming I'll see why later, but a better comment may help.
go func() { | ||
defer utilruntime.HandleCrash() | ||
|
||
for { |
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.
Why are we still trying multiple times? Didn't we decide we preferred crashlooping? I'm ok as a followup.
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.
Only after the first synchronous try which worked. That was my understanding from our small irc discussion. A follow-up is fine. I prefer a hard crash in such a case such that the outer orchestrator (e.g. systemd or kubelet) can do its job.
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 think of a good reason to retry such a fundamental failure internally
// Many clients fail TLS connections if they see two different certs with the same serial number | ||
// from the same signer (and they have a local memory of previously seen certs from a given signer). | ||
// Hence, we put a timestamp here. | ||
SerialNumber: new(big.Int).SetInt64(time.Now().Unix()), |
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 rather see UID. @liggitt did you prefer timestamp?
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, and if it switches to uid, it needs to be positive.
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.
yeah, random would probably be better
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 fact this was an accident that this function was changed at all. It's unrelated to SNI. Will undo the change.
minor comments, lgtm otherwise. |
Jenkins unit/integration failed for commit 2ceca5d7d42bace0766e181b72d8984d603bd54b. Full PR test history. The magic incantation to run this job again is |
Squashed. |
@k8s-bot test this [submit-queue is verifying that this PR is safe to merge] |
Automatic merge from submit-queue |
Automatic merge from submit-queue Restore old apiserver cert CN This patch got lost during rebase of #35109: - set `host@<unix-timestamp>` as CN in self-signed apiserver certs - skip non-domain CN in getNamedCertificateMap
This PR adds the
--tls-sni-key-cert
flag to the apiserver. It can be passed multiple times in the following ways:The first variant explicitly sets the accepted domain names, the second variant reads the common names and DNS names from the certificate itself.
If no domain name matches, the existing certificate (
--tls-cert-file
) is used.This change is