mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
fix: skip namespace check on cluster scoped rbac resources for auth reconcile (#26403)
Signed-off-by: Christopher Coco <ccoco@redhat.com>
This commit is contained in:
@@ -603,11 +603,24 @@ func (k *kubectlResourceOperations) authReconcile(ctx context.Context, obj *unst
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating kube client: %w", err)
|
||||
}
|
||||
|
||||
clusterScoped := obj.GetKind() == "ClusterRole" || obj.GetKind() == "ClusterRoleBinding"
|
||||
|
||||
// `kubectl auth reconcile` has a side effect of auto-creating namespaces if it doesn't exist.
|
||||
// See: https://github.com/kubernetes/kubernetes/issues/71185. This is behavior which we do
|
||||
// not want. We need to check if the namespace exists, before know if it is safe to run this
|
||||
// command. Skip this for dryRuns.
|
||||
if dryRunStrategy == cmdutil.DryRunNone && obj.GetNamespace() != "" {
|
||||
|
||||
// When an Argo CD Application specifies destination.namespace, that namespace
|
||||
// may be propagated even for cluster-scoped resources. Passing a namespace in
|
||||
// this case causes `kubectl auth reconcile` to fail with:
|
||||
// "namespaces <ns> not found"
|
||||
// or may trigger unintended namespace handling behavior.
|
||||
// Therefore, we skip namespace existence checks for cluster-scoped RBAC
|
||||
// resources and allow reconcile to run without a namespace.
|
||||
//
|
||||
// https://github.com/argoproj/argo-cd/issues/24833
|
||||
if dryRunStrategy == cmdutil.DryRunNone && obj.GetNamespace() != "" && !clusterScoped {
|
||||
_, err = kubeClient.CoreV1().Namespaces().Get(ctx, obj.GetNamespace(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting namespace %s: %w", obj.GetNamespace(), err)
|
||||
|
||||
75
gitops-engine/pkg/utils/kube/resource_ops_test.go
Normal file
75
gitops-engine/pkg/utils/kube/resource_ops_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
testingutils "github.com/argoproj/argo-cd/gitops-engine/pkg/utils/testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/rest"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func TestAuthReconcileWithMissingNamespace(t *testing.T) {
|
||||
namespace := "test-ns"
|
||||
fakeBearer := "fake-bearer"
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
status := &metav1.Status{
|
||||
Status: "Failure",
|
||||
Message: fmt.Sprintf("namespace \"%s\" not found", namespace),
|
||||
Reason: metav1.StatusReasonNotFound,
|
||||
Code: http.StatusNotFound,
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
json.NewEncoder(w).Encode(status)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
kubeConfigFlags := genericclioptions.NewConfigFlags(true)
|
||||
kubeConfigFlags.Namespace = &namespace
|
||||
kubeConfigFlags.APIServer = &server.URL
|
||||
kubeConfigFlags.BearerToken = &fakeBearer
|
||||
matchFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
|
||||
fact := cmdutil.NewFactory(matchFlags)
|
||||
|
||||
config := &rest.Config{Host: server.URL}
|
||||
k := &kubectlResourceOperations{
|
||||
config: config,
|
||||
fact: fact,
|
||||
}
|
||||
|
||||
role := testingutils.NewRole()
|
||||
role.SetNamespace(namespace)
|
||||
|
||||
_, err := k.authReconcile(context.Background(), role, "/dev/null", cmdutil.DryRunNone)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, errors.IsNotFound(err), "returned error wasn't not found")
|
||||
|
||||
roleBinding := testingutils.NewRoleBinding()
|
||||
roleBinding.SetNamespace(namespace)
|
||||
|
||||
_, err = k.authReconcile(context.Background(), roleBinding, "/dev/null", cmdutil.DryRunNone)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, errors.IsNotFound(err), "returned error wasn't not found")
|
||||
|
||||
clusterRole := testingutils.NewClusterRole()
|
||||
clusterRole.SetNamespace(namespace)
|
||||
|
||||
_, err = k.authReconcile(context.Background(), clusterRole, "/dev/null", cmdutil.DryRunNone)
|
||||
assert.NoError(t, err)
|
||||
|
||||
clusterRoleBinding := testingutils.NewClusterRoleBinding()
|
||||
clusterRoleBinding.SetNamespace(namespace)
|
||||
|
||||
_, err = k.authReconcile(context.Background(), clusterRoleBinding, "/dev/null", cmdutil.DryRunNone)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -97,3 +97,55 @@ metadata:
|
||||
name: testnamespace
|
||||
spec:`)
|
||||
}
|
||||
|
||||
func NewRole() *unstructured.Unstructured {
|
||||
return Unstructured(`apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: my-role
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "watch", "list"]`)
|
||||
}
|
||||
|
||||
func NewRoleBinding() *unstructured.Unstructured {
|
||||
return Unstructured(`apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: my-role-binding
|
||||
subjects:
|
||||
- kind: User
|
||||
name: user
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: my-role
|
||||
apiGroup: rbac.authorization.k8s.io`)
|
||||
}
|
||||
|
||||
func NewClusterRole() *unstructured.Unstructured {
|
||||
return Unstructured(`apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: my-cluster-role
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "watch", "list"]`)
|
||||
}
|
||||
|
||||
func NewClusterRoleBinding() *unstructured.Unstructured {
|
||||
return Unstructured(`apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: my-cluster-role-binding
|
||||
subjects:
|
||||
- kind: Group
|
||||
name: group
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: my-cluster-role
|
||||
apiGroup: rbac.authorization.k8s.io`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user