From d6e66e7e6d148e677e4a8c77cf784cbdf0e51a0f Mon Sep 17 00:00:00 2001 From: Andrea Dell'Amico Date: Thu, 21 Dec 2017 14:25:33 +0100 Subject: [PATCH] grafana: manage the configuration and the authentication modes. Install and use redis when requested. --- grafana/defaults/main.yml | 88 +++++++++ grafana/handlers/main.yml | 4 + grafana/meta/main.yml | 5 + grafana/tasks/main.yml | 50 +++-- grafana/templates/grafana.ini.j2 | 321 +++++++++++++++++++++++++++++++ grafana/templates/ldap.toml.j2 | 85 ++++++++ grafana/vars/main.yml | 5 + 7 files changed, 546 insertions(+), 12 deletions(-) create mode 100644 grafana/handlers/main.yml create mode 100644 grafana/meta/main.yml create mode 100644 grafana/templates/grafana.ini.j2 create mode 100644 grafana/templates/ldap.toml.j2 create mode 100644 grafana/vars/main.yml diff --git a/grafana/defaults/main.yml b/grafana/defaults/main.yml index a6c7efd0..3ec3915b 100644 --- a/grafana/defaults/main.yml +++ b/grafana/defaults/main.yml @@ -1,7 +1,95 @@ --- grafana_repo_key: https://packagecloud.io/gpg.key grafana_repo: 'deb https://packagecloud.io/grafana/testing/debian/ wheezy main' +grafana_pkg_state: latest grafana_packages: - grafana +grafana_conf_files: + - grafana.ini + - ldap.toml + grafana_enabled: True +grafana_app_mode: production + +grafana_require_nginx: True +grafana_require_redis: True + +grafana_data_path: /var/lib/grafana +grafana_server_protocol: http +grafana_bind_ip_address: 127.0.0.1 +grafana_http_port: 3000 +grafana_domain_name: '{{ ansible_fqdn }}' +grafana_enforce_dom_name: True + +# Either mysql, postgres or sqlite3 +grafana_db_type: sqlite3 +# The other db data is not needed when the choice is sqlite3 +grafana_db_port: 5432 +grafana_db_host: '127.0.0.1' +grafana_db_hostport: '{{ grafana_db_host }}:{{ grafana_db_port }}' +grafana_db_name: grafana +grafana_db_user: grafana_u +#grafana_db_password: 'set_it_into_a_vault_file' +grafana_db_pg_ssl_mode: require +# +grafana_session_provider: file +grafana_session_config: sessions +grafana_session_redis_config: 'addr=127.0.0.1:6379,pool_size=100,db=grafana' + +grafana_analytics_reporting_enabled: 'true' +grafana_analytics_updates_check: 'true' + +grafana_u_allow_signup: 'false' +grafana_u_allow_org_create: 'false' +grafana_u_default_role: Viewer +# dark or light +grafana_u_default_theme: dark +grafana_auth_anon: 'false' + +grafana_log_mode: syslog +grafana_log_level: info +grafana_syslog_facility: daemon + +grafana_dashboard_json: 'true' + +# grafana_additional_plugins: +# - { name: 'foo', url: '', cmd: 'remove' } +# - { name: 'wut', 'repo: '' } +# - { name: 'bar' } + +grafana_ldap_auth: 'false' +grafana_ldap_host: 127.0.0.1 +grafana_ldap_port: 636 +grafana_ldap_use_ssl: 'true' +grafana_ldap_ssl_skip_verify: 'false' +grafana_ldap_bind_dn: 'cn=admin,dc=grafana,dc=org' +grafana_ldap_bind_pwd: 'grafana' +grafana_ldap_u_search_filter: '(uid=%s)' +grafana_ldap_u_search_base: 'dc=grafana,dc=org' +grafana_ldap_posix_groups: 'false' +grafana_ldap_g_search_filter: '(&(objectClass=posixGroup)(memberUid=%s))' +grafana_ldap_g_search_filter_user_attr: 'uid' +grafana_ldap_g_search_base: 'ou=groups,dc=grafana,dc=org' +grafana_ldap_u_email: 'mail' +grafana_ldap_admin_role_group: 'cn=admins,dc=grafana,dc=org' +grafana_ldap_serverattrs_username: 'uid' +grafana_ldap_group_roles: + - { dn: 'cn=users,dc=grafana,dc=org', role: 'Editor' } + - { dn: '*', role: 'Viewer' } + +nginx_virthosts: + - virthost_name: '{{ ansible_fqdn }}' + listen: '80' + server_name: '{{ ansible_fqdn }}' + server_aliases: '' + index: index.html + ssl_enabled: False + ssl_only: False + ssl_letsencrypt_certs: '{{ nginx_letsencrypt_managed }}' + root: '{{ nginx_webroot }}' + server_tokens: 'off' + proxy_standard_setup: True + proxies: + - location: / + target: http://localhost:{{ grafana_http_port }}; diff --git a/grafana/handlers/main.yml b/grafana/handlers/main.yml new file mode 100644 index 00000000..137eccc0 --- /dev/null +++ b/grafana/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: Restart grafana + service: name=grafana-server state=restarted + diff --git a/grafana/meta/main.yml b/grafana/meta/main.yml new file mode 100644 index 00000000..c502ae2a --- /dev/null +++ b/grafana/meta/main.yml @@ -0,0 +1,5 @@ +--- +dependencies: + - { role: '../../library/roles/nginx', when: grafana_require_nginx } + - { role: '../../library/roles/redis', when: grafana_require_redis } + diff --git a/grafana/tasks/main.yml b/grafana/tasks/main.yml index cff66357..481af82f 100644 --- a/grafana/tasks/main.yml +++ b/grafana/tasks/main.yml @@ -1,19 +1,45 @@ --- -- name: Install the grafana repo key - apt_key: url={{ grafana_repo_key }} state=present +- block: + - name: Ensure that grafana is stopped and disabled + service: name=grafana-server state=stopped enabled=no + + - name: Remove the grafana deb packages + apt: name='{{ item }}' state=absent + with_items: '{{ grafana_packages }}' + + - name: Install the grafana deb repository + apt_repository: repo='{{ grafana_repo }}' state=absent update_cache=yes + + when: not grafana_enabled tags: [ 'grafana' ] -- name: Install the grafana deb repository - apt_repository: repo='{{ grafana_repo }}' state=present update_cache=yes - tags: [ 'grafana' ] +- block: + - name: Install the grafana repo key + apt_key: url={{ grafana_repo_key }} state=present -- name: Install the grafana deb packages - apt: name='{{ item }}' state=present - with_items: '{{ grafana_packages }}' - tags: [ 'grafana' ] + - name: Install the grafana deb repository + apt_repository: repo='{{ grafana_repo }}' state=present update_cache=yes + + - name: Install the grafana deb packages + apt: name='{{ item }}' state={{ grafana_pkg_state }} update_cache=yes cache_valid_time=1800 + with_items: '{{ grafana_packages }}' -- name: Ensure that grafana is enabled and running - service: name=grafana-server state=started enabled=yes + - name: Install the grafana configuration files + template: src={{ item }}.j2 dest=/etc/grafana/{{ item }} mode=0440 owner=root group=grafana + with_items: '{{ grafana_conf_files }}' + notify: Restart grafana + + - name: Create the local dashboards directory + file: dest=/var/lib/grafana/dashboards state=directory mode=0755 owner=grafana group=grafana + + - name: Install additional plugins, if any + command: grafana-cli plugins {{ item.cmd | default('install') }} {% if item.repo is defined %} --repo {{ item.repo }} {% endif %} {% if item.url is defined %} --pluginUrl {{ item.url }} {% else %} {{ item.name }} {% endif %} + with_items: '{{ grafana_additional_plugins }}' + when: grafana_additional_plugins is defined + tags: [ 'grafana', 'grafana_plugins' ] + + - name: Ensure that grafana is enabled and running + service: name=grafana-server state=started enabled=yes + when: grafana_enabled tags: [ 'grafana' ] - diff --git a/grafana/templates/grafana.ini.j2 b/grafana/templates/grafana.ini.j2 new file mode 100644 index 00000000..6ef95558 --- /dev/null +++ b/grafana/templates/grafana.ini.j2 @@ -0,0 +1,321 @@ +##################### Grafana Configuration Example ##################### +# +# Everything has defaults so you only need to uncomment things you want to +# change + +# possible values : production, development +app_mode = {{ grafana_app_mode }} + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +instance_name = {{ ansible_fqdn }} + +#################################### Paths #################################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +# +data = {{ grafana_data_path }} +# +# Directory where grafana can store logs +# +;logs = /var/log/grafana +# +# Directory where grafana will automatically scan and look for plugins +# +;plugins = /var/lib/grafana/plugins + +# +#################################### Server #################################### +[server] +# Protocol (http or https) +protocol = {{ grafana_server_protocol }} + +# The ip address to bind to, empty will bind to all interfaces +http_addr = {{ grafana_bind_ip_address }} + +# The http port to use +http_port = {{ grafana_http_port }} + +# The public facing domain name used to access grafana from a browser +domain = {{ grafana_domain_name }} + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +enforce_domain = {{ grafana_enforce_dom_name }} + +# The full public facing url +;root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Log web requests +;router_logging = false + +# the path relative working path +;static_root_path = public + +# enable gzip +;enable_gzip = false + +# https certs & key file +;cert_file = +;cert_key = + +#################################### Database #################################### +[database] +# Either "mysql", "postgres" or "sqlite3", it's your choice +type = {{ grafana_db_type }} +host = {{ grafana_db_hostport }} +name = {{ grafana_db_name }} +user = {{ grafana_db_user }} +password = {{ grafana_db_password }} + +# For "postgres" only, either "disable", "require" or "verify-full" +ssl_mode = {{ grafana_db_pg_ssl_mode }} + +# For "sqlite3" only, path relative to data_path setting +;path = grafana.db + +#################################### Session #################################### +[session] +# Either "memory", "file", "redis", "mysql", "postgres", default is "file" +{% if grafana_require_redis %} +provider = redis +{% else %} +provider = {{ grafana_session_provider }} +{% endif %} + +# Provider config options +# memory: not have any config yet +# file: session dir path, is relative to grafana data_path +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=grafana` +# mysql: go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name` +# postgres: user=a password=b host=localhost port=5432 dbname=c sslmode=disable +{% if grafana_require_redis %} +provider_config = {{ grafana_session_redis_config }} +{% else %} +provider_config = {{ grafana_session_config }} +{% endif %} + +# Session cookie name +;cookie_name = grafana_sess + +# If you use session in https only, default is false +;cookie_secure = false + +# Session life time, default is 86400 +;session_life_time = 86400 + +#################################### Analytics #################################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +reporting_enabled = {{ grafana_analytics_reporting_enabled }} + +# Set to false to disable all checks to https://grafana.net +# for new vesions (grafana itself and plugins), check is used +# in some UI views to notify that grafana or plugin update exists +# This option does not cause any auto updates, nor send any information +# only a GET request to http://grafana.net to get latest versions +check_for_updates = {{ grafana_analytics_updates_check }} + +# Google Analytics universal tracking code, only enabled if you specify an id here +;google_analytics_ua_id = + +#################################### Security #################################### +[security] +# default admin user, created on startup +;admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +;admin_password = admin + +# used for signing +;secret_key = SW2YcwTIb9zpOOhoPsMm + +# Auto-login remember days +;login_remember_days = 7 +;cookie_username = grafana_user +;cookie_remember_name = grafana_remember + +# disable gravatar profile images +;disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +;data_source_proxy_whitelist = + +[snapshots] +# snapshot sharing options +;external_enabled = true +;external_snapshot_url = https://snapshots-origin.raintank.io +;external_snapshot_name = Publish to snapshot.raintank.io + +#################################### Users #################################### +[users] +# disable user signup / registration +allow_sign_up = {{ grafana_u_allow_signup }} + +# Allow non admin users to create organizations +allow_org_create = {{ grafana_u_allow_org_create }} + +# Set to true to automatically assign new users to the default organization (id 1) +auto_assign_org = true + +# Default role new users will be automatically assigned (if disabled above is set to true) +auto_assign_org_role = {{ grafana_u_default_role }} + +# Background text for the user field on the login page +;login_hint = email or username + +# Default UI theme ("dark" or "light") +default_theme = {{ grafana_u_default_theme }} + +#################################### Anonymous Auth ########################## +[auth.anonymous] +# enable anonymous access +enabled = {{ grafana_auth_anon }} + +# specify organization name that should be used for unauthenticated users +;org_name = Main Org. + +# specify role for unauthenticated users +;org_role = Viewer + +#################################### Github Auth ########################## +[auth.github] +;enabled = false +;allow_sign_up = false +;client_id = some_id +;client_secret = some_secret +;scopes = user:email,read:org +;auth_url = https://github.com/login/oauth/authorize +;token_url = https://github.com/login/oauth/access_token +;api_url = https://api.github.com/user +;team_ids = +;allowed_organizations = + +#################################### Google Auth ########################## +[auth.google] +;enabled = false +;allow_sign_up = false +;client_id = some_client_id +;client_secret = some_client_secret +;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email +;auth_url = https://accounts.google.com/o/oauth2/auth +;token_url = https://accounts.google.com/o/oauth2/token +;api_url = https://www.googleapis.com/oauth2/v1/userinfo +;allowed_domains = + +#################################### Auth Proxy ########################## +[auth.proxy] +;enabled = false +;header_name = X-WEBAUTH-USER +;header_property = username +;auto_sign_up = true + +#################################### Basic Auth ########################## +[auth.basic] +;enabled = true + +#################################### Auth LDAP ########################## +[auth.ldap] +enabled = {{ grafana_ldap_auth }} +config_file = /etc/grafana/ldap.toml + +#################################### SMTP / Emailing ########################## +[smtp] +;enabled = false +;host = localhost:25 +;user = +;password = +;cert_file = +;key_file = +;skip_verify = false +;from_address = admin@grafana.localhost + +[emails] +;welcome_email_on_sign_up = false + +#################################### Logging ########################## +[log] +# Either "console", "file", "syslog". Default is console and file +# Use space to separate multiple modes, e.g. "console file" +mode = {{ grafana_log_mode }} + +# Either "trace", "debug", "info", "warn", "error", "critical", default is "info" +level = {{ grafana_log_level }} + +# For "console" mode only +[log.console] +;level = + +# log line format, valid options are text, console and json +;format = console + +# For "file" mode only +[log.file] +;level = + +# log line format, valid options are text, console and json +;format = text + +# This enables automated log rotate(switch of following options), default is true +;log_rotate = true + +# Max line number of single file, default is 1000000 +;max_lines = 1000000 + +# Max size shift of single file, default is 28 means 1 << 28, 256MB +;max_size_shift = 28 + +# Segment log daily, default is true +;daily_rotate = true + +# Expired days of log file(delete after max days), default is 7 +;max_days = 7 + +[log.syslog] +level = {{ grafana_log_level }} + +# log line format, valid options are text, console and json +;format = text + +# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used. +;network = +;address = + +# Syslog facility. user, daemon and local0 through local7 are valid. +facility = {{ grafana_syslog_facility }} + +# Syslog tag. By default, the process' argv[0] is used. +;tag = + + +#################################### AMQP Event Publisher ########################## +[event_publisher] +;enabled = false +;rabbitmq_url = amqp://localhost/ +;exchange = grafana_events + +;#################################### Dashboard JSON files ########################## +[dashboards.json] +enabled = {{ grafana_dashboard_json }} +path = /var/lib/grafana/dashboards + +#################################### Internal Grafana Metrics ########################## +# Metrics available at HTTP API Url /api/metrics +[metrics] +# Disable / Enable internal metrics +;enabled = true + +# Publish interval +;interval_seconds = 10 + +# Send internal metrics to Graphite +; [metrics.graphite] +; address = localhost:2003 +; prefix = prod.grafana.%(instance_name)s. + +#################################### Internal Grafana Metrics ########################## +# Url used to to import dashboards directly from Grafana.net +[grafana_net] +url = https://grafana.net diff --git a/grafana/templates/ldap.toml.j2 b/grafana/templates/ldap.toml.j2 new file mode 100644 index 00000000..f7a7f414 --- /dev/null +++ b/grafana/templates/ldap.toml.j2 @@ -0,0 +1,85 @@ +# Set to true to log user information returned from LDAP +verbose_logging = false + +[[servers]] +# Ldap server host (specify multiple hosts space separated) +host = "{{ grafana_ldap_host }}" +# Default port is 389 or 636 if use_ssl = true +port = {{ grafana_ldap_port }} +# Set to true if ldap server supports TLS +use_ssl = {{ grafana_ldap_use_ssl }} +# set to true if you want to skip ssl cert validation +ssl_skip_verify = {{ grafana_ldap_ssl_skip_verify }} +# 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 = "{{ grafana_ldap_bind_dn }}" +# Search user bind password +bind_password = '{{ grafana_ldap_bind_pwd }}' + +# User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)" +search_filter = "{{ grafana_ldap_u_search_filter }}" + +# An array of base dns to search through +search_base_dns = ["{{ grafana_ldap_u_search_base }}"] + +# 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) +{% if grafana_ldap_posix_groups %} +group_search_filter = "{{ grafana_ldap_g_search_filter }}" +## 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 = "{{ grafana_ldap_g_search_filter_user_attr }}" +## An array of the base DNs to search through for groups. Typically uses ou=groups +group_search_base_dns = ["{{ grafana_ldap_g_search_base }}"] +{% endif %} + +# Specify names of the ldap attributes your ldap uses +[servers.attributes] +name = "givenName" +surname = "sn" +username = "{{ grafana_ldap_serverattrs_username }}" +{% if grafana_ldap_posix_groups %} +member_of = "cn" +{% else %} +member_of = "memberOf" +{% endif %} +email = "{{ grafana_ldap_u_email }}" + +# Map ldap groups to grafana org roles +[[servers.group_mappings]] +group_dn = "{{ grafana_ldap_admin_role_group }}" +org_role = "Admin" +# The Grafana organization database id, optional, if left out the default org (id 1) will be used +# org_id = 1 + +{% for map in grafana_ldap_group_roles %} +[[servers.group_mappings]] +group_dn = "{{ map.dn }}" +org_role = "{{ map.role }}" + +{% endfor %} diff --git a/grafana/vars/main.yml b/grafana/vars/main.yml new file mode 100644 index 00000000..a616266f --- /dev/null +++ b/grafana/vars/main.yml @@ -0,0 +1,5 @@ +--- +redis_install: True +setup_nginx: True +nginx_use_common_virthost: True +