Technic Blog

Technik BlogEgress Traffic Filter und FQDN in Firewall ACLs

21. Juli 2017 Max Fruth

Egress Traffic Filtering

Mit Egress Filtering wird die Kontrolle jener Netzverbindungen bezeichnet, die vom eigenen Netzwerk aufgebaut werden. Immer mehr Firewall Administratoren verfolgen dabei eine Policy die sinngemäß besagt mein internes Netzwerk soll Verbindungen mit beliebigen Netzadressen im Internet aufbauen und uneingeschränkt Daten austauschen können.

Ein Grund für diese Aufweichung der Security Policy sind öffentliche Internetdienste deren IP Adressen sich im DNS häufig ändern, hier kurz dynamische IPs genannt. Verbindungen zu diesen Services sind oft essentiell für Applikationen (z.B. Services von Google oder Facebook) und können mit einfachen IP basierten Filterregeln nicht freigegeben werden.

Diese Vorgehensweise steht im krassen Widerspruch zu den Bedrohungen der Unternehmen, aufgrund der Vielzahl von mit Malware oder Schadcode infizierten Systeme im Internet. Angreifer nutzen diese Systeme als Zieladresse für geleakte Daten und um sensible Informationen aus einem Netzwerk weiterzuleiten. Zudem werden kompromittierte Unternehmensnetze häufig für Attacken auf fremde Netze missbraucht. Egress Filter können hier einen zweiten Schutzwall bilden, der verhindert, daß kompromittierte Systeme Schadcode verbreiten oder andere Systeme attackieren.

Einfach ausgedrückt, verhindern Egress Filter daß unerwünscht Daten ins Internet transferiert werden.

Unternehmen sind gut beraten, wenn deren Firewall Administratoren die Gefahren im Zusammenhang mit sog. Outbound Verbindungen gleichrangig wie die Bedrohungen durch eingehende Verbindungen behandeln. Dies gilt auch deshalb, weil die Ursache von Datendiebstahl oft auf Schwachstellen in der eingesetzten Software oder auf Konfigurationsfehler zurückzuführen sind. Die Bedrohung durch diese Schwachstellen ist ohne Egress Filtering latent vorhanden und nur mit hohem Kotrollaufwand zu reduzieren.

Die Konfiguration einer strengen Egress Filter Policy ist im Cloud Zeitalter keine einfache Aufgabe, da viele Funktionen unsinnigerweise in einem "in die Jahre gekommenen" DNS abgebildet werden. Entweder hat man das Geld für eine teure Application-Layer Firewall (z.B. WAF) oder man konfiguriert Freigaben für eine Vielzahl großer Netzblöcke der Cloud Provider auf seiner IP Firewall. Insbesondere letzteres ist ein eher schlechter Kompromiss, da es ja keine Garantie gibt, daß Clouds frei von infizierten Systemen sind.

Eine Möglichkeit, jedoch mit etlichen Schönheitsfehlern, ist die Konfiguration von sog. FQDN ACLs auf einer IP Firewall.

FQDN basierte ACLs

IP Firewall Systeme die diese Funktion anbieten lösen den FQDN (Full Qualified Domain Name) via DNS auf und speichern die zugehörigen IPs in einen Cache. Verbindungen und deren Datenverkehr wird dann entsprechend der Policy erlaubt oder verhindert. Es sei an dieser Stelle nochmals darauf hingewiesen, daß dieses Verfahren keinen Ersatz für ein sog. URL Filtering auf Application Layer darstellt. Warum dies so ist, soll im Folgenden näher erläutert werden.

IT-Security

Da DNS für die Namensauflösung verwendet werden muß, ergibt sich daraus ein zusätzlicher Angriffsvektor (z.B. Cache Poisoning Attacks) und somit eine neue Bedrohung für die Firewall Policy. Aus diesem Grund empfiehlt es sich einen vertrauenswürdigen (internen) DNS Resolver zu konfigurieren und diesen mit DNSSEC zu betreiben.

