diff --git a/README.rst b/README.rst index f806ffe..369b262 100644 --- a/README.rst +++ b/README.rst @@ -44,6 +44,83 @@ Server installed with PostgreSQL database user: grafana password: passwd +Server installed with LDAP authentication and all authenticated users are +administrators + +.. code-block:: yaml + + grafana: + server: + enabled: true + admin: + user: admin + password: passwd + auth: + ldap: + enabled: true + host: '127.0.0.1' + port: 389 + use_ssl: false + bind_dn: "cn=admin,dc=grafana,dc=org" + bind_password: "grafana" + user_search_filter: "(cn=%s)" + user_search_base_dns: + - "dc=grafana,dc=org" + +Server installed with LDAP and basic authentication + +.. code-block:: yaml + + grafana: + server: + enabled: true + admin: + user: admin + password: passwd + auth: + basic: + enabled: true + ldap: + enabled: true + host: '127.0.0.1' + port: 389 + use_ssl: false + bind_dn: "cn=admin,dc=grafana,dc=org" + bind_password: "grafana" + user_search_filter: "(cn=%s)" + user_search_base_dns: + - "dc=grafana,dc=org" + +Server installed with LDAP for authentication and authorization + +.. code-block:: yaml + + grafana: + server: + enabled: true + admin: + user: admin + password: passwd + auth: + ldap: + enabled: true + host: '127.0.0.1' + port: 389 + use_ssl: false + bind_dn: "cn=admin,dc=grafana,dc=org" + bind_password: "grafana" + user_search_filter: "(cn=%s)" + user_search_base_dns: + - "dc=grafana,dc=org" + group_search_filter: "(&(objectClass=posixGroup)(memberUid=%s))" + group_search_base_dns: + - "ou=groups,dc=grafana,dc=org" + authorization: + enabled: true + admin_group: "admins" + editor_group: "editors" + viewer_group: "viewers" + Server installed with default StackLight JSON dashboards. This will be replaced by the possibility for a service to provide its own dashboard using salt-mine. diff --git a/grafana/files/grafana.ini b/grafana/files/grafana.ini index 9ae09b9..f2071bb 100644 --- a/grafana/files/grafana.ini +++ b/grafana/files/grafana.ini @@ -144,15 +144,15 @@ auto_assign_org_role = {{ server.auto_assign_role }} #################################### Anonymous Auth ########################## [auth.anonymous] -{%- if server.auth.engine == 'anonymous' %} +{%- if server.auth.engine == 'anonymous' or server.auth.get('anonymous', {}).get('enabled', False) %} enabled = true -{%- if server.auth.organization is defined %} -org_name = {{ server.auth.organization }} +{%- if server.auth.organization is defined or server.auth.anonymous.organization is defined %} +org_name = {{ server.auth.get('organization', server.auth.anonymous.organization) }} {%- endif %} -{%- if server.auth.role is defined %} -org_name = {{ server.auth.role }} +{%- if server.auth.role is defined or server.auth.anonymous.role is defined %} +org_name = {{ server.auth.get('role', server.auth.anonymous.role) }} {%- endif %} {%- else %} @@ -193,16 +193,16 @@ org_name = {{ server.auth.role }} #################################### Auth Proxy ########################## [auth.proxy] -{%- if server.auth.engine == 'proxy' %} +{%- if server.auth.engine == 'proxy' or server.auth.get('proxy', {}).get('enabled', False) %} enabled = true -header_name = {{ server.auth.get('header', 'X-Forwarded-User') }} -header_property = {{ server.auth.get('header_property', 'username') }} +header_name = {{ server.auth.get('proxy', {}).get('header', server.auth.get('header', 'X-Forwarded-User')) }} +header_property = {{ server.auth.get('proxy', {}).get('header_property', server.auth.get('header_property', 'username')) }} auto_sign_up = true {%- endif %} #################################### Basic Auth ########################## [auth.basic] -{%- if server.auth.engine == 'basic' %} +{%- if server.auth.engine == 'basic' or server.auth.get('basic', {}).get('enabled', False) %} enabled = true {%- else %} enabled = false @@ -210,8 +210,12 @@ enabled = false #################################### Auth LDAP ########################## [auth.ldap] -;enabled = false -;config_file = /etc/grafana/ldap.toml +{%- if server.auth.get('ldap', {}).get('enabled', False) %} +enabled = true +config_file = /etc/grafana/ldap.toml +{%- else %} +enabled = false +{%- endif %} #################################### SMTP / Emailing ########################## [smtp] diff --git a/grafana/files/ldap.toml b/grafana/files/ldap.toml new file mode 100644 index 0000000..ff5fb6e --- /dev/null +++ b/grafana/files/ldap.toml @@ -0,0 +1,108 @@ +{%- from "grafana/map.jinja" import server with context %} +{%- set ldap_params = server.auth.ldap %} +# Set to true to log user information returned from LDAP +verbose_logging = false + +[[servers]] +# Ldap server host (specify multiple hosts space separated) +host = "{{ ldap_params.host }}" +# Default port is 389 or 636 if use_ssl = true +port = {{ ldap_params.port }} +# Set to true if ldap server supports TLS +use_ssl = {{ ldap_params.use_ssl|lower }} + +# Set to true if connect ldap server with STARTTLS pattern (create connection in insecure, then upgrade to secure connection with TLS) +start_tls = false +# set to true if you want to skip ssl cert validation +ssl_skip_verify = false +# set to the path to your root CA certificate or leave unset to use system defaults +# root_ca_cert = /path/to/certificate.crt + +# Search user bind dn +bind_dn = "{{ ldap_params.bind_dn }}" +# Search user bind password +bind_password = "{{ ldap_params.bind_password }}" + +# User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)" +search_filter = "{{ ldap_params.user_search_filter }}" + +# An array of base dns to search through +search_base_dns = {{ ldap_params.user_search_base_dns }} + +# In POSIX LDAP schemas, without memberOf attribute a secondary query must be made for groups. +# This is done by enabling group_search_filter below. You must also set member_of= "cn" +# in [servers.attributes] below. + +# Users with nested/recursive group membership and an LDAP server that supports LDAP_MATCHING_RULE_IN_CHAIN +# can set group_search_filter, group_search_filter_user_attribute, group_search_base_dns and member_of +# below in such a way that the user's recursive group membership is considered. +# +# Nested Groups + Active Directory (AD) Example: +# +# AD groups store the Distinguished Names (DNs) of members, so your filter must +# recursively search your groups for the authenticating user's DN. For example: +# +# group_search_filter = "(member:1.2.840.113556.1.4.1941:=%s)" +# group_search_filter_user_attribute = "distinguishedName" +# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"] +# +# [servers.attributes] +# ... +# member_of = "distinguishedName" + +## Group search filter, to retrieve the groups of which the user is a member (only set if memberOf attribute is not available) +# group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))" +## Group search filter user attribute defines what user attribute gets substituted for %s in group_search_filter. +## Defaults to the value of username in [server.attributes] +## Valid options are any of your values in [servers.attributes] +## If you are using nested groups you probably want to set this and member_of in +## [servers.attributes] to "distinguishedName" +# group_search_filter_user_attribute = "distinguishedName" +## An array of the base DNs to search through for groups. Typically uses ou=groups +# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"] +# Specify names of the ldap attributes your ldap uses + +{%- if ldap_params.group_search_filter is defined %} +group_search_filter = "{{ ldap_params.group_search_filter }}" +{%- endif %} +{%- if ldap_params.group_search_base_dns is defined %} +group_search_base_dns = {{ ldap_params.group_search_base_dns }} +{%- endif %} + +[servers.attributes] +name = "givenName" +surname = "sn" +username = "cn" +member_of = "memberOf" +email = "email" + +{%- if ldap_params.get('authorization', {}).get('enabled', False) %} + +# Map ldap groups to grafana org roles +{%- if ldap_params.authorization.admin_group is defined %} +[[servers.group_mappings]] +group_dn = "{{ ldap_params.authorization.admin_group }}" +org_role = "Admin" +# The Grafana organization database id, optional, if left out the default org (id 1) will be used +# org_id = 1 +{%- endif %} + +{%- if ldap_params.authorization.editor_group is defined %} +[[servers.group_mappings]] +group_dn = "{{ ldap_params.authorization.editor_group }}" +org_role = "Editor" +{%- endif %} + +{%- if ldap_params.authorization.viewer_group is defined %} +[[servers.group_mappings]] +# If you want to match all (or no ldap groups) then you can use wildcard +group_dn = "{{ ldap_params.authorization.viewer_group }}" +org_role = "Viewer" +{%- endif %} + +{%- else %} +{# Every user that can be authenticated is an admin #} +[[servers.group_mappings]] +group_dn = "*" +org_role = "Admin" +{%- endif %} diff --git a/grafana/map.jinja b/grafana/map.jinja index 88ac842..4c9fe60 100644 --- a/grafana/map.jinja +++ b/grafana/map.jinja @@ -11,6 +11,16 @@ Debian: engine: file auth: engine: application + ldap: + enabled: false + host: '127.0.0.1' + port: 389 + use_ssl: false + bind_dn: "cn=admin,dc=grafana,dc=org" + bind_password: "grafana" + user_search_filter: "(cn=%s)" + user_search_base_dns: + - "dc=grafana,dc=org" admin: user: admin password: admin diff --git a/grafana/server.sls b/grafana/server.sls index 8ab3403..feb4f79 100644 --- a/grafana/server.sls +++ b/grafana/server.sls @@ -14,6 +14,20 @@ grafana_packages: - require: - pkg: grafana_packages +{%- if server.auth.get('ldap', {}).get('enabled', False) %} +/etc/grafana/ldap.toml: + file.managed: + - source: salt://grafana/files/ldap.toml + - template: jinja + - user: grafana + - group: grafana + - require: + - pkg: grafana_packages + - watch_in: + - service: grafana_service +{%- endif %} + + {%- if server.dashboards.enabled %} grafana_copy_default_dashboards: