1620 lines
50 KiB
Bash
Executable File
1620 lines
50 KiB
Bash
Executable File
#!/bin/bash
|
|
# _____ _ _
|
|
# | __|___ ___ ___ _| |___ _____| |_ ___ ___ ___
|
|
# | __| _| -_| -_| . | . | | . | . | | -_|
|
|
# |__| |_| |___|___|___|___|_|_|_|___|___|_|_|___|
|
|
#
|
|
# Freedom in the Cloud
|
|
#
|
|
# Alters the security settings
|
|
#
|
|
# License
|
|
# =======
|
|
#
|
|
# Copyright (C) 2015-2018 Bob Mottram <bob@freedombone.net>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
PROJECT_NAME='freedombone'
|
|
|
|
export TEXTDOMAIN=${PROJECT_NAME}-sec
|
|
export TEXTDOMAINDIR="/usr/share/locale"
|
|
|
|
CONFIGURATION_FILE=$HOME/${PROJECT_NAME}.cfg
|
|
COMPLETION_FILE=$HOME/${PROJECT_NAME}-completed.txt
|
|
|
|
UTILS_FILES="/usr/share/${PROJECT_NAME}/utils/${PROJECT_NAME}-utils-*"
|
|
for f in $UTILS_FILES
|
|
do
|
|
source "$f"
|
|
done
|
|
|
|
SSL_PROTOCOLS=
|
|
SSL_CIPHERS=
|
|
SSH_CIPHERS=
|
|
SSH_MACS=
|
|
SSH_KEX=
|
|
SSH_HOST_KEY_ALGORITHMS=
|
|
SSH_PASSWORDS=
|
|
XMPP_CIPHERS=
|
|
XMPP_ECC_CURVE=
|
|
|
|
WEBSITES_DIRECTORY='/etc/nginx/sites-available'
|
|
DOVECOT_CIPHERS='/etc/dovecot/conf.d/10-ssl.conf'
|
|
SSH_CONFIG='/etc/ssh/sshd_config'
|
|
XMPP_CONFIG='/etc/prosody/conf.avail/xmpp.cfg.lua'
|
|
|
|
MINIMUM_LENGTH=6
|
|
|
|
IMPORT_FILE=
|
|
EXPORT_FILE=
|
|
|
|
CURRENT_DIR=$(pwd)
|
|
|
|
DH_KEYLENGTH=2048
|
|
LETSENCRYPT_SERVER='https://acme-v01.api.letsencrypt.org/directory'
|
|
|
|
MY_USERNAME=
|
|
|
|
function ping_enable_disable {
|
|
ping_str=$"\\nDo you want to enable other systems to ping this machine?\\n\\nPing may be useful for diagnostic purposes, but for added security you may not want to enable it."
|
|
enable_ping="no"
|
|
dialog --title $"Enable Ping / ICMP" \
|
|
--backtitle $"Freedombone Control Panel" \
|
|
--defaultno \
|
|
--yesno "$ping_str" 10 60
|
|
sel=$?
|
|
case $sel in
|
|
0) enable_ping="yes";;
|
|
255) return;;
|
|
esac
|
|
|
|
if [[ $enable_ping == "yes" ]]; then
|
|
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
|
|
iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
|
|
echo "0" > /proc/sys/net/ipv4/icmp_echo_ignore_all
|
|
else
|
|
iptables -D INPUT -p icmp --icmp-type echo-request -j ACCEPT
|
|
iptables -D OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
|
|
echo "1" > /proc/sys/net/ipv4/icmp_echo_ignore_all
|
|
fi
|
|
}
|
|
|
|
function any_key_verify {
|
|
echo ''
|
|
read -n1 -rsp $"Press any key to continue or C to check a hash..." key
|
|
if [[ "$key" != 'c' && "$key" != 'C' ]]; then
|
|
return
|
|
fi
|
|
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Check tripwire hash" \
|
|
--backtitle $"Freedombone Control Panel" \
|
|
--inputbox $"Paste your tripwire hash below and it will be checked against the current database" 12 60 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
GIVEN_HASH=$(<"$data")
|
|
if [ ${#GIVEN_HASH} -gt 8 ]; then
|
|
if [[ "$GIVEN_HASH" == *' '* ]]; then
|
|
dialog --title $"Check tripwire" \
|
|
--msgbox $"\\nThe hash should not contain any spaces" 10 40
|
|
else
|
|
DBHASH=$(sha512sum "/var/lib/tripwire/${HOSTNAME}.twd" | awk -F ' ' '{print $1}')
|
|
if [[ "$DBHASH" == "$GIVEN_HASH" ]]; then
|
|
dialog --title $"Check tripwire" \
|
|
--msgbox $"\\nSuccess\\n\\nThe hash you gave matches the current tripwire database" 10 40
|
|
else
|
|
dialog --title $"Check tripwire" \
|
|
--msgbox $"\\nFailed\\n\\nThe hash you gave does not match the current tripwire database. This might be because you reset the tripwire, or there could have been an unauthorised modification of the system" 12 50
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
}
|
|
|
|
function show_tripwire_verification_code {
|
|
if [ ! -f "/var/lib/tripwire/${HOSTNAME}.twd" ]; then
|
|
return
|
|
fi
|
|
clear
|
|
echo ''
|
|
echo $'Tripwire Verification Code'
|
|
echo ''
|
|
DBHASH=$(sha512sum "/var/lib/tripwire/${HOSTNAME}.twd")
|
|
echo -n "$DBHASH" | qrencode -t UTF8
|
|
echo ''
|
|
echo "$DBHASH"
|
|
echo ''
|
|
}
|
|
|
|
function reset_tripwire {
|
|
if [ ! -f /usr/bin/reset-tripwire ]; then
|
|
echo $'Missing /usr/bin/reset-tripwire'
|
|
any_key
|
|
return
|
|
fi
|
|
if [ ! -f "/etc/tripwire/${HOSTNAME}-local.key" ]; then
|
|
if [ -f "/etc/tripwire/${PROJECT_NAME}-local.key" ]; then
|
|
# shellcheck disable=SC2086
|
|
mv /etc/tripwire/${PROJECT_NAME}-local.key /etc/tripwire/${HOSTNAME}-local.key
|
|
# shellcheck disable=SC2086
|
|
mv /etc/tripwire/${PROJECT_NAME}-site.key /etc/tripwire/${HOSTNAME}-site.key
|
|
else
|
|
echo $'Error: missing local key'
|
|
any_key
|
|
return
|
|
fi
|
|
fi
|
|
clear
|
|
echo $'Turing off logging...'
|
|
"${PROJECT_NAME}-logging" off
|
|
echo $'Locking down permissions...'
|
|
lockdown_permissions
|
|
echo $'Creating configuration...'
|
|
echo '
|
|
|
|
' | twadmin --create-cfgfile -S "/etc/tripwire/${HOSTNAME}-site.key" /etc/tripwire/twcfg.txt
|
|
echo $'Resetting policy...'
|
|
echo '
|
|
|
|
' | twadmin --create-polfile -S "/etc/tripwire/${HOSTNAME}-site.key" /etc/tripwire/twpol.txt
|
|
echo $'Creating tripwire database'
|
|
echo '
|
|
|
|
' | tripwire --init --cfgfile /etc/tripwire/tw.cfg --polfile /etc/tripwire/tw.pol --dbfile "/var/lib/tripwire/${HOSTNAME}.twd"
|
|
echo $'Resetting the Tripwire...'
|
|
echo ''
|
|
echo '
|
|
|
|
' | reset-tripwire
|
|
echo ''
|
|
|
|
# Sometimes nginx fails to restart if matrix is installed
|
|
# Restart matrix first
|
|
if [ -d /etc/matrix ]; then
|
|
systemctl restart matrix
|
|
systemctl restart nginx
|
|
fi
|
|
|
|
if [ -f "/var/lib/tripwire/${HOSTNAME}.twd" ]; then
|
|
show_tripwire_verification_code
|
|
echo $'Tripwire is now reset. Take a note of the above hash, or record'
|
|
echo $'the QR code using a mobile device. This will enable you to independently'
|
|
echo $'verify the integrity of the tripwire.'
|
|
else
|
|
echo $'ERROR: tripwire database was not created'
|
|
fi
|
|
any_key
|
|
}
|
|
|
|
function passwords_show_apps {
|
|
SELECTED_APP=
|
|
i=0
|
|
W=()
|
|
name=()
|
|
# shellcheck disable=SC2068
|
|
for a in ${APPS_AVAILABLE[@]}
|
|
do
|
|
if grep -q "change_password_" "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${a}"; then
|
|
i=$((i+1))
|
|
W+=("$i" "$a")
|
|
name+=("$a")
|
|
fi
|
|
done
|
|
i=$((i+1))
|
|
W+=("$i" "mariadb")
|
|
name+=("mariadb")
|
|
|
|
# shellcheck disable=SC2068
|
|
selected_app_index=$(dialog --backtitle $"Freedombone Control Panel" --title $"User $SELECTED_USERNAME: Select App" --menu $"Select one of the following:" 24 40 17 ${W[@]} 3>&2 2>&1 1>&3)
|
|
|
|
# shellcheck disable=SC2181
|
|
if [ $? -eq 0 ]; then
|
|
SELECTED_APP="${name[$((selected_app_index-1))]}"
|
|
fi
|
|
}
|
|
|
|
function view_or_change_passwords {
|
|
passwords_select_user
|
|
if [ ! "$SELECTED_USERNAME" ]; then
|
|
return
|
|
fi
|
|
detect_installed_apps
|
|
passwords_show_apps
|
|
if [ ! "$SELECTED_APP" ]; then
|
|
return
|
|
fi
|
|
|
|
CURR_PASSWORD=$("${PROJECT_NAME}-pass" -u "${SELECTED_USERNAME}" -a "${SELECTED_APP}")
|
|
|
|
icann_address=$(get_app_icann_address "${SELECTED_APP}")
|
|
onion_address=$(get_app_onion_address "${SELECTED_APP}")
|
|
|
|
titlestr=$"View or Change Password"
|
|
if [ ${#onion_address} -gt 0 ]; then
|
|
viewstr=$"${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address or $onion_address\\n\\nCopy or change it if you wish."
|
|
else
|
|
viewstr=$"${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address\\n\\nCopy or change it if you wish."
|
|
fi
|
|
|
|
if [ -f /root/.nostore ]; then
|
|
titlestr=$"Change Password"
|
|
if [ ${#onion_address} -gt 0 ]; then
|
|
viewstr=$"Change the ${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address or $onion_address."
|
|
else
|
|
viewstr=$"Change the ${SELECTED_APP} password for ${SELECTED_USERNAME} on $icann_address."
|
|
fi
|
|
fi
|
|
|
|
if [[ "${SELECTED_APP}" == 'mariadb' ]]; then
|
|
CURR_PASSWORD=$("${PROJECT_NAME}-pass" -u root -a mariadb)
|
|
dialog --title $"MariaDB database password" \
|
|
--msgbox "\\n ${CURR_PASSWORD}" 7 40
|
|
return
|
|
fi
|
|
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title "$titlestr" \
|
|
--backtitle $"Freedombone Control Panel" \
|
|
--inputbox "$viewstr" 12 75 "$CURR_PASSWORD" 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
CURR_PASSWORD=$(<"$data")
|
|
if [ ${#CURR_PASSWORD} -gt 8 ]; then
|
|
"${PROJECT_NAME}-pass" -u "${SELECTED_USERNAME}" -a "${SELECTED_APP}" -p "${CURR_PASSWORD}"
|
|
"change_password_${SELECTED_APP}" "${SELECTED_USERNAME}" "${CURR_PASSWORD}"
|
|
dialog --title $"Change password" \
|
|
--msgbox $"The password was changed" 6 40
|
|
else
|
|
dialog --title $"Change password" \
|
|
--msgbox $"The password given must be at least 8 characters" 6 40
|
|
fi
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
}
|
|
|
|
function show_firewall {
|
|
W=()
|
|
while read -r line; do
|
|
firewall_name=$(echo "$line" | awk -F '=' '{print $1}')
|
|
firewall_port=$(echo "$line" | awk -F '=' '{print $2}')
|
|
W+=("${firewall_name}" "${firewall_port}")
|
|
done < "$FIREWALL_CONFIG"
|
|
|
|
# shellcheck disable=SC2068
|
|
dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Firewall" --menu $"Press ESC to return to main menu" 28 50 28 "${W[@]}" 3>&2 2>&1 1>&3
|
|
}
|
|
|
|
function export_passwords {
|
|
detect_usb_drive
|
|
dialog --title $"Export passwords to USB drive $USB_DRIVE" \
|
|
--backtitle $"Security Settings" \
|
|
--defaultno \
|
|
--yesno $"\\nPlease confirm that you wish to export passwords to a LUKS formatted USB drive. The drive should be plugged in." 10 60
|
|
sel=$?
|
|
case $sel in
|
|
1) return;;
|
|
255) return;;
|
|
esac
|
|
|
|
dialog --title $"Export passwords to USB drive $USB_DRIVE" \
|
|
--backtitle $"Security Settings" \
|
|
--defaultno \
|
|
--yesno $"\\nDo you need to format the drive as LUKS encrypted?" 12 60
|
|
sel=$?
|
|
case $sel in
|
|
0) ${PROJECT_NAME}-format "$USB_DRIVE";;
|
|
esac
|
|
|
|
clear
|
|
backup_mount_drive "${USB_DRIVE}"
|
|
${PROJECT_NAME}-pass --export "${USB_MOUNT}/${PROJECT_NAME}-passwords.xml"
|
|
backup_unmount_drive "${USB_DRIVE}"
|
|
}
|
|
|
|
function get_protocols_from_website {
|
|
if [ ! -f "$WEBSITES_DIRECTORY/$1" ]; then
|
|
return
|
|
fi
|
|
SSL_PROTOCOLS=$(grep 'ssl_protocols ' "$WEBSITES_DIRECTORY/$1" | awk -F "ssl_protocols " '{print $2}' | awk -F ';' '{print $1}')
|
|
}
|
|
|
|
function get_ciphers_from_website {
|
|
if [ ! -f "$WEBSITES_DIRECTORY/$1" ]; then
|
|
return
|
|
fi
|
|
SSL_CIPHERS=$(grep 'ssl_ciphers ' "$WEBSITES_DIRECTORY/$1" | awk -F "ssl_ciphers " '{print $2}' | awk -F "'" '{print $2}')
|
|
}
|
|
|
|
function get_imap_settings {
|
|
if [ ! -f $DOVECOT_CIPHERS ]; then
|
|
return
|
|
fi
|
|
# clear commented out cipher list
|
|
sed -i "s|#ssl_cipher_list.*||g" $DOVECOT_CIPHERS
|
|
if [ "$SSL_CIPHERS" ]; then
|
|
return
|
|
fi
|
|
if [ ${#SSL_CIPHERS} -gt $MINIMUM_LENGTH ]; then
|
|
return
|
|
fi
|
|
SSL_CIPHERS=$(grep 'ssl_cipher_list' "$DOVECOT_CIPHERS" | awk -F '=' '{print $2}' | awk -F "'" '{print $2}')
|
|
}
|
|
|
|
function get_xmpp_settings {
|
|
if [ ! -f $XMPP_CONFIG ]; then
|
|
return
|
|
fi
|
|
XMPP_CIPHERS=$(grep 'ciphers ' "$XMPP_CONFIG" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}')
|
|
XMPP_ECC_CURVE=$(grep 'curve ' "$XMPP_CONFIG" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}')
|
|
}
|
|
|
|
function get_ssh_settings {
|
|
if [ -f $SSH_CONFIG ]; then
|
|
SSH_PASSWORDS=$(grep 'PasswordAuthentication ' "$SSH_CONFIG" | awk -F 'PasswordAuthentication ' '{print $2}')
|
|
fi
|
|
if [ -f /etc/ssh/ssh_config ]; then
|
|
SSH_HOST_KEY_ALGORITHMS=$(grep 'HostKeyAlgorithms ' "/etc/ssh/ssh_config" | awk -F 'HostKeyAlgorithms ' '{print $2}')
|
|
fi
|
|
}
|
|
|
|
function change_website_settings {
|
|
if [ ! "$SSL_PROTOCOLS" ]; then
|
|
return
|
|
fi
|
|
if [ ! "$SSL_CIPHERS" ]; then
|
|
return
|
|
fi
|
|
if [ ${#SSL_PROTOCOLS} -lt $MINIMUM_LENGTH ]; then
|
|
return
|
|
fi
|
|
if [ ${#SSL_CIPHERS} -lt $MINIMUM_LENGTH ]; then
|
|
return
|
|
fi
|
|
if [ ! -d $WEBSITES_DIRECTORY ]; then
|
|
return
|
|
fi
|
|
|
|
cd $WEBSITES_DIRECTORY || exit 2468724628
|
|
for file in $(dir -d "*") ; do
|
|
sed -i "s|ssl_protocols .*|ssl_protocols $SSL_PROTOCOLS;|g" "$WEBSITES_DIRECTORY/$file"
|
|
if ! grep -q "Mobile compatible ciphers" "$WEBSITES_DIRECTORY/$file"; then
|
|
|
|
sed -i "s|ssl_ciphers .*|ssl_ciphers '$SSL_CIPHERS';|g" "$WEBSITES_DIRECTORY/$file"
|
|
else
|
|
sed -i "s|ssl_ciphers .*|ssl_ciphers '$SSL_CIPHERS_MOBILE';|g" "$WEBSITES_DIRECTORY/$file"
|
|
fi
|
|
done
|
|
systemctl restart nginx
|
|
echo $'Web security settings changed'
|
|
}
|
|
|
|
function change_imap_settings {
|
|
if [ ! -f $DOVECOT_CIPHERS ]; then
|
|
return
|
|
fi
|
|
if [ ! "$SSL_CIPHERS" ]; then
|
|
return
|
|
fi
|
|
if [ ${#SSL_CIPHERS} -lt $MINIMUM_LENGTH ]; then
|
|
return
|
|
fi
|
|
sed -i "s|ssl_cipher_list.*|ssl_cipher_list = '$SSL_CIPHERS'|g" $DOVECOT_CIPHERS
|
|
sed -i "s|ssl_protocols.*|ssl_protocols = '$SSL_PROTOCOLS'|g" $DOVECOT_CIPHERS
|
|
systemctl restart dovecot
|
|
echo $'imap security settings changed'
|
|
}
|
|
|
|
function change_ssh_settings {
|
|
if [ -f /etc/ssh/ssh_config ]; then
|
|
if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
|
|
sed -i "s|HostKeyAlgorithms .*|HostKeyAlgorithms $SSH_HOST_KEY_ALGORITHMS|g" /etc/ssh/ssh_config
|
|
echo $'ssh client security settings changed'
|
|
fi
|
|
fi
|
|
if [ -f $SSH_CONFIG ]; then
|
|
if [ ! $SSH_CIPHERS ]; then
|
|
return
|
|
fi
|
|
if [ ! $SSH_MACS ]; then
|
|
return
|
|
fi
|
|
if [ ! $SSH_KEX ]; then
|
|
return
|
|
fi
|
|
if [ ! "$SSH_PASSWORDS" ]; then
|
|
SSH_PASSWORDS='yes'
|
|
fi
|
|
|
|
sed -i "s|Ciphers .*|Ciphers $SSH_CIPHERS|g" $SSH_CONFIG
|
|
sed -i "s|MACs .*|MACs $SSH_MACS|g" $SSH_CONFIG
|
|
sed -i "s|KexAlgorithms .*|KexAlgorithms $SSH_KEX|g" $SSH_CONFIG
|
|
sed -i "s|#PasswordAuthentication .*|PasswordAuthentication $SSH_PASSWORDS|g" $SSH_CONFIG
|
|
sed -i "s|PasswordAuthentication .*|PasswordAuthentication $SSH_PASSWORDS|g" $SSH_CONFIG
|
|
systemctl restart ssh
|
|
echo $'ssh server security settings changed'
|
|
fi
|
|
}
|
|
|
|
function change_xmpp_settings {
|
|
if [ ! -f $XMPP_CONFIG ]; then
|
|
return
|
|
fi
|
|
if [ ! "$XMPP_CIPHERS" ]; then
|
|
return
|
|
fi
|
|
if [ ! "$XMPP_ECC_CURVE" ]; then
|
|
return
|
|
fi
|
|
sed -i "s|ciphers =.*|ciphers = \"$XMPP_CIPHERS\";|g" $XMPP_CONFIG
|
|
sed -i "s|curve =.*|curve = \"$XMPP_ECC_CURVE\";|g" $XMPP_CONFIG
|
|
systemctl restart prosody
|
|
echo $'xmpp security settings changed'
|
|
}
|
|
|
|
function allow_ssh_passwords {
|
|
dialog --title $"SSH Passwords" \
|
|
--backtitle $"Freedombone Security Configuration" \
|
|
--yesno $"\\nAllow SSH login using passwords?" 7 60
|
|
sel=$?
|
|
case $sel in
|
|
0) SSH_PASSWORDS="yes";;
|
|
1) SSH_PASSWORDS="no";;
|
|
255) exit 0;;
|
|
esac
|
|
}
|
|
|
|
function interactive_setup {
|
|
if [ "$SSL_CIPHERS" ]; then
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --backtitle $"Freedombone Security Configuration" \
|
|
--form $"\\nWeb/IMAP Ciphers:" 10 95 2 \
|
|
$"Protocols:" 1 1 "$SSL_PROTOCOLS" 1 15 90 90 \
|
|
$"Ciphers:" 2 1 "$SSL_CIPHERS" 2 15 90 512 \
|
|
2> "$data"
|
|
sel=$?
|
|
case $sel in
|
|
1) SSL_PROTOCOLS=$(sed -n 1p < "$data")
|
|
SSL_CIPHERS=$(sed -n 2p < "$data")
|
|
;;
|
|
255) rm -f "$data"
|
|
exit 0;;
|
|
esac
|
|
rm -f "$data"
|
|
fi
|
|
|
|
data=$(mktemp 2>/dev/null)
|
|
if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
|
|
dialog --backtitle $"Freedombone Security Configuration" \
|
|
--form $"\\nSecure Shell Ciphers:" 13 95 4 \
|
|
$"Ciphers:" 1 1 "$SSH_CIPHERS" 1 15 90 512 \
|
|
$"MACs:" 2 1 "$SSH_MACS" 2 15 90 512 \
|
|
$"KEX:" 3 1 "$SSH_KEX" 3 15 90 512 \
|
|
$"Host key algorithms:" 4 1 "$SSH_HOST_KEY_ALGORITHMS" 4 15 90 512 \
|
|
2> "$data"
|
|
sel=$?
|
|
case $sel in
|
|
1) SSH_CIPHERS=$(sed -n 1p < "$data")
|
|
SSH_MACS=$(sed -n 2p < "$data")
|
|
SSH_KEX=$(sed -n 3p < "$data")
|
|
SSH_HOST_KEY_ALGORITHMS=$(sed -n 4p < "$data")
|
|
;;
|
|
255) rm -f "$data"
|
|
exit 0;;
|
|
esac
|
|
else
|
|
dialog --backtitle $"Freedombone Security Configuration" \
|
|
--form $"\\nSecure Shell Ciphers:" 11 95 3 \
|
|
$"Ciphers:" 1 1 "$SSH_CIPHERS" 1 15 90 512 \
|
|
$"MACs:" 2 1 "$SSH_MACS" 2 15 90 512 \
|
|
$"KEX:" 3 1 "$SSH_KEX" 3 15 90 512 \
|
|
2> "$data"
|
|
sel=$?
|
|
case $sel in
|
|
1) SSH_CIPHERS=$(sed -n 1p < "$data")
|
|
SSH_MACS=$(sed -n 2p < "$data")
|
|
SSH_KEX=$(sed -n 3p < "$data")
|
|
;;
|
|
255) rm -f "$data"
|
|
exit 0;;
|
|
esac
|
|
fi
|
|
rm -f "$data"
|
|
|
|
if [ "$XMPP_CIPHERS" ]; then
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --backtitle $"Freedombone Security Configuration" \
|
|
--form $"\\nXMPP Ciphers:" 10 95 2 \
|
|
$"Ciphers:" 1 1 "$XMPP_CIPHERS" 1 15 90 512 \
|
|
$"ECC Curve:" 2 1 "$XMPP_ECC_CURVE" 2 15 50 50 \
|
|
2> "$data"
|
|
sel=$?
|
|
case $sel in
|
|
1) XMPP_CIPHERS=$(sed -n 1p < "$data")
|
|
XMPP_ECC_CURVE=$(sed -n 2p < "$data")
|
|
;;
|
|
255) rm -f "$data"
|
|
exit 0;;
|
|
esac
|
|
rm -f "$data"
|
|
fi
|
|
|
|
dialog --title $"Final Confirmation" \
|
|
--backtitle $"Freedombone Security Configuration" \
|
|
--defaultno \
|
|
--yesno $"\\nPlease confirm that you wish your security settings to be changed?\\n\\nWARNING: any mistakes made in the security settings could compromise your system, so be extra careful when answering 'yes'." 12 60
|
|
sel=$?
|
|
case $sel in
|
|
1) clear
|
|
echo $'Exiting without changing security settings'
|
|
exit 0;;
|
|
255) clear
|
|
echo $'Exiting without changing security settings'
|
|
exit 0;;
|
|
esac
|
|
|
|
clear
|
|
}
|
|
|
|
function send_monkeysphere_server_keys_to_users {
|
|
monkeysphere_server_keys=$(monkeysphere-host show-key | grep $"OpenPGP fingerprint" | awk -F ' ' '{print $3}')
|
|
for d in /home/*/ ; do
|
|
USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
|
|
if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
|
|
if [ ! -d "/home/$USERNAME/.monkeysphere" ]; then
|
|
mkdir "/home/$USERNAME/.monkeysphere"
|
|
fi
|
|
echo "$monkeysphere_server_keys" > "/home/$USERNAME/.monkeysphere/server_keys"
|
|
chown -R "$USERNAME":"$USERNAME" "/home/$USERNAME/.monkeysphere"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function regenerate_ssh_host_keys {
|
|
rm -f /etc/ssh/ssh_host_*
|
|
dpkg-reconfigure openssh-server
|
|
echo $'ssh host keys regenerated'
|
|
# remove small moduli
|
|
awk '$5 > 2000' /etc/ssh/moduli > ~/moduli
|
|
mv ~/moduli /etc/ssh/moduli
|
|
echo $'ssh small moduli removed'
|
|
# update monkeysphere
|
|
DEFAULT_DOMAIN_NAME=
|
|
read_config_param "DEFAULT_DOMAIN_NAME"
|
|
monkeysphere-host import-key /etc/ssh/ssh_host_rsa_key "ssh://$DEFAULT_DOMAIN_NAME"
|
|
SSH_ONION_HOSTNAME=$(grep 'ssh onion domain' "${COMPLETION_FILE}" | awk -F ':' '{print $2}')
|
|
monkeysphere-host import-key /etc/ssh/ssh_host_rsa_key "ssh://$SSH_ONION_HOSTNAME"
|
|
monkeysphere-host publish-key
|
|
send_monkeysphere_server_keys_to_users
|
|
echo $'updated monkeysphere ssh host key'
|
|
systemctl restart ssh
|
|
}
|
|
|
|
function regenerate_dh_keys {
|
|
if [ ! -d /etc/ssl/mycerts ]; then
|
|
echo $'No dhparam certificates were found'
|
|
return
|
|
fi
|
|
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --backtitle "Freedombone Security Configuration" \
|
|
--title "Diffie-Hellman key length" \
|
|
--radiolist "The smaller length is better suited to low power embedded systems:" 12 40 3 \
|
|
1 "2048 bits" off \
|
|
2 "3072 bits" on \
|
|
3 "4096 bits" off 2> "$data"
|
|
sel=$?
|
|
case $sel in
|
|
1) rm -f "$data"
|
|
exit 1;;
|
|
255) rm -f "$data"
|
|
exit 1;;
|
|
esac
|
|
case $(cat "$data") in
|
|
1) DH_KEYLENGTH=2048;;
|
|
2) DH_KEYLENGTH=3072;;
|
|
3) DH_KEYLENGTH=4096;;
|
|
esac
|
|
rm -f "$data"
|
|
|
|
${PROJECT_NAME}-dhparam --recalc yes -l ${DH_KEYLENGTH}
|
|
}
|
|
|
|
function renew_startssl {
|
|
renew_domain=
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Renew a StartSSL certificate" \
|
|
--backtitle $"Freedombone Security Settings" \
|
|
--inputbox $"Enter the domain name" 8 60 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
renew_domain=$(<"$data")
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
|
|
if [ ! "$renew_domain" ]; then
|
|
return
|
|
fi
|
|
|
|
if [[ $renew_domain == "http"* ]]; then
|
|
dialog --title $"Renew a StartSSL certificate" \
|
|
--msgbox $"Don't include the https://" 6 40
|
|
return
|
|
fi
|
|
|
|
if [ ! -f "/etc/ssl/certs/${renew_domain}.dhparam" ]; then
|
|
dialog --title $"Renew a StartSSL certificate" \
|
|
--msgbox $"An existing certificate for $renew_domain was not found" 6 40
|
|
return
|
|
fi
|
|
|
|
if [[ $renew_domain != *"."* ]]; then
|
|
dialog --title $"Renew a StartSSL certificate" \
|
|
--msgbox $"Invalid domain name: $renew_domain" 6 40
|
|
return
|
|
fi
|
|
|
|
${PROJECT_NAME}-renew-cert -h "$renew_domain" -p startssl
|
|
|
|
exit 0
|
|
}
|
|
|
|
function renew_letsencrypt {
|
|
renew_domain=
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Renew a Let's Encrypt certificate" \
|
|
--backtitle $"Freedombone Security Settings" \
|
|
--inputbox $"Enter the domain name" 8 60 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
renew_domain=$(<"$data")
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
|
|
if [ ! "$renew_domain" ]; then
|
|
return
|
|
fi
|
|
|
|
if [[ $renew_domain == "http"* ]]; then
|
|
dialog --title $"Renew a Let's Encrypt certificate" \
|
|
--msgbox $"Don't include the https://" 6 40
|
|
return
|
|
fi
|
|
|
|
if [ ! -f "/etc/ssl/certs/${renew_domain}.dhparam" ]; then
|
|
dialog --title $"Renew a Let's Encrypt certificate" \
|
|
--msgbox $"An existing certificate for $renew_domain was not found" 6 40
|
|
return
|
|
fi
|
|
|
|
if [[ $renew_domain != *"."* ]]; then
|
|
dialog --title $"Renew a Let's Encrypt certificate" \
|
|
--msgbox $"Invalid domain name: $renew_domain" 6 40
|
|
return
|
|
fi
|
|
|
|
${PROJECT_NAME}-renew-cert -h "$renew_domain" -p 'letsencrypt'
|
|
|
|
exit 0
|
|
}
|
|
|
|
function delete_letsencrypt {
|
|
delete_domain=
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Delete a Let's Encrypt certificate" \
|
|
--backtitle $"Freedombone Security Settings" \
|
|
--inputbox $"Enter the domain name" 8 60 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
delete_domain=$(<"$data")
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
|
|
if [ ! "$delete_domain" ]; then
|
|
return
|
|
fi
|
|
|
|
if [[ $delete_domain == "http"* ]]; then
|
|
dialog --title $"Delete a Let's Encrypt certificate" \
|
|
--msgbox $"Don't include the https://" 6 40
|
|
return
|
|
fi
|
|
|
|
if [ ! -f "/etc/ssl/certs/${delete_domain}.dhparam" ]; then
|
|
dialog --title $"Delete a Let's Encrypt certificate" \
|
|
--msgbox $"An existing certificate for $renew_domain was not found" 6 40
|
|
return
|
|
fi
|
|
|
|
if [[ $delete_domain != *"."* ]]; then
|
|
dialog --title $"Delete a Let's Encrypt certificate" \
|
|
--msgbox $"Invalid domain name: $delete_domain" 6 40
|
|
return
|
|
fi
|
|
|
|
dialog --title $"Delete a Let's Encrypt certificate" \
|
|
--backtitle $"Freedombone Security Settings" \
|
|
--defaultno \
|
|
--yesno $"\\nConfirm deletion of the TLS certificate for $delete_domain ?" 7 60
|
|
sel=$?
|
|
case $sel in
|
|
0) ${PROJECT_NAME}-addcert -r "$delete_domain";;
|
|
255) exit 0;;
|
|
esac
|
|
|
|
exit 0
|
|
}
|
|
|
|
function create_letsencrypt {
|
|
new_domain=
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Create a new Let's Encrypt certificate" \
|
|
--backtitle $"Freedombone Security Settings" \
|
|
--inputbox $"Enter the domain name" 8 60 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
new_domain=$(<"$data")
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
|
|
if [ ! "$new_domain" ]; then
|
|
return
|
|
fi
|
|
|
|
if [[ $new_domain == "http"* ]]; then
|
|
dialog --title $"Create a new Let's Encrypt certificate" \
|
|
--msgbox $"Don't include the https://" 6 40
|
|
return
|
|
fi
|
|
|
|
if [[ $new_domain != *"."* ]]; then
|
|
dialog --title $"Create a new Let's Encrypt certificate" \
|
|
--msgbox $"Invalid domain name: $new_domain" 6 40
|
|
return
|
|
fi
|
|
|
|
if [ ! -d "/var/www/${new_domain}" ]; then
|
|
domain_found=
|
|
if [ -f /etc/nginx/sites-available/radicale ]; then
|
|
if grep -q "${new_domain}" /etc/nginx/sites-available/radicale; then
|
|
domain_found=1
|
|
fi
|
|
fi
|
|
if [ -f "/etc/nginx/sites-available/${new_domain}" ]; then
|
|
domain_found=1
|
|
fi
|
|
if [[ "${new_domain}" == "jitsi"* || "${new_domain}" == "meet"* ]]; then
|
|
domain_found=1
|
|
fi
|
|
if [ ! $domain_found ]; then
|
|
dialog --title $"Create a new Let's Encrypt certificate" \
|
|
--msgbox $'Domain not found within /var/www' 6 40
|
|
return
|
|
fi
|
|
fi
|
|
|
|
${PROJECT_NAME}-addcert -e "$new_domain" -s "$LETSENCRYPT_SERVER" --dhkey "$DH_KEYLENGTH"
|
|
|
|
exit 0
|
|
}
|
|
|
|
function update_ciphersuite {
|
|
RECOMMENDED_SSL_CIPHERS="$SSL_CIPHERS"
|
|
if [ ${#RECOMMENDED_SSL_CIPHERS} -lt 5 ]; then
|
|
return
|
|
fi
|
|
|
|
RECOMMENDED_SSL_PROTOCOLS="$SSL_PROTOCOLS"
|
|
if [ ${#RECOMMENDED_SSL_PROTOCOLS} -lt 5 ]; then
|
|
return
|
|
fi
|
|
|
|
RECOMMENDED_SSH_CIPHERS="$SSH_CIPHERS"
|
|
if [ ${#RECOMMENDED_SSH_CIPHERS} -lt 5 ]; then
|
|
return
|
|
fi
|
|
|
|
RECOMMENDED_SSH_MACS="$SSH_MACS"
|
|
if [ ${#RECOMMENDED_SSH_MACS} -lt 5 ]; then
|
|
return
|
|
fi
|
|
|
|
RECOMMENDED_SSH_KEX="$SSH_KEX"
|
|
if [ ${#RECOMMENDED_SSH_KEX} -lt 5 ]; then
|
|
return
|
|
fi
|
|
|
|
cd $WEBSITES_DIRECTORY || exit 728425476
|
|
for file in $(dir -d "*") ; do
|
|
sed -i "s|ssl_protocols .*|ssl_protocols $RECOMMENDED_SSL_PROTOCOLS;|g" "$WEBSITES_DIRECTORY/$file"
|
|
if ! grep -q "Mobile compatible ciphers" "$WEBSITES_DIRECTORY/$file"; then
|
|
sed -i "s|ssl_ciphers .*|ssl_ciphers '$RECOMMENDED_SSL_CIPHERS';|g" "$WEBSITES_DIRECTORY/$file"
|
|
else
|
|
sed -i "s|ssl_ciphers .*|ssl_ciphers '$SSL_CIPHERS_MOBILE';|g" "$WEBSITES_DIRECTORY/$file"
|
|
fi
|
|
done
|
|
systemctl restart nginx
|
|
write_config_param "SSL_PROTOCOLS" "$RECOMMENDED_SSL_PROTOCOLS"
|
|
write_config_param "SSL_CIPHERS" "$RECOMMENDED_SSL_CIPHERS"
|
|
|
|
sed -i "s|Ciphers .*|Ciphers $RECOMMENDED_SSH_CIPHERS|g" $SSH_CONFIG
|
|
sed -i "s|MACs .*|MACs $RECOMMENDED_SSH_MACS|g" $SSH_CONFIG
|
|
sed -i "s|KexAlgorithms .*|KexAlgorithms $RECOMMENDED_SSH_KEX|g" $SSH_CONFIG
|
|
systemctl restart ssh
|
|
|
|
write_config_param "SSH_CIPHERS" "$RECOMMENDED_SSH_CIPHERS"
|
|
write_config_param "SSH_MACS" "$RECOMMENDED_SSH_MACS"
|
|
write_config_param "SSH_KEX" "$RECOMMENDED_SSH_KEX"
|
|
|
|
dialog --title $"Update ciphersuite" \
|
|
--msgbox $"The ciphersuite has been updated to recommended versions" 6 40
|
|
exit 0
|
|
}
|
|
|
|
function enable_monkeysphere {
|
|
monkey=
|
|
dialog --title $"GPG based authentication" \
|
|
--backtitle $"Freedombone Security Configuration" \
|
|
--defaultno \
|
|
--yesno $"\\nEnable GPG based authentication with monkeysphere ?" 7 60
|
|
sel=$?
|
|
case $sel in
|
|
0) monkey='yes';;
|
|
255) exit 0;;
|
|
esac
|
|
|
|
if [ $monkey ]; then
|
|
read_config_param "MY_USERNAME"
|
|
|
|
if [ ! -f /home/$MY_USERNAME/.monkeysphere/authorized_user_ids ]; then
|
|
dialog --title $"GPG based authentication" \
|
|
--msgbox $"$MY_USERNAME does not currently have any ids within ~/.monkeysphere/authorized_user_ids" 6 40
|
|
exit 0
|
|
fi
|
|
|
|
MY_GPG_PUBLIC_KEY_ID=$(gpg_pubkey_from_email "$MY_USERNAME" "$MY_USERNAME@$HOSTNAME")
|
|
if [ ${#MY_GPG_PUBLIC_KEY_ID} -lt 4 ]; then
|
|
echo $"monkeysphere unable to get GPG key ID for user $MY_USERNAME@$HOSTNAME"
|
|
exit 52825
|
|
fi
|
|
|
|
sed -i 's|#AuthorizedKeysFile|AuthorizedKeysFile|g' /etc/ssh/sshd_config
|
|
sed -i 's|AuthorizedKeysFile.*|AuthorizedKeysFile /var/lib/monkeysphere/authorized_keys/%u|g' /etc/ssh/sshd_config
|
|
monkeysphere-authentication update-users
|
|
|
|
# The admin user is the identity certifier
|
|
fpr=$(gpg --with-colons --fingerprint "$MY_GPG_PUBLIC_KEY_ID" | grep fpr | head -n 1 | awk -F ':' '{print $10}')
|
|
monkeysphere-authentication add-identity-certifier "$fpr"
|
|
monkeysphere-host publish-key
|
|
send_monkeysphere_server_keys_to_users
|
|
else
|
|
sed -i 's|#AuthorizedKeysFile|AuthorizedKeysFile|g' /etc/ssh/sshd_config
|
|
sed -i 's|AuthorizedKeysFile.*|AuthorizedKeysFile %h/.ssh/authorized_keys|g' /etc/ssh/sshd_config
|
|
fi
|
|
|
|
systemctl restart ssh
|
|
|
|
if [ $monkey ]; then
|
|
dialog --title $"GPG based authentication" \
|
|
--msgbox $"GPG based authentication was enabled" 6 40
|
|
else
|
|
dialog --title $"GPG based authentication" \
|
|
--msgbox $"GPG based authentication was disabled" 6 40
|
|
fi
|
|
exit 0
|
|
}
|
|
|
|
function register_website {
|
|
domain="$1"
|
|
|
|
if [[ ${domain} == *".local" ]]; then
|
|
echo $"Can't register local domains"
|
|
return
|
|
fi
|
|
|
|
if [ ! -f "/etc/ssl/private/${domain}.key" ]; then
|
|
echo $"No SSL/TLS private key found for ${domain}"
|
|
return
|
|
fi
|
|
|
|
if [ ! -f "/etc/nginx/sites-available/${domain}" ]; then
|
|
echo $"No virtual host found for ${domain}"
|
|
return
|
|
fi
|
|
|
|
monkeysphere-host import-key "/etc/ssl/private/${domain}.key" "https://${domain}"
|
|
monkeysphere-host publish-key
|
|
echo "0"
|
|
}
|
|
|
|
function register_website_interactive {
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Register a website with monkeysphere" \
|
|
--backtitle $"Freedombone Security Settings" \
|
|
--inputbox $"Enter the website domain name (without https://)" 8 60 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
domain=$(<"$data")
|
|
if ! register_website "$domain"; then
|
|
dialog --title $"Register a website with monkeysphere" \
|
|
--msgbox "$?" 6 40
|
|
else
|
|
dialog --title $"Register a website with monkeysphere" \
|
|
--msgbox $"$domain has been registered" 6 40
|
|
fi
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
}
|
|
|
|
function pin_all_tls_certs {
|
|
${PROJECT_NAME}-pin-cert all
|
|
}
|
|
|
|
function remove_pinning {
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Remove pinning for a domain" \
|
|
--backtitle $"Freedombone Security Settings" \
|
|
--inputbox $"Enter the website domain name (without https://)" 8 60 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
domain=$(<"$data")
|
|
if ! ${PROJECT_NAME}-pin-cert "$domain" remove; then
|
|
dialog --title $"Removed pinning from $domain" \
|
|
--msgbox "$?" 6 40
|
|
fi
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
}
|
|
|
|
function store_passwords {
|
|
dialog --title $"Store Passwords" \
|
|
--backtitle $"Freedombone Security Configuration" \
|
|
--yesno $"\\nDo you wish to store passwords on the system? Stored passwords are convenient but carry some additional security risk." 10 60
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
if [ -f /root/.nostore ]; then
|
|
read_config_param "MY_USERNAME"
|
|
if [ ! -f /home/$MY_USERNAME/.ssh/authorized_keys ]; then
|
|
dialog --title $"Store Passwords" \
|
|
--msgbox $"\\nYou should first enable key based ssh login to improve security" 8 60
|
|
return
|
|
fi
|
|
if [[ $SSH_PASSWORDS == 'yes' ]]; then
|
|
dialog --title $"Store Passwords" \
|
|
--msgbox $"\\nYou should disable ssh passwords to improve security" 8 60
|
|
return
|
|
fi
|
|
${PROJECT_NAME}-pass --enable yes
|
|
dialog --title $"Store Passwords" \
|
|
--msgbox $"\\nUser passwords will now be stored on the system" 8 60
|
|
fi
|
|
return
|
|
;;
|
|
1)
|
|
${PROJECT_NAME}-pass --clear yes
|
|
dialog --title $"Passwords were removed and will not be stored" \
|
|
--msgbox $"\\nFor the best security you should now manually change passwords via web interfaces so that there is no possibility of them being recovered from the disk" 9 60
|
|
return
|
|
;;
|
|
255) return;;
|
|
esac
|
|
}
|
|
|
|
function show_tor_bridges {
|
|
clear
|
|
bridges_list=$(grep "Bridge " /etc/tor/torrc | grep -v '##')
|
|
if [ ${#bridges_list} -eq 0 ]; then
|
|
echo $'No Tor bridges have been added'
|
|
return
|
|
fi
|
|
echo "${bridges_list}"
|
|
}
|
|
|
|
function add_tor_bridge {
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --backtitle $"Freedombone Control Panel" \
|
|
--title $"Add obfs4 Tor bridge" \
|
|
--form "\\n" 9 60 4 \
|
|
$"IP address: " 1 1 " . . . " 1 17 16 16 \
|
|
$"Port: " 2 1 "" 2 17 8 8 \
|
|
$"Key/Nickname: " 3 1 "" 3 17 250 250 \
|
|
2> "$data"
|
|
sel=$?
|
|
case $sel in
|
|
1) rm -f "$data"
|
|
return;;
|
|
255) rm -f "$data"
|
|
return;;
|
|
esac
|
|
bridge_ip_address=$(sed -n 1p < "$data")
|
|
bridge_port=$(sed -n 2p < "$data")
|
|
bridge_key=$(sed -n 3p < "$data")
|
|
rm -f "$data"
|
|
if [[ "${bridge_ip_address}" == *" "* ]]; then
|
|
return
|
|
fi
|
|
if [[ "${bridge_ip_address}" != *"."* ]]; then
|
|
return
|
|
fi
|
|
if [ ${#bridge_port} -eq 0 ]; then
|
|
return
|
|
fi
|
|
if [ ${#bridge_key} -eq 0 ]; then
|
|
return
|
|
fi
|
|
tor_add_bridge "${bridge_ip_address}" "${bridge_port}" "${bridge_key}"
|
|
dialog --title $"Add obfs4 Tor bridge" \
|
|
--msgbox $"Bridge added" 6 40
|
|
}
|
|
|
|
function remove_tor_bridge {
|
|
bridge_removed=
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --title $"Remove obfs4 Tor bridge" \
|
|
--backtitle $"Freedombone Control Panel" \
|
|
--inputbox $"Enter the IP address, key or Nickname of the bridge" 8 65 2>"$data"
|
|
sel=$?
|
|
case $sel in
|
|
0)
|
|
response=$(<"$data")
|
|
if [ ${#response} -gt 2 ]; then
|
|
if [[ "${response}" != *" "* ]]; then
|
|
if [[ "${response}" == *"."* ]]; then
|
|
if grep -q "Bridge ${response}" /etc/tor/torrc; then
|
|
tor_remove_bridge "${response}"
|
|
bridge_removed=1
|
|
fi
|
|
else
|
|
if grep -q " $response" /etc/tor/torrc; then
|
|
tor_remove_bridge "${response}"
|
|
bridge_removed=1
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
rm -f "$data"
|
|
if [ $bridge_removed ]; then
|
|
dialog --title $"Remove obfs4 Tor bridge" \
|
|
--msgbox $"Bridge removed" 6 40
|
|
fi
|
|
}
|
|
|
|
function add_tor_bridge_relay {
|
|
read_config_param 'TOR_BRIDGE_NICKNAME'
|
|
read_config_param 'TOR_BRIDGE_PORT'
|
|
|
|
# remove any previous bridge port from the firewall
|
|
if [ ${#TOR_BRIDGE_PORT} -gt 0 ]; then
|
|
firewall_remove "$TOR_BRIDGE_PORT" tcp
|
|
fi
|
|
|
|
data=$(mktemp 2>/dev/null)
|
|
dialog --backtitle $"Freedombone Control Panel" \
|
|
--title $"Become an obfs4 Tor bridge relay" \
|
|
--form "\\n" 8 60 2 \
|
|
$"Bridge Nickname: " 1 1 "$TOR_BRIDGE_NICKNAME" 1 20 250 250 \
|
|
2> "$data"
|
|
sel=$?
|
|
case $sel in
|
|
1) rm -f "$data"
|
|
return;;
|
|
255) rm -f "$data"
|
|
return;;
|
|
esac
|
|
bridge_nickname=$(sed -n 1p < "$data")
|
|
rm -f "$data"
|
|
if [[ "${bridge_nickname}" == *" "* ]]; then
|
|
return
|
|
fi
|
|
if [ ${#bridge_nickname} -eq 0 ]; then
|
|
return
|
|
fi
|
|
TOR_BRIDGE_NICKNAME="$bridge_nickname"
|
|
TOR_BRIDGE_PORT=$((20000 + RANDOM % 40000))
|
|
write_config_param 'TOR_BRIDGE_NICKNAME' "$TOR_BRIDGE_NICKNAME"
|
|
write_config_param 'TOR_BRIDGE_PORT' "$TOR_BRIDGE_PORT"
|
|
tor_create_bridge_relay
|
|
dialog --title $"You are now an obfs4 Tor bridge relay" \
|
|
--msgbox $"\\nIP address: $(get_ipv4_address)\\n\\nPort: ${TOR_BRIDGE_PORT}\\n\\nNickname: ${TOR_BRIDGE_NICKNAME}" 10 65
|
|
}
|
|
|
|
function remove_tor_bridge_relay {
|
|
tor_remove_bridge_relay
|
|
dialog --title $"Remove Tor bridge relay" \
|
|
--msgbox $"Bridge relay removed" 10 60
|
|
}
|
|
|
|
function menu_tor_bridges {
|
|
W=(1 $"Show bridges"
|
|
2 $"Add a bridge"
|
|
3 $"Remove a bridge"
|
|
4 $"Make this system into a bridge"
|
|
5 $"Stop being a bridge")
|
|
|
|
# shellcheck disable=SC2068
|
|
selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Security Settings" --menu $"Choose an operation, or ESC to go back:" 14 50 6 "${W[@]}" 3>&2 2>&1 1>&3)
|
|
|
|
if [ ! "$selection" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
case $selection in
|
|
1)
|
|
show_tor_bridges
|
|
exit 0
|
|
;;
|
|
2)
|
|
add_tor_bridge
|
|
exit 0
|
|
;;
|
|
3)
|
|
remove_tor_bridge
|
|
exit 0
|
|
;;
|
|
4)
|
|
add_tor_bridge_relay
|
|
exit 0
|
|
;;
|
|
5)
|
|
remove_tor_bridge_relay
|
|
exit 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function menu_security_settings {
|
|
W=(1 $"Passwords"
|
|
2 $"Run STIG tests"
|
|
3 $"Fix STIG test failures"
|
|
4 $"Show tripwire verification code"
|
|
5 $"Reset tripwire"
|
|
6 $"Enable or disable ping"
|
|
7 $"Show ssh host public key"
|
|
8 $"Tor bridges"
|
|
9 $"Password storage"
|
|
10 $"Export passwords"
|
|
11 $"Regenerate ssh host keys"
|
|
12 $"Regenerate Diffie-Hellman keys"
|
|
13 $"Update cipersuite"
|
|
14 $"Create a new Let's Encrypt certificate"
|
|
15 $"Renew Let's Encrypt certificate"
|
|
16 $"Delete a Let's Encrypt certificate"
|
|
17 $"Allow ssh login with passwords"
|
|
18 $"Show firewall")
|
|
|
|
# shellcheck disable=SC2068
|
|
selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Security Settings" --menu $"Choose an operation, or ESC to exit:" 25 76 25 "${W[@]}" 3>&2 2>&1 1>&3)
|
|
|
|
if [ ! "$selection" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
clear
|
|
|
|
read_config_param SSL_CIPHERS
|
|
read_config_param SSL_PROTOCOLS
|
|
read_config_param SSH_CIPHERS
|
|
read_config_param SSH_MACS
|
|
read_config_param SSH_KEX
|
|
|
|
get_imap_settings
|
|
get_ssh_settings
|
|
get_xmpp_settings
|
|
import_settings
|
|
export_settings
|
|
|
|
case $selection in
|
|
1)
|
|
view_or_change_passwords
|
|
exit 0;
|
|
;;
|
|
2)
|
|
clear
|
|
echo $'Running STIG tests...'
|
|
echo ''
|
|
${PROJECT_NAME}-tests --stig showall
|
|
exit 0
|
|
;;
|
|
3)
|
|
clear
|
|
echo $'Fixing any STIG failures...'
|
|
echo ''
|
|
${PROJECT_NAME}-tests --stig fix
|
|
echo $'Fixes applied. You will need to run the STIG tests again to be sure that they were all fixed.'
|
|
exit 0
|
|
;;
|
|
4)
|
|
show_tripwire_verification_code
|
|
any_key_verify
|
|
exit 0
|
|
;;
|
|
5)
|
|
reset_tripwire
|
|
exit 0
|
|
;;
|
|
|
|
6)
|
|
ping_enable_disable
|
|
exit 0
|
|
;;
|
|
7)
|
|
dialog --title $"SSH host public keys" \
|
|
--msgbox "\\n$(get_ssh_server_key)" 12 60
|
|
exit 0
|
|
;;
|
|
8)
|
|
menu_tor_bridges
|
|
exit 0
|
|
;;
|
|
9)
|
|
store_passwords
|
|
exit 0
|
|
;;
|
|
10)
|
|
export_passwords
|
|
exit 0
|
|
;;
|
|
11)
|
|
regenerate_ssh_host_keys
|
|
;;
|
|
12)
|
|
regenerate_dh_keys
|
|
;;
|
|
13)
|
|
interactive_setup
|
|
update_ciphersuite
|
|
;;
|
|
14)
|
|
create_letsencrypt
|
|
;;
|
|
15)
|
|
renew_letsencrypt
|
|
;;
|
|
16)
|
|
delete_letsencrypt
|
|
;;
|
|
17)
|
|
allow_ssh_passwords
|
|
change_ssh_settings
|
|
exit 0
|
|
;;
|
|
18)
|
|
show_firewall
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
change_website_settings
|
|
change_imap_settings
|
|
change_ssh_settings
|
|
change_xmpp_settings
|
|
}
|
|
|
|
function import_settings {
|
|
cd "$CURRENT_DIR" || exit 2468724684
|
|
|
|
if [ ! $IMPORT_FILE ]; then
|
|
return
|
|
fi
|
|
|
|
if [ ! -f $IMPORT_FILE ]; then
|
|
echo $"Import file $IMPORT_FILE not found"
|
|
exit 6393
|
|
fi
|
|
|
|
if grep -q "SSL_PROTOCOLS" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "SSL_PROTOCOLS" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
|
|
SSL_PROTOCOLS=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "SSL_CIPHERS" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "SSL_CIPHERS" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
|
|
SSL_CIPHERS=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "SSH_CIPHERS" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "SSH_CIPHERS" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
|
|
SSH_CIPHERS=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "SSH_MACS" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "SSH_MACS" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
|
|
SSH_MACS=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "SSH_KEX" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "SSH_KEX" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
|
|
SSH_KEX=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "SSH_HOST_KEY_ALGORITHMS" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "SSH_HOST_KEY_ALGORITHMS" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
|
|
SSH_HOST_KEY_ALGORITHMS=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "SSH_PASSWORDS" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "SSH_PASSWORDS" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [[ $TEMP_VALUE == "yes" || $TEMP_VALUE == "no" ]]; then
|
|
SSH_PASSWORDS=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "XMPP_CIPHERS" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "XMPP_CIPHERS" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt $MINIMUM_LENGTH ]; then
|
|
XMPP_CIPHERS=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
if grep -q "XMPP_ECC_CURVE" $IMPORT_FILE; then
|
|
TEMP_VALUE=$(grep "XMPP_ECC_CURVE" $IMPORT_FILE | awk -F '=' '{print $2}')
|
|
if [ ${#TEMP_VALUE} -gt 3 ]; then
|
|
XMPP_ECC_CURVE=$TEMP_VALUE
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function export_settings {
|
|
if [ ! $EXPORT_FILE ]; then
|
|
return
|
|
fi
|
|
|
|
cd "$CURRENT_DIR" || exit 92465274527
|
|
|
|
if [ ! -f $EXPORT_FILE ]; then
|
|
if [ "$SSL_PROTOCOLS" ]; then
|
|
echo "SSL_PROTOCOLS=$SSL_PROTOCOLS" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$SSL_CIPHERS" ]; then
|
|
echo "SSL_CIPHERS=$SSL_CIPHERS" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$SSH_CIPHERS" ]; then
|
|
echo "SSH_CIPHERS=$SSH_CIPHERS" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$SSH_MACS" ]; then
|
|
echo "SSH_MACS=$SSH_MACS" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$SSH_KEX" ]; then
|
|
echo "SSH_KEX=$SSH_KEX" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
|
|
echo "SSH_HOST_KEY_ALGORITHMS=$SSH_HOST_KEY_ALGORITHMS" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$SSH_PASSWORDS" ]; then
|
|
echo "SSH_PASSWORDS=$SSH_PASSWORDS" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$XMPP_CIPHERS" ]; then
|
|
echo "XMPP_CIPHERS=$XMPP_CIPHERS" >> $EXPORT_FILE
|
|
fi
|
|
if [ "$XMPP_ECC_CURVE" ]; then
|
|
echo "XMPP_ECC_CURVE=$XMPP_ECC_CURVE" >> $EXPORT_FILE
|
|
fi
|
|
echo "Security settings exported to $EXPORT_FILE"
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$SSL_PROTOCOLS" ]; then
|
|
if grep -q "SSL_PROTOCOLS" $EXPORT_FILE; then
|
|
sed -i "s|SSL_PROTOCOLS=.*|SSL_PROTOCOLS=$SSL_PROTOCOLS|g" $EXPORT_FILE
|
|
else
|
|
echo "SSL_PROTOCOLS=$SSL_PROTOCOLS" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$SSL_CIPHERS" ]; then
|
|
if grep -q "SSL_CIPHERS" $EXPORT_FILE; then
|
|
sed -i "s|SSL_CIPHERS=.*|SSL_CIPHERS=$SSL_CIPHERS|g" $EXPORT_FILE
|
|
else
|
|
echo "SSL_CIPHERS=$SSL_CIPHERS" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$SSH_CIPHERS" ]; then
|
|
if grep -q "SSH_CIPHERS" $EXPORT_FILE; then
|
|
sed -i "s|SSH_CIPHERS=.*|SSH_CIPHERS=$SSH_CIPHERS|g" $EXPORT_FILE
|
|
else
|
|
echo "SSH_CIPHERS=$SSH_CIPHERS" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$SSH_MACS" ]; then
|
|
if grep -q "SSH_MACS" $EXPORT_FILE; then
|
|
sed -i "s|SSH_MACS=.*|SSH_MACS=$SSH_MACS|g" $EXPORT_FILE
|
|
else
|
|
echo "SSH_MACS=$SSH_MACS" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$SSH_KEX" ]; then
|
|
if grep -q "SSH_KEX" $EXPORT_FILE; then
|
|
sed -i "s|SSH_KEX=.*|SSH_KEX=$SSH_KEX|g" $EXPORT_FILE
|
|
else
|
|
echo "SSH_KEX=$SSH_KEX" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$SSH_HOST_KEY_ALGORITHMS" ]; then
|
|
if grep -q "SSH_HOST_KEY_ALGORITHMS" $EXPORT_FILE; then
|
|
sed -i "s|SSH_HOST_KEY_ALGORITHMS=.*|SSH_HOST_KEY_ALGORITHMS=$SSH_HOST_KEY_ALGORITHMS|g" $EXPORT_FILE
|
|
else
|
|
echo "SSH_HOST_KEY_ALGORITHMS=$SSH_HOST_KEY_ALGORITHMS" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$SSH_PASSWORDS" ]; then
|
|
if grep -q "SSH_PASSWORDS" $EXPORT_FILE; then
|
|
sed -i "s|SSH_PASSWORDS=.*|SSH_PASSWORDS=$SSH_PASSWORDS|g" $EXPORT_FILE
|
|
else
|
|
echo "SSH_PASSWORDS=$SSH_PASSWORDS" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$XMPP_CIPHERS" ]; then
|
|
if grep -q "XMPP_CIPHERS" $EXPORT_FILE; then
|
|
sed -i "s|XMPP_CIPHERS=.*|XMPP_CIPHERS=$XMPP_CIPHERS|g" $EXPORT_FILE
|
|
else
|
|
echo "XMPP_CIPHERS=$XMPP_CIPHERS" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
if [ "$XMPP_ECC_CURVE" ]; then
|
|
if grep -q "XMPP_ECC_CURVE" $EXPORT_FILE; then
|
|
sed -i "s|XMPP_ECC_CURVE=.*|XMPP_ECC_CURVE=$XMPP_ECC_CURVE|g" $EXPORT_FILE
|
|
else
|
|
echo "XMPP_ECC_CURVE=$XMPP_ECC_CURVE" >> $EXPORT_FILE
|
|
fi
|
|
fi
|
|
echo $"Security settings exported to $EXPORT_FILE"
|
|
exit 0
|
|
}
|
|
|
|
function refresh_gpg_keys {
|
|
for d in /home/*/ ; do
|
|
USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
|
|
if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
|
|
su -c 'gpg --refresh-keys' - "$USERNAME"
|
|
fi
|
|
done
|
|
exit 0
|
|
}
|
|
|
|
function monkeysphere_sign_server_keys {
|
|
server_keys_file=/home/$USER/.monkeysphere/server_keys
|
|
if [ ! -f "$server_keys_file" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
keys_signed=
|
|
while read -r line; do
|
|
echo "$line"
|
|
if [ ${#line} -gt 2 ]; then
|
|
fpr=$(gpg --with-colons --fingerprint "$line" | grep fpr | head -n 1 | awk -F ':' '{print $10}')
|
|
if [ ${#fpr} -gt 2 ]; then
|
|
if gpg --sign-key "$fpr"; then
|
|
gpg --update-trustdb
|
|
keys_signed=1
|
|
fi
|
|
fi
|
|
fi
|
|
done <"$server_keys_file"
|
|
|
|
if [ $keys_signed ]; then
|
|
rm "$server_keys_file"
|
|
fi
|
|
exit 0
|
|
}
|
|
|
|
function htmly_hash {
|
|
# produces a hash corresponding to a htmly password
|
|
pass="$1"
|
|
HTMLYHASH_FILENAME=/usr/bin/htmlyhash
|
|
|
|
{ echo '<?php';
|
|
echo "parse_str(implode(\"&\", array_slice(\$argv, 1)), \$_GET);";
|
|
echo "\$password = \$_GET[\"password\"];";
|
|
echo "\$hash = password_hash(\$password, PASSWORD_BCRYPT);";
|
|
echo "if (password_verify(\$password, \$hash)) {";
|
|
echo " echo \$hash;";
|
|
echo '}';
|
|
echo '?>'; } > "$HTMLYHASH_FILENAME"
|
|
|
|
php $HTMLYHASH_FILENAME password="$pass"
|
|
}
|
|
|
|
function show_help {
|
|
echo ''
|
|
echo "${PROJECT_NAME}-sec"
|
|
echo ''
|
|
echo $'Alters the security settings'
|
|
echo ''
|
|
echo ''
|
|
echo $' -h --help Show help'
|
|
echo $' -e --export Export security settings to a file'
|
|
echo $' -i --import Import security settings from a file'
|
|
echo $' -r --refresh Refresh GPG keys for all users'
|
|
echo $' -s --sign Sign monkeysphere server keys'
|
|
echo $' --register [domain] Register a https domain with monkeysphere'
|
|
echo $' -b --htmlyhash [password] Returns the hash of a password for a htmly blog'
|
|
echo ''
|
|
exit 0
|
|
}
|
|
|
|
|
|
# Get the commandline options
|
|
while [ $# -gt 1 ]
|
|
do
|
|
key="$1"
|
|
|
|
case $key in
|
|
-h|--help)
|
|
show_help
|
|
;;
|
|
# Export settings
|
|
-e|--export)
|
|
shift
|
|
EXPORT_FILE="$1"
|
|
;;
|
|
# Export settings
|
|
-i|--import)
|
|
shift
|
|
IMPORT_FILE="$1"
|
|
;;
|
|
# Refresh GPG keys
|
|
-r|--refresh)
|
|
shift
|
|
refresh_gpg_keys
|
|
;;
|
|
# register a website
|
|
--register|--reg|--site)
|
|
shift
|
|
register_website "$1"
|
|
;;
|
|
# user signs monkeysphere server keys
|
|
-s|--sign)
|
|
shift
|
|
monkeysphere_sign_server_keys
|
|
;;
|
|
# get a hash of the given htmly password
|
|
-b|--htmlyhash)
|
|
shift
|
|
htmly_hash "$1"
|
|
exit 0
|
|
;;
|
|
*)
|
|
# unknown option
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
menu_security_settings
|
|
|
|
exit 0
|