Linux Security Teil 3: Code-Details

Autor Renato Testa
Datum 27.02.2020
Lesezeit 12 Minuten

Code-Detail 1

Eine PKI erstellen

$ wget -P ~/ https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.6/EasyRSA-unix-v3.0.6.tgz

und installieren das tar-Archiv:

$ cd ~
$ tar zxvf EasyRSA-3.0.6.tgz

Beachte, dass wir im folgenden immer zwischen dem CA-System und dem VPN-Server hin und her springen müssen!

Genau das Gleiche machst du auf deinem openVPN-Server!

Wieder auf deinem CA-System, wechseln wir in das Verzeichnis «EasyRSA-3.0.6», welches beim entpacken der Tar-Archives erstellt wurde:

$ cd EasyRSA-3.0.6

Wir müssen nun die Daten zu deiner CA erfassen. Dafür kopieren wir “vars.example” und ändern diese Kopie:

$ cp vars.example vars
$ vi vars

Setze folgende Variablen nach deinen Anforderungen:

set_var EASYRSA_REQ_COUNTRY "CH"
set_var EASYRSA_REQ_PROVINCE "Wallis"
set_var EASYRSA_REQ_CITY "Betten"
set_var EASYRSA_REQ_ORG "Goatkeeper"
set_var EASYRSA_REQ_EMAIL "admin@goatkeeper.ch"
set_var EASYRSA_REQ_OU "Security Dpt."

Da EasyRSA SSL/TLS benutzt, vergewissere dich, dass “openssl” installiert ist:

$ openssl version
OpenSSL 1.1.1c 28 May 2019

Nun initialisieren wir die PKI:

$ ./easyrsa init-pki
...
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /home/user/EasyRSA-3.0.6/pki

Im nächsten Schritt werden zwei, für die CA sehr wichtige Dateien kreiert

ca.crt – das öffentliche Zertifikat der CA. Mit diesem werden wir später unserem openVPN-Server mitteilen, dass er Teil dieses Web-of-trust ist.

ca.key – der private Schlüssel der CA welcher zum signieren von Zertifikaten und Schlüsseln dieser CA benutzt wird:
Achtung: Dieser Schlüssel sollte nicht auf einem System liegen, welches online ist. Bewahre diese Datei auf einem ext. Datenträger auf und verwende sie nur bei Bedarf!!!

$ ./easyrsa build-ca nopass
. . .
Common Name (eg: your user, host, or server name) Easy-RSA CA:GoatHaven CA

Soweit so gut. Wir haben nun eine PKI erstellt. Bitte erinnere dich daran, dass wenn der “ca.key” in falsche Hände kommt, jeder Zertifikate für deine CA generieren und signieren kann.

Wechsle nun wieder zu deinem VPN-Server! Wir generieren nun den Schlüssel und Zertifikats-Request für deinen Server. Wechsle in das EasyRSA-Verzeichnis und initialisiere die PKI:

$ cd EasyRSA-3.0.6

$ ./easyrsa init-pki

Anschliessend generieren wir den Request:

$ ./easyrsa gen-req server nopass

mittels “nopass” verhindern wir, dass bei Verwendung des Requests jedesmal ein Passort abgefragt wird. Denn Server-Key kopieren wir nun nach “/etc/openvpn”:

$ sudo cp ~/EasyRSA-3.0.6/pki/private/server.key /etc/openvpn/

Den Request kopieren wir auf unser CA-System (PKI):

$ scp ~/EasyRSA-3.0.6/pki/reqs/server.req user@ip-deiner-CA:/tmp

Nun, wieder auf deinem CA-System (PKI) gehen wir wieder in das EasyRSA-Verzeichnis und importieren den Request:

$ cd ~/EasyRSA-3.0.6

$ ./easyrsa import-req /tmp/server.req server

und signieren anschliessend den Request:

$ ./easyrsa sign-req server server

Ob wir dieser Sache vertrauen wolle,n müssen wir noch bestätigen:

You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 3650 days:

subject=
commonName = server

