SSH-Infrastructure / KEYS_AND_ACCESS.md
1 contributor
327 lines | 10.034kb

SSH Keys and Access Guide

Centralized reference for SSH key management and access paths.

Key Storage

Local Keys (on your machine)

~/.ssh/keys/
  id_ed25519           → ~/.ssh/id_ed25519 (modern, preferred)
  id_ed25519_2026-05
  id_ed25519_2026-05.pub
  id_rsa               (RSA legacy, 2048-bit)
  id_rsa_old           (deprecated, from 2015)
  id_rsa_old.pub
  is-jumper_ed25519    (specific to is-jumper entry point)
  is-jumper_ed25519.pub

Physical Hardware Key (on is-jumper)

⚠️ IMPORTANT: The primary authentication for the company network (J1/J2) is a physical hardware security key (card/smartcard) mounted only on is-jumper (192.168.2.100).

  • Location: is-jumper (192.168.2.100) only
  • Type: Hardware security module / smartcard (RSA 4096-bit)
  • Exposed via: GPG agent at /run/user/0/gnupg/S.gpg-agent.ssh (on is-jumper)
  • Access method: SSH agent forwarding through is-jumper
  • Users served: Multiple users from the 192.168.2.0/24 network

You cannot access J1/J2 directly from your local machine. The connection must go through is-jumper, which holds the only valid physical key for the company network.

Key Usage Matrix

Key Location Used By Purpose Auth Method
Physical card (RSA 4096) Hardware on is-jumper only J1, J2, company network Primary auth for nextgen network Hardware smartcard via GPG agent
is-jumper ED25519 ~/.ssh/keys/is-jumper_ed25519 Your machine → is-jumper Access the entry point pubkey + IdentitiesOnly
Modern ED25519 ~/.ssh/id_ed25519 Local lab, final hosts Secondary/fallback auth pubkey
Legacy RSA ~/.ssh/id_rsa Legacy systems Transitional pubkey
Deprecated RSA ~/.ssh/keys/id_rsa_old Very old hosts Final fallback pubkey

Key insight: You authenticate TO is-jumper with your local ED25519 key, but once on is-jumper, the system uses the physical hardware key to authenticate to the company network (J1/J2).

Access Paths and Routing

1. Local Lab Setup (192.168.2.0/24)

Direct access — no jump needed:

is-jumper (192.168.2.100)
├─ User: root
├─ Port: 22
├─ Key: ~/.ssh/keys/is-jumper_ed25519
├─ IdentitiesOnly: yes
└─ Routing: SSH_ROUTE=local

Local dev machines (192.168.2.110-122)
├─ User: bogdan
├─ Port: 22
└─ Key: ~/.ssh/id_ed25519

Access to is-jumper:

# Direct connection (SSH will use is-jumper_ed25519 via IdentitiesOnly)
ssh is-jumper

# Verify key in use:
ssh -G is-jumper | grep identityfile

2. Production: Via is-jumper → J1/J2 → Final Hosts

Access chain for internal company network (10.253.51.0/24):

Your machine (local)
  ↓ (ED25519 pubkey)
is-jumper (192.168.2.100) [VPN client + physical card key guardian]
  ↓ (SSH agent forwarding + physical card RSA 4096)
J1 (10.253.51.50:25904) or J2 (10.253.51.52:25904) [jump hosts]
  ↓ (SSH forward)
Final host (voip, porta, radius, etc.)

Critical: You cannot connect to J1/J2 directly from your local machine because the physical hardware key is only on is-jumper. All company network access must go through is-jumper first.

Step 1: is-jumper (entry point)

Authenticate to is-jumper with your local ED25519 key:

  • User: root
  • Host: 192.168.2.100
  • Port: 22
  • Key: ~/.ssh/keys/is-jumper_ed25519
  • IdentitiesOnly: yes (forces use of specified key only)
ssh is-jumper

Once logged in, is-jumper has access to the physical card key via GPG agent at /run/user/0/gnupg/S.gpg-agent.ssh.

Step 2: J1/J2 (jump hosts) — via is-jumper's physical key

SSH agent forwarding connects your SSH client to is-jumper's physical card:

  • Routing: ProxyJump is-jumper
  • Authentication on J1/J2: Physical card (RSA 4096) via SSH_AUTH_SOCK=/run/user/0/gnupg/S.gpg-agent.ssh
  • User on J1/J2: bogdan.timofte (company network default)
  • Port: 25904 (VPN jump port)
ssh j1        # Routes: local → is-jumper → J1 (using is-jumper's physical key)
ssh j2        # Routes: local → is-jumper → J2 (using is-jumper's physical key)

The SSH config handles this automatically — you don't need to manually set up agent forwarding. The wrapper or ProxyJump chain uses is-jumper's physical key to authenticate.

Verify J1 config:

ssh -G j1 | grep -E '^(hostname|port|user|proxyjump)'

Step 3: Final Hosts

From J1/J2 to actual hosts (configured in J1's/J2's local SSH config):

  • User: varies by host group (bogdan, bogdan.timofte, root, etc.)
  • Port: 22 (standard) or 24 (jump hosts default) or custom
  • Key: ~/.ssh/id_ed25519
  • ProxyJump: j1 or j2
