generated from knative-extensions/sample-controller
-
Notifications
You must be signed in to change notification settings - Fork 83
/
secret.go
192 lines (171 loc) · 6.47 KB
/
secret.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
Copyright 2019 The Knative 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 resources
import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"strings"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
"knative.dev/networking/pkg/apis/networking"
"knative.dev/networking/pkg/apis/networking/v1alpha1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/tracker"
)
// GetSecrets gets the all of the secrets referenced by the given Ingress, and
// returns a map whose key is the a secret namespace/name key and value is pointer of the secret.
func GetSecrets(ing *v1alpha1.Ingress, secretLister corev1listers.SecretLister) (map[string]*corev1.Secret, error) {
secrets := map[string]*corev1.Secret{}
for _, tls := range ing.Spec.TLS {
ref := secretKey(tls)
if _, ok := secrets[ref]; ok {
continue
}
secret, err := secretLister.Secrets(tls.SecretNamespace).Get(tls.SecretName)
if err != nil {
return nil, err
}
secrets[ref] = secret
}
return secrets, nil
}
// MakeSecrets makes copies of the origin Secrets under the namespace of Istio gateway service.
func MakeSecrets(ctx context.Context, originSecrets map[string]*corev1.Secret, accessor kmeta.OwnerRefableAccessor) ([]*corev1.Secret, error) {
nameNamespaces, err := GetIngressGatewaySvcNameNamespaces(ctx)
if err != nil {
return nil, err
}
secrets := []*corev1.Secret{}
for _, originSecret := range originSecrets {
for _, meta := range nameNamespaces {
if meta.Namespace == originSecret.Namespace {
// no need to copy secret when the target namespace is the same
// as the origin namespace
continue
}
secrets = append(secrets, makeSecret(originSecret, targetSecret(originSecret, accessor), meta.Namespace,
MakeTargetSecretLabels(originSecret.Name, originSecret.Namespace)))
}
}
return secrets, nil
}
// MakeWildcardSecrets copies wildcard certificates from origin namespace to the namespace of gateway servicess so they could
// consumed by Istio ingress.
func MakeWildcardSecrets(ctx context.Context, originWildcardCerts map[string]*corev1.Secret) ([]*corev1.Secret, error) {
nameNamespaces, err := GetIngressGatewaySvcNameNamespaces(ctx)
if err != nil {
return nil, err
}
secrets := []*corev1.Secret{}
for _, secret := range originWildcardCerts {
for _, meta := range nameNamespaces {
if meta.Namespace == secret.Namespace {
// no need to copy secret when the target namespace is the same
// as the origin namespace
continue
}
secrets = append(secrets, makeSecret(secret, targetWildcardSecretName(secret.Name, secret.Namespace), meta.Namespace, map[string]string{}))
}
}
return secrets, nil
}
func targetWildcardSecretName(originSecretName, originSecretNamespace string) string {
return originSecretNamespace + "--" + originSecretName + "-wildcard"
}
func makeSecret(originSecret *corev1.Secret, name, namespace string, labels map[string]string) *corev1.Secret {
labels[networking.CertificateUIDLabelKey] = originSecret.Labels[networking.CertificateUIDLabelKey] // propagate label for informer use
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: labels,
},
Data: originSecret.Data,
Type: originSecret.Type,
}
}
// MakeTargetSecretLabels returns the labels used in target secret.
func MakeTargetSecretLabels(originSecretName, originSecretNamespace string) map[string]string {
return map[string]string{
networking.OriginSecretNameLabelKey: originSecretName,
networking.OriginSecretNamespaceLabelKey: originSecretNamespace,
}
}
// targetSecret returns the name of the Secret that is copied from the origin Secret.
func targetSecret(originSecret *corev1.Secret, accessor kmeta.OwnerRefable) string {
return fmt.Sprintf("%s-%s", accessor.GetObjectMeta().GetName(), originSecret.UID)
}
// SecretRef returns the Reference of a secret given the namespace and name of the secret.
func SecretRef(namespace, name string) tracker.Reference {
gvk := corev1.SchemeGroupVersion.WithKind("Secret")
apiVersion, kind := gvk.ToAPIVersionAndKind()
return tracker.Reference{
APIVersion: apiVersion,
Kind: kind,
Namespace: namespace,
Name: name,
}
}
// Generates the k8s secret key with the given TLS.
func secretKey(tls v1alpha1.IngressTLS) string {
return fmt.Sprintf("%s/%s", tls.SecretNamespace, tls.SecretName)
}
// CategorizeSecrets categorizes secrets into two sets: wildcard cert secrets and non-wildcard cert secrets.
func CategorizeSecrets(secrets map[string]*corev1.Secret) (map[string]*corev1.Secret, map[string]*corev1.Secret, error) {
nonWildcardSecrets := map[string]*corev1.Secret{}
wildcardSecrets := map[string]*corev1.Secret{}
for k, secret := range secrets {
isWildcard, err := isWildcardSecret(secret)
if err != nil {
return nil, nil, err
}
if isWildcard {
wildcardSecrets[k] = secret
} else {
nonWildcardSecrets[k] = secret
}
}
return nonWildcardSecrets, wildcardSecrets, nil
}
func isWildcardSecret(secret *corev1.Secret) (bool, error) {
hosts, err := GetHostsFromCertSecret(secret)
if err != nil {
return false, err
}
return isWildcardHost(hosts[0])
}
func isWildcardHost(domain string) (bool, error) {
splits := strings.Split(domain, ".")
if len(splits) <= 1 {
return false, fmt.Errorf("incorrect domain format, got domain %s", domain)
}
return splits[0] == "*", nil
}
// GetHostsFromCertSecret gets cert hosts from cert secret.
func GetHostsFromCertSecret(secret *corev1.Secret) ([]string, error) {
block, _ := pem.Decode(secret.Data[corev1.TLSCertKey])
if block == nil {
return nil, fmt.Errorf("failed to decode PEM data for secret %s/%s", secret.Namespace, secret.Name)
}
certData, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate for secret %s/%s: %w", secret.Namespace, secret.Name, err)
}
if len(certData.DNSNames) == 0 {
return nil, fmt.Errorf("certificate should have DNS names, but it has %d", len(certData.DNSNames))
}
return certData.DNSNames, nil
}