Firewall from RedOS

In this article I want to share my experience in installing and configuring a firewall based on Linux RHEL, in particular RedOS.

What is NGFW (Next Generation Firewall)? I took the FW Check Point model as a basis. This means, at a minimum:

  • Firewall Blade – The actual firewall itself

  • IPS – Intruder Prevention System – a system for detecting and blocking attacks based on behavioral analysis using a signature database

  • IPSec – building VPN tunnels (site-2-site, client-2-site)

  • Application Control / URL Filtering – application control and URL filtering

  • Antivirus / AntiBot – checking for viruses/bots on the fly, packet analysis

  • SandBlast – sandbox with cleaning active content in files

At the moment, from the entire list of components, only FW, IPS, IPSec VPN are implemented. Let me remind you that all components are OpenSource, and RedOS itself is free for home use.

Software versions:
RedOS Murom 7.3.3 (server-minimal)
Suricata 7.0.1
WireGuard 1.0
OpenVPN 2.6.0
ShadowSocks 2.9.1
StrongSwan 5.9.9

We will use nftables as FW (in RedOS, although it is built into the kernel, there is no management utility itself, it is assumed that the administrator himself will select the firewalld, nft, iptables utility)

When setting up all VPN services, it is important to know that the networks issued to clients should not overlap, repeat, etc.

Let me say right away that it is better to do all settings under root. Now I’m going to get some hate, but I’ll let you know right away that NGFW is not a user task. And yes, SSH access should only be opened for internal administrators; it is better not to open it from the outside. Also set PermitRootLogin to no, use sudoers, etc. and it is advisable to use ssh keys with a password. And yes, the Listen parameter in /etc/ssh/sshd_config is better to specify a specific IP from the internal network.

So let’s get started. Stage 1. Setting up FW

Of course, the first, or rather the zero action, after installing a clean OS must be updated.

dnf update -y
reboot

Firstly, you need to install and pre-configure FW, for this we install and configure nftables. We first check what is installed.

iptables -V
iptables v1.8.9 (nf_tables)

In the output, nf_tables indicates that the kernel uses nftables. In my case, I used a mininal server installation, so I don’t have any “body kits”. But for the general case, you should check and, if necessary, uninstall unnecessary

Removing nftables body kits
systemctl status firewalld
systemctl stop firewalld
systemctl disable firewalld
dnf remove firewalld

Let’s check if all the necessary kernel modules are loaded. xt_conntrack / nf_conntrack (session state monitoring) and 802.1q (for VLAN support)

lsmod | grep xt_conntrack
lsmod | grep 8021q

If there are none, you need to check if these modules are available

modprobe xt_conntrack
modprobe 8021q

If there are no errors, then add modules to load at boot time. Create the file /etc/modules-load.d/mymod.conf

xt_conntrack
8021q

Now let’s install the necessary nft utility and configure the service to start

dnf install nftables
systemctl enable nftables --now

Next, in the file /etc/sysconfig/nftables.conf we indicate where to load the FW config from

include "/etc/nftables/mynetworkfw.nft"

Then there are 2 options for managing the firewall, through the config and service, or with direct nft commands and saving the config nft list ruleset > /etc/nftables/mynetworkfw.nft. Fundamentally there is no difference, however, when the service is rebooted, Handle rules will be reset (the same thing when rebooting system as a whole). When editing rules via nft commands, Handle will be unique.

Now you actually need the default nftables config. In particular, input/output and the forward chain itself. Below is an example file /etc/nftables/mynetworkfw.nft

