Skip to content

Commit 3d12890

Browse files
committed
pkg/tlsutil: implement the case ca and key are in the cluster
1 parent 92a82a7 commit 3d12890

File tree

1 file changed

+215
-0
lines changed

1 file changed

+215
-0
lines changed

pkg/tlsutil/tls.go

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@
1515
package tlsutil
1616

1717
import (
18+
"crypto/rsa"
19+
"crypto/x509"
20+
"errors"
21+
"fmt"
22+
"strings"
23+
24+
"github.com/operator-framework/operator-sdk/pkg/sdk"
25+
1826
"k8s.io/api/core/v1"
27+
apiErrors "k8s.io/apimachinery/pkg/api/errors"
28+
"k8s.io/apimachinery/pkg/api/meta"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1930
"k8s.io/apimachinery/pkg/runtime"
2031
)
2132

@@ -111,3 +122,207 @@ type CertGenerator interface {
111122
// ca.key: ..
112123
GenerateCert(cr runtime.Object, service *v1.Service, config *CertConfig) (*v1.Secret, *v1.ConfigMap, *v1.Secret, error)
113124
}
125+
126+
const (
127+
// TLSPrivateCAKeyKey is the key for the private CA key field.
128+
TLSPrivateCAKeyKey = "ca.key"
129+
// TLSCertKey is the key for tls CA certificates.
130+
TLSCACertKey = "ca.crt"
131+
)
132+
133+
type SDKCertGenerator struct {
134+
}
135+
136+
type keyAndCert struct {
137+
key *rsa.PrivateKey
138+
cert *x509.Certificate
139+
}
140+
141+
func (scg *SDKCertGenerator) GenerateCert(cr runtime.Object, service *v1.Service, config *CertConfig) (*v1.Secret, *v1.ConfigMap, *v1.Secret, error) {
142+
if err := verifyConfig(config); err != nil {
143+
return nil, nil, nil, err
144+
}
145+
146+
k, n, ns, err := toKindNameNamespace(cr)
147+
if err != nil {
148+
return nil, nil, nil, err
149+
}
150+
appSecret, err := getAppSecretInCluster(toAppSecretName(k, n, config.CertName), ns)
151+
if err != nil {
152+
return nil, nil, nil, err
153+
}
154+
caSecret, caConfigMap, err := getCASecretAndConfigMapInCluster(toCASecretAndConfigMapName(k, n), ns)
155+
if err != nil {
156+
return nil, nil, nil, err
157+
}
158+
159+
hasAppSecret := appSecret != nil
160+
hasCASecretAndConfigMap := caSecret != nil && caConfigMap != nil
161+
// TODO: handle passed in CA
162+
if hasAppSecret && hasCASecretAndConfigMap {
163+
return appSecret, caConfigMap, caSecret, nil
164+
} else if hasAppSecret && !hasCASecretAndConfigMap {
165+
// TODO
166+
} else if !hasAppSecret && hasCASecretAndConfigMap {
167+
// TODO
168+
} else {
169+
// TODO
170+
}
171+
return nil, nil, nil, nil
172+
}
173+
174+
func verifyConfig(config *CertConfig) error {
175+
if config == nil {
176+
return errors.New("nil CertConfig not allowed")
177+
}
178+
if config.CertName == "" {
179+
return errors.New("empty CertConfig.CertName not allowed")
180+
}
181+
return nil
182+
}
183+
184+
func toAppSecretName(kind, name, certName string) string {
185+
return strings.ToLower(kind) + "-" + name + "-" + certName
186+
}
187+
188+
func toCASecretAndConfigMapName(kind, name string) string {
189+
return strings.ToLower(kind) + "-" + name + "-ca"
190+
}
191+
192+
func getAppSecretInCluster(name, namespace string) (*v1.Secret, error) {
193+
se := &v1.Secret{
194+
TypeMeta: metav1.TypeMeta{
195+
Kind: "Secret",
196+
APIVersion: "v1",
197+
},
198+
ObjectMeta: metav1.ObjectMeta{
199+
Name: name,
200+
Namespace: namespace,
201+
},
202+
}
203+
err := sdk.Get(se)
204+
if apiErrors.IsNotFound(err) {
205+
return nil, nil
206+
}
207+
if err != nil {
208+
return nil, err
209+
}
210+
return se, nil
211+
}
212+
213+
// getCASecretAndConfigMapInCluster gets CA secret and configmap of the given name and namespace.
214+
// it only returns both if they are found and nil if both are not found. In the case if only one of them is found, then we error out because we expect either both CA secret and configmap exit or not.
215+
func getCASecretAndConfigMapInCluster(name, namespace string) (*v1.Secret, *v1.ConfigMap, error) {
216+
cm := &v1.ConfigMap{
217+
TypeMeta: metav1.TypeMeta{
218+
Kind: "ConfigMap",
219+
APIVersion: "v1",
220+
},
221+
ObjectMeta: metav1.ObjectMeta{
222+
Name: name,
223+
Namespace: namespace,
224+
},
225+
}
226+
hasConfigMap := true
227+
err := sdk.Get(cm)
228+
if apiErrors.IsNotFound(err) {
229+
hasConfigMap = false
230+
}
231+
if err != nil {
232+
return nil, nil, err
233+
}
234+
235+
se := &v1.Secret{
236+
TypeMeta: metav1.TypeMeta{
237+
Kind: "Secret",
238+
APIVersion: "v1",
239+
},
240+
ObjectMeta: metav1.ObjectMeta{
241+
Name: name,
242+
Namespace: namespace,
243+
},
244+
}
245+
hasSecret := true
246+
err = sdk.Get(se)
247+
if apiErrors.IsNotFound(err) {
248+
hasSecret = false
249+
}
250+
if err != nil {
251+
return nil, nil, err
252+
}
253+
254+
if hasConfigMap != hasSecret {
255+
// TODO: this case can happen if creating CA configmap succeeds and creating CA secret failed. We need to handle this case properly.
256+
return nil, nil, fmt.Errorf("expect either both ca configmap and secret both exist or not exist, but got hasCAConfigmap==%v and hasCASecret==%v", hasConfigMap, hasSecret)
257+
}
258+
if hasConfigMap == false {
259+
return nil, nil, nil
260+
}
261+
return se, cm, nil
262+
}
263+
264+
func toKindNameNamespace(cr runtime.Object) (string, string, string, error) {
265+
a := meta.NewAccessor()
266+
k, err := a.Kind(cr)
267+
if err != nil {
268+
return "", "", "", err
269+
}
270+
n, err := a.Name(cr)
271+
if err != nil {
272+
return "", "", "", err
273+
}
274+
ns, err := a.Namespace(cr)
275+
if err != nil {
276+
return "", "", "", err
277+
}
278+
return k, n, ns, nil
279+
}
280+
281+
// toTLSSecret returns a client/server "kubernetes.io/tls" secret.
282+
// TODO: add owner ref.
283+
func toTLSSecret(key *rsa.PrivateKey, cert *x509.Certificate, name, namespace string) *v1.Secret {
284+
return &v1.Secret{
285+
TypeMeta: metav1.TypeMeta{
286+
Kind: "Secret",
287+
APIVersion: "v1",
288+
},
289+
ObjectMeta: metav1.ObjectMeta{
290+
Name: name,
291+
Namespace: namespace,
292+
},
293+
Data: map[string][]byte{
294+
v1.TLSPrivateKeyKey: encodePrivateKeyPEM(key),
295+
v1.TLSCertKey: encodeCertificatePEM(cert),
296+
},
297+
Type: v1.SecretTypeTLS,
298+
}
299+
}
300+
301+
// TODO: add owner ref.
302+
func toCASecretAndConfigmap(key *rsa.PrivateKey, cert *x509.Certificate, name, namespace string) (*v1.ConfigMap, *v1.Secret) {
303+
return &v1.ConfigMap{
304+
TypeMeta: metav1.TypeMeta{
305+
Kind: "ConfigMap",
306+
APIVersion: "v1",
307+
},
308+
ObjectMeta: metav1.ObjectMeta{
309+
Name: name,
310+
Namespace: namespace,
311+
},
312+
Data: map[string]string{
313+
TLSCACertKey: string(encodeCertificatePEM(cert)),
314+
},
315+
}, &v1.Secret{
316+
TypeMeta: metav1.TypeMeta{
317+
Kind: "Secret",
318+
APIVersion: "v1",
319+
},
320+
ObjectMeta: metav1.ObjectMeta{
321+
Name: name,
322+
Namespace: namespace,
323+
},
324+
Data: map[string][]byte{
325+
TLSPrivateCAKeyKey: encodePrivateKeyPEM(key),
326+
},
327+
}
328+
}

0 commit comments

Comments
 (0)