Skip to content

Security: The-Ladder-of-Rrogress/doppler-ssh-skill

Security

docs/security.md

Doppler SSH Skill Security Guide

Security best practices, hardening guide, and security recommendations.

Table of Contents


Security Overview

Security Features

Feature Description Protection Level
Mount Mode Secrets in memory, not env vars High
Service Tokens Read-only, scoped access High
Memory Clearing Secure overwrite on exit Medium
Audit Logging Access tracking Medium
Host Validation Known hosts verification Medium
Name Validation RCE prevention High

Threat Model

  • Credential Theft: Use Service Tokens + Mount Mode
  • Memory Dumping: Enable memory clearing
  • Log Exposure: Enable log redaction
  • RCE: Validate secret names
  • Man-in-Middle: Use host key verification

Token Security

Use Service Tokens

Always use Service Tokens (dp.st.*) in production:

# Good: Service Token
skill = DopplerSSHSkill(
    project="my-project",
    service_token="dp.st.xxx"  # Read-only, scoped
)

# Avoid: Personal Token
skill = DopplerSSHSkill(
    project="my-project",
    service_token="dp.pt.xxx"  # Full account access
)
# SecurityWarning will be issued

Token Configuration

# Via environment variable (recommended)
# In your shell profile:
export DOPPLER_TOKEN="dp.st.xxx"

# Via file (with proper permissions)
# /etc/doppler-ssh/token
chmod 600 /etc/doppler-ssh/token

# Note: Never hardcode tokens in source code

Token Scopes

Create scoped Service Tokens in Doppler Dashboard:

{
  "name": "ssh-mcp-server",
  "slug": "ssh-mcp-prod",
  "scopes": [
    "secrets.read",
    "configs.read",
    "projects.read"
  ],
  "config_access": ["ssh-mcp-server:prod"]
}

Secret Management

Secret Naming Convention

Secrets must follow this naming pattern:

SSHMCP_HOST_{HOSTNAME}_{SECRET_TYPE}

Valid secret types:

Type Description
PASSWORD SSH password
KEY_PASSPHRASE Private key passphrase
SUDO_PASSWORD Sudo password
SSH_KEY Private key content

Creating Secrets in Doppler

# Create secrets via Doppler CLI
doppler secrets set SSHMCP_HOST_PROD_DB_PASSWORD --value="secret123"
doppler secrets set SSHMCP_HOST_PROD_DB_KEY_PASSPHRASE --value="keypass"
doppler secrets set SSHMCP_HOST_PROD_DB_SUDO_PASSWORD --value="sudopass"
doppler secrets set SSHMCP_HOST_PROD_DB_SSH_KEY --value="$(cat id_rsa)"

Secret Validation

The SecurityManager validates secret names:

from doppler_ssh.core.security import SecurityManager

security = SecurityManager()

# Validates against dangerous environment variables
security.validate_secret_name("SSHMCP_HOST_PROD_PASSWORD")  # OK
security.validate_secret_name("LD_PRELOAD_ATTACK")  # SecurityError!

Dangerous variables include:

  • LD_PRELOAD, LD_LIBRARY_PATH
  • PROMPT_COMMAND, BASH_ENV
  • PYTHONSTARTUP, PYTHONPATH
  • NODE_OPTIONS, _JAVA_OPTIONS

Mount Mode Security

Why Mount Mode?

Aspect Mount Mode ENV Mode
Secrets in memory Yes No (env vars)
Process isolation Yes No
Max reads Limited (1) Unlimited
Security Higher Lower

Using Mount Mode

from doppler_ssh.core.config import DopplerMode

skill = DopplerSSHSkill(
    project="my-project",
    mode=DopplerMode.MOUNT,
    mount_path="/run/secrets/ssh",
    mount_max_reads=1  # Read only once
)

Mount Configuration

# doppler run with mount
doppler run \
    --project ssh-mcp-server \
    --config prod \
    --mount /run/secrets/ssh \
    --mount-max-reads 1 \
    -- \
    python app.py

Memory Security

Secure Clearing

Always clear secrets when done:

# Use secure_clear method
skill.secure_clear(host_config)

# Or use context manager
with DopplerSSHSkill(project="my-project") as skill:
    host_config = skill.load_host_config("prod-db")
    # Automatic cleanup

Manual Memory Clearing

from doppler_ssh.core.security import SecurityManager

security = SecurityManager()

# Clear individual string
security.secure_clear_string("secret")

# Clear dictionary
security.secure_clear_dict({"password": "secret", "key": "key"})

# Clear list
security.secure_clear_list(["secret1", "secret2"])

Cache Security

# Disable caching for sensitive data
skill = DopplerSSHSkill(
    project="my-project",
    cache_ttl=0  # No caching
)

# Or clear cache manually
skill.clear_cache()

Network Security

TLS Configuration

# Doppler uses HTTPS by default
api_host = "https://api.doppler.com"

# Verify certificates
import ssl
context = ssl.create_default_context()

Proxy Support

