Posts Secured encrypted networks with OpenVPN and Suricata
Post
Cancel

Secured encrypted networks with OpenVPN and Suricata

Intro

Most of my recent posts have been centered around red team security, but this article will be more from a blue team perspective of network security. I’ll cover setting up an offsite server to be an encrypted network internet gateway for local endpoints secured with an active Intrusion Prevention System (Suricata IPS) coupled Oinkmaster rule pulling software for automatic updates. Everything will be encrypted over the air and wire via an OpenVPN configuration. All data also goes through iptables, which does the packet forwarding.

OpenVPN

First you’ll need to install OpenVPN and it’s dependancies. I suggest using your distribution’s package manager. Since my gateway is running Debian, we’ll be using apt.

sudo apt-get update
sudo apt-get install openvpn

You should also create a vpn group in case OpenVPN gets owned.

groupadd vpn

Next you’ll need to generate some keys. We’ll be using easy-rsa.

cd /usr/share/easy-rsa/
. ./vars
./clean-all
./build-ca
./build-dh
./build-req yourclientsname
./build-key yourclientsname
./build-key-server

Make sure your permission are correct so no unauthorized users can read the ca and server key or you’ll open yourself up to a man in the middle attack. Now you’ll need to configure the OpenVPN server. You can tweak this to your liking, but our lab server config looks something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cipher AES-256-CBC                    # network's encryption
auth SHA256                           # authentication encryption
dh /etc/openvpn/server/dh.pem         # diffie pem
user nobody                           # unpriviledged user
group vpn                             # group to run in
proto udp                             # i'm using the UDP protocol because of low overhead
dev tun                               # we want to use a tun device for udp
cert /etc/openvpn/server/server.crt   # server's cert generated by easy-rsa
key /etc/openvpn/server/server.key    # server's key generated by easy-rsa
ca /etc/openvpn/server/ca.crt         # certificate authority gen'd by easy-rsa
tls-server                            # tells clients this is a tls server
server 10.0.0.0 255.255.255.0         # client's dhcp pool
push "redirect-gateway def1"          # this pushes the gateway to the client (needed if exit)
push "dhcp-option DNS 8.8.8.8"        # dns server to go to clients (needed if an exit)

