06.2 credentials.yml - samerfarida/mcp-ssh-orchestrator GitHub Wiki

6.2 credentials.yml

Purpose: Configure SSH authentication methods, manage secrets, and define credential resolution for mcp-ssh-orchestrator.

Overview

The credentials.yml file defines SSH authentication methods and secret management for connecting to target hosts. It supports both key-based and password-based authentication with secure secret resolution.

File Structure

# credentials.yml
entries:
  - name: "prod_admin"
    username: "ubuntu"
    key_path: "id_ed25519"
    key_passphrase_secret: "prod_key_passphrase"
    password_secret: ""

  - name: "legacy_password"
    username: "admin"
    key_path: ""
    password_secret: "legacy_password"

Configuration Fields

Required Fields

Field Type Description Example
name string Unique credential identifier "prod_admin"
username string SSH username "ubuntu"

Optional Fields

Field Type Default Description Example
key_path string "" SSH private key file path "id_ed25519"
key_passphrase_secret string "" Passphrase secret name "prod_key_passphrase"
password_secret string "" Password secret name "admin_password"

Authentication Methods

Key-Based Authentication (Recommended)

SSH Key Authentication:

entries:
  - name: "prod_admin"
    username: "ubuntu"
    key_path: "id_ed25519"
    key_passphrase_secret: "prod_key_passphrase"
    password_secret: ""  # Empty = no password auth

Key without Passphrase:

entries:
  - name: "dev_admin"
    username: "developer"
    key_path: "dev_key"
    key_passphrase_secret: ""  # Empty = no passphrase
    password_secret: ""

Password-Based Authentication

Password Authentication:

entries:
  - name: "legacy_admin"
    username: "admin"
    key_path: ""  # Empty = no key auth
    password_secret: "legacy_password"

Mixed Authentication (Key + Password):

entries:
  - name: "mixed_auth"
    username: "ubuntu"
    key_path: "id_ed25519"
    key_passphrase_secret: "key_passphrase"
    password_secret: "user_password"

Secret Resolution

Secret Resolution Order

