<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>nunogrl.com - Infrastructure Security</title><link href="https://nunogrl.com/" rel="alternate"></link><link href="https://nunogrl.com/feeds/infrastructure-security.atom.xml" rel="self"></link><id>https://nunogrl.com/</id><updated>2024-03-21T00:00:00+00:00</updated><entry><title>Enforcing GPG-Signed Commits in Git</title><link href="https://nunogrl.com/articles/enforcing-gpg-signed-commits-git/" rel="alternate"></link><published>2024-03-21T00:00:00+00:00</published><updated>2024-03-21T00:00:00+00:00</updated><author><name>Nuno Leitao</name></author><id>tag:nunogrl.com,2024-03-21:/articles/enforcing-gpg-signed-commits-git/</id><summary type="html">&lt;p class="first last"&gt;Learn how to enforce GPG-signed commits in Git to prevent commit impersonation and ensure code authenticity&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="problem-solution"&gt;
&lt;h2&gt;🚀 Problem &amp;amp; Solution&lt;/h2&gt;
&lt;div class="section" id="context-backstory"&gt;
&lt;h3&gt;📌 &lt;strong&gt;Context / Backstory&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In our collaborative development environment, we discovered that Git's flexibility allows commits under any name and email. This became a security concern when we realized that GitHub identifies users by email, not SSH keys.&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram--1710153734321077480"&gt;
sequenceDiagram
  participant Alice
  participant Bob
  participant GitHub

  Alice-&gt;&gt;GitHub: Commit signed with alice@example.com (GPG signed)
  GitHub-&gt;&gt;GitHub: Shows "Verified" commit from Alice

  Bob-&gt;&gt;GitHub: Commit using alice@example.com (no signature)
  GitHub-&gt;&gt;GitHub: Shows commit as from "Alice" (Unverified)

  Note over GitHub: GitHub matches commits by email, not by SSH or true identity.&lt;/div&gt;&lt;/div&gt;
&lt;div class="section" id="the-problem"&gt;
&lt;h3&gt;⚠️ &lt;strong&gt;The Problem&lt;/strong&gt;&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Anyone with repository access can spoof another contributor's identity&lt;/li&gt;
&lt;li&gt;Commit history could be manipulated without detection&lt;/li&gt;
&lt;li&gt;No cryptographic proof of commit authenticity&lt;/li&gt;
&lt;li&gt;GitHub's user identification relies solely on email addresses&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-solution"&gt;
&lt;h3&gt;💡 &lt;strong&gt;The Solution&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We implemented mandatory GPG-signed commits, which:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Cryptographically verify commit authenticity&lt;/li&gt;
&lt;li&gt;Prevent identity spoofing&lt;/li&gt;
&lt;li&gt;Create traceable commit history&lt;/li&gt;
&lt;li&gt;Integrate with GitHub's verification system&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="who-this-helps"&gt;
&lt;h3&gt;👥 &lt;strong&gt;Who This Helps&lt;/strong&gt;&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Security-conscious development teams&lt;/li&gt;
&lt;li&gt;Open-source project maintainers&lt;/li&gt;
&lt;li&gt;Regulated environments requiring audit trails&lt;/li&gt;
&lt;li&gt;Organizations needing verified commit history&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="technical-implementation"&gt;
&lt;h2&gt;⚙️ Technical Implementation&lt;/h2&gt;
&lt;p&gt;Let's visualize the GPG signing workflow:&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram-1440076246032696055"&gt;
flowchart LR
   C[Commit] --&gt;|Sign| G[GPG Key]
   G --&gt;|Verify| GH[GitHub]

   subgraph "Local System"
   C
   G
   end

   subgraph "Remote"
   GH
   end

   style GH fill:#f96,stroke:#333
   style G fill:#9f6,stroke:#333&lt;/div&gt;&lt;div class="section" id="setting-up-gpg-keys"&gt;
&lt;h3&gt;1️⃣ Setting Up GPG Keys&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Generate a new GPG key&lt;/span&gt;
gpg&lt;span class="w"&gt; &lt;/span&gt;--full-generate-key

