Monday, October 28, 2019

One more SIP firewall based on Kamailio

Sometimes it's impossible to put Kamailio in a front of third-party SIP software and it's nice to have sort of SIP firewall upfront.

One of solutions - use iptables (like I did a long ago here), but overall, IPTables is not a SIP processing engine. But Kamailio is.

So, idea is quite simple. We have our main PBX software sitting on ethX (or ensXX), port 5060. We're mirroring SIP traffic on this interface:port to localhost, where Kamailio is listens to them in promiscuous mode. And writing a log file, Fail2Ban can analyze. 
Target here - is to protect main PBX software from consuming a lot of resources on fake INVITE's and REGISTER's from not-too-friendly-scanners.

First - install Kamailio.
I prefer to put syslog messages to separate file.

Configure Kamailio with file

/etc/kamailio/kamailio.cfg
 
#!KAMAILIO

#!define WITH_ANTIFLOOD
#!define WITH_DOMAIN_NAMES
#!define WITH_SANITY_CHECK
#!define WITH_SCANNER_BODY
#!define WITH_SCANNER_MESSAGE

debug=2
log_stderror=no

memdbg=5
memlog=5

log_facility=LOG_LOCAL7

children=6

auto_aliases=no

listen=udp:127.0.0.1:5060


loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "regex.so"
loadmodule "sipcapture.so"
loadmodule "db_text.so"
# ----------------- setting module-specific parameters ---------------
modparam("sipcapture", "db_url", "text:///tmp/")
modparam("sipcapture", "raw_socket_listen", "127.0.0.1:5060")
modparam("sipcapture", "raw_moni_capture_on", 1)
modparam("sipcapture", "raw_interface", "lo")
modparam("sipcapture", "promiscious_on", 1)

#!ifdef WITH_ANTIFLOOD
loadmodule "pike.so"
# ----- pike params -----
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 16)
modparam("pike", "remove_latency", 4)

loadmodule "htable.so"
# ----- htable params -----
/* ip ban htable with autoexpire after 5 minutes */
modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
#!endif

####### Routing Logic ########