table inet fw {
        chain incoming {
                type filter hook input priority filter; policy drop;
                ct state established,related accept
                ip saddr { 192.168.10.50, 192.168.20.25 } tcp dport 22 counter accept comment "SSH_admin"
                iifname "lo" accept comment "Localhost"
                # IPSec VPN Clients
                udp port 1194 counter accept comment "OpenVPN_clients"
                udp port 51820 counter accept comment "WireGuard_clients"
                udp port { 4500,500 } counter accept comment "IPSec_IKEv2_clients"
                tcp port 26789 counter accept comment "ShadowSocks_clients"
        }

        chain outgoing {
                type filter hook output priority filter; policy drop;
                ct state established,related accept
                ip daddr 192.168.1.1 udp dport 53 oifname "ens224.40" counter accept comment "DNS_UDP_requests"
                ip daddr 192.168.1.1 tcp dport 53 oifname "ens224.40" counter accept comment "DNS_TCP_requests"
                tcp dport { 80, 443, 21 } counter accept comment "FW_to_Inet"
                oifname "lo" accept comment "Localhost"
        }

        chain forwarding {
                type filter hook forward priority filter; policy drop;
                ct state established,related accept
                counter queue to 0
        }
}

Let me explain from the config, 192.168.10.50, 192.168.20.25 are the IP addresses of NGFW administrators. 192.168.1.1 is our router, connected to the Internet and performing NAT and playing the role of a DNS server on the local network. Of course this is just an example. Please note that traffic filtering is carried out not only at the entrance to the server, but also at the exit. When we finish setting up NGFW, the main chain will be forwarding, there will be NGFW rules. I’m getting ahead of myself, counter queue to 0 is already an IPS rule, it will need to be specified after launching Suricata. In addition, FW comes pre-configured with VPN clients with default ports. If you specify different ports in the settings of VPN services, then they must be taken into account in FW. Interfaces ens192 (external) and ens224 (internal), the latter of which receives trunc (802.1q). All interface settings are located in /etc/sysconfig/network-scripts/, where ifcfg- – interface settings, route- route settings if the interface does not have 1. Examples of such settings are in the spoiler

Interface settings

ifcfg-ens192

TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="manual"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="ens192"
DEVICE="ens192"
ONBOOT="yes"
UUID="3cec767a-e4ba-3130-839e-5588d3602a81"
IPADDR="192.168.1.254"
NETMASK="255.255.255.0"
GATEWAY="192.168.1.1"
DNS1="192.168.1.1"
DNS2="8.8.8.8"

You can find out the UUID of the interface via nmcli con show

from here DNS will go to /etc/resolv.conf, and GATEWAY will go to the routing table, DEFROUTE=yes means that route 0.0.0.0/0.0.0.0 will go through GATEWAY

ifcfg-ens224

BOOTPROTO="none"
DEVICE="ens224"
ONBOOT="yes"
TYPE="Ethernet"

Please note that there are no special settings here. This is an interface publication for use in 802.1q. The UUID is also omitted here to prevent NetworkManager from managing this stub.

ifcfg-ens224.20

DEVICE=ens224.20
BOOTPROTO=none
ONBOOT=yes
IPADDR=192.168.20.1
PREFIX=24
NETWORK=192.168.20.0
VLAN=yes

after writing all the configuration files, restart NetworkManager

systemctl restart NetworkManager

So, we have pre-configured FW. FW rules are applied “on the fly”, which means that when manually writing rules using the nft command, creating a chain in the table, do not rush to set “drop”, initially it is better to set “accept” so as not to lose the connection to the server.

Stage 2. Installation of Suricata.

By default, the Fedora Extra (epel) repository contains the Suricata package, but it is compiled with restrictions, and the Python version will not suit us, so we will have to build from source. Let’s install the epel repository, download the Suricata source and install the necessary components for compilation.

wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum localinstall epel-release-latest-7.noarch.rpm
cd /usr/src
wget https://www.openinfosecfoundation.org/download/suricata-7.0.1.tar.gz
tar xzvf suricata-7.0.1.tar.gz
rm -f suricata-7.0.1.tar.gz
cd suricata-7.0.1/
dnf groupinstall "Development Tools"
dnf install -y pcre2-devel libyaml-devel jansson-devel libpcap-devel pcapy python3-pcapy libcap-ng-devel file-devel lz4-devel rustc cargo
dnf install -y libunwind libunwind-devel GeoIP-devel zlib zlib-devel libnetfilter_queue-devel libnetfilter_log-devel
./configure --enable-nfqueue --enable-nflog --prefix=/usr/ --sysconfdir=/etc/ --localstatedir=/var/
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py
python -m pip install pyyaml
make
make install-full

