Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDAP configuration example #1369

Closed
Tualua opened this issue Jan 2, 2024 · 14 comments
Closed

LDAP configuration example #1369

Tualua opened this issue Jan 2, 2024 · 14 comments
Labels
documentation Issues relating to documentation, missing, non-clear etc. good first issue Good for newcomers triage/needs-information Indicates an issue needs more information in order to work on it.

Comments

@Tualua
Copy link

Tualua commented Jan 2, 2024

Hello!

Is there any example or how-to with LDAP configuration?

@HVBE
Copy link
Collaborator

HVBE commented Jan 3, 2024

Hi @Tualua This generally shouldn't be any different from a vanilla Grafana LDAP config, as long as you adhere to upstream documentation, and populate the respective fields in the Grafana CR, your LDAP config should be applied properly.

The Operator simply exposes the upstream Grafana API through the Grafana CR spec, so if upstream has a config field called someconfig: myvalue you should be able to populate the Grafana CR spec with the same key and value, and have the operator manage that without having to do anything else.

Does this help? We generally tend to avoid adding extensive examples, as most of what users want to do is already available in the Grafana API structs and documentation, but perhaps, now that I think of it, we should absolutely make that more clear in our own documentation. This isn't the first question of this type, so we should be more explicit and clear in how this works.

@HVBE HVBE added good first issue Good for newcomers documentation Issues relating to documentation, missing, non-clear etc. triage/needs-information Indicates an issue needs more information in order to work on it. labels Jan 3, 2024
@Tualua
Copy link
Author

Tualua commented Jan 5, 2024 via email

@HVBE
Copy link
Collaborator

HVBE commented Jan 5, 2024

@Tualua you should be able to pass it as separate fields, your YAML should look something like:

  config:
    auth.anonymous:
      enabled: "True"
    auth:
      disable_login_form: "False"
      disable_signout_menu: "True"
    auth.basic:
      enabled: "True"
    auth.proxy:
      enabled: "True"
      enable_login_token: "True"
      header_property: "username"
      header_name: "X-Forwarded-User"

when using the following TOML

[auth.anonymous]
enabled = "True"

[auth]
disable_login_form = "False"
disable_signout_menu = "True"

[auth.basic]
enabled = "True"

[auth.proxy]
enabled = "True"
enable_login_token = "True"
header_property = "username"
header_name = "X-Forwarded-User"

See https://github.com/grafana/grafana-operator/blob/master/examples/oauth_proxy/resources.yaml#L140C6-L154 as an example of how that looks in a Grafana CR

@Tualua
Copy link
Author

Tualua commented Jan 8, 2024

@HubertStefanski Hello! Thank you for your reply.

According to LDAP configuration docs https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/ldap/
there should be line in grafana.ini

config_file = /etc/grafana/ldap.toml

And in that file you should put all your LDAP settings.

So my question was how to pass ldap.toml to grafana container?

@HVBE
Copy link
Collaborator

HVBE commented Jan 8, 2024

If that's your preferred method, you can simply volume mount it like any other volume source:

https://github.com/grafana/grafana-operator/blob/master/examples/configmaps_sidecar/resources.yml#L95-L131

The example above shows a sidecar config, for your use case, ignore it, and only pay attention to the volume/volume mounts and how they are structured within the Grafana CR.

So, essentially all you need to do is add your LDAP config to a configmap, then volume mount it within the Grafana CR spec, the Grafana pod should automatically read it when it gets created.

LMK if that helps

@Tualua
Copy link
Author

Tualua commented Jan 8, 2024

If that's your preferred method, you can simply volume mount it like any other volume source:

