Enable support for LDAP authentication

This patch allows to authenticate Grafana users against LDAP. It also
supports a group-based authorization mapping to the admin and viewers
roles. In this implementation we only support POSIX LDAP schemas.
All options are disabled by default.

DocImpact Add support for LDAP
Implements: blueprint ldap-integration-in-stacklight

Change-Id: I7e836d0cd8a5f7df3d934e29bfae337abd90da3c
This commit is contained in:
Guillaume Thouvenin 2016-06-16 11:34:46 +02:00
parent d391f2b77b
commit 7bf3b41572
6 changed files with 378 additions and 14 deletions

View File

@ -22,6 +22,26 @@ $db_password = $influxdb_grafana['mysql_password']
$admin_username = $influxdb_grafana['grafana_username']
$admin_password = $influxdb_grafana['grafana_userpass']
$ldap_enabled = hiera('lma::grafana::ldap::enabled')
if $ldap_enabled {
$ldap_parameters = {
servers => hiera('lma::grafana::ldap::servers'),
protocol => hiera('lma::grafana::ldap::protocol'),
port => hiera('lma::grafana::ldap::port'),
bind_dn => hiera('lma::grafana::ldap::bind_dn'),
bind_password => hiera('lma::grafana::ldap::bind_password'),
user_search_base_dns => hiera('lma::grafana::ldap::user_search_base_dns'),
user_search_filter => hiera('lma::grafana::ldap::user_search_filter'),
authorization_enabled => hiera('lma::grafana::ldap::authorization_enabled'),
group_search_base_dns => hiera('lma::grafana::ldap::group_search_base_dns'),
group_search_filter => hiera('lma::grafana::ldap::group_search_filter'),
admin_group_dn => hiera('lma::grafana::ldap::admin_group_dn', ''),
viewer_group_dn => hiera('lma::grafana::ldap::viewer_group_dn', ''),
}
} else {
$ldap_parameters = undef
}
case $db_mode {
'local': {
@ -38,13 +58,15 @@ case $db_mode {
}
class {'lma_monitoring_analytics::grafana':
db_host => $db_host,
db_name => $db_name,
db_username => $db_username,
db_password => $db_password,
admin_username => $admin_username,
admin_password => $admin_password,
domain => hiera('lma::influxdb::vip'),
http_port => hiera('lma::influxdb::grafana_port'),
version => '3.0.4-1464167696',
db_host => $db_host,
db_name => $db_name,
db_username => $db_username,
db_password => $db_password,
admin_username => $admin_username,
admin_password => $admin_password,
domain => hiera('lma::influxdb::vip'),
http_port => hiera('lma::influxdb::grafana_port'),
version => '3.0.4-1464167696',
ldap_enabled => $ldap_enabled,
ldap_parameters => $ldap_parameters,
}

View File

@ -61,6 +61,29 @@ if $tls_enabled {
}
}
$ldap_enabled = $influxdb_grafana['ldap_enabled']
$ldap_protocol = $influxdb_grafana['ldap_protocol']
$ldap_servers = $influxdb_grafana['ldap_servers']
$ldap_bind_dn = $influxdb_grafana['ldap_bind_dn']
$ldap_bind_password = $influxdb_grafana['ldap_bind_password']
$ldap_user_search_base_dns = $influxdb_grafana['ldap_user_search_base_dns']
$ldap_user_search_filter = $influxdb_grafana['ldap_user_search_filter']
$ldap_authorization_enabled = $influxdb_grafana['ldap_authorization_enabled']
$ldap_group_search_base_dns = $influxdb_grafana['ldap_group_search_base_dns']
$ldap_group_search_filter = $influxdb_grafana['ldap_group_search_filter']
$ldap_admin_group_dn = $influxdb_grafana['ldap_admin_group_dn']
$ldap_viewer_group_dn = $influxdb_grafana['ldap_viewer_group_dn']
if empty($ldap_port) {
if downcase($ldap_protocol) == 'ldap' {
$ldap_port = 389
} else {
$ldap_port = 636
}
} else {
$ldap_port = $influxdb_grafana['ldap_server_port']
}
$calculated_content = inline_template('
---
lma::influxdb::data_dir: "/var/lib/influxdb"
@ -83,6 +106,24 @@ lma::grafana::tls::enabled: <%= @tls_enabled %>
lma::grafana::tls::hostname: "<%= @grafana_hostname %>"
lma::grafana::tls::cert_file_path: "<%= @cert_file_path %>"
<% end -%>
lma::grafana::ldap::enabled: <%= @ldap_enabled %>
lma::grafana::ldap::authorization_enabled: <%= @ldap_authorization_enabled %>
<% if @ldap_enabled -%>
lma::grafana::ldap::servers: <%= @ldap_servers %>
lma::grafana::ldap::protocol: <%= @ldap_protocol %>
lma::grafana::ldap::port: <%= @ldap_port %>
lma::grafana::ldap::bind_dn: <%= @ldap_bind_dn %>
lma::grafana::ldap::bind_password: <%= @ldap_bind_password %>
lma::grafana::ldap::user_search_base_dns: <%= @ldap_user_search_base_dns %>
lma::grafana::ldap::user_search_filter: <%= @ldap_user_search_filter %>
lma::grafana::ldap::group_search_base_dns: <%= @ldap_group_search_base_dns %>
lma::grafana::ldap::group_search_filter: <%= @ldap_group_search_filter %>
<% if @ldap_authorization_enabled -%>
lma::grafana::ldap::admin_group_dn:<%= @ldap_admin_group_dn %>
lma::grafana::ldap::viewer_group_dn: <%= @ldap_viewer_group_dn %>
<% end -%>
<% end -%>
')
file { $hiera_file:

View File

@ -24,6 +24,8 @@ class lma_monitoring_analytics::grafana (
$domain = $lma_monitoring_analytics::params::grafana_domain,
$http_address = $lma_monitoring_analytics::params::grafana_address,
$http_port = $lma_monitoring_analytics::params::grafana_port,
$ldap_enabled = false,
$ldap_parameters = undef,
$version = 'latest',
) inherits lma_monitoring_analytics::params {
@ -32,6 +34,9 @@ class lma_monitoring_analytics::grafana (
validate_string($db_username)
validate_string($db_password)
validate_string($http_address)
if $ldap_enabled {
validate_hash($ldap_parameters)
}
# If no port is specified Grafana will not start. So we check if the
# variable contains a port value and if not, we add ':3306'.
@ -41,33 +46,64 @@ class lma_monitoring_analytics::grafana (
$full_db_host = "${db_host}:3306"
}
$ldap_configuration_file = '/etc/grafana/ldap.toml'
class { '::grafana':
install_method => 'repo',
version => $version,
manage_package_repo => false,
cfg => {
server => {
server => {
http_address => $http_address,
http_port => $http_port,
domain => $domain,
},
database => {
database => {
type => 'mysql',
host => $full_db_host,
name => $db_name,
user => $db_username,
password => $db_password,
},
security => {
'auth.ldap' => {
enabled => $ldap_enabled,
config_file => $ldap_configuration_file,
},
security => {
admin_user => $admin_username,
admin_password => $admin_password,
},
analytics => {
analytics => {
reporting_enabled => false,
},
},
}
if $ldap_enabled {
# Following parameters are used in ldap.toml.erb
$ldap_servers = $ldap_parameters['servers']
$ldap_protocol = $ldap_parameters['protocol']
$ldap_server_port = $ldap_parameters['port']
$ldap_bind_dn = $ldap_parameters['bind_dn']
$ldap_bind_password = $ldap_parameters['bind_password']
$ldap_user_search_base_dns = $ldap_parameters['user_search_base_dns']
$ldap_user_search_filter = $ldap_parameters['user_search_filter']
$ldap_authorization_enabled = $ldap_parameters['authorization_enabled']
$ldap_group_search_base_dns = $ldap_parameters['group_search_base_dns']
$ldap_group_search_filter = $ldap_parameters['group_search_filter']
$ldap_admin_group_dn = $ldap_parameters['admin_group_dn']
$ldap_viewer_group_dn = $ldap_parameters['viewer_group_dn']
file { $ldap_configuration_file:
owner => 'root',
group => 'grafana',
mode => '0640',
content => template('lma_monitoring_analytics/ldap.toml.erb'),
notify => Service['grafana-server'],
}
}
file { '/etc/logrotate.d/grafana.conf':
ensure => present,
content => template('lma_monitoring_analytics/logrotate.conf.erb'),

View File

@ -28,10 +28,96 @@ describe 'lma_monitoring_analytics::grafana', :type => :class do
it { is_expected.to contain_package('grafana').with(
:ensure => 'latest'
)}
it { is_expected.to contain_file('/etc/logrotate.d/grafana.conf') }
end
describe 'with ldap' do
let (:params) do
{:db_host => 'localhost:3306',
:db_name => 'grafana',
:db_username => 'grafana',
:db_password => 'grafana',
:ldap_enabled => true,
:ldap_parameters => {
'servers' => 'localhost',
'protocol' => 'ldap',
'port' => 389,
'bind_dn' => 'cn=admin,dc=example,dc=com',
'bind_password' => 'pass',
'user_search_base_dns' => 'dc=example,dc=com',
'user_search_filter' => '(cn=%s)',
'authorization_enabled' => false,
'group_search_base_dns' => 'ou=groups,dc=example,dc=com',
'group_search_filter' => '(&(objectClass=posixGroup)(memberUid=%s))',
'admin_group_dn' => 'cn=admin_group,dc=example,dc=com',
'viewer_group_dn' => 'cn=viewer_group,dc=example,dc=com',
}
}
end
it do
should contain_file('/etc/grafana/ldap.toml').with_content(/port\s*=\s*389/)
should contain_file('/etc/grafana/ldap.toml').with_content(/use_ssl\s*=\s*false/)
end
end
describe 'with ldaps' do
let (:params) do
{:db_host => 'localhost:3306',
:db_name => 'grafana',
:db_username => 'grafana',
:db_password => 'grafana',
:ldap_enabled => true,
:ldap_parameters => {
'servers' => 'localhost',
'protocol' => 'ldaps',
'port' => '636',
'bind_dn' => 'cn=admin,dc=example,dc=com',
'bind_password' => 'pass',
'user_search_base_dns' => 'dc=example,dc=com',
'user_search_filter' => '(cn=%s)',
'authorization_enabled' => false,
'group_search_base_dns' => 'ou=groups,dc=example,dc=com',
'group_search_filter' => '(&(objectClass=posixGroup)(memberUid=%s))',
'admin_group_dn' => 'cn=admin_group,dc=example,dc=com',
'viewer_group_dn' => 'cn=viewer_group,dc=example,dc=com',
}
}
end
it do
should contain_file('/etc/grafana/ldap.toml').with_content(/port\s*=\s*636/)
should contain_file('/etc/grafana/ldap.toml').with_content(/use_ssl\s*=\s*true/)
end
end
describe 'with ldap and groups' do
let (:params) do
{:db_host => 'localhost:3306',
:db_name => 'grafana',
:db_username => 'grafana',
:db_password => 'grafana',
:ldap_enabled => true,
:ldap_parameters => {
'servers' => 'localhost',
'protocol' => 'ldap',
'port' => 389,
'bind_dn' => 'cn=admin,dc=example,dc=com',
'bind_password' => 'pass',
'user_search_base_dns' => 'dc=example,dc=com',
'user_search_filter' => '(cn=%s)',
'authorization_enabled' => true,
'group_search_base_dns' => 'ou=groups,dc=example,dc=com',
'group_search_filter' => '(&(objectClass=posixGroup)(memberUid=%s))',
'admin_group_dn' => 'cn=admin_group,dc=example,dc=com',
'viewer_group_dn' => 'cn=viewer_group,dc=example,dc=com',
}
}
end
it do
should contain_file('/etc/grafana/ldap.toml').with_content(/group_dn = "cn=admin_group,dc=example,dc=com"/)
should contain_file('/etc/grafana/ldap.toml').with_content(/group_dn = "cn=viewer_group,dc=example,dc=com"/)
end
end
describe 'db_host without port number' do
let (:params) do
{:db_host => 'www.example.com',

View File

@ -0,0 +1,42 @@
[[servers]]
host = "<%= @ldap_servers %>"
port = <%= @ldap_server_port %>
<% if @ldap_protocol.downcase() == 'ldaps' -%>
use_ssl = true
<% else -%>
use_ssl = false
<% end -%>
ssl_skip_verify = true
bind_dn = "<%= @ldap_bind_dn %>"
bind_password = "<%= @ldap_bind_password %>"
search_base_dns = [<%= @ldap_user_search_base_dns.split(' ').collect{|x| "\"#{x}\"" }.join(',') %>]
search_filter = "<%= @ldap_user_search_filter %>"
# In POSIX LDAP schemas, without memberOf attribute a secondary query must be
# made for groups. This is done by enabling group_search_filter below. We must
# also set member_of="cn".
group_search_base_dns = [<%= @ldap_group_search_base_dns.split(' ').collect{|x| "\"#{x}\"" }.join(',') %>]
group_search_filter = "<%= @ldap_group_search_filter %>"
[servers.attributes]
name = "givenName"
surname = "sn"
username = "cn"
member_of = "cn"
email = "email"
<% if @ldap_authorization_enabled -%>
[[servers.group_mappings]]
group_dn = "<%= @ldap_admin_group_dn %>"
org_role = "Admin"
[[servers.group_mappings]]
group_dn = "<%= @ldap_viewer_group_dn %>"
org_role = "Viewer"
<% else -%>
[[servers.group_mappings]]
group_dn = "*"
org_role = "Admin"
<% end -%>

View File

@ -164,3 +164,140 @@ attributes:
- condition: "settings:influxdb_grafana.tls_enabled.value == false"
action: "hide"
# TLS Settings: END
# LDAP Settings: BEGIN
ldap_enabled:
value: false
label: 'Use LDAP for Grafana authentication'
description: ''
weight: 170
type: "checkbox"
ldap_protocol:
type: "radio"
value: 'ldap'
weight: 180
label: 'LDAP protocol'
values:
- data: "ldap"
label: "LDAP"
- data: "ldaps"
label: "LDAPS"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_servers:
value: ''
label: 'LDAP servers'
description: 'Specify one or several LDAP servers separated by space.'
weight: 190
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_server_port:
value: ''
label: 'Port'
description: 'If empty, the default value is 389 for LDAP and 636 for LDAPS.'
weight: 200
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_bind_dn:
value: ''
label: 'Bind DN'
description: 'DN used to bind to the server when searching for entries.'
weight: 210
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_bind_password:
value: ''
label: 'Bind password'
description: 'Password to use in conjunction with the bind DN.'
weight: 220
type: "password"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_user_search_base_dns:
value: ''
label: 'User search base DN'
description: 'The base DN to search for users.'
weight: 230
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_user_search_filter:
value: '(uid=%s)'
label: 'User search filter'
description: 'A valid LDAP search filter.'
weight: 240
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_group_search_base_dns:
value: ''
label: 'Group search base DN'
description: 'The base DN to search for groups.'
weight: 250
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_group_search_filter:
value: '(&(objectClass=posixGroup)(memberUid=%s)'
label: 'Group search filter'
description: 'A valid LDAP search filter.'
weight: 260
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_authorization_enabled:
value: false
label: 'Enable group-based authorization'
description: 'It allows to associate the users with the admin or read-only role. Otherwise all users are assigned to admin role.'
weight: 270
type: "checkbox"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
ldap_admin_group_dn:
value: ''
label: 'Group DN mapping to the Admins role'
description: ''
weight: 280
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
- condition: "settings:influxdb_grafana.ldap_authorization_enabled.value == false"
action: disable
ldap_viewer_group_dn:
value: ''
label: 'Group DN mapping to the Viewers role'
description: ''
weight: 290
type: "text"
restrictions:
- condition: "settings:influxdb_grafana.ldap_enabled.value == false"
action: hide
- condition: "settings:influxdb_grafana.ldap_authorization_enabled.value == false"
action: disable
# LDAP Settings: END