<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jump-Server on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/jump-server/</link><description>Recent content in Jump-Server 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/jump-server/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></channel></rss>