&lt;span class="c1"&gt;# List your keys&lt;/span&gt;
gpg&lt;span class="w"&gt; &lt;/span&gt;--list-secret-keys&lt;span class="w"&gt; &lt;/span&gt;--keyid-format&lt;span class="o"&gt;=&lt;/span&gt;long
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="configuring-git"&gt;
&lt;h3&gt;2️⃣ Configuring Git&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;Configure&lt;span class="w"&gt; &lt;/span&gt;Git&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;use&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;GPG&lt;span class="w"&gt; &lt;/span&gt;key
&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;--global&lt;span class="w"&gt; &lt;/span&gt;user.signingkey&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YOUR_KEY_ID&amp;gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;--global&lt;span class="w"&gt; &lt;/span&gt;commit.gpgsign&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;--global&lt;span class="w"&gt; &lt;/span&gt;gpg.program&lt;span class="w"&gt; &lt;/span&gt;gpg
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="github-integration"&gt;
&lt;h3&gt;3️⃣ GitHub Integration&lt;/h3&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Export your public GPG key:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--armor&lt;span class="w"&gt; &lt;/span&gt;--export&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;YOUR_KEY_ID&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Add the key to your GitHub account settings&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="enforcing-signed-commits"&gt;
&lt;h3&gt;4️⃣ Enforcing Signed Commits&lt;/h3&gt;
&lt;p&gt;In GitHub repository settings:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Navigate to Settings &amp;gt; Branches&lt;/li&gt;
&lt;li&gt;Add branch protection rule&lt;/li&gt;
&lt;li&gt;Enable &amp;quot;Require signed commits&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="troubleshooting-debugging"&gt;
&lt;h2&gt;🛠️ Troubleshooting &amp;amp; Debugging&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;GPG signing fails&lt;/strong&gt;: Check &lt;cite&gt;gpg-agent&lt;/cite&gt; configuration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub doesn't show &amp;quot;Verified&amp;quot;&lt;/strong&gt;: Ensure GPG key is added to GitHub&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CI/CD issues&lt;/strong&gt;: Set up proper &lt;cite&gt;GNUPGHOME&lt;/cite&gt; environment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smart card/YubiKey&lt;/strong&gt;: Verify proper card reader access&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="optimizations-best-practices"&gt;
&lt;h2&gt;🔁 Optimizations &amp;amp; Best Practices&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Use GPG subkeys instead of master keys&lt;/li&gt;
&lt;li&gt;Implement regular key rotation&lt;/li&gt;
&lt;li&gt;Set up separate signing keys for different contexts&lt;/li&gt;
&lt;li&gt;Use environment isolation in CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Consider hardware security keys (YubiKey) for key storage&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion-takeaways"&gt;
&lt;h2&gt;✅ Conclusion &amp;amp; Takeaways&lt;/h2&gt;
&lt;p&gt;GPG-signed commits provide a robust security layer for Git workflows, ensuring:
- Verified commit authenticity
- Protected repository history
- Clear accountability
- Compliance with security best practices&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="comments-next-steps"&gt;
&lt;h2&gt;💬 Comments &amp;amp; Next Steps&lt;/h2&gt;
&lt;p&gt;How do you handle commit verification in your organization? Share your experience or ask questions below!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Infrastructure Security"></category><category term="git"></category><category term="gpg"></category><category term="security"></category><category term="devops"></category><category term="version-control"></category><category term="cryptography"></category><category term="authentication"></category></entry><entry><title>Password Store with GPG and Git</title><link href="https://nunogrl.com/articles/password-store-gpg-git/" rel="alternate"></link><published>2024-03-21T00:00:00+00:00</published><updated>2024-03-21T00:00:00+00:00</updated><author><name>Nuno Leitao</name></author><id>tag:nunogrl.com,2024-03-21:/articles/password-store-gpg-git/</id><summary type="html">&lt;p class="first last"&gt;Learn how to set up a secure, Git-based password management system using password-store and GPG encryption&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="problem-solution"&gt;
&lt;h2&gt;🚀 Problem &amp;amp; Solution&lt;/h2&gt;
&lt;div class="section" id="context-backstory"&gt;
&lt;h3&gt;📌 &lt;strong&gt;Context / Backstory&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We needed a secure way to manage passwords and secrets across multiple servers and team members. Commercial password managers were either too complex, costly, or required external services we wanted to avoid.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-problem"&gt;
&lt;h3&gt;⚠️ &lt;strong&gt;The Problem&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Managing secrets across systems and teams presents several challenges:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Keeping passwords secure yet accessible&lt;/li&gt;
&lt;li&gt;Tracking changes and maintaining history&lt;/li&gt;
&lt;li&gt;Sharing secrets securely between team members&lt;/li&gt;
&lt;li&gt;Avoiding dependency on external services&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-solution"&gt;
&lt;h3&gt;💡 &lt;strong&gt;The Solution&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We implemented &lt;cite&gt;password-store&lt;/cite&gt; with GPG encryption and Git integration, providing:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Secure GPG encryption for all secrets&lt;/li&gt;
&lt;li&gt;Git-based version control and distribution&lt;/li&gt;
&lt;li&gt;Fully local operation with no external dependencies&lt;/li&gt;
&lt;li&gt;Command-line interface for automation&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="who-this-helps"&gt;
&lt;h3&gt;👥 &lt;strong&gt;Who This Helps&lt;/strong&gt;&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;System administrators managing multiple servers&lt;/li&gt;
&lt;li&gt;DevOps teams handling shared credentials&lt;/li&gt;
&lt;li&gt;Security-conscious users wanting local password management&lt;/li&gt;
&lt;li&gt;Teams needing version-controlled secrets&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="technical-implementation"&gt;
&lt;h2&gt;⚙️ Technical Implementation&lt;/h2&gt;
&lt;p&gt;Let's visualize the password-store workflow:&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram--7232498932700614839"&gt;
flowchart LR
   P[Password] --&gt;|Encrypt| G[GPG]
   G --&gt;|Store| PS[password-store]
   PS --&gt;|Version| Git[Git Repository]
   Git --&gt;|Sync| T[Team Members]

   subgraph "Local System"
   P
   G
   PS
   end

   subgraph "Distribution"
   Git
   T
   end&lt;/div&gt;&lt;div class="section" id="generating-gpg-keys-in-batch-mode"&gt;
