I just found back an old note about using iptables in combination with dyndns to open up access from a remote location. For instance, if you have a laptop that you take everywhere and you want to connect to your home or office. The script the other site suggested was broken, so let's write a new one.
Step 1: Create a new chain in the firewall
Create a new chain in the firewall where we can plug in the dynamic rules. On my Fedora machine, the firewall is located in /etc/sysconfig/iptables. I added the bold lines to this example.
*nat
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
<b>:DYNAMICPARENT - [0:0]
-A INPUT -j DYNAMICPARENT</b>
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
Step 2: Write a script
#!/bin/bashHOSTNAME=myname.dyndns.org
CHECK_INTERVAL=60#once a minuteIP=""#initialize $IPwhile[true]; doOIP=$IPIP=$(dig +short $HOSTNAME|grep-iE"^[0-9]+.[0-9]+.[0-9]+.[0-9]+$"|head-n1)if["$OIP"!= "$IP"-a"$IP"!= ""]; thenecho"Changing ip to $IP"/sbin/iptables -N DYNAMICNEW # create new rule/sbin/iptables -I DYNAMICNEW -s$IP-j ACCEPT # allow new ip/sbin/iptables -I DYNAMICPARENT -j DYNAMICNEW # attach new rule to its parentwhile[true]; do# unlink old rule - if multiple exist, remove all/sbin/iptables -D DYNAMICPARENT -j DYNAMICCHILD 2>/dev/null ||breakdone/sbin/iptables -F DYNAMICCHILD #flush all old rules/sbin/iptables -X DYNAMICCHILD #flush all old rules/sbin/iptables -E DYNAMICNEW DYNAMICCHILD #rename new to "current"fisleep$CHECK_INTERVALdone
#!/bin/bash HOSTNAME=myname.dyndns.org
CHECK_INTERVAL=60 #once a minute IP="" #initialize $IP
while [ true ]; do
OIP=$IP
IP=$(dig +short $HOSTNAME | grep -iE "^[0-9]+.[0-9]+.[0-9]+.[0-9]+$"|head -n 1)
if [ "$OIP" != "$IP" -a "$IP" != "" ]; then
echo "Changing ip to $IP"
/sbin/iptables -N DYNAMICNEW # create new rule
/sbin/iptables -I DYNAMICNEW -s $IP -j ACCEPT # allow new ip
/sbin/iptables -I DYNAMICPARENT -j DYNAMICNEW # attach new rule to its parent while [ true ]; do # unlink old rule - if multiple exist, remove all
/sbin/iptables -D DYNAMICPARENT -j DYNAMICCHILD 2>/dev/null || break
done
/sbin/iptables -F DYNAMICCHILD #flush all old rules
/sbin/iptables -X DYNAMICCHILD #flush all old rules /sbin/iptables -E DYNAMICNEW DYNAMICCHILD #rename new to "current"
fi
sleep $CHECK_INTERVAL
done
In this case, the firewall accepts all traffic from $IP, but of course you could restrict it to 1 port. Also, I focussed on IPv4, but you could easily rewrite this script to IPv6 using ip6tables. I saved the file to /usr/local/bin/dynfirewall.sh
Step 3: Run the script
I'd prefer running the script from inittab, but since Fedora doesn't work like this anymore, I put the following line in /etc/rc.d/rc.local:
Please don't forget the ampersand at the end to fork the script!!
Why is this script better than previous version?
- This script can handle cnames - The old script used to delete old rules, before creating new ones. This one does not. Therefore, it will never leave a second where you cannot connect.
In 2011, I wrote this post on Dynamic DNS: https://www.geeklab.info/2011/02/iptables-and-dynamic-dns. While this is still useful, I found a newer, cooler way to do Dynamic DNS in combination with iptables. It's called libnetfilter_queue.
iptables is used to change the inner netfilter tables of the kernel. And because the kernel has no internal resolver, it is impossible for the kernel to do on-the-fly dns lookups. But by offloading this decision to userspace, it is possible. The libnetfilter_queue lib offers that functionality.
libnetfilter_queue is a userspace library providing an API to packets that have been queued by the kernel packet filter. It has bindings for Python and several other languages.
You may need to build the first dependency yourself. The other 3 are available in Fedora 20 by default. If you're running RHEL/CentOS, the Fedora packages can be recompiled for your setup.
iptables rule
First, you need to get iptables to enqueue specific packets to your queue.
iptables -I INPUT -p tcp --dport631-m state --state NEW -j NFQUEUE --queue-num6789-m comment --comment"Remote CUPS printer"
Then we write a script that handles the queue. A quick-and-dirty implementation:
#!/usr/bin/pythonimportsocketfrom netfilterqueue import NetfilterQueue
def getIP(d):
"""
This method returns the first IP address string
that responds as the given domain name
"""try:
data =socket.gethostbyname(d)#ip = repr(data)return data
exceptException:
# fail gracefully!returnFalsedef dnsfilter(pkt):
if pkt.get_payload_len()<0x10:
"Don't know how to handle this too small packet"
pkt.drop()returnFalse
payload=pkt.get_payload()
srcip=".".join("{:d}".format(ord(c))for c in payload[0x0c:0x10])
allowedip=getIP('localhost')print"Debug: SRC="+srcip+" ALLOWED="+allowedip+" RESULT=",if srcip==allowedip:
print"Accept"
pkt.accept()else:
print"Drop"
pkt.drop()
nfqueue = NetfilterQueue()
nfqueue.bind(6789, dnsfilter)try:
nfqueue.run()exceptKeyboardInterrupt:
print
#!/usr/bin/python import socket
from netfilterqueue import NetfilterQueue def getIP(d):
"""
This method returns the first IP address string
that responds as the given domain name
"""
try:
data = socket.gethostbyname(d)
#ip = repr(data)
return data
except Exception:
# fail gracefully!
return False def dnsfilter(pkt):
if pkt.get_payload_len() < 0x10:
"Don't know how to handle this too small packet"
pkt.drop()
return False payload=pkt.get_payload()
srcip=".".join("{:d}".format(ord(c)) for c in payload[0x0c:0x10])
allowedip=getIP('localhost')
print "Debug: SRC="+srcip+" ALLOWED="+allowedip+" RESULT=",
if srcip==allowedip:
print "Accept"
pkt.accept()
else:
print "Drop"
pkt.drop() nfqueue = NetfilterQueue()
nfqueue.bind(6789, dnsfilter)
try:
nfqueue.run()
except KeyboardInterrupt:
print
This is a quick-and-dirty implementation that misses basic features such as caching the result of gethostbyname. This may introduce terrible delays if used wrong.
Ubuntu/Debian
I'm running RedHat-based software on all of my machines. Above information may be useful for Ubuntu/Debian users, but it's not tested and I'm not supporting it.
Servers: RedHat Enterprise Linux/CentOS is more suitable for servers, as there's a lot of professional level support available. I think that's important, because if I say, get a car accident, I want the servers to be managable by another professional.
Desktops/Laptops: RPM packages are pretty exchangable between RedHat-based platforms. That's a good reason to run Fedora on the desktop.
Put all required files, such as pxelinux.0 from the package syslinux in /var/ftpd
Ubuntu/Debian
I'm running RedHat-based software on all of my machines. Above information may be useful for Ubuntu/Debian users, but it's not tested and I'm not supporting it.
Servers: RedHat Enterprise Linux/CentOS is more suitable for servers, as there's a lot of professional level support available. I think that's important, because if I say, get a car accident, I want the servers to be managable by another professional.
Desktops/Laptops: RPM packages are pretty exchangable between RedHat-based platforms. That's a good reason to run Fedora on the desktop.
I just found back an old note about using iptables in combination with dyndns to open up access from a remote location. For instance, if you have a laptop that you take everywhere and you want to connect to your home or office. The script the other site suggested was broken, so let's write a new one.
Step 1: Create a new chain in the firewall
Create a new chain in the firewall where we can plug in the dynamic rules. On my Fedora machine, the firewall is located in /etc/sysconfig/iptables. I added the bold lines to this example.
*nat
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
<b>:DYNAMIC - [0:0]
-A INPUT -j DYNAMIC</b>
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
Step 2: Write a script
#!/bin/bashHOSTNAME=myname.dyndns.org
CHECK_INTERVAL=60#once a minute/sbin/iptables -F DYNAMIC #flush all existing rulesIP=""#initialize $IPwhile[true]; doOIP=$IPIP=$(host $HOSTNAME|grep-iE"[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"|cut-f4-d' '|head-n1)if["$OIP"!= "$IP"-a"$IP"!= ""]; thenecho"Changing ip to $IP"/sbin/iptables -F DYNAMIC #flush all old rules/sbin/iptables -I DYNAMIC -s$IP-j ACCEPT #the new rulefisleep$CHECK_INTERVALdone
#!/bin/bash HOSTNAME=myname.dyndns.org
CHECK_INTERVAL=60 #once a minute /sbin/iptables -F DYNAMIC #flush all existing rules
IP="" #initialize $IP
while [ true ]; do
OIP=$IP
IP=$(host $HOSTNAME | grep -iE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" |cut -f4 -d' '|head -n 1)
if [ "$OIP" != "$IP" -a "$IP" != "" ]; then
echo "Changing ip to $IP"
/sbin/iptables -F DYNAMIC #flush all old rules
/sbin/iptables -I DYNAMIC -s $IP -j ACCEPT #the new rule
fi
sleep $CHECK_INTERVAL
done
In this case, the firewall accepts all traffic from $IP, but of course you could restrict it to 1 port. Also, I focussed on IPv4, but you could easily rewrite this script to IPv6 using ip6tables. I saved the file to /usr/local/bin/dynfirewall.sh
Step 3: Run the script
I'd prefer running the script from inittab, but since Fedora doesn't work like this anymore, I put the following line in /etc/rc.d/rc.local:
Today, my dear wife asked me to help her with her facebook addiction. She wondered if I could block facebook, gmail, some news sites and more during her work hours. Sure, I can. And since she's running Linux as well, I could even do it on her own computer.
Step 1: Install squid
Squid is a FLOSS proxy server that runs on Linux and several other sytems. It's capable of filtering and behaving transparently. Just what we need.
# Example rule allowing access from your local networks. # Adapt to list your (internal) IP networks from where browsing # should be allowed acl localnet src 10.0.0.0/8 # RFC1918 possible internal network acl localnet src 172.16.0.0/12 # RFC1918 possible internal network acl localnet src 192.168.0.0/16 # RFC1918 possible internal network acl localnet src fc00::/7 # RFC 4193 local private network range acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443 acl Safe_ports port 80 # http acl Safe_ports port 21 # ftp acl Safe_ports port 443 # https acl Safe_ports port 70 # gopher acl Safe_ports port 210 # wais acl Safe_ports port 1025-65535 # unregistered ports acl Safe_ports port 280 # http-mgmt acl Safe_ports port 488 # gss-http acl Safe_ports port 591 # filemaker acl Safe_ports port 777 # multiling http acl CONNECT method CONNECT
# Here I define the times and what file contains the rules acl playtime1 time SMTWHFA 8:30-9:30 acl playtime22 time SMTWHFA 16:00-17:00 acl addiction url_regex -i "/etc/squid/addiction"
# Only allow cachemgr access from localhost http_access allow manager localhost http_access deny manager
# Deny requests to certain unsafe ports http_access deny !Safe_ports
# Deny CONNECT to other than secure SSL ports http_access deny CONNECT !SSL_ports
# We strongly recommend the following be uncommented to protect innocent # web applications running on the proxy server who think the only # one who can access services on "localhost" is a local user #http_access deny to_localhost
# # INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS #
# The next few lines actually do the work http_access allow playtime1 addiction http_access allow playtime2 addiction http_access deny addiction # If this ACL is triggered, show the user the WORKONLY error message. deny_info WORKONLY addiction
# Example rule allowing access from your local networks. # Adapt localnet in the ACL section to list your (internal) IP networks # from where browsing should be allowed http_access allow localnet http_access allow localhost
# And finally deny all other access to this proxy http_access deny all
# Squid normally listens to port 3128 # I added the word "transparent", so squid behaves a little different: # it makes itself transparent. NOTE TO SELF: This is the line you're looking for. Used to be httpd_accel_uses_host_header in squid 2
http_port 3128 transparent
# We recommend you to use at least the following line. hierarchy_stoplist cgi-bin ?
# Uncomment and adjust the following to add a disk cache directory. #cache_dir ufs /var/spool/squid 100 16 256
# Leave coredumps in the first cache dir coredump_dir /var/spool/squid
# Add any of your own refresh_pattern entries above these. refresh_pattern ^ftp: 1440 20% 10080 refresh_pattern ^gopher: 1440 0% 1440 refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 refresh_pattern . 0 20% 4320
# Don't show squid to the outside world forwarded_for delete
# I don't need to log what she's doing access_log none # Nor do i need icap logs icap_log none # And i don't want to know what is stored in cache cache_store_log none # To not break web apps, I don't want caching either cache deny all
Step 3: Define blocked sites
Type a list of blocked websites in /etc/squid/addiction. You can use complete urls, domains or even just words. Ie. "facebook" blocks http://www.facebook.com, but also http://wikipedia.org/wiki/facebook
Step 4: Leave a message
In the configuration, I put: deny_info WORKONLY addiction. This means that I can leave the user a message in /usr/share/squid/errors/templates/WORKONLY and /usr/share/squid/errors/en/WORKONLY. Since it's my wife's PC, I decided to leave her a sweet message :-D
Step 5: Route network traffic
I could configure her Firefox to use the proxy. But then she'd use Google Chrome or Konqueror to surf the web. And she could turn the proxy off. So I need to catch all http-traffic that did not pass squid. I used iptables:
#Allow user 'root' to surf the web, for yum update etc.
iptables -t nat -A OUTPUT -m tcp -p tcp --dport80-m owner --uid-owner root -j RETURN
# Allow user 'squid' to pass on http requests
iptables -t nat -A OUTPUT -m tcp -p tcp --dport80-m owner --uid-owner squid -j RETURN
# Redirect all other traffic to the proxy.
iptables -t nat -A OUTPUT -m tcp -p tcp --dport80-j REDIRECT --to-ports3128
#Allow user 'root' to surf the web, for yum update etc.
iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -m owner --uid-owner root -j RETURN
# Allow user 'squid' to pass on http requests
iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -m owner --uid-owner squid -j RETURN
# Redirect all other traffic to the proxy.
iptables -t nat -A OUTPUT -m tcp -p tcp --dport 80 -j REDIRECT --to-ports 3128