Skip to content

Commit

Permalink
Merge branch 'release/v1.7.0' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
nroeske authored and cesmarvin committed Jan 27, 2025
2 parents c876a7c + 0623fbd commit cabe6c3
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [v1.7.0] - 2025-01-27
### Added
- [#85] Proxy support for the registry http client in helm. The proxy will be used from the secret `ces-proxy` which will be created by the setup.

## [v1.6.2] - 2024-12-19
### Fixed
- [#83] CVE-2024-45337 by updating dependencies
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ RUN make compile-generic
FROM gcr.io/distroless/static:nonroot
LABEL maintainer="[email protected]" \
NAME="k8s-component-operator" \
VERSION="1.6.2"
VERSION="1.7.0"

WORKDIR /
COPY --from=builder /workspace/target/k8s-component-operator .
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Set these to the desired values
ARTIFACT_ID=k8s-component-operator
VERSION=1.6.2
VERSION=1.7.0
## Image URL to use all building/pushing image targets
IMAGE=cloudogu/${ARTIFACT_ID}:${VERSION}
GOTAG?=1.23.4
Expand Down
35 changes: 35 additions & 0 deletions docs/development/proxy_configuration_en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Developing with a proxy for external queries to dogu container or helm registry

Component can mount a proxy stored in the `ces-proxy` secret.
To test this behaviour be sure to configure the proxy in the setup values of the setup's helm chart,
or create the secret manually by:

`kubectl create secret generic ces-proxy --from-literal=url=http://test:[email protected]:3128 -n ecosystem`

## Setup local proxy in docker

### Run container with host network mode to reach the development registry in the cluster if required.

- `docker run --net=host -d --name squid -e TZ=UTC -p 3128:3128 ubuntu/squid:5.2-22.04_beta`

### Configure auth and connection to dev registry

- `docker exec -it squid /bin/bash`
- `apt update && apt-get install -y apache2-utils`
- `htpasswd -c -d /etc/squid/passwords test`
- `chmod o+r /etc/squid/passwords`
- `apt-get install vim`
- `vi /etc/squid/conf.d/auth.conf`
- ```
acl allcomputers src all
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwords
auth_param basic realm proxy
acl authenticated proxy_auth REQUIRED
http_access allow authenticated allcomputers
```
- `echo "localhost k3ces.local" >> /etc/hosts`
- `squid -k reconfigure`
### Check access logs
- `docker logs -f squid`
2 changes: 1 addition & 1 deletion k8s/helm/component-patch-tpl.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v1
values:
images:
componentOperator: cloudogu/k8s-component-operator:1.6.2
componentOperator: cloudogu/k8s-component-operator:1.7.0
patches:
values.yaml:
additionalImages:
Expand Down
6 changes: 6 additions & 0 deletions k8s/helm/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ spec:
value: "{{ .Values.manager.env.helmClientTimeoutMins | default "15" }}"
- name: ROLLBACK_RELEASE_TIMEOUT_MINS
value: "{{ .Values.manager.env.rollbackReleaseTimeoutMins | default "15" }}"
- name: PROXY_URL
valueFrom:
secretKeyRef:
name: ces-proxy
key: url
optional: true
image: "{{ .Values.manager.image.registry }}/{{ .Values.manager.image.repository }}:{{ .Values.manager.image.tag }}"
imagePullPolicy: {{ .Values.manager.imagePullPolicy | default "IfNotPresent"}}
livenessProbe:
Expand Down
2 changes: 1 addition & 1 deletion k8s/helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ manager:
image:
registry: docker.io
repository: cloudogu/k8s-component-operator
tag: 1.6.2
tag: 1.7.0
imagePullPolicy: IfNotPresent
env:
logLevel: info
Expand Down
76 changes: 70 additions & 6 deletions pkg/helm/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"crypto/tls"
"fmt"
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"log"
"net"
"net/http"
"net/url"
"os"

"k8s.io/cli-runtime/pkg/genericclioptions"
"time"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
Expand Down Expand Up @@ -95,16 +97,78 @@ func createRegistryClient(options *Options, settings *cli.EnvSettings) (*registr
registry.ClientOptCredentialsFile(settings.RegistryConfig),
}

var err error
clientOpts, err = configureHttpRegistryClientOptions(options, clientOpts)
if err != nil {
return nil, err
}

return registry.NewClient(clientOpts...)
}

func configureHttpRegistryClientOptions(options *Options, clientOpts []registry.ClientOption) ([]registry.ClientOption, error) {
if options.PlainHttp {
clientOpts = append(clientOpts, registry.ClientOptPlainHTTP())
}

var httpTransport *http.Transport
var err error
httpTransport, err = getProxyTransportIfConfigured()
if err != nil {
return nil, err
}

httpTransport = configureTls(options, httpTransport)

if httpTransport != nil {
clientOpts = append(clientOpts, registry.ClientOptHTTPClient(&http.Client{Timeout: time.Second * 10, Transport: httpTransport}))
}

return clientOpts, nil
}

func configureTls(options *Options, transport *http.Transport) *http.Transport {
if !options.PlainHttp && options.InsecureTls {
insecureHttpsClient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
httpClientOpt := registry.ClientOptHTTPClient(insecureHttpsClient)
clientOpts = append(clientOpts, httpClientOpt)
tlsConfig := &tls.Config{InsecureSkipVerify: true}
if transport != nil {
transport.TLSClientConfig = tlsConfig
} else {
transport = &http.Transport{TLSClientConfig: tlsConfig}
}
}
return registry.NewClient(clientOpts...)

return transport
}

func getProxyTransportIfConfigured() (*http.Transport, error) {
proxyURL, found := os.LookupEnv("PROXY_URL")
if !found || len(proxyURL) < 1 {
return nil, nil
}

parsedProxy, err := url.Parse(proxyURL)
if err != nil {
return nil, fmt.Errorf("failed to parse proxy: %w", err)
}

proxyFn := func(request *http.Request) (*url.URL, error) {
return parsedProxy, nil
}

return &http.Transport{
// From https://github.com/google/go-containerregistry/blob/c4dd792fa06c1f8b780ad90c8ab4f38b4eac05bd/pkg/v1/remote/options.go#L113
DisableCompression: true,
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
Proxy: proxyFn,
}, nil
}

// setEnvSettings sets the client's environment settings based on the provided client configuration.
Expand Down
127 changes: 127 additions & 0 deletions pkg/helm/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package client

import (
"context"
"fmt"
"github.com/cloudogu/k8s-component-operator/pkg/helm/client/values"
"github.com/stretchr/testify/mock"
"helm.sh/helm/v3/pkg/chart"
"log"
"net"
"net/http"
"net/url"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -1163,3 +1167,126 @@ func TestHelmClient_GetChart(t *testing.T) {
assert.Equal(t, "testdata/deprecated-chart", chartPath)
})
}