To get the OpenVPN server to actually be a gateway we need to do a couple more steps, such as turning IP forwarding on, as well as configuration of iptables for masquerade.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Generated by iptables-save v1.8.4 on Tue May 18 16:35:45 2021
*nat
:PREROUTING ACCEPT [8965:625941]
:INPUT ACCEPT [343:19575]
:OUTPUT ACCEPT [10:757]
:POSTROUTING ACCEPT [343:22904]
-A POSTROUTING -s 10.0.0.0/8 -o eth0 -j MASQUERADE
COMMIT
# Completed on Tue May 18 16:35:45 2021
# Generated by iptables-save v1.8.4 on Tue May 18 16:35:45 2021
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:LOGGING - [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-I INPUT -m mark ! --mark 1/1 -j NFQUEUE
-A INPUT -p tcp -m tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -p udp -m udp --dport 1194 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -j LOGGING
-I FORWARD -m mark ! --mark 1/1 -j NFQUEUE
-A FORWARD -s 10.0.0.0/8 -p tcp -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -d 10.0.0.0/8 -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -j LOGGING
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-I OUTPUT -m mark ! --mark 1/1 -j NFQUEUE
-A OUTPUT -p icmp -m icmp --icmp-type 0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p icmp -m icmp --icmp-type 8 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -j LOGGING
-A LOGGING -m limit --limit 2/min -j LOG --log-prefix "iptables_drop: "
-A LOGGING -j DROP
COMMIT
# Completed on Tue May 18 16:35:45 2021

Then pull in the rules with:

iptables-restore < iptablesfile

I also suggest installing iptables-persistent if available.

Also finally we need to turn on IP forwarding:

sudo sysctl -w net.ipv4.ip_forward=1
sudo vim /etc/sysctl.conf # (then add net.ipv4.ip_forward = 1 to make it perminate)

Then spin up OpenVPN with:

sudo systemctl start openvpn-server

You’ll also need to generate .ovpn files for your clients connecting. You can use this script to genearte the files, then securely transfer them to your clients.

#!/bin/sh

##
## Usage: ./ovpn-writer.sh SERVER CA_CERT CLIENT_CERT CLIENT_KEY > client.ovpn
##

server=${1?"The server address is required"}
cacert=${2?"The path to the ca certificate file is required"}
client_cert=${3?"The path to the client certificate file is required"}
client_key=${4?"The path to the client private key file is required"}
#tls_key=${5?"The path to the TLS shared secret file is required"}

cat << EOF
client
dev tun
remote ${server}
resolv-retry infinite
nobind
persist-key
persist-tun
verb 1
auth SHA256
keepalive 10 120
port 1194
proto udp
remote-cert-tls server
cipher AES-256-CBC
tls-client
pull
<ca>
EOF
cat ${cacert}
cat << EOF
</ca>
<cert>
EOF
cat ${client_cert}
cat << EOF
</cert>
<key>
EOF
cat ${client_key}
cat << EOF
</key>
EOF

Lastly, install the client .ovpn files on each machine you want to connect. (repeat the above steps for generating a client key for each client!) OpenVPN should now work. If you have any trouble, check /var/log/openvpn/openvpn.log, and that should point you in the general direction of your problem.

Suricata IPS

To secure the network from the internet, we should install an IPS. In this case we’re using Suricata coupled with Oinkmaster and iptables.

First we need to install Suricata and Oinkmaster:

sudo apt-get install suricata oinkmaster

We also need to install the rules (all the .rules files from suricata.yaml). Let’s do this with Oinkmaster.

useradd suri
groupadd suri

Then edit /etc/suricata/suricata.yaml to your liking. Mine looks like:

%YAML 1.1
---

vars:
  address-groups:
    HOME_NET: "[10.0.0.0/8]"
    EXTERNAL_NET: "!$HOME_NET"
    HTTP_SERVERS: "$HOME_NET"
    SMTP_SERVERS: "$HOME_NET"
    SQL_SERVERS: "$HOME_NET"
    DNS_SERVERS: "$HOME_NET"
    TELNET_SERVERS: "$HOME_NET"
    AIM_SERVERS: "$EXTERNAL_NET"
    DC_SERVERS: "$HOME_NET"
    DNP3_SERVER: "$HOME_NET"
    DNP3_CLIENT: "$HOME_NET"
    MODBUS_CLIENT: "$HOME_NET"
    MODBUS_SERVER: "$HOME_NET"
    ENIP_CLIENT: "$HOME_NET"
    ENIP_SERVER: "$HOME_NET"

  port-groups:
    HTTP_PORTS: "80"
    SHELLCODE_PORTS: "!80"
    ORACLE_PORTS: 1521
    SSH_PORTS: 22
    DNP3_PORTS: 20000
    MODBUS_PORTS: 502
    FILE_DATA_PORTS: "[$HTTP_PORTS,110,143]"
    FTP_PORTS: 21
    GENEVE_PORTS: 6081
    VXLAN_PORTS: 4789
    TEREDO_PORTS: 3544

default-log-dir: /var/log/suricata

stats:
  enabled: yes
  interval: 8

outputs:
  - fast:
      enabled: yes
      filename: fast.log
      append: yes

  - eve-log:
      enabled: yes
      filename: eve.json
      pcap-file: false
      community-id: false
      community-id-seed: 0
      xff:
        enabled: no
        mode: extra-data
        deployment: reverse
        header: X-Forwarded-For
      types:
        - alert:
            tagged-packets: yes
        - anomaly:
            enabled: yes
            types:
        - http:
        - dns:
        - tls:
        - files:
        - drop:
        - smtp:
        - ftp
        - rdp
        - nfs
        - smb
        - tftp
        - ikev2
        - dcerpc
        - krb5
        - snmp
        - rfb
        - sip
        - dhcp:
            enabled: yes
            extended: no
        - ssh
        - mqtt:
        - stats:
        - flow
  - http-log:
      enabled: yes
      filename: http.log
      append: yes
  - tls-log:
      append: yes
  - tls-store:
      enabled: no
  - pcap-log:
      enabled: no
      filename: log.pcap
      limit: 1000mb
      max-files: 2000
      compression: none
  - alert-debug:
      enabled: no
      filename: alert-debug.log
      append: yes
  - alert-prelude:
      enabled: no
      profile: suricata
      log-packet-content: no
      log-packet-header: yes
  - stats:
      enabled: yes
      filename: stats.log
  - syslog:
      enabled: no
      facility: local5
  - file-store:
      version: 2
      enabled: no
      xff:
        enabled: no
        mode: extra-data
        deployment: reverse
        header: X-Forwarded-For
  - tcp-data:
      enabled: no
      type: file
      filename: tcp-data.log

  - http-body-data:
      enabled: no
      type: file
      filename: http-data.log

  - lua:
      enabled: no
      scripts:

logging:
  default-log-level: notice
  default-output-filter:
  outputs:
  - console:
      enabled: yes
  - file:
      enabled: yes
      level: info
      filename: suricata.log
  - syslog:
      enabled: no
      facility: local5
      format: "[%i] <%d> -- "

app-layer:
  protocols:
    rfb:
      enabled: yes
      detection-ports:
        dp: 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909
    mqtt:
    krb5:
      enabled: yes
    snmp:
      enabled: yes
    ikev2:
      enabled: yes
    tls:
      enabled: yes
      detection-ports:
        dp: 443
    dcerpc:
      enabled: yes
    ftp:
      enabled: yes
    rdp:
    ssh:
      enabled: yes
    http2:
      enabled: no
    smtp:
      enabled: yes
      raw-extraction: no
      mime:
        decode-mime: yes
        decode-base64: yes
        decode-quoted-printable: yes
        header-value-depth: 2000
        extract-urls: yes
        body-md5: no
      inspected-tracker:
        content-limit: 100000
        content-inspect-min-size: 32768
        content-inspect-window: 4096
    imap:
      enabled: detection-only
    smb:
      enabled: yes
      detection-ports:
        dp: 139, 445
    nfs:
      enabled: yes
    tftp:
      enabled: yes
    dns:
      tcp:
        enabled: yes
        detection-ports:
          dp: 53
      udp:
        enabled: yes
        detection-ports:
          dp: 53
    http:
      enabled: yes
      libhtp:
         default-config:
           personality: IDS
           request-body-limit: 100kb
           response-body-limit: 100kb
           request-body-minimal-inspect-size: 32kb
           request-body-inspect-window: 4kb
           response-body-minimal-inspect-size: 40kb
           response-body-inspect-window: 16kb
           response-body-decompress-layer-limit: 2
           http-body-inline: auto
           swf-decompression:
             enabled: yes
             type: both
             compress-depth: 100kb
             decompress-depth: 100kb
           double-decode-path: no
           double-decode-query: no
         server-config:
    modbus:
      enabled: no
      detection-ports:
        dp: 502
      stream-depth: 0
    dnp3:
      enabled: no
      detection-ports:
        dp: 20000
    enip:
      enabled: no
      detection-ports:
        dp: 44818
        sp: 44818
    ntp:
      enabled: yes
    dhcp:
      enabled: yes
    sip:
asn1-max-frames: 256

run-as:
  user: suri
  group: suri

pid-file: /run/suricata/suricata.pid

coredump:
  max-dump: unlimited

host-mode: auto

unix-command:
  enabled: auto

legacy:
  uricontent: enabled

action-order:
   - pass
   - drop
   - reject
   - alert

engine-analysis:
  rules-fast-pattern: yes
  rules: yes

pcre:
  match-limit: 3500
  match-limit-recursion: 1500

host-os-policy:
  windows: [0.0.0.0/0]
  bsd: []
  bsd-right: []
  old-linux: []
  linux: []
  old-solaris: []
  solaris: []
  hpux10: []
  hpux11: []
  irix: []
  macos: []
  vista: []
  windows2k3: []

defrag:
  memcap: 32mb
  hash-size: 65536
  prealloc: yes
  timeout: 60

flow:
  memcap: 128mb
  hash-size: 65536
  prealloc: 10000
  emergency-recovery: 30

vlan:
  use-for-tracking: true

flow-timeouts:
  default:
    new: 30
    established: 300
    closed: 0
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-closed: 0
    emergency-bypassed: 50
  tcp:
    new: 60
    established: 600
    closed: 60
    bypassed: 100
    emergency-new: 5
    emergency-established: 100
    emergency-closed: 10
    emergency-bypassed: 50
  udp:
    new: 30
    established: 300
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-bypassed: 50
  icmp:
    new: 30
    established: 300
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-bypassed: 50

stream:
  memcap: 64mb
  reassembly:
    memcap: 256mb
    toserver-chunk-size: 2560
    toclient-chunk-size: 2560
    randomize-chunk-size: yes

host:
  hash-size: 4096
  prealloc: 1000
  memcap: 32mb

decoder:
  teredo:
    enabled: true
  vxlan:
    enabled: true
  geneve:
    enabled: true

detect:
  profile: medium
  custom-values:
    toclient-groups: 3
    toserver-groups: 25
  sgh-mpm-context: auto
  inspection-recursion-limit: 3000
  prefilter:
    default: mpm
  grouping:
  profiling:
    grouping:
      dump-to-disk: false
      include-mpm-stats: false

mpm-algo: auto

spm-algo: auto

threading:
  set-cpu-affinity: no
  cpu-affinity:
    - management-cpu-set:
    - receive-cpu-set:
    - worker-cpu-set:
        cpu: [ "all" ]
        mode: "exclusive"
        prio:
          low: [ 0 ]
          medium: [ "1-2" ]
          high: [ 3 ]
          default: "medium"
  detect-thread-ratio: 1.0

luajit:
  states: 128

profiling:
  rules:
    enabled: yes
    filename: rule_perf.log
    append: yes
    limit: 10
    json: yes
  keywords:
    enabled: yes
    filename: keyword_perf.log
    append: yes
  prefilter:
    enabled: yes
    filename: prefilter_perf.log
    append: yes
  rulegroups:
    enabled: yes
    filename: rule_group_perf.log
    append: yes
  packets:
    enabled: yes
    filename: packet_stats.log
    append: yes
    csv:
      enabled: no
      filename: packet_stats.csv
  locks:
    enabled: no
    filename: lock_stats.log
    append: yes
  pcap-log:
    enabled: no
    filename: pcaplog_stats.log
    append: yes

nfq:
  mode: repeat
  fail-open: no

nflog:
  - group: 2
    buffer-size: 18432
  - group: default
    qthreshold: 1
    qtimeout: 100
    max-size: 20000


capture:

netmap:
 - interface: eth2
 - interface: default

pfring:
  - interface: eth0
    threads: auto
    cluster-id: 99
    cluster-type: cluster_flow
  - interface: default

ipfw:

napatech:
    streams: ["0-3"]
    enable-stream-stats: no
    auto-config: yes
    hardware-bypass: yes
    inline: no
    ports: [0-1,2-3]
    hashmode: hash5tuplesorted

default-rule-path: /var/lib/suricata/rules

rule-files:
 - emerging-policy.rules
 - botcc.portgrouped.rules
 - emerging-sql.rules
 - emerging-activex.rules
 - emerging-user_agents.rules
 - emerging-info.rules
 - emerging-malware.rules
 - emerging-exploit.rules
 - emerging-worm.rules
 - emerging-mobile_malware.rules
 - emerging-misc.rules
 - emerging-web_server.rules
 - emerging-web_client.rules
 - emerging-phishing.rules
 - emerging-scada.rules
 - emerging-rpc.rules
 - emerging-web_specific_apps.rules
 - emerging-shellcode.rules
 - emerging-netbios.rules
 - compromised.rules
 - emerging-icmp_info.rules
 - botcc.rules
 - emerging-coinminer.rules
 - emerging-adware_pup.rules
 - emerging-icmp.rules
 - emerging-p2p.rules
 - emerging-scan.rules
 - emerging-dns.rules
 - emerging-attack_response.rules
 - emerging-snmp.rules
 - emerging-chat.rules
 - emerging-exploit_kit.rules
 - emerging-imap.rules
 - emerging-hunting.rules
 - emerging-current_events.rules
 - emerging-dos.rules

classification-file: /etc/suricata/classification.config
reference-config-file: /etc/suricata/reference.config

We also need to install the rules (all the .rules files from suricata.yaml). Lets do this with Oinkmaster.

1
2
3
4
5
6
7
url = https://rules.emergingthreats.net/open/suricata-5.0/emerging.rules.tar.gz
skipfile local.rules
skipfile deleted.rules
skipfile snort.conf
modifysid emerging-exploit.rules, emerging-exploit_kit.rules, drop.rules, emerging-worm.rules "^alert " | "drop "
modifysid emerging-exploit.rules, drop.rules, emerging-worm.rules "^#alert " | "drop "
modifysid 2013457 "alert" | "#alert" # whitelist bitcoin wallet

Then pull the rules in with:

oinkmaster -c /etc/oinkmaster.conf -o /etc/suricata/rules/ # install rules
pkill -u suri -USR2 # live rule reload

I also recommend putting the last two commands in serial in the root user’s crontab so that the IPS will automatically update its rules. Then a systemd script:

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Suricata Intrusion Detection Service
After=syslog.target network-online.target

[Service]
ExecStartPre=/bin/rm -f /run/suricata/suricata.pid
ExecStart=/usr/bin/suricata -c /etc/suricata/suricata.yaml --pidfile /run/suricata/suricata.pid -q 0
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

Then install it:

sudo systemctl start suricata
sudo systemctl enable suricata

Now you can finally run tail -f /var/log/suricata/fast.log and see Suricata filtering your VPN traffic! Enjoy a little peace of mind. I am also a for-hire security professional, see my resume.

This post is licensed under CC BY 4.0 by the author.

The time I enumerated every GitHub admin

A case study whitepaper of the shadow-utils chfn, chsh, and userdel bug

Comments powered by Disqus.