Yes, I've found it (kuber-newbie :)) right after posted question.
These are from my playbook:

  - name: Create LDAP config
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: ConfigMap
          metadata:
            name: ldap-config
            namespace: "{{ namespace }}"
          data:
            ldap.toml: |
              [[servers]]
              host = "{{ ldap.host }}"
              port = {{ ldap.port }}
              use_ssl = true
              start_tls = false
              ssl_skip_verify = true
              bind_dn = "{{ ldap.bind_dn }}"
              bind_password = '$__env{GF_SECURITY_BIND_PASSWORD}'
              search_filter = "(sAMAccountName=%s)"
              search_base_dns = {{ ldap.search_base_dns }}
              [servers.attributes]
              member_of = "memberOf"
              email =  "mail"
  - name: Install {{ service_name }}
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: grafana.integreatly.org/v1beta1
          kind: Grafana
          metadata:
            name: "{{ deployment_name }}"
            namespace: "{{ namespace }}"
            labels:
              dashboards: "{{ deployment_name }}"
          spec:
            deployment:
              spec:
                template:
                  spec:
                    containers:
                      - name: grafana
                        image: grafana/grafana:10.0.10
                        volumeMounts:
                        - name: grafana-configmaps
                          mountPath: /etc/grafana-configmaps
                          readOnly: true
                        env:
                          - name: GF_SECURITY_ADMIN_USER
                            valueFrom:
                              secretKeyRef:
                                key: GF_SECURITY_ADMIN_USER
                                name: "{{ deployment_name }}-admin-account"
                          - name: GF_SECURITY_ADMIN_PASSWORD
                            valueFrom:
                              secretKeyRef:
                                key: GF_SECURITY_ADMIN_PASSWORD
                                name: "{{ deployment_name }}-admin-account"
                          - name: GF_SECURITY_BIND_PASSWORD
                            valueFrom:
                              secretKeyRef:
                                key: GF_SECURITY_BIND_PASSWORD
                                name: "{{ deployment_name }}-bind-account"
                    volumes:
                      - name: grafana-configmaps
                        configMap:
                          name: ldap-config
            configMaps:
              - ldap-config
            config:
              log:
                mode: "console"
              auth:
                disable_login_form: "false"
              auth.ldap:
                enabled: "true"
                config_file: /etc/grafana-configmaps/ldap.toml

May be it will help another newbie.
BTW, it's not my preferred way, it is the only way described in Grafana Docs :). I would like to know another way if it exists.
Is there any way to avoid creating ldap.toml? Can I just put it in auth.ldap?

Something like this:

              auth.ldap:
                enabled: "true"
                servers:
                  host: "dc.domain.com"
                  port: 636
                  bind_dn: "DN"

https://github.com/grafana/grafana-operator/blob/master/examples/configmaps_sidecar/resources.yml#L95-L131

It is 404

@HVBE
Copy link
Collaborator

HVBE commented Jan 8, 2024

Can I just put it in auth.ldap?

Yeah! You should be able to do that!

It is 404

Ahh, we just merged #1370 , that changed the file extension from .yml to .yaml 😅

https://github.com/grafana/grafana-operator/blob/master/examples/configmaps_sidecar/resources.yaml#L96-L131

@Tualua
Copy link
Author

Tualua commented Jan 9, 2024

Yeah! You should be able to do that!

Yes, I can but unfortunately it does not work.

             auth:
                disable_login_form: "false"
              auth.ldap:
                enabled: "true"
                servers:
                  host: "{{ ldap.host }}"
                  port: "{{ ldap.port }}"
                  use_ssl: true
                  start_tls: false
                  ssl_skip_verify: true
                  bind_dn: "{{ ldap.bind_dn }}"
                  bind_password: $__env{GF_SECURITY_BIND_PASSWORD}'
                  search_filter: "(sAMAccountName=%s)"
                  search_base_dns: "{{ ldap.search_base_dns }}"
                  servers.attributes:
                    member_of: "memberOf"
                    email: "mail"

Error is:

"grafana.integreatly.org\",\"kind\":\"Grafana\",\"causes\":[{\"reason\":\"FieldValueTypeInvalid\",\"message\":\"Invalid value: \\\\\"object\\\\\": spec.config.auth.ldap.servers in body must be of type string: \\\\\"object\\\\\"\",\"field\":\"spec.config.auth.ldap.servers\"}]},\"code\":422}\\n'", "reason": "Unprocessable Entity"

What am I doing wrong?

@HVBE
Copy link
Collaborator

HVBE commented Jan 9, 2024

Can you please share a slightly wider spec with any sensitive fields removed?
I Just to make sure the spec structure is correct, since we have two layers of spec, one for the Grafana CR and then a sub-spec for the container, I might be able to spot something and help out then!

Thanks!

@Tualua
Copy link
Author

Tualua commented Jan 9, 2024

