The AI notetaker that gets the hard words right
Your AI is only as good as what you feed it. Feed it a transcript with your product names, acronyms, and numbers spelled wrong, and you spend the afternoon hand-fixing the output.
Wispr Flow Notetaker uses your dictionary and calendar, so product names, acronyms, numbers, and uncommon names come out spelled right. It pops up when your meeting starts and captures Zoom, Google Meet, Teams, and Slack huddles with one click, no bot joining the call.
Then your meetings show up inside Claude or ChatGPT through the built-in connector. No copy paste. Try Notetaker free on Mac and Windows.
Last issue, I pulled a week of SSH logs from my server and counted 53 failed password logins. Root was the most popular target. Nobody got in.
But "nobody got in" isn't much of a security plan. The better question was one I should have asked on day one:
What was this server willing to accept in the first place?
This issue covers four changes, in the order I made them, and how I proved each one worked. That last part is the real lesson.
The before state
The server for this series is a disposable Ubuntu test instance. Nothing important runs on it, which is exactly why I'm comfortable showing you its flaws.
First, I asked SSH what it was actually doing:
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication'It printed:
permitrootlogin yes
passwordauthentication yes[IMAGE: 01-before-sshd-T.png]
So anyone on the internet could try a password against the root account. That's exactly what 23 of those 53 attempts were doing.
One important caveat: this is what my test instance showed. Cloud images and provider defaults vary, and some ship with password login off. Check yours instead of assuming.
About that command. sshd -T reads every config file SSH would load and prints the effective configuration it derives from them. That's different from reading /etc/ssh/sshd_config yourself, and the difference matters later. (If your config has Match blocks, which apply different rules to certain users or addresses, those can change behavior for specific connections. Treat this output as your baseline.)
Before you touch anything: keep your session open
Before I changed a single SSH setting, I left my working terminal connected and opened a second terminal for testing. A typo in the SSH config can turn a five-minute hardening job into a console-recovery exercise.
Here's the order. Don't shuffle it:
Create a non-root user.
Give that user sudo access.
Add your SSH key for that user.
Open a second terminal.
Confirm the new user can log in with the key.
Only then disable root login and password authentication.
Validate the SSH config.
Reload SSH.
Test again from the second terminal before you close the original one.
Do not close your working session just because the config file looks right.
(Find your provider's web console before you need it. Akamai's is called Lish.)
Why these four come first
There are dozens of hardening steps you could take. These four come first because they shrink what the server will accept from strangers, and none of them needs extra software.
1. A non-root user with sudo
Routine admin work shouldn't mean logging in as root. A normal user with sudo separates everyday work from privileged actions. You choose when to elevate, and those actions get logged.
Sudo isn't magic. If someone gets that user's access, they can still do damage. But it means root no longer has to be reachable over SSH, and that's the step that matters here.
sudo adduser yourname
sudo usermod -aG sudo yournameSwap in your own username. adduser will ask you for a password. Keep it, because sudo will ask for it.
2. SSH key authentication
Keys take password guessing out of the normal login path. Bots that spray common passwords at SSH have nothing to guess, because they can't brute-force a private key over the network the way they can a weak password.
On your own computer (not the server), create a key if you don't have one:
ssh-keygen -t ed25519Give it a passphrase. That protects the key file if someone copies it, and it's why you'll see your local key ask to be unlocked when you connect.
Then the public key (the .pub file, never the private one) has to land in ~/.ssh/authorized_keys for your new user. On Linux, ssh-copy-id yourname@your_server_ip does it in one step. Windows' built-in OpenSSH normally doesn't include it.
On my Linode I'd already logged in as root with my key, so I copied root's file across from the server:
sudo mkdir -p /home/yourname/.ssh
sudo cp /root/.ssh/authorized_keys /home/yourname/.ssh/authorized_keys
sudo chown -R yourname:yourname /home/yourname/.ssh
sudo chmod 700 /home/yourname/.ssh
sudo chmod 600 /home/yourname/.ssh/authorized_keysIf your provider didn't put a key on root, the Akamai guide linked below shows how to add one manually.
Now the step people skip. Open a second terminal and log in as the new user:
ssh yourname@your_server_ip
sudo whoamiIf that logs you in with the key (it may ask for the key's passphrase, which is your local key unlocking, not the server asking for a password), and sudo whoami prints root after you enter your account password, keys and sudo both work. Leave both terminals open.
[IMAGE: 03-key-login-works.png]
3. Turn off direct root login and password login
Here's the part that catches people. On many Ubuntu systems, the main config file pulls in extra files from /etc/ssh/sshd_config.d/, and it does that at the top of the file. SSH uses the first value it finds for most settings. So a cloud-provided file in that folder can quietly overrule the setting you just edited further down in sshd_config.
You'd edit the file, it would look right, and the old behavior would keep running.
First, see what's already set and where:
sudo grep -rEi 'permitrootlogin|passwordauthentication' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/Note which file each line comes from. Lines starting with # are comments and do nothing. If a file in sshd_config.d/ sets either option, that file wins over the main one, so change it there.
On my test instance the sshd_config.d folder was empty, and the main file had both settings written out as yes. Yours may look different.
[IMAGE: 02-dropins.png]
Make a backup, then edit the main file:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo nano /etc/ssh/sshd_configFind the existing PermitRootLogin and PasswordAuthentication lines and change the values. If a line is commented out, remove the #. Change the existing lines instead of adding second copies, because SSH uses the first value it finds and a duplicate further down is ignored.
PermitRootLogin no
PasswordAuthentication noSave and exit. In nano that's Ctrl+O, Enter, then Ctrl+X.
If you'd rather keep your change separate from the main file, a file such as /etc/ssh/sshd_config.d/00-hardening.conf also works, because those files load first in alphabetical order and the name must end in .conf. I edited the main file, so that's the method I verified.
One caution. Keyboard-interactive authentication is a separate SSH mechanism from PasswordAuthentication, and some MFA/PAM setups intentionally use it. Verify your own effective settings rather than disabling something you depend on.
Now three separate steps, which people tend to blur together:
Validate the syntax:
sudo sshd -tNo output usually means the config passed. If it prints an error, fix it before going any further. Reloading a broken config is how lockouts happen.
Reload the service:
sudo systemctl reload sshOn Ubuntu the service is ssh, not sshd. (That trips up a lot of people coming from other distros.) This asks the running service to pick up the new config.
Check the effective configuration:
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication'Mine showed:
permitrootlogin no
passwordauthentication no[IMAGE: 05-after-sshd-T.png]
Editing, validating, reloading, and verifying are four different things. sshd -T shows the effective configuration SSH derives from the files. The connection tests below show what a client actually experiences. You want both.
4. Turn on the firewall
Ubuntu ships with UFW (Uncomplicated Firewall). It's usually installed but not turned on.
The order matters here too. Allow SSH before you enable the firewall. Get it backwards and the firewall can cut off the session you're working in.
sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status verboseOpenSSH is an app profile that covers port 22. Run sudo ufw app list if you want to see it. UFW will warn that enabling may disrupt existing SSH connections and ask you to confirm with y, which is exactly why the allow rule goes first.
My status output showed Status: active, Default: deny (incoming), allow (outgoing), and two lines allowing 22/tcp (OpenSSH) in, one for IPv4 and one for IPv6.
[IMAGE: ufw_allow_enable_status.png]
If you use a custom SSH port, allow that port instead before enabling UFW. The OpenSSH profile only covers 22.
UFW protects the host itself. Many providers also offer a network firewall in front of the server (Akamai's cloud firewalls, AWS security groups). Running both is fine. Just make sure they agree, or you'll spend an afternoon wondering why a port is "open" and still unreachable.
Prove it worked
This is the habit I most want you to build:
Configure. Validate. Test. Verify.
Editing a file is just the configure step. Here's how I checked the rest.
On the server, to see the effective configuration and the firewall state:
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication'
sudo ufw status verboseFrom the second terminal, on your own machine, against your own server:
Your user still gets in with a key:
ssh yourname@your_server_ipPassword login is refused. This tells SSH not to use your key, so it has to fall back to a password:
ssh -o PubkeyAuthentication=no yourname@your_server_ipYou want Permission denied (publickey) and no password prompt. That's what I got. If you still get a password prompt, run sudo sshd -T | grep kbdinteractive. Keyboard-interactive login can ask for a password even when PasswordAuthentication is off. Mine printed kbdinteractiveauthentication no, but that's why you test instead of assuming.
[IMAGE: pubkeyauthentication=no.png]
Root is refused:
ssh root@your_server_ipThat should be denied too.
[IMAGE: root failed.png]
Only after all three pass do I close the original session.
The one that surprised me
My first root test did not pass. The file said PermitRootLogin no, sshd -t was clean, I restarted SSH, and sshd -T printed no. Root still logged in with its key.
The config was fine. The problem was the process list:
ps -eo pid,lstart,cmd | grep '[s]shd'
sudo ss -tlnp | grep sshdThere were two sshd listeners sharing port 22. One had been started at 14:55, before I changed anything, and a restart hadn't removed it. Whichever listener accepted a connection decided the answer, so root could be refused on one attempt and accepted on the next.
[IMAGE: two-listeners-before-kill.png]
I checked that systemd's Main PID was the newer process, killed the old listener, and the refusals became consistent:
systemctl status ssh --no-pager | grep 'Main PID'
sudo kill <old_pid>[IMAGE: PID.png]
I can't tell you exactly how mine got stranded, and yours may never do it. But sshd -T was right the whole time. It only showed what the files said, and the login test showed what the server did. Don't kill anything until you've confirmed which process systemd is actually tracking.
These tests are for your own server. Doing this to someone else's is the same thing the bots in #011 were doing.
Back to the logs
So did the knocking stop? I waited about a day after hardening, then pulled the last 48 hours and counted:
sudo journalctl -u ssh --since "48 hours ago" --no-pager > ~/ssh48.log
grep -c "Failed password" ~/ssh48.logMine printed 0.
Don't read too much into that. About half of those 48 hours were before I changed anything, so I can't give hardening the credit. The 53 in #011 came mostly from five addresses over a week, and scanners show up in bursts. One quiet couple of days on one server is a data point, not a trend.
Then I looked at what else was in there:
grep -i "preauth" ~/ssh48.log | tail -n 20The last 20 [preauth] lines were my own test logins and refusals. Here's what a refused root login looked like (address removed):
sshd[69025]: ROOT LOGIN REFUSED FROM x.x.x.x port 64249 [preauth]
sshd[69025]: Connection reset by authenticating user root x.x.x.x port 64249 [preauth][IMAGE: journal and test.png]
That's one example of what a refusal looks like on this setup, not a rule. You may see different connection and [preauth] messages, depending on client behavior and your SSH configuration.
Hardening doesn't hide an internet-facing SSH port, so scanners can still find it and try. It changes what they find when they get there. And an empty grep "Failed password" result does not mean the server stopped receiving unwanted connections. Remember #011: no output doesn't mean nothing happened.
The bots can keep knocking. The goal is to make sure the easy doors aren't open.
Try This: a mini lab
Use a disposable Ubuntu VM, a cheap VPS, or a home-lab box. Not a server someone depends on. It took me about 35 minutes on a fresh Linode, including creating the server and recovering from a lockout I caused myself, and I had already done it once. Plan on an hour your first time.
Record the current effective SSH settings with
sshd -T.Create a non-root sudo user.
Set up SSH key login for that user.
Confirm a second login works before changing anything.
Disable root SSH login.
Disable password authentication.
Validate with
sudo sshd -t.Reload SSH.
Allow OpenSSH, then enable UFW.
Capture the final
sshd -Tandufw status verboseoutput.
Then write it up. Save the before output, the after output, the commands you ran, a few sentences on what changed and why, and the three login tests with their results.
This is the kind of small project you can document on GitHub and talk through in a technical interview: what you changed, how you avoided locking yourself out, and how you proved it worked. If something goes wrong along the way, write that down too, along with how you fixed it. "Here's what broke and how I fixed it" is often the best interview answer.
Learn it, practice it, troubleshoot it, fix it, verify it, document it. Then you can demonstrate it.
Worth Knowing
Akamai: Set up and secure a Linode
https://techdocs.akamai.com/cloud-computing/docs/set-up-and-secure-a-compute-instance
This is the provider guide I'd point a beginner to. It covers the limited user, SSH keys, SSH hardening, a firewall, and what to do if you lock yourself out. It's written for Akamai's platform, so some steps (like Lish console recovery) are specific to them. It's a good reference, not the only right way to do this.
Remote roles worth a look this week
Why spend your evenings on SSH configs and firewall rules? Because employers keep asking for these skills. Here are five current entry-to-mid-level remote roles where the fundamentals from this issue show up.
Listings can close quickly. I checked each one on the employer's own site while preparing this issue, but always check the employer's page before applying.
Cloud Services Ops, Associate Engineer, PTC
Remote U.S. | $55K-$65K (includes on-target incentive)
A practical bridge from help desk into cloud operations. The work is Linux-heavy application configuration, ticketing, incident priority, and tools like Apache, Tomcat, ServiceNow, and VMware. They prefer 2+ years of help-desk experience and basic Linux command-line skills. A bachelor's degree is required, along with the ability to obtain a security clearance, and the role includes occasional nights and weekends for maintenance.
Notice how Linux, troubleshooting, and incident handling show up together here.
Cloud Engineer, Associate, Purdue University
Remote for the right candidate | $82,560-$87,720
An early-career cloud role asking for a bachelor's degree and at least a year of cloud experience. It mentions Terraform or CloudFormation, CI/CD for infrastructure as code, and networking, with Git and cloud certifications preferred. Terraform being listed by name is a good argument for documenting your Infrastructure as Code projects.
Customer Support Engineer (Tier 1), Tailscale
Remote U.S. | $63K-$99K base + equity
A networking-focused support role at a company whose whole product is secure connections. They want DNS, IP addressing, firewalls, and routing fundamentals, troubleshooting across macOS, Windows, and Linux, and strong writing. The posting specifically mentions homelabs and personal projects as evidence of a tinkerer's mindset. Sound familiar?
Technical Support Engineer, Lumana
Remote, U.S. Eastern Time Zone (hybrid for NYC) | $70K-$90K + equity | 1-3 years
Hands-on troubleshooting across TCP/IP, DNS, DHCP, routing, firewalls, servers, and edge devices. Linux and Python are listed as nice-to-haves. Know before you apply: this is an evenings and weekends support position, and you must live in the Eastern Time Zone.
Technical Support Engineer L2, LucidLink
Remote U.S. | $72K-$90K base + equity + bonus eligibility | 2+ years
Support for a cloud file-streaming product across macOS, Windows, and Linux. The work includes reproducing customer issues, running live debug sessions, and collecting logs and debug data. TCP/IP knowledge is required, with cloud, networking, and scripting as pluses. Daytime U.S. hours.
What these have in common
Look at the five together and the same things keep coming up: Linux, networking basics, troubleshooting, reading logs, documenting your work, and explaining it clearly to someone else.
None of that guarantees a job. Nothing does. But these skills keep showing up because employers keep needing them, and every one of them is something you can practice on a cheap server or a free VM this weekend.
If you're building the fundamentals
I'm also building SecureByDefault Learning Paths, a free GitHub repo that organizes Linux, networking, troubleshooting, cloud, hands-on projects, and career paths into a learning order. It's a work in progress and I add to it regularly.
Your next step
New to Linux?
The free Linux Starter Kit includes the commands, a practice plan, log examples, and a beginner server checklist. It's a good companion to this issue's lab.
If someone forwarded you this issue, you can subscribe here: https://newsletter.securebydefault.io/
Next issue
We locked down what SSH accepts. But SSH probably isn't the only thing listening on your server.
Next time: what's actually exposed on a Linux server, how to see every open port with one command, and how to tell whether it's reachable from the internet or only from inside the machine.
Talk soon,
Ron
SecureByDefault


