Code Monkey home page Code Monkey logo

keycloak-operator's Introduction

ARCHIVED Operator for Keycloak WildFly distribution

With Keycloak 20 the WildFly based distribution is no longer supported. For the newer Quarkus distribution of Keycloak, check out the new documentation, or the updated Operator sources.

keycloak-operator's People

Contributors

abstractj avatar aszc avatar briangallagher avatar cathaloconnorrh avatar chlunde avatar christianwoehrle avatar clive-jevons avatar david-martin avatar davidffrench avatar davidkirwan avatar drichtarik avatar grdryn avatar kampe avatar kervel avatar mashail avatar matskiv avatar mhajas avatar miquelsi avatar nbjohnson avatar nbonavia avatar pb82 avatar pskopek avatar rajagopalan-ranganathan avatar robbilie avatar sergioifg94 avatar slaskawi avatar smithjosh avatar soleblaze avatar stianst avatar vmuzikar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

keycloak-operator's Issues

Add support for Microsoft SQL Server external database

Description

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.

Discussion

No response

Motivation

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.

Details

No response

OLM structure for 16.1.0

Description

Create OLM structure for 16.1.0 to be published on operatorhub

Discussion

No response

Motivation

No response

Details

No response

Operator is not synchronizing realm changes to Keycloak

Describe the bug

Documentation says that KeycloakRealms are automatically synchronized, but this is not working.

Version

16.1.0

Expected behavior

When we apply the changes to the realm CR, the operator should update the realm at Keycloak.

Actual behavior

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:

  • displayName
  • enabled
  • registrationAllowed

How to Reproduce?

  • Create a Realm CR yaml file
  • Apply the file to create the CR
  • Operator will create the realm
  • Modify some info at yaml file
  • Apply the file to modify the CR
  • The CR is updated, but the realm is not change as we can see at web console

Anything else?

Already reported twice in former issue tracking system:

KeycloakRealm deletion stuck on namespace deletion

Describe the bug

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

Version

I let olm manage the operator version so i should be the latest available on olm

Expected behavior

The keycloakRealm resource should be deleted when the namespace ask for it

Actual behavior

The keycloakRealm resource won't be deleted and block the deletions of the namespace

How to Reproduce?

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

Anything else?

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

Suspected ERROR on keycloak service target port

Description

$ 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.

How to change the version of Keycloak?

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!

Setting "false" to some boolean fields for an existing client does not actually change the client on keycloak

Describe the bug

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:

  • enabled
  • consentRequired
  • serviceAccountsEnabled

Fields that got updated:

  • standardFlowEnabled
  • implicitFlowEnabled
  • directAccessGrantsEnabled

Transition from enabled: false to enabled: true is working fine in all tested fields.

Version

16.1.0

Expected behavior

All the fields should be updated.

Actual behavior

Not all boolean fields were tested, some were updated, but some didn't.

Fields that didn't get updated:

  • enabled
  • consentRequired
  • serviceAccountsEnabled

Fields that got updated:

  • standardFlowEnabled
  • implicitFlowEnabled
  • directAccessGrantsEnabled

How to Reproduce?

  • Create a new "disabled" client
  • Open Keycloak and verify that the client is disabled
  • Update the client to enable it.
  • Open Keycloak and verify the client is enabled
  • Attempt to disable the Client
  • Open Keycloak and verify the client is not update to disabled

You can try these steps with the other reported fields:

  • consentRequired
  • serviceAccountsEnabled

Anything else?

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.

Missing password on user credentials

Describe the bug

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.

Version

15.0.2

Expected behavior

Secret will have both username and password not only username

Actual behavior

Secret is created with only username

How to Reproduce?

Create a user through Keycloak user example (without credentials)

Anything else?

No response

KeycloakAPIAuthenticationFlow ID not set

Describe the bug

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…

Version

15.1.1

Expected behavior

The Flow in Keycloak has the configured ID.

Actual behavior

The Flow in Keycloak has a newly generated ID.

How to Reproduce?

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

Anything else?

No response

