How to Build a Secure Remote Access Server

A Debian-first guide to securely reaching your system from outside your network. The main build uses WireGuard for the private network path and SSH for the actual administration session. The goal is simple: remote access without careless exposure.

What this is

A remote access server gives you a secure way to reach your system when you are not physically present. In practical terms, that means remote administration, troubleshooting, and file management.

This guide intentionally separates remote access from public exposure. You are not going to just throw SSH onto the open internet and hope for the best. You will build a private access path first, then use SSH through that path.

Why build this?

Why this matters:

• You can manage your system from outside your home without relying on unsafe shortcuts.

• You reduce the chance of exposing SSH broadly to the public internet.

• You build a reusable foundation for later services like a NAS, private dashboards, and internal admin tools.

• You learn real Debian, Linux account, SSH, VPN, and firewall practices while building something useful.

Design

Recommended approach

WireGuard VPN + SSH over the VPN

You connect to a private VPN first, then SSH to the server using its VPN address.

Why: This avoids the lazy and risky habit of exposing SSH directly to the world.

Important

Scope of this build

This setup is intentionally limited to accessing the server itself. It does not route your entire home network through the VPN.

Why: That keeps the build simpler and avoids the extra complexity of IP forwarding, route design, and broader firewall policy. This guide is about secure access to the server, not full remote LAN access.

What you need

Minimum requirements

For the free self-hosted path:

• A Debian machine you control

• A stable internet connection

• Router access if manual port forwarding is needed

• A client device such as a laptop or phone

For the easier managed path:

• The same Debian machine

• A Tailscale account

• Client devices joined to the same secure network

Why this matters: Secure remote access is not just one package. It is identity, authentication, network path, and firewall policy all working together.

Free Build

Build a private access path with WireGuard, then use SSH through it

Step 1: Update the system

sudo apt update
sudo apt full-upgrade -y

What this does:
sudo apt update refreshes Debian’s package list. sudo apt full-upgrade -y installs the currently available upgrades.

Why this matters:
You do not want to build remote access on top of stale software.

What to expect:
Package lists download, then upgrades install if any are available.

Step 2: Install the required packages

sudo apt install -y wireguard wireguard-tools openssh-server ufw qrencode

What this does:
Installs WireGuard, the WireGuard command-line tools, the OpenSSH server, UFW, and the optional QR code tool.

Why this matters:
WireGuard provides the private network path. OpenSSH provides the administration session. UFW gives you host firewall control.

What to expect:
Debian downloads and installs the packages. This may take a minute or two.

Step 3: Enable SSH

sudo systemctl enable --now ssh
sudo systemctl status ssh

What this does:
Starts the SSH service immediately and makes sure it starts automatically on boot.

Why this matters:
SSH is the actual remote administration service in this design.

What to expect:
The status output should show the service as active and running.

Step 4: Create a dedicated admin user

sudo adduser adminops
sudo usermod -aG sudo adminops

What this does:
Creates a normal user account and adds it to the sudo group.

Why this matters:
Remote administration should not happen through direct root login.

What to expect:
You will be prompted to set a password and basic account information.

Step 5: Generate an SSH key on the client device

ssh-keygen -t ed25519 -C "remote-admin-key"

What this does:
Creates a public/private SSH key pair on the machine you will connect from.

Why this matters:
SSH key authentication is stronger and cleaner than relying on passwords.

What to expect:
You will be asked where to save the key and whether to use a passphrase. A passphrase is strongly recommended.

Step 6: Copy your public key to the server

ssh-copy-id adminops@SERVER-IP

What this does:
Installs your public key into the server account’s authorized keys.

Why this matters:
This is what allows you to log in with your SSH key.

What to expect:
After this, you should be able to authenticate with your key instead of a password.

Step 7: Harden SSH with a drop-in config

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Paste:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AllowUsers adminops
sudo systemctl reload ssh

What this does:
Adds a small override file for SSH settings, then reloads the SSH service.

Why this matters:
PermitRootLogin no disables direct root login
PasswordAuthentication no disables password-based SSH logins
KbdInteractiveAuthentication no closes another password-like path
PubkeyAuthentication yes keeps key auth enabled
AllowUsers adminops limits SSH access to the intended account

What to expect:
SSH reloads quietly if the file is valid.

Step 8: Test SSH before doing anything else

ssh adminops@SERVER-IP

What this does:
Tests whether SSH key-based login works correctly.

Why this matters:
You should always confirm admin access works before tightening access further.

Step 9: Create the WireGuard directory and generate keys

sudo mkdir -p /etc/wireguard
cd /etc/wireguard
umask 077
wg genkey | sudo tee server.key | wg pubkey | sudo tee server.pub
wg genkey | sudo tee client.key | wg pubkey | sudo tee client.pub

What this does:
Creates the WireGuard config directory, locks down default file permissions with umask 077, then generates server and client key pairs.

Why this matters:
WireGuard authenticates peers using public/private key pairs.

What to expect:
Four key files should appear: server.key, server.pub, client.key, and client.pub.

Step 10: Create the WireGuard server config

sudo nano /etc/wireguard/wg0.conf

Paste this template:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = YOUR_REAL_SERVER_PRIVATE_KEY

[Peer]
PublicKey = YOUR_REAL_CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32

