[SOLVED] Creating Custom SSH iptable rules for use with UFW
Linux - SecurityThis forum is for all security related questions.
Questions, tips, system compromises, firewalls, etc. are all included here.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Creating Custom SSH iptable rules for use with UFW
Hi,
I'm trying to set up a firewall at the moment that allows access to my custom SSH port from only my friend's url (they have a static url but dynamic IP). I find iptables a bit of a nightmare and was hoping to use UFW for most of my day to day firewall maintenance and just make a few extra iptable rules to cover exceptional circumstances like this. Fortunately it seems UFW allows this with /etc/ufw/before.rules and /etc/ufw/after.rules.
So at the moment I'm just trying to get the basic iptables rules right. As I say I'm not very good with iptables, does this look right?
Code:
## Drop Default SSH port access With Logging
iptables -N SSH_DEFAULT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSH_DEFAULT
iptables -A SSH_DEFAULT -m recent --set --name SSH -j LOG --log-prefix "SSH_default_port"
iptables -A SSH_DEFAULT -m recent --set --name SSH -j DROP
## Drop Custom SSH port access With Logging
iptables -N SSH_CUSTOM
iptables -A INPUT -p tcp --dport 118118 -m state --state NEW -j SSH_CUSTOM
iptables -A SSH_CUSTOM -m recent --set --name SSH -j LOG --log-prefix "SSH_friend_port"
iptables -A SSH_CUSTOM -m recent --set --name SSH -j DROP
## Allow Custom SSH port from allowed URL with brute force logging
iptables -N SSH_FRIEND
iptables -A SSH_CUSTOM -s my.friends.url.net -j SSH_FRIEND
iptables -A SSH_FRIEND -m recent --set --name SSH -j ACCEPT
iptables -A SSH_FRIEND -m recent --update --seconds 60 --hitcount 4 --name SSH -j LOG --log-prefix "SSH_friend_port_brute_force "
iptables -A SSH_FRIEND -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
(edit) I thought this was the easy bit!
Oh, well, I've been looking into haw to convert the basic itables to a UFW before.rules. I found that before.rules uses iptables-restore syntax. So I've had a shot at converting the code above to before.rules. (Bearing in mind that I don't know yet whether the code above is right, without some help.)
Code:
#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
# ufw-before-input
# ufw-before-output
# ufw-before-forward
#
# Don't delete these required lines, otherwise there will be errors
*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]
# End required lines
:SSH_DEFAULT - [0:0]
:SSH_CUSTOM - [0:0]
:SSH_FRIEND - [0:0]
# End Additional chains
# allow all on loopback
-A ufw-before-input -i lo -j ACCEPT
-A ufw-before-output -o lo -j ACCEPT
# quickly process packets for which we already have a connection
-A ufw-before-input -m state --state RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-output -m state --state RELATED,ESTABLISHED -j ACCEPT
# drop INVALID packets (logs these in loglevel medium and higher)
-A ufw-before-input -m state --state INVALID -j ufw-logging-deny
-A ufw-before-input -m state --state INVALID -j DROP
# ok icmp codes
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-input -p icmp --icmp-type source-quench -j ACCEPT
-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT
# allow dhcp client to work
-A ufw-before-input -p udp --sport 67 --dport 68 -j ACCEPT
#
# ufw-not-local
#
-A ufw-before-input -j ufw-not-local
# if LOCAL, RETURN
-A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN
# if MULTICAST, RETURN
-A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN
# if BROADCAST, RETURN
-A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN
# all other non-local packets are dropped
-A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny
-A ufw-not-local -j DROP
# allow MULTICAST, be sure the MULTICAST line above is uncommented
-A ufw-before-input -s 224.0.0.0/4 -j ACCEPT
-A ufw-before-input -d 224.0.0.0/4 -j ACCEPT
### Begin Additional Rules ###
# Script kiddie check
#-A ufw-before-input -p tcp --dport 22 -m state --state NEW -j SSH_CHECK
#-A SSH_CHECK -m recent --set --name SSH
#-A SSH_CHECK -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
## Drop Default SSH port access With Logging
-A ufw-before-input -p tcp --dport 22 -m state --state NEW -j SSH_DEFAULT
-A SSH_DEFAULT -m recent --set --name SSH -j LOG --log-prefix "SSH_default_port"
-A SSH_DEFAULT -m recent --set --name SSH -j DROP
## Drop Custom SSH port access With Logging
-A ufw-before-input -p tcp --dport 118118 -m state --state NEW -j SSH_CUSTOM
-A SSH_CUSTOM -m recent --set --name SSH -j LOG --log-prefix "SSH_friend_port"
-A SSH_CUSTOM -m recent --set --name SSH -j DROP
## Allow Custom SSH port from allowed URL with brute force logging
-A SSH_CUSTOM -s my.friends.url.net -j SSH_FRIEND
-A SSH_FRIEND -m recent --set --name SSH -j ACCEPT
-A SSH_FRIEND -m recent --update --seconds 60 --hitcount 4 --name SSH -j LOG --log-prefix "SSH_friend_port_brute_force"
-A SSH_FRIEND -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
### End Additional Rules ###
# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT
Using iptables to do hostname-based filtering is a bad idea, IMO. For starters, the lookup is done when the command is executed (and the IP substituted), therefore eliminating the match as soon as your friend's IP changes.
There's a warning about this right in in the manual. From man iptables:
Code:
[!] -s, --source address[/mask]
Source specification. Address can be either a network name, a hostname (please note that specifying any name to be resolved with a remote
query such as DNS is a really bad idea), a network IP address (with /mask), or a plain IP address. The mask can be either a network mask
or a plain number, specifying the number of 1's at the left side of the network mask. Thus, a mask of 24 is equivalent to 255.255.255.0.
A "!" argument before the address specification inverts the sense of the address. The flag --src is an alias for this option.
Have you considered using TCP wrapper instead (or in combination with iptables)? This way the DNS is checked on each connect. It's not a rock-solid solution (security-wise), but it should reduce the amount of times your friend gets locked out due to IP mismatch. That said, if you know your friend's ISP netblock, you also have the option of allowing the whole thing, and when combined with a passwordless login setup, the result would be very, very tight compared to the other approaches.
Well an IP range for their netblock would be nice but I've no idea how to find that out (in the UK). I could use UFW rather than iptables then. I've spent a fruitless day trying to write a firewall using iptables from scratch.
I've already switched to using denyhosts to manage sshd connections, and am setup for passwordless anyway.
Urg! Well it's little wonder I get confused about networking. I've tried gathering some more info on my Netblock by attaching directly to my cable modem and when I do so my external IP address range changes! And reverts when put back behind my router.
A traceroute shows what's happening:
Code:
## Direct attachment to cable modem
$traceroute google.co.uk
raceroute: Warning: google.co.uk has multiple addresses; using 74.125.230.145
traceroute to google.co.uk (74.125.230.145), 30 hops max, 52 byte packets
1 MMMMMMMMMMMMMMMMMM.NNNN.cable.virginmedia.com (82.XX.YY.Z) 8.920 ms 36.155 ms 36.436 ms
## Connection to cable modem via router
$ traceroute google.co.uk
traceroute: Warning: www.google.co.uk has multiple addresses; using 74.125.230.144
traceroute to www.l.google.com (74.125.230.144), 30 hops max, 52 byte packets
1 router (192.168.A.B) 0.874 ms 0.576 ms 0.573 ms
2 MMMMMMMMMMMMMMMMMM.NNNN.cable.virginmedia.com (213.DD.EE.F) 10.674 ms 9.106 ms 11.849 ms
(edit) whois shows a smaller range on the directly connected block 82.XXX.16.0 - 82.XXX.31.255
(31-16+1)*256 = 4096
So it would be nice to use if it means anything to my friend's computer and vice versa. Also since my friend is local and on the same supplier to me does this mean we share a similar IP environment?
I always use the AllowUsers option to only allow connections from certain users. You can also change the port you use for ssh. That will reduce the noise from script kiddies. Also consider using public key authentication. Then someone can't try to guess the username & password.
You can add an option in the first field of the authorized_keys file. One option you can use is from="<canonical hostname>" which will restrict access to a certain host.
If you really, really wanna make it so that iptables only allows your friend's IP (and you don't wanna do port knocking), there's another way: Set up something like DynDNS on your friend's computer, which gives it a permanent, non-changing DNS address. Then have a cron job on the server periodically check what IP is tied to the DNS address. If the IP hasn't changed, the script exits. If the IP has changed, the old iptables rule gets flushed and one with the new IP is added. In fact, if you're certain only your friend's IP changes (and not the DNS address), then you could do this without having to sign up for a DNS service like the example I linked.
And yes, I do think you're over-worrying, but that can be a healthy habit.
win32sux. That thought had occurred to me but I hadn't investigated it fully. I've found a cron script to do just that here. I've not had a chnace to test it yet and am concerned about how it would integrate with UFW. As I understand it UFW has a habit of resetting your firewall rules when it feels like it.
Ah, I had a go at that but I didn't have much success with iptables. I find them a complete headache and am never sure if what I've produced is stable or not. This was my recent attempt:
Code:
#!/bin/bash
# IPTABLES is order specific so start with the most general rules and
# move to the specific.
# iptables use:
# -A Append one or more rules to the end of the selected chain.
# -j what to do if the packet matches rule - target can be a
# user-defined chain or one of the special builtin targets.
# Bit Torrent port
bt_port=12600
# Mediatomb port
mediatomb_port=50500
# SSH port
ssh_port=22 # This will be changed to a non-standard port
# Dubious interface
dub_if="eth0"
# Dubious network
dub_net="192.168.1.0/24"
# Disable port forwarding (there's no NAT on this machine)
echo "0" > /proc/sys/net/ipv4/ip_forward
# Modprobe goes here. I don't know if it's needed
### Clear old iptables ###
# Set defensive policies (only valid for built-in chains)
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 'Flush' iptables for all built-in chains (i.e. delete all rules for them)
# Note: these Chains cannot be deleted
iptables -F
# Delete user defined chains
iptables -X
### Start of new iptables ###
# Create new user defined chains
#iptables -N TCP
#iptables -N UDP
# Single machine with no NAT (Network Address Translation, AKA Masquerade),
# so Drop all packets looking to be forwared
#iptables -P FORWARD DROP # Already set
# Policy to Accept outward bound packets (don't block packets originating
# from this machine)
#iptables -P OUTPUT ACCEPT # Already set
# Policy to Drop incomming packets (secure this machine)
#iptables -P INPUT DROP # Already set
# Zero packet and byte counters
#iptables -Z
# Create user defined drop & log table
iptables -N drop-and-log
iptables -A drop-and-log -j LOG --log-level info
iptables -A drop-and-log -j REJECT
# Allow communication on the loopback device 127.0.0.1 (local sockets)
iptables -A INPUT -i lo -j ACCEPT
# Protection against spoofing attacks
#iptables -I INPUT -i $dub_net -s 127.0.0.0/8 -j DROP
## Statefull packet filtering ##
# Drop packets that are invalid
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# Allow incomming packets that are from established and related connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow all echo requests from dubious network.
# First packet counts as NEW, the rest are RELATED,ESTABLISHED
iptables -A INPUT -s $dub_net -p icmp --icmp-type 8 -m state --state NEW -j ACCEPT
# Accept mediatomb tcp traffic from dubious network
iptables -A INPUT -p tcp -s $dub_net --dport $mediatomb_port -j ACCEPT
# Accept ssh tcp traffic from dubious network
iptables -A INPUT -p tcp -s $dub_net --dport $ssh_port -j ACCEPT
# Accept bit torrent tcp & udp traffic from everywhere network
iptables -A INPUT --dport $bt_port -j ACCEPT
# Enable kernel builtin source address verification
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
I use dyndns to allow me to get into my home network when I'm away from home. Works well for me, but I'm using pf on a BSD box that has port 22 exposed to the internet.
Regarding iptables, if you can use ufw, you should be able to use iptables without issues. It's the same concept (they all are)...you just have to get used to the syntax. There are scripts on the interwebz that can help you develop a good iptables script. Me? I bought a book. Once you come up with a good script, it'll hardly ever change.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.