ingress-nginx-helm/internal/ingress/controller/store/backend_ssl.go

217 lines
6 KiB
Go
Raw Normal View History

/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package store
import (
"fmt"
"strings"
"github.com/imdario/mergo"
2018-12-05 16:28:28 +00:00
"k8s.io/klog"
2016-11-16 18:24:26 +00:00
2017-09-17 18:42:31 +00:00
apiv1 "k8s.io/api/core/v1"
2017-09-30 23:42:42 +00:00
extensions "k8s.io/api/extensions/v1beta1"
2018-01-19 18:44:31 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/file"
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/k8s"
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/net/ssl"
)
2018-07-02 20:59:54 +00:00
// syncSecret synchronizes the content of a TLS Secret (certificate(s), secret
// key) with the filesystem. The resulting files can be used by NGINX.
func (s k8sStore) syncSecret(key string) {
s.mu.Lock()
defer s.mu.Unlock()
2018-12-05 16:27:55 +00:00
klog.V(3).Infof("Syncing Secret %q", key)
2017-04-09 16:52:10 +00:00
// TODO: getPemCertificate should not write to disk to avoid unnecessary overhead
cert, err := s.getPemCertificate(key)
2017-07-18 18:21:38 +00:00
if err != nil {
if !isErrSecretForAuth(err) {
2018-12-05 16:27:55 +00:00
klog.Warningf("Error obtaining X.509 certificate: %v", err)
}
2017-07-18 18:21:38 +00:00
return
}
2017-07-18 18:21:38 +00:00
// create certificates and add or update the item in the store
cur, err := s.GetLocalSSLCert(key)
if err == nil {
if cur.Equal(cert) {
2017-07-18 18:21:38 +00:00
// no need to update
return
}
2018-12-05 16:27:55 +00:00
klog.Infof("Updating Secret %q in the local store", key)
s.sslStore.Update(key, cert)
2017-09-13 14:14:24 +00:00
// this update must trigger an update
// (like an update event from a change in Ingress)
s.sendDummyEvent()
2017-07-18 18:21:38 +00:00
return
}
2017-07-18 18:21:38 +00:00
2018-12-05 16:27:55 +00:00
klog.Infof("Adding Secret %q to the local store", key)
s.sslStore.Add(key, cert)
2017-10-01 00:48:14 +00:00
// this update must trigger an update
2017-09-30 23:42:42 +00:00
// (like an update event from a change in Ingress)
s.sendDummyEvent()
}
// getPemCertificate receives a secret, and creates a ingress.SSLCert as return.
// It parses the secret and verifies if it's a keypair, or a 'ca.crt' secret only.
func (s k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
secret, err := s.listers.Secret.ByKey(secretName)
if err != nil {
2018-07-02 20:59:54 +00:00
return nil, err
}
2017-09-17 18:42:31 +00:00
cert, okcert := secret.Data[apiv1.TLSCertKey]
key, okkey := secret.Data[apiv1.TLSPrivateKeyKey]
ca := secret.Data["ca.crt"]
auth := secret.Data["auth"]
// namespace/secretName -> namespace-secretName
nsSecName := strings.Replace(secretName, "/", "-", -1)
var sslCert *ingress.SSLCert
if okcert && okkey {
if cert == nil {
2018-07-02 20:59:54 +00:00
return nil, fmt.Errorf("key 'tls.crt' missing from Secret %q", secretName)
}
if key == nil {
2018-07-02 20:59:54 +00:00
return nil, fmt.Errorf("key 'tls.key' missing from Secret %q", secretName)
2017-09-18 14:38:23 +00:00
}
if s.isDynamicCertificatesEnabled {
sslCert, err = ssl.CreateSSLCert(nsSecName, cert, key, ca)
if err != nil {
return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err)
}
} else {
// If 'ca.crt' is also present, it will allow this secret to be used in the
// 'nginx.ingress.kubernetes.io/auth-tls-secret' annotation
sslCert, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca, s.filesystem)
if err != nil {
return nil, fmt.Errorf("unexpected error creating pem file: %v", err)
}
2017-07-18 20:26:28 +00:00
}
2018-07-02 20:59:54 +00:00
msg := fmt.Sprintf("Configuring Secret %q for TLS encryption (CN: %v)", secretName, sslCert.CN)
if ca != nil {
2018-07-02 20:59:54 +00:00
msg += " and authentication"
}
2018-12-05 16:27:55 +00:00
klog.V(3).Info(msg)
2018-07-02 20:59:54 +00:00
} else if ca != nil {
sslCert, err = ssl.AddCertAuth(nsSecName, ca, s.filesystem)
2017-07-18 20:26:28 +00:00
if err != nil {
2018-07-02 20:59:54 +00:00
return nil, err
2017-07-18 20:26:28 +00:00
}
// makes this secret in 'syncSecret' to be used for Certificate Authentication
// this does not enable Certificate Authentication
2018-12-05 16:27:55 +00:00
klog.V(3).Infof("Configuring Secret %q for TLS authentication", secretName)
} else {
if auth != nil {
return nil, ErrSecretForAuth
}
return nil, fmt.Errorf("secret %q contains no keypair or CA certificate", secretName)
}
sslCert.Name = secret.Name
sslCert.Namespace = secret.Namespace
return sslCert, nil
}
func (s k8sStore) checkSSLChainIssues() {
for _, item := range s.ListLocalSSLCerts() {
2018-07-02 20:59:54 +00:00
secrKey := k8s.MetaNamespaceKey(item)
secret, err := s.GetLocalSSLCert(secrKey)
if err != nil {
continue
}
if secret.FullChainPemFileName != "" {
// chain already checked
continue
}
data, err := ssl.FullChainCert(secret.PemFileName, s.filesystem)
if err != nil {
2018-12-05 16:27:55 +00:00
klog.Errorf("Error generating CA certificate chain for Secret %q: %v", secrKey, err)
continue
}
fullChainPemFileName := fmt.Sprintf("%v/%v-%v-full-chain.pem", file.DefaultSSLDirectory, secret.Namespace, secret.Name)
file, err := s.filesystem.Create(fullChainPemFileName)
if err != nil {
2018-12-05 16:27:55 +00:00
klog.Errorf("Error creating SSL certificate file for Secret %q: %v", secrKey, err)
continue
}
_, err = file.Write(data)
if err != nil {
2018-12-05 16:27:55 +00:00
klog.Errorf("Error creating SSL certificate for Secret %q: %v", secrKey, err)
continue
}
dst := &ingress.SSLCert{}
err = mergo.MergeWithOverwrite(dst, secret)
if err != nil {
2018-12-05 16:27:55 +00:00
klog.Errorf("Error creating SSL certificate for Secret %q: %v", secrKey, err)
continue
}
dst.FullChainPemFileName = fullChainPemFileName
2018-12-05 16:27:55 +00:00
klog.Infof("Updating local copy of SSL certificate %q with missing intermediate CA certs", secrKey)
2018-07-02 20:59:54 +00:00
s.sslStore.Update(secrKey, dst)
// this update must trigger an update
// (like an update event from a change in Ingress)
s.sendDummyEvent()
}
}
2018-01-19 18:44:31 +00:00
// sendDummyEvent sends a dummy event to trigger an update
// This is used in when a secret change
func (s *k8sStore) sendDummyEvent() {
s.updateCh.In() <- Event{
2018-01-19 18:44:31 +00:00
Type: UpdateEvent,
Obj: &extensions.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "dummy",
Namespace: "dummy",
},
},
}
}
// ErrSecretForAuth error to indicate a secret is used for authentication
var ErrSecretForAuth = fmt.Errorf("secret is used for authentication")
func isErrSecretForAuth(e error) bool {
return e == ErrSecretForAuth
}