Type the word 'yes' to continue, or any other input to abort.
Confirm request details: yes

Nun transferieren wir das signierte Zertifikat zu unseren VPN-Server:

$ scp pki/issued/server.crt user@your_vpn_server_ip:/tmp

und gleich noch das Zertifikat der CA:

$ scp pki/ca.crt user@your_vpn_server_ip:/tmp

Damit es nicht langweilig wird, loggen wir unser wieder auf dem VPN-Server ein und kopieren die Zertifikate:

$ sudo cp /tmp/{server.crt,ca.crt} /etc/openvpn/

Navigiere wieder zu deinem EasyRSA Directory:

$ cd EasyRSA-3.0.6/

Hier generierst du einen starken Diffie-Hellman-Schlüssel, welcher für den Austausch der Schlüssel gebraucht wird:

$ ./easyrsa gen-dh

Das dauert ein Momentchen. Ist das beendet, kreieren wir eine HMAC-Signature, um die Server-TLS-Integrität zu härten:

Nach Beendigung kopieren wir die Dateien in das “/etc/openvpn/” Verzeichnis:

$ sudo cp ~/EasyRSA-3.0.6/ta.key /etc/openvpn/

$ sudo cp ~/EasyRSA-3.0.4/pki/dh.pem /etc/openvpn/


Code-Detail 2

Generieren der Client Zertifikate/Schlüssel

#!/bin/bash

set -o errexit # be strong with errors
set -o nounset # be strong with unset vars

PROG="${0##/}" # Scriptname
USAGE="usage: $PROG clientname"
EASYRSA="EasyRSA-<VERSION>"
CA_SERVER="user@my-CA-server"
VPN_SERVER="<IP-Address of the openvpn server>"
VPN_SERVER_PORT="1194"
ZIP=/usr/bin/zip
if ! -x $ZIP ; then
echo "${PROG}: $ZIP not found, install it first" >&2
exit 1
fi

if (( $# != 1 )) ; then
echo $USAGE
exit 1
fi

if ! -d $EASYRSA ; then
echo "$EASYRSA missing or wrong version" >&2
exit 1
fi

CLIENT=$1
CLIENTCONFIG=$HOME/${CLIENT}-vpnconfig # define directory for config
echo "-----------------------------------------------------------------------------------------"
echo "
This script generates the keys/certs and a config file for your connetion to the openVPN
server.

EasyRSA is: $EASYRSA
CA-Server (PKI) is: $CA_SERVER
openVPN server is: $VPN_SERVER

Build a config for: $CLIENT
Config built in: $CLIENTCONFIG

NOTE: you need a working ssh-connection between your $VPN_SERVER and the $CA_SERVER!

If that's not what you want, hit ^C. Hit <ENTER> if that's OK
"
read OK

-d ${CLIENTCONFIG} || mkdir -pm 700 ${CLIENTCONFIG}

echo "generate the request"
cd ~/$EASYRSA
./easyrsa gen-req $CLIENT nopass
cp pki/private/${CLIENT}.key ${CLIENTCONFIG}

echo "secure copy the req to the CA-server"
scp pki/reqs/${CLIENT}.req $CA_SERVER:/tmp && stat=$? || stat=$?
case $stat in
0) ;; # all fine
*) echo "$PROG: scp to $CA_SERVER failed" >&2
exit 1
;;
esac

echo "Login to your CA-server and import/sign the request"

ssh -T $CA_SERVER "cd $EASYRSA;./easyrsa import-req /tmp/${CLIENT}.req $CLIENT;./easyrsa sign-req client $CLIENT" && stat=$? || stat=$?
case $stat in
0) ;; # all fine
*) echo "$PROG: scp to $CA_SERVER failed" >&2
exit 1
;;
esac

echo "Copy the ${CLIENT}.crt from your CA-Server to your local ${CLIENTCONFIG} directory."
scp ${CA_SERVER}:${EASYRSA}/pki/issued/${CLIENT}.crt ${CLIENTCONFIG}