Keycloak POD intermittently restarted because PostgreSQL POD is not yet ready

Describe the bug

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)

Version

15.0.2 (rhsso-operator.7.5.1-opr-001)

Expected behavior

no restart of Keycloak POD is needed

Actual behavior

No response

How to Reproduce?

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;

Anything else?

No response

Admin password gets outdated when Keycloak is recreated having external database turned on

Describe the bug

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-".

Version

1.16.0

Expected behavior

The admin password is expected to be updated, so we can use it at the keycloak web console and through REST API calls.

Actual behavior

The admin password is not getting updated, and we cannot login into Keycloak's web console nor through REST API calls.

How to Reproduce?

  • Configure an deploy a Keycloak resource using the external database option
  • At this point it's possible to login to the Keycloak web console using the password stored in the secret named "credentials-".
  • Delete the Keycloak resource
  • At this point the stateful set and respective pods should get destroyed, but the external database remains untouched
  • Recreate the Keycloak resource
  • At this point, "credentials-" secret will receive new values, but the corresponding data (admin password) is not updated in the Kecloak's database. The result is we cannot login to the web console usign these crentials and CR are not getting created because they try to login using this non-working admin password stored in the secret.

Anything else?

  • We have found a workarond for this: everytime the problem occurs, we can get rid of it deleting the admin user information in the database using these queries:
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

Set "accessType" and "codeChallenge" in KeycloakClient CRD

Description

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.

Discussion

No response

Motivation

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.

Details

No response

Error creating a user in different namespace than his realm

Describe the bug

When creating a user in a different namespace than his realm, user is not properly created

Version

16.1.0

Expected behavior

User created properly, with error messages at keycloak's console neither operator's console, and without the reported side-effects

Actual behavior

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:

  • if we try to delete the resource using kubectl delete, the CR gets deleted, but the user remains in keycloak
  • if we try to recreate the same user afterwards, it results in a "409 conflit", because the user will be still in the keycloak database

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.

How to Reproduce?

  • Install the operator having the variable WATCH_NAMESPACE set to ""
  • Create a basic-realm from the operator's docs in a namespace called, for example, namespace1
  • Create a user in another namespace, for example, namespace2
  • This will result in 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"
  • Afterwards, if we try to delete the resource using kubectl delete, the CR gets deleted, but the user remains in keycloak
  • if we try to recreate the same user afterwards, it results in a "409 conflit", because the user will be still in the keycloak database

Anything else?

  • We need to test this in an environment that has the operator deployed with the variable WATCH_NAMESPACE set to "", so it can watch over all namespaces in the cluster.
  • If this is confirmed as a bug, is there any workaround you can think of, like for example: there is a way to provide our own created secret to the user so it don't has to be created by the operator?

Keycloak CRD should allow for customizing liveness/readiness probes

Description

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.

Discussion

No response

Motivation

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.

Details

I believe this will be a simple change to the CRD, and the const values defined in the accompanying KeycloakDeployment code in Go.

COPY failed: stat /var/lib/docker/tmp/docker-builderxxxxxxxxx/tools: no such file or directory

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)

Description

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

Expected Behavior

succeed to build operator image

Actual Behavior

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)

Environment

  • OpenShift or Kubernetes version:
    oc v3.11.0+0cbc58b
    kubernetes v1.11.0+d4cacc0
    features: Basic-Auth GSSAPI Kerberos SPNEGO

Server https://127.0.0.1:8443
kubernetes v1.11.0+d4cacc0

  • Project Version/Tag: (E.g release-1.0.1)

Steps to reproduce

operator-sdk build xxxxxxxx/operator-hub/official-keycloak-operator

Liveness and Readiness fails are killing pod

Description

Keycloak can't start as it takes longer in some env for the liveness and readiness probes to success

Expected Behavior

Be able to extend the time for the probes from configuration so keycloak can start when deploying for the first time.

Actual Behavior

Keycloak is being killed since readiness and liveness probes fail for the time config.

Environment

  • OpenShift or Kubernetes version:
  • Project Version/Tag: (E.g release-1.0.1)
    Kubernetes 1.16 on Digital Ocean

