Skip to content

Commit 1b44a2c

Browse files
committed
Add proxy support for OCIRepository API
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
1 parent e1ff038 commit 1b44a2c

File tree

10 files changed

+622
-21
lines changed

10 files changed

+622
-21
lines changed

api/v1beta2/ocirepository_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ type OCIRepositorySpec struct {
116116
// +optional
117117
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
118118

119+
// ProxySecretRef specifies the Secret containing the proxy configuration
120+
// to use while communicating with the container registry.
121+
// +optional
122+
ProxySecretRef *meta.LocalObjectReference `json:"proxySecretRef,omitempty"`
123+
119124
// Interval at which the OCIRepository URL is checked for updates.
120125
// This interval is approximate and may be subject to jitter to ensure
121126
// efficient use of resources.

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,17 @@ spec:
131131
- azure
132132
- gcp
133133
type: string
134+
proxySecretRef:
135+
description: |-
136+
ProxySecretRef specifies the Secret containing the proxy configuration
137+
to use while communicating with the container registry.
138+
properties:
139+
name:
140+
description: Name of the referent.
141+
type: string
142+
required:
143+
- name
144+
type: object
134145
ref:
135146
description: |-
136147
The OCI reference to pull and monitor for changes,

docs/api/v1beta2/source.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,21 @@ been deprecated.</p>
12351235
</tr>
12361236
<tr>
12371237
<td>
1238+
<code>proxySecretRef</code><br>
1239+
<em>
1240+
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
1241+
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
1242+
</a>
1243+
</em>
1244+
</td>
1245+
<td>
1246+
<em>(Optional)</em>
1247+
<p>ProxySecretRef specifies the Secret containing the proxy configuration
1248+
to use while communicating with the container registry.</p>
1249+
</td>
1250+
</tr>
1251+
<tr>
1252+
<td>
12381253
<code>interval</code><br>
12391254
<em>
12401255
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
@@ -3313,6 +3328,21 @@ been deprecated.</p>
33133328
</tr>
33143329
<tr>
33153330
<td>
3331+
<code>proxySecretRef</code><br>
3332+
<em>
3333+
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
3334+
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
3335+
</a>
3336+
</em>
3337+
</td>
3338+
<td>
3339+
<em>(Optional)</em>
3340+
<p>ProxySecretRef specifies the Secret containing the proxy configuration
3341+
to use while communicating with the container registry.</p>
3342+
</td>
3343+
</tr>
3344+
<tr>
3345+
<td>
33163346
<code>interval</code><br>
33173347
<em>
33183348
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">

docs/spec/v1beta2/ocirepositories.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,39 @@ data:
330330
deprecated. If you have any Secrets using these keys and specified in an
331331
OCIRepository, the controller will log a deprecation warning.
332332

333+
### Proxy secret reference
334+
335+
`.spec.proxySecretRef.name` is an optional field used to specify the name of a
336+
Secret that contains the proxy settings for the object. These settings are used
337+
for all the remote operations related to the OCIRepository.
338+
The Secret can contain three keys:
339+
340+
- `address`, to specify the address of the proxy server. This is a required key.
341+
- `username`, to specify the username to use if the proxy server is protected by
342+
basic authentication. This is an optional key.
343+
- `password`, to specify the password to use if the proxy server is protected by
344+
basic authentication. This is an optional key.
345+
346+
Example:
347+
348+
```yaml
349+
---
350+
apiVersion: v1
351+
kind: Secret
352+
metadata:
353+
name: http-proxy
354+
type: Opaque
355+
stringData:
356+
address: http://proxy.com
357+
username: mandalorian
358+
password: grogu
359+
```
360+
361+
Proxying can also be configured in the source-controller Deployment directly by
362+
using the standard environment variables such as `HTTPS_PROXY`, `ALL_PROXY`, etc.
363+
364+
`.spec.proxySecretRef.name` takes precedence over all environment variables.
365+
333366
### Insecure
334367

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

internal/controller/ocirepository_controller.go

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"fmt"
2525
"io"
2626
"net/http"
27+
"net/url"
2728
"os"
2829
"path/filepath"
2930
"regexp"
@@ -437,7 +438,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
437438
conditions.GetObservedGeneration(obj, sourcev1.SourceVerifiedCondition) != obj.Generation ||
438439
conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) {
439440

440-
result, err := r.verifySignature(ctx, obj, ref, keychain, auth, opts...)
441+
result, err := r.verifySignature(ctx, obj, ref, keychain, auth, transport, opts...)
441442
if err != nil {
442443
provider := obj.Spec.Verify.Provider
443444
if obj.Spec.Verify.SecretRef == nil && obj.Spec.Verify.Provider == "cosign" {
@@ -623,7 +624,10 @@ func (r *OCIRepositoryReconciler) digestFromRevision(revision string) string {
623624
// If not, when using cosign it falls back to a keyless approach for verification.
624625
// When notation is used, a trust policy is required to verify the image.
625626
// The verification result is returned as a VerificationResult and any error encountered.
626-
func (r *OCIRepositoryReconciler) verifySignature(ctx context.Context, obj *ociv1.OCIRepository, ref name.Reference, keychain authn.Keychain, auth authn.Authenticator, opt ...remote.Option) (soci.VerificationResult, error) {
627+
func (r *OCIRepositoryReconciler) verifySignature(ctx context.Context, obj *ociv1.OCIRepository,
628+
ref name.Reference, keychain authn.Keychain, auth authn.Authenticator,
629+
transport *http.Transport, opt ...remote.Option) (soci.VerificationResult, error) {
630+
627631
ctxTimeout, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
628632
defer cancel()
629633

@@ -753,6 +757,7 @@ func (r *OCIRepositoryReconciler) verifySignature(ctx context.Context, obj *ociv
753757
notation.WithInsecureRegistry(obj.Spec.Insecure),
754758
notation.WithLogger(ctrl.LoggerFrom(ctx)),
755759
notation.WithRootCertificates(certs),
760+
notation.WithTransport(transport),
756761
}
757762

758763
verifier, err := notation.NewNotationVerifier(defaultNotationOciOpts...)
@@ -920,16 +925,40 @@ func (r *OCIRepositoryReconciler) keychain(ctx context.Context, obj *ociv1.OCIRe
920925

921926
// transport clones the default transport from remote and when a certSecretRef is specified,
922927
// the returned transport will include the TLS client and/or CA certificates.
928+
// If the insecure flag is set, the transport will skip the verification of the server's certificate.
929+
// Additionally, if a proxy is specified, transport will use it.
923930
func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *ociv1.OCIRepository) (*http.Transport, error) {
924931
transport := remote.DefaultTransport.(*http.Transport).Clone()
925932

933+
tlsConfig, err := r.getTLSConfig(ctx, obj)
934+
if err != nil {
935+
return nil, err
936+
}
937+
if tlsConfig != nil {
938+
transport.TLSClientConfig = tlsConfig
939+
}
940+
941+
proxyURL, err := r.getProxyURL(ctx, obj)
942+
if err != nil {
943+
return nil, err
944+
}
945+
if proxyURL != nil {
946+
transport.Proxy = http.ProxyURL(proxyURL)
947+
}
948+
949+
return transport, nil
950+
}
951+
952+
// getTLSConfig gets the TLS configuration for the transport based on the
953+
// specified secret reference in the OCIRepository object, or the insecure flag.
954+
func (r *OCIRepositoryReconciler) getTLSConfig(ctx context.Context, obj *ociv1.OCIRepository) (*cryptotls.Config, error) {
926955
if obj.Spec.CertSecretRef == nil || obj.Spec.CertSecretRef.Name == "" {
927956
if obj.Spec.Insecure {
928-
transport.TLSClientConfig = &cryptotls.Config{
957+
return &cryptotls.Config{
929958
InsecureSkipVerify: true,
930-
}
959+
}, nil
931960
}
932-
return transport, nil
961+
return nil, nil
933962
}
934963

935964
certSecretName := types.NamespacedName{
@@ -955,9 +984,42 @@ func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *ociv1.OCIR
955984
Info("warning: specifying TLS auth data via `certFile`/`keyFile`/`caFile` is deprecated, please use `tls.crt`/`tls.key`/`ca.crt` instead")
956985
}
957986
}
958-
transport.TLSClientConfig = tlsConfig
959987

960-
return transport, nil
988+
return tlsConfig, nil
989+
}
990+
991+
// getProxyURL gets the proxy configuration for the transport based on the
992+
// specified proxy secret reference in the OCIRepository object.
993+
func (r *OCIRepositoryReconciler) getProxyURL(ctx context.Context, obj *ociv1.OCIRepository) (*url.URL, error) {
994+
if obj.Spec.ProxySecretRef == nil || obj.Spec.ProxySecretRef.Name == "" {
995+
return nil, nil
996+
}
997+
998+
proxySecretName := types.NamespacedName{
999+
Namespace: obj.Namespace,
1000+
Name: obj.Spec.ProxySecretRef.Name,
1001+
}
1002+
var proxySecret corev1.Secret
1003+
if err := r.Get(ctx, proxySecretName, &proxySecret); err != nil {
1004+
return nil, err
1005+
}
1006+
1007+
proxyData := proxySecret.Data
1008+
address, ok := proxyData["address"]
1009+
if !ok {
1010+
return nil, fmt.Errorf("invalid proxy secret '%s/%s': key 'address' is missing",
1011+
obj.Namespace, obj.Spec.ProxySecretRef.Name)
1012+
}
1013+
proxyURL, err := url.Parse(string(address))
1014+
if err != nil {
1015+
return nil, fmt.Errorf("failed to parse proxy address '%s': %w", address, err)
1016+
}
1017+
user, hasUser := proxyData["username"]
1018+
password, hasPassword := proxyData["password"]
1019+
if hasUser || hasPassword {
1020+
proxyURL.User = url.UserPassword(string(user), string(password))
1021+
}
1022+
return proxyURL, nil
9611023
}
9621024

9631025
// reconcileStorage ensures the current state of the storage matches the

0 commit comments

Comments
 (0)