From c77fff94c3da59e80465948f4bc94740e16c8108 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 8 Mar 2024 16:20:03 +0000 Subject: [PATCH 01/15] add config for revoking certificates --- brski-server/installer/payload/ca.conf | 21 +++++++ brski-server/installer/payload/installer | 28 +++++++++- .../installer/payload/local_revoke.sh | 32 +++++++++++ .../local_revoke_serial_multiple_args.sh | 55 +++++++++++++++++++ .../installer/payload/wlan-secure.conf | 13 ++++- 5 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 brski-server/installer/payload/ca.conf create mode 100755 brski-server/installer/payload/local_revoke.sh create mode 100755 brski-server/installer/payload/local_revoke_serial_multiple_args.sh diff --git a/brski-server/installer/payload/ca.conf b/brski-server/installer/payload/ca.conf new file mode 100644 index 00000000..9f6df40d --- /dev/null +++ b/brski-server/installer/payload/ca.conf @@ -0,0 +1,21 @@ +[ ca ] +default_ca = CA_default + +[ CA_default ] +database = /etc/hostapd/CA/index.txt +certificate = /etc/brski/registrar-tls-ca.crt +private_key = /etc/brski/registrar-tls-ca.key +crlnumber = /etc/hostapd/CA/crlnumber +serial = /etc/hostapd/CA/serial +default_md = sha256 +default_crl_days = 30 +default_days = 365 +policy = policy_match + +[ policy_match ] +countryName = supplied +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional \ No newline at end of file diff --git a/brski-server/installer/payload/installer b/brski-server/installer/payload/installer index 0a18b38c..7858668d 100755 --- a/brski-server/installer/payload/installer +++ b/brski-server/installer/payload/installer @@ -3,6 +3,10 @@ IF0="wlan0" IF1="wlan1" +CA_DIR="/etc/hostapd/CA" +CA_CONF="$CA_DIR/ca.conf" +COMBINED_CA_CRL="$CA_DIR/registrar-tls-ca-and-crl.crt" + ip a show dev $IF0 if [ $? -ne 0 ]; then @@ -18,9 +22,9 @@ if [ $? -ne 0 ]; then fi # Configure BRSK tool -wget -O /tmp/brski_0.2.3_arm64.deb https://github.com/nqminds/brski/releases/download/0.2.3/brski_0.2.3_arm64.deb -dpkg -i /tmp/brski_0.2.3_arm64.deb -rm /tmp/brski_0.2.3_arm64.deb +wget -O /tmp/brski_0.2.6_arm64.deb https://github.com/nqminds/brski/releases/download/0.2.6/brski_0.2.6_arm64.deb +dpkg -i /tmp/brski_0.2.6_arm64.deb +rm /tmp/brski_0.2.6_arm64.deb cp certs/* /etc/brski @@ -71,6 +75,24 @@ sed -i s/@wlan@/$IF0/g /etc/default/dnsmasq.$IF0 cp dnsmasq-secure.conf /etc/default/dnsmasq.$IF1 sed -i s/@wlan@/$IF1/g /etc/default/dnsmasq.$IF1 +sudo mkdir -p "$CA_DIR/" +if [ $? -ne 0 ]; then + echo "Failed to create directory: $CA_DIR/" + exit 1 +fi + +cp ca.conf "$CA_DIR/" +touch "$CA_DIR/index.txt" +echo '1000' > "$CA_DIR/serial" +touch "$CA_DIR/crlnumber" +echo '01' > "$CA_DIR/crlnumber" + +openssl ca -gencrl -out "$CA_DIR/crl.crt" -config "$CA_CONF" +sudo sh -c "cat $CA_CERT $CA_DIR/crl.crt > $COMBINED_CA_CRL" + +cp local_revoke_serial_multiple_args.sh "$CA_DIR/" +cp local_revoke.sh "$CA_DIR/" + sudo systemctl disable dnsmasq@$IF0.service sudo systemctl disable dnsmasq@$IF1.service diff --git a/brski-server/installer/payload/local_revoke.sh b/brski-server/installer/payload/local_revoke.sh new file mode 100755 index 00000000..847a660d --- /dev/null +++ b/brski-server/installer/payload/local_revoke.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# local_revoke.sh + +# Check if a certificate file path is provided +if [ "$#" -ne 1 ]; then + echo "Usage: $0 /path/to/client_cert.pem" + exit 1 +fi + +CLIENT_CERT="$1" +CA_CONFIG="/etc/hostapd/CA/ca.conf" +CA_CERT="/etc/brski/registrar-tls-ca.crt" +CRL="/etc/hostapd/CA/crl.crt" +COMBINED_CA_CRL="/etc/hostapd/CA/registrar-tls-ca-and-crl.crt" + +# Revoke the client certificate +sudo openssl ca -revoke "$CLIENT_CERT" -config "$CA_CONFIG" + +# Update the CRL +sudo openssl ca -gencrl -out "$CRL" -config "$CA_CONFIG" + +# Concatenate CA certificate and CRL +sudo sh -c "cat $CA_CERT $CRL > $COMBINED_CA_CRL" + +# Verify if the certificate has been revoked +if sudo openssl verify -extended_crl -verbose -CAfile "$COMBINED_CA_CRL" -crl_check "$CLIENT_CERT"; then + echo "Certificate revocation verification failed. Not restarting hostapd." +else + echo "Certificate has been revoked. Restarting hostapd..." + # Restart hostapd + sudo systemctl restart hostapd@wlan1.service +fi diff --git a/brski-server/installer/payload/local_revoke_serial_multiple_args.sh b/brski-server/installer/payload/local_revoke_serial_multiple_args.sh new file mode 100755 index 00000000..f4fd046b --- /dev/null +++ b/brski-server/installer/payload/local_revoke_serial_multiple_args.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# local_revoke_serial_multiple_args.sh + +# Check if at least two serial numbers are provided and the number of arguments is even +if [ "$#" -lt 2 ] || [ $(( $# % 2 )) -ne 0 ]; then + echo "Usage: $0 unique_serial_number1 serialNumber1 [unique_serial_number2 serialNumber2 ...]" + exit 1 +fi + +CA_DIR="/etc/hostapd/CA" +CA_CONFIG="$CA_DIR/ca.conf" +CRL="$CA_DIR/crl.crt" +CA_CERT="/etc/brski/registrar-tls-ca.crt" +COMBINED_CA_CRL="$CA_DIR/registrar-tls-ca-and-crl.crt" +INDEX_FILE="$CA_DIR/index.txt" +REVOCATION_DATE=$(date +"%y%m%d%H%M%SZ") +update_crl=false + +while [ "$#" -gt 0 ]; do + UNIQUE_SERIAL="$1" + SERIAL_NUMBER="$2" + shift 2 + + # Convert and pad the UNIQUE_SERIAL number to the correct format + UNIQUE_SERIAL=$(echo "$UNIQUE_SERIAL" | sed 's/^0x//' | tr '[:lower:]' '[:upper:]' | sed 's/^0*//') + + UNIQUE_SERIAL=$(printf '%016s' "$UNIQUE_SERIAL" | tr ' ' '0') + + SERIAL_NUMBER=$(echo "$SERIAL_NUMBER" | tr '[:lower:]' '[:upper:]') + + # Check if the serial number already exists in index.txt + if grep -q "$UNIQUE_SERIAL" "$INDEX_FILE"; then + echo "Serial Number $UNIQUE_SERIAL already exists in index.txt. Skipping addition." + else + # Add entry to the index.txt file + echo "Adding revocation entry to index.txt for certificate with serials $UNIQUE_SERIAL and $SERIAL_NUMBER..." + printf "R\t99991231235959Z\t%s\t%s\tunknown\t/C=IE/CN=ldevid-cert/serialNumber=%s\n" "$REVOCATION_DATE" "$UNIQUE_SERIAL" "$SERIAL_NUMBER" | sudo tee -a "$INDEX_FILE" + # Update the CRL + echo "Updating CRL..." + sudo openssl ca -gencrl -out "$CRL" -config "$CA_CONFIG" + + # Concatenate CA certificate and CRL + echo "Updating combined CA and CRL file..." + sudo sh -c "cat $CA_CERT $CRL > $COMBINED_CA_CRL" + + update_crl=true + fi +done + +if [ "$update_crl" = true ]; then + + echo "Certificates have been revoked. Restarting hostapd..." + # Restart hostapd + sudo systemctl restart hostapd@wlan1.service +fi diff --git a/brski-server/installer/payload/wlan-secure.conf b/brski-server/installer/payload/wlan-secure.conf index d5af6cd5..fd047a46 100644 --- a/brski-server/installer/payload/wlan-secure.conf +++ b/brski-server/installer/payload/wlan-secure.conf @@ -33,10 +33,21 @@ auth_server_shared_secret=1234554321 # Use integrated EAP server instead of external RADIUS authentication server eap_server=1 +# Enable CRL verification. +# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a +# valid CRL signed by the CA is required to be included in the ca_cert file. +# This can be done by using PEM format for CA certificate and CRL and +# concatenating these into one file. Whenever CRL changes, hostapd needs to be +# restarted to take the new CRL into use. +# 0 = do not verify CRLs (default) +# 1 = check the CRL of the user certificate +# 2 = check all CRLs in the certificate path +check_crl=2 + # Path for EAP server user database eap_user_file=/etc/hostapd/eap_user -ca_cert=/etc/brski/registrar-tls-ca.crt +ca_cert=/etc/hostapd/CA/registrar-tls-ca-and-crl.crt server_cert=/etc/brski/registrar-tls.crt private_key=/etc/brski/registrar-tls.key From c1d615828f02213ea808e7c115de62bb609c81a0 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Tue, 12 Mar 2024 14:15:56 +0000 Subject: [PATCH 02/15] add draft settup for vlan and freeradius --- brski-server/installer/gateway-install.sh | Bin 16657 -> 20030 bytes brski-server/installer/payload/clients.conf | 7 +++++ brski-server/installer/payload/dh | 8 +++++ brski-server/installer/payload/eap | 18 +++++++++++ brski-server/installer/payload/installer | 14 +++++++-- .../installer/payload/local_revoke.sh | 3 +- .../local_revoke_serial_multiple_args.sh | 3 +- brski-server/installer/payload/run.sh | 15 ++++++++++ brski-server/installer/payload/vlan-dhcp.conf | 4 +++ .../installer/payload/vlan-secure.vlan | 1 + brski-server/installer/payload/vlans | 15 ++++++++++ .../installer/payload/wlan-secure.conf | 28 ++++-------------- 12 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 brski-server/installer/payload/clients.conf create mode 100644 brski-server/installer/payload/dh create mode 100644 brski-server/installer/payload/eap create mode 100644 brski-server/installer/payload/vlan-dhcp.conf create mode 100644 brski-server/installer/payload/vlan-secure.vlan create mode 100644 brski-server/installer/payload/vlans diff --git a/brski-server/installer/gateway-install.sh b/brski-server/installer/gateway-install.sh index c69a4baf7ecdbbdb07a74fa46c76f4585fc5376c..c1bf3752387ebc3d520f66ff8c35ccb3eb09a562 100755 GIT binary patch delta 19883 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 iIpmN-4msqILk>CQkV6hR* delta 16483 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 diff --git a/brski-server/installer/payload/clients.conf b/brski-server/installer/payload/clients.conf new file mode 100644 index 00000000..49548483 --- /dev/null +++ b/brski-server/installer/payload/clients.conf @@ -0,0 +1,7 @@ +# /etc/freeradius/3.0/clients.conf +client hostapd { + ipaddr = 127.0.0.1 + secret = 1234554321 + require_message_authenticator = no + nas_type = other +} diff --git a/brski-server/installer/payload/dh b/brski-server/installer/payload/dh new file mode 100644 index 00000000..92b5e6f8 --- /dev/null +++ b/brski-server/installer/payload/dh @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA0ysfciGWca1Uf01+rHzrapUZrRMb8d7ED4eb6AbSiox9Z/yCLVOb +ZS6HEse5T+H5yA44+OvY1CAeRFMlPTBJjH3T/oppEBjEV6fuQyzQ6WMz1PHnEoz6 +ERzslbJBf2Qliai3bzb9rgNNsSOGeeSHd39I3+ENN1iCh5AaDjelM/1585QJVRYY +0MbMt0ikGERf12O9rke4Ms103WnW50g/iDaSOIPKpjW2q8Qy083ObQJitFGTkYtf +cKU1xkrxLUMlSMJO3jh5+lqD2TZJnCYvJjhjGApVQ0eNnx0ZLh6X/AbUOl/dka4F +JlyBIyJepBmy0tm3juuj+ukBb+7AypUwdwIBAg== +-----END DH PARAMETERS----- diff --git a/brski-server/installer/payload/eap b/brski-server/installer/payload/eap new file mode 100644 index 00000000..ed210d02 --- /dev/null +++ b/brski-server/installer/payload/eap @@ -0,0 +1,18 @@ +# /etc/freeradius/3.0/mods-enabled/eap +eap { + default_eap_type = tls + + tls { + + private_key_password = s3cr3t + + private_key_file = /etc/brski/registrar-tls.key + certificate_file = /etc/brski/registrar-tls.key +. + ca_file = /etc/hostapd/CA/registrar-tls-ca_and_crl.crt + + dh_file = /etc/freeradius/3.0/certs/dh + check_crl = yes + } +} + diff --git a/brski-server/installer/payload/installer b/brski-server/installer/payload/installer index 7858668d..e5b83222 100755 --- a/brski-server/installer/payload/installer +++ b/brski-server/installer/payload/installer @@ -1,11 +1,11 @@ -#!/bin/bash +# #!/bin/bash IF0="wlan0" IF1="wlan1" CA_DIR="/etc/hostapd/CA" CA_CONF="$CA_DIR/ca.conf" -COMBINED_CA_CRL="$CA_DIR/registrar-tls-ca-and-crl.crt" +COMBINED_CA_CRL="$CA_DIR/registrar-tls-ca_and_crl.crt" ip a show dev $IF0 @@ -37,7 +37,8 @@ systemctl daemon-reload systemctl enable nist-demo.service # Insatll all packages -apt install hostapd dnsmasq iptables avahi-daemon -y +apt install hostapd dnsmasq iptables avahi-daemon vlan freeradius freeradius-utils -y +echo "8021q" | sudo tee -a /etc/modules # Configure avahi # Always enable @@ -54,6 +55,13 @@ systemctl disable wpa_supplicant systemctl disable hostapd@$IF0.service systemctl disable hostapd@$IF1.service +cp vlan-dhcp.conf /etc/dnsmasq.d/ +cp vlans /etc/dnsmasq.d/ +cp vlan-secure.vlan /etc/hostapd/ +cp clients.conf /etc/freeradius/3.0/ +cp eap /etc/freeradius/3.0/mods-enabled/ +cp dh /etc/freeradius/3.0/certs/ + # secure hostapd cp wlan-secure.conf /etc/hostapd/$IF1.conf sed -i s/@wlan@/$IF1/g /etc/hostapd/$IF1.conf diff --git a/brski-server/installer/payload/local_revoke.sh b/brski-server/installer/payload/local_revoke.sh index 847a660d..59a903b3 100755 --- a/brski-server/installer/payload/local_revoke.sh +++ b/brski-server/installer/payload/local_revoke.sh @@ -11,7 +11,7 @@ CLIENT_CERT="$1" CA_CONFIG="/etc/hostapd/CA/ca.conf" CA_CERT="/etc/brski/registrar-tls-ca.crt" CRL="/etc/hostapd/CA/crl.crt" -COMBINED_CA_CRL="/etc/hostapd/CA/registrar-tls-ca-and-crl.crt" +COMBINED_CA_CRL="/etc/hostapd/CA/registrar-tls-ca_and_crl.crt" # Revoke the client certificate sudo openssl ca -revoke "$CLIENT_CERT" -config "$CA_CONFIG" @@ -29,4 +29,5 @@ else echo "Certificate has been revoked. Restarting hostapd..." # Restart hostapd sudo systemctl restart hostapd@wlan1.service + sudo systemctl restart freeradius.service fi diff --git a/brski-server/installer/payload/local_revoke_serial_multiple_args.sh b/brski-server/installer/payload/local_revoke_serial_multiple_args.sh index f4fd046b..679aaf87 100755 --- a/brski-server/installer/payload/local_revoke_serial_multiple_args.sh +++ b/brski-server/installer/payload/local_revoke_serial_multiple_args.sh @@ -11,7 +11,7 @@ CA_DIR="/etc/hostapd/CA" CA_CONFIG="$CA_DIR/ca.conf" CRL="$CA_DIR/crl.crt" CA_CERT="/etc/brski/registrar-tls-ca.crt" -COMBINED_CA_CRL="$CA_DIR/registrar-tls-ca-and-crl.crt" +COMBINED_CA_CRL="$CA_DIR/registrar-tls-ca_and_crl.crt" INDEX_FILE="$CA_DIR/index.txt" REVOCATION_DATE=$(date +"%y%m%d%H%M%SZ") update_crl=false @@ -52,4 +52,5 @@ if [ "$update_crl" = true ]; then echo "Certificates have been revoked. Restarting hostapd..." # Restart hostapd sudo systemctl restart hostapd@wlan1.service + sudo systemctl restart freeradius.service fi diff --git a/brski-server/installer/payload/run.sh b/brski-server/installer/payload/run.sh index 2b696754..3b1ca2c2 100755 --- a/brski-server/installer/payload/run.sh +++ b/brski-server/installer/payload/run.sh @@ -2,19 +2,34 @@ IF0="wlan0" IF1="wlan1" +IF1_10="wlan1.10" +IF1_20="wlan1.20" +IF1_30="wlan1.30" +IF1_40="wlan1.40" IP0="192.168.17.1/24" IP1="192.168.16.1/24" +IP1_10="192.168.30.1/24" +IP1_20="192.168.31.1/24" +IP1_30="192.168.32.1/24" +IP1_40="192.168.33.1/24" ip addr add $IP0 dev $IF0 ip addr add $IP1 dev $IF1 +ip addr add $IP1_10 dev $IF1_10 +ip addr add $IP1_20 dev $IF1_20 +ip addr add $IP1_30 dev $IF1_30 +ip addr add $IP1_40 dev $IF1_40 + systemctl start hostapd@$IF0.service systemctl start hostapd@$IF1.service systemctl restart dnsmasq@$IF0.service systemctl restart dnsmasq@$IF1.service +sudo systemctl restart freeradius.service + brski -c /etc/brski/config.ini masa -d & brski -c /etc/brski/config.ini registrar -d diff --git a/brski-server/installer/payload/vlan-dhcp.conf b/brski-server/installer/payload/vlan-dhcp.conf new file mode 100644 index 00000000..45454cee --- /dev/null +++ b/brski-server/installer/payload/vlan-dhcp.conf @@ -0,0 +1,4 @@ +dhcp-range=wlan1.10,192.168.30.100,192.168.30.199,255.255.255.0,24h +dhcp-range=wlan1.20,192.168.31.100,192.168.31.199,255.255.255.0,24h +dhcp-range=wlan1.30,192.168.32.100,192.168.32.199,255.255.255.0,24h +dhcp-range=wlan1.40,192.168.33.100,192.168.33.199,255.255.255.0,24h diff --git a/brski-server/installer/payload/vlan-secure.vlan b/brski-server/installer/payload/vlan-secure.vlan new file mode 100644 index 00000000..b99a4b82 --- /dev/null +++ b/brski-server/installer/payload/vlan-secure.vlan @@ -0,0 +1 @@ +# MAC Address VLAN ID \ No newline at end of file diff --git a/brski-server/installer/payload/vlans b/brski-server/installer/payload/vlans new file mode 100644 index 00000000..6effcd9a --- /dev/null +++ b/brski-server/installer/payload/vlans @@ -0,0 +1,15 @@ +auto wlan1.10 +iface wlan1.10 inet manual + vlan-raw-device wlan1 + +auto wlan1.20 +iface wlan1.20 inet manual + vlan-raw-device wlan1 + +auto wlan1.30 +iface wlan1.30 inet manual + vlan-raw-device wlan1 + +auto wlan1.40 +iface wlan1.40 inet manual + vlan-raw-device wlan1 diff --git a/brski-server/installer/payload/wlan-secure.conf b/brski-server/installer/payload/wlan-secure.conf index fd047a46..6c2af779 100644 --- a/brski-server/installer/payload/wlan-secure.conf +++ b/brski-server/installer/payload/wlan-secure.conf @@ -28,26 +28,10 @@ auth_server_addr=127.0.0.1 auth_server_port=1812 auth_server_shared_secret=1234554321 -# RADIUS SERVER -# INTEGRATED EAP SERVER -# Use integrated EAP server instead of external RADIUS authentication server -eap_server=1 - -# Enable CRL verification. -# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a -# valid CRL signed by the CA is required to be included in the ca_cert file. -# This can be done by using PEM format for CA certificate and CRL and -# concatenating these into one file. Whenever CRL changes, hostapd needs to be -# restarted to take the new CRL into use. -# 0 = do not verify CRLs (default) -# 1 = check the CRL of the user certificate -# 2 = check all CRLs in the certificate path -check_crl=2 - -# Path for EAP server user database -eap_user_file=/etc/hostapd/eap_user - -ca_cert=/etc/hostapd/CA/registrar-tls-ca-and-crl.crt -server_cert=/etc/brski/registrar-tls.crt -private_key=/etc/brski/registrar-tls.key +# VLAN configuration +vlan_bridge=br0 +dynamic_vlan=1 +vlan_naming=1 +vlan_file=/etc/hostapd/vlan-secure.vlan +eap_user_file=/etc/hostapd/eap_user From 6fee87caa58cc0a282391eddfb8803e503bcba08 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:45:15 +0000 Subject: [PATCH 03/15] removed files --- brski-server/installer/payload/dh | 8 -------- brski-server/installer/payload/vlan-dhcp.conf | 4 ---- brski-server/installer/payload/vlans | 15 --------------- 3 files changed, 27 deletions(-) delete mode 100644 brski-server/installer/payload/dh delete mode 100644 brski-server/installer/payload/vlan-dhcp.conf delete mode 100644 brski-server/installer/payload/vlans diff --git a/brski-server/installer/payload/dh b/brski-server/installer/payload/dh deleted file mode 100644 index 92b5e6f8..00000000 --- a/brski-server/installer/payload/dh +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA0ysfciGWca1Uf01+rHzrapUZrRMb8d7ED4eb6AbSiox9Z/yCLVOb -ZS6HEse5T+H5yA44+OvY1CAeRFMlPTBJjH3T/oppEBjEV6fuQyzQ6WMz1PHnEoz6 -ERzslbJBf2Qliai3bzb9rgNNsSOGeeSHd39I3+ENN1iCh5AaDjelM/1585QJVRYY -0MbMt0ikGERf12O9rke4Ms103WnW50g/iDaSOIPKpjW2q8Qy083ObQJitFGTkYtf -cKU1xkrxLUMlSMJO3jh5+lqD2TZJnCYvJjhjGApVQ0eNnx0ZLh6X/AbUOl/dka4F -JlyBIyJepBmy0tm3juuj+ukBb+7AypUwdwIBAg== ------END DH PARAMETERS----- diff --git a/brski-server/installer/payload/vlan-dhcp.conf b/brski-server/installer/payload/vlan-dhcp.conf deleted file mode 100644 index 45454cee..00000000 --- a/brski-server/installer/payload/vlan-dhcp.conf +++ /dev/null @@ -1,4 +0,0 @@ -dhcp-range=wlan1.10,192.168.30.100,192.168.30.199,255.255.255.0,24h -dhcp-range=wlan1.20,192.168.31.100,192.168.31.199,255.255.255.0,24h -dhcp-range=wlan1.30,192.168.32.100,192.168.32.199,255.255.255.0,24h -dhcp-range=wlan1.40,192.168.33.100,192.168.33.199,255.255.255.0,24h diff --git a/brski-server/installer/payload/vlans b/brski-server/installer/payload/vlans deleted file mode 100644 index 6effcd9a..00000000 --- a/brski-server/installer/payload/vlans +++ /dev/null @@ -1,15 +0,0 @@ -auto wlan1.10 -iface wlan1.10 inet manual - vlan-raw-device wlan1 - -auto wlan1.20 -iface wlan1.20 inet manual - vlan-raw-device wlan1 - -auto wlan1.30 -iface wlan1.30 inet manual - vlan-raw-device wlan1 - -auto wlan1.40 -iface wlan1.40 inet manual - vlan-raw-device wlan1 From 284817488ed3775fbf33217a2b32eb4adc8c3d99 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:46:33 +0000 Subject: [PATCH 04/15] add configuration files for freeradius --- brski-server/installer/payload/clients.conf | 291 ++++- brski-server/installer/payload/eap | 1125 ++++++++++++++++++- 2 files changed, 1399 insertions(+), 17 deletions(-) diff --git a/brski-server/installer/payload/clients.conf b/brski-server/installer/payload/clients.conf index 49548483..9ee15933 100644 --- a/brski-server/installer/payload/clients.conf +++ b/brski-server/installer/payload/clients.conf @@ -1,7 +1,288 @@ -# /etc/freeradius/3.0/clients.conf +# -*- text -*- +## +## clients.conf -- client configuration directives +## +## $Id: 60f9f4bf8a32804182e4516ac69ac510d25215d1 $ + +####################################################################### +# +# Define RADIUS clients (usually a NAS, Access Point, etc.). + +# +# Defines a RADIUS client. +# +# '127.0.0.1' is another name for 'localhost'. It is enabled by default, +# to allow testing of the server after an initial installation. If you +# are not going to be permitting RADIUS queries from localhost, we suggest +# that you delete, or comment out, this entry. +# +# + +# +# Each client has a "short name" that is used to distinguish it from +# other clients. +# +# In version 1.x, the string after the word "client" was the IP +# address of the client. In 2.0, the IP address is configured via +# the "ipaddr" or "ipv6addr" fields. For compatibility, the 1.x +# format is still accepted. +# client hostapd { - ipaddr = 127.0.0.1 - secret = 1234554321 - require_message_authenticator = no - nas_type = other + # Only *one* of ipaddr, ipv4addr, ipv6addr may be specified for + # a client. + # + # ipaddr will accept IPv4 or IPv6 addresses with optional CIDR + # notation '/' to specify ranges. + # + # ipaddr will accept domain names e.g. example.org resolving + # them via DNS. + # + # If both A and AAAA records are found, A records will be + # used in preference to AAAA. + ipaddr = 127.0.0.1 + + # Same as ipaddr but allows v4 addresses only. Requires A + # record for domain names. +# ipv4addr = * # any. 127.0.0.1 == localhost + + # Same as ipaddr but allows v6 addresses only. Requires AAAA + # record for domain names. +# ipv6addr = :: # any. ::1 == localhost + + # + # A note on DNS: We STRONGLY recommend using IP addresses + # rather than host names. Using host names means that the + # server will do DNS lookups when it starts, making it + # dependent on DNS. i.e. If anything goes wrong with DNS, + # the server won't start! + # + # The server also looks up the IP address from DNS once, and + # only once, when it starts. If the DNS record is later + # updated, the server WILL NOT see that update. + # + + # + # The transport protocol. + # + # If unspecified, defaults to "udp", which is the traditional + # RADIUS transport. It may also be "tcp", in which case the + # server will accept connections from this client ONLY over TCP. + # + proto = * + + # + # The shared secret use to "encrypt" and "sign" packets between + # the NAS and FreeRADIUS. You MUST change this secret from the + # default, otherwise it's not a secret any more! + # + # The secret can be any string, up to 8k characters in length. + # + # Control codes can be entered vi octal encoding, + # e.g. "\101\102" == "AB" + # Quotation marks can be entered by escaping them, + # e.g. "foo\"bar" + # + # A note on security: The security of the RADIUS protocol + # depends COMPLETELY on this secret! We recommend using a + # shared secret that is composed of: + # + # upper case letters + # lower case letters + # numbers + # + # And is at LEAST 8 characters long, preferably 16 characters in + # length. The secret MUST be random, and should not be words, + # phrase, or anything else that is recognisable. + # + # The default secret below is only for testing, and should + # not be used in any real environment. + # + secret = 1234554321 + + # + # Old-style clients do not send a Message-Authenticator + # in an Access-Request. RFC 5080 suggests that all clients + # SHOULD include it in an Access-Request. The configuration + # item below allows the server to require it. If a client + # is required to include a Message-Authenticator and it does + # not, then the packet will be silently discarded. + # + # allowed values: yes, no + require_message_authenticator = no + + # + # The short name is used as an alias for the fully qualified + # domain name, or the IP address. + # + # It is accepted for compatibility with 1.x, but it is no + # longer necessary in >= 2.0 + # +# shortname = localhost + + # + # the following three fields are optional, but may be used by + # checkrad.pl for simultaneous use checks + # + + # + # The nas_type tells 'checkrad.pl' which NAS-specific method to + # use to query the NAS for simultaneous use. + # + # Permitted NAS types are: + # + # cisco + # computone + # livingston + # juniper + # max40xx + # multitech + # netserver + # pathras + # patton + # portslave + # tc + # usrhiper + # other # for all other types + + # + nas_type = other # localhost isn't usually a NAS... + + # + # The following two configurations are for future use. + # The 'naspasswd' file is currently used to store the NAS + # login name and password, which is used by checkrad.pl + # when querying the NAS for simultaneous use. + # +# login = !root +# password = someadminpas + + # + # As of 2.0, clients can also be tied to a virtual server. + # This is done by setting the "virtual_server" configuration + # item, as in the example below. + # +# virtual_server = home1 + + # + # A pointer to the "home_server_pool" OR a "home_server" + # section that contains the CoA configuration for this + # client. For an example of a coa home server or pool, + # see raddb/sites-available/originate-coa +# coa_server = coa + + # + # Response window for proxied packets. If non-zero, + # then the lower of (home, client) response_window + # will be used. + # + # i.e. it can be used to lower the response_window + # packets from one client to a home server. It cannot + # be used to raise the response_window. + # +# response_window = 10.0 + + # + # Connection limiting for clients using "proto = tcp". + # + # This section is ignored for clients sending UDP traffic + # + limit { + # + # Limit the number of simultaneous TCP connections from a client + # + # The default is 16. + # Setting this to 0 means "no limit" + max_connections = 16 + + # The per-socket "max_requests" option does not exist. + + # + # The lifetime, in seconds, of a TCP connection. After + # this lifetime, the connection will be closed. + # + # Setting this to 0 means "forever". + lifetime = 0 + + # + # The idle timeout, in seconds, of a TCP connection. + # If no packets have been received over the connection for + # this time, the connection will be closed. + # + # Setting this to 0 means "no timeout". + # + # We STRONGLY RECOMMEND that you set an idle timeout. + # + idle_timeout = 30 + } } + +# IPv6 Client +#client localhost_ipv6 { +# ipv6addr = ::1 +# secret = testing123 +#} + +# All IPv6 Site-local clients +#client sitelocal_ipv6 { +# ipv6addr = fe80::/16 +# secret = testing123 +#} + +#client example.org { +# ipaddr = radius.example.org +# secret = testing123 +#} + +# +# You can now specify one secret for a network of clients. +# When a client request comes in, the BEST match is chosen. +# i.e. The entry from the smallest possible network. +# +#client private-network-1 { +# ipaddr = 192.0.2.0/24 +# secret = testing123-1 +#} + +#client private-network-2 { +# ipaddr = 198.51.100.0/24 +# secret = testing123-2 +#} + +####################################################################### +# +# Per-socket client lists. The configuration entries are exactly +# the same as above, but they are nested inside of a section. +# +# You can have as many per-socket client lists as you have "listen" +# sections, or you can re-use a list among multiple "listen" sections. +# +# Un-comment this section, and edit a "listen" section to add: +# "clients = per_socket_clients". That IP address/port combination +# will then accept ONLY the clients listed in this section. +# +# There are additional considerations when using clients from SQL. +# +# A client can be link to a virtual server via modules such as SQL. +# This link is done via the following process: +# +# If there is no listener in a virtual server, SQL clients are added +# to the global list for that virtual server. +# +# If there is a listener, and the first listener does not have a +# "clients=..." configuration item, SQL clients are added to the +# global list. +# +# If there is a listener, and the first one does have a "clients=..." +# configuration item, SQL clients are added to that list. The client +# { ...} ` configured in that list are also added for that listener. +# +# The only issue is if you have multiple listeners in a virtual +# server, each with a different client list, then the SQL clients are +# added only to the first listener. +# +#clients per_socket_clients { +# client socket_client { +# ipaddr = 192.0.2.4 +# secret = testing123 +# } +#} diff --git a/brski-server/installer/payload/eap b/brski-server/installer/payload/eap index ed210d02..b4dbf1c5 100644 --- a/brski-server/installer/payload/eap +++ b/brski-server/installer/payload/eap @@ -1,18 +1,1119 @@ -# /etc/freeradius/3.0/mods-enabled/eap +# -*- text -*- +## +## eap.conf -- Configuration for EAP types (PEAP, TTLS, etc.) +## +## $Id: 8fd8eb5edc69f5b72c5d3b92d90ccf018660dd4e $ + +####################################################################### +# +# Whatever you do, do NOT set 'Auth-Type := EAP'. The server +# is smart enough to figure this out on its own. The most +# common side effect of setting 'Auth-Type := EAP' is that the +# users then cannot use ANY other authentication method. +# eap { - default_eap_type = tls + # Invoke the default supported EAP type when + # EAP-Identity response is received. + # + # The incoming EAP messages DO NOT specify which EAP + # type they will be using, so it MUST be set here. + # + # For now, only one default EAP type may be used at a time. + # + # If the EAP-Type attribute is set by another module, + # then that EAP type takes precedence over the + # default type configured here. + # + default_eap_type = tls + use_tunneled_reply = yes + # A list is maintained to correlate EAP-Response + # packets with EAP-Request packets. After a + # configurable length of time, entries in the list + # expire, and are deleted. + # + timer_expire = 60 - tls { + # There are many EAP types, but the server has support + # for only a limited subset. If the server receives + # a request for an EAP type it does not support, then + # it normally rejects the request. By setting this + # configuration to "yes", you can tell the server to + # instead keep processing the request. Another module + # MUST then be configured to proxy the request to + # another RADIUS server which supports that EAP type. + # + # If another module is NOT configured to handle the + # request, then the request will still end up being + # rejected. + # + ignore_unknown_eap_types = no - private_key_password = s3cr3t + # Cisco AP1230B firmware 12.2(13)JA1 has a bug. When given + # a User-Name attribute in an Access-Accept, it copies one + # more byte than it should. + # + # We can work around it by configurably adding an extra + # zero byte. + # + cisco_accounting_username_bug = no - private_key_file = /etc/brski/registrar-tls.key - certificate_file = /etc/brski/registrar-tls.key -. - ca_file = /etc/hostapd/CA/registrar-tls-ca_and_crl.crt + # Help prevent DoS attacks by limiting the number of + # sessions that the server is tracking. For simplicity, + # this is taken from the "max_requests" directive in + # radiusd.conf. + # + max_sessions = ${max_requests} - dh_file = /etc/freeradius/3.0/certs/dh - check_crl = yes - } -} + ############################################################ + # + # Supported EAP-types + # + + + # EAP-MD5 + # + # We do NOT recommend using EAP-MD5 authentication + # for wireless connections. It is insecure, and does + # not provide for dynamic WEP keys. + # + #md5 { + #} + + + # EAP-pwd -- secure password-based authentication + # + #pwd { + # group = 19 + + # server_id = theserver@example.com + + # This has the same meaning as for TLS. + # + # fragment_size = 1020 + + # The virtual server which determines the + # "known good" password for the user. + # Note that unlike TLS, only the "authorize" + # section is processed. EAP-PWD requests can be + # distinguished by having a User-Name, but + # no User-Password, CHAP-Password, EAP-Message, etc. + # + # virtual_server = "inner-tunnel" + #} + + + # Cisco LEAP + # + # We do not recommend using LEAP in new deployments. See: + # http://www.securiteam.com/tools/5TP012ACKE.html + # + # LEAP is not supported. + # It is insecure, and no one should be using it. + # + + + # EAP-GTC -- Generic Token Card + # + # Currently, this is only permitted inside of EAP-TTLS, + # or EAP-PEAP. The module "challenges" the user with + # text, and the response from the user is taken to be + # the User-Password. + # + # Proxying the tunneled EAP-GTC session is a bad idea, + # the users password will go over the wire in plain-text, + # for anyone to see. + # + # gtc { + # The default challenge, which many clients + # ignore.. + # + # challenge = "Password: " + + # The plain-text response which comes back + # is put into a User-Password attribute, + # and passed to another module for + # authentication. This allows the EAP-GTC + # response to be checked against plain-text, + # or crypt'd passwords. + # + # If you say "Local" instead of "PAP", then + # the module will look for a User-Password + # configured for the request, and do the + # authentication itself. + # + # auth_type = PAP + #} + + + # Common TLS configuration for TLS-based EAP types + # ------------------------------------------------ + # + # See raddb/certs/README.md for additional comments + # on certificates. + # + # If OpenSSL was not found at the time the server was + # built, the "tls", "ttls", and "peap" sections will + # be ignored. + # + # If you do not currently have certificates signed by + # a trusted CA you may use the 'snakeoil' certificates. + # Included with the server in raddb/certs. + # + # If these certificates have not been auto-generated: + # cd raddb/certs + # make + # + # These test certificates SHOULD NOT be used in a normal + # deployment. They are created only to make it easier + # to install the server, and to perform some simple + # tests with EAP-TLS, TTLS, or PEAP. + # + # Note that you should NOT use a globally known CA here! + # e.g. using a Verisign cert as a "known CA" means that + # ANYONE who has a certificate signed by them can + # authenticate via EAP-TLS! This is likely not what you want. + # + #tls-config tls-common { + # private_key_password = whatever + # private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key + + # If Private key & Certificate are located in + # the same file, then private_key_file & + # certificate_file must contain the same file + # name. + # + # If ca_file (below) is not used, then the + # certificate_file below SHOULD also include all of + # the intermediate CA certificates used to sign the + # server certificate, but NOT the root CA. + # + # Including the ROOT CA certificate is not useful and + # merely inflates the exchanged data volume during + # the TLS negotiation. + # + # This file should contain the server certificate, + # followed by intermediate certificates, in order. + # i.e. If we have a server certificate signed by CA1, + # which is signed by CA2, which is signed by a root + # CA, then the "certificate_file" should contain + # server.pem, followed by CA1.pem, followed by + # CA2.pem. + # + # When using "ca_file" or "ca_path", the + # "certificate_file" should contain only + # "server.pem". And then you may (or may not) need + # to set "auto_chain", depending on your version of + # OpenSSL. + # + # In short, SSL / TLS certificates are complex. + # There are many versions of software, each of which + # behave slightly differently. It is impossible to + # give advice which will work everywhere. Instead, + # we give general guidelines. + # + # certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem + + # Trusted Root CA list + # + # This file can contain multiple CA certificates. + # ALL of the CA's in this list will be trusted to + # issue client certificates for authentication. + # + # In general, you should use self-signed + # certificates for 802.1x (EAP) authentication. + # In that case, this CA file should contain + # *one* CA certificate. + # + # ca_file = /etc/ssl/certs/ca-certificates.crt + + # OpenSSL will automatically create certificate chains, + # unless we tell it to not do that. The problem is that + # it sometimes gets the chains right from a certificate + # signature view, but wrong from the clients view. + # + # When setting "auto_chain = no", the server certificate + # file MUST include the full certificate chain. + # + # auto_chain = yes + + # If OpenSSL supports TLS-PSK, then we can use a + # fixed PSK identity and (hex) password. These can + # be used at the same time as the certificate + # configuration, but only for TLS 1.0 through 1.2. + # + # If PSK and certificates are configured at the same + # time for TLS 1.3, then the server will warn you, + # and will disable TLS 1.3, as it will not work. + # + # The work around is to have two modules (or for + # RadSec, two listen sections). One will have PSK + # configured, and the other will have certificates + # configured. + # + # psk_identity = "test" + # psk_hexphrase = "036363823" + + # Dynamic queries for the PSK. If TLS-PSK is used, + # and psk_query is set, then you MUST NOT use + # psk_identity or psk_hexphrase. + # + # Instead, use a dynamic expansion similar to the one + # below. It keys off of TLS-PSK-Identity. It should + # return a of string no more than 512 hex characters. + # That string will be converted to binary, and will + # be used as the dynamic PSK hexphrase. + # + # Note that this query is just an example. You will + # need to customize it for your installation. + # + # psk_query = "%{sql:select hex(key) from psk_keys where keyid = '%{TLS-PSK-Identity}'}" + + # For DH cipher suites to work in OpenSSL < 1.1.0, + # you have to run OpenSSL to create the DH file + # first: + # + # openssl dhparam -out certs/dh 2048 + # + # For OpenSSL >= 1.1.0, just leave this commented + # out, and OpenSSL will do the right thing. + # + # dh_file = ${certdir}/dh + + # If your system doesn't have /dev/urandom, + # you will need to create this file, and + # periodically change its contents. + # + # For security reasons, FreeRADIUS doesn't + # write to files in its configuration + # directory. + # + # random_file = /dev/urandom + + # This can never exceed the size of a RADIUS + # packet (4096 bytes), and is preferably half + # that, to accommodate other attributes in + # RADIUS packet. On most APs the MAX packet + # length is configured between 1500 - 1600 + # In these cases, fragment size should be + # 1024 or less. + # + # fragment_size = 1024 + + # include_length is a flag which is + # by default set to yes If set to + # yes, Total Length of the message is + # included in EVERY packet we send. + # If set to no, Total Length of the + # message is included ONLY in the + # First packet of a fragment series. + # + # include_length = yes + + + # Check the Certificate Revocation List + # + # 1) Copy CA certificates and CRLs to same directory. + # 2) Execute 'c_rehash '. + # 'c_rehash' is OpenSSL's command. + # 3) uncomment the lines below. + # 5) Restart radiusd + # check_crl = yes + + # Check if intermediate CAs have been revoked. + # check_all_crl = yes + + # ca_path = ${cadir} + + # OpenSSL does not reload contents of ca_path dir over time. + # That means that if check_crl is enabled and CRLs are loaded + # from ca_path dir, at some point CRLs will expire and + # RADIUSd will stop authenticating users. + # If ca_path_reload_interval is non-zero, it will force OpenSSL + # to reload all data from ca_path periodically + # + # Flush ca_path each hour + # ca_path_reload_interval = 3600 + + + # Accept an expired Certificate Revocation List + # + # allow_expired_crl = no + + # If check_cert_issuer is set, the value will + # be checked against the DN of the issuer in + # the client certificate. If the values do not + # match, the certificate verification will fail, + # rejecting the user. + # + # This check can be done more generally by checking + # the value of the TLS-Client-Cert-Issuer attribute. + # This check can be done via any mechanism you + # choose. + # + # check_cert_issuer = "/C=GB/ST=Berkshire/L=Newbury/O=My Company Ltd" + + # If check_cert_cn is set, the value will + # be xlat'ed and checked against the CN + # in the client certificate. If the values + # do not match, the certificate verification + # will fail rejecting the user. + # + # This check is done only if the previous + # "check_cert_issuer" is not set, or if + # the check succeeds. + # + # This check can be done more generally by writing + # "unlang" statements to examine the value of the + # TLS-Client-Cert-Common-Name attribute. + # + # check_cert_cn = %{User-Name} + + # + # This configuration item only applies when there is + # an intermediate CA between the "root" CA, and the + # client certificate. If we trust the root CA, then + # by definition we also trust ANY intermediate CA + # which is signed by that root. This means ANOTHER + # intermediate CA can issue client certificates, and + # have them accepted by the EAP module. + # + # The solution is to list ONLY the trusted CAs in the + # FreeRADIUS configuration, and then set this + # configuration item to "yes". + # + # Then, when the server receives a client certificate + # from an untrusted CA, that authentication request + # can be rejected. + # + # It is possible to do these checks in "unlang", by + # checking for unknown names in the + # TLS-Cert-Common-Name attribute, but that is + # more complex. So we add a configuration option + # which can be set once, and which works for all + # possible intermediate CAs, no matter what their + # value. + # + # reject_unknown_intermediate_ca = no + + # Set this option to specify the allowed + # TLS cipher suites. The format is listed + # in "man 1 ciphers". + # + # cipher_list = "DEFAULT" + + # Set this option to specify the allowed + # TLS signature algorithms for OpenSSL 1.1.1 and above. + # The format and available signature algorithms are listed + # in "man 3 SSL_CTX_set1_sigalgs_list". + # + # sigalgs_list = "" + + # If enabled, OpenSSL will use server cipher list + # (possibly defined by cipher_list option above) + # for choosing right cipher suite rather than + # using client-specified list which is OpenSSl default + # behavior. Setting this to "yes" means that OpenSSL + # will choose the servers ciphers, even if they do not + # best match what the client sends. + # + # TLS negotiation is usually good, but can be imperfect. + # This setting allows administrators to "fine tune" it + # if necessary. + # + # cipher_server_preference = no + + # You can selectively disable TLS versions for + # compatability with old client devices. + # + # If your system has OpenSSL 1.1.0 or greater, do NOT + # use these. Instead, set tls_min_version and + # tls_max_version. + # +# disable_tlsv1_2 = yes +# disable_tlsv1_1 = yes +# disable_tlsv1 = yes + + + # Set min / max TLS version. + # + # Generally speaking you should NOT use TLS 1.0 or + # TLS 1.1. They are old, possibly insecure, and + # deprecated. However, it is sometimes necessary to + # enable it for compatibility with legact systems. + # We recommend replacing those legacy systems, and + # using at least TLS 1.2. + # + # Some Debian versions disable older versions of TLS, + # and requires the application to manually enable + # them. + # + # If you are running such a distribution, you should + # set these options, otherwise older clients will not + # be able to connect. + # + # Allowed values are "1.0", "1.1", "1.2", and "1.3". + # + # As of 2021, it is STRONGLY RECOMMENDED to set + # + # tls_min_version = "1.2" + # + # Older TLS versions are insecure and deprecated. + # + # In order to enable TLS 1.0 and TLS 1.1, you may + # also need to update cipher_list below to: + # + # * OpenSSL >= 3.x + # + # cipher_list = "DEFAULT@SECLEVEL=0" + # + # * OpenSSL < 3.x + # + # cipher_list = "DEFAULT@SECLEVEL=1" + # + # The values must be in quotes. + # + # We also STRONGLY RECOMMEND to set + # + # tls_max_version = "1.2" + # + # While the server will accept "1.3" as a value, + # most EAP supplicants WILL NOT DO TLS 1.3 PROPERLY. + # + # i.e. they WILL NOT WORK, SO DO NOT ASK QUESTIONS ON + # THE LIST ABOUT WHY IT DOES NOT WORK. + # + # The TLS 1.3 support is here for future + # compatibility, as clients get upgraded, and people + # don't upgrade their copies of FreeRADIUS. + # + # Also note that we only support TLS 1.3 for EAP-TLS. + # Other versions of EAP (PEAP, TTLS, FAST) DO NOT + # SUPPORT TLS 1.3. + # + # tls_min_version = "1.2" + # tls_max_version = "1.2" + + # Elliptical cryptography configuration + # + # This configuration should be one of the following: + # + # * a name of the curve to use, e.g. "prime256v1". + # + # * a colon separated list of curve NIDs or names. + # + # * an empty string, in which case OpenSSL will choose + # the "best" curve for the situation. + # + # For supported curve names, please run + # + # openssl ecparam -list_curves + # + # ecdh_curve = "" + + # Session resumption / fast reauthentication + # cache. + # + # The cache contains the following information: + # + # session Id - unique identifier, managed by SSL + # User-Name - from the Access-Accept + # Stripped-User-Name - from the Access-Request + # Cached-Session-Policy - from the Access-Accept + # + # See also the "store" subsection below for + # additional attributes which can be cached. + # + # The "Cached-Session-Policy" is the name of a + # policy which should be applied to the cached + # session. This policy can be used to assign + # VLANs, IP addresses, etc. It serves as a useful + # way to re-apply the policy from the original + # Access-Accept to the subsequent Access-Accept + # for the cached session. + # + # On session resumption, these attributes are + # copied from the cache, and placed into the + # reply list. + # + # You probably also want "use_tunneled_reply = yes" + # when using fast session resumption. + # + # You can check if a session has been resumed by + # looking for the existence of the EAP-Session-Resumed + # attribute. Note that this attribute will *only* + # exist in the "post-auth" section. + # + # CAVEATS: The cache is stored and reloaded BEFORE + # the "post-auth" section is run. This limitation + # makes caching more difficult than it should be. In + # practice, it means that the first authentication + # session must set the reply attributes before the + # post-auth section is run. + # + # When the session is resumed, the attributes are + # restored and placed into the session-state list. + # + # cache { + # Enable it. The default is "no". Deleting the entire "cache" + # subsection also disables caching. + # + # The session cache requires the use of the + # "name" and "persist_dir" configuration + # items, below. + # + # The internal OpenSSL session cache has been permanently + # disabled. + # + # You can disallow resumption for a particular user by adding the + # following attribute to the control item list: + # + # Allow-Session-Resumption = No + # + # If "enable = no" below, you CANNOT enable resumption for just one + # user by setting the above attribute to "yes". + # + # enable = no + + # Lifetime of the cached entries, in hours. The sessions will be + # deleted/invalidated after this time. + # + # lifetime = 24 # hours + + # Internal "name" of the session cache. Used to + # distinguish which TLS context sessions belong to. + # + # The server will generate a random value if unset. + # This will change across server restart so you MUST + # set the "name" if you want to persist sessions (see + # below). + # + # name = "EAP module" + + # Simple directory-based storage of sessions. + # Two files per session will be written, the SSL + # state and the cached VPs. This will persist session + # across server restarts. + # + # The default directory is ${logdir}, for historical + # reasons. You should ${db_dir} instead. And check + # the value of db_dir in the main radiusd.conf file. + # It should not point to ${raddb} + # + # The server will need write perms, and the directory + # should be secured from anyone else. You might want + # a script to remove old files from here periodically: + # + # find ${logdir}/tlscache -mtime +2 -exec rm -f {} \; + # + # This feature REQUIRES "name" option be set above. + # + # persist_dir = "${logdir}/tlscache" + + # + # It is possible to partially + # control which attributes exist in the + # session cache. This subsection lists + # attributes which are taken from the reply, + # and saved to the on-disk cache. When the + # session is resumed, these attributes are + # added to the "session-state" list. The + # default configuration will then take care + # of copying them to the reply. + # + # store { + # Tunnel-Private-Group-Id + # } + # } + + # Client certificates can be validated via an + # external command. This allows dynamic CRLs or OCSP + # to be used. + # + # This configuration is commented out in the + # default configuration. Uncomment it, and configure + # the correct paths below to enable it. + # + # If OCSP checking is enabled, and the OCSP checks fail, + # the verify section is not run. + # + # If OCSP checking is disabled, the verify section is + # run on successful certificate validation. + # + # verify { + # If the OCSP checks succeed, the verify section + # is run to allow additional checks. + # + # If you want to skip verify on OCSP success, + # uncomment this configuration item, and set it + # to "yes". + # + # skip_if_ocsp_ok = no + + # A temporary directory where the client + # certificates are stored. This directory + # MUST be owned by the UID of the server, + # and MUST not be accessible by any other + # users. When the server starts, it will do + # "chmod go-rwx" on the directory, for + # security reasons. The directory MUST + # exist when the server starts. + # + # You should also delete all of the files + # in the directory when the server starts. + # + # tmpdir = /tmp/radiusd + + # The command used to verify the client cert. + # We recommend using the OpenSSL command-line + # tool. + # + # The ${..ca_path} text is a reference to + # the ca_path variable defined above. + # + # The %{TLS-Client-Cert-Filename} is the name + # of the temporary file containing the cert + # in PEM format. This file is automatically + # deleted by the server when the command + # returns. + # + # client = "/path/to/openssl verify -CApath ${..ca_path} %{TLS-Client-Cert-Filename}" + # } + + # OCSP Configuration + # + # Certificates can be verified against an OCSP + # Responder. This makes it possible to immediately + # revoke certificates without the distribution of + # new Certificate Revocation Lists (CRLs). + # + # ocsp { + # Enable it. The default is "no". + # Deleting the entire "ocsp" subsection + # also disables ocsp checking + # + # enable = no + + # The OCSP Responder URL can be automatically + # extracted from the certificate in question. + # To override the OCSP Responder URL set + # "override_cert_url = yes". + # + # override_cert_url = yes + + # If the OCSP Responder address is not extracted from + # the certificate, the URL can be defined here. + # + # url = "http://127.0.0.1/ocsp/" + + # If the OCSP Responder can not cope with nonce + # in the request, then it can be disabled here. + # + # For security reasons, disabling this option + # is not recommended as nonce protects against + # replay attacks. + # + # Note that Microsoft AD Certificate Services OCSP + # Responder does not enable nonce by default. It is + # more secure to enable nonce on the responder than + # to disable it in the query here. + # See http://technet.microsoft.com/en-us/library/cc770413%28WS.10%29.aspx + # + # use_nonce = yes + + # Number of seconds before giving up waiting + # for OCSP response. 0 uses system default. + # + # timeout = 0 + + # Normally an error in querying the OCSP + # responder (no response from server, server did + # not understand the request, etc) will result in + # a validation failure. + # + # To treat these errors as 'soft' failures and + # still accept the certificate, enable this + # option. + # + # Warning: this may enable clients with revoked + # certificates to connect if the OCSP responder + # is not available. Use with caution. + # + # softfail = no + # } + + # + # The server can present different certificates based + # on the realm presented in EAP. See + # raddb/certs/realms/README.md for examples of how to + # configure this. + # + # Note that the default is to use the same set of + # realm certificates for both EAP and RadSec! If + # this is not what you want, you should use different + # subdirectories or each, e.g. ${certdir}/realms/radsec/, + # and ${certdir}/realms/eap/ + # + # realm_dir = ${certdir}/realms/ + #} + + + # EAP-TLS + # + # The TLS configuration for TLS-based EAP types is held in + # the "tls-config" section, above. + # + tls { + # Point to the common TLS configuration + # + use_tunneled_reply = yes + private_key_password = s3cr3t + + private_key_file = /etc/brski/registrar-tls.key + certificate_file = /etc/brski/registrar-tls.crt + + ca_file = /etc/hostapd/CA/registrar-tls-ca_and_crl.crt + check_crl = yes + + # As part of checking a client certificate, the EAP-TLS + # sets some attributes such as TLS-Client-Cert-Common-Name. This + # virtual server has access to these attributes, and can + # be used to accept or reject the request. + # + # virtual_server = check-eap-tls + + # You can control whether or not EAP-TLS requires a + # client certificate by setting + # + # configurable_client_cert = yes + # + # Once that setting has been changed, you can then set + # + # EAP-TLS-Require-Client-Cert = No + # + # in the control items for a request, and the EAP-TLS + # module will not require a client certificate from + # the supplicant. + # + # WARNING: This configuration should only be used + # when the users are placed into a "captive portal" + # or "walled garden", where they have limited network + # access. Otherwise the configuraton will allow + # anyone on the network, without authenticating them! + # +# configurable_client_cert = no + } + + + # EAP-TTLS -- Tunneled TLS + # + # The TTLS module implements the EAP-TTLS protocol, + # which can be described as EAP inside of Diameter, + # inside of TLS, inside of EAP, inside of RADIUS... + # + # Surprisingly, it works quite well. + # + #ttls { + # Which tls-config section the TLS negotiation parameters + # are in - see EAP-TLS above for an explanation. + # + # In the case that an old configuration from FreeRADIUS + # v2.x is being used, all the options of the tls-config + # section may also appear instead in the 'tls' section + # above. If that is done, the tls= option here (and in + # tls above) MUST be commented out. + # + # tls = tls-common + + # The tunneled EAP session needs a default EAP type + # which is separate from the one for the non-tunneled + # EAP module. Inside of the TTLS tunnel, we recommend + # using EAP-MD5. If the request does not contain an + # EAP conversation, then this configuration entry is + # ignored. + # + # default_eap_type = md5 + + # The tunneled authentication request does not usually + # contain useful attributes like 'Calling-Station-Id', + # etc. These attributes are outside of the tunnel, + # and normally unavailable to the tunneled + # authentication request. + # + # By setting this configuration entry to 'yes', + # any attribute which is NOT in the tunneled + # authentication request, but which IS available + # outside of the tunnel, is copied to the tunneled + # request. + # + # allowed values: {no, yes} + # +# copy_request_to_tunnel = no + + # This configuration item is deprecated. Instead, + # you should use: + # + # update outer.session-state { + # ... + # } + # + # This will cache attributes for the final Access-Accept. + # + # See "update outer.session-state" in the "post-auth" + # sections of sites-available/default, and of + # sites-available/inner-tunnel + # + # The reply attributes sent to the NAS are usually + # based on the name of the user 'outside' of the + # tunnel (usually 'anonymous'). If you want to send + # the reply attributes based on the user name inside + # of the tunnel, then set this configuration entry to + # 'yes', and the reply to the NAS will be taken from + # the reply to the tunneled request. + # + # allowed values: {no, yes} + # +# use_tunneled_reply = no + + # The inner tunneled request can be sent + # through a virtual server constructed + # specifically for this purpose. + # + # A virtual server MUST be specified. + # +# virtual_server = "inner-tunnel" + + # This has the same meaning, and overwrites, the + # same field in the "tls" configuration, above. + # The default value here is "yes". + # + # include_length = yes + + # Unlike EAP-TLS, EAP-TTLS does not require a client + # certificate. However, you can require one by setting the + # following option. You can also override this option by + # setting + # + # EAP-TLS-Require-Client-Cert = Yes + # + # in the control items for a request. + # + # Note that the majority of supplicants do not support using a + # client certificate with EAP-TTLS, so this option is unlikely + # to be usable for most people. + # + # require_client_cert = yes +# } + + + # EAP-PEAP + # + + ################################################## + # + # !!!!! WARNINGS for Windows compatibility !!!!! + # + ################################################## + # + # If you see the server send an Access-Challenge, + # and the client never sends another Access-Request, + # then + # + # STOP! + # + # The server certificate has to have special OID's + # in it, or else the Microsoft clients will silently + # fail. See the "scripts/xpextensions" file for + # details, and the following page: + # + # https://support.microsoft.com/en-us/help/814394/ + # + # If is still doesn't work, and you're using Samba, + # you may be encountering a Samba bug. See: + # + # https://bugzilla.samba.org/show_bug.cgi?id=6563 + # + # Note that we do not necessarily agree with their + # explanation... but the fix does appear to work. + # + ################################################## + + # The tunneled EAP session needs a default EAP type + # which is separate from the one for the non-tunneled + # EAP module. Inside of the TLS/PEAP tunnel, we + # recommend using EAP-MS-CHAPv2. + # + peap { + # Which tls-config section the TLS negotiation parameters + # are in - see EAP-TLS above for an explanation. + # + # In the case that an old configuration from FreeRADIUS + # v2.x is being used, all the options of the tls-config + # section may also appear instead in the 'tls' section + # above. If that is done, the tls= option here (and in + # tls above) MUST be commented out. + # +# tls = tls-common + + # The tunneled EAP session needs a default + # EAP type which is separate from the one for + # the non-tunneled EAP module. Inside of the + # PEAP tunnel, we recommend using MS-CHAPv2, + # as that is the default type supported by + # Windows clients. + # +# default_eap_type = mschapv2 + + # The PEAP module also has these configuration + # items, which are the same as for TTLS. + # + copy_request_to_tunnel = no + + # This configuration item is deprecated. Instead, + # you should use: + # + # update outer.session-state { + # ... + # } + # + # This will cache attributes for the final Access-Accept. + # + # See "update outer.session-state" in the "post-auth" + # sections of sites-available/default, and of + # sites-available/inner-tunnel + # + use_tunneled_reply = yes + + # When the tunneled session is proxied, the + # home server may not understand EAP-MSCHAP-V2. + # Set this entry to "no" to proxy the tunneled + # EAP-MSCHAP-V2 as normal MSCHAPv2. + # + # This setting can be over-ridden on a packet by + # packet basis by setting + # + # &control:Proxy-Tunneled-Request-As-EAP = yes + # + proxy_tunneled_request_as_eap = no + + # The inner tunneled request can be sent + # through a virtual server constructed + # specifically for this purpose. + # + # A virtual server MUST be specified. + # + virtual_server = "inner-tunnel" + + # This option enables support for MS-SoH + # see doc/SoH.txt for more info. + # It is disabled by default. + # + # soh = yes + + # The SoH reply will be turned into a request which + # can be sent to a specific virtual server: + # + # soh_virtual_server = "soh-server" + + # Unlike EAP-TLS, PEAP does not require a client certificate. + # However, you can require one by setting the following + # option. You can also override this option by setting + # + # EAP-TLS-Require-Client-Cert = Yes + # + # in the control items for a request. + # + # Note that the majority of supplicants do not support using a + # client certificate with PEAP, so this option is unlikely to + # be usable for most people. + # + # require_client_cert = yes + } + + + # EAP-MSCHAPv2 + # + # Note that it is the EAP MS-CHAPv2 sub-module, not + # the main 'mschap' module. + # + # Note also that in order for this sub-module to work, + # the main 'mschap' module MUST ALSO be configured. + # + # This module is the *Microsoft* implementation of MS-CHAPv2 + # in EAP. There is another (incompatible) implementation + # of MS-CHAPv2 in EAP by Cisco, which FreeRADIUS does not + # currently support. + # + #mschapv2 { + # In earlier versions of the server, this module + # never sent the MS-CHAP-Error message to the client. + # This worked, but it had issues when the cached + # password was wrong. The server *should* send + # "E=691 R=0" to the client, which tells it to prompt + # the user for a new password. + # + # The default is to use that functionality. which is + # known to work. If you set "send_error = yes", then + # the error message will be sent back to the client. + # This *may* help some clients work better, but *may* + # also cause other clients to stop working. + # + # send_error = no + + # Server identifier to send back in the challenge. + # This should generally be the host name of the + # RADIUS server. Or, some information to uniquely + # identify it. + # + # identity = "FreeRADIUS" + #} + + + # EAP-FAST + # + # The FAST module implements the EAP-FAST protocol + # + #fast { + # Point to the common TLS configuration + # + # tls = tls-common + + # If 'cipher_list' is set here, it will over-ride the + # 'cipher_list' configuration from the 'tls-common' + # configuration. The EAP-FAST module has it's own + # over-ride for 'cipher_list' because the + # specifications mandata a different set of ciphers + # than are used by the other EAP methods. + # + # cipher_list though must include "ADH" for anonymous provisioning. + # This is not as straight forward as appending "ADH" alongside + # "DEFAULT" as "DEFAULT" contains "!aNULL" so instead it is + # recommended "ALL:!EXPORT:!eNULL:!SSLv2" is used + # + # cipher_list = "ALL:!EXPORT:!eNULL:!SSLv2" + + # PAC lifetime in seconds (default: seven days) + # + # pac_lifetime = 604800 + + # Authority ID of the server + # + # If you are running a cluster of RADIUS servers, you should make + # the value chosen here (and for "pac_opaque_key") the same on all + # your RADIUS servers. This value should be unique to your + # installation. We suggest using a domain name. + # + # authority_identity = "1234" + + # PAC Opaque encryption key (must be exactly 32 bytes in size) + # + # This value MUST be secret, and MUST be generated using + # a secure method, such as via 'openssl rand -hex 32' + # + # pac_opaque_key = "0123456789abcdef0123456789ABCDEF" + + # Same as for TTLS, PEAP, etc. + # + # virtual_server = inner-tunnel + #} +} From 7ff267bb063b30238b6e2d99d86435baae741817 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:47:38 +0000 Subject: [PATCH 05/15] addapted hostapd for freeradius and vlans --- brski-server/installer/payload/vlan-secure.vlan | 5 ++++- brski-server/installer/payload/wlan-secure.conf | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/brski-server/installer/payload/vlan-secure.vlan b/brski-server/installer/payload/vlan-secure.vlan index b99a4b82..eb1dc318 100644 --- a/brski-server/installer/payload/vlan-secure.vlan +++ b/brski-server/installer/payload/vlan-secure.vlan @@ -1 +1,4 @@ -# MAC Address VLAN ID \ No newline at end of file +10 br0 +20 br1 +30 br2 +40 br3 diff --git a/brski-server/installer/payload/wlan-secure.conf b/brski-server/installer/payload/wlan-secure.conf index 6c2af779..fba5bd43 100644 --- a/brski-server/installer/payload/wlan-secure.conf +++ b/brski-server/installer/payload/wlan-secure.conf @@ -9,6 +9,7 @@ channel=6 # Set driver driver=nl80211 +hw_mode=g # Set logging logger_stdout=1 @@ -29,9 +30,9 @@ auth_server_port=1812 auth_server_shared_secret=1234554321 # VLAN configuration -vlan_bridge=br0 dynamic_vlan=1 vlan_naming=1 vlan_file=/etc/hostapd/vlan-secure.vlan +# Path for EAP server user database eap_user_file=/etc/hostapd/eap_user From d4ab4407a7a216db3c165de6f7e0c15d93e13a30 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:48:18 +0000 Subject: [PATCH 06/15] add dhcp ranges to vlans --- brski-server/installer/payload/dnsmasq-secure.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brski-server/installer/payload/dnsmasq-secure.conf b/brski-server/installer/payload/dnsmasq-secure.conf index 4fc56ef1..af668d60 100644 --- a/brski-server/installer/payload/dnsmasq-secure.conf +++ b/brski-server/installer/payload/dnsmasq-secure.conf @@ -1,3 +1,3 @@ DNSMASQ_EXCEPT="lo" -DNSMASQ_INTERFACE="@wlan@" -DNSMASQ_OPTS='--bind-interfaces --dhcp-range=192.168.16.100,192.168.16.199,24h' +DNSMASQ_INTERFACE="@wlan@,brvlan10,brvlan20,brvlan30,brvlan40" +DNSMASQ_OPTS='--bind-interfaces --dhcp-range=tag:@wlan@,192.168.16.100,192.168.16.199,24h --dhcp-range=tag:brvlan10,192.168.34.100,192.168.34.200,24h --dhcp-range=tag:brvlan20,192.168.35.100,192.168.35.200,24h --dhcp-range=tag:brvlan30,192.168.36.100,192.168.36.200,24h --dhcp-range=tag:brvlan40,192.168.37.100,192.168.37.200,24h' \ No newline at end of file From fa99d5683451aa57037ffd0606f86e0f1dd71c54 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:50:21 +0000 Subject: [PATCH 07/15] add configuration for freeradius and CRL --- brski-server/installer/payload/installer | 28 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/brski-server/installer/payload/installer b/brski-server/installer/payload/installer index e5b83222..9dffcf61 100755 --- a/brski-server/installer/payload/installer +++ b/brski-server/installer/payload/installer @@ -6,6 +6,7 @@ IF1="wlan1" CA_DIR="/etc/hostapd/CA" CA_CONF="$CA_DIR/ca.conf" COMBINED_CA_CRL="$CA_DIR/registrar-tls-ca_and_crl.crt" +CA_CERT="/etc/brski/registrar-tls-ca.crt" ip a show dev $IF0 @@ -27,17 +28,29 @@ dpkg -i /tmp/brski_0.2.6_arm64.deb rm /tmp/brski_0.2.6_arm64.deb cp certs/* /etc/brski +chown freerad:freerad /etc/brski/registrar-tls.crt +chown freerad:freerad /etc/brski/registrar-tls.crt mkdir -p /opt/demo-server cp run.sh /opt/demo-server +cp link_bridges.sh /opt/demo-server/ +cp assign_client_to_vlan.sh /opt/demo-server/ + +chmod +x /opt/demo-server/link_bridges.sh +chmod +x /opt/demo-server/assign_client_to_vlan.sh cp nist-demo.service /etc/systemd/system/ chmod 644 /etc/systemd/system/nist-demo.service +cp configure-vlans.service /etc/systemd/system/ +chmod 644 /etc/systemd/system/configure-vlans.service + systemctl daemon-reload + systemctl enable nist-demo.service +systemctl enable configure-vlans.service # Insatll all packages -apt install hostapd dnsmasq iptables avahi-daemon vlan freeradius freeradius-utils -y +apt install hostapd dnsmasq iptables avahi-daemon vlan freeradius freeradius-mysql freeradius-utils echo "8021q" | sudo tee -a /etc/modules # Configure avahi @@ -55,12 +68,15 @@ systemctl disable wpa_supplicant systemctl disable hostapd@$IF0.service systemctl disable hostapd@$IF1.service -cp vlan-dhcp.conf /etc/dnsmasq.d/ -cp vlans /etc/dnsmasq.d/ cp vlan-secure.vlan /etc/hostapd/ cp clients.conf /etc/freeradius/3.0/ -cp eap /etc/freeradius/3.0/mods-enabled/ -cp dh /etc/freeradius/3.0/certs/ +cp eap /etc/freeradius/3.0/mods-available/ + +chown freerad:freerad /etc/freeradius/3.0/mods-available/eap +chmod 640 /etc/freeradius/3.0/mods-enabled/eap + +chown freerad:freerad /etc/freeradius/3.0/clients.conf +chmod 640 /etc/freeradius/3.0/clients.conf # secure hostapd cp wlan-secure.conf /etc/hostapd/$IF1.conf @@ -97,6 +113,7 @@ echo '01' > "$CA_DIR/crlnumber" openssl ca -gencrl -out "$CA_DIR/crl.crt" -config "$CA_CONF" sudo sh -c "cat $CA_CERT $CA_DIR/crl.crt > $COMBINED_CA_CRL" +chown freerad:freerad "$COMBINED_CA_CRL" cp local_revoke_serial_multiple_args.sh "$CA_DIR/" cp local_revoke.sh "$CA_DIR/" @@ -109,4 +126,3 @@ systemctl restart avahi-daemon.service # Start the runner systemctl restart nist-demo.service - From de2784f67a981a58e6e6849efc23d388c92987ba Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:53:12 +0000 Subject: [PATCH 08/15] latest build --- brski-server/installer/gateway-install.sh | Bin 20030 -> 35135 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/brski-server/installer/gateway-install.sh b/brski-server/installer/gateway-install.sh index c1bf3752387ebc3d520f66ff8c35ccb3eb09a562..46e9be6f4b1a7454da1a610f08cde87398d95ab2 100755 GIT binary patch literal 35135 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 Q&+q;Dzqglda{vee0MDyb+W-In delta 19883 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 iIpmN-4msqILk>CQkV6hR Date: Fri, 22 Mar 2024 15:54:50 +0000 Subject: [PATCH 09/15] add static ip address to vlans --- .../installer/payload/link_bridges.sh | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 brski-server/installer/payload/link_bridges.sh diff --git a/brski-server/installer/payload/link_bridges.sh b/brski-server/installer/payload/link_bridges.sh new file mode 100755 index 00000000..e53897fe --- /dev/null +++ b/brski-server/installer/payload/link_bridges.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# This function adds an IP address to the network bridges. +add_ip_to_bridge() { + local bridge_name="$1" + local ip_addr="$2" + local subnet="$3" + + # Check if the bridge exists + if ip link show "$bridge_name" > /dev/null 2>&1; then + # Attempt to add the IP address to the bridge + ip addr add "${ip_addr}/${subnet}" dev "$bridge_name" 2>&1 + if [ $? -eq 0 ]; then + echo "IP address ${ip_addr}/${subnet} added to bridge $bridge_name." + else + echo "Failed to add IP address to bridge $bridge_name." + fi + else + echo "$bridge_name does not exist." + fi +} + +# Add IP addresses to bridges +add_ip_to_bridge brvlan10 192.168.34.1 24 +add_ip_to_bridge brvlan20 192.168.35.1 24 +add_ip_to_bridge brvlan30 192.168.36.1 24 +add_ip_to_bridge brvlan40 192.168.37.1 24 From 07e9f28d2d69a463afc0abd488a6591275ad9207 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:55:38 +0000 Subject: [PATCH 10/15] add vlans service --- .../installer/payload/configure-vlans.service | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 brski-server/installer/payload/configure-vlans.service diff --git a/brski-server/installer/payload/configure-vlans.service b/brski-server/installer/payload/configure-vlans.service new file mode 100644 index 00000000..8e315938 --- /dev/null +++ b/brski-server/installer/payload/configure-vlans.service @@ -0,0 +1,13 @@ +[Unit] +Description=Configure VLAN Bridges with IP Addresses +After=hostapd@wlan1.service +Wants=network.target +Before=network.target + +[Service] +Type=oneshot +ExecStart=/opt/demo-server/link_bridges.sh +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target From 1daeb14f5e36ef7da43be3e111a84e87e5f67e90 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:56:33 +0000 Subject: [PATCH 11/15] update to support the freeradius CRL --- .../installer/payload/local_revoke_serial_multiple_args.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/brski-server/installer/payload/local_revoke_serial_multiple_args.sh b/brski-server/installer/payload/local_revoke_serial_multiple_args.sh index 679aaf87..316d9894 100755 --- a/brski-server/installer/payload/local_revoke_serial_multiple_args.sh +++ b/brski-server/installer/payload/local_revoke_serial_multiple_args.sh @@ -50,7 +50,9 @@ done if [ "$update_crl" = true ]; then echo "Certificates have been revoked. Restarting hostapd..." + sudo chown freerad:freerad $COMBINED_CA_CRL # Restart hostapd sudo systemctl restart hostapd@wlan1.service + sudo systemctl restart configure-vlans.service sudo systemctl restart freeradius.service fi From ea342bf91e6d8e4dc32a9612c079c20e19f0ed65 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:57:13 +0000 Subject: [PATCH 12/15] add new services --- brski-server/installer/payload/run.sh | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/brski-server/installer/payload/run.sh b/brski-server/installer/payload/run.sh index 3b1ca2c2..d0991fd9 100755 --- a/brski-server/installer/payload/run.sh +++ b/brski-server/installer/payload/run.sh @@ -2,33 +2,21 @@ IF0="wlan0" IF1="wlan1" -IF1_10="wlan1.10" -IF1_20="wlan1.20" -IF1_30="wlan1.30" -IF1_40="wlan1.40" IP0="192.168.17.1/24" -IP1="192.168.16.1/24" -IP1_10="192.168.30.1/24" -IP1_20="192.168.31.1/24" -IP1_30="192.168.32.1/24" -IP1_40="192.168.33.1/24" +IP1="192.168.16.1/24" ip addr add $IP0 dev $IF0 ip addr add $IP1 dev $IF1 -ip addr add $IP1_10 dev $IF1_10 -ip addr add $IP1_20 dev $IF1_20 -ip addr add $IP1_30 dev $IF1_30 -ip addr add $IP1_40 dev $IF1_40 - systemctl start hostapd@$IF0.service systemctl start hostapd@$IF1.service systemctl restart dnsmasq@$IF0.service systemctl restart dnsmasq@$IF1.service -sudo systemctl restart freeradius.service +systemctl restart configure-vlans.service +systemctl restart freeradius.service brski -c /etc/brski/config.ini masa -d & brski -c /etc/brski/config.ini registrar -d From 7ebbb7c688deeb3849508147961b0a107bd3da8a Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:58:54 +0000 Subject: [PATCH 13/15] add assign client tto vlan script --- .../payload/assign_client_to_vlan.sh | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 brski-server/installer/payload/assign_client_to_vlan.sh diff --git a/brski-server/installer/payload/assign_client_to_vlan.sh b/brski-server/installer/payload/assign_client_to_vlan.sh new file mode 100755 index 00000000..3e17c5ba --- /dev/null +++ b/brski-server/installer/payload/assign_client_to_vlan.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +SERIAL_NUMBER="$1" +TUNNEL_PRIVATE_GROUP_ID="$2" +LINE_NUMBER=823 +CONFIG_FILE_ENABLED="/etc/freeradius/3.0/sites-enabled/default" +CONFIG_FILE_AVAILABLE="/etc/freeradius/3.0/sites-available/default" +OWNER="freerad" +GROUP="freerad" + +INSERTION_BLOCK=$(cat < "$TEMP_FILE" + + +if ! sudo mv "$TEMP_FILE" "$CONFIG_FILE_AVAILABLE"; then + echo "Failed to update the configuration file." + exit 1 +fi + +if ! chown $OWNER:$GROUP "$CONFIG_FILE_AVAILABLE"; then + echo "Failed to set the file ownership." + exit 1 +fi + +# Check and recreate the symbolic link if necessary +if [ ! -L "$CONFIG_FILE_ENABLED" ] || [ "$(readlink -- "$CONFIG_FILE_ENABLED")" != "$CONFIG_FILE_AVAILABLE" ]; then + if ! ln -sfn "$CONFIG_FILE_AVAILABLE" "$CONFIG_FILE_ENABLED" || ! chown -h $OWNER:$GROUP "$CONFIG_FILE_ENABLED"; then + echo "Failed to recreate the symbolic link." + exit 1 + fi +fi + +chmod 640 "$CONFIG_FILE_AVAILABLE" +echo "Configuration updated successfully." + +systemctl restart freeradius.service +systemctl restart hostapd@wlan1.service +systemctl restart configure-vlans.service + From a57777fdd992c0dd87ae8b983965a985353f7249 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Fri, 22 Mar 2024 15:59:19 +0000 Subject: [PATCH 14/15] update readme --- brski-server/installer/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/brski-server/installer/README.md b/brski-server/installer/README.md index 9ba48b65..923069b6 100644 --- a/brski-server/installer/README.md +++ b/brski-server/installer/README.md @@ -17,3 +17,19 @@ If custom installation is needed then copy the `payload` folder on the target ma $ cd payload $ ./install ``` +# Usage + +* A device can be added to the Certificate Revocation List (CRL) list by calling the following command + +```sh +sudo ./etc/hostapd/CA/local_revoke_serial_multiple_args.sh 0xd8d5be97 46fc6d2a1fbfcf48 +``` +In the command above:
+    0xd8d5be97 = LdevId certificate's serial number
+    46fc6d2a1fbfcf48 = LdevId Subject SerialNumber
+ +* A device an be assign to one of the four available vlans id's(10, 20, 30, 40) by calling the following command: + +```sh +sudo ./opt/demo-server/assign_client_to_vlan.sh 46fc6d2a1fbfcf48 10 +``` \ No newline at end of file From 0b2a94db1f3c057cd4e8f3c1df611e02ecabc428 Mon Sep 17 00:00:00 2001 From: ionut-cmd Date: Tue, 2 Apr 2024 10:59:38 +0100 Subject: [PATCH 15/15] fix freerad own --- brski-server/installer/payload/installer | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brski-server/installer/payload/installer b/brski-server/installer/payload/installer index 9dffcf61..3bedd15b 100755 --- a/brski-server/installer/payload/installer +++ b/brski-server/installer/payload/installer @@ -28,8 +28,8 @@ dpkg -i /tmp/brski_0.2.6_arm64.deb rm /tmp/brski_0.2.6_arm64.deb cp certs/* /etc/brski -chown freerad:freerad /etc/brski/registrar-tls.crt -chown freerad:freerad /etc/brski/registrar-tls.crt +sudo chown freerad:freerad /etc/brski/registrar-tls.crt +sudo chown freerad:freerad /etc/brski/registrar-tls.key mkdir -p /opt/demo-server cp run.sh /opt/demo-server