# Example: PortaOne database server
ssh porta-db        # → is-jumper → J1 → 193.16.148.11

# Example: VoIP PBX
ssh voip-prov       # → is-jumper → J1 → 10.253.51.139

# Example: Radius database
ssh falticeni.radius-db  # → is-jumper → J1 → falticeni.radius-db:24

3. Emergency Public Routes (j1.next-gen.ro / j2.next-gen.ro)

If internal VPN is down, use public DNS names:

# Standard (internal VPN)
ssh j1              # 10.253.51.50:25904

# Emergency (public DNS)
ssh j1.next-gen.ro  # j1.next-gen.ro:25904
ssh j2.next-gen.ro  # j2.next-gen.ro:25904

Both routes go through is-jumper first (no direct connection).

Key Migration Status

Check which hosts have modern keys

# Test all local lab hosts
for h in is-baobab is-ebony is-tapia is-jumper is-mazeri is-toltec is-andrafiabe is-anjohibe is-nasturel is-mat; do
  timeout 2 ssh -o BatchMode=yes "$h" true 2>/dev/null && echo "$h: ✓" || echo "$h: ⚠"
done

Migrate a host to modern ED25519 key

# Automatic migration (uses legacy key to install modern key)
tools/migrate-modern-key.sh is-baobab

# Or migrate all
tools/migrate-modern-key.sh

See docs/KEY_MIGRATION.md for manual procedures.

SSH Config Generation

The ~/.ssh/config is auto-generated from inventory — do not edit manually.

# Regenerate after inventory changes
python3 tools/generate-configs.py

# Deploy to ~/.ssh/config
cp generated/client.conf ~/.ssh/config
# or use the deploy script:
tools/deploy-local.sh

Config files generated:

File Target Purpose
generated/client.conf local client (~/.ssh/config) local access config
generated/is-jumper.conf is-jumper (via deploy) is-jumper alias config
generated/j1.conf J1 (server-side) final host access on J1
generated/j2.conf J2 (server-side) final host access on J2

Troubleshooting

"Permission denied (publickey)" on J1/J2

⚠️ First check: Do you have access to is-jumper?

ssh is-jumper "echo access ok"

If this fails, the issue is your local ED25519 key or is-jumper authentication. Fix that first.

If is-jumper works but J1 fails:

The issue is likely the physical card key is not properly exposed or SSH agent forwarding isn't working.

Diagnosis:

# 1. Check if you can reach is-jumper
ssh is-jumper

# 2. From is-jumper, verify the physical card is available
ssh is-jumper "ls -la /run/user/0/gnupg/S.gpg-agent.ssh"
# Should exist and be readable

# 3. Check J1 connectivity with verbose output
ssh -vvv j1 2>&1 | grep -E "ProxyJump|Offering|Authentications|Trying"

Solutions:

  1. is-jumper's SSH agent not running: ```bash ssh is-jumper "ps aux | grep gpg-agent"

    If not running, restart it or contact the system administrator

  2. SSH config not set up for agent forwarding:

    • Verify ProxyJump is configured: ssh -G j1 | grep proxyjump
    • Verify ForwardAgent yes is set (check generated config)
  3. Physical card key not accessible:

    • This is a system issue on is-jumper — contact the key administrator
    • The card may need to be re-mounted or re-authenticated
  4. SSH on your machine not supporting agent forwarding: ```bash

    Ensure SSH_AUTH_SOCK is set when you SSH to is-jumper

    ssh -A is-jumper ```

"Permission denied (publickey)" on is-jumper

The issue: Your local ED25519 key isn't authorized on is-jumper.

Verify your key is present:

ls -la ~/.ssh/keys/is-jumper_ed25519
cat ~/.ssh/keys/is-jumper_ed25519.pub

Check that is-jumper has your key:

ssh is-jumper "cat ~/.ssh/authorized_keys | grep -i ed25519"

If your key isn't there, it needs to be added by the is-jumper administrator.

"No route to host" (10.253.51.x)

Cause: Trying to reach J1/J2 directly without going through is-jumper.

Check ProxyJump:

ssh -G j1 | grep proxyjump
# Must show: proxyjump is-jumper

If not, regenerate config: python3 tools/generate-configs.py && cp generated/client.conf ~/.ssh/config

SSH hangs or timeouts

For jump hosts (reduce to 5-10 seconds):

ssh -o ConnectTimeout=5 is-jumper
ssh -o ConnectTimeout=5 j1

Check network:

# is-jumper reachable?
ping 192.168.2.100

# If behind firewall, may need port 22 open to is-jumper

Maintenance Checklist

  • [ ] Keep both id_ed25519 and id_rsa_old until all hosts migrated
  • [ ] After adding new hosts: run tools/migrate-modern-key.sh <host>
  • [ ] After inventory changes: regenerate with python3 tools/generate-configs.py
  • [ ] Periodically check: ssh -G <alias> to verify config without connecting
  • [ ] Never manually edit ~/.ssh/config — it's auto-generated
  • [ ] Keep keys in ~/.ssh/keys/ with mode 600

References