cmd/k8s-operator: workload identity support for multi-tailnet (#20016)
This commit modifies the reconciler for the `Tailnet` custom resource to allow referenced secrets to specify an `audience` field. If a referenced secret contains both an `audience` and `client_id` we assume the user's intention is to use workload identity. In that case, we configure the tailscale API client to authenticate using the Kubernetes token request API against the operator's service account. This requires the operator to be aware of its own service account name. A small change has also been made to the messages added to the `Tailnet` CRD's status field in the even that it is missing scopes to make it clearer that certain scopes may not be applied. Closes: #19090 Updates: #19471 Signed-off-by: David Bond <davidsbond93@gmail.com>
This commit is contained in:
@@ -141,7 +141,7 @@ func TestReconciler_Reconcile(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "invalid-status-missing-client-secret",
|
||||
Name: "invalid-status-missing-client-secret-and-audience",
|
||||
Request: reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: "test",
|
||||
@@ -171,7 +171,7 @@ func TestReconciler_Reconcile(t *testing.T) {
|
||||
Type: string(tsapi.TailnetReady),
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: tailnet.ReasonInvalidSecret,
|
||||
Message: `Secret "test" is missing the client_secret field`,
|
||||
Message: `Secret "test" must contain either a client_secret or an audience field`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -210,7 +210,7 @@ func TestReconciler_Reconcile(t *testing.T) {
|
||||
Type: string(tsapi.TailnetReady),
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: tailnet.ReasonInvalidOAuth,
|
||||
Message: `failed to list devices: EOF`,
|
||||
Message: `failed to list devices: EOF (client may be missing the devices scope)`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -249,7 +249,7 @@ func TestReconciler_Reconcile(t *testing.T) {
|
||||
Type: string(tsapi.TailnetReady),
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: tailnet.ReasonInvalidOAuth,
|
||||
Message: `failed to list tailscale services: EOF`,
|
||||
Message: `failed to list tailscale services: EOF (client may be missing the services scope)`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -288,7 +288,7 @@ func TestReconciler_Reconcile(t *testing.T) {
|
||||
Type: string(tsapi.TailnetReady),
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: tailnet.ReasonInvalidOAuth,
|
||||
Message: `failed to list auth keys: EOF`,
|
||||
Message: `failed to list auth keys: EOF (client may be missing the keys scope)`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -331,6 +331,45 @@ func TestReconciler_Reconcile(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ready-workload-identity",
|
||||
Request: reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: "default",
|
||||
},
|
||||
},
|
||||
Tailnet: &tsapi.Tailnet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
},
|
||||
Spec: tsapi.TailnetSpec{
|
||||
Credentials: tsapi.TailnetCredentials{
|
||||
SecretName: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
Secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "tailscale",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"client_id": []byte("test"),
|
||||
"audience": []byte("https://tailscale.com"),
|
||||
},
|
||||
},
|
||||
ClientFunc: func(_ *tsapi.Tailnet, _ *corev1.Secret) tsclient.Client {
|
||||
return &MockTailnetClient{}
|
||||
},
|
||||
ExpectedConditions: []metav1.Condition{
|
||||
{
|
||||
Type: string(tsapi.TailnetReady),
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: tailnet.ReasonValid,
|
||||
Message: tailnet.ReasonValid,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
|
||||
Reference in New Issue
Block a user