func Test_getProxyTransportIfConfigured(t *testing.T) {
testProxy := "http://user:pass@host:3128"

parsedProxy, err := url.Parse(testProxy)
require.NoError(t, err)

testProxyFn := func(request *http.Request) (*url.URL, error) {
return parsedProxy, nil
}

expectedTransport := &http.Transport{
// From https://github.com/google/go-containerregistry/blob/31786c6cbb82d6ec4fb8eb79cd9387905130534e/pkg/v1/remote/options.go#L87
DisableCompression: true,
DialContext: (&net.Dialer{
// By default we wrap the transport in retries, so reduce the
// default dial timeout to 5s to avoid 5x 30s of connection
// timeouts when doing the "ping" on certain http registries.
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
Proxy: testProxyFn,
}

tests := []struct {
name string
wantErr assert.ErrorAssertionFunc
setEnv func(t *testing.T)
expectFn func(t *testing.T, got *http.Transport)
}{
{
name: "return with proxy if configured",
expectFn: func(t *testing.T, got *http.Transport) {
assert.Equalf(t, expectedTransport.DisableCompression, got.DisableCompression, "getProxyTransportIfConfigured()")
assert.Equalf(t, expectedTransport.ForceAttemptHTTP2, got.ForceAttemptHTTP2, "getProxyTransportIfConfigured()")
assert.Equalf(t, expectedTransport.MaxIdleConns, got.MaxIdleConns, "getProxyTransportIfConfigured()")
assert.Equalf(t, expectedTransport.IdleConnTimeout, got.IdleConnTimeout, "getProxyTransportIfConfigured()")
assert.Equalf(t, expectedTransport.TLSHandshakeTimeout, got.TLSHandshakeTimeout, "getProxyTransportIfConfigured()")
assert.Equalf(t, expectedTransport.ExpectContinueTimeout, got.ExpectContinueTimeout, "getProxyTransportIfConfigured()")

gotProxy, err := got.Proxy(nil)
require.NoError(t, err)

wantProxy, err := expectedTransport.Proxy(nil)
require.NoError(t, err)

assert.Equal(t, wantProxy, gotProxy)
},
wantErr: assert.NoError,
setEnv: func(t *testing.T) {
t.Setenv("PROXY_URL", testProxy)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.setEnv != nil {
tt.setEnv(t)
}

got, err := getProxyTransportIfConfigured()
if !tt.wantErr(t, err, fmt.Sprintf("getProxyTransportIfConfigured()")) {
return
}

tt.expectFn(t, got)
})
}
}

func Test_configureTls(t *testing.T) {
type args struct {
options *Options
transport *http.Transport
expectFn func(t *testing.T, transport *http.Transport)
}
tests := []struct {
name string
args args
expectFn func(t *testing.T, transport *http.Transport)
}{
{
name: "should set tls config in existing transport",
args: args{
options: &Options{
PlainHttp: false,
InsecureTls: true,
},
transport: http.DefaultTransport.(*http.Transport),
},
expectFn: func(t *testing.T, transport *http.Transport) {
require.NotNil(t, transport.TLSClientConfig)
assert.Equal(t, true, transport.TLSClientConfig.InsecureSkipVerify)
},
},
{
name: "should create tls config in non existing transport",
args: args{
options: &Options{
PlainHttp: false,
InsecureTls: true,
},
transport: nil,
},
expectFn: func(t *testing.T, transport *http.Transport) {
require.NotNil(t, transport.TLSClientConfig)
assert.Equal(t, true, transport.TLSClientConfig.InsecureSkipVerify)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := configureTls(tt.args.options, tt.args.transport)

tt.expectFn(t, got)
})
}
}

0 comments on commit cabe6c3

Please sign in to comment.