mirror of
https://github.com/tailscale/tailscale.git
synced 2025-09-21 13:41:46 +02:00
Removes ACL edits from e2e tests in favour of trying to simplify the tests and separate the actual test logic from the environment setup logic as much as possible. Also aims to fit in with the requirements that will generally be filled anyway for most devs working on the operator; in particular using tags that fit in with our documentation. Updates tailscale/corp#32085 Change-Id: I7659246e39ec0b7bcc4ec0a00c6310f25fe6fac2 Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
111 lines
2.8 KiB
Go
111 lines
2.8 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/client-go/rest"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/tstest"
|
|
)
|
|
|
|
// See [TestMain] for test requirements.
|
|
func TestProxy(t *testing.T) {
|
|
if apiClient == nil {
|
|
t.Skip("TestIngress requires TS_API_CLIENT_SECRET set")
|
|
}
|
|
|
|
cfg := config.GetConfigOrDie()
|
|
cl, err := client.New(cfg, client.Options{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create role and role binding to allow a group we'll impersonate to do stuff.
|
|
createAndCleanup(t, cl, &rbacv1.Role{
|
|
ObjectMeta: objectMeta("tailscale", "read-secrets"),
|
|
Rules: []rbacv1.PolicyRule{{
|
|
APIGroups: []string{""},
|
|
Verbs: []string{"get"},
|
|
Resources: []string{"secrets"},
|
|
}},
|
|
})
|
|
createAndCleanup(t, cl, &rbacv1.RoleBinding{
|
|
ObjectMeta: objectMeta("tailscale", "read-secrets"),
|
|
Subjects: []rbacv1.Subject{{
|
|
Kind: "Group",
|
|
Name: "ts:e2e-test-proxy",
|
|
}},
|
|
RoleRef: rbacv1.RoleRef{
|
|
Kind: "Role",
|
|
Name: "read-secrets",
|
|
},
|
|
})
|
|
|
|
// Get operator host name from kube secret.
|
|
operatorSecret := corev1.Secret{
|
|
ObjectMeta: objectMeta("tailscale", "operator"),
|
|
}
|
|
if err := get(t.Context(), cl, &operatorSecret); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Join tailnet as a client of the API server proxy.
|
|
proxyCfg := &rest.Config{
|
|
Host: fmt.Sprintf("https://%s:443", hostNameFromOperatorSecret(t, operatorSecret)),
|
|
Dial: tailnetClient.Dial,
|
|
}
|
|
proxyCl, err := client.New(proxyCfg, client.Options{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Expect success.
|
|
allowedSecret := corev1.Secret{
|
|
ObjectMeta: objectMeta("tailscale", "operator"),
|
|
}
|
|
// Wait for up to a minute the first time we use the proxy, to give it time
|
|
// to provision the TLS certs.
|
|
if err := tstest.WaitFor(time.Minute, func() error {
|
|
return get(t.Context(), proxyCl, &allowedSecret)
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Expect forbidden.
|
|
forbiddenSecret := corev1.Secret{
|
|
ObjectMeta: objectMeta("default", "operator"),
|
|
}
|
|
if err := get(t.Context(), proxyCl, &forbiddenSecret); err == nil || !apierrors.IsForbidden(err) {
|
|
t.Fatalf("expected forbidden error fetching secret from default namespace: %s", err)
|
|
}
|
|
}
|
|
|
|
func hostNameFromOperatorSecret(t *testing.T, s corev1.Secret) string {
|
|
t.Helper()
|
|
prefsBytes, ok := s.Data[string(s.Data["_current-profile"])]
|
|
if !ok {
|
|
t.Fatalf("no state in operator Secret data: %#v", s.Data)
|
|
}
|
|
|
|
prefs := ipn.Prefs{}
|
|
if err := json.Unmarshal(prefsBytes, &prefs); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if prefs.Persist == nil {
|
|
t.Fatalf("no hostname in operator Secret data: %#v", s.Data)
|
|
}
|
|
return prefs.Persist.UserProfile.LoginName
|
|
}
|