Multiple FQDN`s

Außerdem sollte man sich bewusst sein, daß unterschiedliche FQDN's zu einer bestimmten Zeit auf ein und dieselbe IP auflösen. Für die Firewall Policy bedeutet dies, daß sowohl der Zugriff auf abc.com als auch ein Zugriff auf xyz.com erlaubt ist.

Dynamic DNS

Insbesondere DNS Namen mit kurzen TTLs und ständig wechselnden IPs stellen eine besondere Herausforderung für die Implementierung von FQDN ACLs dar. Wie man sich unschwer vorstellen kann, ist hier die Gefahr gegeben, daß Verbindungen kurzfristig erlaubt und dann wieder blockert werden können. Ein für die Problemanalyse sehr schwieriger Sachverhalt.

Multi-DNS Resolver und Loadbalanced DNS TTLs

Üblicherweise werden aus Gründen der Ausfallsicherheit mehrere DNS Resolver in einem Unternehmen konfiguriert. Bei der Namensauflösung wird zunächst ein Resolver (z.B. nach dem Round Robin Verfahren) ausgewählt und falls die DNS Antwort ausbleibt (Timeout) wird die DNS Abfrage an einen weiteren DNS Resolver weitergeleitet.

Eine weitere Methode die DNS Verfügbarkeit zu gewährleisten, ist die Verwendung eines Loadbalancers. Dieser stellt eine Farm an DNS Resolver unter einer einzigen IP den Clients und somit auch der Firewall zur Verfügung. Für beide Verfahren gilt, daß die in der der DNS Antwort mitgelieferte TTL, die Zeit im Cache des befragten DNS Resolver wiederspiegelt und diese üblicherweise unterschiedlich ist, je nachdem welcher Resolver abgefragt wird.

Warum dieser Sachverhalt für die Implementierung bedeutend ist, soll folgendes Szenario veranschaulichen.

  • Die Firewall versucht den DNS Namen für xyz.com aufzulösen.
  • Die Anfrage wird an den zuerst gelisteten DNS Resolver oder zum Loadbalanced Pool von DNS Resolvern gesendet. Der DNS Resolver A liefert als Antwort 1.1.1.1 mit einer TTL von 30 Sekunden.
  • Ein Client (z.B. ein Webserver) stellt ebenfalls eine Anfrage für xyz.com.
  • Diese Anfrage wird aber an einen anderen gelisteten DNS Resolver bzw. an den Loadbalancer Pool gesendet. DNS Resolver B antwortet ebenfalls mit der IP 1.1.1.1 aber mit einer TTL von 3600 Sekunden.
  • Der DNS Cache Eintrag auf DNS Resolver A läuft 30 Sekunden später ab.
  • Die Firewall versucht anschließend xyz.com erneut aufzulösen. Der DNS Resolver A liefert als Antwort 2.2.2.2, da sich die IP mittlerweile geändert hat. Aber B würde 1.1.1.1 liefern, da der Datensatz im Cache noch nicht abgelaufen ist.
  • Dies bedeutet, dass in Abhängigkeit zum befragten DNS Resolver die Verbindung erlaubt oder blockiert wird.

Aus diesem Grund ist der bei vielen Firewalls konfigurierbare Expire Timout von Bedeutung. Dieser Wert bestimmt die Zeit, die auf eine TTL addiert wird, bevor der zugehörige Eintrag aus dem Firewall Cache entfernt wird.

Eine weitere Möglichkeit wäre das Deaktivieren des Resolver Caches, was aber in der Regel zu einer erheblichen Erhöhung der DNS Antwortzeiten führt.

Nicht verfügbare DNS Resolver

Natürlich sollten der Service DNS Resolving für die Firewall rund um die Uhr verfügbar sein. Firewalls markieren FQDN ACL Regeln im Fehlerfall als unresolved, was mit inaktiv gleichzusetzen ist.

Eine einfache Implementierung für die IPTABLES Hostfirewall

In den folgenden Scripts ist das beschriebene Verfahren für iptables unter RHEL 7 nachimplementiert. Da es sich um kein iptables Modul sondern und einfache Bash Scripts handelt, sollte das Wrapper Script via cron in kurzen Zeitabständen (z.B. jede Minute) aufgerufen werden.
  • #!/bin/bash
    #
    # AUTHOR:   Max Fruth
    # VERSION:  1.0
    # HISTORY:  23.09.2015 intitial creation
    #
    # DESCRIPTION:
    # wrapper script to set OUTBOUND FQDN allow rules. Destinations are defined in a 
    # config file. The file path of this config file is required as argument.
    # This feature works together with iptables and firewalld and was tested under RHEL 7.
    #
    
    function log
    {
      SYSLOG="kern.debug"
      if [ "$1" == "SCREEN" ]; then
        echo "$2"
      fi
      if [ "$1" == "SYSLOG" ]; then
        logger -p $SYSLOG -t $SCRIPT "$2"
      fi
    }
    
    function usage
    {
      MSG=$1
      log SCREEN "$MSG"
      log SCREEN "usage: $0 "
      exit 1
    }
    
    if [ $# -ne 1 ]; then
      usage "wrong argument count"
    fi
    
    CONFIG=$1
    CMD="/usr/local/sbin/firewalld_domain_rule.sh"
    
    if [ ! -f  $CONFIG ]; then
      log SCREEN "firewall config file [$CONFIG] does not exist"
      exit 1
    fi
    
    source $CONFIG
    
    if [ ! -z "$OUTBOUND_DYNAMIC" ]; then
      RULES=(${OUTBOUND_DYNAMIC//$/ })
      for i in "${!RULES[@]}"
      do
        TMP=${RULES[$i]} ;
        RULE=(${TMP//|/ })
        $CMD OUTPUT ${RULE[0]} ${RULE[1]} ${RULE[2]}
      done
    fi
    
    if [ ! -z "$INPUT_DYNAMIC" ]; then
      RULES=(${INPUT_DYNAMIC//$/ })
      for i in "${!RULES[@]}"
      do
        TMP=${RULES[$i]} ;
        RULE=(${TMP//|/ })
        $CMD INPUT ${RULE[0]} ${RULE[1]} ${RULE[2]}
      done
    fi
    
    • #!/bin/bash
      #
      # AUTHOR:   Max Fruth / Tobias Maier
      # VERSION:  1.0
      # HISTORY:  10.01.2017 intitial creation
      #
      # DESCRIPTION:
      # firewalld_domain_rule.sh
      # configure FQDN accept rules in iptables.
      #
      
      function log
      {
        SYSLOG="kern.debug"
      
        if [ "$1" == "SCREEN" ]; then
          echo "$2"
        fi
        if [ "$1" == "SYSLOG" ]; then
          logger -p $SYSLOG -t $SCRIPT "$2"
        fi
      }
      
      function log_msg
      {
        if [ -t 1 ]
        then
          # stdout is a terminal
          log SCREEN "$1"
        else
          # stdout is not a terminal
          log SYSLOG "$1"
        fi
      }
      
      function usage
      {
        log_msg "$1"
        if [ -t 1 ]
        then
          # show usage if stdout is a terminal
          log SCREEN "usage: $0 (INPUT|OUTPUT) DOMAIN (TCP|UDP) PORT"
        fi
        exit 1
      }
      
      IPSET=/usr/sbin/ipset
      SCRIPT=$0
      CHAIN=$1
      HOST=$2
      PORT=$4
      
      # Expiry Timeout
      TIMEOUT=7200
      
      if [ $# -ne 4 ]
      then
        usage "invalid number of arguments"
      fi
      
      if [ "$1" != "INPUT" -a "$1" != "OUTPUT" ]
      then
        usage "invalid argument '$1'"
      fi
      
      PROTOCOL=`echo $3 | awk '{print tolower($0)}'`
      if [ "$PROTOCOL" != "tcp" -a $PROTOCOL != "udp" ]
      then
        usage "invalid argument '$3'"
      fi
      IPS=`host $HOST | awk '/has address/ {print $NF}' | egrep -e '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'`
      if [ -z "$IPS" ]; then
        log_msg "lookup for host $HOST returns empty string"
        exit 1
      fi
      
      if [ "$CHAIN" == "OUTPUT" ]
      then
        setname="dynamic-out"
        table=filter
        chain=OUTPUT_allow
        priority=0
        #args="-p $PROTOCOL -m conntrack --ctstate NEW -m set --match-set $setname dst,dst -j ACCEPT"
        args="-m conntrack --ctstate NEW -m set --match-set $setname dst,dst -j ACCEPT"
      
        # assure netplace chains are installed
        if ! firewall-cmd --direct --query-chain ipv4 $table $chain >/dev/null
        then
          log_msg "failed to add dynamic rule, netplace chains not installed"
          exit 1
        fi
      
        # assure used ipset exists
        if ! $IPSET list -name $setname >/dev/null 2>&1
        then
          # create ipset
          $IPSET create $setname hash:ip,port timeout $TIMEOUT
        fi
      
        # assure required firewalld rule exists
        if ! firewall-cmd --direct --query-rule ipv4 $table $chain $priority $args >/dev/null
        then
          # add firewalld direct rule
          firewall-cmd --direct --add-rule ipv4 $table $chain $priority $args >/dev/null
        fi
      
        for ip in $IPS
        do
          # push ip on ipset
          $IPSET -exist add $setname "$ip,$PROTOCOL:$PORT" timeout $TIMEOUT
        done
      else
        log_msg "chain '$CHAIN' not yet implemented"
        exit 1
      fi
      
      • OUTBOUND_DYNAMIC="
          downloads.wordpress.org|tcp|443
          downloads.wordpress.org|tcp|80
          api.wordpress.org|tcp|443
          api.wordpress.org|tcp|80
          piwik.org|tcp|80
          graph.facebook.com|tcp|443
        "