request_route {


#!ifdef WITH_ANTIFLOOD
    # flood detection from same IP and traffic ban for a while
    # be sure you exclude checking trusted peers, such as pstn gateways
    # - local host excluded (e.g., loop to self)
    if(src_ip!=myself) {
        if($sht(ipban=>$si)!=$null) {
            # ip is already blocked
            xlog("L_NOTICE", "[SIP-FIREWALL][ANTIFLOOD-BANNED] [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]\n");
            exit;
        }
        if (!pike_check_req()) {
            $sht(ipban=>$si) = 1;
            xlog("L_NOTICE", "[SIP-FIREWALL][ANTIFLOOD-ADD] [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]\n");
            xlog("L_ALERT", "[SIP-FIREWALL][FAIL2BAN] $si\n");
            exit;
        }
    }
#!endif

#!ifdef WITH_SCANNER_MESSAGE
    if (search("friendly-scanner|sipvicious|sipcli*|vaxasip|sip-scan|iWar|sipsak")) {
        xlog("L_NOTICE", "[SIP-FIREWALL][FRIENDLYSCANNER_MESSAGE]: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        xlog("L_ALERT", "[SIP-FIREWALL][FAIL2BAN] $si\n");
        exit;
    }
#!endif
#!ifdef WITH_SCANNER_BODY
    if (search_body("friendly-scanner|sipvicious|sipcli*|vaxasip|sip-scan|iWar|sipsak")) {
        xlog("L_NOTICE", "[SIP-FIREWALL][FRIENDLYSCANNER_BODY]: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        xlog("L_ALERT", "[FAIL2BAN] $si\n");
        exit;
    }
#!endif
#!ifdef WITH_SANITY_CHECK
    if(!sanity_check("17895", "7")) {
        xlog("L_NOTICE", "[SIP-FIREWALL][MALFORMED] [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]\n");
        xlog("L_ALERT", "[SIP-FIREWALL][FAIL2BAN] $si\n");
        exit;
    }
#!endif
#!ifdef WITH_DOMAIN_NAMES
    if (!is_method("INVITE|REGISTER")) {
        exit;
    }

    if (pcre_match("$rd", "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$$")) {
        if (pcre_match("$fd", "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$$")) {
            xlog("L_NOTICE", "[SIP-FIREWALL][NOT_DOMAIN_BASED_URI]: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
            xlog("L_ALERT", "[SIP-FIREWALL][FAIL2BAN] $si\n");
            exit;
        }
    }
#!endif
}



It's actually my file, but you can adopt rules to yours.

Next - set up Fail2Ban

/etc/fail2ban/filter.d/kamailio.conf

[Definition]
# filter for kamailio messages
failregex = [SIP-FIREWALL][FAIL2BAN] <HOST>


/etc/fail2ban/jail.conf
...
[kamailio-iptables]
enabled  = true
filter   = kamailio
action   = iptables-allports[name=KAMAILIO, protocol=all]
logpath  = /var/log/kamailio.log
maxretry = 5
bantime  = 1800
findtime = 60


Next - make sure you don't have net.ipv4.ip_forward set to 1. You will run into infinite loops in this case

# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0


And last - use iptables TEE target to mirror traffic to localhost

# iptables -A PREROUTING -t mangle -i eth0 -p udp --dport 5060 -j TEE --gateway 127.0.0.1

That's it.




Wednesday, October 23, 2019

Building RTPEngine with Docker

Idea is quite simple.
To build rtpengine packet on Debian system, but not having all build dependencies on target machine.
So, we'll use Docker to generate deb files for target system and than - just remove image. This way system will held only minimum needed packets.

As a target, Debian 10 (buster) will be used, as I succeed to install rtpengine mr8.0 (with G.729 support) on it and can't get it working on Debian 9 (maybe use lower version?)

So. All is done on Debian 10 netinst.
  • First step - install Docker

    apt update
    apt install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
    curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
    add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
    apt update
    apt install -y docker-ce
  • Prepare Dockerfile

    FROM debian:buster
    MAINTAINER Igor Olhovskiy <igorolhovskiy@gmail.com>

    ENV DEBIAN_FRONTEND noninteractive
    ENV RTPENGINE_VERSION mr8.0
    ENV BCG729_VERSION 1.0.4

    RUN apt-get update && \
        apt-get upgrade -y && \
        apt-get install -y git \
                    dpkg-dev \
                    cmake \
                    unzip \
                    wget \
                    debhelper-compat \
                    default-libmysqlclient-dev \
                    gperf \
                    iptables-dev \
                    libavcodec-dev \
                    libavfilter-dev \
                    libavformat-dev \
                    libavutil-dev \
                    libbencode-perl \
                    libcrypt-openssl-rsa-perl \
                    libcrypt-rijndael-perl \
                    libcurl4-openssl-dev \
                    libdigest-crc-perl \
                    libdigest-hmac-perl \
                    libevent-dev libglib2.0-dev \
                    libhiredis-dev libio-multiplex-perl \
                    libio-socket-inet6-perl libiptc-dev \
                    libjson-glib-dev libnet-interface-perl \
                    libpcap0.8-dev \
                    libpcre3-dev \
                    libsocket6-perl \
                    libspandsp-dev \
                    libssl-dev \
                    libswresample-dev \
                    libsystemd-dev \
                    libxmlrpc-core-c3-dev \
                    markdown \
                    curl \
                    wget \
                    zlib1g-dev && \
        cd /usr/src && \
        curl https://codeload.github.com/BelledonneCommunications/bcg729/tar.gz/$BCG729_VERSION > bcg729_$BCG729_VERSION.orig.tar.gz && \
        tar zxf bcg729_$BCG729_VERSION.orig.tar.gz && \
        cd bcg729-$BCG729_VERSION && \
        git clone https://github.com/ossobv/bcg729-deb.git debian && \
        dpkg-buildpackage -us -uc -sa && \
        cd /usr/src && \
        dpkg -i *.deb && \
        cd /usr/src && \
        git clone -b $RTPENGINE_VERSION https://github.com/sipwise/rtpengine.git && \
        cd rtpengine && \
        dpkg-buildpackage && \
        apt-get clean && \
        rm -rf /var/lib/apt/lists/*

    RUN mkdir -p /opt/deb && \
        mv /usr/src/*.deb /opt/deb

    VOLUME ["/opt/deb"]
  • Build and get deb files

    docker build -t rtpengine:build .
    docker create --name rtpengine-build rtpengine:build
    docker cp rtpengine-build:/opt/deb .
  • Install rtpengine


    cd deb
    dpkg -i libbcg729-0*.deb
    dpkg -i ngcp-rtpengine-daemon_*.deb
    apt-get install -f
    dpkg -i ngcp-rtpengine-recording-daemon_*.deb

    apt-get install -f
    dpkg -i ngcp-rtpengine-utils_*.deb

    apt-get install -f
    dpkg -i ngcp-rtpengine-iptables_*.deb

    apt-get install -f
    dpkg -i ngcp-rtpengine-kernel-dkms_*.deb
    dpkg -i ngcp-rtpengine_*.deb
    Idea of repeating apt-get install -f is to apt to add missing packets.
    Next - configure rtpengine as usual