Iptables setup to secure Asterisk server

In this article I’m having a look at securing an AsteriskNOW VoIP server with iptables using custom chains, starting with a simple secure configuration, expanding to multiple users and looking at the issue of dynamic IP addresses.

AsteriskNOW is a ready-made CentOS distribution with pre-installed Asterisk and FreePBX. Getting your box hacked can directly cost you money, and depending on the fraud detection speed of your VoIP provider, this could be a lot.

Basic ports needed

For a basic Asterisk system, you need the following ports:

  • SIP: TCP/UDP 5060, for call related events and messages.
  • RTP: UDP, usually the range 10000-20000, for voice streaming.
  • HTTP: Standard TCP 80, for the FreePBX admin interface.

Let’s build a simple network of computers to build our rules against:

  • 192.168.1.2 is the Asterisk/FreePBX server
  • 192.168.1.10 is the phone user’s PC
  • 88.88.88.88 is our provider’s SIP trunk

For this simple setup, firewall rules look something like the following:

#!/bin/bash

# Flush out the existing rules
iptables -F

# Allow localhost loopback interface
iptables -A INPUT -i lo -j ACCEPT

# Allow already established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow FreePBX administration from user's PC
iptables -A INPUT -p tcp -s 192.168.1.10 --dport 80 -j ACCEPT

# Allow SIP from the SIP trunk and user's PC
iptables -A INPUT -p tcp --dport 5060 -s 88.88.88.88 -j ACCEPT
iptables -A INPUT -p udp --dport 5060 -s 88.88.88.88 -j ACCEPT
iptables -A INPUT -p tcp --dport 5060 -s 192.168.1.10 -j ACCEPT
iptables -A INPUT -p udp --dport 5060 -s 192.168.1.10 -j ACCEPT

# Allow voice streaming for user
iptables -A INPUT -p udp --dport 10000:20000 -s 192.168.1.10 -j ACCEPT

# Set default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Save configuration
service iptables save

This makes sure that anyone who has no business with our phone system doesn’t even see the server. As the default policy, we drop any INPUT that hasn’t matched our rules. FORWARD is always dropped, as we’re not using the machine as a router. OUTPUT is always accepted, allowing the server to make any outgoing connections.

The above example assumes that you’re doing root administration tasks locally at the server terminal and that you run the rules as a script in one go. If you’re doing it remotely over ssh, don’t forget to allow port 22, or you will lock yourself out. If you’re also entering the above command by command at a terminal, start by setting the default INPUT policy to ACCEPT before flushing out the rules. This replaces the first flush command by something like the following:

iptables -P INPUT ACCEPT # default policy
iptables -F # flush
iptables -A INPUT -p tcp -s 192.168.1.10 --dport 22 -j ACCEPT

Multiple users, iptables chains

For a single home user, the above should be enough, however in a business environment, things can get more complicated.

Let’s assume that our internal network is secured properly by a router and everyone on it is a trusted user, who can connect to the phone system and access the admin interface. We should also allow ssh port 22 for remote administration internally. To make things worse, we also have multiple legitimate users outside our network who want to make phonecalls.

If we would carry on with the previous method of listing rules one by one, that would mean even more duplications of our filters for each IP address range separately. This is where iptables chains come in.

By creating custom chains of IP filters per user access level, we can cut down duplication and make alterations easier. Now we only need one rule per port range and if the rule matches, we pass processing to one of the IP filters to only allow access for a specific set of devices.

The user levels I’ve set up are incremental, they’re chained together to make each less specific.

#!/bin/bash

iptables -F     # Flush out the rules
iptables -X     # Delete all custom chains

# Setting up custom IP chains

# Administrator level access
iptables -N ADMIN-LEVEL
iptables -A ADMIN-LEVEL -s 192.168.1.0/255.255.255.0 -j ACCEPT
iptables -A ADMIN-LEVEL -j DROP

# User level access (add external phone users, extensions)
iptables -N USER-LEVEL
iptables -A USER-LEVEL -s 99.99.99.99 -j ACCEPT
iptables -A USER-LEVEL -j ADMIN-LEVEL

# Trunk level access (add additional SIP trunks here)
iptables -N TRUNK-LEVEL
iptables -A TRUNK-LEVEL -s 88.88.88.88 -j ACCEPT
iptables -A TRUNK-LEVEL -j USER-LEVEL

# Basic rules
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow ports
iptables -A INPUT -p icmp --icmp-type 8 -j USER-LEVEL        # allow ping (optional)
iptables -A INPUT -p tcp --dport 22 -j ADMIN-LEVEL           # ssh
iptables -A INPUT -p tcp --dport 80 -j ADMIN-LEVEL           # freepbx http interface
iptables -A INPUT -p tcp --dport 5060 -j TRUNK-LEVEL         # sip
iptables -A INPUT -p udp --dport 5060 -j TRUNK-LEVEL         # sip
iptables -A INPUT -p udp --dport 10000:20000 -j USER-LEVEL   # rtp range

# Set default actions
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Save configuration
service iptables save

Dynamic IP addresses

If you have remote users connecting to your phone system, you will eventually see a problem with completely locking your system down. Unfortunately most consumer packages from ISPs come with dynamic IP addresses, and whether it changes every 6 months or every day, there will be a time when you have to manually adjust your rules to allow them to log in.

