Configure Jaeger authentication with OAuth2 and RBAC for enterprise security

Advanced 45 min Jun 11, 2026 43 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up enterprise-grade authentication for Jaeger distributed tracing using OAuth2 with Keycloak integration and role-based access control policies for secure production deployments.

Prerequisites

  • Root or sudo access
  • At least 4GB RAM
  • Valid SSL certificates
  • Network connectivity to OAuth2 provider

What this solves

Jaeger provides powerful distributed tracing capabilities, but out of the box it lacks authentication and authorization. This tutorial configures enterprise-grade security for Jaeger using OAuth2 authentication with Keycloak, implementing role-based access control (RBAC) policies, and adding SSL/TLS encryption for production environments.

Step-by-step configuration

Install Jaeger components

Start by installing Jaeger collector, query, and agent components with their dependencies.

sudo apt update
sudo apt install -y curl wget gnupg2 software-properties-common
wget https://github.com/jaegertracing/jaeger/releases/download/v1.52.0/jaeger-1.52.0-linux-amd64.tar.gz
tar -xzf jaeger-1.52.0-linux-amd64.tar.gz
sudo cp jaeger-1.52.0-linux-amd64/jaeger-* /usr/local/bin/
sudo chmod +x /usr/local/bin/jaeger-*
sudo dnf update -y
sudo dnf install -y curl wget gnupg2
wget https://github.com/jaegertracing/jaeger/releases/download/v1.52.0/jaeger-1.52.0-linux-amd64.tar.gz
tar -xzf jaeger-1.52.0-linux-amd64.tar.gz
sudo cp jaeger-1.52.0-linux-amd64/jaeger-* /usr/local/bin/
sudo chmod +x /usr/local/bin/jaeger-*

Install and configure Elasticsearch backend

Set up Elasticsearch as the storage backend for Jaeger traces with security enabled.

curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update
sudo apt install -y elasticsearch
sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
echo '[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
autorefresh=1
type=rpm-md' | sudo tee /etc/yum.repos.d/elasticsearch.repo
sudo dnf install -y --enablerepo=elasticsearch elasticsearch
cluster.name: jaeger-cluster
node.name: jaeger-node-1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 127.0.0.1
http.port: 9200
xpack.security.enabled: true
xpack.security.authc.api_key.enabled: true
sudo systemctl enable --now elasticsearch
sudo systemctl status elasticsearch

Install and configure Keycloak for OAuth2

Set up Keycloak as the OAuth2 identity provider for Jaeger authentication.

wget https://github.com/keycloak/keycloak/releases/download/22.0.5/keycloak-22.0.5.tar.gz
tar -xzf keycloak-22.0.5.tar.gz
sudo mv keycloak-22.0.5 /opt/keycloak
sudo useradd -r -s /bin/false keycloak
sudo chown -R keycloak:keycloak /opt/keycloak
[Unit]
Description=Keycloak Authentication Server
After=network.target

[Service]
Type=idle
User=keycloak
Group=keycloak
ExecStart=/opt/keycloak/bin/kc.sh start --http-enabled=true --http-host=0.0.0.0 --http-port=8080
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now keycloak
sudo systemctl status keycloak

Configure Keycloak realm and client

Create a Keycloak realm and OAuth2 client for Jaeger authentication.

curl -X POST http://localhost:8080/admin/realms \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $(curl -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "username=admin&password=admin&grant_type=password&client_id=admin-cli" \
    | jq -r .access_token)" \
  -d '{
    "realm": "jaeger",
    "enabled": true,
    "displayName": "Jaeger Tracing"
  }'
curl -X POST http://localhost:8080/admin/realms/jaeger/clients \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "clientId": "jaeger-query",
    "enabled": true,
    "protocol": "openid-connect",
    "publicClient": false,
    "redirectUris": ["https://example.com:16686/oauth/callback"],
    "webOrigins": ["https://example.com:16686"]
  }'

Create Jaeger service configurations

Configure the Jaeger collector and query services with OAuth2 authentication.

[Unit]
Description=Jaeger Collector
After=network.target elasticsearch.service

[Service]
Type=simple
User=jaeger
Group=jaeger
ExecStart=/usr/local/bin/jaeger-collector \
  --es.server-urls=http://localhost:9200 \
  --es.username=elastic \
  --es.password=changeme \
  --collector.grpc-tls.enabled=true \
  --collector.grpc-tls.cert=/etc/jaeger/tls/collector.crt \
  --collector.grpc-tls.key=/etc/jaeger/tls/collector.key
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
[Unit]
Description=Jaeger Query Service
After=network.target elasticsearch.service keycloak.service

[Service]
Type=simple
User=jaeger
Group=jaeger
Environment=OAUTH2_PROXY_PROVIDER=keycloak-oidc
Environment=OAUTH2_PROXY_KEYCLOAK_GROUP=jaeger-users
Environment=OAUTH2_PROXY_CLIENT_ID=jaeger-query
Environment=OAUTH2_PROXY_CLIENT_SECRET=your-client-secret
Environment=OAUTH2_PROXY_OIDC_ISSUER_URL=http://localhost:8080/realms/jaeger
Environment=OAUTH2_PROXY_REDIRECT_URL=https://example.com:16686/oauth/callback
ExecStart=/usr/local/bin/jaeger-query \
  --es.server-urls=http://localhost:9200 \
  --es.username=elastic \
  --es.password=changeme \
  --query.bearer-token-propagation=true \
  --query.ui-config=/etc/jaeger/ui-config.json
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Install OAuth2 proxy for authentication

Set up OAuth2 proxy to handle authentication between Jaeger and Keycloak.

wget https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.5.1/oauth2-proxy-v7.5.1.linux-amd64.tar.gz
tar -xzf oauth2-proxy-v7.5.1.linux-amd64.tar.gz
sudo cp oauth2-proxy-v7.5.1.linux-amd64/oauth2-proxy /usr/local/bin/
sudo chmod +x /usr/local/bin/oauth2-proxy
http_address = "0.0.0.0:4180"
upstreams = [
  "http://127.0.0.1:16686"
]

provider = "keycloak-oidc"
oidc_issuer_url = "http://localhost:8080/realms/jaeger"
client_id = "jaeger-query"
client_secret = "your-client-secret-here"

email_domains = [
  "*"
]

cookie_secret = "$(openssl rand -base64 32 | head -c 32)"
cookie_secure = true
cookie_httponly = true
cookie_samesite = "lax"

set_xauthrequest = true
pass_authorization_header = true
pass_access_token = true
pass_user_headers = true

authorized_groups = [
  "/jaeger-admins",
  "/jaeger-users"
]

Configure RBAC policies

Set up role-based access control with different permission levels for Jaeger users.

apiVersion: rbac.jaeger.io/v1
kind: RoleBinding
metadata:
  name: jaeger-admin-binding
subjects:
  • kind: Group
name: jaeger-admins apiGroup: rbac.jaeger.io roleRef: kind: Role name: admin apiGroup: rbac.jaeger.io --- apiVersion: rbac.jaeger.io/v1 kind: RoleBinding metadata: name: jaeger-user-binding subjects:
  • kind: Group
name: jaeger-users apiGroup: rbac.jaeger.io roleRef: kind: Role name: viewer apiGroup: rbac.jaeger.io --- apiVersion: rbac.jaeger.io/v1 kind: Role metadata: name: admin rules:
  • apiGroups: [""]
resources: ["traces", "services", "operations"] verbs: ["get", "list", "create", "update", "delete"] --- apiVersion: rbac.jaeger.io/v1 kind: Role metadata: name: viewer rules:
  • apiGroups: [""]
resources: ["traces", "services", "operations"] verbs: ["get", "list"]

Generate SSL certificates

Create SSL certificates for secure communication between Jaeger components.

sudo mkdir -p /etc/jaeger/tls
sudo openssl req -x509 -newkey rsa:4096 -keyout /etc/jaeger/tls/jaeger.key -out /etc/jaeger/tls/jaeger.crt -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/OU=OrgUnit/CN=example.com"
sudo openssl req -x509 -newkey rsa:4096 -keyout /etc/jaeger/tls/collector.key -out /etc/jaeger/tls/collector.crt -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/OU=OrgUnit/CN=collector.example.com"
sudo chown -R jaeger:jaeger /etc/jaeger/tls
sudo chmod 600 /etc/jaeger/tls/*.key
sudo chmod 644 /etc/jaeger/tls/*.crt

Configure NGINX reverse proxy

Set up NGINX as a reverse proxy with SSL termination for Jaeger services.

sudo apt install -y nginx
sudo dnf install -y nginx
upstream oauth2_proxy {
    server 127.0.0.1:4180;
}

upstream jaeger_query {
    server 127.0.0.1:16686;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/jaeger/tls/jaeger.crt;
    ssl_certificate_key /etc/jaeger/tls/jaeger.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;

    location /oauth/ {
        proxy_pass http://oauth2_proxy;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location / {
        auth_request /oauth/auth;
        error_page 401 = @error401;
        
        proxy_pass http://jaeger_query;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Authorization $http_authorization;
    }

    location @error401 {
        return 302 /oauth/start?rd=$request_uri;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}
sudo ln -s /etc/nginx/sites-available/jaeger /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Create system users and directories

Set up the jaeger system user and required directories with proper permissions.

sudo useradd -r -s /bin/false jaeger
sudo mkdir -p /var/log/jaeger /etc/jaeger
sudo chown -R jaeger:jaeger /var/log/jaeger /etc/jaeger
sudo chmod 755 /var/log/jaeger /etc/jaeger
Never use chmod 777. It gives every user on the system full access to your files. Instead, fix ownership with chown and use minimal permissions like 755 for directories and 644 for files.

Start and enable all services

Enable and start all Jaeger services in the correct order.

sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch
sudo systemctl enable --now keycloak
sudo systemctl enable --now jaeger-collector
sudo systemctl enable --now jaeger-query
[Unit]
Description=OAuth2 Proxy
After=network.target keycloak.service

[Service]
Type=simple
User=oauth2-proxy
Group=oauth2-proxy
ExecStart=/usr/local/bin/oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo useradd -r -s /bin/false oauth2-proxy
sudo chown -R oauth2-proxy:oauth2-proxy /etc/oauth2-proxy
sudo systemctl enable --now oauth2-proxy

Configure Keycloak groups and users

Create user groups

Set up Keycloak groups for different access levels in Jaeger.

TOKEN=$(curl -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=admin&password=admin&grant_type=password&client_id=admin-cli" \
  | jq -r .access_token)

curl -X POST http://localhost:8080/admin/realms/jaeger/groups \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name": "jaeger-admins"}'

curl -X POST http://localhost:8080/admin/realms/jaeger/groups \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name": "jaeger-users"}'

Create test users

Add test users with different permission levels for validation.

curl -X POST http://localhost:8080/admin/realms/jaeger/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "username": "admin@example.com",
    "email": "admin@example.com",
    "enabled": true,
    "credentials": [{
      "type": "password",
      "value": "SecureP@ssw0rd123",
      "temporary": false
    }]
  }'

curl -X POST http://localhost:8080/admin/realms/jaeger/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "username": "viewer@example.com",
    "email": "viewer@example.com",
    "enabled": true,
    "credentials": [{
      "type": "password",
      "value": "ViewerP@ssw0rd123",
      "temporary": false
    }]
  }'

Verify your setup

Test that all components are running and authentication is working properly.

sudo systemctl status elasticsearch keycloak jaeger-collector jaeger-query oauth2-proxy nginx
curl -k https://example.com/api/services
curl http://localhost:8080/realms/jaeger/.well-known/openid_configuration

Access the Jaeger UI at https://example.com and verify that:

  • You are redirected to Keycloak for authentication
  • Login works with the test credentials
  • Different users have appropriate access levels
  • SSL certificates are properly configured

Common issues

SymptomCauseFix
OAuth2 redirect failsIncorrect redirect URI in KeycloakUpdate client redirect URIs in Keycloak admin
SSL certificate errorsSelf-signed certificatesUse Let's Encrypt or proper CA-signed certificates
Elasticsearch connection failsAuthentication credentialsVerify ES username/password in service configs
NGINX proxy errorsUpstream service not runningCheck sudo systemctl status jaeger-query oauth2-proxy
Keycloak groups not workingGroup mapping misconfigurationVerify group assignments in Keycloak admin console
RBAC permissions deniedRole binding configurationCheck /etc/jaeger/rbac-config.yaml syntax

Next steps

Running this in production?

Want this handled for you? Running this at scale adds a second layer of work: capacity planning, failover drills, cost control, and on-call. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.