Unix Networking and Security
Most of this is learned by breaking things. SSH config in particular has a way of locking you out of a remote box at the worst possible time. These are the commands and concepts I actually use, with the caveats that took trial and error to internalize.
Checking What's Listening
Before anything else — know what's running on your machine:
$ ss -tlnp # TCP, listening, numeric, show process — the modern tool $ ss -ulnp # UDP version $ netstat -tlnp # older alternative (same idea, on systems without ss) $ lsof -i :22 # which process is using a specific port
ss replaces netstat on modern Linux. It's faster and shows more information. Both are available on most systems.
Network Interface Management
The ip command is the modern tool. ifconfig still works but has been deprecated for years:
| Command | Purpose |
|---|---|
ip addr show | All interfaces and their addresses |
ip addr show eth0 | Specific interface |
ip link set eth0 up | Bring interface up |
ip link set eth0 down | Bring interface down |
ip addr add 192.168.1.100/24 dev eth0 | Temporary address (lost on reboot) |
ip addr del 192.168.1.100/24 dev eth0 | Remove an address |
ip route show | Routing table |
ip route add default via 192.168.1.1 | Set default gateway |
SSH — Key-Based Auth
Password-based SSH auth is a security liability on any internet-facing server. Keys are the right answer:
# Generate a key — ed25519 is better than the old RSA default
$ ssh-keygen -t ed25519 -C "jason@hostname"
# Copy public key to the remote server (requires password login once)
$ ssh-copy-id user@remote_host
# Or manually — useful when ssh-copy-id isn't available
$ cat ~/.ssh/id_ed25519.pub | ssh user@host \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys"
Hardening sshd_config
Edit /etc/ssh/sshd_config. Always keep a second SSH session open while testing — if you lock yourself out of a remote machine, it's a datacenter trip or a support ticket:
PermitRootLogin no # never log in as root directly PasswordAuthentication no # keys only PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys AllowUsers jason # whitelist specific users MaxAuthTries 3 # limit brute-force attempts LoginGraceTime 30 # close unauthenticated connections quickly
# Test the config before reloading — catches syntax errors $ sshd -t # Reload (not restart — keeps existing sessions alive) $ systemctl reload sshd
SSH Client Config (~/.ssh/config)
Saves a lot of typing and enables advanced workflows like jump hosts:
Host myserver
HostName 203.0.113.10
User jason
IdentityFile ~/.ssh/id_ed25519
Port 2222
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/id_ed25519
Host *.internal
User admin
ProxyJump bastion # tunnel through bastion host automatically
After this, ssh myserver just works. ssh app01.internal automatically tunnels through the bastion without any extra flags.
Firewalls
ufw (Ubuntu/Debian)
$ ufw status verbose $ ufw allow 22/tcp $ ufw allow 80/tcp $ ufw allow 443/tcp $ ufw allow from 192.168.1.0/24 to any port 5432 # postgres, local net only $ ufw deny 23/tcp # block telnet $ ufw enable $ ufw reload
iptables
$ iptables -L -n -v # list rules with packet counts $ iptables -A INPUT -p tcp --dport 22 -j ACCEPT $ iptables -A INPUT -p tcp --dport 80 -j ACCEPT $ iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT $ iptables -P INPUT DROP # default deny # Persist across reboots $ iptables-save > /etc/iptables/rules.v4 $ iptables-restore < /etc/iptables/rules.v4
Diagnostic Tools
| Command | Purpose |
|---|---|
ping -c 4 8.8.8.8 | Basic reachability |
traceroute 8.8.8.8 | Path packets take |
mtr 8.8.8.8 | Live updating traceroute — better than traceroute |
dig google.com | DNS lookup |
dig @8.8.8.8 google.com | DNS lookup against a specific resolver |
curl -I https://example.com | HTTP headers only |
curl -v https://example.com | Full HTTP request/response details (includes TLS handshake) |
tcpdump -i eth0 port 80 | Capture HTTP traffic (needs root) |
tcpdump -i eth0 -w dump.pcap | Save capture to file for Wireshark analysis |
Fail2ban
Any SSH port exposed to the internet will get constant brute-force attempts. Fail2ban watches your logs and bans IPs that fail too many times:
$ apt install fail2ban $ cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
In /etc/fail2ban/jail.local:
[sshd] enabled = true port = ssh maxretry = 5 bantime = 3600 # ban for 1 hour findtime = 600 # count failures within 10-minute window
$ systemctl enable --now fail2ban $ fail2ban-client status sshd # current bans $ fail2ban-client set sshd unbanip 1.2.3.4 # unban an IP
SSH Tunnels
# Local port forwarding — access remote_host:5432 (Postgres) as localhost:5432 $ ssh -L 5432:localhost:5432 user@remote_host # Remote port forwarding — expose local port 8080 on the remote server $ ssh -R 8080:localhost:8080 user@remote_host # Dynamic SOCKS proxy — route traffic through remote_host $ ssh -D 1080 user@remote_host # Then configure your browser to use SOCKS proxy at localhost:1080 # Keep tunnels alive $ ssh -N -f -L 5432:localhost:5432 user@remote_host # -N: don't execute commands, -f: go to background
dispelled