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
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.