<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tyler Russo</title>
    <description>The latest articles on DEV Community by Tyler Russo (@tyler_russo_f3ae739f97551).</description>
    <link>https://dev.to/tyler_russo_f3ae739f97551</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4161582%2Fbb21cbc9-4e57-4aba-a34f-5c44d935a9dc.png</url>
      <title>DEV Community: Tyler Russo</title>
      <link>https://dev.to/tyler_russo_f3ae739f97551</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tyler_russo_f3ae739f97551"/>
    <language>en</language>
    <item>
      <title>Your SSH hardening guide has a bug: on cloud servers, 60 beats 99</title>
      <dc:creator>Tyler Russo</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:37:21 +0000</pubDate>
      <link>https://dev.to/tyler_russo_f3ae739f97551/your-ssh-hardening-guide-has-a-bug-on-cloud-servers-60-beats-99-2fcf</link>
      <guid>https://dev.to/tyler_russo_f3ae739f97551/your-ssh-hardening-guide-has-a-bug-on-cloud-servers-60-beats-99-2fcf</guid>
      <description>&lt;p&gt;I sell a server hardening checklist, so last week I did something most checklist authors never do: I re-ran every single command against a real Ubuntu 24.04 box instead of trusting the docs.&lt;/p&gt;

&lt;p&gt;Two of the commands were wrong. Not "suboptimal" — wrong. One of them fails &lt;em&gt;silently&lt;/em&gt;, which is worse. Here's the interesting one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Most SSH hardening guides tell you the same thing: don't edit &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; directly. Drop your settings into &lt;code&gt;/etc/ssh/sshd_config.d/&lt;/code&gt;, conventionally in a file named something like &lt;code&gt;99-hardening.conf&lt;/code&gt;. High number, loads last, wins. Clean, package-manager-safe advice.&lt;/p&gt;

&lt;p&gt;It's wrong on cloud images.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happens
&lt;/h2&gt;

&lt;p&gt;Ubuntu cloud images (AWS, DigitalOcean, Hetzner, all of them) ship a file at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="k"&gt;etc&lt;/span&gt;/ssh/sshd_config.d/60-cloudimg-settings.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the part the guides skip: sshd reads the drop-in directory with a glob, in &lt;strong&gt;alphabetical order&lt;/strong&gt; — and for most keywords, sshd uses &lt;strong&gt;first-value-wins&lt;/strong&gt; semantics. The first setting it encounters is the one that sticks.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;60&lt;/code&gt; sorts before &lt;code&gt;99&lt;/code&gt;. So your carefully written &lt;code&gt;99-hardening.conf&lt;/code&gt; &lt;strong&gt;loses to the cloud image's file for every keyword they both set&lt;/strong&gt; — and it loses silently. No error, no warning. Your hardening setting just... doesn't apply.&lt;/p&gt;

&lt;p&gt;I proved it the honest way: wrote a test value into &lt;code&gt;99-hardening.conf&lt;/code&gt; on a real 24.04 box and ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sshd &lt;span class="nt"&gt;-T&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &amp;lt;keyword&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The effective config showed the cloud image's value, not mine. My file might as well not have existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix (30 seconds)
&lt;/h2&gt;

&lt;p&gt;Name your file so it sorts &lt;em&gt;before&lt;/em&gt; the cloud image's file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mv&lt;/span&gt; /etc/ssh/sshd_config.d/99-hardening.conf &lt;span class="se"&gt;\&lt;/span&gt;
        /etc/ssh/sshd_config.d/10-hardening.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then verify — don't trust, verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sshd &lt;span class="nt"&gt;-T&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &amp;lt;keyword&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sshd -T&lt;/code&gt; prints the &lt;em&gt;effective&lt;/em&gt; configuration after all includes are resolved. If your value shows up there, it won. If it doesn't, something earlier in the sort order is beating you, and now you know how to find it.&lt;/p&gt;

&lt;p&gt;This also means the generic advice "put it in the main &lt;code&gt;sshd_config&lt;/code&gt;" is doubly broken: the main file &lt;code&gt;Include&lt;/code&gt;s the drop-in directory at the &lt;em&gt;top&lt;/em&gt;, so drop-ins beat the main file too. The drop-in directory is the right place — you just have to win the sort order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus bug: &lt;code&gt;systemctl restart sshd&lt;/code&gt; fails on Ubuntu
&lt;/h2&gt;

&lt;p&gt;While I was at it: half the guides say &lt;code&gt;systemctl restart sshd&lt;/code&gt; after editing. On Ubuntu there is no &lt;code&gt;sshd.service&lt;/code&gt; — the unit is &lt;code&gt;ssh&lt;/code&gt;. The command fails, and if you're following a guide at 2 AM you get to debug that instead of sleeping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart ssh   &lt;span class="c"&gt;# not sshd, on Ubuntu/Debian&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(RHEL-family distros do use &lt;code&gt;sshd&lt;/code&gt;. Know your box.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual lesson
&lt;/h2&gt;

&lt;p&gt;Doc-based audits miss ordering and naming subtleties. The only audit I trust anymore is running the real binary and reading what it says: &lt;code&gt;sshd -T&lt;/code&gt; for SSH, &lt;code&gt;nginx -t&lt;/code&gt; for nginx, and so on. If your hardening checklist wasn't verified against a live system, it's a wish list.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I turned the full verified checklist into a 46-checkpoint playbook (every command tested like this one), but the 7-step starter version is free — &lt;a href="https://rogertg.gumroad.com/l/free-server-lockdown-checklist" rel="noopener noreferrer"&gt;Emergency Server Lockdown Checklist&lt;/a&gt;. Lock down a VPS tonight with copy-paste commands.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ssh</category>
      <category>security</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