cp ta.key ${CLIENTCONFIG}

echo "Copy the ca.crt (CA certificate) into your ${CLIENTCONFIG} directory"
scp root@${CA_SERVER}:/etc/openvpn/ca.crt ${CLIENTCONFIG}

cd $CLIENTCONFIG
echo -n "Create the ${CLIENT}.ovpn file now"
cat > ${CLIENTCONFIG}/${CLIENT}.ovpn << EdF
client
dev tun
persist-key
persist-tun
proto udp
nobind
remote-cert-tls server
auth SHA512
verb 3
remote ${VPN_SERVER} ${VPN_SERVER_PORT}

# To successfully import this profile, you
# want the client device's CA certificate copy,
# client certificate and key, and HMAC signature
# all in the same location as this .ovpn file.
ca ca.crt
cert ${CLIENT}.crt
key ${CLIENT}.key
tls-crypt ta.key
EdF

echo " done"

if -f ca.crt && -f ${CLIENT}.crt &&
-f ${CLIENT}.key && -f ta.key && -f ${CLIENT}.ovpn ; then

echo -n "Your kit seems complete. Will create ${CLIENT}.zip"
zip -r ${CLIENT}-openvpn.zip ${CLIENT}.ovpn ${CLIENT}.crt ${CLIENT}.key ca.crt ta.key
echo " done"

else
echo "you miss some files" >&2
exit 1
fi


exit 0

Code-Detail 3

Aufsetzen des openVPN-Servers

dev tun
persist-key
persist-tun
topology subnet
port 1194
proto udp
keepalive 10 120

# Location of certificate authority's cert.
ca /etc/openvpn/server/ca.crt

# Location of VPN server's TLS cert.
cert /etc/openvpn/server/server.crt

# Location of server's TLS key
key /etc/openvpn/server/server.key

# Location of DH parameter file.
dh /etc/openvpn/server/dh.pem

# The VPN's address block starts here.
server 10.89.0.0 255.255.255.0

explicit-exit-notify 1

# Drop root privileges and switch to the `ovpn` user after startup.
user ovpn

# OpenVPN process is exclusive member of ovpn group.
group ovpn

# Cryptography options. We force these onto clients by
# setting them here and not in client.ovpn. See
# `openvpn --show-tls`, `openvpn --show-ciphers` and
#`openvpn --show-digests` for all supported options.
tls-crypt /etc/openvpn/server/ta.key
auth SHA512 # This needs to be in client.ovpn too though.
tls-version-min 1.2
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256
ncp-ciphers AES-256-GCM:AES-256-CBC

# Logging options.
ifconfig-pool-persist ipp.txt
status openvpn-status.log
log /var/log/openvpn.log
verb 3

# Clients are to use this server as a network gateway.
push "redirect-gateway def1 bypass-dhcp"

# Push these DNS addresses to clients.
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"


Code-Details 4

rules.v6

*filter
:INPUT ACCEPT 0:0
:FORWARD ACCEPT 0:0
:OUTPUT ACCEPT 0:0
-A INPUT -j REJECT --reject-with icmp6-port-unreachable
-A FORWARD -j REJECT --reject-with icmp6-port-unreachable
-A OUTPUT -j REJECT --reject-with icmp6-port-unreachable
COMMIT

Code-Details 5

rules.v4