Secrets are resolved in this order:

  1. Direct environment variable (Docker MCP Gateway): <SECRET_NAME> (uppercase)
    • Docker MCP Gateway injects secrets as env vars matching the env: field in server.yml
    • Example: server.yml has env: SSH_KEY_PASSPHRASE_01 → container gets SSH_KEY_PASSPHRASE_01
    • Secrets are ONLY injected when Docker MCP Gateway runs the container (not when running manually)
  2. Prefixed environment variable (standalone/backward compatibility): MCP_SSH_SECRET_<NAME> (uppercase)
    • Supports existing standalone deployments using prefixed env vars
  3. .env file: /app/secrets/.env
    • Consolidated secrets file with KEY=value format
    • Supports comments (lines starting with #) and empty lines
    • Supports quoted values (single or double quotes)
    • Values can contain = characters
    • Case-insensitive key lookup
  4. Docker secret file: /app/secrets/<name>
    • Individual file-based secret storage for standalone deployments
    • Maintains backward compatibility with existing setups
  5. Empty string: If not found

Docker MCP Gateway Secret Resolution

When using mcp-ssh-orchestrator with Docker MCP Gateway, secrets are injected as environment variables matching the env: field in server.yml.

Important Notes:

  • Secrets are ONLY injected when Docker MCP Gateway runs the container (not when running manually)
  • Docker MCP secret name: mcp-ssh-orchestrator.ssh_password_secret_01 (stored in Docker MCP)
  • Container env var: SSH_PASSWORD_SECRET_01 (injected by gateway, matches env: field, ALL CAPS)
  • The env: field in server.yml determines the environment variable name in the container

Example:

# server.yml (Docker MCP Registry)
secrets:
  - name: mcp-ssh-orchestrator.ssh_password_secret_01
    env: SSH_PASSWORD_SECRET_01
    example: <SSH_PASSWORD_SECRET_01>

# credentials.yml
entries:
  - name: "prod_admin"
    username: "ubuntu"
    key_path: "id_ed25519"
    key_passphrase_secret: "SSH_KEY_PASSPHRASE_01"  # Matches env: in server.yml
    password_secret: "SSH_PASSWORD_SECRET_01"        # Matches env: in server.yml

Verification:

# Set secret in Docker MCP
docker mcp secret set mcp-ssh-orchestrator.ssh_password_secret_01="my-password"

# When gateway runs the container, verify env var exists:
docker exec <gateway-container> env | grep SSH_PASSWORD_SECRET_01
# Should output: SSH_PASSWORD_SECRET_01=my-password

Using .env File for Secrets

The .env file provides a convenient way to manage all secrets in a single consolidated file, making it easier to organize and maintain credentials.

Creating a .env File

  1. Copy the example file:

    cp secrets/.env.example secrets/.env
  2. Edit secrets/.env and add your secrets:

    # SSH Passwords
    SSH_PASSWORD_SECRET_01=your-actual-password
    lab_maint_password=your-lab-password
    
    # SSH Key Passphrases
    SSH_KEY_PASSPHRASE_01=your-actual-passphrase
    prod_admin_passphrase=your-prod-passphrase
  3. Set secure file permissions:

    chmod 600 secrets/.env

.env File Format

  • Basic format: KEY=value (one per line)
  • Comments: Lines starting with # are ignored
  • Empty lines: Ignored
  • Quoted values: Supports single ('value') and double ("value") quotes
  • Values with =: Values can contain = characters (split on first =)
  • Whitespace: Keys and values are automatically stripped of leading/trailing whitespace

Example .env file:

# SSH Passwords
SSH_PASSWORD_SECRET_01=my-password-123
lab_maint_password="password with spaces"

# SSH Key Passphrases
SSH_KEY_PASSPHRASE_01=my-passphrase-456
prod_admin_passphrase='another passphrase'

# Values with equals signs
COMPLEX_VALUE=key1=value1&key2=value2

Security Best Practices

  • File permissions: Always set .env file to 600 (owner read/write only)

    chmod 600 secrets/.env
  • Never commit: Ensure .env is in .gitignore (it is by default)

  • Location: Keep .env file in the secrets/ directory

  • Backup: Store backups securely, never in version control

Migration from Individual Files

To migrate from individual secret files to .env:

  1. List all your secret files:

    ls -1 secrets/
  2. Read each secret and add to .env:

    echo "SECRET_NAME=$(cat secrets/SECRET_NAME)" >> secrets/.env
  3. Verify the .env file:

    cat secrets/.env
  4. Test that secrets still resolve correctly

  5. Optionally remove individual files (after verifying everything works)

.env File Caching

The .env file is cached after first load to improve performance. The cache is keyed by secrets_dir, so different directories maintain separate caches. If you modify the .env file, you may need to restart the application for changes to take effect (cache is not automatically invalidated).

Path Traversal Protection

Security Feature: Secret resolution includes path traversal protection to prevent unauthorized file access.

Input Length Limits

All string parameters in credentials configuration have length limits:

  • Secret Names: Maximum 100 characters
    • Validated when resolving secrets via password_secret, key_passphrase_secret
    • Names exceeding limit are rejected with security event logging
  • SSH Key Paths: Maximum 500 characters
    • Validated when resolving key paths
    • Paths exceeding limit are rejected with security event logging

Security: Length limits prevent resource exhaustion attacks via oversized inputs.

Secret Name Validation

  • Allowed characters: Alphanumeric (a-z, A-Z, 0-9), dash (-), underscore (_)

  • Rejected: Special characters, path separators, traversal patterns

  • Examples:

    #  Valid
    password_secret: "prod_password"
    password_secret: "key-passphrase"
    
    #  Invalid (rejected)
    password_secret: "../etc/passwd"    # Path traversal
    password_secret: "/absolute/path"   # Absolute path
    password_secret: "secret.name"      # Special characters

Path Resolution Security

  1. Relative paths only: Secrets must use relative paths (no absolute paths)
  2. Directory confinement: All resolved paths must stay within /app/secrets
  3. Normalization: Paths are normalized to handle encoded traversal attempts
  4. File type validation: Only regular files are accepted (directories and symlinks rejected)
  5. Security logging: Path traversal and file validation failures are logged as security events

File Type Requirements:

  • Regular files within secrets directory
  • Directories (must be files, not directories)
  • Symbolic links (rejected for security)

Effect: Prevents accessing files outside the secrets directory via path traversal attacks and blocks access to directories or symlinks that could pose security risks.

Environment Variables

Setting secrets via environment:

# Set secret via environment variable
export MCP_SSH_SECRET_PROD_KEY_PASSPHRASE="my-passphrase"
export MCP_SSH_SECRET_ADMIN_PASSWORD="admin-password"

# Use in Docker
docker run -i --rm \
  -e MCP_SSH_SECRET_PROD_KEY_PASSPHRASE="my-passphrase" \
  -e MCP_SSH_SECRET_ADMIN_PASSWORD="admin-password" \
  ghcr.io/samerfarida/mcp-ssh-orchestrator:latest

Claude Desktop configuration:

{
  "mcpServers": {
    "ssh-orchestrator": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_SSH_SECRET_PROD_PASSWORD=your-password",
        "-v", "/Users/YOUR_USERNAME/mcp-ssh/config:/app/config:ro",
        "-v", "/Users/YOUR_USERNAME/mcp-ssh/keys:/app/keys:ro",
        "ghcr.io/samerfarida/mcp-ssh-orchestrator:latest"
      ]
    }
  }
}

Docker Secrets

Creating Docker secrets:

# Create secret
echo "my-passphrase" | docker secret create ssh_key_passphrase -

# Use in Docker Compose
services:
  mcp-ssh:
    image: ghcr.io/samerfarida/mcp-ssh-orchestrator:latest
    secrets:
      - ssh_key_passphrase
    volumes:
      - ./config:/app/config:ro
      - ./keys:/app/keys:ro

secrets:
  ssh_key_passphrase:
    external: true

File-based secrets:

# Create secrets directory
mkdir -p ~/mcp-ssh/secrets
chmod 0700 ~/mcp-ssh/secrets

# Add secret files
echo "passphrase" > ~/mcp-ssh/secrets/key_passphrase
echo "password" > ~/mcp-ssh/secrets/admin_password

# Set permissions
chmod 0400 ~/mcp-ssh/secrets/*

SSH Key Management

Key Generation

Ed25519 Keys (Recommended):

# Generate Ed25519 key pair
ssh-keygen -t ed25519 -f ~/.ssh/mcp_orchestrator -C "mcp-ssh-orchestrator"

# Copy to keys directory
cp ~/.ssh/mcp_orchestrator ~/mcp-ssh/keys/id_ed25519
cp ~/.ssh/mcp_orchestrator.pub ~/mcp-ssh/keys/id_ed25519.pub

# Set permissions
chmod 0400 ~/mcp-ssh/keys/id_ed25519
chmod 0444 ~/mcp-ssh/keys/id_ed25519.pub

RSA Keys (Legacy):

# Generate RSA 4096-bit key pair
ssh-keygen -t rsa -b 4096 -f ~/.ssh/mcp_orchestrator_rsa -C "mcp-ssh-orchestrator"

# Copy to keys directory
cp ~/.ssh/mcp_orchestrator_rsa ~/mcp-ssh/keys/id_rsa
cp ~/.ssh/mcp_orchestrator_rsa.pub ~/mcp-ssh/keys/id_rsa.pub

# Set permissions
chmod 0400 ~/mcp-ssh/keys/id_rsa
chmod 0444 ~/mcp-ssh/keys/id_rsa.pub

Key Deployment

Deploy public key to target hosts:

# Copy public key to target host
ssh-copy-id -i ~/mcp-ssh/keys/id_ed25519.pub [email protected]

# Or manually add to authorized_keys
cat ~/mcp-ssh/keys/id_ed25519.pub | ssh [email protected] "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Key Rotation

Rotate SSH keys:

# Generate new key pair
ssh-keygen -t ed25519 -f ~/.ssh/mcp_orchestrator_new -C "mcp-ssh-orchestrator-new"

# Deploy new public key to all hosts
for host in $(ssh_list_hosts); do
  ssh-copy-id -i ~/.ssh/mcp_orchestrator_new.pub ubuntu@$host
done

# Update configuration
cp ~/.ssh/mcp_orchestrator_new ~/mcp-ssh/keys/id_ed25519
chmod 0400 ~/mcp-ssh/keys/id_ed25519

# Remove old key from hosts
for host in $(ssh_list_hosts); do
  ssh ubuntu@$host "sed -i '/mcp-ssh-orchestrator/d' ~/.ssh/authorized_keys"
done

Environment-Specific Configurations

Development Environment

# credentials.yml - Development
entries:
  - name: "dev_admin"
    username: "developer"
    key_path: "dev_key"
    key_passphrase_secret: ""  # No passphrase for dev
    password_secret: ""

  - name: "dev_root"
    username: "root"
    key_path: "dev_root_key"
    key_passphrase_secret: ""
    password_secret: ""

Staging Environment

# credentials.yml - Staging
entries:
  - name: "staging_admin"
    username: "ubuntu"
    key_path: "staging_key"
    key_passphrase_secret: "staging_key_passphrase"
    password_secret: ""

  - name: "staging_service"
    username: "service"
    key_path: "staging_service_key"
    key_passphrase_secret: "staging_service_passphrase"
    password_secret: ""

Production Environment

# credentials.yml - Production
entries:
  - name: "prod_admin"
    username: "ubuntu"
    key_path: "prod_key"
    key_passphrase_secret: "prod_key_passphrase"
    password_secret: ""

  - name: "prod_service"
    username: "service"
    key_path: "prod_service_key"
    key_passphrase_secret: "prod_service_passphrase"
    password_secret: ""

  - name: "prod_monitoring"
    username: "monitoring"
    key_path: "prod_monitoring_key"
    key_passphrase_secret: "prod_monitoring_passphrase"
    password_secret: ""

Multi-User Configurations

Different Users per Host Type

entries:
  # Web server credentials
  - name: "web_admin"
    username: "ubuntu"
    key_path: "web_key"
    key_passphrase_secret: "web_key_passphrase"
    password_secret: ""

  # Database server credentials
  - name: "db_admin"
    username: "postgres"
    key_path: "db_key"
    key_passphrase_secret: "db_key_passphrase"
    password_secret: ""

  # Monitoring server credentials
  - name: "monitor_admin"
    username: "monitoring"
    key_path: "monitor_key"
    key_passphrase_secret: "monitor_key_passphrase"
    password_secret: ""

Service Account Credentials

entries:
  # Application service account
  - name: "app_service"
    username: "app"
    key_path: "app_service_key"
    key_passphrase_secret: "app_service_passphrase"
    password_secret: ""

  # Backup service account
  - name: "backup_service"
    username: "backup"
    key_path: "backup_service_key"
    key_passphrase_secret: "backup_service_passphrase"
    password_secret: ""

  # Monitoring service account
  - name: "monitoring_service"
    username: "monitoring"
    key_path: "monitoring_service_key"
    key_passphrase_secret: "monitoring_service_passphrase"
    password_secret: ""

Path Traversal Protection for Keys

Security Feature: SSH key path resolution includes path traversal protection.

Key Path Validation

  • Relative paths: Resolved relative to keys_dir, validated to stay within directory
  • Absolute paths: Must be within keys_dir (rejected if outside)
  • Traversal patterns: Paths containing .. or ..\\ are rejected
  • File type validation: Only regular files accepted (directories and symlinks rejected)
  • Security logging: Path traversal and file validation failures are logged

Examples:

#  Valid key paths
key_path: "id_ed25519"                    # Relative, within keys_dir
key_path: "/app/keys/id_ed25519"         # Absolute, within keys_dir

#  Invalid (rejected)
key_path: "../outside_key"                # Path traversal
key_path: "/etc/passwd"                  # Outside keys_dir
key_path: "../../root/.ssh/id_rsa"       # Traversal attempt
key_path: "subdirectory"                 # Points to directory, not file
key_path: "symlink_key"                  # Points to symlink, not regular file

File Type Requirements:

  • Regular files within keys_dir (or non-existent files - validated when used)
  • Directories
  • Symbolic links (rejected for security)

Effect: Prevents accessing SSH keys from unauthorized locations and blocks access to directories or symlinks that could pose security risks.

Security Best Practices

Key Security

  1. Use Ed25519 keys for new deployments
  2. Set private key permissions to 0400
  3. Use passphrases for private keys
  4. Rotate keys quarterly
  5. Use separate keys for different environments

Secret Security

  1. Never hardcode secrets in YAML files
  2. Use Docker secrets for production
  3. Use environment variables for development
  4. Rotate secrets regularly
  5. Use strong, unique secrets

Access Control

  1. Use least-privilege usernames
  2. Separate service accounts from admin accounts
  3. Use different credentials per environment
  4. Monitor credential usage
  5. Revoke unused credentials

Validation and Testing

Configuration Validation

# Validate credentials.yml syntax
python -c "import yaml; yaml.safe_load(open('config/credentials.yml'))"

# Validate credential references
python -c "
from mcp_ssh.config import Config
config = Config('config/')
print('Credential validation:', config.validate_credentials())
"

Authentication Testing

# Test SSH key authentication
ssh -i keys/id_ed25519 [email protected] "echo 'Key auth successful'"

# Test password authentication
ssh [email protected] "echo 'Password auth successful'"

# Test via mcp-ssh-orchestrator
ssh_ping  # Health check
ssh_describe_host --alias "web1"  # Get host details

Secret Resolution Testing

# Test secret resolution
python -c "
from mcp_ssh.config import Config
config = Config('config/')
cred = config.get_credentials('prod_admin')
print('Key path:', cred.key_path)
print('Username:', cred.username)
"

Troubleshooting

Common Issues

  1. Invalid YAML syntax

    # Check syntax
    python -c "import yaml; yaml.safe_load(open('config/credentials.yml'))"
  2. Missing secret files

    # Check secret files
    ls -la ~/mcp-ssh/secrets/
  3. Wrong key permissions

    # Fix key permissions
    chmod 0400 ~/mcp-ssh/keys/*
  4. SSH connection failures

    # Test SSH connection
    ssh -v -i keys/id_ed25519 [email protected]

Authentication Issues

  1. Key authentication failed

    # Check key format
    ssh-keygen -l -f keys/id_ed25519
    
    # Test key manually
    ssh -i keys/id_ed25519 [email protected]
  2. Password authentication failed

    # Check password secret
    echo $MCP_SSH_SECRET_ADMIN_PASSWORD
    
    # Test password manually
    ssh [email protected]
  3. Passphrase authentication failed

    # Check passphrase secret
    echo $MCP_SSH_SECRET_KEY_PASSPHRASE
    
    # Test passphrase manually
    ssh -i keys/id_ed25519 [email protected]

Next Steps

⚠️ **GitHub.com Fallback** ⚠️