Project 22: “The SSH Tunnel Agent” — Networking
| Attribute | Value |
|---|---|
| File | KIRO_CLI_LEARNING_PROJECTS.md |
| Main Programming Language | SSH Config |
| Coolness Level | Level 4: Hardcore Tech Flex |
| Difficulty | Level 3: Advanced |
| Knowledge Area | Networking |
What you’ll build: Run Kiro locally but execute commands remotely via ssh.
Why it teaches Hybrid Workflows: Brain local, execution remote.
Success criteria:
- A deploy task runs end-to-end on a remote host.
Real World Outcome
You’ll have a hybrid workflow where Kiro runs on your local machine (with GUI, editor, browser) but executes commands on remote servers via SSH. This combines local intelligence with remote execution power:
Local Kiro, remote execution:
# On your laptop
$ cat ~/.kiro/config.json
{
"remote_execution": {
"enabled": true,
"host": "prod-server.company.com",
"user": "deploy",
"tools": ["Bash", "Read", "Write", "Edit"]
}
}
$ kiro "deploy the latest version to production"
[Kiro running locally] Analyzing deployment strategy...
[SSH Tunnel] Connecting to prod-server.company.com...
[SSH Tunnel] Forwarding Bash tool execution...
[Remote Bash] ssh deploy@prod-server.company.com
$ cd /var/www/app
$ git pull origin main
$ npm install --production
$ pm2 restart app
✓ Deployment complete
[Kiro running locally] Deployment successful. Health check passing.
Transparent remote file access:
$ kiro "show me the nginx config on the production server"
[Kiro] Reading remote file via SSH...
[Remote Read Tool]
ssh deploy@prod-server.company.com 'cat /etc/nginx/nginx.conf'
[Kiro displays config and suggests improvements]
$ kiro "update the worker_processes to 4"
[Remote Edit Tool]
ssh deploy@prod-server.company.com 'cat > /tmp/nginx.conf.new << EOF
worker_processes 4;
...
EOF && sudo mv /tmp/nginx.conf.new /etc/nginx/nginx.conf'
[Remote Bash]
ssh deploy@prod-server.company.com 'sudo nginx -s reload'
✓ Configuration updated and reloaded
SSH config for seamless tunneling:
$ cat ~/.ssh/config
Host prod-server
HostName prod-server.company.com
User deploy
Port 22
IdentityFile ~/.ssh/deploy_key
ForwardAgent yes
ControlMaster auto
ControlPath ~/.ssh/control-%r@%h:%p
ControlPersist 10m
Host staging-server
HostName staging.company.com
User deploy
ProxyJump bastion.company.com
LocalForward 5432 localhost:5432
This setup enables “brain local, muscle remote” workflows where you get local responsiveness with remote execution power.
The Core Question You’re Answering
“How do I leverage my local development environment while executing commands on remote production servers?”
Before you start coding, consider: Installing Kiro on every server is impractical and risky. Running Kiro locally but SSHing to execute each command is slow and error-prone. SSH tunneling with ControlMaster lets you maintain a single persistent connection, forward tool execution transparently, and keep your local editor/browser while working on remote systems. This project teaches you to build hybrid architectures that combine local and remote strengths.
Concepts You Must Understand First
Stop and research these before coding:
- SSH ControlMaster (Connection Multiplexing)
- What is ControlMaster and how does it reuse SSH connections?
- How do you configure ControlPath for persistent sockets?
- What is ControlPersist and when does it close connections?
- How do you debug stuck control sockets (
-O check,-O exit)? - Book Reference: “SSH Mastery” by Michael W. Lucas - Ch. 8 (Multiplexing)
- Remote Command Execution Patterns
- How do you execute a single command via SSH (
ssh host 'command')? - How do you handle stdin/stdout redirection over SSH?
- What’s the difference between
ssh -t(pseudo-TTY) vs non-interactive? - How do you escape shell metacharacters in remote commands?
- Book Reference: “Unix Network Programming” by W. Richard Stevens - Ch. 19 (Remote Execution)
- How do you execute a single command via SSH (
- SSH Port Forwarding (Tunneling)
- What is local forwarding (
-L) vs remote forwarding (-R)? - How do you forward multiple ports simultaneously?
- What is dynamic forwarding (
-D) for SOCKS proxy? - How do you debug forwarding failures (
-vverbose mode)? - Book Reference: “SSH Mastery” by Michael W. Lucas - Ch. 7 (Forwarding)
- What is local forwarding (
- Tool Execution Proxying
- How do you intercept tool calls and route them to SSH?
- Should you proxy all tools or only specific ones (Bash, Read, Write)?
- How do you handle tool failures (network errors, timeouts)?
- What about file path translation (local vs remote paths)?
- Book Reference: Kiro CLI docs - Remote Execution Configuration
Questions to Guide Your Design
Before implementing, think through these:
- Tool Selection for Remote Execution
- Which tools should execute remotely (Bash, Read, Write, Edit)?
- Should Grep run remotely or locally after fetching files?
- What about tools that need local state (TodoWrite, EnterPlanMode)?
- How do you handle mixed workflows (some local, some remote)?
- Connection Management
- Do you open a new SSH connection per tool call or reuse one?
- How do you detect connection failures and retry?
- Should you establish the connection lazily (on first use) or eagerly?
- What’s the timeout for idle connections (ControlPersist)?
- Path Translation
- How do you map local file paths to remote paths?
- Do you assume identical directory structures?
- How do you handle absolute vs relative paths?
- What about symlinks that resolve differently locally vs remotely?
- Security and Permissions
- Should you use password auth or key-based auth?
- How do you handle sudo commands that require passwords?
- Do you need to validate the remote host’s fingerprint?
- How do you prevent command injection via shell escaping?
Thinking Exercise
Manual SSH Tunnel Execution Trace
Before writing code, trace how a tool call is proxied through SSH:
Scenario: Kiro executes a Bash command remotely
Step 1: User asks Kiro to deploy
$ kiro "deploy the app to production"
Kiro decides: Run `npm run build && pm2 restart app`
Step 2: Kiro invokes Bash tool
Tool: Bash
Arguments: {
"command": "npm run build && pm2 restart app"
}
Step 3: Remote execution hook intercepts
// Hook detects remote execution is enabled
if (config.remote_execution.enabled) {
if (config.remote_execution.tools.includes('Bash')) {
execute_remotely(tool, args);
}
}
Step 4: Build SSH command
ssh_command = [
'ssh',
'-o', 'ControlMaster=auto',
'-o', 'ControlPath=~/.ssh/control-%r@%h:%p',
'-o', 'ControlPersist=10m',
'deploy@prod-server.company.com',
'cd /var/www/app && npm run build && pm2 restart app'
]
Step 5: Execute via SSH
$ ssh deploy@prod-server.company.com 'cd /var/www/app && npm run build && pm2 restart app'
# SSH reuses existing connection via ControlMaster socket
# Output is streamed back to local Kiro
Step 6: Return result to Kiro
{
"exitCode": 0,
"stdout": "Build complete. PM2 restarted app.",
"stderr": "",
"duration": 12345
}
Questions while tracing:
- How do you handle commands that need interactive input (sudo passwords)?
- What if the SSH connection breaks mid-execution?
- How do you capture real-time output (streaming vs buffered)?
- What if the remote command takes hours—do you keep the connection open?
The Interview Questions They’ll Ask
Prepare to answer these:
- “Explain SSH ControlMaster and how it enables connection multiplexing. What are the performance benefits?”
- “How would you handle shell escaping when passing user-generated commands over SSH to prevent command injection?”
- “What are the differences between SSH local forwarding (-L), remote forwarding (-R), and dynamic forwarding (-D)? When would you use each?”
- “How would you design a retry mechanism for tool execution that fails due to transient network errors?”
- “Explain the security implications of SSH agent forwarding. How would you mitigate the risks?”
- “How would you implement path translation for tools that operate on files when local and remote directory structures differ?”
Hints in Layers
Hint 1: Configure SSH ControlMaster
Add to ~/.ssh/config:
Host prod-server
HostName prod-server.company.com
User deploy
ControlMaster auto
ControlPath ~/.ssh/control-%r@%h:%p
ControlPersist 10m
This creates a persistent connection socket that’s reused for all subsequent SSH commands.
Hint 2: Proxy Bash Tool Calls Intercept Bash tool and route to SSH:
# In a hook or wrapper script
original_command="$1"
ssh prod-server "cd /app && $original_command"
Hint 3: Test Connection Reuse Verify ControlMaster is working:
# First connection (slow, establishes socket)
time ssh prod-server 'echo hello' # ~500ms
# Subsequent connections (fast, reuse socket)
time ssh prod-server 'echo hello' # ~50ms
Hint 4: Handle Shell Escaping
Use printf %q to safely escape commands:
safe_command=$(printf '%q' "$user_command")
ssh prod-server "bash -c $safe_command"
Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| SSH Multiplexing | “SSH Mastery” by Michael W. Lucas | Ch. 8 (Multiplexing), Ch. 7 (Forwarding) |
| Remote Execution | “Unix Network Programming” by W. Richard Stevens | Ch. 19 (Remote Execution) |
| Shell Escaping | “The Linux Command Line” by William Shotts | Ch. 35 (Strings and Numbers) |
| SSH Configuration | “SSH: The Secure Shell” by Barrett, Silverman, Byrnes | Ch. 7 (Advanced Client Use) |
| Networking Basics | “TCP/IP Illustrated, Volume 1” by W. Richard Stevens | Ch. 2 (The Internet Protocol) |
Common Pitfalls & Debugging
Problem 1: “Each SSH command takes 500ms, making Kiro unbearably slow”
- Why: Opening new SSH connection for every command
- Fix: Enable ControlMaster connection reuse:
Host prod-server ControlMaster auto ControlPath ~/.ssh/control-%r@%h:%p ControlPersist 10m - Quick test: Time 10 rapid SSH commands—should be <100ms each after first
Problem 2: “Commands fail with ‘command not found’ on remote host”
- Why: SSH non-interactive sessions don’t source
.bashrc - Fix: Source profile or use login shell:
ssh prod-server 'source ~/.bashrc && npm run build' # OR ssh -t prod-server 'npm run build' # Force pseudo-TTY - Quick test:
ssh prod-server 'echo $PATH'vsssh -t prod-server 'echo $PATH'
Problem 3: “Command injection via user input”
- Why: User command contains shell metacharacters (
;,|,&&) - Fix: Use parameterized execution or escape properly:
# BAD: ssh prod-server "rm $user_file" # Injection risk # GOOD: safe_file=$(printf '%q' "$user_file") ssh prod-server "rm $safe_file" - Quick test: Try
user_file="; rm -rf /"and verify it’s escaped
Problem 4: “Stuck control socket prevents new connections”
- Why: ControlMaster socket is hung or orphaned
- Fix: Kill stuck socket:
ssh -O exit prod-server # OR manually: rm ~/.ssh/control-deploy@prod-server.company.com:22 - Quick test:
ssh -O check prod-servershows socket status
Problem 5: “Remote commands don’t stream output, buffered until completion”
- Why: stdout is buffered when not connected to a TTY
- Fix: Use
stdbuforscriptto force line buffering:ssh prod-server 'stdbuf -oL npm run build' # OR force pseudo-TTY: ssh -t prod-server 'npm run build' - Quick test: Long-running command should show incremental output
Definition of Done
- SSH ControlMaster is configured for connection reuse
- Bash tool calls are proxied to remote host via SSH
- Read/Write/Edit tools can operate on remote files
- Tool execution respects working directory on remote host
- Shell commands are properly escaped to prevent injection
- ControlPersist keeps connections open for 10 minutes
- Failed SSH connections are retried with exponential backoff
- Remote execution can be toggled on/off via config file
- Path translation handles local vs remote directory structures
- Streaming output works for long-running commands
- Documentation explains SSH config setup and troubleshooting
- Supports bastion/jump hosts for accessing internal servers