Next we need to fix a few things. First, you need to configure the launch of the suricata service. To do this, create the file /usr/lib/systemd/system/suricata.service

[Unit]
Description=Suricata IDS/IDP daemon
After=network.target
Requires=network.target
Documentation=man:suricata(8) man:suricatasc(8)
Documentation=https://redmine.openinfosecfoundation.org/projects/suricata/wiki

[Service]
Type=forking
Environment=LD_PREDLOAD=/usr/lib/libtcmalloc_minimal.so.4
Environment=CFG=/etc/suricata/suricata.yaml PID=/var/run/suricata/suricata.pid
CapabilityBoundingSet=CAP_NET_ADMIN
PIDFile=/var/run/suricata/suricata.pid
ExecStart=/usr/bin/suricata -D -q 0 -q 1 -c $CFG --pidfile $PID -D
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill $MAINPID
PrivateTmp=yes
InaccessibleDirectories=/home /root
ReadOnlyDirectories=/boot /usr /etc

[Install]
WantedBy=multi-user.target

Next we do chmod 755 /usr/lib/systemd/system/suricata.service And systemctl daemon-reload. We check access rights to the created folders, files, etc. /var/run/suricata, /etc/suricata/, /var/log/suricata. If all rights are set correctly, then there will be no problems with launching. We start the service and set it to autostart. systemctl enable suricata --now

Next, we add database updating and reloading of these rules to cron. crontab -e

0 0 * * * /usr/lib/suricata-update >>/var/log/suricata/update.log
5 0 * * * /usr/lib/suricatasc -c reload-rules

Stage 3. Part 1: Installing WireGuard

Installing WireGuard on RedOS is not a trivial task.

First you need to install the necessary repositories. In particular, from Oracle Linux 7.

cd
wget https://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oraclelinux-release-el7-1.0-17.el7.x86_64.rpm
wget https://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oraclelinux-developer-release-el7-1.0-6.el7.x86_64.rpm
yum localinstall oraclelinux-release-el7-1.0-17.el7.x86_64.rpm
yum localinstall oraclelinux-developer-release-el7-1.0-6.el7.x86_64.rpm

Then perform preconfiguration and installation in accordance with Installation manual WireGuard

yum-config-manager --disable ol7_developer
yum-config-manager --enable ol7_developer_UEKR6
yum-config-manager --save --setopt=ol7_developer_UEKR6.includepkgs="wireguard-tools*"
dnf install wireguard-tools

After which, these repositories can be disabled/deleted, etc. They won’t be needed anymore, but they might get in the way. It’s better to turn it off and then clean it up. dnf clean packages

yum-config-manager --disable ol7_developer
yum-config-manager --disable ol7_developer_UEKR6
# и д.р. пакеты от Oracle Linux. либо вручную в файлах /etc/yum.repos.d/.repo
# установить параметр enable=0
# oraclelinux-developer-ol7.repo
# oracle-linux-ol7.repo
# uek-ol7.repo
# virt-ol7.repo

After which we also check the presence of the kernel module, its autoloading, etc.

lsmod | grep wireguard
modprobe wireguard
echo wireguard >>/etc/modules-load.d/mymod.conf

We create keys. The public key will be needed by clients to connect to our server. We use the private key in the tunnel config.

cd /etc/wireguard
wg genkey | tee priv.key | wg pubkey > pub.key
chmod 600 *.key

Setting up a wireguard connection. Create the file /etc/wireguard/wgtun.conf

