Skip to content
The 31 guidesFREN中文
Guides
Part 3 · guide 2 of 4 Level: Advanced Reading time: 20 min Platforms: Linux and Mac

Securing access

A machine reachable from anywhere, running code generated by an AI: security isn't optional. SSH keys, firewall, secrets, agent permissions. How not to shoot yourself in the foot.

In this guide
  1. 011. SSH: keys, never passwords
  2. 022. The firewall: everything closed, except the essentials
  3. 033. Secrets: never in plain text, never in the code
  4. 044. Keeping the agent in check: it executes, you decide
  5. 055. Keeping the machine up to date (without thinking about it)
  6. 066. The safety net: backups
  7. 07Frequently asked questions

In short

To secure a mini-PC that is reachable from anywhere and driven by an AI agent: SSH with keys only (passwords off, root login refused), a ufw firewall closed by default, no raw service exposed on the Internet (Tailscale for private, Cloudflare Tunnel for public) and secrets kept out of git, in a .env file set to chmod 600. The agent runs as your user, without automatic sudo, inside the project folder, with its permission rules or its sandbox turned on. Add automatic security updates and backups whose restore you have actually tested.

Do this first: Installing the agent (very early)

Let’s recap what you’ve built: a machine that runs 24/7, reachable from your phone, exposing services on the Internet, and running commands proposed by an AI. That’s awesome. It’s also an attack surface you need to take seriously, not out of paranoia, out of hygiene.

The good news: 90% of security comes down to a handful of simple gestures, done once. This guide is those gestures. None of them is hard. Skipping them, on the other hand, can turn your workshop into a spam relay or worse.

1. SSH: keys, never passwords

Remote access goes through SSH. An exposed SSH password is the number-one attack on the Internet, bots test millions of combinations per day. The defense is definitive: you disable password authentication and switch to keys.

An SSH key is a pair: a private part that stays on your laptop (never anywhere else), and a public part you drop on the machine. Without the private key, getting in is impossible, even knowing your username.

0 of 3 steps done Your ticks stay in this browser.

  1. Generate a key on your computer (not on the mini-PC)

    # On your laptop. Ed25519 = modern, short, solid.
    ssh-keygen -t ed25519 -C "laptop-vers-minipc"
    

    Leave the default path, set a passphrase (it’s the last line of defense if your laptop gets stolen).

  2. Drop the public key on the machine

    ssh-copy-id ulrich@adresse-de-la-machine
    

    Test the connection: ssh ulrich@adresse-de-la-machine should log in without asking for a password.

  3. Turn off passwords

    On the mini-PC, edit /etc/ssh/sshd_config (your agent can do it, show it what you want):

    PasswordAuthentication no
    PermitRootLogin no
    

    Then reload: sudo systemctl restart ssh. Keep a session open while you verify that a fresh key-based connection works, just in case.

    One trap: on Ubuntu, sshd_config starts by including the files in /etc/ssh/sshd_config.d/, and the first value read wins. A file dropped there, like the 50-cloud-init.conf an Ubuntu Server install sometimes leaves behind, can quietly turn passwords back on. Check the configuration actually in force:

    # Should print "passwordauthentication no" and "permitrootlogin no"
    sudo sshd -T | grep -Ei '^(passwordauthentication|permitrootlogin) '
    

2. The firewall: everything closed, except the essentials

By default, you block everything incoming, and open up sparingly. ufw makes this trivial.

sudo ufw default deny incoming      # refuse everything by default
sudo ufw default allow outgoing     # the machine can reach out
sudo ufw allow 22/tcp               # SSH (or nothing if you go through Tailscale)
sudo ufw enable
sudo ufw status verbose             # check

Two commands to see where you stand:

# what listens on every interface, so reachable from outside the machine
sudo ss -ltnp | grep -E "0\.0\.0\.0:|\[::\]:"

# is ufw really filtering IPv6? (should print IPV6=yes)
grep IPV6 /etc/default/ufw

3. Secrets: never in plain text, never in the code

API keys, tokens, service passwords: these are secrets. The rule is absolute: a secret never lives in plain text in the code, nor in a file versioned by git.

0 of 3 steps done Your ticks stay in this browser.

  1. Store secrets in a .env file outside git

    # A .env file at the project root
    echo "ANTHROPIC_API_KEY=sk-..." >> .env
    # And ABOVE ALL, exclude it from git
    echo ".env" >> .gitignore
    

    The code reads the environment variable, it never contains the value.

  2. Lock down the permissions of sensitive files

    chmod 600 .env ~/.ssh/id_ed25519   # readable by you alone
    
  3. Rotate anything that leaked

    If a secret has been lying around somewhere (a commit, a copy-paste, a screenshot), consider it compromised and regenerate it. An API key is revoked and recreated in two clicks. Better a rotation for nothing than a leak you ignored.