# Use corporate proxy
import os
os.environ["HTTPS_PROXY"] = "http://proxy.example.com:8080"
os.environ["NO_PROXY"] = "localhost,127.0.0.1"

Firewall Rules

# Allow only Doppler API
iptables -A OUTPUT -p tcp -d api.doppler.com --dport 443 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -j DROP

Access Control

File Permissions

# Configuration file
chmod 600 /etc/doppler-ssh/config.yaml

# SSH keys
chmod 600 ~/.ssh/id_*

# Logs (read-only for app)
chmod 640 /var/log/doppler-ssh.log

Service User

Run as dedicated user:

# Create service user
useradd -r -s /bin/false doppler-ssh

# Set ownership
chown -R doppler-ssh:doppler-ssh /opt/doppler-ssh

# No login capability
usermod -L doppler-ssh

systemd Hardening

[Service]
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/doppler-ssh /run/secrets

# Deny access to sensitive paths
RuntimeDirectory=!doppler-ssh

Audit Logging

Enable Access Logging

import logging

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger("doppler-ssh")
logger.setLevel(logging.INFO)

# Log to file
handler = logging.FileHandler("/var/log/doppler-ssh-audit.log")
handler.setFormatter(logging.Formatter("%(asctime)s %(message)s"))
logger.addHandler(handler)

# Log access
security.log_access("prod-db", "password")

Log Format

2024-01-15 10:30:00 Secret accessed: host=prod-db, type=password
2024-01-15 10:30:01 SSH connected: host=prod-db, user=admin
2024-01-15 10:30:05 Secret cleared: host=prod-db

Redact Sensitive Data

# Automatic redaction in logs
security = SecurityManager()

# Mask secrets
masked = security.mask_secrets_in_logs(
    "password is secret123",
    {"password": "secret123"}
)
# Result: "password is se***23"

Hardening Guide

OS Hardening

# Disable unnecessary services
systemctl disable bluetooth
systemctl disable avahi-daemon

# Enable firewall
ufw enable
ufw default deny incoming
ufw allow out to api.doppler.com port 443

Python Security

# Use environment variable isolation
os.environ.pop("PYTHONPATH", None)
os.environ.pop("PYTHONSTARTUP", None)

# Disable dangerous features
importwarnings.filterwarnings("error", category=BytesWarning)

Application Hardening

# Validate all inputs
from doppler_ssh.core.security import SecurityManager

security = SecurityManager()

# Validate secret names
def get_secret(name: str) -> str:
    security.validate_secret_name(name)  # Raises if dangerous
    return client.get_secret(name)

# Limit retries
MAX_RETRIES = 3

Container Hardening

# Non-root user
RUN useradd -r -u 1000 appuser

# Drop capabilities
RUN setcap cap_net_bind_service=+ep /usr/bin/python3

# Read-only filesystem
RUN chown -R appuser /app

USER appuser

Security Checklist

Pre-Deployment

  • Use Service Token (dp.st.*)
  • Enable Mount Mode
  • Set mount_max_reads = 1
  • Enable cache_ttl properly
  • Validate secret names

Configuration

  • File permissions (600)
  • Service user created
  • No hardcoded secrets
  • Firewall configured
  • TLS verification enabled

Runtime

  • Audit logging enabled
  • Secrets cleared after use
  • Log redaction enabled
  • Health checks in place
  • Backups tested

Monitoring

  • Access logs monitored
  • Error alerts configured
  • Rate limiting enabled
  • Anomaly detection active

Incident Response

Suspected Compromise

  1. Immediate Actions:
# Disable token
doppler tokens revoke dp.st.xxx

# Rotate all secrets
doppler secrets set SSHMCP_HOST_PROD_DB_PASSWORD --value="newpassword"

# Kill running processes
pkill -f doppler-ssh

# Preserve logs
cp /var/log/doppler-ssh.log /backup/
  1. Investigation:
# Check access logs
grep "2024-01-15" /var/log/doppler-ssh.log

# Check for anomalies
journalctl -u doppler-ssh --since "2024-01-15 10:00"

# Review failed auth attempts
grep "Authentication failed" /var/log/auth.log
  1. Recovery:
# Generate new token
doppler tokens create --name "replacement-token"

# Deploy new configuration
cp /backup/config.yaml /opt/doppler-ssh/config.yaml

# Restart service
systemctl restart doppler-ssh

Compliance

Security Standards

Standard Implementation
SOC 2 Audit logs + access control
PCI DSS Tokenization + encryption
HIPAA Audit logging + secure clearing
ISO 27001 Full hardening guide

Additional Measures

  • Enable SELinux/AppArmor
  • Use hardware security keys
  • Implement MFA for Doppler access
  • Regular security audits
  • Penetration testing

Reporting Security Issues

If you find a security vulnerability:

  1. Do NOT create a public issue

  2. Email security@example.com

  3. Include:

    • Description of the issue
    • Steps to reproduce
    • Potential impact
    • Suggested fix (optional)
  4. Expected response:

    • Acknowledge within 24 hours
    • Fix timeline within 30 days
    • Public disclosure after fix

There aren't any published security advisories