Skip to content

Commit 730eb87

Browse files
committed
feat: expose chart-values api without rbac-proxy
Signed-off-by: Ilya Drey <[email protected]>
1 parent 69772d1 commit 730eb87

9 files changed

Lines changed: 87 additions & 61 deletions

File tree

images/chart-values-controller/cmd/chart-values-controller/main.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ func main() {
5555
metricsAddr string
5656
healthProbeAddr string
5757
apiAddr string
58+
apiTLSCertFile string
59+
apiTLSKeyFile string
5860
cacheDir string
5961
cacheTTL time.Duration
6062
sourceInterval time.Duration
@@ -64,6 +66,8 @@ func main() {
6466
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metrics endpoint binds to.")
6567
flag.StringVar(&healthProbeAddr, "health-probe-bind-address", ":9440", "The address the health probe endpoint binds to.")
6668
flag.StringVar(&apiAddr, "api-bind-address", "127.0.0.1:8081", "The address the chart-values HTTP API binds to.")
69+
flag.StringVar(&apiTLSCertFile, "api-tls-cert-file", "", "Path to the PEM-encoded certificate for the chart-values HTTP API; enables TLS together with --api-tls-key-file.")
70+
flag.StringVar(&apiTLSKeyFile, "api-tls-key-file", "", "Path to the PEM-encoded private key for the chart-values HTTP API; enables TLS together with --api-tls-cert-file.")
6771
flag.StringVar(&cacheDir, "cache-dir", "/cache", "Directory used to cache extracted values.yaml files.")
6872
flag.DurationVar(&cacheTTL, "chart-values-cache-ttl", time.Hour, "Lifetime of auxiliary source resources and their cache entries; refreshed on each request.")
6973
flag.DurationVar(&sourceInterval, "source-interval", 10*time.Minute, "Reconcile interval set on auxiliary source resources.")
@@ -81,6 +85,11 @@ func main() {
8185
}
8286
maxArtifactBytes := int64(maxChartSizeMB) << 20
8387

88+
if (apiTLSCertFile == "") != (apiTLSKeyFile == "") {
89+
logger.Error(nil, "--api-tls-cert-file and --api-tls-key-file must be set together")
90+
os.Exit(1)
91+
}
92+
8493
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
8594
Scheme: scheme,
8695
Metrics: metricsserver.Options{
@@ -107,7 +116,11 @@ func main() {
107116
maxArtifactBytes,
108117
)
109118

110-
if err := mgr.Add(server.New(apiAddr, res)); err != nil {
119+
apiServer := server.New(apiAddr, res, server.NewOptions{
120+
TLSCertFile: apiTLSCertFile,
121+
TLSKeyFile: apiTLSKeyFile,
122+
})
123+
if err := mgr.Add(apiServer); err != nil {
111124
logger.Error(err, "unable to add HTTP server")
112125
os.Exit(1)
113126
}

images/chart-values-controller/internal/server/server.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,26 @@ type chartValuesResolver interface {
4040
// Server exposes the chart-values HTTP API as a controller-runtime Runnable.
4141
// It does not require leader election so it can serve from any replica.
4242
type Server struct {
43-
addr string
44-
resolver chartValuesResolver
43+
addr string
44+
resolver chartValuesResolver
45+
tlsCertFile string
46+
tlsKeyFile string
4547
}
4648

47-
func New(addr string, res chartValuesResolver) *Server {
48-
return &Server{addr: addr, resolver: res}
49+
// NewOptions carries optional server configuration. When both TLSCertFile and
50+
// TLSKeyFile are set the API is served over TLS.
51+
type NewOptions struct {
52+
TLSCertFile string
53+
TLSKeyFile string
54+
}
55+
56+
func New(addr string, res chartValuesResolver, opts NewOptions) *Server {
57+
return &Server{
58+
addr: addr,
59+
resolver: res,
60+
tlsCertFile: opts.TLSCertFile,
61+
tlsKeyFile: opts.TLSKeyFile,
62+
}
4963
}
5064

5165
// NeedLeaderElection reports that the HTTP server runs on every replica.
@@ -72,7 +86,14 @@ func (s *Server) Start(ctx context.Context) error {
7286
_ = srv.Shutdown(shutdownCtx) //nolint:contextcheck // parent ctx is done, a fresh one is required for graceful shutdown
7387
}()
7488

75-
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
89+
serve := srv.ListenAndServe
90+
if s.tlsCertFile != "" && s.tlsKeyFile != "" {
91+
serve = func() error {
92+
return srv.ListenAndServeTLS(s.tlsCertFile, s.tlsKeyFile)
93+
}
94+
}
95+
96+
if err := serve(); err != nil && !errors.Is(err, http.ErrServerClosed) {
7697
return err
7798
}
7899

images/chart-values-controller/internal/server/server_test.go

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func (f fakeResolver) Resolve(_ context.Context, _ resolver.Request) (resolver.R
3939
func do(t *testing.T, res chartValuesResolver, body string) *httptest.ResponseRecorder {
4040
t.Helper()
4141

42-
srv := New("", res)
42+
srv := New("", res, NewOptions{})
4343
rec := httptest.NewRecorder()
4444
req := httptest.NewRequest(http.MethodPost, "/v1/chart-values", strings.NewReader(body))
4545
srv.handleChartValues(rec, req)
@@ -96,7 +96,7 @@ func TestHandleOutcomeStatusCodes(t *testing.T) {
9696
if rec.Code != c.wantStatus {
9797
t.Fatalf("outcome %s: status = %d, want %d", c.outcome, rec.Code, c.wantStatus)
9898
}
99-
assertErrorCode(t, rec.Body.Bytes(), c.wantCode)
99+
assertCode(t, rec.Body.Bytes(), "code", c.wantCode)
100100
}
101101
}
102102

@@ -105,39 +105,23 @@ func TestHandleInvalidJSON(t *testing.T) {
105105
if rec.Code != http.StatusBadRequest {
106106
t.Fatalf("status = %d, want 400", rec.Code)
107107
}
108-
assertErrorCode(t, rec.Body.Bytes(), "INVALID_REQUEST")
108+
assertCode(t, rec.Body.Bytes(), "code", "INVALID_REQUEST")
109109
}
110110

111111
func TestHandleMissingFields(t *testing.T) {
112112
rec := do(t, fakeResolver{}, `{"repositoryName":"github","chart":"podinfo"}`)
113113
if rec.Code != http.StatusBadRequest {
114114
t.Fatalf("status = %d, want 400", rec.Code)
115115
}
116-
assertErrorCode(t, rec.Body.Bytes(), "INVALID_REQUEST")
116+
assertCode(t, rec.Body.Bytes(), "code", "INVALID_REQUEST")
117117
}
118118

119119
func TestHandleInternalError(t *testing.T) {
120120
rec := do(t, fakeResolver{err: context.DeadlineExceeded}, validBody)
121121
if rec.Code != http.StatusInternalServerError {
122122
t.Fatalf("status = %d, want 500", rec.Code)
123123
}
124-
assertErrorCode(t, rec.Body.Bytes(), "INTERNAL")
125-
}
126-
127-
func assertErrorCode(t *testing.T, body []byte, want string) {
128-
t.Helper()
129-
130-
var resp struct {
131-
Error struct {
132-
Code string `json:"code"`
133-
} `json:"error"`
134-
}
135-
if err := json.Unmarshal(body, &resp); err != nil {
136-
t.Fatalf("decode: %v", err)
137-
}
138-
if resp.Error.Code != want {
139-
t.Fatalf("error code = %q, want %q", resp.Error.Code, want)
140-
}
124+
assertCode(t, rec.Body.Bytes(), "code", "INTERNAL")
141125
}
142126

143127
func assertCode(t *testing.T, body []byte, field, want string) {

images/hooks/pkg/hooks/tls-certificates-controller/hook.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,20 @@ var _ = tlscertificate.RegisterInternalTLSHookEM(tlscertificate.GenSelfSignedTLS
3838
FullValuesPathPrefix: fmt.Sprintf("%s.internal.controller.cert", settings.ModuleName),
3939
CommonCAValuesPath: fmt.Sprintf("%s.internal.rootCA", settings.ModuleName),
4040
})
41+
42+
var _ = tlscertificate.RegisterInternalTLSHookEM(tlscertificate.GenSelfSignedTLSHookConf{
43+
CN: settings.ChartValuesControllerCertCN,
44+
TLSSecretName: "chart-values-controller-tls",
45+
Namespace: settings.ModuleNamespace,
46+
SANs: tlscertificate.DefaultSANs([]string{
47+
"localhost",
48+
"127.0.0.1",
49+
settings.ChartValuesControllerCertCN,
50+
settings.ChartValuesAPIServiceName,
51+
fmt.Sprintf("%s.%s", settings.ChartValuesAPIServiceName, settings.ModuleNamespace),
52+
fmt.Sprintf("%s.%s.svc", settings.ChartValuesAPIServiceName, settings.ModuleNamespace),
53+
}),
54+
55+
FullValuesPathPrefix: fmt.Sprintf("%s.internal.chartValuesController.cert", settings.ModuleName),
56+
CommonCAValuesPath: fmt.Sprintf("%s.internal.rootCA", settings.ModuleName),
57+
})

images/hooks/pkg/settings/certificate.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ package settings
1919
const (
2020
ControllerCertCN string = "operator-helm-controller"
2121
ChartValuesControllerCertCN string = "chart-values-controller"
22+
ChartValuesAPIServiceName string = "chart-values-api"
2223
)

templates/chart-values-controller/deployment.yaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,24 @@ spec:
7676
args:
7777
- --metrics-bind-address=:8080
7878
- --health-probe-bind-address=:9440
79-
- --api-bind-address=127.0.0.1:8081
79+
- --api-bind-address=:9443
80+
- --api-tls-cert-file=/etc/chart-values-controller/tls/tls.crt
81+
- --api-tls-key-file=/etc/chart-values-controller/tls/tls.key
8082
- --cache-dir=/cache
8183
- --chart-values-cache-ttl=1h
8284
- --max-chart-size-mb=100
8385
volumeMounts:
8486
- mountPath: /cache
8587
name: cache
88+
- mountPath: /etc/chart-values-controller/tls
89+
name: api-tls
90+
readOnly: true
8691
{{- include "kube_api_rewriter.kubeconfig_volume_mount" . | nindent 12 }}
8792
ports:
8893
- containerPort: 8080
8994
name: metrics
9095
protocol: TCP
91-
- containerPort: 8081
96+
- containerPort: 9443
9297
name: api
9398
protocol: TCP
9499
- containerPort: 9440
@@ -121,7 +126,6 @@ spec:
121126
{{- $_ := set $kubeRbacProxySettings "portName" "rbac-proxy" }}
122127
{{- $_ := set $kubeRbacProxySettings "upstreams" (list
123128
(dict "upstream" "http://127.0.0.1:8080/metrics" "path" "/metrics" "name" "chart-values-controller")
124-
(dict "upstream" "http://127.0.0.1:8081/v1/chart-values" "path" "/v1/chart-values" "name" "chart-values-controller" "subresource" "api")
125129
(dict "upstream" "http://127.0.0.1:9090/metrics" "path" "/proxy/metrics" "name" "kube-api-rewriter")
126130
(dict "upstream" "http://127.0.0.1:9090/healthz" "path" "/proxy/healthz" "name" "kube-api-rewriter")
127131
(dict "upstream" "http://127.0.0.1:9090/readyz" "path" "/proxy/readyz" "name" "kube-api-rewriter")
@@ -136,4 +140,7 @@ spec:
136140
volumes:
137141
- name: cache
138142
emptyDir: {}
143+
- name: api-tls
144+
secret:
145+
secretName: chart-values-controller-tls
139146
{{- include "kube_api_rewriter.kubeconfig_volume" . | nindent 8 }}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
apiVersion: v1
3+
kind: Secret
4+
metadata:
5+
name: chart-values-controller-tls
6+
namespace: d8-{{ .Chart.Name }}
7+
{{- include "helm_lib_module_labels" (list . (dict "app" "chart-values-controller")) | nindent 2 }}
8+
type: kubernetes.io/tls
9+
data:
10+
ca.crt: {{ .Values.operatorHelm.internal.chartValuesController.cert.ca | b64enc }}
11+
tls.crt: {{ .Values.operatorHelm.internal.chartValuesController.cert.crt | b64enc }}
12+
tls.key: {{ .Values.operatorHelm.internal.chartValuesController.cert.key | b64enc }}

templates/chart-values-controller/service.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ metadata:
77
{{- include "helm_lib_module_labels" (list . (dict "app" "chart-values-controller")) | nindent 2 }}
88
spec:
99
ports:
10-
- name: rbac-proxy
10+
- name: api
1111
port: 443
12-
targetPort: rbac-proxy
12+
targetPort: api
1313
protocol: TCP
1414
selector:
1515
app: chart-values-controller

templates/rbac-to-us.yaml

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,3 @@ subjects:
2929
name: prometheus
3030
namespace: d8-monitoring
3131
{{- end }}
32-
{{- if (.Values.global.enabledModules | has "console") }}
33-
---
34-
apiVersion: rbac.authorization.k8s.io/v1
35-
kind: Role
36-
metadata:
37-
name: console-access-to-operator-helm
38-
namespace: d8-{{ .Chart.Name }}
39-
{{- include "helm_lib_module_labels" (list .) | nindent 2 }}
40-
rules:
41-
- apiGroups: ["apps"]
42-
resources: ["deployments/api"]
43-
resourceNames: ["chart-values-controller"]
44-
verbs: ["create"]
45-
---
46-
apiVersion: rbac.authorization.k8s.io/v1
47-
kind: RoleBinding
48-
metadata:
49-
name: console-access-to-operator-helm
50-
namespace: d8-{{ .Chart.Name }}
51-
{{- include "helm_lib_module_labels" (list .) | nindent 2 }}
52-
roleRef:
53-
apiGroup: rbac.authorization.k8s.io
54-
kind: Role
55-
name: console-access-to-operator-helm
56-
subjects:
57-
- kind: ServiceAccount
58-
name: backend
59-
namespace: d8-console
60-
{{- end }}

0 commit comments

Comments
 (0)