The easy way to solve it is to set up a VPN, preferably on a different subnet, and let them connect “locally”. I tried this fix in a real-world scenario, but the results were far from perfect. Our telephone operators were not power users, and asking them to set up a VPN connection and make sure it’s connected all the time was not always an easy task.

I was lucky enough to have an in-house developed softphone which connected to our application server as the first line of authentication, before connecting to Asterisk. Without further ado, here’s what’s happening:

  • User enters his/her login details to the softphone and get authenticated by the application server.
  • On successful authentication:
    • The application server registers the user’s IP in the firewall script.
    • The softphone receives Asterisk connection details and makes the connection.

First, I created a virtual host on Apache to create an isolated area for server only PHP scripts. Simply added the following to a new file at /etc/httpd/conf.d/server.conf:

Listen 50080

NameVirtualHost 192.168.1.2:50080

<VirtualHost 192.168.1.2:50080>
        DocumentRoot /var/www/server-only
</VirtualHost>

I had to add exception for the server to have access to the special PHP script:

...
# Setting up custom IP chains

# Application server only access
iptables -N SERVER-ONLY
iptables -A SERVER-ONLY -s 192.168.1.3 -j ACCEPT
iptables -A SERVER-ONLY -j DROP
...
# Allow ports
...
iptables -A INPUT -p tcp --dport 50080 -j SERVER-ONLY   # server scripts

I copied my firewall script to /usr/sbin/firewall-refresh. I added one additional script to /usr/sbin/firewall-add-extension:

#!/bin/bash

if [ $# -lt 2 ]; then
        echo -e "Usage:\n\n./firewall-add-extension [extension] [ip-address]\n"
        exit
fi

ext="$1"
ip="$2"

# Add extension to firewall rules

rm -f /tmp/firewall-refresh
cat /usr/sbin/firewall-refresh | grep -v "# auto-extension-$ext$" | sed "s/^# auto-extensions$/# auto-extensions\niptables -A USER-LEVEL -s $ip -j ACCEPT # auto-extension-$ext/" > /tmp/firewall-refresh
mv -f /tmp/firewall-refresh /usr/sbin/firewall-refresh

# Reload firewall rules

chmod +x /usr/sbin/firewall-refresh
/usr/sbin/firewall-refresh

The script above registers an extension in the firewall with a specific IP address, as well as removes previously assigned IPs, then reloads the firewall rules. It needs a special placeholder # auto-extensions in the script to know where to put them, and tags the automatic rules with the comment # auto-extension-NNNN to know what to remove next time.

All that’s left is a simple PHP script in /var/www/server-only to allow the application server to register new IP addresses with a simple web request. The firewall-add-extension script needs root privileges to run, if you’re not sure how to allow it from PHP check out my previous post on the subject.

This is the final script with the placeholders added:

#!/bin/bash

iptables -F     # Flush out the rules
iptables -X     # Delete all custom chains

# Setting up custom IP chains

# Application server only access
iptables -N SERVER-ONLY
iptables -A SERVER-ONLY -s 192.168.1.3 -j ACCEPT
iptables -A SERVER-ONLY -j DROP

# Administrator level access
iptables -N ADMIN-LEVEL
iptables -A ADMIN-LEVEL -s 192.168.1.0/255.255.255.0 -j ACCEPT
iptables -A ADMIN-LEVEL -j DROP

# User level access (add external phone users, extensions)
iptables -N USER-LEVEL
# auto-extensions
iptables -A USER-LEVEL -s 99.99.99.91 -j ACCEPT # auto-extension-1001
iptables -A USER-LEVEL -s 99.99.99.92 -j ACCEPT # auto-extension-1002
iptables -A USER-LEVEL -s 99.99.99.93 -j ACCEPT # auto-extension-1003
iptables -A USER-LEVEL -j ADMIN-LEVEL

# Trunk level access (add additional SIP trunks here)
iptables -N TRUNK-LEVEL
iptables -A TRUNK-LEVEL -s 88.88.88.88 -j ACCEPT
iptables -A TRUNK-LEVEL -j USER-LEVEL

# Basic rules
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow ports
iptables -A INPUT -p icmp --icmp-type 8 -j USER-LEVEL        # allow ping (optional)
iptables -A INPUT -p tcp --dport 22 -j ADMIN-LEVEL           # ssh
iptables -A INPUT -p tcp --dport 80 -j ADMIN-LEVEL           # freepbx http interface
iptables -A INPUT -p tcp --dport 5060 -j TRUNK-LEVEL         # sip
iptables -A INPUT -p udp --dport 5060 -j TRUNK-LEVEL         # sip
iptables -A INPUT -p udp --dport 10000:20000 -j USER-LEVEL   # rtp range
iptables -A INPUT -p tcp --dport 50080 -j SERVER-ONLY        # server scripts

# Set default actions
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Save configuration
service iptables save

Final note: If you want to be even more secure, you can mirror the firewall rules on the OUTPUT chain as well, but for the purpose of this article, I assumed the server can make any outgoing connections. If you decide to filter outgoing traffic, make sure you think about allowing anything else that might be needed, including CentOS system and FreePBX updates.

Share Button
Posted in Blog Tagged with: , , , , ,

Leave a Reply