-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
Adds correct support for TLS Muthual autentication #235
Conversation
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 pr! I haven't looked at the code yet, just making sure I understand the feature so I left some comments on the example.
|
||
## Prerequisites | ||
|
||
You need a valid CA File, composed of a group of valid enabled CAs. This MUST be in PEM Format. |
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 add somewhere a doc of how to set this up using openssl, maybe in the prerequisites section, and link to it from here? https://github.com/kubernetes/ingress/blob/master/examples/PREREQUISITES.md
## Generating a CA
Some examples might require an explicty CA. You can act as your own CA by running...
## Generating Client certs
Some examples require client certs. You can generate your own, and sign them using the CA generated above, as follows...
## Prerequisites | ||
|
||
You need a valid CA File, composed of a group of valid enabled CAs. This MUST be in PEM Format. | ||
Also the Ingress must terminate TLS, otherwise this makes no sense ;) |
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 you clarify why only tls termination, can't the ingress re-encrypt 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.
Used the wrong words. You must have a TLS/HTTPs ingress, but no termination is really needed.
|
||
You need a valid CA File, composed of a group of valid enabled CAs. This MUST be in PEM Format. | ||
Also the Ingress must terminate TLS, otherwise this makes no sense ;) | ||
|
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 add a Terminology section and define (something like, feel free to make corrections or use your own text and add links where necessary):
- CA cert: Certificate Authority public key. Client certs must chain back to this cert, meaning the Issuer field of some certificate in the chain leading up to the client cert must contain the name of this CA. For purposes of this example, this is a self signed certificate.
- Client certs: Certificate used by clients to authenticate themselves with the loadbalancer/backends.
- CA: Certificate authority signing the client cert, in this example we will play the role of a CA. You can generate a CA cert as show in this doc.
- CA chains: A chain of certificates where the parent has a Subject field matching the Issuer field of the child, except for the root, which has Issuer == Subect.
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.
Your terminology is fine! No changes. Will just write an adittional doc explaining how to generate you're very own CA (or point to CoreOS documentation!)
## Deployment | ||
|
||
The following command instructs the controller to enable the TLS Authentication using | ||
the secret containing the valid CA chains. |
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.
The following command instructs the controller to enalbe TLS authentication using the secret from the ingress.kubernetes.io/auth-tls-secret
annotation on the Ingress. Clients must present this cert to the loadbalancer, or they will receive a HTTP 400 response.
7s 7s 1 {nginx-ingress-controller } Warning MAPPING Ingress rule 'default/nginx-test' contains no path definition. Assuming / | ||
|
||
|
||
$ curl -k https://ingress.test.com |
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 you use the host header for this or do you need to use the full DNS name in the command line?
metadata: | ||
annotations: | ||
# Create this with kubectl create secret generic caingress --from-file=ca.crt --namespace=default | ||
ingress.kubernetes.io/auth-tls-secret: default/caingress |
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 add the ingress.class
annotation as described in the PREREQUISITES doc
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.
✅
@bprashanth Corrected as you suggested |
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.
@rikatz good change overall, just some stylistic nits and refactor suggestions
examples/PREREQUISITES.md
Outdated
You can act as your very own CA, or use an existing one. As an exercise / learning, we're going to generate our | ||
own CA, and also generate a client certificate. | ||
|
||
These instructions are based in CoreOS OpenSSL instructions: https://coreos.com/kubernetes/docs/latest/openssl.html |
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 embed this link like so [instructions](https://coreos)
examples/PREREQUISITES.md
Outdated
-----END CERTIFICATE----- | ||
``` | ||
|
||
You can have as many certificates as you wan't. If they're in the binary DER format, |
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.
s/wan't/want
@@ -0,0 +1,73 @@ | |||
# TLS termination |
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 title is wrong, and actually I think a better place to keep this example would be in a auth directory, so I'd imagine something like:
auth
oauth
password
client-certs
Wdyt?
We could also instead create a tls directory like
tls
termination
client-certs
reencrypt ...
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.
Hum, I think that inside the auth directory is better. Will change here as suggested.
|
||
* Client Cert: Certificate used by the clients to authenticate themselves with the loadbalancer/backends. | ||
|
||
* CA: Certificate authority signing the client cert, in this example we will play the role of a CA. |
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.
Suggest defining CA before CA Certificate
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.
✅
## Prerequisites | ||
|
||
You need a valid CA File, composed of a group of valid enabled CAs. This MUST be in PEM Format. | ||
The instructions are described here: https://github.com/kubernetes/ingress/blob/master/examples/PREREQUISITES.md#ca-authentication |
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 embed the link
core/pkg/net/ssl/ssl.go
Outdated
return nil, fmt.Errorf("could not close CA temp pem file %v: %v", tempCAPemFile.Name(), err) | ||
} | ||
|
||
pemCACerts, err := ioutil.ReadFile(tempCAPemFile.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.
why write it then read it then validate, can you validate the raw CA before even creating the file so we can exit early if it's invalid? i.e something like
// AddCA validates then writes the given CA to a pem file located in the DefaultSSLDirectory (where is this coming from?)
// and returns an SSLCert struct containing the location the CA was written to.
AddCA(name, ca []bytes) (SSLCert, error) {
if err := validate(ca) != nil {
return nil, err
}
// write CA to path
return ingress.SSLCerts{}, 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.
Have changed the function, so don't need to write into a temp and move, but also don't need to use a new function :)
ic.secretQueue.Enqueue(key) | ||
} | ||
// Enough time to enqueue the new certificate | ||
time.Sleep(5 * time.Second) |
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.
do we need this 5 second sleep or can we rely on the enqueue backing off with each failure? (I'd really prefer to not sleep, we can instead document that the secret must exist, and if it dosn't the controller will throw some events and possibly enter exponential backoff)
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.
@bprashanth Had some failure here because of this, but can't remember right now. So I'm removing this sleep to see what happens
glog.V(3).Infof("Found certificate and private key, configuring %v as a TLS Secret", secretName) | ||
s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca) | ||
} else if ca != nil { | ||
glog.V(3).Infof("Found only ca.crt, configuring %v as an AuthCert secret", secretName) |
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 clumsy but I guess you don't have any other way to do it?
IIUC you're assuming that if there's no key, it must be a ca.crt?
The log message is also a little confusing, please clarify what you mean by "configuring as an AuthCert"? (you don't seem to have defined what an AuthCert is anywhere, though I'm assuming you mean a client cert used for authentication. Suggest spelling that out.)
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.
Yeap, the message is a bit confusing. Changed here to 'Found only ca.crt, configuring %v as an Certificate Authentication secret'
Also, this is the followed workflow:
- If it contains a cert and a key, assume this is a keypair and configure it as the keypair from the Ingress (even if it contains a ca.crt also)
- It it contains only the ca.crt, configure it as a CA only file
- Otherwise, no valid secret, no file.
var s *ingress.SSLCert | ||
if okcert && okkey { | ||
glog.V(3).Infof("Found certificate and private key, configuring %v as a TLS Secret", secretName) | ||
s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca) |
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 it possible to have a single function replace AddOrUpdateCertAndKey/AddOrUpdateCertAuth, called AddTLSKeyPair(nsSecName, cert, key, ca), which if cert and key are non-empty, appends ca to cert like AddOrUpdateCertAndKey and if they're empty, writes ca to file directly like AddOrUpdateCertAuth ? (I haven't though too much about the refactor so feel free to tell me it's too hard, just seems confusing to have 2 similar functions)
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.
@bprashanth We can work on this. In the first moments I though it would be the best way. But looking at both functions, we would have to insert a lot of 'ifs' in it, to keep the same behaviour and only one function.
We can assume both ways, no restriction about that. Just followed the easiest one :)
# PEM sha: {{ $location.CertificateAuth.PemSHA }} | ||
ssl_client_certificate {{ $location.CertificateAuth.CAFileName }}; | ||
ssl_verify_client on; | ||
ssl_verify_depth 10; |
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 my own education, what is the average depth in the wild? do most people just have 1 CA that signs the 1 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.
2 is the most common depth for normal public-ca-minted certs, though 3 isn't uncommon (e.g. cross-signed certs, CAs that have second layers of intermediates).
Out of the alexa top 100 sites, 80% are depth=2, 9% depth=1, and 11% didn't feel like responding on 443.
However, for client cert auth, public CAs wouldn't generally be used. In that case, 2 and 1 are both quite common. 1 is common for organizations that have a root CA and directly issue client certs from that, while organizations that try to practice something akin to what public CAs do (e.g. keeping the root CA in offline storage and signing from intermediates on HSMs) will have a depth of 2.
It's worth noting that libnss (firefox?), iirc, has a max verify_depth of 8 before it gives up.
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.
@euank You're right, i've been pretty lazy here. I'll add another annotation instead of hardcoding this, so the user can choose what it's verify depth.
Just FYI, here we use ssl_verify_depth 3 as he have the structure root -> intermediate -> final -> client :)
@bprashanth Have to leave to a meeting now, will keep reviewing in the weekend |
@rikatz no hurry, please take your time. |
Jenkins error is valid, will take a look at this this weekend |
OK, corrected all the errors. Waiting for review to improve the code :) |
@rikatz please rebase |
…tion modified: controllers/nginx/configuration.md modified: controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl modified: core/pkg/ingress/annotations/authtls/main.go modified: core/pkg/ingress/controller/backend_ssl.go modified: core/pkg/ingress/controller/controller.go modified: core/pkg/ingress/controller/util_test.go modified: core/pkg/ingress/resolver/main.go modified: core/pkg/ingress/types.go modified: core/pkg/net/ssl/ssl.go modified: examples/PREREQUISITES.md new file: examples/auth/client-certs/nginx/README.md new file: examples/auth/client-certs/nginx/nginx-tls-auth.yaml
95cf6cd
to
a342c0b
Compare
@aledbf Did it as asked. Don't know if this is correct, but here it is :) |
/lgtm |
@rikatz thanks! |
This PR adds support for TLS muthual autenthication by making the following changes:
TODO (on future PR):
Hope this helps in this feature.