Here is whole Ansible task

 - name: Install {{ service_name }}
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: grafana.integreatly.org/v1beta1
          kind: Grafana
          metadata:
            name: "{{ deployment_name }}"
            namespace: "{{ namespace }}"
            labels:
              dashboards: "{{ deployment_name }}"
          spec:
            deployment:
              spec:
                template:
                  spec:
                    containers:
                      - name: grafana
                        image: grafana/grafana:10.0.10
                        volumeMounts:
                        - name: grafana-configmaps
                          mountPath: /etc/grafana-configmaps
                          readOnly: true
                        env:
                          - name: GF_SECURITY_ADMIN_USER
                            valueFrom:
                              secretKeyRef:
                                key: GF_SECURITY_ADMIN_USER
                                name: "{{ deployment_name }}-admin-account"
                          - name: GF_SECURITY_ADMIN_PASSWORD
                            valueFrom:
                              secretKeyRef:
                                key: GF_SECURITY_ADMIN_PASSWORD
                                name: "{{ deployment_name }}-admin-account"
                          - name: GF_SECURITY_BIND_PASSWORD
                            valueFrom:
                              secretKeyRef:
                                key: GF_SECURITY_BIND_PASSWORD
                                name: "{{ deployment_name }}-bind-account"
                    volumes:
                      - name: grafana-configmaps
                        configMap:
                          name: ldap-config
            configMaps:
              - ldap-config
            config:
              log:
                mode: "console"
              auth:
                disable_login_form: "false"
              auth.ldap:
                enabled: "true"
                # config_file: /etc/grafana-configmaps/ldap.toml
                servers:
                  host: "{{ ldap.host }}"
                  port: "{{ ldap.port }}"
                  use_ssl: true
                  start_tls: false
                  ssl_skip_verify: true
                  bind_dn: "{{ ldap.bind_dn }}"
                  bind_password: $__env{GF_SECURITY_BIND_PASSWORD}'
                  search_filter: "(sAMAccountName=%s)"
                  search_base_dns: "{{ ldap.search_base_dns }}"
                  servers.attributes:
                    member_of: "memberOf"
                    email: "mail"

@HVBE
Copy link
Collaborator

HVBE commented Jan 15, 2024

Sorry for only getting back now, @Tualua did you manage to make this work? or would you still like me to have a look at it?

@kcepaxe
Copy link
Contributor

kcepaxe commented Jan 17, 2024

Complete working example for future searchers

ldap-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: ldap-config
data:
  ldap.toml: |
    verbose_logging = true
    [[servers]]
    host = "ldap.office.com"
    port = 636
    use_ssl = true
    start_tls = false
    ssl_skip_verify = true
    bind_dn = "user@office.com" 
    bind_password = 'password123'
    search_filter = "(sAMAccountName=%s)"
    search_base_dns = ["DC=office,DC=com"]
    [servers.attributes]
    name = "givenName"
    surname = "sn"
    username = "sAMAccountName"
    member_of = "memberOf"
    email =  "mail"
    # RBAC section. Customize for your purpose
    [[servers.group_mappings]]
    group_dn = "CN=_G_Grafana_Admins,OU=Service Groups,OU=Work_Groups,OU=Management,DC=office,DC=com"
    org_role = "Admin"
    grafana_admin = false

ldap-ingress-grafana.yaml

---
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
  name: grafana
  namespace: grafana-ns
  labels:
    dashboards: "grafana"
spec:
  ingress:
    spec:
      rules:
        - host: grafana-example.domain.com
          http:
            paths:
              - backend:
                  service:
                    name: grafana-service
                    port:
                      number: 3000
                path: /
                pathType: Prefix
      tls:
        - hosts:
            - grafana-example.domain.com
  deployment:
    spec:
      template:
        spec:
          containers:
            - name: grafana
              image: grafana/grafana:10.2.2 # change me
              volumeMounts:
                - mountPath: /etc/grafana-configmaps
                  name: grafana-configmaps
                  readOnly: false
          volumes:
            - name: grafana-configmaps
              configMap:
                name: ldap-config
  config:
    log:
      mode: "console"
    auth:
      disable_login_form: "false"
    auth.ldap:
      enabled: "true"
      config_file: /etc/grafana-configmaps/ldap.toml   

@HVBE
Copy link
Collaborator

HVBE commented Jan 17, 2024

@kcepaxe Thanks for the example! 🙌 would you mind submitting a PR to add it to our /examples?

@HVBE
Copy link
Collaborator

HVBE commented Jan 17, 2024

Closed through #1376

@HVBE HVBE closed this as completed Jan 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Issues relating to documentation, missing, non-clear etc. good first issue Good for newcomers triage/needs-information Indicates an issue needs more information in order to work on it.
Projects
None yet
Development

No branches or pull requests

3 participants