#
# NAT wird erlaubt aus dem openvpn Subnetz
#
*nat
:PREROUTING ACCEPT 2:92
:INPUT ACCEPT 0:0
:POSTROUTING ACCEPT 0:0
:OUTPUT ACCEPT 0:0
-A POSTROUTING -s 10.89.0.0/24 -o ens18 -j MASQUERADE
COMMIT
#
# INPUT/FORWARD/OUTPUT Definition
#
*filter
:INPUT ACCEPT 0:0
:FORWARD ACCEPT 0:0
:OUTPUT ACCEPT 0:0
#
# Loopback darf alles
#
-A INPUT -i lo -j ACCEPT
-A INPUT -s 127.0.0.0/8 ! -i lo -j REJECT --reject-with icmp-port-unreachable
#
# ping erlaubt
#
-A INPUT -p icmp -m state --state NEW -m icmp --icmp-type 8 -j ACCEPT
-A INPUT -p icmp -m state --state RELATED,ESTABLISHED -j ACCEPT
#
# Ports 22222 (SSH), 1194 (openvpn), 53 (DNS) eingehend erlauben
#
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i ens18 -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 22222 -j ACCEPT
-A INPUT -i ens18 -p udp -m state --state NEW,ESTABLISHED -m udp --dport 1194 -j ACCEPT
-A INPUT -i ens18 -p udp -m state --state ESTABLISHED -m udp --sport 53 -j ACCEPT
-A INPUT -i ens18 -p tcp -m state --state ESTABLISHED -m tcp --sport 53 -j ACCEPT
#
Aus tun* Interface akzeptieren
#
-A INPUT -i tun0 -j ACCEPT
#
# Brute force Attacken blockieren
#
-A INPUT -m limit --limit 3/min -j LOG --log-prefix "iptables_INPUT_denied: "
-A INPUT -j REJECT --reject-with icmp-port-unreachable
#
# tun* Interface akzeptieren in der Chain (FORWARD)
#
-A FORWARD -i tun0 -j ACCEPT
#
# tunnel Netzwerk von tun* zu äusserem Interface (ens18)
#
-A FORWARD -s 10.89.0.0/24 -i tun0 -o ens18 -j ACCEPT
#
# Erlaube zugehoerige und bestehende Verbindungen
#
-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
#
# Brute force Attacken blockieren
#
-A FORWARD -m limit --limit 3/min -j LOG --log-prefix "iptables_FORWARD_denied: "
-A FORWARD -j REJECT --reject-with icmp-port-unreachable
#
# Loopback darf alles und icmp (ping & Co) geht durch
#
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -p icmp -j ACCEPT
#
# Erlaube zugehoerige und bestehende Verbindungen
#
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
#
# Erlaubte Ports nach aussen
-A OUTPUT -o ens18 -p tcp -m tcp --sport 22 -j ACCEPT
-A OUTPUT -o ens18 -p tcp -m tcp --dport 22 -j ACCEPT
-A OUTPUT -o ens18 -p tcp -m tcp --sport 22222 -j ACCEPT
-A OUTPUT -o ens18 -p tcp -m tcp --dport 22222 -j ACCEPT
-A OUTPUT -o ens18 -p udp -m state --state ESTABLISHED -m udp --sport 1194 -j ACCEPT
-A OUTPUT -o ens18 -p udp -m state --state NEW,ESTABLISHED -m udp --dport 53 -j ACCEPT
-A OUTPUT -o ens18 -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 53 -j ACCEPT
-A OUTPUT -o ens18 -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 80 -j ACCEPT
-A OUTPUT -o ens18 -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 443 -j ACCEPT
#
# tun* Interface akzeptieren in der Chain (OUTPUT)
#
-A OUTPUT -o tun0 -j ACCEPT
#
# Brute force Attacken blockieren
#
-A OUTPUT -m limit --limit 3/min -j LOG --log-prefix "iptables_OUTPUT_denied: "
-A OUTPUT -j REJECT --reject-with icmp-port-unreachable
COMMIT

Über den Autor

Renato Testa

Renato Testa ist seit 25 Jahren in der IT und im Unix-Umfeld tätig. Mitte der 90er-Jahre führte er die ersten Linux-Kurse  bei Digicomp durch. Neben der Systemprogrammierung ist vor allem Systemadministration von Unix/Linux-Systemen sein Hauptgebiet. Im Rahmen verschiedener OSS-Events bei Digicomp hielt er Referate zu Themen wie HA-Firewalls, OpenSolaris oder Linux-Cluster. Seit 2014 ist sein Hauptthema DevOps in verschiedenen Cloud-Projekten. Renato Testa ist seit 1994 Trainer bei Digicomp und ist im Besitze der SVEB I Zertifizierung.