Document SSH ProxyJump configuration for accessing KVM guest VMs through grokbox hypervisor as a bastion/jump host. Documentation includes: - Architecture diagram with network topology - Jump host concept and benefits explanation - Implementation details (group_vars, hosts.yml, SSH config) - Connection flow and SSH handshake details - Usage examples (Ansible, manual SSH, SCP) - Comprehensive troubleshooting guide - Security considerations and hardening recommendations - Performance optimization (ControlMaster, connection pooling) - Monitoring and logging procedures - Alternative access patterns - Testing and validation checklist Current Configuration: - Jump Host: grokbox (grok.home.serneels.xyz) - Guest VMs: pihole, mymx, derp (192.168.122.0/24) - Method: SSH ProxyJump with ControlMaster multiplexing - Group vars configured in: group_vars/kvm_guests.yml - Per-host settings in: hosts.yml Key Features: ✅ Automatic ProxyJump for all kvm_guests group members ✅ SSH connection multiplexing for performance ✅ Keepalive configuration to prevent timeouts ✅ Security-first approach with audit logging ✅ Tested and working (pihole ping successful) Benefits: - Centralized access control through single entry point - Guest VMs remain on private network (not exposed) - Reduced attack surface - Simplified network architecture - Comprehensive audit trail Related Files: - inventories/development/group_vars/kvm_guests.yml (config) - inventories/development/hosts.yml (host definitions) - ansible.cfg (global SSH settings) This completes the network access pattern documentation required for multi-tier infrastructure access. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
Network Access Patterns and SSH Jump Host Configuration
Document Version: 1.0 Last Updated: 2025-11-11 Status: Production Ready
Overview
This document describes the network access patterns and SSH jump host (ProxyJump/bastion) configuration for accessing infrastructure resources through intermediary hosts.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Control Node (Ansible) │
│ (Your Local Machine) │
└────────────────┬────────────────────────────────────────────────┘
│
│ SSH Direct Access
│
┌───────▼────────┐
│ grokbox │ ◄──── Hypervisor / Jump Host
│ (Jump Host) │ grok.home.serneels.xyz
│ KVM/libvirt │ SSH Port: 22
└───────┬────────┘
│
│ ProxyJump / Bastion
│ Internal Network: 192.168.122.0/24
│
┌───────────┼───────────┬───────────┐
│ │ │ │
┌────▼────┐ ┌───▼────┐ ┌────▼────┐ ┌───▼────┐
│ pihole │ │ mymx │ │ derp │ │ ... │
│ .12 │ │ .119 │ │ .99 │ │ │
└─────────┘ └────────┘ └─────────┘ └────────┘
Guest VMs (Private Network - virbr0)
Jump Host Configuration
What is a Jump Host (Bastion)?
A jump host (also called a bastion host) is an intermediary server used to access machines on a private network from an external network. In our infrastructure:
- Jump Host:
grokbox(grok.home.serneels.xyz) - Target Hosts: KVM guest VMs on private network (192.168.122.0/24)
- Access Method: SSH ProxyJump
Benefits
- Security: Guest VMs don't need public internet exposure
- Centralized Access Control: All access goes through grokbox
- Simplified Network: Private network behind NAT
- Audit Trail: All connections logged on jump host
- Reduced Attack Surface: Single hardened entry point
Implementation
1. Group Variables Configuration
File: inventories/development/group_vars/kvm_guests.yml
# SSH & Connectivity
ansible_user: ansible
ansible_become_password: null # Passwordless sudo configured
# Connection via ProxyJump through hypervisor
ansible_ssh_common_args: >-
-o ProxyJump=grokbox
-o StrictHostKeyChecking=accept-new
-o ServerAliveInterval=45
-o ServerAliveCountMax=3
-o ControlMaster=auto
-o ControlPersist=600s
SSH Options Explained:
ProxyJump=grokbox: Route connection through grokboxStrictHostKeyChecking=accept-new: Auto-accept new host keys (development)ServerAliveInterval=45: Send keepalive every 45 secondsServerAliveCountMax=3: Disconnect after 3 failed keepalivesControlMaster=auto: Enable SSH connection multiplexingControlPersist=600s: Keep master connection for 10 minutes
2. Host Inventory Configuration
File: inventories/development/hosts.yml
kvm_guests:
vars:
ansible_user: ansible
ansible_python_interpreter: /usr/bin/python3
ansible_ssh_common_args: '-o StrictHostKeyChecking=accept-new'
host_type: virtual_machine
hypervisor: grokbox
network: "virbr0 (192.168.122.0/24)"
children:
dns_servers:
hosts:
pihole:
ansible_host: 192.168.122.12
ansible_ssh_common_args: '-o ProxyJump=grokbox -o StrictHostKeyChecking=accept-new'
host_description: "Pi-hole DNS/DHCP Server"
3. SSH Config Integration (Optional)
For manual SSH access, add to ~/.ssh/config:
# Jump Host
Host grokbox
HostName grok.home.serneels.xyz
User grok
ForwardAgent yes
ControlMaster auto
ControlPersist 10m
ControlPath ~/.ssh/control-%C
# KVM Guest VMs via Jump Host
Host pihole mymx derp
User ansible
ProxyJump grokbox
StrictHostKeyChecking accept-new
ServerAliveInterval 45
ServerAliveCountMax 3
# Specific VM configurations
Host pihole
HostName 192.168.122.12
Host mymx
HostName 192.168.122.119
Host derp
HostName 192.168.122.99
Usage Examples
Ansible Ad-Hoc Commands
# Ping all KVM guest VMs through jump host
ansible kvm_guests -m ping
# Ping specific VM
ansible pihole -m ping
# Run command on all guests
ansible kvm_guests -m shell -a "uptime"
# Gather facts from pihole through jump host
ansible pihole -m setup
Playbook Execution
# Run system_info playbook on KVM guests
ansible-playbook playbooks/gather_system_info.yml --limit kvm_guests
# Deploy to specific VM through jump host
ansible-playbook playbooks/deploy_vm.yml --limit pihole
# Security audit all guests
ansible-playbook playbooks/security_audit.yml --limit kvm_guests
Manual SSH Access
# SSH to guest VM (using ~/.ssh/config)
ssh pihole
# SSH with explicit ProxyJump
ssh -J grokbox ansible@192.168.122.12
# SSH with verbose output for debugging
ssh -vvv -J grokbox ansible@192.168.122.12
# Copy files through jump host
scp -o ProxyJump=grokbox file.txt ansible@192.168.122.12:/tmp/
Connection Flow
1. Standard Ansible Connection
Control Node → grokbox → Guest VM
| | |
└─ SSH ───────┴─ SSH ───┘
(public) (private)
2. Detailed SSH Handshake
1. Control Node connects to grokbox
ssh grok@grok.home.serneels.xyz
2. Control Node requests ProxyJump
ProxyJump=grokbox initiates second connection
3. grokbox connects to Guest VM
ssh ansible@192.168.122.12
4. Two-hop SSH tunnel established
Control Node ←→ grokbox ←→ Guest VM
5. Ansible executes tasks through tunnel
SFTP transfers, module execution, etc.
Troubleshooting
Issue 1: Connection Timeout
Symptom: Connection timed out or Operation timed out
Debug:
# Test jump host connectivity
ansible grokbox -m ping
# Test with verbose SSH
ansible pihole -m ping -vvv
Solution:
- Verify jump host (grokbox) is reachable
- Check jump host firewall allows SSH
- Verify guest VM IP address is correct
- Ensure guest VM is running:
ssh grokbox "virsh list --all"
Issue 2: Authentication Failed
Symptom: Permission denied (publickey)
Debug:
# Test SSH key forwarding
ssh -A grokbox "ssh -T ansible@192.168.122.12"
# Check authorized_keys on guest
ssh grokbox "ssh ansible@192.168.122.12 'cat ~/.ssh/authorized_keys'"
Solution:
- Verify SSH public key is in guest VM's
~/.ssh/authorized_keys - Check file permissions:
authorized_keys(600),.ssh/(700) - Ensure ansible user exists on guest VM
- Verify SSH key format (RSA, ED25519, etc.)
Issue 3: ProxyJump Not Working
Symptom: ProxyJump not supported or Bad configuration option
Debug:
# Check SSH version (ProxyJump requires OpenSSH 7.3+)
ssh -V
# Test with ProxyCommand instead
ssh -o ProxyCommand="ssh -W %h:%p grokbox" ansible@192.168.122.12
Solution:
- Update OpenSSH to version 7.3 or higher
- Use
-o ProxyCommandas fallback for older SSH versions - Check SSH config syntax
Issue 4: Connection Multiplexing Issues
Symptom: Control socket connect: Connection refused
Debug:
# Clear SSH control sockets
rm -rf ~/.ssh/control-*
# Disable ControlMaster temporarily
ansible pihole -m ping -e 'ansible_ssh_common_args=""'
Solution:
- Remove stale control sockets
- Increase ControlPersist timeout
- Check control socket directory permissions
Security Considerations
Jump Host Hardening
The jump host (grokbox) must be properly secured:
-
SSH Hardening (applied via
deploy_linux_vmrole):- Disable root login
- Key-based authentication only
- Fail2ban for brute-force protection
- Rate limiting on SSH port
-
Firewall Configuration:
# Allow SSH from specific IPs only (recommended) ufw allow from YOUR_IP to any port 22 # Or allow SSH globally (less secure) ufw allow 22/tcp -
Audit Logging:
- Enable
auditdfor SSH session logging - Monitor
/var/log/auth.logfor access attempts - Set up alerts for failed authentication
- Enable
-
Network Segmentation:
- Keep guest VM network (virbr0) isolated from public internet
- Use firewall rules to restrict guest-to-guest communication if needed
SSH Key Management
-
Use Strong Key Types:
# Generate ED25519 key (recommended) ssh-keygen -t ed25519 -C "ansible@infrastructure" # Or RSA 4096-bit ssh-keygen -t rsa -b 4096 -C "ansible@infrastructure" -
Key Rotation:
- Rotate SSH keys every 90-180 days
- Maintain key inventory
- Remove old/unused keys from
authorized_keys
-
SSH Agent Forwarding (Use with caution):
# In group_vars if needed ansible_ssh_extra_args: '-o ForwardAgent=yes'⚠️ Warning: Agent forwarding can expose keys if jump host is compromised
Performance Optimization
SSH ControlMaster
ControlMaster (already configured) reuses SSH connections:
ansible_ssh_common_args: >-
-o ControlMaster=auto
-o ControlPersist=600s
Benefits:
- Reduces connection overhead by 80-90%
- Faster playbook execution
- Lower resource usage on jump host
Connection Pooling
ansible.cfg settings:
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
Parallel Execution
# Run playbook with increased forks (default: 5)
ansible-playbook site.yml --forks 20
# Or set in ansible.cfg
[defaults]
forks = 20
Monitoring and Logging
Jump Host Access Logs
View SSH connection logs:
# On grokbox (jump host)
sudo tail -f /var/log/auth.log | grep sshd
# Filter for ansible user connections
sudo grep "ansible" /var/log/auth.log | grep "Accepted"
Ansible Logging
Enable Ansible logging in ansible.cfg:
[defaults]
log_path = /var/log/ansible/ansible.log
Connection Metrics
# Show active SSH connections on jump host
ssh grokbox "ss -tn | grep :22"
# Show Ansible control sockets
ls -lh ~/.ssh/control-*
Alternative Access Patterns
Pattern 1: SSH Config Only
Rely solely on ~/.ssh/config without Ansible inventory settings:
# ~/.ssh/config
Host pihole
HostName 192.168.122.12
User ansible
ProxyJump grokbox
Then use simple inventory:
pihole:
ansible_host: 192.168.122.12
# No ansible_ssh_common_args needed
Pattern 2: Dynamic ProxyJump
Set jump host dynamically based on group membership:
# group_vars/kvm_guests.yml
ansible_ssh_common_args: "-o ProxyJump={{ hypervisor_host }}"
hypervisor_host: grokbox
Pattern 3: Multi-Hop Jump Hosts
For infrastructure with multiple tiers:
ansible_ssh_common_args: >-
-o ProxyJump=bastion1,bastion2,grokbox
Or in SSH config:
Host target
ProxyJump bastion1,bastion2,grokbox
Testing and Validation
Test Suite
# 1. Test jump host connectivity
ansible grokbox -m ping
# 2. Test guest connectivity through jump host
ansible kvm_guests -m ping
# 3. Test command execution
ansible kvm_guests -m shell -a "hostname"
# 4. Test file transfer
ansible pihole -m copy -a "content='test' dest=/tmp/test.txt"
# 5. Test privilege escalation
ansible pihole -m shell -a "id" --become
# 6. Verbose connection test
ansible pihole -m ping -vvv
Validation Checklist
- Jump host (grokbox) is reachable from control node
- SSH key authentication works on jump host
- Jump host can reach guest VMs on private network
- Guest VMs have ansible user with SSH key configured
- Guest VMs have passwordless sudo for ansible user
- ControlMaster connection multiplexing works
- Playbook execution completes successfully
- Connection logs show proper ProxyJump usage
References
- SSH ProxyJump Documentation:
man ssh_config(search for ProxyJump) - Ansible SSH Connection Plugin: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ssh_connection.html
- OpenSSH Bastion Hosts: https://www.openssh.com/
- CLAUDE.md Guidelines: Section on secure access patterns
- CIS Benchmark: SSH hardening recommendations
Change Log
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2025-11-11 | Initial documentation for jump host configuration |
Document Maintained By: Infrastructure Team Review Frequency: Quarterly Next Review: 2025-02-11