Skip to content

Commit c96efe1

Browse files
committed
add very basic tests for the webhook functionality
On-behalf-of: @SAP [email protected]
1 parent a92ee8c commit c96efe1

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: Config
3+
clusters:
4+
- name: httest
5+
cluster:
6+
certificate-authority: .httest/ca.crt
7+
server: https://localhost:8080/
8+
current-context: webhook
9+
contexts:
10+
- name: webhook
11+
context:
12+
cluster: httest
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
Copyright 2024 The KCP Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package authorizer
18+
19+
import (
20+
"context"
21+
"os/exec"
22+
"testing"
23+
"time"
24+
25+
kcpkubernetesclientset "github.com/kcp-dev/client-go/kubernetes"
26+
"github.com/kcp-dev/logicalcluster/v3"
27+
"github.com/stretchr/testify/require"
28+
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
kubernetesscheme "k8s.io/client-go/kubernetes/scheme"
31+
"k8s.io/client-go/rest"
32+
33+
kcpclientset "github.com/kcp-dev/kcp/sdk/client/clientset/versioned/cluster"
34+
"github.com/kcp-dev/kcp/test/e2e/framework"
35+
)
36+
37+
func TestWebhook(t *testing.T) {
38+
framework.Suite(t, "control-plane")
39+
40+
ctx, cancelFunc := context.WithCancel(context.Background())
41+
t.Cleanup(cancelFunc)
42+
43+
// start a webhook that allows kcp to boot up
44+
webhookStop := runWebhook(ctx, t, "kubernetes:authz:allow")
45+
t.Cleanup(webhookStop)
46+
47+
server := framework.PrivateKcpServer(t, framework.WithCustomArguments(
48+
"--authorization-webhook-config-file",
49+
"webhook.kubeconfig",
50+
))
51+
52+
// create clients
53+
kcpConfig := server.BaseConfig(t)
54+
kubeClusterClient, err := kcpkubernetesclientset.NewForConfig(kcpConfig)
55+
require.NoError(t, err, "failed to construct client for server")
56+
kcpClusterClient, err := kcpclientset.NewForConfig(kcpConfig)
57+
require.NoError(t, err, "failed to construct client for server")
58+
59+
t.Log("Admin should be allowed to list Workspaces.")
60+
_, err = kcpClusterClient.Cluster(logicalcluster.NewPath("root")).TenancyV1alpha1().Workspaces().List(ctx, metav1.ListOptions{})
61+
require.NoError(t, err)
62+
63+
// stop the webhook and switch to a deny policy
64+
webhookStop()
65+
66+
webhookStop = runWebhook(ctx, t, "kubernetes:authz:deny")
67+
t.Cleanup(webhookStop)
68+
69+
t.Log("Admin should not be allowed to list ConfigMaps.")
70+
_, err = kubeClusterClient.Cluster(logicalcluster.NewPath("root")).CoreV1().ConfigMaps("default").List(ctx, metav1.ListOptions{})
71+
require.Error(t, err)
72+
73+
// access to health endpoints should still be granted based on --always-allow-paths,
74+
// even if the webhook rejects the request
75+
rootShardCfg := server.RootShardSystemMasterBaseConfig(t)
76+
if rootShardCfg.NegotiatedSerializer == nil {
77+
rootShardCfg.NegotiatedSerializer = kubernetesscheme.Codecs.WithoutConversion()
78+
}
79+
80+
// Ensure the request is unauthenticated, as Kubernetes' webhook authorizer is wrapped
81+
// in a reloadable authorizer that also always injects a privilegedGroup authorizer
82+
// that lets system:masters users in.
83+
rootShardCfg.BearerToken = ""
84+
85+
restClient, err := rest.UnversionedRESTClientFor(rootShardCfg)
86+
require.NoError(t, err)
87+
88+
for _, endpoint := range []string{"/livez", "/readyz"} {
89+
req := rest.NewRequest(restClient).RequestURI(endpoint)
90+
t.Logf("%s should still be accessible.", req.URL().String())
91+
_, err := req.Do(ctx).Raw()
92+
require.NoError(t, err)
93+
}
94+
}
95+
96+
func runWebhook(ctx context.Context, t *testing.T, response string) context.CancelFunc {
97+
args := []string{
98+
"--tls",
99+
"--response", response,
100+
}
101+
102+
t.Logf("Starting webhook with %s policy...", response)
103+
104+
ctx, cancel := context.WithCancel(ctx)
105+
106+
cmd := exec.CommandContext(ctx, "httest", args...)
107+
if err := cmd.Start(); err != nil {
108+
cancel()
109+
t.Fatalf("Failed to start webhook: %v", err)
110+
}
111+
112+
// give httest a moment to boot up
113+
time.Sleep(2 * time.Second)
114+
115+
return func() {
116+
t.Log("Stopping webhook...")
117+
cancel()
118+
// give it some time to shutdown
119+
time.Sleep(2 * time.Second)
120+
}
121+
}

0 commit comments

Comments
 (0)