You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Documentation and issues setting up (external) SSO via Keycloak IdP #271
error: error validating "STDIN": error validating data: failed to download openapi: Get "http://localhost:8080/openapi/v2?timeout=32s": dial tcp [::1]:8080: connect: connection refused; if you choose to ignore these errors, turn validation off with --validate=false
error: kubectl apply -f - failed with exit code 1: error: error validating "STDIN": error validating data: failed to download openapi: Get "http://localhost:8080/openapi/v2?timeout=32s": dial tcp [::1]:8080: connect: connection refused; if you choose to ignore these errors, turn validation off with --validate=false
ops: Failed to run task "keycloak": exit status 1
Not really sure what to do here.
When fixing the state and getting it running, I'll gladly offer to do a documentation page on this!
Thanks for reporting this. The error suggests that kubectl, invoked by ops config sso, is not picking up a valid Kubernetes configuration. It is trying to reach localhost:8080, so the command fails while applying the SSO configuration, before we can assess the Keycloak integration.
Could you run these checks in the same terminal, as the same user you use for ops config sso?
Once that command successfully connects to the intended cluster, retry ops config sso keycloak in the same terminal. The kubeconfig must provide sufficient permissions to apply the SSO configuration.
I would avoid --validate=false here: disabling manifest validation does not fix the underlying cluster connection.
Please share the check results and how your cluster was installed, redacting any sensitive details. There is no need to post your full kubeconfig, tokens, certificates, or client secret.
Thank you @miki3421 I'm working on my local machine, while the host itself is remote.
Here are the outputs from my local machine:
mdescher@marcos-mbp-2025 ~ [1]> printf 'KUBECONFIG=%s\n' "$KUBECONFIG"
kubectl config current-context
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'
printf '\n'
kubectl cluster-info
KUBECONFIG=
error: current-context is not set
error: current-context must exist in order to minify
E1014 18:51:21.588198 35936 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E1014 18:51:21.588592 35936 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E1014 18:51:21.589665 35936 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E1014 18:51:21.589986 35936 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E1014 18:51:21.591071 35936 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
The connection to the server localhost:8080 was refused - did you specify the right host or port?
I don't know the location of my kubeconfig, the .kube directory in my $HOME does however not have any directory besides cache.
I installed my cluster as documented here #258 from my local notebook via ssh to an external debian 13 machine.
Thanks @col-panic, this clarifies the problem. Your local kubectl has no active context, but the kubeconfig may already have been downloaded by OPS into its own directory rather than ~/.kube/config.
Please run:
ops -info
Look for OPS_TMP. With the default configuration, check:
ls -l "$HOME/.ops/tmp/kubeconfig"
kubectl --kubeconfig="$HOME/.ops/tmp/kubeconfig" config current-context
kubectl --kubeconfig="$HOME/.ops/tmp/kubeconfig" cluster-info
If OPS_TMP shows a different directory, use that path instead.
If the file is missing, OPS provides a command to retrieve it from your K3s server:
Use the same server and SSH user used during installation. This retrieves the configuration; it does not reinstall the cluster. Then repeat the connection check.
Once cluster-info succeeds, explicitly pass the same kubeconfig when running your original SSO command:
If the connection check now reports a timeout or certificate error, please share that error with sensitive details redacted. That would identify a separate connectivity or TLS issue. There is no need to share the kubeconfig itself.
mdescher@marcos-mbp-2025 ~> ops -info
OPS & OPS_CMD: /Users/mdescher/.local/bin/ops
OPS_VERSION: 0.9.0-incubating+26i11g51-snapshot
OPS_BRANCH: 0.9.0
OPS_BIN: /Users/mdescher/.ops/darwin-arm64/bin
OPS_TMP: /Users/mdescher/.ops/tmp
OPS_HOME: /Users/mdescher/.ops
OPS_ROOT: /Users/mdescher/.ops/0.9.0/oplugins
OPS_REPO: https://github.com/apache/openserverless-task
OPS_PWD: /Users/mdescher
OPS_TASKS: 7981be97d0b8df4fa7453ff493b3cb65c4117ebf
OPS_ROOT_PLUGIN: /Users/mdescher
mdescher@marcos-mbp-2025 ~> ls -l /Users/mdescher/.ops/tmp
total 16
-rw------- 1 mdescher staff 2959 29 Sep. 12:55 k3s-openserverless.something.com.kubeconfig
-rw------- 1 mdescher staff 2959 29 Sep. 12:55 kubeconfig
mdescher@marcos-mbp-2025 ~> kubectl --kubeconfig=/Users/mdescher/.ops/tmp/kubeconfig config current-context
default
mdescher@marcos-mbp-2025 ~ [1]> kubectl --kubeconfig=/Users/mdescher/.ops/tmp/kubeconfig cluster-info
Kubernetes control plane is running at https://openserverless.something.com:6443
CoreDNS is running at https://openserverless.something.com:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://openserverless.something.com:6443/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
mdescher@marcos-mbp-2025 ~> env KUBECONFIG="/Users/mdescher/.ops/tmp/kubeconfig" ops config sso keycloak --enable --issuer-url=https://keycloak.something.com/realms/watershed --jwks-url=https://keycloak.something.com/realms/watershed/protocol/openid-connect/certs --required-group=admin --client-id=openserverless --client-secret=some-secret
configmap/openserverless-sso-config created
secret/openserverless-sso-secret created
statefulset.apps/openserverless-system-api patched
statefulset.apps/openserverless-system-api restarted
Waiting for partitioned roll out to finish: 0 out of 1 new pods have been updated...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
partitioned roll out complete: 1 new pods have been updated...
SSO configuration applied to admin-api.
ConfigMap: openserverless/openserverless-sso-config
Secret: openserverless/openserverless-sso-secret
so it seems the only hindering part was that somehow my config file is in a non-expected location?!
Thanks @col-panic. I checked the implementation, including the task revision shown in your ops -info, and also saw your follow-up question on PR #96.
The kubeconfig was already present in the OPS directory. In your case, the SSO command was not using it automatically; explicitly setting KUBECONFIG resolved that connection issue. Your output confirms that the SSO configuration was applied successfully, although an actual SSO login still needs to be verified.
Regarding groups versus roles, the distinction is:
The current configuration command requires --required-group.
The backend checks whether that value appears in the claim selected by --groups-claim, which defaults to groups.
That check does not distinguish a Keycloak group from a role mapped into a token claim. It is an admission check, not a mapping of Keycloak roles to OPS administrative permissions. In particular, the value admin does not itself grant an OPS administrator role.
The guide uses a Group Membership mapper because it produces the claim expected by the default configuration. That should not be presented as a general requirement of OIDC or as a recommendation against realm/client roles.
If you prefer roles, the current validator can consume a top-level claim containing the permitted role names, populated by an appropriate Keycloak mapper. For example, with this claim in the access token:
However, the validator currently performs a direct top-level claim lookup. It does not traverse nested paths such as realm_access.roles or resource_access.openserverless.roles. Passing those strings to --groups-claim would not read the nested roles.
There is also a distinction between the backend and the configuration command: the backend skips this membership check if OIDC_REQUIRED_GROUP is unset, but the current command does not expose that as an optional configuration. So simply omitting --required-group is not supported by this command.
For your update question: you do not need to delete or disable SSO first. Run the same complete ops config sso keycloak --enable ... command again, changing --required-group to the new value and retaining your other options and explicit KUBECONFIG. It reapplies the configuration and restarts the admin-api by default.
Please include your existing --client-secret again when doing this. In the task revision you are using, omitting it switches the configuration to public-client mode and deletes the managed SSO Secret; it does not retain the previous secret. Likewise, retain any non-default audience or claim options.
We will update the guide in PR #96 to clarify the distinction between Keycloak groups and role claims, the current limitation on nested claims, and how to update an existing SSO configuration—including the need to pass the client secret and any non-default options again.
thanks for all the input, but somehow I can't get this to work. I created a separate docker container based on debian
and installed the ops cli plus the required tools (docu should mention to have tools like zip, curl and ssh already available,
else you will get output like failed preflight check: curl not installed but is required)
Running like this
root@7b39c5551b94:~/openserverless-cli_0.9.0-incubating+26i11g51-snapshot_linux_arm64# OPS_APIHOST=https://openserverless.somehost.com ./ops -login --sso-flow password --sso-username mdescher
Using the default user: openserverless
Logging in https://openserverless.somehost.com as openserverless
Enter Password:
does not work, it's also showing the wrong username?! This does not match documentation.
My real target is to be able to have an sso login for a non-existing user to auto-create ops -wsk namespace etc. ATM I'm not really sure, if this is the right way at all?!
I thought of something like this
User musterfrau (does exist in sso, but not in openserverless)
OPS_APIHOST=https://openserverless.somehost.com./ops -login --sso-flow password --sso-username musterfra
(Authenticates via keycloak)
ops -wsk action list
(now works with empty list, but namespace etc all created)
Your intended flow—an existing Keycloak user logging in for the first time and receiving an OpenServerless namespace—is supported by the SSO provisioning implementation. However, the output you posted shows that the CLI is taking the traditional username/password login path.
I checked the source for your exact CLI tag, v0.9.0-incubating+26i11g51-snapshot (d3f767a). In that version, the CLI must see SSO_ENABLED=true, from its local configuration or environment. --sso-flow password and --sso-username select options within SSO mode; they do not enable it. Without that switch, the CLI uses its default openserverless user, which explains the output. See the login branch selection.
The earlier ops config sso keycloak --enable ... command saves SSO settings in the local OPS profile where it runs, as well as configuring the server. A new Docker container does not inherit that local profile. The task revision reported by your ops -info explicitly saves SSO_ENABLED=true locally.
In the new container, enable SSO locally and select the password flow you were trying to use, then run the normal login command:
./ops -config SSO_ENABLED=true OPS_SSO_LOGIN_FLOW=password
./ops ide login mdescher https://openserverless.somehost.com
# After login succeeds:
./ops -wsk action list
Once the local settings are saved, you can also run ./ops ide login interactively. OPS_SSO_LOGIN_FLOW=password is the configuration/environment equivalent of the embedded tool's --sso-flow password flag; see the flow selection. The password selection is specific to this example; it is not required for every SSO flow.
The login should now print with backend-managed OIDC password grant and prompt for Enter SSO Password:. Enter the Keycloak user's password. This local configuration command does not reconfigure the cluster or require the Keycloak client secret. Keycloak must allow the password grant for the configured client.
For automatic provisioning, the backend validates the token and required membership, derives the namespace from the configured claims, and, if it is missing, creates a WhiskUser and waits for the operator to provision it. This requires SSO_AUTOPROVISION_ON_LOGIN=true, an email claim, and working operator reconciliation. Your SSO configuration task sets that server flag to true. See also the backend provisioning implementation.
The CLI saves the returned credentials for subsequent ops -wsk commands, and ops ide login saves the workspace settings. In the password example above, mdescher is both the IdP username and the requested OPS namespace. This assumes that the configured claims resolve to that namespace. If your namespace mapping produces a different name, the lower-level ops -login --sso-flow password --sso-username ... form can separate the IdP username from the requested namespace (or omit the requested namespace in a clean profile).
I verified the relevant CLI and backend unit tests locally, using simulated services. This confirms the implemented behavior, but does not establish which Admin API version is installed on your cluster. If the retry fails, please share the error with secrets redacted and the deployed Admin API image tag or digest. Do not include passwords, tokens, or client secrets.
The documentation should make this local activation step and the fresh-container prerequisites explicit.
Activity
miki3421 commentedon Oct 13, 2026
Thanks for reporting this. The error suggests that
kubectl, invoked byops config sso, is not picking up a valid Kubernetes configuration. It is trying to reachlocalhost:8080, so the command fails while applying the SSO configuration, before we can assess the Keycloak integration.Could you run these checks in the same terminal, as the same user you use for
ops config sso?An empty
KUBECONFIGvariable is not necessarily a problem:kubectlnormally falls back to~/.kube/config.If your cluster configuration is stored elsewhere, point to the existing file explicitly:
export KUBECONFIG=/path/to/your/kubeconfig kubectl cluster-infoOnce that command successfully connects to the intended cluster, retry
ops config sso keycloakin the same terminal. The kubeconfig must provide sufficient permissions to apply the SSO configuration.I would avoid
--validate=falsehere: disabling manifest validation does not fix the underlying cluster connection.We have also prepared an SSO configuration guide, currently proposed for the documentation website in PR #96.
Please share the check results and how your cluster was installed, redacting any sensitive details. There is no need to post your full kubeconfig, tokens, certificates, or client secret.
col-panic commentedon Oct 14, 2026
Thank you @miki3421 I'm working on my local machine, while the host itself is remote.
Here are the outputs from my local machine:
I don't know the location of my kubeconfig, the
.kubedirectory in my $HOME does however not have any directory besidescache.I installed my cluster as documented here #258 from my local notebook via ssh to an external debian 13 machine.
miki3421 commentedon Oct 14, 2026
Thanks @col-panic, this clarifies the problem. Your local
kubectlhas no active context, but the kubeconfig may already have been downloaded by OPS into its own directory rather than~/.kube/config.Please run:
Look for
OPS_TMP. With the default configuration, check:If
OPS_TMPshows a different directory, use that path instead.If the file is missing, OPS provides a command to retrieve it from your K3s server:
Use the same server and SSH user used during installation. This retrieves the configuration; it does not reinstall the cluster. Then repeat the connection check.
Once
cluster-infosucceeds, explicitly pass the same kubeconfig when running your original SSO command:env KUBECONFIG="$HOME/.ops/tmp/kubeconfig" ops config sso keycloak ...Replace
...with your existing Keycloak options.If the connection check now reports a timeout or certificate error, please share that error with sensitive details redacted. That would identify a separate connectivity or TLS issue. There is no need to share the kubeconfig itself.
col-panic commentedon Oct 15, 2026
thanks a lot @miki3421 here we go:
so it seems the only hindering part was that somehow my config file is in a non-expected location?!
col-panic commentedon Oct 15, 2026
Concerning the SSO configuration as described in https://github.com/miki3421/openserverless-site/blob/docs-sso-configuration/content/en/docs/installation/configure/sso/_index.md - why is a "Group Membership" required? Why did you not settle for a realm role or a client role? I know there are some dicussions on this like keycloak/keycloak#36325
I'd be interested to hear your take on this, and why you selected Group Membership as requirement?
col-panic commentedon Oct 15, 2026
Added a question concerning this to apache/openserverless-site#96 (review)
miki3421 commentedon Oct 15, 2026
Thanks @col-panic. I checked the implementation, including the task revision shown in your
ops -info, and also saw your follow-up question on PR #96.The kubeconfig was already present in the OPS directory. In your case, the SSO command was not using it automatically; explicitly setting
KUBECONFIGresolved that connection issue. Your output confirms that the SSO configuration was applied successfully, although an actual SSO login still needs to be verified.Regarding groups versus roles, the distinction is:
--required-group.--groups-claim, which defaults togroups.admindoes not itself grant an OPS administrator role.The guide uses a Group Membership mapper because it produces the claim expected by the default configuration. That should not be presented as a general requirement of OIDC or as a recommendation against realm/client roles.
If you prefer roles, the current validator can consume a top-level claim containing the permitted role names, populated by an appropriate Keycloak mapper. For example, with this claim in the access token:
{ "ops_roles": ["openserverless-access"] }the corresponding options would be:
However, the validator currently performs a direct top-level claim lookup. It does not traverse nested paths such as
realm_access.rolesorresource_access.openserverless.roles. Passing those strings to--groups-claimwould not read the nested roles.There is also a distinction between the backend and the configuration command: the backend skips this membership check if
OIDC_REQUIRED_GROUPis unset, but the current command does not expose that as an optional configuration. So simply omitting--required-groupis not supported by this command.For your update question: you do not need to delete or disable SSO first. Run the same complete
ops config sso keycloak --enable ...command again, changing--required-groupto the new value and retaining your other options and explicitKUBECONFIG. It reapplies the configuration and restarts the admin-api by default.Please include your existing
--client-secretagain when doing this. In the task revision you are using, omitting it switches the configuration to public-client mode and deletes the managed SSO Secret; it does not retain the previous secret. Likewise, retain any non-default audience or claim options.We will update the guide in PR #96 to clarify the distinction between Keycloak groups and role claims, the current limitation on nested claims, and how to update an existing SSO configuration—including the need to pass the client secret and any non-default options again.
Code references: configuration task at your revision and backend membership validation.
col-panic commentedon Oct 22, 2026
thanks for all the input, but somehow I can't get this to work. I created a separate docker container based on debian
and installed the ops cli plus the required tools (docu should mention to have tools like zip, curl and ssh already available,
else you will get output like
failed preflight check: curl not installed but is required)Running like this
does not work, it's also showing the wrong username?! This does not match documentation.
My real target is to be able to have an sso login for a non-existing user to auto-create
ops -wsknamespace etc. ATM I'm not really sure, if this is the right way at all?!I thought of something like this
miki3421 commentedon Oct 23, 2026
Thanks @col-panic. The normal login command is
ops ide login. It delegates authentication to the embeddedops -logintool and also configures the local workspace. See the IDE login task in your reported task revision.Your intended flow—an existing Keycloak user logging in for the first time and receiving an OpenServerless namespace—is supported by the SSO provisioning implementation. However, the output you posted shows that the CLI is taking the traditional username/password login path.
I checked the source for your exact CLI tag,
v0.9.0-incubating+26i11g51-snapshot(d3f767a). In that version, the CLI must seeSSO_ENABLED=true, from its local configuration or environment.--sso-flow passwordand--sso-usernameselect options within SSO mode; they do not enable it. Without that switch, the CLI uses its defaultopenserverlessuser, which explains the output. See the login branch selection.The earlier
ops config sso keycloak --enable ...command saves SSO settings in the local OPS profile where it runs, as well as configuring the server. A new Docker container does not inherit that local profile. The task revision reported by yourops -infoexplicitly savesSSO_ENABLED=truelocally.In the new container, enable SSO locally and select the password flow you were trying to use, then run the normal login command:
./ops -config SSO_ENABLED=true OPS_SSO_LOGIN_FLOW=password ./ops ide login mdescher https://openserverless.somehost.com # After login succeeds: ./ops -wsk action listOnce the local settings are saved, you can also run
./ops ide logininteractively.OPS_SSO_LOGIN_FLOW=passwordis the configuration/environment equivalent of the embedded tool's--sso-flow passwordflag; see the flow selection. The password selection is specific to this example; it is not required for every SSO flow.The login should now print
with backend-managed OIDC password grantand prompt forEnter SSO Password:. Enter the Keycloak user's password. This local configuration command does not reconfigure the cluster or require the Keycloak client secret. Keycloak must allow the password grant for the configured client.For automatic provisioning, the backend validates the token and required membership, derives the namespace from the configured claims, and, if it is missing, creates a
WhiskUserand waits for the operator to provision it. This requiresSSO_AUTOPROVISION_ON_LOGIN=true, anemailclaim, and working operator reconciliation. Your SSO configuration task sets that server flag to true. See also the backend provisioning implementation.The CLI saves the returned credentials for subsequent
ops -wskcommands, andops ide loginsaves the workspace settings. In the password example above,mdescheris both the IdP username and the requested OPS namespace. This assumes that the configured claims resolve to that namespace. If your namespace mapping produces a different name, the lower-levelops -login --sso-flow password --sso-username ...form can separate the IdP username from the requested namespace (or omit the requested namespace in a clean profile).I verified the relevant CLI and backend unit tests locally, using simulated services. This confirms the implemented behavior, but does not establish which Admin API version is installed on your cluster. If the retry fails, please share the error with secrets redacted and the deployed Admin API image tag or digest. Do not include passwords, tokens, or client secrets.
The documentation should make this local activation step and the fresh-container prerequisites explicit.