LowEndBox - Cheap VPS, Hosting and Dedicated Server Deals

Locking Down Access to Your VPS

Locked DoorThere are a number of ways you can restrict access to your VPS. Passwords (specifically, good passwords) is the most basic method. Restricting access to ssh keys only is better. You can use Google Authenticator to require a short-lived number as a second factor of authentication. You could also setup a VPN so that only connections from that network are allowed.

One alternative method I’ve sometimes used is to allow connections only from a specific IP or set of IPs. There are a couple different ways to achieve this.

Restricting in sshd_config

In the sshd_config file, you can add rules to match users and match addresses. Consider the following directives

PermitRootLogin no
PermitEmptyPasswords no
PasswordAuthentication no
PubkeyAuthentication no
Match Address
PermitRootLogin prohibit-password
PubkeyAuthentication yes

The first four lines turn off all forms of authentication and restrict root logins. Then from the IP address, we permit root login (by SSH key only) and allow sshd key authentication for other users. This combination of “default deny, then permit from a specific IP” effectively limits connections from one IP.

sshd_config supports a number of “Match” rules, including “Match User” and “Match Host”. Most of the major configuration options can be overridden using Match directives. Consult the sshd_config man page for full details.

Restricting via Firewall

If you are connecting to things besides ssh, you can restrict the entire box’s access to an IP via a firewall. One wrinkle is if you want to limit access to your home IP, but you have a DHCP’d IP from your ISP.

One way around this is to sign up with a Dynamic DNS provider. For example, you can sign up with afraid.org and they will grant you (for free) a DNS name in one of their domains you pick. So if you pick example.com (not a real afraid.org domain, just an example), you then choose what subdomain you have. In this tutorial, we’ll assume I’ve chosen lowend.example.com.

If your router (Netgear, etc.) has a Dynamic DNS client for afraid.org, just configure it in the router’s panel and you’re done. If not, you can use this little script to update afraid.org:

#FreeDNS updater script


registered=$(nslookup $DOMAIN|tail -n2|grep A|sed s/[^0-9.]//g)
current=$(wget -q -O - http://checkip.dyndns.org|sed s/[^0-9.]//g)
if [ "$current" != "$registered" ] ; then
  wget -q -O /dev/null $UPDATEURL 
  MSG="FYI, DNS updated for ${DOMAIN}, now $current"
  echo "${MSG}" | mailx -s "${MSG}" someone@example.com

Put that in any user’s crontab on a home Linux box to run (perhaps every 12 hours, or more frequently if you need constant access). Every time it runs, it checks what the IP address is at afraid.org, then checks what the Internet IP is for your home. If they’re different, it updates afraid.org.

Now, on the server you want to lock down, all you need is a script to query afraid.org and update your iptables rules. Here is an example:


# fix the PATH since we run from cron
export PATH




nslookup $dns > /tmp/nslookup.out 2>&1
home_ip=$(grep Address: /tmp/nslookup.out | tail -1 | awk -F: '{ print $2 }' | sed 's/ //')

echo "----------------------------------------------------------" > ${LOG}
echo "`date` updating iptables using home IP ${home_ip}" >> ${LOG}

# make sure we can stay connected
iptables -P INPUT ACCEPT >> $LOG 2>&1

# flush existing rules
iptables -F >> $LOG 2>&1

# anything on loopback is OK
iptables -A INPUT -i lo -j ACCEPT >> $LOG 2>&1

# anything for established/related connections is OK
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT >> $LOG 2>&1

# open SSH
iptables -A INPUT -s ${home_ip} -p tcp --dport ${ssh_port} -j ACCEPT >> $LOG 2>&1

# open web
iptables -A INPUT -s ${home_ip} -p tcp --dport 80 -j ACCEPT >> $LOG 2>&1
iptables -A INPUT -s ${home_ip} -p tcp --dport 443 -j ACCEPT >> $LOG 2>&1

# otherwise drop
iptables -P INPUT DROP >> $LOG 2>&1

# drop all forwards - we're not a router
iptables -P FORWARD DROP >> $LOG 2>&1

# anything outgoing is OK
iptables -P OUTPUT ACCEPT >> $LOG 2>&1

# list at end
iptables -L -v >> $LOG 2>&1

What this script does is:

  • looks the current address of your afraid.org dynamic DNS
  • flushes all iptables rules
  • allows everything on loopback, and anything established
  • opens ssh, and web (ports 80 and 443) to the dynamic DNS IP only
  • drops everything else and disable routing
  • logs all actions to /root/iptables_update.log.

If you put this in cron to run at the same time as your dynamic dns updater script (perhaps offset by a couple minutes), your server will always have your home IP address in its iptables rules.

Of course, if you don’t use dynamic DNS, you can just hardwire an IP for the home_ip variable.

As a bit of bonus advice, whenever I’m developing an iptables ruleset, I usually create a script like this called “flush_iptables.sh” and have it called out of cron every couple minutes.


export PATH

iptables -P INPUT ACCEPT 
iptables -F 
echo "warning! firewall disabled on $(hostname)" |mailx -s "firewall disabled on $(hostname)!" someone@example.com

Update your email address, and make sure you have mailx installed (it’s in the mailutils package on Debian).

This way, if I screw something and am locked out, the lockout will reverse itself after a couple minutes and I don’t have to go through the hassle of finding the provider’s console. It also emails me (every couple minutes) to warn me that it’s in place so I don’t forget to disable it once my rules are set.


No Comments

    Leave a Reply

    Some notes on commenting on LowEndBox:

    • Do not use LowEndBox for support issues. Go to your hosting provider and issue a ticket there. Coming here saying "my VPS is down, what do I do?!" will only have your comments removed.
    • Akismet is used for spam detection. Some comments may be held temporarily for manual approval.
    • Use <pre>...</pre> to quote the output from your terminal/console, or consider using a pastebin service.

    Your email address will not be published. Required fields are marked *