kopf icon indicating copy to clipboard operation
kopf copied to clipboard

kopf run --namespace=mynamespace not considered

Open Olgoetz opened this issue 3 months ago • 2 comments

Long story short

I run

kopf run --namespace=mcm-obs-prod-ago-fr aws_secrets_operator.py

as I am not allowed to act cluster-wide. I had the assumption when adding --namespace=xxx kopf would only act within this namespace. But I receive a lot of

[2025-09-10 18:10:37,647] kopf._core.reactor.o [ERROR   ] Request attempt #1/9 failed; will retry: GET https://xxxxxxxxxxx6443/api/v1/namespaces -> APIForbiddenError('namespaces is forbidden: User "xxxxxxxxxx" cannot list resource "namespaces" in API group "" at the cluster scope', {'kind': 'Status', 'apiVersion': 'v1', 'metadata': {}, 'status': 'Failure', 'message': 'namespaces is forbidden: User "xxxxxxxxxxxxxx" cannot list resource "namespaces" in API group "" at the cluster scope', 'reason': 'Forbidden', 'details': {'kind': 'namespaces'}, 'code': 403})

Kopf version

1.38.0

Kubernetes version

OpenShift 4.16.40

Python version

3.9.21

Code

import hashlib
import kopf
import kubernetes
import logging
def compute_secret_hash(secret):
    data = secret.data or {}
    serialized = "".join(f"{k}:{v}" for k, v in sorted(data.items()))
    return hashlib.sha256(serialized.encode()).hexdigest()

def load_service_account_token():
    configuration = kubernetes.client.Configuration()
    configuration.host = "xxxxxxxxxxxxx"  # from oc whoami
    configuration.verify_ssl = False
   # configuration.ssl_ca_cert = "ca.crt"  # path to downloaded CA
    with open("sa-token.txt") as f:
        token = f.read().strip()
    configuration.api_key = {"authorization": "Bearer " + token}
    kubernetes.client.Configuration.set_default(configuration)


@kopf.on.startup()
def configure(settings: kopf.OperatorSettings, **_):
    #settings.posting.level = 'INFO'
    settings.peering.standalone = True
    load_service_account_token()
    #kubernetes.config.load_incluster_config()


@kopf.on.update('v1', 'secrets')
def secret_changed(name, namespace, body, **kwargs):
    logging.info(f"Secret {name} in namespace {namespace} has changed.")

Logs

2025-09-10 18:10:37,587] kopf._core.reactor.r [DEBUG   ] Starting Kopf 1.38.0.
[2025-09-10 18:10:37,587] kopf.activities.star [DEBUG   ] Activity 'configure' is invoked.
[2025-09-10 18:10:37,588] kopf.activities.star [INFO    ] Activity 'configure' succeeded.
[2025-09-10 18:10:37,589] kopf._core.engines.a [INFO    ] Initial authentication has been initiated.
[2025-09-10 18:10:37,589] kopf.activities.auth [DEBUG   ] Activity 'login_via_client' is invoked.
[2025-09-10 18:10:37,604] kopf.activities.auth [DEBUG   ] Client is configured via kubeconfig file.
[2025-09-10 18:10:37,604] kopf.activities.auth [INFO    ] Activity 'login_via_client' succeeded.
[2025-09-10 18:10:37,604] kopf._core.engines.a [INFO    ] Initial authentication has finished.
[2025-09-10 18:10:37,645] kopf._cogs.clients.w [DEBUG   ] Starting the watch-stream for customresourcedefinitions.v1.apiextensions.k8s.io cluster-wide.
[2025-09-10 18:10:37,647] kopf._core.reactor.o [ERROR   ] Request attempt #1/9 failed; will retry: GET https://xxxxxxxxxxx6443/api/v1/namespaces -> APIForbiddenError('namespaces is forbidden: User "xxxxxxxxxx" cannot list resource "namespaces" in API group "" at the cluster scope', {'kind': 'Status', 'apiVersion': 'v1', 'metadata': {}, 'status': 'Failure', 'message': 'namespaces is forbidden: User "xxxxxxxxxxxxxx" cannot list resource "namespaces" in API group "" at the cluster scope', 'reason': 'Forbidden', 'details': {'kind': 'namespaces'}, 'code': 403})

Additional information

No response

Olgoetz avatar Sep 10 '25 18:09 Olgoetz

Hello. Thanks for the report.

That is indeed the behaviour as designed — to scan all the existing namespaces to check if this one exists (and to resolve the ? & * globs, if any).

It seems, you are forbidden from even listing the namespaces. In that case, Kopf will try it a few times, and will fallback to using the specified namespace "as written".

There is a setting settings.scanning.disabled, which should be set to True to prevent such scanning and to fallback to the "as written" approach instantly. Source code & docstrings:

  • The setting: https://github.com/nolar/kopf/blob/1.38.0/kopf/_cogs/configs/configuration.py#L238-L254
  • The usage: https://github.com/nolar/kopf/blob/1.38.0/kopf/_core/reactor/observation.py#L50 (and a few other places in the same module).

For other readers: please mind that the setting disables BOTH namespace- & resource-scanning, so your resource specifications must be very precise, i.e. including the group, version, and the plural of the resource — not its kinds, aliases, or partial names. In this issue, the …('v1', 'secrets') is sufficiently precise. The full resource specs are given as an example here:

  • https://kopf.readthedocs.io/en/stable/resources/#resource-specification

Nevertheless, please keep the issue open — it is indeed a couple of little bugs:

  • This setting is completely missing from the docs for some reason — I probably overlooked it.
  • The "Access Forbidden" should not be retried in these cases (I thought we already solved this particular issue, hm…).

I will take a look at this improvement some time later — unless it is a blocker now.

Related: #901

nolar avatar Sep 10 '25 19:09 nolar

Hi,

that setting fixed it, thank you :) In large enterprises (I work for one) you usually do not get cluster-level permissions unless you part of the team that manages the cluster. I am a consumer of the provided cluster and thus, I only have permissions on namespace level. Technical inter-namespace communication must be facilitated via service accounts

Olgoetz avatar Sep 11 '25 10:09 Olgoetz