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:

CommandPurpose
ip addr showAll interfaces and their addresses
ip addr show eth0Specific interface
ip link set eth0 upBring interface up
ip link set eth0 downBring interface down
ip addr add 192.168.1.100/24 dev eth0Temporary address (lost on reboot)
ip addr del 192.168.1.100/24 dev eth0Remove an address
ip route showRouting table
ip route add default via 192.168.1.1Set 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

CommandPurpose
ping -c 4 8.8.8.8Basic reachability
traceroute 8.8.8.8Path packets take
mtr 8.8.8.8Live updating traceroute — better than traceroute
dig google.comDNS lookup
dig @8.8.8.8 google.comDNS lookup against a specific resolver
curl -I https://example.comHTTP headers only
curl -v https://example.comFull HTTP request/response details (includes TLS handshake)
tcpdump -i eth0 port 80Capture HTTP traffic (needs root)
tcpdump -i eth0 -w dump.pcapSave 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

References