[Interface]
PrivateKey = 8Kfqf4QdHL1pBKxT1NknbC7k9JCjyRj6hO1Mj+a4yU8=
Address = 172.16.0.1/23
ListenPort = 51820

# Далее настройки каждого клиента
# Клиент 1
[Peer]
PublicKey = OIhY7IdxElaDH0amgCiZnLXx22sFn8QV8t40iihLaz8=
AllowedIPs = 172.16.0.2/30

# Клиент 2
[Peer]
PublicKey = QKqUuHDAtSw/xYs2AywdqiRHvcr1UzBwrKY7nphU8xg=
AllowedIPs = 172.16.0.5/30

Then we check it, if there are no errors, then enable the service

wg-quick up wgtun
systemctl enable wg-quick@wgtun.service

Stage 3. Part 2. Installing OpenVPN / ShadowSocks

Installing OpenVPN is simple, RedOS has 2 versions of OpenVPN and OpenVPN-GOST. Who likes which one? I don’t need GOST, so everything is simple here. dnf install openvpn -y

OpenVPN configuration example. I will describe the creation of the CA below. This is the CA we will use for OpenVPN/StrongSwan. Please note that what is critical for OpenVPN configurations is the placement of the client and server configs. In general, specifying port is mandatory for server and optional for client. We will start the OpenVPN service like this: systemctl start openvpn@config.service (for the configuration file /etc/openvpn/config.conf), systemctl start openvpn-client@config.service (for the file /etc/openvpn/client/config.conf) or systemctl start openvpn-server@config.service (for file /etc/openvpn/server/config.conf). In our case, this is the file /etc/openvpn/server/myvpnserver.conf

port 1194
proto udp
dev tun0

ca /etc/openvpn/server/myvpnserver/ca.crt
cert /etc/openvpn/server/myvpnserver/server.crt
key /etc/openvpn/server/myvpnserver/server.key
crl-verify /etc/openvpn/server/myvpnserver/crl.pem
dh /etc/openvpn/server/myvpnserver/dh.pem

tls-server
tls-timeout 120
tls-auth /etc/openvpn/server/myvpnserver/ta-srv.key 0

server 172.16.2.0 255.255.254.0
route 172.16.2.0 255.255.254.0

;routes
;see DEFAULT in CCD

;DHCP-Option
;see DEFAULT in CCD

ifconfig-pool-persist /etc/openvpn/server/00-universal/cips
client-config-dir /etc/openvpn/server/00-universal/ccd

management localhost 7510 /etc/openvpn/server/myvpnserver/pwd
keepalive 10 120
comp-lzo

persist-key
persist-tun

status /var/log/openvpn/myvpnserver-status.log
log /var/log/openvpn/myvpnserver-log.log

Now before starting the service you need to create all the files used and configure SELinux

dnf install libsemanage libsemanage-python
semanage port -a -t openvpn_port_t -p tcp 7510
mkdir /etc/openvpn/server/myvpnserver
mkdir /etc/openvpn/server/myvpnserver/ccd
mkdir /var/log/openvpn
echo "superhiddenpassword" >/etc/openvpn/server/myvpnserver/pwd
chmod 700 /etc/openvpn/server/myvpnserver/pwd
openvpn --genkey --secret /etc/openvpn/server/myvpnserver/ta-srv.key

OpenVPN will create the cips file itself; it will store the IP addresses assigned to users/servers. About certificates, Diffie-Hellman keys, certificate revocation lists below, in the section about StrongSwan. In the meantime, let’s check the launch of OpenVPN (there will be errors due to missing files)

systemctl start openvpn-server@myvpnserver.service
# увидели ошибку, теперь добавим в загрузку, но без запуска
systemctl enable openvpn-server@myvpnserver.service

Installing ShadowSocks is not trivial and will again require assembly and compilation