&lt;h3&gt;1️⃣ Generating GPG Keys in Batch Mode&lt;/h3&gt;
&lt;p&gt;For automated environments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;gpg-server-key.conf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF&lt;/span&gt;
&lt;span class="s"&gt;%no-protection&lt;/span&gt;
&lt;span class="s"&gt;Key-Type: default&lt;/span&gt;
&lt;span class="s"&gt;Subkey-Type: default&lt;/span&gt;
&lt;span class="s"&gt;Name-Real: Server Automation Key&lt;/span&gt;
&lt;span class="s"&gt;Name-Email: server@example.com&lt;/span&gt;
&lt;span class="s"&gt;Expire-Date: 0&lt;/span&gt;
&lt;span class="s"&gt;%commit&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;

gpg&lt;span class="w"&gt; &lt;/span&gt;--batch&lt;span class="w"&gt; &lt;/span&gt;--generate-key&lt;span class="w"&gt; &lt;/span&gt;gpg-server-key.conf
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="setting-up-the-environment"&gt;
&lt;h3&gt;2️⃣ Setting Up the Environment&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PASSWORD_STORE_GPG_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--armor&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GNUPGHOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/password-store/.gnupg
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PASSWORD_STORE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/password-store/store
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="initializing-the-password-store"&gt;
&lt;h3&gt;3️⃣ Initializing the Password Store&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$GNUPGHOME&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PASSWORD_STORE_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$GNUPGHOME&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PASSWORD_STORE_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
pass&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;server@example.com
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="git-integration"&gt;
&lt;h3&gt;4️⃣ Git Integration&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PASSWORD_STORE_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;init
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;.
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Initial password store&amp;quot;&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;remote&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;git@example.com:secrets.git
git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;main
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="troubleshooting-debugging"&gt;
&lt;h2&gt;🛠️ Troubleshooting &amp;amp; Debugging&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Ensure proper GPG key permissions (700 for directories, 600 for files)&lt;/li&gt;
&lt;li&gt;Verify GPG recipient when encryption fails&lt;/li&gt;
&lt;li&gt;Check Git remote access rights for sync issues&lt;/li&gt;
&lt;li&gt;Monitor Git conflicts when multiple users update simultaneously&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="optimizations-alternatives"&gt;
&lt;h2&gt;🔁 Optimizations &amp;amp; Alternatives&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Consider using GPG agent for improved key handling&lt;/li&gt;
&lt;li&gt;Implement Git hooks for pre-commit validation&lt;/li&gt;
&lt;li&gt;Use Git branches for testing password updates&lt;/li&gt;
&lt;li&gt;Consider &lt;cite&gt;pass&lt;/cite&gt; extensions for additional features&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion-takeaways"&gt;
&lt;h2&gt;✅ Conclusion &amp;amp; Takeaways&lt;/h2&gt;
&lt;p&gt;Using GPG with password-store provides a &lt;strong&gt;flexible, secure, and lightweight&lt;/strong&gt; method for managing secrets across machines. With Git integration, you get version history, team sharing, and distributed backup—&lt;strong&gt;without compromising security&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="comments-next-steps"&gt;
&lt;h2&gt;💬 Comments &amp;amp; Next Steps&lt;/h2&gt;
&lt;p&gt;How do you manage shared secrets in your infrastructure? Share your experience or ask questions below!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Infrastructure Security"></category><category term="gpg"></category><category term="git"></category><category term="password-management"></category><category term="security"></category><category term="encryption"></category><category term="devops"></category><category term="secrets-management"></category></entry><entry><title>Testing Microservices Securely Using SSH Tunnels</title><link href="https://nunogrl.com/articles/testing-microservices-securely-using-ssh-tunnels/" rel="alternate"></link><published>2024-03-21T00:00:00+00:00</published><updated>2024-03-21T00:00:00+00:00</updated><author><name>Nuno Leitao</name></author><id>tag:nunogrl.com,2024-03-21:/articles/testing-microservices-securely-using-ssh-tunnels/</id><summary type="html">&lt;p class="first last"&gt;A practical guide to testing local and staging microservices against production endpoints using SSH tunnels — without exposing anything publicly&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="problem-solution"&gt;
&lt;h2&gt;🚀 Problem &amp;amp; Solution&lt;/h2&gt;
&lt;div class="section" id="context-backstory"&gt;
&lt;h3&gt;📌 &lt;strong&gt;Context / Backstory&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;This is a solution to connect two different servers on different networks.&lt;/p&gt;
&lt;p&gt;What I was trying to achieve was to get the server from network-A to reach the
port 80 from the network-B.&lt;/p&gt;
&lt;p&gt;These networks are different VPCs on AWS, and connecting the networks is not an
option, because of some conflicts with DNS.&lt;/p&gt;
&lt;p&gt;We needed to test a &lt;strong&gt;local microservice using a production endpoint&lt;/strong&gt;, which wasn't publicly accessible. This endpoint couldn't be mocked or duplicated in staging — and pushing untested code to production would have been reckless.&lt;/p&gt;
&lt;p&gt;Additionally, we had a &lt;strong&gt;staging microservice&lt;/strong&gt; that also needed to interact with the same production service. The risk of breaking production due to untested integration was high, but access was restricted for good reason.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-problem"&gt;
&lt;h3&gt;⚠️ &lt;strong&gt;The Problem&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;How do we test these microservices — local and staging — against a &lt;strong&gt;real production service&lt;/strong&gt; without:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Exposing the service publicly&lt;/li&gt;
&lt;li&gt;Deploying unverified code&lt;/li&gt;
&lt;li&gt;Breaking the security model&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-solution"&gt;
&lt;h3&gt;💡 &lt;strong&gt;The Solution&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We used &lt;strong&gt;SSH tunnels&lt;/strong&gt; to create secure, temporary links between the testing environments (local and staging) and the production endpoint. This allowed us to route traffic safely without exposing any services over the internet.&lt;/p&gt;
&lt;p&gt;👥 &lt;strong&gt;Who This Helps&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;DevOps engineers needing to test services under real conditions&lt;/li&gt;
&lt;li&gt;Developers in environments with locked-down production APIs&lt;/li&gt;
&lt;li&gt;Homelab or cloud setups requiring secure point-to-point testing&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="technical-implementation"&gt;
&lt;h2&gt;⚙️ Technical Implementation&lt;/h2&gt;
&lt;p&gt;I can reach both networks from my laptop through VPNs, so the solution I'm
sharing here is on how to &lt;strong&gt;forward the port 80 from a server to
localhost:8080 of the other server&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;server-net-b
&lt;span class="nv"&gt;CANARY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;server-net-a&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;# workstation&lt;/span&gt;
ssh&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;:&lt;span class="nv"&gt;$SERVER&lt;/span&gt;:80&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$SERVER&lt;/span&gt;

