Configure Grafana with OAuth SSO authentication, implement role-based access control (RBAC), and harden security with SSL certificates for enterprise-grade monitoring dashboard access.
Prerequisites
- Domain name with DNS access
- GitHub organization or similar OAuth provider
- SSL certificate or ability to generate one
What this solves
Grafana's default admin login creates security risks in production environments where multiple teams need different access levels. OAuth authentication integrates with existing identity providers like Google, GitHub, or Azure AD, while RBAC ensures users only access appropriate dashboards and data sources.
Prerequisites and OAuth provider setup
Update system packages
Start with fresh package lists and security updates.
sudo apt update && sudo apt upgrade -y
Install Grafana if not already present
Install the latest Grafana version from the official repository.
sudo apt install -y apt-transport-https software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
Create GitHub OAuth application
Register a new OAuth app in GitHub to enable authentication. Go to GitHub Settings > Developer settings > OAuth Apps > New OAuth App.
Use these settings:
- Application name:
Grafana Production - Homepage URL:
https://monitoring.example.com - Authorization callback URL:
https://monitoring.example.com/login/github
Save the Client ID and generate a new Client Secret. You'll need both for Grafana configuration.
Configure Grafana OAuth settings
Configure OAuth authentication
Edit the main Grafana configuration to enable GitHub OAuth and disable local signups.
[server]
protocol = https
http_port = 3000
domain = monitoring.example.com
root_url = https://monitoring.example.com/
[security]
admin_user = admin
admin_password = $__file{/etc/grafana/admin_password}
secret_key = $__file{/etc/grafana/secret_key}
disable_gravatar = true
cookie_secure = true
cookie_samesite = strict
[auth]
disable_login_form = false
disable_signout_menu = false
oauth_auto_login = false
[auth.github]
enabled = true
allow_sign_up = true
client_id = your_github_client_id_here
client_secret = $__file{/etc/grafana/github_client_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 = your-org-name
role_attribute_path =
allow_assign_grafana_admin = false
[users]
allowed_sign_up = false
auto_assign_org = true
auto_assign_org_id = 1
auto_assign_org_role = Viewer
default_theme = dark
Store sensitive credentials securely
Create separate files for secrets to avoid exposing them in the main config.
sudo mkdir -p /etc/grafana/secrets
sudo chmod 750 /etc/grafana/secrets
sudo chown grafana:grafana /etc/grafana/secrets
echo "your_github_client_secret_here" | sudo tee /etc/grafana/github_client_secret
echo "$(openssl rand -hex 32)" | sudo tee /etc/grafana/secret_key
echo "$(openssl rand -base64 32)" | sudo tee /etc/grafana/admin_password
sudo chmod 640 /etc/grafana/github_client_secret /etc/grafana/secret_key /etc/grafana/admin_password
sudo chown root:grafana /etc/grafana/github_client_secret /etc/grafana/secret_key /etc/grafana/admin_password
Configure SSL certificates
Install SSL certificates for secure authentication. This example uses Let's Encrypt certificates.
sudo apt install -y certbot
sudo certbot certonly --standalone -d monitoring.example.com
Update Grafana configuration with SSL certificate paths:
[server]
protocol = https
http_port = 3000
cert_file = /etc/letsencrypt/live/monitoring.example.com/fullchain.pem
cert_key = /etc/letsencrypt/live/monitoring.example.com/privkey.pem
Set up RBAC and team mapping
Enable RBAC in Grafana configuration
Configure role-based access control for fine-grained permissions.
[rbac]
permission_cache = true
[feature_toggles]
enable = accesscontrol
[auth.github]
role_attribute_path = contains(groups[], 'monitoring-admins') && 'Admin' || contains(groups[], 'monitoring-editors') && 'Editor' || 'Viewer'
Create team mapping configuration
Set up automatic team assignment based on GitHub organization membership.
sudo mkdir -p /etc/grafana/provisioning/access-control
sudo mkdir -p /etc/grafana/provisioning/teams
apiVersion: 1
teams:
- name: monitoring-admins
email: monitoring-admins@example.com
orgId: 1
- name: monitoring-editors
email: monitoring-editors@example.com
orgId: 1
- name: monitoring-viewers
email: monitoring-viewers@example.com
orgId: 1
Configure role permissions
Define granular permissions for each role using RBAC policies.
apiVersion: 1
roles:
- name: monitoring:viewer:restricted
description: "Restricted viewer with limited dashboard access"
version: 1
orgId: 1
permissions:
- action: dashboards:read
scope: dashboards:*
- action: datasources:query
scope: datasources:*
- name: monitoring:editor:infrastructure
description: "Infrastructure team editor permissions"
version: 1
orgId: 1
permissions:
- action: dashboards:read
scope: dashboards:*
- action: dashboards:write
scope: dashboards:*
- action: datasources:query
scope: datasources:*
- action: alerts:read
scope: alerts:*
- action: alerts:write
scope: alerts:*
role_assignments:
- role_uid: monitoring:viewer:restricted
teams:
- monitoring-viewers
- role_uid: monitoring:editor:infrastructure
teams:
- monitoring-editors
Security hardening
Configure security headers and session settings
Implement additional security measures to protect against common web vulnerabilities.
[security]
content_type_protection_header = true
x_content_type_options = nosniff
x_xss_protection = true
strict_transport_security = true
strict_transport_security_max_age_seconds = 86400
strict_transport_security_preload = true
strict_transport_security_subdomains = true
x_frame_options = deny
cookie_secure = true
cookie_samesite = strict
session_life_time = 86400
token_rotation_interval_minutes = 10
[log]
mode = syslog
level = warn
filters = oauth:debug
Configure firewall rules
Restrict access to Grafana to authorized networks only.
sudo ufw allow from 203.0.113.0/24 to any port 3000
sudo ufw allow from 198.51.100.0/24 to any port 3000
sudo ufw --force enable
Set correct file permissions
Secure configuration files with appropriate ownership and permissions.
sudo chown -R grafana:grafana /etc/grafana/
sudo chmod 640 /etc/grafana/grafana.ini
sudo chmod -R 640 /etc/grafana/provisioning/
sudo chmod 750 /etc/grafana/provisioning/
sudo find /etc/grafana/provisioning/ -type d -exec chmod 750 {} \;
Start and enable Grafana service
Enable automatic startup and start the Grafana service with new configuration.
sudo systemctl daemon-reload
sudo systemctl enable grafana-server
sudo systemctl restart grafana-server
sudo systemctl status grafana-server
Verify your setup
Test OAuth authentication and RBAC configuration.
sudo systemctl status grafana-server
sudo journalctl -u grafana-server -f --no-pager
Visit https://monitoring.example.com:3000 and verify:
- SSL certificate is valid and secure
- "Sign in with GitHub" button appears
- OAuth flow redirects to GitHub and back successfully
- User roles are assigned based on GitHub organization membership
curl -k -I https://monitoring.example.com:3000/api/health
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| OAuth callback error | Wrong callback URL in GitHub app | Update GitHub OAuth app callback URL to match root_url + /login/github |
| SSL certificate errors | Grafana can't read certificate files | Check ownership: sudo chown grafana:grafana /etc/letsencrypt/live/monitoring.example.com/*.pem |
| Users assigned wrong roles | GitHub team mapping not working | Verify GitHub organization visibility and user membership in teams |
| Grafana won't start | Configuration syntax error | Check logs: sudo journalctl -u grafana-server and validate config syntax |
| Permission denied for secrets | Wrong file ownership | Fix with: sudo chown root:grafana /etc/grafana/github_client_secret && sudo chmod 640 /etc/grafana/github_client_secret |
Next steps
- Set up Grafana alerting with Slack and Microsoft Teams integration
- Configure advanced Grafana dashboards and alerting with Prometheus integration
- Implement two-factor authentication for SSH with Google Authenticator and TOTP
- Configure Grafana LDAP authentication and RBAC with Active Directory
- Set up Grafana high availability clustering with load balancing
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # No Color
# Script configuration
readonly SCRIPT_NAME="$(basename "$0")"
readonly GRAFANA_CONFIG="/etc/grafana/grafana.ini"
readonly GRAFANA_USER="grafana"
# Usage function
usage() {
echo "Usage: $SCRIPT_NAME <domain> <github_client_id> <github_client_secret> <github_org>"
echo "Example: $SCRIPT_NAME monitoring.example.com abc123 def456 your-org-name"
exit 1
}
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Cleanup function
cleanup() {
log_error "Installation failed. Cleaning up..."
systemctl stop grafana-server 2>/dev/null || true
# Remove repos if they were added
rm -f /etc/apt/sources.list.d/grafana.list 2>/dev/null || true
rm -f /etc/yum.repos.d/grafana.repo 2>/dev/null || true
}
# Set trap for cleanup on error
trap cleanup ERR
# Check arguments
if [ $# -ne 4 ]; then
usage
fi
DOMAIN="$1"
GITHUB_CLIENT_ID="$2"
GITHUB_CLIENT_SECRET="$3"
GITHUB_ORG="$4"
# Validate domain format
if [[ ! "$DOMAIN" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
log_error "Invalid domain format: $DOMAIN"
exit 1
fi
# Check if running as root
if [ "$EUID" -ne 0 ]; then
log_error "This script must be run as root"
exit 1
fi
log_info "[1/8] Detecting operating system..."
# Detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
log_error "Cannot detect operating system"
exit 1
fi
log_info "Detected OS: $PRETTY_NAME (Package manager: $PKG_MGR)"
log_info "[2/8] Updating system packages..."
$PKG_UPDATE
log_info "[3/8] Installing prerequisites..."
if [ "$PKG_MGR" = "apt" ]; then
$PKG_INSTALL apt-transport-https software-properties-common wget gnupg2 openssl
else
$PKG_INSTALL wget gnupg2 openssl
fi
log_info "[4/8] Installing Grafana..."
# Install Grafana based on package manager
if [ "$PKG_MGR" = "apt" ]; then
# Debian/Ubuntu
wget -q -O - https://packages.grafana.com/gpg.key | apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" > /etc/apt/sources.list.d/grafana.list
apt update
$PKG_INSTALL grafana
else
# RHEL-based
rpm --import https://packages.grafana.com/gpg.key
cat > /etc/yum.repos.d/grafana.repo << EOF
[grafana]
name=grafana
baseurl=https://packages.grafana.com/oss/rpm
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://packages.grafana.com/gpg.key
EOF
$PKG_INSTALL grafana
fi
log_info "[5/8] Creating secure credential storage..."
# Create secrets directory
mkdir -p /etc/grafana/secrets
chown root:$GRAFANA_USER /etc/grafana/secrets
chmod 750 /etc/grafana/secrets
# Generate and store secrets
echo "$GITHUB_CLIENT_SECRET" > /etc/grafana/github_client_secret
openssl rand -hex 32 > /etc/grafana/secret_key
openssl rand -base64 32 > /etc/grafana/admin_password
# Set secure permissions on secret files
for file in /etc/grafana/github_client_secret /etc/grafana/secret_key /etc/grafana/admin_password; do
chown root:$GRAFANA_USER "$file"
chmod 640 "$file"
done
log_info "[6/8] Configuring Grafana OAuth settings..."
# Backup original config
cp "$GRAFANA_CONFIG" "${GRAFANA_CONFIG}.backup"
# Create new Grafana configuration
cat > "$GRAFANA_CONFIG" << EOF
[server]
protocol = http
http_port = 3000
domain = $DOMAIN
root_url = http://$DOMAIN:3000/
[security]
admin_user = admin
admin_password = \$__file{/etc/grafana/admin_password}
secret_key = \$__file{/etc/grafana/secret_key}
disable_gravatar = true
cookie_secure = false
cookie_samesite = lax
[auth]
disable_login_form = false
disable_signout_menu = false
oauth_auto_login = false
[auth.github]
enabled = true
allow_sign_up = true
client_id = $GITHUB_CLIENT_ID
client_secret = \$__file{/etc/grafana/github_client_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 = $GITHUB_ORG
role_attribute_path =
allow_assign_grafana_admin = false
[users]
allow_sign_up = false
auto_assign_org = true
auto_assign_org_id = 1
auto_assign_org_role = Viewer
default_theme = dark
[log]
mode = file
level = info
EOF
# Set proper ownership on config file
chown root:$GRAFANA_USER "$GRAFANA_CONFIG"
chmod 640 "$GRAFANA_CONFIG"
log_info "[7/8] Starting and enabling Grafana service..."
# Start and enable Grafana
systemctl daemon-reload
systemctl enable grafana-server
systemctl start grafana-server
# Wait for service to start
sleep 5
log_info "[8/8] Configuring firewall..."
# Configure firewall based on available tools
if command -v ufw >/dev/null 2>&1; then
ufw allow 3000/tcp
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=3000/tcp
firewall-cmd --reload
elif command -v iptables >/dev/null 2>&1; then
iptables -A INPUT -p tcp --dport 3000 -j ACCEPT
# Save iptables rules (method varies by distro)
if command -v iptables-save >/dev/null 2>&1; then
iptables-save > /etc/iptables/rules.v4 2>/dev/null || true
fi
fi
log_info "Performing installation verification..."
# Verify Grafana service is running
if ! systemctl is-active --quiet grafana-server; then
log_error "Grafana service is not running"
exit 1
fi
# Verify configuration files exist and have correct permissions
for file in "$GRAFANA_CONFIG" /etc/grafana/admin_password /etc/grafana/secret_key /etc/grafana/github_client_secret; do
if [ ! -f "$file" ]; then
log_error "Missing configuration file: $file"
exit 1
fi
done
# Test if Grafana is responding
if ! curl -s -o /dev/null -w "%{http_code}" "http://localhost:3000" | grep -q "200\|302"; then
log_error "Grafana is not responding on port 3000"
exit 1
fi
# Clear trap
trap - ERR
log_info "Grafana installation completed successfully!"
echo
echo "Next steps:"
echo "1. Access Grafana at: http://$DOMAIN:3000"
echo "2. Login with GitHub OAuth or admin credentials"
echo "3. Admin password is stored in: /etc/grafana/admin_password"
echo "4. Configure SSL certificates for production use"
echo "5. Update GitHub OAuth app with the correct callback URL"
echo
log_warn "Remember to:"
log_warn "- Set up SSL/TLS certificates for production"
log_warn "- Configure proper backup procedures"
log_warn "- Review and adjust RBAC settings as needed"
Review the script before running. Execute with: bash install.sh