keycloak / keycloak-operator Goto Github PK
View Code? Open in Web Editor NEWARCHIVED Kubernetes Operator for the no longer supported WildFly distribution of Keycloak
License: Apache License 2.0
ARCHIVED Kubernetes Operator for the no longer supported WildFly distribution of Keycloak
License: Apache License 2.0
I was trying to use keycloak plugin for enmasse and I was unable to do that because it needs to expose ports that are not exposed by keycloak pods (5671 or 5672).
And operator will override any changes to pod template in StatefulSet, so support for this kind of plugins would need to be added to the operator code.
What would be your thoughts on adding an optional field to "Keycloak" spec, that would define a list of additional ports that need to be exposed?
Ping @slaskawi @abstractj
Using the operator, I have rolled out keycloak in 3 instances on Azure AKS behind nginx.
Since this evening, only one pod is created, and its liveness/readiness-probes keep failing - while keycloak pod-logs seem normal except for certificate expired warnings (for which an issue has already been created).
When I manually and verbosely execute the liveness-probe's curl-statement as curl -v http://$(hostname -i):8080/auth
, I can see a 303 response with Location http://[host-ip]:8080/auth/
- so it redirects to the same URL with a trailing slash... but probably since the option -L
is not set in curl in the liveness-probe, curl does not follow the redirect and the probe never succeeds.
16.0.0
Liveness- and -readiness probes succeed
Pods never appear as ready.
(I cannot say precisely which circumstances are relevant)
No response
When creating a user in a different namespace than his realm, user is not properly created
16.1.0
User created properly, with error messages at keycloak's console neither operator's console, and without the reported side-effects
User is being created and can ben seen at web console, but according to the logs there are some problems with the creation and there are also some side-effects
Message logs:
0) SUCCESS check if keycloak is available"}
1) SUCCESS update user test1-christian"}
2) FAILED create credential secret for user test1-christian in realm default/basic-realm : "cross-namespace owner references are disallowed, owner's namespace namespace2, obj's namespace namespace1"
Side-effects:
We are suspecting that this error is because the operator is trying to create the user's secret in another namespace, which is not allowed by kubernetes.
0) SUCCESS check if keycloak is available"}
1) SUCCESS update user test1-christian"}
2) FAILED create credential secret for user test1-christian in realm default/basic-realm : "cross-namespace owner references are disallowed, owner's namespace namespace2, obj's namespace namespace1"
The KeycloakClient object's secret field is the actual password to load, not a reference to a secret where the actual password is stored. It is considered bad practice to put secret material in any Kubernetes object other then a secret. There does not seem to be any other way to load a secret into the Keycloak Client by api other then the secret field. Some more details here: https://keycloak.discourse.group/t/keycloakclient-secret/11787. Currently no one has answered.
15.0.2
spec.secret should be a reference to a secret, or a new spec.secretRef should be added.
the secret is plantext in the CR.
No response
No response
Hi, @abstractj can you give me a hand?
I am trying to change the version of keycloak deployed by this operator (7.0.1 -> 7.0.0 or 6.0.1)
Seems that the deploy of keycloak is written in pkg/model/keycloak_deployment.go
,
and the image of keycloak is specified in pkg/model/constant.go
, right?
I tried to change the value of KeycloakImage
from quay.io/keycloak/keycloak:7.0.1
to quay.io/keycloak/keycloak:6.0.1
. Then I build a new operator image, change the operator image in deploy/operator.yaml
and deploy the keycloak operator.
However, when I use deploy/examples/keycloak/keycloak.yaml
to create keycloak statefulset, I find the version of keycloak is still 7.0.1.
I am confused. Can you teach me how to make the correct keycloak version management by this operator? Thank you!
ScopeMappings for realm roles does not work and ends up with no result but a lot of stacktraces in keycloak log.
It seems that when I do the scope mapping manually by the administrator ui, the operator detects, that the mapping is done and nothing have to be changed.
15.1.1
Realm roles of ScopeMappings are applied to the created client.
Exception in keycloak
21:49:07,798 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (default task-2) Uncaught server error: java.lang.NullPointerException: Null keys are not supported!
at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at [email protected]//org.infinispan.cache.impl.SimpleCacheImpl.get(SimpleCacheImpl.java:529)
at [email protected]//org.infinispan.cache.impl.StatsCollectingCache.get(StatsCollectingCache.java:54)
at [email protected]//org.infinispan.cache.impl.AbstractDelegatingCache.get(AbstractDelegatingCache.java:439)
at [email protected]//org.infinispan.cache.impl.EncoderCache.get(EncoderCache.java:696)
at [email protected]//org.infinispan.cache.impl.AbstractDelegatingCache.get(AbstractDelegatingCache.java:439)
at [email protected]//org.keycloak.models.cache.infinispan.CacheManager.get(CacheManager.java:95)
at [email protected]//org.keycloak.models.cache.infinispan.RealmCacheSession.getRoleById(RealmCacheSession.java:819)
at [email protected]//org.keycloak.models.cache.infinispan.RealmAdapter.getRoleById(RealmAdapter.java:732)
at [email protected]//org.keycloak.services.resources.admin.ScopeMappedResource.addRealmScopeMappings(ScopeMappedResource.java:208)
at jdk.internal.reflect.GeneratedMethodAccessor514.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at [email protected]//org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:546)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:435)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:396)
at [email protected]//org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:398)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:365)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:150)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:110)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:141)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:110)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:141)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:110)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:141)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:110)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:141)
at [email protected]//org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:104)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:440)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
at [email protected]//org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:245)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:61)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at [email protected]//javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
at [email protected]//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at [email protected]//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at [email protected]//org.keycloak.provider.wildfly.WildFlyRequestFilter.lambda$doFilter$0(WildFlyRequestFilter.java:41)
at [email protected]//org.keycloak.services.filters.AbstractRequestFilter.filter(AbstractRequestFilter.java:43)
at [email protected]//org.keycloak.provider.wildfly.WildFlyRequestFilter.doFilter(WildFlyRequestFilter.java:39)
at [email protected]//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at [email protected]//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at [email protected]//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at [email protected]//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at [email protected]//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at [email protected]//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at [email protected]//org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
at [email protected]//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
at [email protected]//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at [email protected]//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at [email protected]//io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at [email protected]//io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at [email protected]//io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at [email protected]//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at [email protected]//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//io.undertow.server.handlers.MetricsHandler.handleRequest(MetricsHandler.java:64)
at [email protected]//io.undertow.servlet.core.MetricsChainHandler.handleRequest(MetricsChainHandler.java:59)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)
at [email protected]//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at [email protected]//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at [email protected]//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99)
at [email protected]//io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
at [email protected]//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:841)
at [email protected]//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at [email protected]//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1280)
at java.base/java.lang.Thread.run(Thread.java:829)
Kog in Operator
{"level":"info","ts":1640554606.0752141,"logger":"action_runner","msg":"( 6) FAILED create client realm scope mappings for customer-a/frontend : failed to create client realm scope mappings: (500) 500 Internal Server Error"}
Creating a client with a scope mapping depending on realm roles:
In my realm I already have to existing roles mobile_user
and administrator
. The client is fullScopeAllowed = false.
apiVersion: keycloak.org/v1alpha1
kind: KeycloakClient
metadata:
name: frontend
namespace: customer-a
spec:
client:
attributes:
oauth2.device.authorization.grant.enabled: 'false'
baseUrl: 'https://my.url/'
clientAuthenticatorType: client-secret
clientId: frontend
defaultClientScopes:
- web-origins
- profile
- roles
- email
directAccessGrantsEnabled: false
enabled: true
fullScopeAllowed: false
name: '${client_frontend}'
optionalClientScopes:
- address
- phone
- offline_access
- microprofile-jwt
protocol: openid-connect
publicClient: true
redirectUris:
- 'https://my.ur/*'
rootUrl: 'https://my.ur/'
standardFlowEnabled: true
webOrigins:
- 'https://my.ur'
realmSelector:
matchLabels:
app: customer-a
scopeMappings:
realmMappings:
- name: mobile_user
- name: administrator
No response
I have keycloak installed using the Operator on top of Openshift 4.9 and I upgraded recently from version 15 to version 16 and keycloak won't start with the following error in the logs:
�[0m�[33m18:10:01,755 WARN [org.wildfly.extension.elytron] (MSC service thread 1-2) WFLYELY00024: Certificate [dstrootcax3] in KeyStore is not valid: java.security.cert.CertificateExpiredException: NotAfter: Thu Sep 30 14:01:15 GMT 2021
I suspect it depends on a secret created with the first installation of keycloak operator (version 13)
16
complete the upgrade using the operator correctly and seeing the keycloak pod starting up correctly
No response
No response
No response
When using an external database option for the Keycloak resource, we are observing the following issue:
When the Keycloak resource is deleted and afterwards recreated, the admin user's password in the database is not updated with the respective value in the secret called "credentials-".
1.16.0
The admin password is expected to be updated, so we can use it at the keycloak web console and through REST API calls.
The admin password is not getting updated, and we cannot login into Keycloak's web console nor through REST API calls.
delete from credential
where user_id = (select id from user_entity where username = 'admin');
delete from user_role_mapping
where user_id = (select id from user_entity where username = 'admin');
delete from user_entity where username = 'admin';
Then, we have to delete and recreate the Keycloak resource, so the admin password is reinserted in the database with the new values
We need the possiblity to mount files like keytab from secret.
Furthermore it would be nice to mount themes from sidecar volume or to retrieve via curl/wget from url like init-extensions?
you can mount files from secret
not posible to mount custom secrets
Documentation says that KeycloakRealms are automatically synchronized, but this is not working.
16.1.0
When we apply the changes to the realm CR, the operator should update the realm at Keycloak.
After the realm creation, no matter how many times you apply and updated CR, the realm never get updated at Keycloak.
If we delete the CR and create it again, it gets created with the updated information.
Tested fields:
Already reported twice in former issue tracking system:
Publish Keycloak Operator 15.1.0 in OperatorHub.io
No response
No response
No response
Constants are currently specified within /pkg/model/constants.go
These constants may not be useful for all deployments, so would there be a way of specifying/changing these at runtime/ within a ConfigMap rather than having to clone the repository and rebuild your own operator to change these.
After the failing update to 15.1.1 due to missing container images a few days ago, this time the startup of keycloak container image 16.0.0 is failing. It spams the logfile so the root cause (first exception) can't be found, only hundrets of lines with
[0m�[31m10:23:16,913` ERROR [org.jboss.as.controller.management-operation] (ServerService Thread Pool -- 38) WFLYCTL0190: Step handler org.jboss.as.controller.AbstractAddStepHandler$1@64475b67 for operation add at address [ ("subsystem" => "infinispan"), ("cache-container" => "ejb"), ("distributed-cache" => "dist"), ("component" => "transaction") ] failed -- java.util.concurrent.RejectedExecutionException: java.util.concurrent.RejectedExecutionException at [email protected]//org.jboss.threads.RejectingExecutor.execute(RejectingExecutor.java:37) at [email protected]//org.jboss.threads.EnhancedQueueExecutor.rejectShutdown(EnhancedQueueExecutor.java:2029) at [email protected]//org.jboss.threads.EnhancedQueueExecutor.execute(EnhancedQueueExecutor.java:757) at [email protected]//org.jboss.as.controller.notification.NotificationSupports$NonBlockingNotificationSupport.emit(NotificationSupports.java:95) at [email protected]//org.jboss.as.controller.OperationContextImpl.notifyModificationBegun(OperationContextImpl.java:891) at [email protected]//org.jboss.as.controller.OperationContextImpl.ensureWriteLockForRuntime(OperationContextImpl.java:880) at [email protected]//org.jboss.as.controller.OperationContextImpl.removeService(OperationContextImpl.java:653) at [email protected]//org.jboss.as.controller.ParallelBootOperationContext.removeService(ParallelBootOperationContext.java:190) at [email protected]//org.jboss.as.clustering.controller.SimpleResourceServiceHandler.removeServices(SimpleResourceServiceHandler.java:48) at [email protected]//org.jboss.as.clustering.controller.AddStepHandler.rollbackRuntime(AddStepHandler.java:226) at [email protected]//org.jboss.as.controller.AbstractAddStepHandler$1$1.handleRollback(AbstractAddStepHandler.java:169) at [email protected]//org.jboss.as.controller.AbstractOperationContext$RollbackDelegatingResultHandler.handleResult(AbstractOperationContext.java:1598) at [email protected]//org.jboss.as.controller.AbstractOperationContext$Step.invokeResultHandler(AbstractOperationContext.java:1570) at [email protected]//org.jboss.as.controller.AbstractOperationContext$Step.handleResult(AbstractOperationContext.java:1552) at [email protected]//org.jboss.as.controller.AbstractOperationContext$Step.finalizeInternal(AbstractOperationContext.java:1509) at [email protected]//org.jboss.as.controller.AbstractOperationContext$Step.finalizeStep(AbstractOperationContext.java:1492) at [email protected]//org.jboss.as.controller.AbstractOperationContext$Step.access$400(AbstractOperationContext.java:1356) at [email protected]//org.jboss.as.controller.AbstractOperationContext.executeResultHandlerPhase(AbstractOperationContext.java:910) at [email protected]//org.jboss.as.controller.AbstractOperationContext.executeDoneStage(AbstractOperationContext.java:896) at [email protected]//org.jboss.as.controller.AbstractOperationContext.processStages(AbstractOperationContext.java:803) at [email protected]//org.jboss.as.controller.AbstractOperationContext.executeOperation(AbstractOperationContext.java:466) at [email protected]//org.jboss.as.controller.ParallelBootOperationStepHandler$ParallelBootTask.run(ParallelBootOperationStepHandler.java:384) at [email protected]//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) at [email protected]//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990) at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486) at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1348) at java.base/java.lang.Thread.run(Thread.java:829) at [email protected]//org.jboss.threads.JBossThread.run(JBossThread.java:513) Suppressed: java.util.concurrent.RejectedExecutionException: Executor is being shut down at [email protected]//org.jboss.threads.EnhancedQueueExecutor.rejectShutdown(EnhancedQueueExecutor.java:2031) ... 26 more
But that seem to be problem while shutting down - but as I said I was unable to catch the first exception since it got spammed away in OKD.
16.0.0
the container starts and functions correctly.
The container fails during initialization.
Let the OKD 4.9 cluster run on 15.1.1 and the level 4 operator tries to update to 16.0.0
No response
Current implementation of keycloak operator is using a user account. As service operator isn't a user but a service without user interaction, it's permission should be configured from a client id with client credentials or a signed jwt.
There was already a concept in
https://issues.redhat.com/browse/KEYCLOAK-12023
No response
Currently Keycloak Operator uses admin username and password to create Realm/Client or Users. This approach doesn't scale well when considering multi-namespace support. With that use case in mind, every Realm Admin should use a dedicated Service Account to manage his own Realm (and not the admin username and password).
Keycloak Service Accounts uses Client Credentials to obtain an Access Token. A Client needs to be Confidential and needs to have Service Accounts enabled. Also, proper roles (such as create-realm or admin) need to be associated with a Client.
The implementation will require changing the internal wiring inside Keycloak Operator. Whenever the Operator spins up a new Keycloak cluster (by reconciling Keycloak CR), an additional Client, called <>-operator-cli will be created. Similar thing will happen whenever the Operator creates a new Realm, a new <<KeycloakRealmCR-Name-operator-cli will be created within it. The idea is to keep the naming predictable and consistent.
The Operator will use a fallback mechanism to obtain proper credentials for managing Keycloak:
If KeycloakClient CR is being reconciled, Keycloak Operator will look at the parent KeyclaokRealm CR (KeycloakClient CR points to the Realm using proper labels) and will figure out proper Client Secret. Then it will obtain an Access Token to create a Client.
If Keycloak Realm CR is being reconciled, Keycloak Operator will look at the parent Keycloak CR (Keycloak Realm CR points to the Keycloak using proper labels) and will figure out proper Client Secret.
If all of the above fails, Keycloak Operator will fall back to the admin username and password.
The fallback mechanism should be enough to satisfy backwards compatibility as well as upgrades. In the worse case, we'll be using admin username and password, which we do now.
In order to simplify the implementation, the Operator should assume that given namespace contains KeycloakUser/KeycloakClient CRs pointing to a KeycloakRealm CR in the same namespace. The initial version should not assume that a Realm might be defined somewhere else.
We have several applications in different namespaces needing SSO. Those can take the oidc client secret as input from a k8s secret in the same namespace as the application.
I first tried to expand the keycloak-operator to look for keycloakclients in other namespaces (#398), this was rejected because of incomplete implementation.
My next try is to copy the created secret using kubed. But kubed only copy secrets having a label (for example kubed.appscode.com/sync: "kubernetes.io/metadata.name=grafana"
, ref: https://appscode.com/products/kubed/v0.13.0-beta.0/guides/config-syncer/intra-cluster/).
I'm proposing to extend the CRD. For example:
apiVersion: keycloak.org/v1alpha1
kind: KeycloakClient
metadata:
namespace: keycloak
name: grafana-oidc-client-secret
labels:
app: sso
annotations:
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
realmSelector:
matchLabels:
app: sso
client:
clientId: grafana
clientAuthenticatorType: client-secret
standardFlowEnabled: true
redirectUris:
- https://grafana.example.org/*
webOrigins:
- '+' # redirectUris
defaultClientScopes:
- email
- profile
# V V V V V V V V V V V here V V V V V V V V V V V
secretTemplate:
metadata:
labels:
kubed.appscode.com/sync: "kubernetes.io/metadata.name=grafana"
# Λ Λ Λ Λ Λ Λ Λ Λ Λ Λ Λ here Λ Λ Λ Λ Λ Λ Λ Λ Λ Λ Λ
The code changes are around:
keycloak-operator/pkg/model/client_secret.go
Lines 13 to 26 in 77d1261
See also my WIP Ansible playbooks: https://gitlab.com/kubitus-project/kubitus-installer/-/merge_requests/364
when creating 2 keycloak instances (not 2 replicas) the 2nd instance will try to create same prometheus service monitor
then the operator will fail to create the prometheus resources because they exists and already monitoring the 1st keycloak instance
below is the status of the 2 clusters
1st cluster:
message: ''
phase: reconciling
ready: true
secondaryResources:
ServiceMonitor:
- keycloak-service-monitor
PrometheusRule:
- keycloak
2nd cluster:
message: >-
prometheusrules.monitoring.coreos.com "keycloak" is forbidden: cannot set an
ownerRef on a resource you can't delete: ,
phase: failing
ready: false
secondaryResources:
ServiceMonitor:
- keycloak-service-monitor
PrometheusRule:
- keycloak
16.1.0
create the service monitor/extra resources based on the keycloak cluster name
message: >-
prometheusrules.monitoring.coreos.com "keycloak" is forbidden: cannot set an
ownerRef on a resource you can't delete: ,
phase: failing
ready: false
secondaryResources:
ServiceMonitor:
- keycloak-service-monitor
PrometheusRule:
- keycloak
No response
No response
Create new OLM structure that will be published on OperatorHub and internal OCP marketplace
No response
No response
No response
Be able to define a client with the PCKE challenge method enabled.
No response
The feature could be useful to configure clients authorization flows that are more resilient against CSRF and authorization code injection attacks.
No response
Currently, operator is hardcoded for nginx. I am running:
Unable to find any documentation on how to get this working.
No response
There are multiple popular ingress controllers in the market. Keycloak operator should be ingress independent and able to work with any ingress controller
No response
Create OLM structure for 16.1.0 to be published on operatorhub
No response
No response
No response
Right now, realms have an identityProviders
option to add external identity providers. The provider's configuration is done with a config
object which settings can by extracted from a running Keycloak instance via realm export.
Right now, client-secret used by the Keycloak instance at that external provider is hard-coded.
Example configuration:
apiVersion: keycloak.org/v1alpha1
kind: KeycloakRealm
metadata:
name: my-realm
labels:
app: sso
spec:
realm:
id: my-realm-id
realm: my-realm-name
enabled: true
displayName: My Realm
identityProviders:
- alias: external-oidc-provider
internalId: 0cdbffa4-017b-4b03-98b8-38d98f386ec8
providerId: oidc
displayName: Login with External Provider Name
enabled: true
config:
acceptsPromptNoneForwardFromClient: 'true'
authorizationUrl: https://external.oidcprovider.example.com/authorize
clientAuthMethod: client_secret_basic
clientId: client-id-given-to-me-by-external-oidc-provider
clientSecret: SECRET_IN_TEXT_PLAIN_COMMITED_TO_GIT_AND_AVAIABLE_VIA_KB_GET_KeycloakRealm_UNFORTUNATELY
defaultScope: openid email phone profile govbr_confiabilidades
issuer: https://external.oidcprovider.example.com/
jwksUrl: https://external.oidcprovider.example.com/jwk
loginHint: 'false'
logoutUrl: https://external.oidcprovider.example.com/logout
pkceEnabled: 'true'
pkceMethod: S256
publicKeySignatureVerifierKeyId: rsa1
syncMode: FORCE
tokenUrl: https://external.oidcprovider.example.com/token
uiLocales: 'false'
userInfoUrl: https://external.oidcprovider.example.com/userinfo
validateSignature: 'false'
No response
Right now, I suppose this can be mitigated by changing kubernete's users permissions to deny access to KeycloakRealm objects on the API, but it would be very nice to be able to use secrets for that.
I'm not very familiar with the inner workings of an operator, but I suppose something can be done in the fashion of deployments. Something like:
envFrom:
- secretRef:
name: env-secrets
I could be something like:
apiVersion: keycloak.org/v1alpha1
kind: KeycloakRealm
spec:
realm:
id: my-realm-id
realm: my-realm-name
enabled: true
displayName: My Realm
identityProviders:
- alias: external-oidc-provider
internalId: 0cdbffa4-017b-4b03-98b8-38d98f386ec8
providerId: oidc
displayName: Login with External Provider Name
enabled: true
config:
clientSecretFrom:
- secretRef:
name: external-oidc-secrets
I suppose the situation with other Keycloak CRD is the same. So my proposition is actually how to protect secrets for keycloak-operators.
There are still mentions of JIRA.
No response
No response
No response
Currently the e2e tests are failing on the extension test because the keycloak-metrics-spi has an incompatiblity with KC 16.0.0
(aerogear/keycloak-metrics-spi#124)
16.0.0
not failing
failing
add the aerogear/keycloak-metrics-spi extension ( by default in the CR )
No response
The Keycloak POD connects via JDBC to the PostgreSQL POD;
It may happen that, if the PostgreSQL POD isn't yet ready when the Keycloak POD tries to connect to it, then the Keycloak POD fails and needs to be restarted;
This introduces a delay of several minutes before the Keycloak POD is actually ready to server requests and the Keycloak POD has the Restarts count set to 1 (instead of 0)
15.0.2 (rhsso-operator.7.5.1-opr-001)
no restart of Keycloak POD is needed
No response
oc new-project rhsso-operator-issue
cat <<EOT > OperatorGroup.yaml
apiVersion: "operators.coreos.com/v1"
kind: "OperatorGroup"
metadata:
name: "rhsso-operator-issue-operators"
namespace: "rhsso-operator-issue"
spec:
targetNamespaces:
- "rhsso-operator-issue"
EOT
oc apply -f OperatorGroup.yaml
Using Keycloak image registry.redhat.io/rh-sso-7/sso74-openshift-rhel8:
cat <<EOT > Subscription.yaml
apiVersion: "operators.coreos.com/v1alpha1"
kind: "Subscription"
metadata:
name: "rhsso-operator"
namespace: "rhsso-operator-issue"
spec:
channel: "alpha"
config:
env:
- name: "PROFILE"
value: "RHSSO"
- name: "RELATED_IMAGE_RHSSO"
value: "registry.redhat.io/rh-sso-7/sso74-openshift-rhel8:latest"
name: "rhsso-operator"
source: "redhat-operators"
sourceNamespace: "openshift-marketplace"
EOT
oc apply -f Subscription.yaml
cat <<EOT > Keycloak.yaml
apiVersion: keycloak.org/v1alpha1
kind: Keycloak
metadata:
labels:
app: sso
name: example-sso
namespace: rhsso-operator-issue
spec:
externalAccess:
enabled: true
instances: 1
EOT
oc apply -f Keycloak.yaml
Then with oc get pods
you observe:
both keycloak and keycloak-postgresql pods are starting:
Name Status Ready Restarts
keycloak-0 Running 0/1 0
keycloak-postgresql-6d966cb4fb-869bq Running 0/1 0
rhsso-operator-56bf8c94df-n52dg Running 1/1 0
then only the keycloak-postgresql pods becomes ready:
Name Status Ready Restarts
keycloak-0 Running 0/1 0
keycloak-postgresql-6d966cb4fb-869bq Running 1/1 0
rhsso-operator-56bf8c94df-n52dg Running 1/1 0
meanwhile In keycloak-0
log pod you see:
�[0m�[31m16:36:08,386 FATAL [com.arjuna.ats.arjuna] (MSC service thread 1-1) ARJUNA012260: Received exception for com.arjuna.ats.internal.arjuna.objectstore.jdbc.accessors.DataSourceJDBCAccess:oskeycloak0JBossTSTxTable: java.sql.SQLException: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/KeycloakDSObjectStore
Finally, after keycloak-0
is restarted (takes several minutes):
Name Status Ready Restarts
keycloak-0 Running 1/1 1
keycloak-postgresql-6d966cb4fb-869bq Running 1/1 0
rhsso-operator-56bf8c94df-n52dg Running 1/1 0
Using Keycloak image registry.redhat.io/rh-sso-7/sso75-openshift-rhel8 (the default image) we were able to reproduce the bug by deleting and the recreating the Keycloak custom resource;
No response
Now the resources labels are added after the creation of a CR ( KEYCLOAK-19405 ) but this won't cover adding them to the current existing resources after the operator upgrade.
Take into consideration keeping the rest of labels already set in the resources ( statefulset ).
No response
No response
No response
The validation error is must only have "properties", "required" or "description" at the root if the status subresource is enabled
I found the same problem in this issue: knative/eventing-contrib#114
To fix we would have to do something like this: https://github.com/knative/eventing-contrib/pull/115/files
OpenShift 3.11
Deploy CRD's to an existing OCP 3.11 environment
When defining a KeycloakAPIAuthenticationFlow on the Realm object there is a property to define the ID. When configuring that and applying the Realm Ressource (not existing prior to that) the Flow is created but the ID is a different one from the configured one.
Sadly that makes overwriting the default flow on a client impossible…
15.1.1
The Flow in Keycloak has the configured ID.
The Flow in Keycloak has a newly generated ID.
Apply this yaml and check the x509 flow:
apiVersion: keycloak.org/v1alpha1
kind: KeycloakRealm
metadata:
name: testrealm
labels:
realm: testrealm
spec:
realm:
realm: "testrealm"
enabled: True
displayName: "testrealm Realm"
userManagedAccessAllowed: true
clientScopes:
- name: web-origins
description: OpenID Connect scope for add allowed web origins to the access token
protocol: openid-connect
attributes:
include.in.token.scope: 'false'
display.on.consent.screen: 'false'
consent.screen.text: ''
protocolMappers:
- name: allowed web origins
protocol: openid-connect
protocolMapper: oidc-allowed-origins-mapper
consentRequired: false
config: {}
- name: email
description: 'OpenID Connect built-in scope: email'
protocol: openid-connect
attributes:
include.in.token.scope: 'true'
display.on.consent.screen: 'true'
consent.screen.text: "${emailScopeConsentText}"
protocolMappers:
- name: email verified
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: emailVerified
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: email_verified
jsonType.label: boolean
- name: email
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: email
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: email
jsonType.label: String
- name: role_list
description: SAML role list
protocol: saml
attributes:
consent.screen.text: "${samlRoleListScopeConsentText}"
display.on.consent.screen: 'true'
protocolMappers:
- name: role list
protocol: saml
protocolMapper: saml-role-list-mapper
consentRequired: false
config:
single: 'false'
attribute.nameformat: Basic
attribute.name: Role
- name: mqtt
description: OpenID Connect scope to allow connecting to the mqtt broker
protocol: openid-connect
attributes:
include.in.token.scope: 'true'
display.on.consent.screen: 'false'
- name: roles
description: OpenID Connect scope for add user roles to the access token
protocol: openid-connect
attributes:
include.in.token.scope: 'false'
display.on.consent.screen: 'true'
consent.screen.text: "${rolesScopeConsentText}"
protocolMappers:
- name: realm roles
protocol: openid-connect
protocolMapper: oidc-usermodel-realm-role-mapper
consentRequired: false
config:
user.attribute: foo
access.token.claim: 'true'
claim.name: realm_access.roles
jsonType.label: String
multivalued: 'true'
- name: audience resolve
protocol: openid-connect
protocolMapper: oidc-audience-resolve-mapper
consentRequired: false
config: {}
- name: client roles
protocol: openid-connect
protocolMapper: oidc-usermodel-client-role-mapper
consentRequired: false
config:
user.attribute: foo
access.token.claim: 'true'
claim.name: resource_access.${client_id}.roles
jsonType.label: String
multivalued: 'true'
- name: microprofile-jwt
description: Microprofile - JWT built-in scope
protocol: openid-connect
attributes:
include.in.token.scope: 'true'
display.on.consent.screen: 'false'
protocolMappers:
- name: groups
protocol: openid-connect
protocolMapper: oidc-usermodel-realm-role-mapper
consentRequired: false
config:
multivalued: 'true'
user.attribute: foo
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: groups
jsonType.label: String
- name: upn
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: username
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: upn
jsonType.label: String
- name: phone
description: 'OpenID Connect built-in scope: phone'
protocol: openid-connect
attributes:
include.in.token.scope: 'true'
display.on.consent.screen: 'true'
consent.screen.text: "${phoneScopeConsentText}"
protocolMappers:
- name: phone number verified
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: phoneNumberVerified
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: phone_number_verified
jsonType.label: boolean
- name: phone number
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: phoneNumber
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: phone_number
jsonType.label: String
- name: offline_access
description: 'OpenID Connect built-in scope: offline_access'
protocol: openid-connect
attributes:
consent.screen.text: "${offlineAccessScopeConsentText}"
display.on.consent.screen: 'true'
- name: address
description: 'OpenID Connect built-in scope: address'
protocol: openid-connect
attributes:
include.in.token.scope: 'true'
display.on.consent.screen: 'true'
consent.screen.text: "${addressScopeConsentText}"
protocolMappers:
- name: address
protocol: openid-connect
protocolMapper: oidc-address-mapper
consentRequired: false
config:
user.attribute.formatted: formatted
user.attribute.country: country
user.attribute.postal_code: postal_code
userinfo.token.claim: 'true'
user.attribute.street: street
id.token.claim: 'true'
user.attribute.region: region
access.token.claim: 'true'
user.attribute.locality: locality
- name: profile
description: 'OpenID Connect built-in scope: profile'
protocol: openid-connect
attributes:
include.in.token.scope: 'true'
display.on.consent.screen: 'true'
consent.screen.text: "${profileScopeConsentText}"
protocolMappers:
- name: nickname
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: nickname
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: nickname
jsonType.label: String
- name: gender
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: gender
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: gender
jsonType.label: String
- name: website
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: website
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: website
jsonType.label: String
- name: picture
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: picture
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: picture
jsonType.label: String
- name: zoneinfo
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: zoneinfo
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: zoneinfo
jsonType.label: String
- name: family name
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: lastName
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: family_name
jsonType.label: String
- name: updated at
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: updatedAt
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: updated_at
jsonType.label: String
- name: username
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: username
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: preferred_username
jsonType.label: String
- name: given name
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: firstName
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: given_name
jsonType.label: String
- name: middle name
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: middleName
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: middle_name
jsonType.label: String
- name: profile
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: profile
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: profile
jsonType.label: String
- name: birthdate
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: birthdate
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: birthdate
jsonType.label: String
- name: full name
protocol: openid-connect
protocolMapper: oidc-full-name-mapper
consentRequired: false
config:
id.token.claim: 'true'
access.token.claim: 'true'
userinfo.token.claim: 'true'
- name: locale
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: 'true'
user.attribute: locale
id.token.claim: 'true'
access.token.claim: 'true'
claim.name: locale
jsonType.label: String
authenticationFlows:
- alias: Account verification options
description: Method with which to verity the existing account
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: idp-email-verification
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: ALTERNATIVE
priority: 20
flowAlias: Verify Existing Account by Re-authentication
userSetupAllowed: false
- alias: Authentication Options
description: Authentication options.
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: basic-auth
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticator: basic-auth-otp
authenticatorFlow: false
requirement: DISABLED
priority: 20
userSetupAllowed: false
- authenticator: auth-spnego
authenticatorFlow: false
requirement: DISABLED
priority: 30
userSetupAllowed: false
- alias: Browser - Conditional OTP
description: Flow to determine if the OTP is required for the authentication
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: conditional-user-configured
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticator: auth-otp-form
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
- alias: Direct Grant - Conditional OTP
description: Flow to determine if the OTP is required for the authentication
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: conditional-user-configured
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticator: direct-grant-validate-otp
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
- alias: First broker login - Conditional OTP
description: Flow to determine if the OTP is required for the authentication
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: conditional-user-configured
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticator: auth-otp-form
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
- alias: Handle Existing Account
description: Handle what to do if there is existing account with same email/username
like authenticated identity provider
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: idp-confirm-link
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: REQUIRED
priority: 20
flowAlias: Account verification options
userSetupAllowed: false
- alias: Reset - Conditional OTP
description: Flow to determine if the OTP should be reset or not. Set to REQUIRED
to force.
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: conditional-user-configured
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticator: reset-otp
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
- alias: User creation or linking
description: Flow for the existing/non-existing user alternatives
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticatorConfig: create unique user config
authenticator: idp-create-user-if-unique
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: ALTERNATIVE
priority: 20
flowAlias: Handle Existing Account
userSetupAllowed: false
- alias: Verify Existing Account by Re-authentication
description: Reauthentication of existing account
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: idp-username-password-form
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: CONDITIONAL
priority: 20
flowAlias: First broker login - Conditional OTP
userSetupAllowed: false
- alias: browser
description: browser based authentication
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: auth-cookie
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 10
userSetupAllowed: false
- authenticator: auth-spnego
authenticatorFlow: false
requirement: DISABLED
priority: 20
userSetupAllowed: false
- authenticator: identity-provider-redirector
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 25
userSetupAllowed: false
- authenticatorFlow: true
requirement: ALTERNATIVE
priority: 30
flowAlias: forms
userSetupAllowed: false
- alias: clients
description: Base authentication for clients
providerId: client-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: client-secret
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 10
userSetupAllowed: false
- authenticator: client-jwt
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 20
userSetupAllowed: false
- authenticator: client-secret-jwt
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 30
userSetupAllowed: false
- authenticator: client-x509
authenticatorFlow: false
requirement: ALTERNATIVE
priority: 40
userSetupAllowed: false
- alias: direct grant
description: OpenID Connect Resource Owner Grant
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: direct-grant-validate-username
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticator: direct-grant-validate-password
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
- authenticatorFlow: true
requirement: CONDITIONAL
priority: 30
flowAlias: Direct Grant - Conditional OTP
userSetupAllowed: false
- alias: docker auth
description: Used by Docker clients to authenticate against the IDP
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: docker-http-basic-authenticator
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- alias: first broker login
description: Actions taken after first broker login with identity provider account,
which is not yet linked to any Keycloak account
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticatorConfig: review profile config
authenticator: idp-review-profile
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: REQUIRED
priority: 20
flowAlias: User creation or linking
userSetupAllowed: false
- alias: forms
description: Username, password, otp and other auth forms.
providerId: basic-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: auth-username-password-form
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: CONDITIONAL
priority: 20
flowAlias: Browser - Conditional OTP
userSetupAllowed: false
- alias: http challenge
description: An authentication flow based on challenge-response HTTP Authentication
Schemes
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: no-cookie-redirect
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticatorFlow: true
requirement: REQUIRED
priority: 20
flowAlias: Authentication Options
userSetupAllowed: false
- alias: registration
description: registration flow
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: registration-page-form
authenticatorFlow: true
requirement: REQUIRED
priority: 10
flowAlias: registration form
userSetupAllowed: false
- alias: registration form
description: registration form
providerId: form-flow
topLevel: false
builtIn: true
authenticationExecutions:
- authenticator: registration-user-creation
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
- authenticator: registration-profile-action
authenticatorFlow: false
requirement: REQUIRED
priority: 40
userSetupAllowed: false
- authenticator: registration-password-action
authenticatorFlow: false
requirement: REQUIRED
priority: 50
userSetupAllowed: false
- authenticator: registration-recaptcha-action
authenticatorFlow: false
requirement: DISABLED
priority: 60
userSetupAllowed: false
- alias: reset credentials
description: Reset credentials for a user if they forgot their password or something
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: reset-credentials-choose-user
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- authenticator: reset-credential-email
authenticatorFlow: false
requirement: REQUIRED
priority: 20
userSetupAllowed: false
- authenticator: reset-password
authenticatorFlow: false
requirement: REQUIRED
priority: 30
userSetupAllowed: false
- authenticatorFlow: true
requirement: CONDITIONAL
priority: 40
flowAlias: Reset - Conditional OTP
userSetupAllowed: false
- alias: saml ecp
description: SAML ECP Profile Authentication Flow
providerId: basic-flow
topLevel: true
builtIn: true
authenticationExecutions:
- authenticator: http-basic-authenticator
authenticatorFlow: false
requirement: REQUIRED
priority: 10
userSetupAllowed: false
- alias: x509 direct grant
id: 96f2c609-7966-4887-a51a-e647e14a3605
description: X509 Direct Grant
providerId: basic-flow
topLevel: true
builtIn: false
authenticationExecutions:
- authenticator: direct-grant-auth-x509-username
authenticatorConfig: x509 config
authenticatorFlow: false
requirement: REQUIRED
userSetupAllowed: false
authenticatorConfig:
- alias: create unique user config
config:
require.password.update.after.registration: 'false'
- alias: review profile config
config:
update.profile.on.first.login: missing
- alias: x509 config
config:
x509-cert-auth.canonical-dn-enabled: "false"
x509-cert-auth.crldp-checking-enabled: "false"
x509-cert-auth.mapper-selection: "Username or Email"
x509-cert-auth.mapping-source-selection: "Subject's Common Name"
x509-cert-auth.regular-expression: "(.*?)(?:$)"
x509-cert-auth.serialnumber-hex-enabled: "false"
x509-cert-auth.timestamp-validation-enabled: "false"
instanceSelector:
matchLabels:
app: keycloak
No response
I just noticed that the keycloak client is using context.TODO()
and have recently learned that this is not a good, as this context can never be cancelled. For example here: https://github.com/keycloak/keycloak-operator/blob/master/pkg/common/cluster_actions.go#L52
There is an interesting article on it here:
https://www.sohamkamani.com/blog/golang/2018-06-17-golang-using-context-cancellation/
The general idea is that you pass down a context that you have a handle to the cancel on, so that any point the context can be cancelled and all in progress or further requests will be unsuccessful, this is useful if, for example, the CR has just been deleted.
You can see how we use it in the integreatly-operator, here:
https://github.com/integr8ly/integreatly-operator/blob/master/pkg/controller/installation/installation_controller.go#L44-L55
When creating a Keycloak client via CRD with enabled: false, this isn't mirrored to the Keycloak Realm.
15.02
A client is created in the realm with enabled: false.
A client is created in the realm with enabled: true.
I use this Client CRD to create the client. A client appears with the expected name in the expected realm, but has enabled: true.
apiVersion: keycloak.org/v1alpha1
kind: KeycloakClient
metadata:
name: minimal-client-example
labels:
app: mykeycloak
spec:
realmSelector:
matchLabels:
realm: myrealm
client:
enabled: false
clientId: "minimal-client-example"
No response
Keycloak can't start as it takes longer in some env for the liveness and readiness probes to success
Be able to extend the time for the probes from configuration so keycloak can start when deploying for the first time.
Keycloak is being killed since readiness and liveness probes fail for the time config.
I have a use case where .x509 Authentication is used in a particular realm.
However the certificates used by these users are signed by a custom CA therefore unable to validate unless Keycloak has access to the CA.
How can we specify this CA cert when using the operator?
In this case the nginx ingress controller is used and therefore we may need to configure the client cert lookup.
How can we configure it when using the operator?
For standalone we use
{code}
nginx
{code}
14.0.0
Ability to add CA cert to the trust store as well as configure the client cert lookup.
No response
No response
No response
Change the Keycloak CRD and associated constructs to take in parameters defining different healthcheck criteria for liveness/readiness. Could be just a change to the failureThreshold, or the initialDelaySeconds, or a redefinition of what constitutes the health checks themselves.
No response
Deploying a Keycloak instance via the operator in a resource-constrained environment can lead to Kubernetes killing and restarting the deployment due to failing liveness probes, as it takes longer for the keycloak server to actually bootstrap (I imagine CPU throttling being the underlying cause in our case). Being able to extend or modify those health checks would allow for Keycloak deployment in a wider variety of environments.
I believe this will be a simple change to the CRD, and the const values defined in the accompanying KeycloakDeployment code in Go.
Add support for Microsoft SQL Server external databases to allow users to use MS SQL server if they prefer to do so. Currently the keycloak-operator only supports PostgreSQL external databases, but keycloak itself has support for many database engines.
No response
Currently keycloak-operator only supports PostgreSQL external databases. Providing a wider selection of options allows end users to choose the database they prefer. Support for MS SQL Server is already included in the Keycloak docker image.
No response
For app deployments using PKCE flows, I need to be able to create keycloak client with the access type set to "public", and the code challenge set to "S256". These attributes are not present in the KeycloakClient CRD schema that I can see.
No response
We are deploying applications that have user based accounts as well as backend services that need auth. So we require both the PKCE auth flow (for user accounts) and client credentials (for service auth). Web applications at a large require user login. I feel that if the KeycloakClient CRD only allows for client credentials, then it is not useful beyond machine-to-machine authentication.
No response
I am editing an existing KeycloakUser
in an attempt to disable the user's access. However, if the user is currently "enabled", setting enabled: false
or removing the enabled: true
fails to actually disable the user in Keycloak.
16.1.0
Keycloak should disable the user
Once a user is "enabled", it can never be "disabled".
Create a new "disabled" user:
cat <<EOF | kubectl -n keycloak apply -f -
apiVersion: keycloak.org/v1alpha1
kind: KeycloakUser
metadata:
name: disabled-user
spec:
user:
username: "disabled-user"
firstName: "Disabled"
lastName: "Test"
email: "[email protected]"
enabled: False
emailVerified: False
credentials:
- type: "password"
value: "12345"
realmRoles:
- "offline_access"
clientRoles:
account:
- "manage-account"
realm-management:
- "manage-users"
realmSelector:
matchLabels:
app: example-keycloak
EOF
Open Keycloak and verify that the user is disabled
Update the user to enable it.
kubectl patch -n keycloak --type=merge keycloakuser disabled-user -p '{"spec":{"user":{"enabled":true}}}'
keycloakuser.keycloak.org/disabled-user patched
Open Keycloak and verify the user is enabled
Attempt to disable the user
kubectl patch -n keycloak --type=merge keycloakuser disabled-user -p '{"spec":{"user":{"enabled":false}}}'
keycloakuser.keycloak.org/disabled-user patched
Keycloak controller logs show it successfully reconciling the user after each patch
{"level":"info","ts":1641405498.2681048,"logger":"controller_keycloakuser","msg":"Reconciling KeycloakUser","Request.Namespace":"keycloak","Request.Name":"disabled-user"}
{"level":"info","ts":1641405498.2682028,"logger":"controller_keycloakuser","msg":"found 1 matching realm(s) for user keycloak/disabled-user"}
{"level":"info","ts":1641405498.4976873,"logger":"action_runner","msg":"( 1) SUCCESS update user disabled-user"}
I followed these instructions and installed the Keycloak operator into a minikube cluster using the command line instructions.
When a new user is created, the operator create the relative secret without random password.
The documentation said that also a random password is created but unfortunately is not like this.
15.0.2
Secret will have both username and password not only username
Secret is created with only username
Create a user through Keycloak user example (without credentials)
No response
Currently while adding extra namespaces to the 'targetNamespaces' in the operatorgroup definition for keycloak an error is presented: 'installmodeset does not support operatorgroups namespace selection'.
I have not tried to run the operator on it's own yet and it seems like in cmd/manager/main.go there is support for multiple namespaces. Would it be as simple as enabling the support for deployments through OLM?
No response
Deploying an additional dev/staging keycloak would be pretty nice.
No response
When i delete a namespace that have a keycloak instance and a keycloak realm custom resource managed by the keycloak operator, the namespace get stuck in terminating state and it seems that it came from the keycloakRealm resource deletions
I let olm manage the operator version so i should be the latest available on olm
The keycloakRealm resource should be deleted when the namespace ask for it
The keycloakRealm resource won't be deleted and block the deletions of the namespace
Start a k8s cluster
Install OLM operator
Install Keycloak Operator
Create a Keycloak Instance in the namespace watched by the operator
Create a Keycloak Realm in the the namespace watched by the operator with a selector corresponding to the keycloak instance previously created
Check that both the keycloak instance and the realm are working
Delete the namespace containing the resources
You should the the namespace stuck in the terminating status and you won't be able to manually delete the realm
When i first delete the realm then delete the namespace it's working well. I think it's might be because the instance got deleted before the realm and then the realm can't delete itself from the non existing instance
I can only define scopes, resources, policies using KeycloakClient
CRD during creation of given client. When I change existing definition by adding new (or removing/modifying existing) scopes/policies/resources/permissions then changes aren't reflected until i drop and recreate the client. Is it expected behaviour?
15.0.2
I'd expect that applied change of defined scopes/resources/policies/permissions would be reflected in a given client Keycloak configuration.
Changes of authorization settings (policies/permissions/etc) applied to existing KeycloakClient
definition are ignored until the client is dropped and then re-created.
Create some basic client with some scopes and resources: i.e.
apiVersion: keycloak.org/v1alpha1
kind: KeycloakClient
metadata:
name: api
labels:
app: sso
spec:
realmSelector:
matchLabels:
app: sso
client:
clientId: api
secret: DEADBEAF
baseUrl: http://api.example
clientAuthenticatorType: client-secret
protocol: openid-connect
serviceAccountsEnabled: true
authorizationServicesEnabled: true
authorizationSettings:
scopes:
- name: "Scope"
displayName: "Scope"
resources:
- name: "Resource"
displayName: "Resource"
uris:
- "/RESOURCE/*"
scopes:
- name: "resource.scope"
create that client:
kubectl apply -f client.yaml
and then try to modify or extend scopes or resources, i.e.
apiVersion: keycloak.org/v1alpha1
kind: KeycloakClient
metadata:
name: api
labels:
app: sso
spec:
realmSelector:
matchLabels:
app: sso
client:
clientId: api
secret: DEADBEAF
baseUrl: http://api.example
clientAuthenticatorType: client-secret
protocol: openid-connect
serviceAccountsEnabled: true
authorizationServicesEnabled: true
authorizationSettings:
scopes:
- name: "Scope"
displayName: "Scope"
- name: "New Scope"
displayName: "New Scope"
resources:
- name: "Resource with new name"
displayName: "Resource"
uris:
- "/RESOURCE/*"
scopes:
- name: "resource.scope"
and apply changes:
kubectl apply -f client.yaml
Changes aren't reflected until I recreate the client.
No response
It is possible to configure the domain of the ingress controller?.
example: keycloak.test.com.ar
Configure ingress url in keycloak custom resource.
There are no properties to create ingress domain.
Default ingress IP address
Kubernetes version: 1.14
Regards
Hi, I am trying to create local keycloak operator image by command operator-sdk build <image>
However, I meet such following error. Any advice is helpful!
(seems there is no .dockerignore file in this folder)
Step 9/15 : COPY tools /opt/jboss/tools FAILs when build operator image
COPY failed: stat /var/lib/docker/tmp/docker-builderxxxxxxxxx/tools: no such file or directory
succeed to build operator image
Step 9/15 : COPY tools /opt/jboss/tools
COPY failed: stat /var/lib/docker/tmp/docker-builder896005541/tools: no such file or directory
Error: failed to output build image xxxxxxx/operator-hub/official-keycloak-operator: (failed to exec []string{"docker", "build", "-f", "build/Dockerfile", "-t", "xxxxxxx/operator-hub/official-keycloak-operator", "."}: exit status 1)
Server https://127.0.0.1:8443
kubernetes v1.11.0+d4cacc0
operator-sdk build xxxxxxxx/operator-hub/official-keycloak-operator
$ kubectl describe service keycloak -n keycloak
Type: ClusterIP
IP: 10.108.62.181
Port: keycloak 8443/TCP
TargetPort: 8443/TCP
Endpoints: 172.17.0.4:8443
while keycloak pod ip is using port 8080:
livenessProbe:
failureThreshold: 3
httpGet:
path: /auth/realms/master
port: 8080
scheme: HTTP
Due to this error, we cannot visit keycloak page through ingress.
Either fix the keycloak pod port or change the target port of keycloak service will fix this error.
We run tainted nodes which require us to annotate our pods, having this facility in the operator would be very helpful
No response
No response
No response
Expose DB_SCHEMA
to the operator configuration.
The database configuration should be more normal and specified more flexibly. Consider an immutable CRD specifically for configuration like this.
16.1.0
There should be a way to specify what schema is used by Keycloak.
There is not.
Try to configure a schema other than public
.
No response
The theme never gets deployed, there is no track of the extension being downloaded during the deployment process.:
kubectl get keycloak -n keycloak-operator keycloak-test -oyaml
apiVersion: keycloak.org/v1alpha1
kind: Keycloak
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"keycloak.org/v1alpha1","kind":"Keycloak","metadata":{"annotations":{},"labels":{"app":"sso"},"name":"keycloak-test","namespace":"keycloak-operator"},"spec":{"externalAccess":{"enabled":true,"host":"auth.domain.de"},"externalDatabase":{"enabled":true},"instances":1,"keycloakDeploymentSpec":{"experimental":{"env":[{"name":"PROXY_ADDRESS_FORWARDING","value":"true"}]}},"migration":{"backups":{"enabled":true},"strategy":"recreate"}}}
creationTimestamp: "2021-11-04T13:52:16Z"
generation: 3
labels:
app: sso
name: keycloak-test
namespace: keycloak-operator
resourceVersion: "525798390"
uid: 563b5a3b-b33e-4b08-a895-9a2b026f236e
spec:
extensions:
- https://sourcecode.domain.de/sysadmin-public/keycloack-domain-theme/-/raw/master/theme.jar
externalAccess:
enabled: true
host: auth.domain.de
externalDatabase:
enabled: true
instances: 1
keycloakDeploymentSpec:
experimental:
env:
- name: PROXY_ADDRESS_FORWARDING
value: "true"
migration:
backups:
enabled: false
strategy: recreate
status:
credentialSecret: credential-keycloak-test
externalURL: https://auth.domain.de
internalURL: https://keycloak.keycloak-operator.svc:8443
message: ""
phase: reconciling
ready: true
secondaryResources:
ConfigMap:
- keycloak-probes
Ingress:
- keycloak
Secret:
- keycloak-db-secret
- credential-keycloak-test
Service:
- keycloak-postgresql
- keycloak
- keycloak-discovery
StatefulSet:
- keycloak
version: 16.1.0
16.1.0
deploy the theme
doesn't do it
just adding:
spec:
extensions:
- https://sourcecode.domain.de/sysadmin-public/keycloack-domain-theme/-/raw/master/theme.jar
to the CR
No response
The operator has set the pod env DB_ADDR
value to keycloak-postgresql.keycloak
instead of the value of POSTGRES_EXTERNAL_ADDRESS
in Secret
keycloak-db-secret
in the same namespace (keycloak
). This behavior is unexpected. Seems to be a bug, but is there some documentation missing? The values for DB_DATABASE
, DB_PASSWORD
, DB_USER
, and DB_PORT
seem correct.
Was the operator supposed to start a proxy service to the database with name keycloak-postgresql
before keycloak-0
was supposed to start? I don't know if I saw this documented anywhere. This causes the Keycloak pod to not start:
WARN [org.jboss.jca.core.connectionmanager.pool.strategy.OnePool] (ServerService Thread Pool -- 63) IJ000604: Throwable while attempting to get a new connection: null: javax.resource.ResourceException: IJ031084: Unable to create connection
[...]
Caused by: org.postgresql.util.PSQLException: The connection attempt failed.
[...]
Caused by: java.net.UnknownHostException: keycloak-postgresql.keycloak
Here is the 'Keycloak' resource used:
apiVersion: keycloak.org/v1alpha1
kind: Keycloak
metadata:
creationTimestamp: '2021-12-20T16:43:39Z'
generation: 2
labels:
app: keycloak
managedFields:
- apiVersion: keycloak.org/v1alpha1
fieldsType: FieldsV1
fieldsV1:
'f:metadata':
'f:labels':
'f:app': {}
'f:spec':
'f:extensions':
'v:"https://github.com/aerogear/keycloak-metrics-spi/releases/download/1.0.4/keycloak-metrics-spi-1.0.4.jar"': {}
'f:externalDatabase':
'f:enabled': {}
'f:instances': {}
'f:podDisruptionBudget':
'f:enabled': {}
manager: Terraform
operation: Apply
time: '2021-12-20T17:09:21Z'
- apiVersion: keycloak.org/v1alpha1
fieldsType: FieldsV1
fieldsV1:
'f:status':
.: {}
'f:credentialSecret': {}
'f:internalURL': {}
'f:message': {}
'f:phase': {}
'f:ready': {}
'f:secondaryResources':
.: {}
'f:ConfigMap': {}
'f:PodDisruptionBudget': {}
'f:Secret': {}
'f:Service': {}
'f:StatefulSet': {}
'f:version': {}
manager: keycloak-operator
operation: Update
time: '2021-12-20T16:44:16Z'
name: keycloak-db1
namespace: keycloak
resourceVersion: '13752823'
selfLink: /apis/keycloak.org/v1alpha1/namespaces/keycloak/keycloaks/keycloak-db1
uid: f24c88ed-a272-491c-9f41-396340f6d414
spec:
extensions:
- >-
https://github.com/aerogear/keycloak-metrics-spi/releases/download/1.0.4/keycloak-metrics-spi-1.0.4.jar
externalDatabase:
enabled: true
instances: 1
podDisruptionBudget:
enabled: true
status:
credentialSecret: credential-keycloak-db1
internalURL: 'https://keycloak.keycloak.svc:8443'
message: ''
phase: initialising
ready: false
secondaryResources:
ConfigMap:
- keycloak-probes
PodDisruptionBudget:
- keycloak
Secret:
- keycloak-db-secret
- credential-keycloak-db1
Service:
- keycloak-postgresql
- keycloak
- keycloak-discovery
StatefulSet:
- keycloak
version: 15.1.1
And the following secret (decoded and redacted):
kind: Secret
apiVersion: v1
metadata:
name: keycloak-db-secret
namespace: keycloak
data:
POSTGRES_DATABASE: db1
POSTGRES_EXTERNAL_ADDRESS: db1-primary.database.svc
POSTGRES_EXTERNAL_PORT: 5432
POSTGRES_PASSWORD: [... REDACTED ...]
POSTGRES_USERNAME: db1
type: Opaque
Which results in this pod:
kind: Pod
apiVersion: v1
metadata:
name: keycloak-0
generateName: keycloak-
namespace: keycloak
selfLink: /api/v1/namespaces/keycloak/pods/keycloak-0
uid: 10a2415e-f2e1-423b-8bb0-769b7190cabf
resourceVersion: '13784573'
creationTimestamp: '2021-12-20T17:12:19Z'
labels:
app: keycloak
component: keycloak
controller-revision-hash: keycloak-74ff4c8f8b
statefulset.kubernetes.io/pod-name: keycloak-0
annotations:
cni.projectcalico.org/podIP: 10.1.7.254/32
cni.projectcalico.org/podIPs: 10.1.7.254/32
ownerReferences:
- apiVersion: apps/v1
kind: StatefulSet
name: keycloak
uid: 44287c9e-04e1-4553-a644-c82fc7f744c6
controller: true
blockOwnerDeletion: true
managedFields:
- manager: calico
operation: Update
apiVersion: v1
time: '2021-12-20T17:12:19Z'
fieldsType: FieldsV1
fieldsV1:
'f:metadata':
'f:annotations':
.: {}
'f:cni.projectcalico.org/podIP': {}
'f:cni.projectcalico.org/podIPs': {}
- manager: kubelite
operation: Update
apiVersion: v1
time: '2021-12-20T17:12:22Z'
fieldsType: FieldsV1
fieldsV1:
'f:metadata':
'f:generateName': {}
'f:labels':
.: {}
'f:app': {}
'f:component': {}
'f:controller-revision-hash': {}
'f:statefulset.kubernetes.io/pod-name': {}
'f:ownerReferences':
.: {}
'k:{"uid":"44287c9e-04e1-4553-a644-c82fc7f744c6"}':
.: {}
'f:apiVersion': {}
'f:blockOwnerDeletion': {}
'f:controller': {}
'f:kind': {}
'f:name': {}
'f:uid': {}
'f:spec':
'f:containers':
'k:{"name":"keycloak"}':
.: {}
'f:env':
.: {}
'k:{"name":"CACHE_OWNERS_AUTH_SESSIONS_COUNT"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"CACHE_OWNERS_COUNT"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"DB_ADDR"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"DB_DATABASE"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"DB_PASSWORD"}':
.: {}
'f:name': {}
'f:valueFrom':
.: {}
'f:secretKeyRef':
.: {}
'f:key': {}
'f:name': {}
'k:{"name":"DB_PORT"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"DB_SCHEMA"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"DB_USER"}':
.: {}
'f:name': {}
'f:valueFrom':
.: {}
'f:secretKeyRef':
.: {}
'f:key': {}
'f:name': {}
'k:{"name":"DB_VENDOR"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"JGROUPS_DISCOVERY_PROPERTIES"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"JGROUPS_DISCOVERY_PROTOCOL"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"KEYCLOAK_PASSWORD"}':
.: {}
'f:name': {}
'f:valueFrom':
.: {}
'f:secretKeyRef':
.: {}
'f:key': {}
'f:name': {}
'k:{"name":"KEYCLOAK_POSTGRESQL_SERVICE_HOST"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"KEYCLOAK_POSTGRESQL_SERVICE_PORT"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"KEYCLOAK_STATISTICS"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"KEYCLOAK_USER"}':
.: {}
'f:name': {}
'f:valueFrom':
.: {}
'f:secretKeyRef':
.: {}
'f:key': {}
'f:name': {}
'k:{"name":"NAMESPACE"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"PROXY_ADDRESS_FORWARDING"}':
.: {}
'f:name': {}
'f:value': {}
'k:{"name":"X509_CA_BUNDLE"}':
.: {}
'f:name': {}
'f:value': {}
'f:image': {}
'f:imagePullPolicy': {}
'f:livenessProbe':
.: {}
'f:exec':
.: {}
'f:command': {}
'f:failureThreshold': {}
'f:initialDelaySeconds': {}
'f:periodSeconds': {}
'f:successThreshold': {}
'f:timeoutSeconds': {}
'f:name': {}
'f:ports':
.: {}
'k:{"containerPort":8443,"protocol":"TCP"}':
.: {}
'f:containerPort': {}
'f:protocol': {}
'k:{"containerPort":8778,"protocol":"TCP"}':
.: {}
'f:containerPort': {}
'f:protocol': {}
'k:{"containerPort":9990,"protocol":"TCP"}':
.: {}
'f:containerPort': {}
'f:protocol': {}
'f:readinessProbe':
.: {}
'f:exec':
.: {}
'f:command': {}
'f:failureThreshold': {}
'f:initialDelaySeconds': {}
'f:periodSeconds': {}
'f:successThreshold': {}
'f:timeoutSeconds': {}
'f:resources': {}
'f:terminationMessagePath': {}
'f:terminationMessagePolicy': {}
'f:volumeMounts':
.: {}
'k:{"mountPath":"/etc/x509/https"}':
.: {}
'f:mountPath': {}
'f:name': {}
'k:{"mountPath":"/opt/jboss/keycloak/standalone/deployments"}':
.: {}
'f:mountPath': {}
'f:name': {}
'k:{"mountPath":"/probes"}':
.: {}
'f:mountPath': {}
'f:name': {}
'f:dnsPolicy': {}
'f:enableServiceLinks': {}
'f:hostname': {}
'f:initContainers':
.: {}
'k:{"name":"extensions-init"}':
.: {}
'f:env':
.: {}
'k:{"name":"KEYCLOAK_EXTENSIONS"}':
.: {}
'f:name': {}
'f:value': {}
'f:image': {}
'f:imagePullPolicy': {}
'f:name': {}
'f:resources': {}
'f:terminationMessagePath': {}
'f:terminationMessagePolicy': {}
'f:volumeMounts':
.: {}
'k:{"mountPath":"/opt/extensions"}':
.: {}
'f:mountPath': {}
'f:name': {}
'f:restartPolicy': {}
'f:schedulerName': {}
'f:securityContext': {}
'f:serviceAccount': {}
'f:serviceAccountName': {}
'f:terminationGracePeriodSeconds': {}
'f:volumes':
.: {}
'k:{"name":"keycloak-extensions"}':
.: {}
'f:emptyDir': {}
'f:name': {}
'k:{"name":"keycloak-probes"}':
.: {}
'f:configMap':
.: {}
'f:defaultMode': {}
'f:name': {}
'f:name': {}
'k:{"name":"sso-x509-https-secret"}':
.: {}
'f:name': {}
'f:secret':
.: {}
'f:defaultMode': {}
'f:optional': {}
'f:secretName': {}
'f:status':
'f:conditions':
'k:{"type":"ContainersReady"}':
.: {}
'f:lastProbeTime': {}
'f:lastTransitionTime': {}
'f:message': {}
'f:reason': {}
'f:status': {}
'f:type': {}
'k:{"type":"Initialized"}':
.: {}
'f:lastProbeTime': {}
'f:lastTransitionTime': {}
'f:status': {}
'f:type': {}
'k:{"type":"Ready"}':
.: {}
'f:lastProbeTime': {}
'f:lastTransitionTime': {}
'f:message': {}
'f:reason': {}
'f:status': {}
'f:type': {}
'f:containerStatuses': {}
'f:hostIP': {}
'f:initContainerStatuses': {}
'f:phase': {}
'f:podIP': {}
'f:podIPs':
.: {}
'k:{"ip":"10.1.7.254"}':
.: {}
'f:ip': {}
'f:startTime': {}
spec:
volumes:
- name: sso-x509-https-secret
secret:
secretName: sso-x509-https-secret
defaultMode: 420
optional: true
- name: keycloak-extensions
emptyDir: {}
- name: keycloak-probes
configMap:
name: keycloak-probes
defaultMode: 365
- name: kube-api-access-qpglx
projected:
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
defaultMode: 420
initContainers:
- name: extensions-init
image: 'quay.io/keycloak/keycloak-init-container:master'
env:
- name: KEYCLOAK_EXTENSIONS
value: >-
https://github.com/aerogear/keycloak-metrics-spi/releases/download/1.0.4/keycloak-metrics-spi-1.0.4.jar
resources: {}
volumeMounts:
- name: keycloak-extensions
mountPath: /opt/extensions
- name: kube-api-access-qpglx
readOnly: true
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
containers:
- name: keycloak
image: 'quay.io/keycloak/keycloak:15.1.1'
ports:
- containerPort: 8443
protocol: TCP
- containerPort: 9990
protocol: TCP
- containerPort: 8778
protocol: TCP
env:
- name: DB_VENDOR
value: POSTGRES
- name: DB_SCHEMA
value: public
- name: DB_ADDR
value: keycloak-postgresql.keycloak
- name: DB_PORT
value: '5432'
- name: DB_DATABASE
value: db1
- name: DB_USER
valueFrom:
secretKeyRef:
name: keycloak-db-secret
key: POSTGRES_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-db-secret
key: POSTGRES_PASSWORD
- name: NAMESPACE
value: keycloak
- name: JGROUPS_DISCOVERY_PROTOCOL
value: dns.DNS_PING
- name: JGROUPS_DISCOVERY_PROPERTIES
value: dns_query=keycloak-discovery.keycloak
- name: CACHE_OWNERS_COUNT
value: '2'
- name: CACHE_OWNERS_AUTH_SESSIONS_COUNT
value: '2'
- name: KEYCLOAK_USER
valueFrom:
secretKeyRef:
name: credential-keycloak-db1
key: ADMIN_USERNAME
- name: KEYCLOAK_PASSWORD
valueFrom:
secretKeyRef:
name: credential-keycloak-db1
key: ADMIN_PASSWORD
- name: X509_CA_BUNDLE
value: /var/run/secrets/kubernetes.io/serviceaccount/*.crt
- name: PROXY_ADDRESS_FORWARDING
value: 'true'
- name: KEYCLOAK_STATISTICS
value: all
- name: KEYCLOAK_POSTGRESQL_SERVICE_HOST
value: keycloak-postgresql.keycloak.svc.cluster.local
- name: KEYCLOAK_POSTGRESQL_SERVICE_PORT
value: '5432'
resources: {}
volumeMounts:
- name: sso-x509-https-secret
mountPath: /etc/x509/https
- name: keycloak-extensions
mountPath: /opt/jboss/keycloak/standalone/deployments
- name: keycloak-probes
mountPath: /probes
- name: kube-api-access-qpglx
readOnly: true
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
livenessProbe:
exec:
command:
- /bin/sh
- '-c'
- /probes/liveness_probe.sh
initialDelaySeconds: 30
timeoutSeconds: 22
periodSeconds: 30
successThreshold: 1
failureThreshold: 10
readinessProbe:
exec:
command:
- /bin/sh
- '-c'
- /probes/readiness_probe.sh
initialDelaySeconds: 40
timeoutSeconds: 22
periodSeconds: 30
successThreshold: 1
failureThreshold: 10
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
serviceAccountName: default
serviceAccount: default
nodeName: [... REDACTED ...]
securityContext: {}
hostname: keycloak-0
schedulerName: default-scheduler
tolerations:
- key: node.kubernetes.io/not-ready
operator: Exists
effect: NoExecute
tolerationSeconds: 300
- key: node.kubernetes.io/unreachable
operator: Exists
effect: NoExecute
tolerationSeconds: 300
priority: 0
enableServiceLinks: true
preemptionPolicy: PreemptLowerPriority
status:
phase: Running
conditions:
- type: Initialized
status: 'True'
lastProbeTime: null
lastTransitionTime: '2021-12-20T17:12:21Z'
- type: Ready
status: 'False'
lastProbeTime: null
lastTransitionTime: '2021-12-20T17:12:19Z'
reason: ContainersNotReady
message: 'containers with unready status: [keycloak]'
- type: ContainersReady
status: 'False'
lastProbeTime: null
lastTransitionTime: '2021-12-20T17:12:19Z'
reason: ContainersNotReady
message: 'containers with unready status: [keycloak]'
- type: PodScheduled
status: 'True'
lastProbeTime: null
lastTransitionTime: '2021-12-20T17:12:19Z'
hostIP: 192.168.1.42
podIP: 10.1.7.254
podIPs:
- ip: 10.1.7.254
startTime: '2021-12-20T17:12:19Z'
initContainerStatuses:
- name: extensions-init
state:
terminated:
exitCode: 0
reason: Completed
startedAt: '2021-12-20T17:12:20Z'
finishedAt: '2021-12-20T17:12:21Z'
containerID: >-
containerd://c57c093c72ae93565a6550bf0147b077577b83217f95ddca0254a8016010cf8e
lastState: {}
ready: true
restartCount: 0
image: 'quay.io/keycloak/keycloak-init-container:master'
imageID: 'sha256:37b4a18e3c2ca9369490079af2f0526d48d909d07951d4ff1f44cea105af6c68'
containerID: >-
containerd://c57c093c72ae93565a6550bf0147b077577b83217f95ddca0254a8016010cf8e
containerStatuses:
- name: keycloak
state:
waiting:
reason: CrashLoopBackOff
message: >-
back-off 5m0s restarting failed container=keycloak
pod=keycloak-0_keycloak(10a2415e-f2e1-423b-8bb0-769b7190cabf)
lastState:
terminated:
exitCode: 1
reason: Error
startedAt: '2021-12-20T17:39:38Z'
finishedAt: '2021-12-20T17:40:18Z'
containerID: >-
containerd://2fa6acd7e41871017e09e8a9259ac95fdefe6ffd8708dca53a9bfc3c5a5d3c31
ready: false
restartCount: 9
image: 'quay.io/keycloak/keycloak:15.1.1'
imageID: >-
quay.io/keycloak/keycloak@sha256:abade9a9cf985b454b30a9119183e62a0018ec546c8fc4fbd8572457afb4a666
containerID: >-
containerd://2fa6acd7e41871017e09e8a9259ac95fdefe6ffd8708dca53a9bfc3c5a5d3c31
started: false
qosClass: BestEffort
15.1.1
DB_ADDR
should directly be set from POSTGRES_EXTERNAL_ADDRESS
in the keycloak-db-secret
Secret
.
DB_ADDR
was set to keycloak-postgresql.keycloak
.
See info in description above.
Running in microk8s on Ubuntu 20.04 deployed using Terraform.
Publish Keycloak Operator 15.1.1 in OperatorHub.io
No response
New KC version
No response
Not all nginx ingress admission controllers allow the provisioning of the "nginx.ingress.kubernetes.io/server-snippet" annotation; this is mainly related to CVE-2021-25742 discussed here: kubernetes/ingress-nginx#7837.
https://groups.google.com/g/keycloak-dev/c/ONHs1-wyDAw
To make the keycloak-operator be usable in environments where the "nginx.ingress.kubernetes.io/server-snippet" annotation is not allowed.
Going to submit a PR that introduces an environment variable in order to skip that one.
The operator logs look fine:
s","controller":"keycloakbackup-controller","worker count":1}
{"level":"info","ts":1640102636.31718,"logger":"controller_keycloakbackup","msg":"Reconciling KeycloakBackup","Request.Namespace":"keycloak-operator","Request.Name":"keycloak-minio-backup-scheduler"}
{"level":"info","ts":1640102638.7181783,"logger":"controller-runtime.controller","msg":"Starting Controller","controller":"keycloakuser-controller"}
{"level":"info","ts":1640102642.6195655,"logger":"controller-runtime.controller","msg":"Starting workers","controller":"keycloakuser-controller","worker count":1}
I1221 16:04:05.216134 1 request.go:655] Throttling request took 2.498708727s, request: GET:https://10.133.0.1:443/apis/batch/v1beta1?timeout=32s
{"level":"info","ts":1640102647.3250031,"logger":"action_runner","msg":"( 0) SUCCESS Update Keycloak admin secret"}
{"level":"info","ts":1640102647.3295093,"logger":"action_runner","msg":"( 1) SUCCESS Update Postgresql KeycloakService"}
{"level":"info","ts":1640102647.3330376,"logger":"action_runner","msg":"( 2) SUCCESS Update keycloak Service"}
{"level":"info","ts":1640102647.3362212,"logger":"action_runner","msg":"( 3) SUCCESS Update keycloak Discovery Service"}
{"level":"info","ts":1640102647.3464978,"logger":"action_runner","msg":"( 4) SUCCESS Update Keycloak Deployment (StatefulSet)"}
{"level":"info","ts":1640102647.3492367,"logger":"action_runner","msg":"( 5) SUCCESS Update Keycloak Ingress"}
{"level":"info","ts":1640102647.3549726,"logger":"controller_keycloak","msg":"desired cluster state met"}
{"level":"info","ts":1640102647.6260815,"logger":"controller_keycloakbackup","msg":"Reconciling KeycloakBackup","Request.Namespace":"keycloak-operator","Request.Name":"keycloak-minio-backup-scheduler"}
{"level":"info","ts":1640102652.631358,"logger":"controller_keycloakbackup","msg":"Reconciling KeycloakBackup","Request.Namespace":"keycloak-operator","Request.Name":"keycloak-minio-backup-scheduler"}
I1221 16:04:15.258599 1 request.go:655] Throttling request took 3.598538261s, request: GET:https://10.133.0.1:443/apis/flowcontrol.apiserver.k8s.io/v1beta1?timeout=32s
{"level":"info","ts":1640102657.6372457,"logger":"controller_keycloakbackup","msg":"Reconciling KeycloakBackup","Request.Namespace":"keycloak-operator","Request.Name":"keycloak-minio-backup-scheduler"}
{"level":"info","ts":1640102662.6418972,"logger":"controller_keycloakbackup","msg":"Reconciling KeycloakBackup","Request.Namespace":"keycloak-operator","Request.Name":"keycloak-minio-backup-scheduler"}
I1221 16:04:25.308378 1 request.go:655] Throttling request took 1.948071215s, request: GET:https://10.133.0.1:443/apis/velero.io/v1?timeout=32s
{"level":"info","ts":1640102667.6500669,"logger":"controller_keycloakbackup","msg":"Reconciling KeycloakBackup","Request.Namespace":"keycloak-operator","Request.Name":"keycloak-minio-backup-scheduler"}
but the keycloak is not being updated like defined in the instance:
apiVersion: keycloak.org/v1alpha1
kind: Keycloak
metadata:
name: keycloak-test
labels:
app: sso
namespace: keycloak-operator
spec:
instances: 1
#extensions:
#- https://sourcecode.domain.com/sysadmin-public/keycloack-theme/-/raw/master/theme.jar
migration:
strategy: recreate
backups:
enabled: True
externalAccess:
enabled: true
host: "auth-test.ktest.domain.com"
externalDatabase:
enabled: true
keycloakDeploymentSpec:
experimental:
env:
- name: PROXY_ADDRESS_FORWARDING
value: "true"
- name: RELATED_IMAGE_KEYCLOAK
value: "quay.io/keycloak/keycloak:16.1.0"
- name: RELATED_IMAGE_KEYCLOAK_INIT_CONTAINER
value: "quay.io/keycloak/keycloak-init-container:16.1.0"
16.1.0
update the deployment and probe working
keucloak is down:
Normal Pulling 21m kubelet Pulling image "quay.io/keycloak/keycloak-init-container:master"
Normal Pulled 21m kubelet Successfully pulled image "quay.io/keycloak/keycloak-init-container:master" in 961.60902ms
Normal Created 21m kubelet Created container extensions-init
Normal Started 21m kubelet Started container extensions-init
Normal Pulled 21m kubelet Container image "quay.io/keycloak/keycloak:16.0.0" already present on machine
Normal Created 21m kubelet Created container keycloak
Normal Started 21m kubelet Started container keycloak
Warning Unhealthy 20m kubelet Liveness probe failed:
Warning Unhealthy 40s (x40 over 20m) kubelet Readiness probe failed:
No response
No response
I am editing an existing KeycloakClient in an attempt update some boolean fields.
However, if the fields are currently set to "true", setting them to "false" fails to actually update the client in Keycloak.
Not all boolean fields were tested, some worked, but some didn't.
Fields that didn't get updated:
Fields that got updated:
Transition from enabled: false
to enabled: true
is working fine in all tested fields.
16.1.0
All the fields should be updated.
Not all boolean fields were tested, some were updated, but some didn't.
Fields that didn't get updated:
Fields that got updated:
You can try these steps with the other reported fields:
I found the issue #412, related to the "enabled" field. As I was not sure if these other fields would be treated there, I opened this issue, sorry if this is considered a duplicate.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.