-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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 service UID as x509 extension to service server certs #12413
Add service UID as x509 extension to service server certs #12413
Conversation
@jcantrill the OID of the certificate extension is |
[test]
|
@smarterclayton how are we to consume this OID? Is it your intention we simply replace the existing one with the one here and it will be added as a SAN? Will it be some other attribute of the certificate for which we will need to modify the existing code which reads it? |
You should use this OID to lookup the extension value that matches it, and
compare the remote certificate's value with the value on your local
certificate. If you want to generate your config at startup, you can use a
script to extract this value from your local cert and then check the
hardcoded value. I.e.
!/bin/sh
value=$( extract_oid_value('1.3.6....') )
echo "cert.oid=1.3.6.9.2312..." >> ./searchguard.ini
echo "cert.value=$(value)" >> ./searchguard.ini
…On Mon, Jan 9, 2017 at 8:08 AM, Jeff Cantrill ***@***.***> wrote:
@smarterclayton <https://github.com/smarterclayton> how are we to consume
this OID? Is it your intention we simply replace the existing one with the
one here and it will be added as a SAN? Will it be some other attribute of
the certificate for which we will need to modify the existing code which
reads it?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#12413 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABG_p8sWRVW2EYuR_wczcDhHBjxZ3Upxks5rQjFFgaJpZM4LdU81>
.
|
@jcantrill You'll need to ensure that |
What rich said, and yes, looks pretty close. |
@richm and @jcantrill asked me to review your proposal. A custom X509v3 extension with a registered OID is much more sane than a RID general name in SAN. A registered ID in the SAN extension is rather uncommon and even revealed a bug in Python, https://bugs.python.org/issue27691. Your proposal could even be in line with the CA/B forum's baseline requirements, https://cabforum.org/baseline-requirements-documents/ . Section 7.1.2.3 "Subscriber Certificate" does neither forbid nor explicitly allow custom X509v3 extensions. On the other hand section 7.1.2.4 "All Certificates" states that
If you make a CA aware of the reason for the presence of your custom X509v3 extension, then you might be able to get a publicly trusted EE cert from e.g. Lets Encrypt. I'm not entirely sure, though. In case publicly trusted EE certs are a use case for OpenShift, more investigation is required. |
Good question. OpenShift in general, yes - I believe someone is already looking into Let's Encrypt integration. However, for this very particular and specific case - these certificates are only used for intra-cluster Elasticsearch-to-Elasticsearch communication - they are not used by external clients at all, and are not even used by intra-cluster services such as OpenShift itself, or fluentd/kibana/etc. So what exactly do we need to worry about in this case? |
Thanks.
In this case the CA is openshift - the cert is being issued by the
platform. We have CSR APIs that allows an external CA to make the
decision, but someone who doesn't want this field would decide not to
approve it and use an alternate cert mechanism. This is an internal CA
that leverages the trust relationship between the centralized
infrastructure manager and the centralized signer to provide automated and
bounded PKI.
For reference, this usage is inline with Puppets use of CSR for authorizing
nodes and they also act as a signing authority or allow delegating.
https://docs.puppet.com/puppet/latest/ssl_attributes_extensions.html
|
// service UID as an x509 v3 extension to the server certificate. | ||
func ServiceServerCertificateExtension(svc *kapi.Service) crypto.CertificateExtensionFunc { | ||
return func(cert *x509.Certificate) error { | ||
uid, err := asn1.Marshal(svc.UID) |
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 showing up oddly when I dump the resulting cert:
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Alternative Name:
DNS:foo.default.svc, DNS:foo.default.svc.cluster.local
1.3.6.1.4.1.2312.17.100.2.1:
.$e8162fce-d88e-11e6-b7df-acbc32c1ca87
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.
nm, openssl is just odd
Yeah, it's encoded correctly as far as i can tell, but will dig in further
to verify.
…On Thu, Jan 12, 2017 at 1:25 AM, Jordan Liggitt ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In pkg/cmd/server/crypto/extensions/serversigning.go
<#12413 (review)>
:
> + // OpenShiftServerSigningServiceOID describes the IANA arc for extensions to server certificates generated by the
+ // OpenShift service signing mechanism. All elements in this arc should only be used when signing server certificates
+ // for use under a service.
+ OpenShiftServerSigningServiceOID = oid(OpenShiftServerSigningOID, 2)
+ // OpenShiftServerSigningServiceUIDOID is an x509 extension that is applied to server certificates generated for services
+ // representing the UID of the service this certificate was generated for. This value is not guaranteed to match the
+ // current service UID if the certificates are in the process of being rotated out. The value MUST be an asn.1 encoded
+ // UTF8 string.
+ OpenShiftServerSigningServiceUIDOID = oid(OpenShiftServerSigningServiceOID, 1)
+)
+
+// ServiceServerCertificateExtension returns a CertificateExtensionFunc that will add the
+// service UID as an x509 v3 extension to the server certificate.
+func ServiceServerCertificateExtension(svc *kapi.Service) crypto.CertificateExtensionFunc {
+ return func(cert *x509.Certificate) error {
+ uid, err := asn1.Marshal(svc.UID)
this is showing up oddly when I dump the resulting cert:
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Alternative Name:
DNS:foo.default.svc, DNS:foo.default.svc.cluster.local
1.3.6.1.4.1.2312.17.100.2.1:
.$e8162fce-d88e-11e6-b7df-acbc32c1ca87
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#12413 (review)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABG_pxKHQAEe9RwO0ndKUqabyPYhvNAuks5rRcd2gaJpZM4LdU81>
.
|
yeah, dumping the asn.1 tree, it looks right:
openssl just renders it weirdly |
// OpenShiftServerSigningServiceUIDOID is an x509 extension that is applied to server certificates generated for services | ||
// representing the UID of the service this certificate was generated for. This value is not guaranteed to match the | ||
// current service UID if the certificates are in the process of being rotated out. The value MUST be an asn.1 encoded | ||
// UTF8 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.
go outputs the string as a PrintableString
(not a UTF8
string) if the characters are in a limited range
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.
Hrm, I thought I updated this comment. Not sure where it went.
sets.NewString(dnsName, fqDNSName), | ||
certificateLifetime, | ||
extensions.ServiceServerCertificateExtension(&kapi.Service{ | ||
ObjectMeta: kapi.ObjectMeta{UID: types.UID(secret.Annotations[ServiceUIDAnnotation])}, |
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.
hrmm, I really don't want to take the annotation's word for 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.
Aren't we taking the annotation's word for it on the secret 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.
On the service 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.
yeah, which doesn't seem great. I expected at least verification of the data on the service object itself
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.
@deads2k can you say why you didn't re-look up the service and verify the both way here? Is it happening earlier?
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.
@deads2k can you say why you didn't re-look up the service and verify the both way here? Is it happening earlier?
Previously, the name was signed for, but the UID was not signed for. The worst someone could do is make a set of non-sensical metadata that made the controller do nothing. Now that you're signing this information, you need a stronger guarantee.
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.
To state it a different way. By signing for this information, you're asserting that the information is true, you're asserting that no one can trick the signer into signing arbitrary data, and you need to be sure that if this cert is stolen the information it asserts cannot be used by someone else without significant control of the rest of system (say owning DNS and IP routing).
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, this is fixed 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.
Actually, all this worked before. The regenerateNeeded was already checking uid equality against the annotation. I changed the code to return the service that was already checked, and so this protected already.
OpenShiftServerSigningServiceOID = oid(OpenShiftServerSigningOID, 2) | ||
// OpenShiftServerSigningServiceUIDOID is an x509 extension that is applied to server certificates generated for services | ||
// representing the UID of the service this certificate was generated for. This value is not guaranteed to match the | ||
// current service UID if the certificates are in the process of being rotated out. The value MUST be an asn.1 encoded |
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.
what does this mean? cert rotation because a service was deleted/recreated? or rotation for some other reason?
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.
Was trying to imply that you are not guaranteed to have it be equal immediately. And right now, the SSCC doesn't regenerate certs in all cases (deletion of the secret) so I wanted to add weasel words.
// if the extension attempt failed. | ||
type CertificateExtensionFunc func(*x509.Certificate) error | ||
|
||
func (ca *CA) MakeServerCert(hostnames sets.String, expireDays int, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, 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.
"bring my by big hammer". This seems excessively broad.
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 generic code, and cert extension really has nothing to do with the common spot. Also this is the normal pattern for extension in Go now when you're doing object customization.
62084c9
to
4a5e6de
Compare
if err != nil || !exists { | ||
return nil | ||
} | ||
service := serviceObj.(*kapi.Service) |
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.
check the service references this secret as its serving cert secret?
} | ||
serviceObj, exists, err := sc.serviceCache.GetByKey(secret.Namespace + "/" + secret.Annotations[ServiceNameAnnotation]) | ||
if err != nil || !exists { | ||
return 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.
what happens in this case? retry later?
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.
At the resync interval or on the next change to the secret.
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.
Crap... I just realized all this logic was already in regenerate. Simplifying this again.
744e3b0
to
1c48f88
Compare
Any more comments? |
} | ||
|
||
service := serviceObj.(*kapi.Service) | ||
if secret.Annotations[ServiceUIDAnnotation] != string(service.UID) { | ||
return false | ||
return false, 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.
double check the back-reference (that the service still has an annotation asking for a cert to be put in that secret)
check the back-reference, then LGTM |
1c48f88
to
778391d
Compare
ElasticSearch SearchGuard has a requirement that an attribute of the certificate be used to identify intracluster traffic. That value is currently a registeredID as a SubjectAlternativeName, which is questionable. This PR includes the service UID as an x509 extension (using the new OpenShift OID arc we have been allocated) in order to enable a caller to have an unforgeable value on the certificate they can consult to identifier other cluster members. This also adds more traceability to the certificate for future use.
Back-ref-i-checked [merge] |
Evaluated for origin test up to 778391d |
Evaluated for origin merge up to 778391d |
continuous-integration/openshift-jenkins/test SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pr_origin/12925/) (Base Commit: a3e2498) |
continuous-integration/openshift-jenkins/merge SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pr_origin/12932/) (Base Commit: 0b38fe0) (Image: devenv-rhel7_5695) |
ElasticSearch SearchGuard has a requirement that an attribute of the
certificate be used to identify intracluster traffic. That value is
currently a registeredID as a SubjectAlternativeName, which is
questionable. This PR includes the service UID as an x509 extension
(using the new OpenShift OID arc we have been allocated) in order to
enable a caller to have an unforgeable value on the certificate they can
consult to identifier other cluster members. This also adds more
traceability to the certificate for future use.
[test]