Important:
Replace YOUR_REAL_SERVER_PRIVATE_KEY and YOUR_REAL_CLIENT_PUBLIC_KEY with the actual contents of your key files. Do not paste the placeholder text literally.

sudo chmod 600 /etc/wireguard/wg0.conf

What this does:
Creates the WireGuard server configuration and then restricts the file so only root can read it.

Why this matters:
The config contains the server’s private key and should not be broadly readable.

Why the addresses matter:
10.8.0.1/24 is the server’s VPN address
10.8.0.2/32 is the client’s assigned VPN address
• This setup is intentionally limited to reaching the server, not the entire LAN

Step 11: Start WireGuard and verify the interface

sudo systemctl enable --now wg-quick@wg0
sudo systemctl status wg-quick@wg0
ip a

What this does:
Starts the WireGuard tunnel, enables it at boot, checks service status, and then shows network interfaces.

Why this matters:
You want to confirm that the VPN interface actually exists before moving on.

What to expect:
You should see a wg0 interface with the address 10.8.0.1/24.

Step 12: Configure the firewall

sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw enable
sudo ufw status verbose

What this does:
Allows SSH temporarily for setup, allows WireGuard’s UDP port, enables the firewall, and shows the resulting rules.

Why this matters:
You need the VPN port open, and you want to keep admin access while you finish testing.

What to expect:
UFW should show rules for SSH and UDP port 51820.

Step 13: If needed, forward UDP 51820 on your router

If your server is behind a typical home router, you will usually need to forward external UDP port 51820 to the Debian server’s LAN IP.

Why this matters: Outside clients need a path to reach the WireGuard server. Some environments, such as CGNAT or managed networks, may require a different approach.

Step 14: Build the client config

Use this template on the client device:

[Interface]
Address = 10.8.0.2/24
PrivateKey = YOUR_REAL_CLIENT_PRIVATE_KEY

[Peer]
PublicKey = YOUR_REAL_SERVER_PUBLIC_KEY
Endpoint = YOUR_PUBLIC_IP_OR_DOMAIN:51820
AllowedIPs = 10.8.0.1/32
PersistentKeepalive = 25

What this does:
Defines how the client reaches the WireGuard server and what traffic is allowed through the tunnel.

Why this matters:
Address = 10.8.0.2/24 gives the client its VPN IP
Endpoint tells the client where the server lives
AllowedIPs = 10.8.0.1/32 keeps this build focused on reaching only the server
PersistentKeepalive = 25 helps clients behind NAT stay reachable

Step 15: Optional phone onboarding

qrencode -t ansiutf8 < client.conf

What this does:
Converts a client config file into a terminal QR code.

Why this matters:
It can be faster to import a WireGuard config into a mobile app by scanning it.

Step 16: Test the VPN and SSH through it

ping 10.8.0.1
ssh adminops@10.8.0.1

What this does:
Tests the VPN path first, then tests SSH through the VPN.

Why this matters:
This confirms the actual design works: private tunnel first, SSH second.

Step 17: Restrict SSH to the VPN only

Warning: Only do this after you have confirmed the VPN works reliably. Keep a backup access method available, such as local console access or an already-open session.

sudo ufw delete allow OpenSSH
sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp
sudo ufw status numbered

What this does:
Removes the broad SSH rule and replaces it with a rule that only allows SSH from inside the WireGuard subnet.

Why this matters:
This is the real payoff of the design. SSH is no longer generally exposed. It is only reachable through the VPN.

Managed Option

Tailscale for an easier path

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

What this does:
Installs Tailscale and then brings the node online in your tailnet.

Why this matters:
This path is easier for people who want secure remote access without manually handling router forwarding and WireGuard peer setup.

What to expect:
After tailscale up, you will authenticate and the node will join your secure Tailscale network. You can then SSH to the Tailscale IP or MagicDNS name instead of the normal LAN IP.

Hardening

Minimum security habits to follow

Keep Debian updated

sudo apt update
sudo apt full-upgrade -y

Why: Most attacks target known vulnerabilities that already have patches.

Use SSH keys only

Once key-based access is confirmed, password-based SSH access should stay disabled.

Why: Password logins are a common brute-force target.

Do not leave SSH broadly exposed

Once the VPN is working, SSH should only be reachable through the VPN network.

Why: This reduces attack surface significantly.

Limit access to intended users

Use settings like AllowUsers to keep SSH access limited to the accounts you actually want remotely available.

Why: Good access control is part of good security.

Monitor service health

sudo systemctl status ssh
sudo systemctl status wg-quick@wg0

Why: Knowing when a service fails is part of operating it safely.

Keep a backup access path during setup

Do not remove your last working admin path until you have tested the new one.

Why: Misconfiguration can lock you out.

Testing

Make sure it actually works

• Verify SSH is running

• Verify WireGuard is running

• Confirm the VPN client can reach 10.8.0.1

• Confirm SSH works over the VPN

• Reboot the system and confirm both services come back

sudo systemctl status ssh
sudo systemctl status wg-quick@wg0
sudo reboot

Why this matters:
A remote access build is not finished until it still works after a reboot.

What comes next

Natural follow-up builds

• A Debian NAS you can reach privately

• A personal cloud that is only reachable through your secure access path

• A monitoring stack that stays off the public internet

• A later guide for secure access into internal services beyond just the server itself