There 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 1.2.3.4/32 PermitRootLogin prohibit-password PubkeyAuthentication yes
The first four lines turn off all forms of authentication and restrict root logins. Then from the IP address 1.2.3.4, 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:
#!/bin/bash #FreeDNS updater script UPDATEURL="http://freedns.afraid.org/dynamic/update.php?YOUR-AFRAID-ORG-KEY-HERE" DOMAIN="lowend.example.com" 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 fi
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:
#!/bin/bash # fix the PATH since we run from cron PATH=/usr/bin:/sbin:/bin:usr/sbin:/usr/local/bin export PATH # CONFIG ssh_port=5555 dns="lowend.example.com" LOG="/root/iptables_update.log" # MAIN 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.
#!/bin/bash PATH=/usr/bin:/sbin:/bin:usr/sbin:/usr/local/bin 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.
Related Posts:
- One Week From Tomorrow…THE WORLD WILL LOSE THEIR MINDS!Lines Are Already Forming! - November 21, 2024
- Crunchbits Discontinuing Popular Annual Plans – The Community Mourns! - November 20, 2024
- RackNerd’s Black Friday 2024: Bigger, Better, and Now in Dublin! - November 19, 2024
Leave a Reply