&lt;span class="c1"&gt;# workstation&lt;/span&gt;
ssh&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;:localhost:8080&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CANARY&lt;/span&gt;

&lt;span class="c1"&gt;# CANARY&lt;/span&gt;
curl&lt;span class="w"&gt; &lt;/span&gt;localhost:8080
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here's a diagram of the solution:&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram-395275778961399839"&gt;
flowchart LR

   subgraph local network
   L[Local Machine]
   end

   subgraph vpc-prod
   S[Server
     *webapp-0ef31
     localhost:80*]
   end

   subgraph vpc-dev
   P[Canary]
   end

   L --- |*webapp-0ef31
          localhost:8080*|L
   L --- |Local Port Forward
         ssh -L 8080:Server:80| S
   L --&gt;|Remote Port Forward
         ssh -R 8080:CANARY:8080| P
   P --&gt; |*webapp-0ef31
          localhost:8080*| P&lt;/div&gt;&lt;p&gt;Let's visualize the different types of SSH tunnels we can use:&lt;/p&gt;
&lt;div class="section" id="local-port-forwarding-local-to-production"&gt;
&lt;h3&gt;1️⃣ Local Port Forwarding (Local to Production)&lt;/h3&gt;
&lt;p&gt;To test a local service against a production database or API:&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram-8101960594763041844"&gt;
flowchart LR
   L &lt;--&gt;|Local Port Forward
         ssh -L 5432| S
   L --&gt;|localhost:5432| L

   subgraph local network
   L[Local Machine]
   end

   subgraph vpc-dev
   S[Staging Server
     localhost:5432]
   end&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5432&lt;/span&gt;:localhost:5432&lt;span class="w"&gt; &lt;/span&gt;user@prod-host
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This allows you to connect to &lt;cite&gt;localhost:5432&lt;/cite&gt;, which transparently tunnels to the production service on &lt;cite&gt;prod-host&lt;/cite&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="remote-port-forwarding-expose-local-to-remote"&gt;
&lt;h3&gt;2️⃣ Remote Port Forwarding (Expose Local to Remote)&lt;/h3&gt;
&lt;p&gt;To make your local microservice available to a remote server (like staging or prod):&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram--4872684985663348909"&gt;
flowchart LR
   L &lt;--&gt;|Local Port Forward
         ssh -R 8080:localhost:3000| S
   S --&gt;|localhost:8080| S

   subgraph local network
   L[Local Machine
     localhost:3000]
   end

   subgraph vpc-dev
   S[Staging Server]
   end&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;:localhost:3000&lt;span class="w"&gt; &lt;/span&gt;user@remote-server
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, &lt;cite&gt;remote-server:8080&lt;/cite&gt; connects to your local microservice running on port &lt;cite&gt;3000&lt;/cite&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="socks-proxy-dynamic-tunnel"&gt;
&lt;h3&gt;3️⃣ SOCKS Proxy (Dynamic Tunnel)&lt;/h3&gt;
&lt;p&gt;To route your web or tool traffic through a secure production jump host:&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram-7343683011801147043"&gt;
flowchart LR
   L --&gt;|web proxy:
         localhost:1080| L

   L &lt;--&gt;|ssh -D 1080| J
   J ==&gt; G

   subgraph local network
   L[Local Machine]
   end
   subgraph network
   J[server]
   G((internet
     gateway))
   end&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-D&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1080&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;-N&lt;span class="w"&gt; &lt;/span&gt;user@prod-host
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then configure your browser or curl to use SOCKS proxy at &lt;cite&gt;localhost:1080&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;With this, you can access any service that is hosted on the production server, such as a database or API.&lt;/p&gt;
&lt;p&gt;Also, all your traffic will be encrypted and routed through the production server.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reverse-ssh-tunnel-access-machines-behind-nat"&gt;
&lt;h3&gt;4️⃣ Reverse SSH Tunnel (Access Machines Behind NAT)&lt;/h3&gt;
&lt;p&gt;To allow remote SSH access into a local machine that's behind NAT.&lt;/p&gt;
&lt;p&gt;This is useful when you need to access a machine that is behind a firewall or NAT.
In the situation you need to access a machine that is behind a firewall someone else controls, this is a great solution.&lt;/p&gt;
&lt;p&gt;the person will connect to the jumbox server and then you will connect to the jumbox server to access the machine that is behind the firewall.&lt;/p&gt;
&lt;div class="mermaid" id="mermaid-diagram-5864542454423108947"&gt;
flowchart LR
   H &lt;--&gt;|Remote Port Forward
          ssh -R
          /hooked ssh connection| J
   L --&gt;|ssh
   to jumpbox| J
   J --&gt;|ssh to
         localhost:2222| J

   subgraph local network
   L[Local Machine]
   end
   subgraph Public network
   J[jumpbox]
   end
   subgraph private network
   H[server]
   end&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2222&lt;/span&gt;:localhost:22&lt;span class="w"&gt; &lt;/span&gt;user@jumpbox
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then connect to the local machine from jumpbox:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2222&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;user@localhost
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="persistent-tunnels-with-autossh"&gt;
&lt;h3&gt;5️⃣ Persistent Tunnels with autossh&lt;/h3&gt;
&lt;p&gt;To keep a tunnel alive automatically:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;autossh&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;-N&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;:localhost:8080&lt;span class="w"&gt; &lt;/span&gt;user@remote-server
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="troubleshooting-debugging"&gt;
&lt;h2&gt;🛠️ Troubleshooting &amp;amp; Debugging&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Connection hangs&lt;/strong&gt;? Add &lt;cite&gt;-v&lt;/cite&gt; or &lt;cite&gt;-vvv&lt;/cite&gt; to see what SSH is doing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Port not forwarding&lt;/strong&gt;? Make sure &lt;cite&gt;GatewayPorts&lt;/cite&gt; is allowed in sshd config.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Firewall blocking traffic&lt;/strong&gt;? Test with &lt;cite&gt;telnet&lt;/cite&gt;, &lt;cite&gt;nc&lt;/cite&gt;, or &lt;cite&gt;curl&lt;/cite&gt; to confirm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service only binds to 127.0.0.1?&lt;/strong&gt; Use &lt;cite&gt;ssh -L&lt;/cite&gt; with explicit hostnames.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="optimizations-alternatives"&gt;
&lt;h2&gt;🔁 Optimizations &amp;amp; Alternatives&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;For longer-term infrastructure, consider &lt;strong&gt;WireGuard&lt;/strong&gt; or &lt;strong&gt;Tailscale&lt;/strong&gt; for persistent tunnels.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;SSH certificates&lt;/strong&gt; to avoid managing multiple authorized keys.&lt;/li&gt;
&lt;li&gt;Run &lt;cite&gt;autossh&lt;/cite&gt; as a systemd or runit service for reliability.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion-takeaways"&gt;
&lt;h2&gt;✅ Conclusion &amp;amp; Takeaways&lt;/h2&gt;
&lt;p&gt;Using SSH tunnels allowed us to test services against production safely, without deploying code or violating security constraints. This pattern is lightweight, robust, and fits well into environments where:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;VPN is not an option&lt;/li&gt;
&lt;li&gt;Public exposure is forbidden&lt;/li&gt;
&lt;li&gt;Controlled testing against production is required&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="comments-next-steps"&gt;
&lt;h2&gt;💬 Comments &amp;amp; Next Steps&lt;/h2&gt;
&lt;p&gt;Have you used SSH tunnels in your microservices architecture? Share your experience or ask questions below!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Infrastructure Security"></category><category term="ssh"></category><category term="tunnels"></category><category term="secure-access"></category><category term="devops-tools"></category><category term="microservices"></category></entry></feed>