Where to actually store your keys

The .env file is enough to get started, but as soon as you accumulate keys (AI APIs, GitHub tokens, service access), ask yourself the question of the right vault. There’s a little hierarchy, from simplest to most robust:

  • The per-project .env file: the starting point. Outside git, with chmod 600. Perfect for a personal project. Its limit: the secret sits in plain text on disk, and it gets duplicated from project to project.
  • The keychain / password manager: for your personal stash of keys (the source of truth), a real manager (Bitwarden, 1Password, KeePassXC…) encrypted beats a thousand scattered text files. You pull from it to fill a .env when you need to.
  • An encrypted-at-rest vault: to store secrets inside a repository without exposing them, tools like sops + age or pass encrypt the values: the file can be versioned, only whoever holds the decryption key reads it. The pro step when a project grows or gets shared.
  • A dedicated secrets manager: for serious multi-machine setups (Vault, Infisical, Doppler…), a service centralizes, audits, and rotates secrets. Probably beyond your needs at the start, but that’s where the path leads.

4. Keeping the agent in check: it executes, you decide

This is the part specific to our setup, and the most important. A code agent is powerful because it can run commands. That’s also what makes it dangerous if it runs off unsupervised. A few common-sense rules:

  • Never run the agent as root, never on automatic sudo. It runs under your normal user. When a command needs sudo, you want to see it and approve it by hand. A destructive command run as root doesn’t forgive.
  • “Auto-approve” mode has to be earned. Agents often offer a mode where they execute without asking. Great for iterating on tests; to be banned for anything touching the network, secrets, file deletions (rm), or prod.
  • Work in a project folder, not in ~ or /. Limit the agent’s blast radius to the project folder. If it goes off the rails, the damage stays contained.
  • Generated code gets reviewed before it runs in prod. An agent can introduce a flaw without meaning to (a poorly escaped SQL query, an over-broad permission). A review, by you, or by a second agent in “review” mode, is not a luxury.
  • git is your safety net. Frequent commits. You can always roll back if the agent did something dumb. A project with no git history is a trapeze artist with no net.

5. Keeping the machine up to date (without thinking about it)

A patched flaw is no longer a flaw, provided you install the patch. You automate security updates:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades   # answer "Yes"

The machine now applies security patches on its own. For the rest (version upgrades), a sudo apt update && sudo apt upgrade now and then is enough, your agent can remind you.

6. The safety net: backups

Security isn’t just blocking intruders. It’s also surviving your own mistake, a disk that gives out, an over-eager agent. Back up what matters (your projects, your configs, your .env, encrypted) somewhere other than the machine: another disk, a NAS, remote storage. A backup you’ve never tested restoring is not a backup.

Frequently asked questions

Why does SSH still accept passwords after I turned them off?

On Ubuntu, sshd_config starts by including the files in /etc/ssh/sshd_config.d/, and the first value read wins. A file sitting there, like the 50-cloud-init.conf that an Ubuntu Server install sometimes leaves behind, can turn passwords back on behind your back. Check the configuration actually in effect with sudo sshd -T, which should show passwordauthentication no and permitrootlogin no.

Does the ufw firewall protect Docker containers?

No. When you publish a port with -p 8080:8080, Docker writes its own rules ahead of ufw's, and the container stays reachable from the whole local network even with the firewall on. The fix lies in the listening address: with -p 127.0.0.1:8080:8080, the service can only be reached from the machine itself.

Is my mini-PC exposed to the Internet over IPv6?

It can be. IPv6 has no NAT: the machine has its own public address, and a service listening on [::] can be reached directly from the Internet. What protects you then is the router's IPv6 firewall, on by default on most models. Also check that ufw filters IPv6: the /etc/default/ufw file should contain IPV6=yes.

Where should I keep my API keys once they start piling up?

A per-project .env file, kept out of git and set to chmod 600, is enough to start. For your personal stash of keys, an encrypted password manager such as Bitwarden, 1Password or KeePassXC serves as the source of truth. To version secrets inside a repository, sops with age or pass encrypt them, and across several machines a dedicated manager such as Vault, Infisical or Doppler centralizes and rotates them.

Can I give my agent an API key by pasting it into the chat?

Better not: a secret that passes through a conversation is one more secret to watch. Teach the agent to read the key from an environment variable instead, and add a rule to your CLAUDE.md or AGENTS.md so it never writes a secret in plain text. If a key has already been lying around somewhere, treat it as compromised and regenerate it.

Terms in this guide: SSHSSH keyAPI keyTokenGitRepository (repo)AgentCommit

Spotted a mistake?

A command stopped working, a price changed?

Tools change every month. Tell me what is wrong in this chapter and I will fix it and update its date.

Only the page, your message and the optional contact are kept. Nothing else.

Guide 16 of 31 · part 3 no guides read yet Open the list of guides