|
15 | 15 | package tlsutil
|
16 | 16 |
|
17 | 17 | import (
|
| 18 | + "crypto/rsa" |
| 19 | + "crypto/x509" |
| 20 | + "errors" |
| 21 | + "strings" |
| 22 | + "sync" |
| 23 | + |
18 | 24 | "k8s.io/api/core/v1"
|
| 25 | + "k8s.io/apimachinery/pkg/api/meta" |
| 26 | + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
19 | 27 | "k8s.io/apimachinery/pkg/runtime"
|
20 | 28 | )
|
21 | 29 |
|
@@ -111,3 +119,135 @@ type CertGenerator interface {
|
111 | 119 | // ca.key: ..
|
112 | 120 | GenerateCert(cr runtime.Object, service *v1.Service, config *CertConfig) (*v1.Secret, *v1.ConfigMap, *v1.Secret, error)
|
113 | 121 | }
|
| 122 | + |
| 123 | +const ( |
| 124 | + // TLSPrivateCAKeyKey is the key for the private CA key field. |
| 125 | + TLSPrivateCAKeyKey = "ca.key" |
| 126 | + // TLSCertKey is the key for tls CA certificates. |
| 127 | + TLSCACertKey = "ca.crt" |
| 128 | +) |
| 129 | + |
| 130 | +type SDKCertGenerator struct { |
| 131 | + // appKeyAndCertMap holds the application key and cert for a given cr + config.CertName. |
| 132 | + appKeyAndCertMap sync.Map |
| 133 | + // caKeyAndCertMap holds the CA key and cert for a given cr. |
| 134 | + caKeyAndCertMap sync.Map |
| 135 | +} |
| 136 | + |
| 137 | +type keyAndCert struct { |
| 138 | + key *rsa.PrivateKey |
| 139 | + cert *x509.Certificate |
| 140 | +} |
| 141 | + |
| 142 | +func (scg *SDKCertGenerator) GenerateCert(cr runtime.Object, service *v1.Service, config *CertConfig) (*v1.Secret, *v1.ConfigMap, *v1.Secret, error) { |
| 143 | + if err := verifyConfig(config); err != nil { |
| 144 | + return nil, nil, nil, err |
| 145 | + } |
| 146 | + |
| 147 | + n, k, ns, err := toKindNameNamespace(cr) |
| 148 | + if err != nil { |
| 149 | + return nil, nil, nil, err |
| 150 | + } |
| 151 | + |
| 152 | + asn := toAppSecretName(k, n, config.CertName) |
| 153 | + cascn := toCASecretAndConfigMapName(k, n) |
| 154 | + appkcv, hasAppKeyAndCert := scg.appKeyAndCertMap.Load(asn + "-" + ns) |
| 155 | + cakcv, hasCAKeyAndCert := scg.caKeyAndCertMap.Load(cascn + "-" + ns) |
| 156 | + // TODO: handle passed in CA |
| 157 | + if hasAppKeyAndCert && hasCAKeyAndCert { |
| 158 | + s := toTLSSecret(appkcv.(*keyAndCert), asn, ns) |
| 159 | + cacm, cas := toCASecretAndConfigmap(cakcv.(*keyAndCert), cascn, ns) |
| 160 | + return s, cacm, cas, nil |
| 161 | + } else if hasAppKeyAndCert && !hasCAKeyAndCert { |
| 162 | + // TODO |
| 163 | + } else if !hasAppKeyAndCert && hasCAKeyAndCert { |
| 164 | + // TODO |
| 165 | + } else { |
| 166 | + // TODO |
| 167 | + } |
| 168 | + return nil, nil, nil, nil |
| 169 | +} |
| 170 | + |
| 171 | +func verifyConfig(config *CertConfig) error { |
| 172 | + if config == nil { |
| 173 | + return errors.New("nil CertConfig not allowed") |
| 174 | + } |
| 175 | + if config.CertName == "" { |
| 176 | + return errors.New("empty CertConfig.CertName not allowed") |
| 177 | + } |
| 178 | + return nil |
| 179 | +} |
| 180 | + |
| 181 | +func toAppSecretName(kind, name, certName string) string { |
| 182 | + return strings.ToLower(kind) + "-" + name + "-" + certName |
| 183 | +} |
| 184 | + |
| 185 | +func toCASecretAndConfigMapName(kind, name string) string { |
| 186 | + return strings.ToLower(kind) + "-" + name + "-ca" |
| 187 | +} |
| 188 | + |
| 189 | +func toKindNameNamespace(cr runtime.Object) (string, string, string, error) { |
| 190 | + a := meta.NewAccessor() |
| 191 | + k, err := a.Kind(cr) |
| 192 | + if err != nil { |
| 193 | + return "", "", "", err |
| 194 | + } |
| 195 | + n, err := a.Name(cr) |
| 196 | + if err != nil { |
| 197 | + return "", "", "", err |
| 198 | + } |
| 199 | + ns, err := a.Namespace(cr) |
| 200 | + if err != nil { |
| 201 | + return "", "", "", err |
| 202 | + } |
| 203 | + return k, n, ns, nil |
| 204 | +} |
| 205 | + |
| 206 | +// toTLSSecret returns a client/server "kubernetes.io/tls" secret. |
| 207 | +// TODO: add owner ref. |
| 208 | +func toTLSSecret(kc *keyAndCert, name, namespace string) *v1.Secret { |
| 209 | + return &v1.Secret{ |
| 210 | + TypeMeta: metav1.TypeMeta{ |
| 211 | + Kind: "Secret", |
| 212 | + APIVersion: "v1", |
| 213 | + }, |
| 214 | + ObjectMeta: metav1.ObjectMeta{ |
| 215 | + Name: name, |
| 216 | + Namespace: namespace, |
| 217 | + }, |
| 218 | + Data: map[string][]byte{ |
| 219 | + v1.TLSPrivateKeyKey: encodePrivateKeyPEM(kc.key), |
| 220 | + v1.TLSCertKey: encodeCertificatePEM(kc.cert), |
| 221 | + }, |
| 222 | + Type: v1.SecretTypeTLS, |
| 223 | + } |
| 224 | +} |
| 225 | + |
| 226 | +// TODO: add owner ref. |
| 227 | +func toCASecretAndConfigmap(cakc *keyAndCert, name, namespace string) (*v1.ConfigMap, *v1.Secret) { |
| 228 | + return &v1.ConfigMap{ |
| 229 | + TypeMeta: metav1.TypeMeta{ |
| 230 | + Kind: "ConfigMap", |
| 231 | + APIVersion: "v1", |
| 232 | + }, |
| 233 | + ObjectMeta: metav1.ObjectMeta{ |
| 234 | + Name: name, |
| 235 | + Namespace: namespace, |
| 236 | + }, |
| 237 | + Data: map[string]string{ |
| 238 | + TLSCACertKey: string(encodeCertificatePEM(cakc.cert)), |
| 239 | + }, |
| 240 | + }, &v1.Secret{ |
| 241 | + TypeMeta: metav1.TypeMeta{ |
| 242 | + Kind: "Secret", |
| 243 | + APIVersion: "v1", |
| 244 | + }, |
| 245 | + ObjectMeta: metav1.ObjectMeta{ |
| 246 | + Name: name, |
| 247 | + Namespace: namespace, |
| 248 | + }, |
| 249 | + Data: map[string][]byte{ |
| 250 | + TLSPrivateCAKeyKey: encodePrivateKeyPEM(cakc.key), |
| 251 | + }, |
| 252 | + } |
| 253 | +} |
0 commit comments