Skip to main content

Local Users (Basic Auth)

Basic Authentication provides programmatic access for CI/CD pipelines, monitoring tools, and API clients. This guide covers local user configuration and best practices.

Overview

Local users are defined in Keyline configuration and authenticate using HTTP Basic Auth. Unlike OIDC users, local users do not create sessions - each request includes credentials.

Configuration

Basic Configuration

local_users:
enabled: true
users:
- username: ci-pipeline
password_bcrypt: ${CI_PASSWORD_BCRYPT}
groups:
- ci
email: ci@example.com
full_name: CI Pipeline

Configuration Options

OptionRequiredDescription
enabledNoEnable local user authentication (default: false)
usersYes (if enabled)Array of user definitions

User Definition

OptionRequiredDescription
usernameYesUnique username
password_bcryptYesBcrypt-hashed password
groupsNoUser groups for role mapping
emailNoUser email address
full_nameNoUser display name

Generating Bcrypt Passwords

# Generate bcrypt hash (cost factor 10)
htpasswd -bnBC 10 "" your-password | tr -d ':\n'

# Example output:
# $2y$10$nK4h5MH0MAIzd.R1wZFrUOrYoPiCdXRc3LACW0vByWWGS6LbQHlce

Using OpenSSL

# OpenSSL bcrypt
openssl passwd -bcrypt your-password

Using Python

import bcrypt
password = "your-password".encode('utf-8')
hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=10))
print(hashed.decode('utf-8'))

Using Node.js

const bcrypt = require('bcrypt');
const password = 'your-password';
const hash = await bcrypt.hash(password, 10);
console.log(hash);

Example Configurations

Single Admin User

local_users:
enabled: true
users:
- username: admin
password_bcrypt: ${ADMIN_PASSWORD_BCRYPT}
groups:
- admin
- superusers
email: admin@example.com
full_name: Admin User

Multiple Service Accounts

local_users:
enabled: true
users:
- username: ci-pipeline
password_bcrypt: ${CI_PASSWORD_BCRYPT}
groups:
- ci
email: ci@example.com
full_name: CI Pipeline

- username: monitoring
password_bcrypt: ${MONITORING_PASSWORD_BCRYPT}
groups:
- monitoring
email: monitoring@example.com
full_name: Monitoring Service

- username: backup-agent
password_bcrypt: ${BACKUP_PASSWORD_BCRYPT}
groups:
- backup
email: backup@example.com
full_name: Backup Agent

User with No Groups (Uses Default Roles)

local_users:
enabled: true
users:
- username: viewer
password_bcrypt: ${VIEWER_PASSWORD_BCRYPT}
email: viewer@example.com
full_name: Viewer User
# No groups - will use default_es_roles

Authentication Flow

Role Mapping Integration

Local user groups are mapped to Elasticsearch roles using role_mappings:

local_users:
enabled: true
users:
- username: developer
password_bcrypt: ${DEV_PASSWORD_BCRYPT}
groups:
- developers
- users

role_mappings:
- claim: groups
pattern: "developers"
es_roles:
- developer
- kibana_user

- claim: groups
pattern: "users"
es_roles:
- user

default_es_roles:
- viewer

Evaluation Logic:

  1. User developer has groups: ["developers", "users"]
  2. Both groups match role_mappings
  3. User gets ES roles: ["developer", "kibana_user", "user"]
  4. Elasticsearch handles multiple roles natively

Security Best Practices

Password Requirements

RequirementRecommendation
LengthMinimum 16 characters
ComplexityMix of uppercase, lowercase, numbers, symbols
RotationRotate every 90 days
StorageStore in secrets manager (Vault, AWS Secrets Manager)

Bcrypt Configuration

SettingRecommendedPurpose
Cost Factor10-12Higher = more secure but slower
Algorithm2y or 2bUse modern bcrypt variant

Environment Variables

Never store plaintext passwords in config files:

# ❌ BAD: Plaintext password in config
local_users:
users:
- username: admin
password_bcrypt: "$2y$10$..." # Don't commit hashes to git!

# ✅ GOOD: Environment variable substitution
local_users:
users:
- username: admin
password_bcrypt: ${ADMIN_PASSWORD_BCRYPT}

Service Account Guidelines

  1. Use descriptive usernames: ci-pipeline, monitoring, backup-agent
  2. Limit permissions: Assign only required roles
  3. Rotate regularly: Automate password rotation
  4. Audit usage: Monitor service account activity
  5. Use separate accounts: Don't share service accounts

Testing Authentication

Using curl

# Test Basic Auth
curl -u ci-pipeline:password http://localhost:9000/_cluster/health?pretty

# Expected response:
# {
# "cluster_name": "keyline-cluster",
# "status": "green",
# ...
# }

Using HTTPie

http --auth ci-pipeline:password http://localhost:9000/_cluster/health

Using Python

import requests
from requests.auth import HTTPBasicAuth

response = requests.get(
'http://localhost:9000/_cluster/health',
auth=HTTPBasicAuth('ci-pipeline', 'password')
)
print(response.json())

Troubleshooting

401 Unauthorized

Error: 401 Unauthorized with WWW-Authenticate: Basic realm="Keyline"

Causes:

  • Invalid username or password
  • User not found in local_users
  • Bcrypt hash corrupted

Solution:

  1. Verify username matches exactly (case-sensitive)
  2. Regenerate bcrypt hash
  3. Check environment variable substitution

User Not Found

Error: No matching username found

Solution:

  1. Verify local_users.enabled is true
  2. Check user is defined in config
  3. Validate YAML syntax

Bcrypt Validation Failed

Error: Invalid bcrypt hash

Solution:

  1. Ensure hash starts with $2y$ or $2a$
  2. Verify hash is complete (60 characters)
  3. Regenerate with cost factor 10-12

Next Steps