<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ssh on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/ssh/</link><description>Recent content in Ssh on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 05 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/tags/ssh/index.xml" rel="self" type="application/rss+xml"/><item><title>BIFROST — Raspberry Pi jump node</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/bifrost/</link><pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/bifrost/</guid><description>&lt;p&gt;The homelab needed a permanent always-on entry point — something low power, always reachable, a stable first hop. A first-gen Raspberry Pi in the rack fills that role. BIFROST.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hardware"&gt;Hardware
&lt;/h2&gt;&lt;p&gt;Raspberry Pi 1 Model B running Raspbian, mounted in the rack with a &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rack-3d-prints/" &gt;3D printed 1U mount&lt;/a&gt;. Draws under 2W at idle. Nothing runs on it except sshd.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-it-works"&gt;How it works
&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;ssh -p 22222 user@bifrost.mjnet.info
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The chain:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;bifrost.mjnet.info&lt;/code&gt; — Route53 CNAME pointing to &lt;code&gt;router.mjnet.info&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;router.mjnet.info&lt;/code&gt; — HEIMDAL (SYS-009), kept current via DDNS&lt;/li&gt;
&lt;li&gt;OPNsense port forward: external TCP 22222 → Pi:22&lt;/li&gt;
&lt;li&gt;OPNsense DNS override: &lt;code&gt;bifrost.mjnet.info&lt;/code&gt; → Pi&amp;rsquo;s internal IP&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The DNS override means the same hostname resolves to the internal IP when used inside the network — no split config needed in &lt;code&gt;~/.ssh/config&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="opnsense-config"&gt;OPNsense config
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Port forward&lt;/strong&gt; (Firewall → NAT → Port Forward):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interface: WAN&lt;/li&gt;
&lt;li&gt;Protocol: TCP&lt;/li&gt;
&lt;li&gt;Destination port: 22222&lt;/li&gt;
&lt;li&gt;Redirect target: Pi internal IP, port 22&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;DNS override&lt;/strong&gt; (Services → Unbound DNS → Host Overrides):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Host: &lt;code&gt;bifrost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Domain: &lt;code&gt;mjnet.info&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;IP: Pi internal IP&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="ssh-config"&gt;SSH config
&lt;/h2&gt;&lt;p&gt;Add to &lt;code&gt;~/.ssh/config&lt;/code&gt; on any client:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Host bifrost
 HostName bifrost.mjnet.info
 Port 22222
 User pi
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then &lt;code&gt;ssh bifrost&lt;/code&gt; from anywhere.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If a more robust solution becomes necessary later (no open ports, survives CGNAT), the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/bifrost-rpi-options/" &gt;options doc&lt;/a&gt; covers Tailscale, Cloudflare Tunnel, and WireGuard.&lt;/p&gt;</description></item><item><title>Bastion / jump server</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/</guid><description>&lt;p&gt;A bastion host (jump server) is a single, hardened machine exposed to the outside that acts as the entry point into a private network. You SSH into the bastion, then hop from there to internal hosts — or configure your SSH client to do it transparently in one step.&lt;/p&gt;
&lt;p&gt;The pattern is simple: minimise the network attack surface to one well-monitored machine, harden that machine specifically, and keep everything else off the public internet.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="basic-jump-manual-two-hop"&gt;Basic jump: manual two-hop
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 1: SSH into the bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh user@bastion.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 2: from bastion, SSH into internal host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh user@192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Works, but requires your private key to be on the bastion — which you want to avoid.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="proxyjump-recommended"&gt;ProxyJump (recommended)
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;ProxyJump&lt;/code&gt; tells SSH to tunnel through the bastion transparently. Your key never leaves your local machine.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# One-liner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -J user@bastion.example.com user@192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Or in ~/.ssh/config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host internal
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HostName 192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ProxyJump bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HostName bastion.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IdentityFile ~/.ssh/id_ed25519
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After this, &lt;code&gt;ssh internal&lt;/code&gt; works as a single command with no key on the bastion.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="agent-forwarding-vs-proxyjump"&gt;Agent forwarding vs ProxyJump
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Agent forwarding&lt;/strong&gt; (&lt;code&gt;-A&lt;/code&gt;, &lt;code&gt;ForwardAgent yes&lt;/code&gt;) forwards your SSH agent socket through the bastion so you can authenticate onward with your local key. Older approach — it works but the agent socket is briefly accessible on the bastion host, which is a lateral movement risk if the bastion is compromised.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ProxyJump&lt;/strong&gt; is strictly better for the common case: the connection to the internal host is established from your machine through an SSH tunnel, not from the bastion itself. No agent socket on the bastion.&lt;/p&gt;
&lt;p&gt;Use ProxyJump unless you specifically need agent forwarding for something else.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hardening-basics"&gt;Hardening basics
&lt;/h2&gt;&lt;p&gt;A bastion is only useful if it&amp;rsquo;s actually hardened. Minimum:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/ssh/sshd_config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PasswordAuthentication no
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PermitRootLogin no
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AuthorizedKeysFile .ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AllowUsers jumpuser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Key-only authentication — no passwords&lt;/li&gt;
&lt;li&gt;Dedicated user with no shell access to the bastion itself (optional: &lt;code&gt;ForceCommand&lt;/code&gt; to restrict what they can do)&lt;/li&gt;
&lt;li&gt;Non-standard port reduces log noise, not actual security&lt;/li&gt;
&lt;li&gt;fail2ban or equivalent for rate-limiting auth attempts&lt;/li&gt;
&lt;li&gt;Minimal installed software — the bastion should do one thing&lt;/li&gt;
&lt;li&gt;Regular log review (&lt;code&gt;/var/log/auth.log&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="restricting-to-jump-only"&gt;Restricting to jump-only
&lt;/h2&gt;&lt;p&gt;If you want users to be able to jump through the bastion but not get a shell on it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# In authorized_keys, prefix the key with:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;restrict,port-forwarding ssh-ed25519 AAAA...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or use &lt;code&gt;AllowTcpForwarding yes&lt;/code&gt; with &lt;code&gt;ForceCommand /usr/sbin/nologin&lt;/code&gt; — though the interaction between these options is subtle; test carefully.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;For making the bastion reachable from outside without a public IP → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/" &gt;Tunnels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For exposing internal web services via public URLs → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunneled-reverse-proxy/" &gt;Tunneled reverse proxy platforms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>SSH</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/</guid><description>&lt;p&gt;SSH (Secure Shell) is the standard protocol for encrypted remote access to Linux and Unix systems. It replaced telnet and rsh by wrapping the session in a cryptographic tunnel — authentication, commands, and data transfer all protected against interception and tampering.&lt;/p&gt;
&lt;h2 id="public-key-authentication"&gt;Public key authentication
&lt;/h2&gt;&lt;p&gt;The default auth mechanism for any serious setup. You generate a key pair: a private key that never leaves your machine, and a public key that goes on the remote host.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-keygen -t ed25519 -C &lt;span style="color:#e6db74"&gt;&amp;#34;your@email.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-copy-id user@host &lt;span style="color:#75715e"&gt;# appends public key to ~/.ssh/authorized_keys on remote&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Prefer &lt;code&gt;ed25519&lt;/code&gt; over the older &lt;code&gt;rsa&lt;/code&gt; — smaller keys, faster, stronger. Keep your private key protected with a passphrase; use &lt;code&gt;ssh-agent&lt;/code&gt; to avoid typing it repeatedly.&lt;/p&gt;
&lt;h2 id="key-deployment-at-scale"&gt;Key deployment at scale
&lt;/h2&gt;&lt;p&gt;When you have many hosts, distributing public keys manually doesn&amp;rsquo;t scale. A typical pattern: submit your public key to a central approval system, which then deploys it to the relevant hosts via automation (Ansible, Puppet, etc.).&lt;/p&gt;
&lt;p&gt;&lt;img alt="SSH key deployment flow" class="gallery-image" data-flex-basis="480px" data-flex-grow="200" height="1280" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm_hu_7067fdd6ddfbd76d.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm_hu_a732934bde95612.png 1600w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm_hu_4339d3dc0eb426e1.png 2400w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-deploy-rgm.png 2560w" width="2560"&gt;&lt;/p&gt;
&lt;p&gt;This keeps a clear audit trail — keys are approved, recorded, and can be revoked centrally without touching individual &lt;code&gt;authorized_keys&lt;/code&gt; files.&lt;/p&gt;
&lt;h2 id="certificate-authentication"&gt;Certificate authentication
&lt;/h2&gt;&lt;p&gt;At larger scale, even centralised key distribution gets unwieldy. SSH certificates solve this properly: instead of deploying individual public keys, you run an SSH Certificate Authority (CA). Hosts and users trust the CA, not each other&amp;rsquo;s individual keys.&lt;/p&gt;
&lt;p&gt;&lt;img alt="SSH certificate authentication architecture" class="gallery-image" data-flex-basis="528px" data-flex-grow="220" height="1018" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication_hu_3c89cbd02831450b.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication_hu_98e8f600ea8421b2.png 1600w, https://backend-engineering-strategy-tools.github.io/site/public-notes/security/ssh/ssh-certificate-authentication.png 2242w" width="2242"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate a key pair locally&lt;/li&gt;
&lt;li&gt;Submit the public key to the SSH CA (backed by your identity provider — FreeIPA, Vault, etc.)&lt;/li&gt;
&lt;li&gt;The CA issues a signed certificate with a short TTL&lt;/li&gt;
&lt;li&gt;SSH to any host — the host trusts the CA, so the certificate is accepted without any pre-deployed keys&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Short-lived certificates (hours, not days) dramatically reduce the blast radius of a compromised credential. No revocation lists to maintain.&lt;/p&gt;
&lt;h2 id="ssh-config"&gt;SSH config
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;~/.ssh/config&lt;/code&gt; avoids repetitive flags on every connection:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Host bastion
 HostName bastion.example.com
 User deploy
 IdentityFile ~/.ssh/id_ed25519
 ForwardAgent yes

Host internal-*
 ProxyJump bastion
 User deploy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ProxyJump&lt;/code&gt; (formerly &lt;code&gt;-J&lt;/code&gt;) lets you reach hosts that aren&amp;rsquo;t directly accessible — you SSH through the bastion transparently.&lt;/p&gt;
&lt;h2 id="port-forwarding"&gt;Port forwarding
&lt;/h2&gt;&lt;p&gt;SSH can tunnel TCP traffic, useful for reaching services on private networks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Local forward: reach remote Postgres via localhost:5432&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -L 5432:db.internal:5432 bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Dynamic forward: SOCKS proxy through the bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -D &lt;span style="color:#ae81ff"&gt;1080&lt;/span&gt; bastion
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="hardening-basics"&gt;Hardening basics
&lt;/h2&gt;&lt;p&gt;Key settings in &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers deploy ansible
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Disable password auth entirely once keys are in place. Restrict which users can log in. Run &lt;code&gt;sshd -t&lt;/code&gt; to validate config before reloading.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.openssh.com/manual.html" target="_blank" rel="noopener"
 &gt;OpenSSH manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.hashicorp.com/vault/docs/secrets/ssh/signed-ssh-certificates" target="_blank" rel="noopener"
 &gt;SSH certificate authentication (HashiCorp Vault)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://infosec.mozilla.org/guidelines/openssh" target="_blank" rel="noopener"
 &gt;Mozilla SSH Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>