Dashy Security Testing: Default Credentials, API Keys, Configuration File, and Embedded Secrets

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.

Table of Contents

  1. Authentication Status and Configuration Access
  2. conf.yml API Key and Service Secret Extraction
  3. Internal Network Topology Enumeration
  4. Dashy Security Hardening

Authentication Status and Configuration Access

# 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

conf.yml API Key and Service Secret Extraction

# 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

Internal Network Topology Enumeration

# 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

Dashy Security Hardening

Dashy Security Hardening Checklist:
Security TestMethodRisk
Unauthenticated dashboard access and service URL enumerationGET / with no authentication — Dashy has no auth by default; view all configured service URLs, internal IP addresses, and port numbers; complete infrastructure topology mapCritical
conf.yml embedded API key extractionRead /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 widgetCritical
statusCheck URL internal network topology mappingParse statusCheckUrl fields — reveals all internal service IP addresses, hostnames, and ports including management interfaces not otherwise visible externallyHigh
Widget API token extractionParse widget.options fields for apiKey, token, key fields — weather API keys, stock API keys, GitHub tokens, status page API keys embedded in widget configurationHigh
statusCheck Authorization header credential extractionParse statusCheckHeaders — may contain Authorization: Bearer or basic auth headers for service authentication; grants direct API access to all services with headers configuredHigh

Automate Dashy Security Testing

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