dnf install git pcre-devel asciidoc xmlto mbedtls-devel libsodium-devel libcares-devel libev-devel
cd /usr/src
git clone https://github.com/shadowsocks/shadowsocks-libev.git
cd shadowsocks-libev/
git submodule update --init --recursive
./autogen.sh
./configure
make
make install

We create a user, a config folder and a service (for convenience, an openvpn-like service)

adduser --system --no-create-home -s /sbin/nologin shadowsocks
mkdir -p /etc/shadowsocks

Contents of the file /usr/lib/systemd/system/shadowsocks@.service

[Unit]
Description=Shadowsocks proxy server

[Service]
User=root
Group=root
Type=simple
ExecStart=/usr/local/bin/ss-server -c /etc/shadowsocks/%i.json -a shadowsocks -v start
ExecStop=/usr/local/bin/ss-server -c /etc/shadowsocks/%i.json -a shadowsocks -v stop

[Install]
WantedBy=multi-user.target

Approximate contents of the ss config (/etc/shadowsocks/main.json)

{
    "server":"178.128.122.54",
    "server_port": 26789,
    "password":"your_password",
    "timeout": 300,
    "method": "aes-256-gcm",
    "fast_open": true
}

Rights and service startup

chmod 755 /etc/systemd/system/shadowsocks@.service
systemctl daemon-reload
systemctl enable shadowsocks@main.service --now

Stage 3. Part 3. Installing and configuring StrongSwan (IPSec IKEv2)

Install and launch

dnf install strongswan
systemctl enable strongswan --now

Create a CA (without it you cannot build tunnels based on certificates). It’s better not to keep such a CA on a server on a public network, at least not private key. For this task, you can normally use strongswan pki, openssl, or the ready-made easy-rsa script. We will take the latter, since we will be making a universal CA for OpenVPN and StrongSwan services.

dnf install easy-rsa
# забираем установленный набор в папку
mkdir /usr/ca
chmod 700 /usr/ca
cd /usr/ca
cp -Rv /usr/share/easy-rsa/3.0.8/ ./
# Инициируем CA
./easyrsa init-pki
./easyrsa build-ca
./easyrsa gen-dh
# Создаем сертификат для OpenVPN сервера
./easyrsa gen-req ovpn nopass
./easyrsa sign-req server ovpn
./easyrsa gen-crl
# Копируем нужные ключи для OpenVPN
cp ./pki/ca.crt /etc/openvpn/server/myvpnserver/
cp ./pki/issued/ovpn.crt /etc/openvpn/server/myvpnserver/server.crt
cp ./pki/private/ovpn.key /etc/openvpn/server/myvpnserver/server.key
cp ./pki/dh.pem /etc/openvpn/server/myvpnserver/
cp ./pki/crl.pem /etc/openvpn/server/myvpnserver/
# Можно запускать наш OpenVPN
systemctl start openvpn-server@myvpnserver.service

We initiated the CA, issued the certificate, launched OpenVPN. What’s next? Setting up strongswan.

# Создаем новый тип сертификата IPSec
cp ./x509-types/serverClient ./x509-types/ipsec
# Заменяем строку
extendedKeyUsage = serverAuth,clientAuth
# на
extendedKeyUsage = serverAuth, clientAuth, 1.3.6.1.5.5.7.3.17
# и добавляем Subject Alternative Names. Важно указать реальные IP и привязанные при наличии DNS
subjectAltName=DNS:vpn.example.com,DNS:localhost,IP:127.0.0.1,IP:185.185.101.23,DNS:185.185.101.23
# В файл./vars добавляем переменные
set_var EASYRSA_CERT_EXPIRE     3650
set_var EASYRSA_EXTRA_EXTS      "DNS:vpn.example.com,DNS:localhost,IP:127.0.0.1,IP:185.185.101.23,DNS:185.185.101.23"
# Все готово, выпускаем сертификат
./easyrsa gen-req swan nopass
./easyrsa sign-req ipsec swan
# Проверяем наши сертификаты
openssl x509 -noout -text -in ./pki/issued/swan.crt
# Должны быть указаны параметры:
X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication, 1.3.6.1.5.5.7.3.17
X509v3 Subject Alternative Name:
                DNS:vpn.example.com, DNS:localhost, IP Address: 127.0.0.1, IP Address:185.185.101.23, DNS:185.185.101.23