Steps to reproduce

Allow secret management for Realm IdentityProviders

Description

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'

Discussion

No response

Motivation

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.

Details

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.

`POSTGRES_EXTERNAL_ADDRESS` in `Secret` `keycloak-db-secret` not being used in Keycloak pod

Describe the bug

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

Version

15.1.1

Expected behavior

DB_ADDR should directly be set from POSTGRES_EXTERNAL_ADDRESS in the keycloak-db-secret Secret.

Actual behavior

DB_ADDR was set to keycloak-postgresql.keycloak.

How to Reproduce?

See info in description above.

Anything else?

Running in microk8s on Ubuntu 20.04 deployed using Terraform.

Allow to specify secrets labels and annotations from keycloakclient

Description

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:

return &v1.Secret{
ObjectMeta: v12.ObjectMeta{
Name: escapedSecretName,
Namespace: cr.Namespace,
Labels: map[string]string{
"app": ApplicationName,
},
},
Data: map[string][]byte{
ClientSecretClientIDProperty: []byte(cr.Spec.Client.ClientID),
ClientSecretClientSecretProperty: []byte(cr.Spec.Client.Secret),
},
}
}

See also my WIP Ansible playbooks: https://gitlab.com/kubitus-project/kubitus-installer/-/merge_requests/364

Unable to set custom CA certificate(s) for using the Operator

Describe the bug

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}

Version

14.0.0

Expected behavior

Ability to add CA cert to the trust store as well as configure the client cert lookup.

Actual behavior

No response

How to Reproduce?

No response

Anything else?

No response

Define a client with PCKE challenge method

Description

Be able to define a client with the PCKE challenge method enabled.

Discussion

No response

Motivation

The feature could be useful to configure clients authorization flows that are more resilient against CSRF and authorization code injection attacks.

Details

No response

Doesn't upgrade images and probe just fails without any error

Describe the bug

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"

Version

16.1.0

Expected behavior

update the deployment and probe working

Actual behavior

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:

How to Reproduce?

No response

Anything else?

No response

keycloak client using context.TODO

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

And here:
https://github.com/integr8ly/integreatly-operator/blob/master/pkg/controller/installation/installation_controller.go#L138

@pb82 @davidffrench @sebastienblanc

Liveness Probe gets 303 but does not follow

Describe the bug

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.

Version

16.0.0

Expected behavior

Liveness- and -readiness probes succeed

Actual behavior

Pods never appear as ready.

How to Reproduce?

(I cannot say precisely which circumstances are relevant)

  1. Have an operator-managed Keycloak instance in a k8s cluster in version 15.1.1 (3 instances, behind nginx LoadBalancer with cert-manager certificate)
  2. Have the operator auto-upgrade to 16.0.0

Anything else?

No response

Operator failing to update from 15.1.1 to 16.0.0 on OKD 4.9

Describe the bug

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.

Version

16.0.0

Expected behavior

the container starts and functions correctly.

Actual behavior

The container fails during initialization.

How to Reproduce?

Let the OKD 4.9 cluster run on 15.1.1 and the level 4 operator tries to update to 16.0.0

Anything else?

No response

Support and related documentation for ingress controller other than nginx

Description

Currently, operator is hardcoded for nginx. I am running:

  • traefik in production
  • ambassador for my personal playground on kind cluster

Unable to find any documentation on how to get this working.

Discussion

No response

Motivation

There are multiple popular ingress controllers in the market. Keycloak operator should be ingress independent and able to work with any ingress controller

Details

No response

Consider making Constants non-constant

Description

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.

Keycloak Client CRD doesn't reconcile enabled flag

Describe the bug

When creating a Keycloak client via CRD with enabled: false, this isn't mirrored to the Keycloak Realm.

Version

15.02

Expected behavior

A client is created in the realm with enabled: false.

Actual behavior

A client is created in the realm with enabled: true.

How to Reproduce?

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"

Anything else?

No response

KeycloakClient secret reference

Describe the bug

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.

Version

15.0.2

Expected behavior

spec.secret should be a reference to a secret, or a new spec.secretRef should be added.

Actual behavior

the secret is plantext in the CR.

How to Reproduce?

No response

Anything else?

No response

expired certificate during upgrade

Describe the bug

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)

Version

16

Expected behavior

complete the upgrade using the operator correctly and seeing the keycloak pod starting up correctly

Actual behavior

No response

How to Reproduce?

No response

Anything else?

No response

feature request: possiblity to mount files

Description

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?

Expected Behavior

you can mount files from secret

Actual Behavior

not posible to mount custom secrets

Environment

  • Kubernetes version: v1.15.3

Allow setting the Resources Labels also in the reconciliation process

Description

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 ).

Discussion

No response

Motivation

No response

Details

No response

Cannot manage authorization settings (scopes, resources, policies, permissions) through KeycloakClient CRD

Describe the bug

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?

Version

15.0.2

Expected behavior

I'd expect that applied change of defined scopes/resources/policies/permissions would be reflected in a given client Keycloak configuration.

Actual behavior

Changes of authorization settings (policies/permissions/etc) applied to existing KeycloakClient definition are ignored until the client is dropped and then re-created.

How to Reproduce?

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.

Anything else?

No response

Make nginx.ingress.kubernetes.io/server-snippet annotation optional

Description

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.

Discussion

https://groups.google.com/g/keycloak-dev/c/ONHs1-wyDAw

Motivation

To make the keycloak-operator be usable in environments where the "nginx.ingress.kubernetes.io/server-snippet" annotation is not allowed.

Details

Going to submit a PR that introduces an environment variable in order to skip that one.

Theme not being deployed

Describe the bug

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

Version

16.1.0

Expected behavior

deploy the theme

Actual behavior

doesn't do it

How to Reproduce?

just adding:

spec:
  extensions:
  - https://sourcecode.domain.de/sysadmin-public/keycloack-domain-theme/-/raw/master/theme.jar

to the CR

Anything else?

No response

The operator cannot specify a Postgres schema

Describe the bug

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.

Version

16.1.0

Expected behavior

There should be a way to specify what schema is used by Keycloak.

Actual behavior

There is not.

How to Reproduce?

Try to configure a schema other than public.

Anything else?

No response

OLM structure for v16.0.0

Description

Create new OLM structure that will be published on OperatorHub and internal OCP marketplace

Discussion

No response

Motivation

No response

Details

No response

Plugin needs to expose a port on the keycloak pod

Description

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

Setting "enabled: false" for an existing user does not actually disable the user

Describe the bug

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.

Version

16.1.0

Expected behavior

Keycloak should disable the user

Actual behavior

Once a user is "enabled", it can never be "disabled".

How to Reproduce?

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
image

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
image

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

image

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"}

Anything else?

I followed these instructions and installed the Keycloak operator into a minikube cluster using the command line instructions.

Operator should use Service Accounts for managing Keycloak

Description

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

Discussion

No response

Motivation

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).

Details

Design

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.

grafik

Risks/Limitations

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.

OLM MultiNamespace support

Description

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?

Discussion

No response

Motivation

Deploying an additional dev/staging keycloak would be pretty nice.

Details

No response

ScopeMappings for realm roles does not work

Describe the bug

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.

Version

15.1.1

Expected behavior

Realm roles of ScopeMappings are applied to the created client.

Actual behavior

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"}

How to Reproduce?

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

Anything else?

No response

prometheus service monitor naming

Describe the bug

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

Version

16.1.0

Expected behavior

create the service monitor/extra resources based on the keycloak cluster name

Actual behavior

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

How to Reproduce?

No response

Anything else?

No response

Configure ingress controller domain

Description

It is possible to configure the domain of the ingress controller?.
example: keycloak.test.com.ar

Expected Behavior

Configure ingress url in keycloak custom resource.

Actual Behavior

There are no properties to create ingress domain.
Default ingress IP address

Environment

Kubernetes version: 1.14

Regards

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.