Skip to content

Commit 34ee875

Browse files
committed
ocirepo: adopt Kubernetes style TLS secrets for .spec.certSecretRef
Adopt Kubernetes TLS secrets API to check for TLS data in the Secret referred to by `.spec.certSecretRef`, i.e. check for keys `tls.crt` and `tls.key` for the certificate and private key. Use `ca.crt` for the CA certificate. Deprecate the usage of `caFile`, `certFile` and `keyFile` keys. Signed-off-by: Sanskar Jaiswal <jaiswalsanskar078@gmail.com>
1 parent 61218f8 commit 34ee875

File tree

6 files changed

+145
-70
lines changed

6 files changed

+145
-70
lines changed

api/v1beta2/ocirepository_types.go

+12-8
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,21 @@ type OCIRepositorySpec struct {
9797
// +optional
9898
ServiceAccountName string `json:"serviceAccountName,omitempty"`
9999

100-
// CertSecretRef can be given the name of a secret containing
100+
// CertSecretRef can be given the name of a Secret containing
101101
// either or both of
102102
//
103-
// - a PEM-encoded client certificate (`certFile`) and private
104-
// key (`keyFile`);
105-
// - a PEM-encoded CA certificate (`caFile`)
103+
// - a PEM-encoded client certificate (`tls.crt`) and private
104+
// key (`tls.key`);
105+
// - a PEM-encoded CA certificate (`ca.crt`)
106106
//
107-
// and whichever are supplied, will be used for connecting to the
108-
// registry. The client cert and key are useful if you are
109-
// authenticating with a certificate; the CA cert is useful if
110-
// you are using a self-signed server certificate.
107+
// and whichever are supplied, will be used for connecting to the
108+
// registry. The client cert and key are useful if you are
109+
// authenticating with a certificate; the CA cert is useful if
110+
// you are using a self-signed server certificate. The Secret must
111+
// be of type `Opaque` or `kubernetes.io/tls`.
112+
//
113+
// Note: Support for the `caFile`, `certFile` and `keyFile` keys has
114+
// been deprecated.
111115
// +optional
112116
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
113117

config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml

+6-4
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,15 @@ spec:
5050
description: OCIRepositorySpec defines the desired state of OCIRepository
5151
properties:
5252
certSecretRef:
53-
description: "CertSecretRef can be given the name of a secret containing
54-
either or both of \n - a PEM-encoded client certificate (`certFile`)
55-
and private key (`keyFile`); - a PEM-encoded CA certificate (`caFile`)
53+
description: "CertSecretRef can be given the name of a Secret containing
54+
either or both of \n - a PEM-encoded client certificate (`tls.crt`)
55+
and private key (`tls.key`); - a PEM-encoded CA certificate (`ca.crt`)
5656
\n and whichever are supplied, will be used for connecting to the
5757
registry. The client cert and key are useful if you are authenticating
5858
with a certificate; the CA cert is useful if you are using a self-signed
59-
server certificate."
59+
server certificate. The Secret must be of type `Opaque` or `kubernetes.io/tls`.
60+
\n Note: Support for the `caFile`, `certFile` and `keyFile` keys
61+
has been deprecated."
6062
properties:
6163
name:
6264
description: Name of the referent.

docs/api/v1beta2/source.md

+16-10
Original file line numberDiff line numberDiff line change
@@ -1119,17 +1119,20 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
11191119
</td>
11201120
<td>
11211121
<em>(Optional)</em>
1122-
<p>CertSecretRef can be given the name of a secret containing
1122+
<p>CertSecretRef can be given the name of a Secret containing
11231123
either or both of</p>
11241124
<ul>
1125-
<li>a PEM-encoded client certificate (<code>certFile</code>) and private
1126-
key (<code>keyFile</code>);</li>
1127-
<li>a PEM-encoded CA certificate (<code>caFile</code>)</li>
1125+
<li>a PEM-encoded client certificate (<code>tls.crt</code>) and private
1126+
key (<code>tls.key</code>);</li>
1127+
<li>a PEM-encoded CA certificate (<code>ca.crt</code>)</li>
11281128
</ul>
11291129
<p>and whichever are supplied, will be used for connecting to the
11301130
registry. The client cert and key are useful if you are
11311131
authenticating with a certificate; the CA cert is useful if
1132-
you are using a self-signed server certificate.</p>
1132+
you are using a self-signed server certificate. The Secret must
1133+
be of type <code>Opaque</code> or <code>kubernetes.io/tls</code>.</p>
1134+
<p>Note: Support for the <code>caFile</code>, <code>certFile</code> and <code>keyFile</code> keys has
1135+
been deprecated.</p>
11331136
</td>
11341137
</tr>
11351138
<tr>
@@ -3024,17 +3027,20 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
30243027
</td>
30253028
<td>
30263029
<em>(Optional)</em>
3027-
<p>CertSecretRef can be given the name of a secret containing
3030+
<p>CertSecretRef can be given the name of a Secret containing
30283031
either or both of</p>
30293032
<ul>
3030-
<li>a PEM-encoded client certificate (<code>certFile</code>) and private
3031-
key (<code>keyFile</code>);</li>
3032-
<li>a PEM-encoded CA certificate (<code>caFile</code>)</li>
3033+
<li>a PEM-encoded client certificate (<code>tls.crt</code>) and private
3034+
key (<code>tls.key</code>);</li>
3035+
<li>a PEM-encoded CA certificate (<code>ca.crt</code>)</li>
30333036
</ul>
30343037
<p>and whichever are supplied, will be used for connecting to the
30353038
registry. The client cert and key are useful if you are
30363039
authenticating with a certificate; the CA cert is useful if
3037-
you are using a self-signed server certificate.</p>
3040+
you are using a self-signed server certificate. The Secret must
3041+
be of type <code>Opaque</code> or <code>kubernetes.io/tls</code>.</p>
3042+
<p>Note: Support for the <code>caFile</code>, <code>certFile</code> and <code>keyFile</code> keys has
3043+
been deprecated.</p>
30383044
</td>
30393045
</tr>
30403046
<tr>

docs/spec/v1beta2/ocirepositories.md

+46-26
Original file line numberDiff line numberDiff line change
@@ -310,42 +310,62 @@ fetch the image pull secrets attached to the service account and use them for au
310310
**Note:** that for a publicly accessible image repository, you don't need to provide a `secretRef`
311311
nor `serviceAccountName`.
312312

313-
### TLS Certificates
313+
### Cert secret reference
314314

315-
`.spec.certSecretRef` field names a secret with TLS certificate data. This is for two separate
316-
purposes:
315+
`.spec.certSecretRef.name` is an optional field to specify a secret containing
316+
TLS certificate data. The secret can contain the following keys:
317317

318-
- to provide a client certificate and private key, if you use a certificate to authenticate with
319-
the container registry; and,
320-
- to provide a CA certificate, if the registry uses a self-signed certificate.
318+
* `tls.crt` and `tls.key`, to specify the client certificate and private key used
319+
for TLS client authentication. These must be used in conjunction, i.e.
320+
specifying one without the other will lead to an error.
321+
* `ca.crt`, to specify the CA certificate used to verify the server, which is
322+
required if the server is using a self-signed certificate.
321323

322-
These will often go together, if you are hosting a container registry yourself. All the files in the
323-
secret are expected to be [PEM-encoded][pem-encoding]. This is an ASCII format for certificates and
324-
keys; `openssl` and such tools will typically give you an option of PEM output.
324+
If the server is using a self-signed certificate and has TLS client
325+
authentication enabled, all three values are required.
325326

326-
Assuming you have obtained a certificate file and private key and put them in the files `client.crt`
327-
and `client.key` respectively, you can create a secret with `kubectl` like this:
327+
The Secret should be of type `Opaque` or `kubernetes.io/tls`. All the files in
328+
the Secret are expected to be [PEM-encoded][pem-encoding]. Assuming you have
329+
three files; `client.key`, `client.crt` and `ca.crt` for the client private key,
330+
client certificate and the CA certificate respectively, you can generate the
331+
required Secret using the `flux create secret tls` command:
328332

329-
```bash
330-
kubectl create secret generic tls-certs \
331-
--from-file=certFile=client.crt \
332-
--from-file=keyFile=client.key
333+
```sh
334+
flux create secret tls --tls-key-file=client.key --tls-crt-file=client.crt --ca-crt-file=ca.crt
333335
```
334336

335-
You could also [prepare a secret and encrypt it][sops-guide]; the important bit is that the data
336-
keys in the secret are `certFile` and `keyFile`.
337-
338-
If you have a CA certificate for the client to use, the data key for that is `caFile`. Adapting the
339-
previous example, if you have the certificate in the file `ca.crt`, and the client certificate and
340-
key as before, the whole command would be:
337+
Example usage:
341338

342-
```bash
343-
kubectl create secret generic tls-certs \
344-
--from-file=certFile=client.crt \
345-
--from-file=keyFile=client.key \
346-
--from-file=caFile=ca.crt
339+
```yaml
340+
---
341+
apiVersion: source.toolkit.fluxcd.io/v1beta2
342+
kind: OCIRepository
343+
metadata:
344+
name: example
345+
namespace: default
346+
spec:
347+
interval: 5m0s
348+
url: oci://example.com
349+
certSecretRef:
350+
name: example-tls
351+
---
352+
apiVersion: v1
353+
kind: Secret
354+
metadata:
355+
name: example-tls
356+
namespace: default
357+
type: kubernetes.io/tls # or Opaque
358+
data:
359+
tls.crt: <BASE64>
360+
tls.key: <BASE64>
361+
# NOTE: Can be supplied without the above values
362+
ca.crt: <BASE64>
347363
```
348364

365+
**Warning:** Support for the `caFile`, `certFile` and `keyFile` keys have been
366+
deprecated. If you have any Secrets using these keys and specified in an
367+
OCIRepository, the controller will log a deprecation warning.
368+
349369
### Insecure
350370

351371
`.spec.insecure` is an optional field to allow connecting to an insecure (HTTP)

internal/controller/ocirepository_controller.go

+12-20
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ package controller
1818

1919
import (
2020
"context"
21-
"crypto/tls"
22-
"crypto/x509"
2321
"errors"
2422
"fmt"
2523
"io"
@@ -71,6 +69,7 @@ import (
7169
soci "github.com/fluxcd/source-controller/internal/oci"
7270
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
7371
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
72+
"github.com/fluxcd/source-controller/internal/tls"
7473
"github.com/fluxcd/source-controller/internal/util"
7574
)
7675

@@ -841,29 +840,22 @@ func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *ociv1.OCIR
841840
}
842841

843842
transport := remote.DefaultTransport.(*http.Transport).Clone()
844-
tlsConfig := transport.TLSClientConfig
845-
846-
if clientCert, ok := certSecret.Data[oci.ClientCert]; ok {
847-
// parse and set client cert and secret
848-
if clientKey, ok := certSecret.Data[oci.ClientKey]; ok {
849-
cert, err := tls.X509KeyPair(clientCert, clientKey)
850-
if err != nil {
851-
return nil, err
852-
}
853-
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
854-
} else {
855-
return nil, fmt.Errorf("'%s' found in secret, but no %s", oci.ClientCert, oci.ClientKey)
856-
}
843+
tlsConfig, _, err := tls.KubeTLSClientConfigFromSecret(certSecret, "")
844+
if err != nil {
845+
return nil, err
857846
}
858-
859-
if caCert, ok := certSecret.Data[oci.CACert]; ok {
860-
syscerts, err := x509.SystemCertPool()
847+
if tlsConfig == nil {
848+
tlsConfig, _, err = tls.TLSClientConfigFromSecret(certSecret, "")
861849
if err != nil {
862850
return nil, err
863851
}
864-
syscerts.AppendCertsFromPEM(caCert)
865-
tlsConfig.RootCAs = syscerts
852+
if tlsConfig != nil {
853+
ctrl.LoggerFrom(ctx).
854+
Info("warning: specifying TLS auth data via `certFile`/`keyFile`/`caFile` is deprecated, please use `tls.crt`/`tls.key`/`ca.crt` instead")
855+
}
866856
}
857+
transport.TLSClientConfig = tlsConfig
858+
867859
return transport, nil
868860
}
869861

internal/controller/ocirepository_controller_test.go

+53-2
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,31 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
557557
},
558558
}),
559559
},
560+
tlsCertSecret: &corev1.Secret{
561+
ObjectMeta: metav1.ObjectMeta{
562+
Name: "ca-file",
563+
},
564+
Data: map[string][]byte{
565+
"ca.crt": tlsCA,
566+
},
567+
},
568+
assertConditions: []metav1.Condition{
569+
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
570+
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
571+
},
572+
},
573+
{
574+
name: "HTTPS with valid certfile using deprecated keys",
575+
want: sreconcile.ResultSuccess,
576+
registryOpts: registryOptions{
577+
withTLS: true,
578+
},
579+
craneOpts: []crane.Option{crane.WithTransport(&http.Transport{
580+
TLSClientConfig: &tls.Config{
581+
RootCAs: pool,
582+
},
583+
}),
584+
},
560585
tlsCertSecret: &corev1.Secret{
561586
ObjectMeta: metav1.ObjectMeta{
562587
Name: "ca-file",
@@ -605,11 +630,37 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) {
605630
Name: "ca-file",
606631
},
607632
Data: map[string][]byte{
633+
"ca.crt": []byte("invalid"),
634+
},
635+
},
636+
assertConditions: []metav1.Condition{
637+
*conditions.TrueCondition(sourcev1.FetchFailedCondition, ociv1.AuthenticationFailedReason, "cannot append certificate into certificate pool: invalid CA certificate"),
638+
},
639+
},
640+
{
641+
name: "HTTPS with certfile using both caFile and ca.crt ignores caFile",
642+
want: sreconcile.ResultSuccess,
643+
registryOpts: registryOptions{
644+
withTLS: true,
645+
},
646+
craneOpts: []crane.Option{crane.WithTransport(&http.Transport{
647+
TLSClientConfig: &tls.Config{
648+
RootCAs: pool,
649+
},
650+
}),
651+
},
652+
tlsCertSecret: &corev1.Secret{
653+
ObjectMeta: metav1.ObjectMeta{
654+
Name: "ca-file",
655+
},
656+
Data: map[string][]byte{
657+
"ca.crt": tlsCA,
608658
"caFile": []byte("invalid"),
609659
},
610660
},
611661
assertConditions: []metav1.Condition{
612-
*conditions.TrueCondition(sourcev1.FetchFailedCondition, ociv1.OCIPullFailedReason, "failed to determine artifact digest"),
662+
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
663+
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new revision '<revision>' for '<url>'"),
613664
},
614665
},
615666
{
@@ -1257,7 +1308,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
12571308
Generation: 1,
12581309
},
12591310
Data: map[string][]byte{
1260-
"caFile": tlsCA,
1311+
"ca.crt": tlsCA,
12611312
},
12621313
}
12631314

0 commit comments

Comments
 (0)