# Теперь копируем все необходимое
cp ./pki/ca.crt /etc/strongswan/ipsec.d/cacerts/ca.pem
cp ./pki/issued/swan.crt /etc/strongswan/ipsec.d/certs/swan.pem
cp ./pki/private/swan.key /etc/strongswan/ipsec.d/private/swan.pem
# Подключаем сертификат, в файл /etc/strongswan/ipsec.secrets вписываем приватный ключ
: RSA swan.pem

Now we write the config /etc/strongswan/ipsec.conf

config setup
    charondebug="ike 2, knl 2, cfg 2, net 2, esp 2, dmn 2,  mgr 2"

conn %default
    keyexchange=ikev2
    ike=aes128-sha256-ecp256,aes256-sha384-ecp384,aes128-sha256-modp2048,aes128-sha1-modp2048,aes256-sha384-modp4096,aes256-sha256-modp4096,aes256-sha1-modp4096,aes128-sha256-modp1536,aes128-sha1-modp1536,aes256-sha384-modp2048,aes256-sha256-modp2048,aes256-sha1-modp2048,aes128-sha256-modp1024,aes128-sha1-modp1024,aes256-sha384-modp1536,aes256-sha256-modp1536,aes256-sha1-modp1536,aes256-sha384-modp1024,aes256-sha256-modp1024,aes256-sha1-modp1024!
    esp=aes128gcm16-ecp256,aes256gcm16-ecp384,aes128-sha256-ecp256,aes256-sha384-ecp384,aes128-sha256-modp2048,aes128-sha1-modp2048,aes256-sha384-modp4096,aes256-sha256-modp4096,aes256-sha1-modp4096,aes128-sha256-modp1536,aes128-sha1-modp1536,aes256-sha384-modp2048,aes256-sha256-modp2048,aes256-sha1-modp2048,aes128-sha256-modp1024,aes128-sha1-modp1024,aes256-sha384-modp1536,aes256-sha256-modp1536,aes256-sha1-modp1536,aes256-sha384-modp1024,aes256-sha256-modp1024,aes256-sha1-modp1024,aes128gcm16,aes256gcm16,aes128-sha256,aes128-sha1,aes256-sha384,aes256-sha256,aes256-sha1!
    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftsubnet=0.0.0.0/0
    leftcert=swan.pem
    right=%any
    rightdns=8.8.8.8,8.8.4.4
    rightsourceip=172.16.4.0/23

conn IPSec-IKEv2
    keyexchange=ikev2
    auto=add

conn IPSec-IKEv2-EAP
    also="IPSec-IKEv2"
    rightauth=eap-mschapv2
    rightauthby2=pubkey
    rightsendcert=never
    eap_identity=%any

conn CiscoIPSec
    keyexchange=ikev1
    forceencaps=yes
    authby=xauthrsasig
    xauth=server
    auto=add

StrongSwan customers will need their certificates in P12. For OpenVPN you can use pem, or when using eToken, ruToken, JaCarta, etc. – pfx with a password. To do this, you can use the cmdlets built into easyrsa. export-p7, export-p12.

Step 4: Enable Routing

To enable routing, we make changes to sysctl. /etc/sysctl.d/10-forward.conf

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
# Включаем "на лету". После переагрузки ОС параметры подтянутся автоматически.
sysctl -p /etc/sysctl.d/10-forward.conf

Afterword

Well, in the end, after setting up the necessary VPN services, do not forget to open incoming and outgoing rules for them on FW. Also, for routing and NAT, it is necessary to register the appropriate ME rules.

useful links

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *