Skip to content

Commit 5d0a511

Browse files
authored
Let front-proxy verify against non-fp client CA (#178)
* Add reconciler for a merged front proxy client CA + client CA bundle Signed-off-by: Nelo-T. Wallus <red.brush9525@fastmail.com> Signed-off-by: Nelo-T. Wallus <n.wallus@sap.com> * Deploy both external and internal front proxy with merged client CA bundle Signed-off-by: Nelo-T. Wallus <red.brush9525@fastmail.com> Signed-off-by: Nelo-T. Wallus <n.wallus@sap.com> --------- Signed-off-by: Nelo-T. Wallus <red.brush9525@fastmail.com> Signed-off-by: Nelo-T. Wallus <n.wallus@sap.com>
1 parent 8294c4d commit 5d0a511

File tree

7 files changed

+115
-17
lines changed

7 files changed

+115
-17
lines changed

internal/controller/frontproxy/controller_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,31 @@ func TestReconciling(t *testing.T) {
7979

8080
for _, testcase := range testcases {
8181
t.Run(testcase.name, func(t *testing.T) {
82+
// The merged client CA reconciler fetches FrontProxyClientCA and ClientCA.
83+
frontProxyClientCASecret := &corev1.Secret{
84+
ObjectMeta: metav1.ObjectMeta{
85+
Name: testcase.rootShard.Name + "-front-proxy-client-ca",
86+
Namespace: namespace,
87+
},
88+
Data: map[string][]byte{
89+
"tls.crt": []byte("front-proxy-client-ca-cert"),
90+
},
91+
}
92+
clientCASecret := &corev1.Secret{
93+
ObjectMeta: metav1.ObjectMeta{
94+
Name: testcase.rootShard.Name + "-client-ca",
95+
Namespace: namespace,
96+
},
97+
Data: map[string][]byte{
98+
"tls.crt": []byte("client-ca-cert"),
99+
},
100+
}
101+
82102
client := ctrlruntimefakeclient.
83103
NewClientBuilder().
84104
WithScheme(scheme).
85105
WithStatusSubresource(testcase.rootShard, testcase.frontProxy).
86-
WithObjects(testcase.rootShard, testcase.frontProxy).
106+
WithObjects(testcase.rootShard, testcase.frontProxy, frontProxyClientCASecret, clientCASecret).
87107
Build()
88108

89109
ctx := context.Background()

internal/controller/rootshard/controller_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/stretchr/testify/require"
2424

25+
corev1 "k8s.io/api/core/v1"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2627
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
2728
ctrlruntimefakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -64,11 +65,31 @@ func TestReconciling(t *testing.T) {
6465

6566
for _, testcase := range testcases {
6667
t.Run(testcase.name, func(t *testing.T) {
68+
// The merged client CA reconciler fetches FrontProxyClientCA and ClientCA.
69+
frontProxyClientCASecret := &corev1.Secret{
70+
ObjectMeta: metav1.ObjectMeta{
71+
Name: testcase.rootShard.Name + "-front-proxy-client-ca",
72+
Namespace: namespace,
73+
},
74+
Data: map[string][]byte{
75+
"tls.crt": []byte("front-proxy-client-ca-cert"),
76+
},
77+
}
78+
clientCASecret := &corev1.Secret{
79+
ObjectMeta: metav1.ObjectMeta{
80+
Name: testcase.rootShard.Name + "-client-ca",
81+
Namespace: namespace,
82+
},
83+
Data: map[string][]byte{
84+
"tls.crt": []byte("client-ca-cert"),
85+
},
86+
}
87+
6788
client := ctrlruntimefakeclient.
6889
NewClientBuilder().
6990
WithScheme(scheme).
7091
WithStatusSubresource(testcase.rootShard).
71-
WithObjects(testcase.rootShard).
92+
WithObjects(testcase.rootShard, frontProxyClientCASecret, clientCASecret).
7293
Build()
7394

7495
ctx := context.Background()

internal/resources/frontproxy/ca_bundle.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package frontproxy
1818

1919
import (
20+
"bytes"
2021
"context"
2122
"fmt"
2223

@@ -30,6 +31,66 @@ import (
3031
operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1"
3132
)
3233

34+
func (r *reconciler) mergedClientCASecretName() string {
35+
if r.frontProxy != nil {
36+
return fmt.Sprintf("%s-merged-client-ca", r.frontProxy.Name)
37+
}
38+
return fmt.Sprintf("%s-proxy-merged-client-ca", r.rootShard.Name)
39+
}
40+
41+
// mergedClientCASecretReconciler creates a single secret with the
42+
// FrontProxyClientCA and shard ClientCA concatenated so that the front
43+
// proxy accepts clients signed by either CA.
44+
func (r *reconciler) mergedClientCASecretReconciler(ctx context.Context, kubeClient ctrlruntimeclient.Client) k8creconciling.NamedSecretReconcilerFactory {
45+
getCA := func(caType operatorv1alpha1.CA) ([]byte, error) {
46+
caSecret := &corev1.Secret{}
47+
caSecretName := resources.GetRootShardCAName(r.rootShard, caType)
48+
if err := kubeClient.Get(ctx, types.NamespacedName{
49+
Namespace: r.rootShard.Namespace,
50+
Name: caSecretName,
51+
}, caSecret); err != nil {
52+
return nil, fmt.Errorf("failed to get %s secret %s: %w", caType, caSecretName, err)
53+
}
54+
55+
cert, ok := caSecret.Data["tls.crt"]
56+
if !ok {
57+
return nil, fmt.Errorf("%s secret %s missing tls.crt", caType, caSecretName)
58+
}
59+
return cert, nil
60+
}
61+
62+
return func() (string, k8creconciling.SecretReconciler) {
63+
return r.mergedClientCASecretName(), func(secret *corev1.Secret) (*corev1.Secret, error) {
64+
if secret.Data == nil {
65+
secret.Data = make(map[string][]byte)
66+
}
67+
68+
fpClientCA, err := getCA(operatorv1alpha1.FrontProxyClientCA)
69+
if err != nil {
70+
return nil, fmt.Errorf("error getting front proxy client ca: %w", err)
71+
}
72+
73+
clientCA, err := getCA(operatorv1alpha1.ClientCA)
74+
if err != nil {
75+
return nil, fmt.Errorf("error getting client ca: %w", err)
76+
}
77+
78+
secret.Data["tls.crt"] = bytes.Join([][]byte{fpClientCA, clientCA}, []byte{'\n'})
79+
80+
if secret.Labels == nil {
81+
secret.Labels = make(map[string]string)
82+
}
83+
if r.frontProxy != nil {
84+
secret.Labels[resources.FrontProxyLabel] = r.frontProxy.Name
85+
} else {
86+
secret.Labels[resources.RootShardLabel] = r.rootShard.Name
87+
}
88+
89+
return secret, nil
90+
}
91+
}
92+
}
93+
3394
func (r *reconciler) mergedCABundleSecretName() string {
3495
// Validate whether called for frontProxy or rootShardFrontProxy
3596
if r.frontProxy != nil {

internal/resources/frontproxy/deployment.go

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -176,16 +176,9 @@ func (r *reconciler) deploymentReconciler() reconciling.NamedDeploymentReconcile
176176
mountSecret(r.mergedCABundleSecretName(), getCAMountPath(operatorv1alpha1.CABundleCA), true)
177177
}
178178

179-
// Regular front-proxies use a dedicated client CA. However the internal rootshard proxy
180-
// uses the internal client CA instead to make it easier for the kcp-operator to just use
181-
// a single certificate to access all components.
182-
if r.frontProxy != nil {
183-
// rootshard frontproxy client ca
184-
mountSecret(resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.FrontProxyClientCA), frontProxyBasepath+"/client-ca", true)
185-
} else {
186-
// kcp client ca
187-
mountSecret(resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.ClientCA), kcpBasepath+"/tls/client-ca", true)
188-
}
179+
// Mount the merged client CA (FrontProxyClientCA + ClientCA) so
180+
// that clients signed by either CA are accepted.
181+
mountSecret(r.mergedClientCASecretName(), frontProxyBasepath+"/client-ca", true)
189182

190183
// front-proxy config
191184
{
@@ -269,15 +262,13 @@ var defaultArgs = []string{
269262
func (r *reconciler) getArgs() []string {
270263
args := defaultArgs
271264

265+
args = append(args, fmt.Sprintf("--client-ca-file=%s/client-ca/tls.crt", frontProxyBasepath))
266+
272267
// rootshard proxy mode
273268
if r.frontProxy == nil {
274-
args = append(args, fmt.Sprintf("--client-ca-file=%s/tls/client-ca/tls.crt", kcpBasepath))
275269
return args
276270
}
277271

278-
// regular front-proxy
279-
args = append(args, fmt.Sprintf("--client-ca-file=%s/client-ca/tls.crt", frontProxyBasepath))
280-
281272
if auth := r.frontProxy.Spec.Auth; auth != nil {
282273
if auth.DropGroups != nil {
283274
args = append(args, fmt.Sprintf("--authentication-drop-groups=%q", strings.Join(auth.DropGroups, ",")))

internal/resources/frontproxy/deployment_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func TestDeploymentReconciler(t *testing.T) {
8282
resources.GetFrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.ServerCertificate),
8383
resources.GetFrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.RequestHeaderClientCertificate),
8484
resources.GetFrontProxyConfigName(&operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}),
85-
resources.GetRootShardCAName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.FrontProxyClientCA),
85+
resources.GetMergedClientCAName("test-front-proxy"),
8686
resources.GetRootShardCAName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.RootCA),
8787
}
8888

internal/resources/frontproxy/reconciler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func (r *reconciler) Reconcile(ctx context.Context, client ctrlruntimeclient.Cli
8888

8989
secretReconcilers := []k8creconciling.NamedSecretReconcilerFactory{
9090
r.dynamicKubeconfigSecretReconciler(),
91+
r.mergedClientCASecretReconciler(ctx, client),
9192
}
9293

9394
if r.getCABundleSecretRef() != nil {

internal/resources/resources.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,7 @@ func GetRootShardProxyServiceName(r *operatorv1alpha1.RootShard) string {
246246
func GetBundleName(ownerName string) string {
247247
return fmt.Sprintf("%s-bundle", ownerName)
248248
}
249+
250+
func GetMergedClientCAName(ownerName string) string {
251+
return fmt.Sprintf("%s-merged-client-ca", ownerName)
252+
}

0 commit comments

Comments
 (0)