Files
cloudron-box/scripts/cloudron-support
2023-12-14 17:04:05 +01:00

277 lines
9.2 KiB
Bash
Executable File

#!/bin/bash
set -eu -o pipefail
# scripts requires root
if [[ ${EUID} -ne 0 ]]; then
echo "This script should be run as root. Run with sudo"
exit 1
fi
readonly PASTEBIN="https://paste.cloudron.io"
readonly LINE="\n========================================================\n"
readonly HELP_MESSAGE="
Cloudron Support and Diagnostics Tool
Options:
--disable-dnssec Disable DNSSEC
--enable-remote-access Enable SSH Remote Access for the Cloudron support team
--send-diagnostics Collects server diagnostics and uploads it to ${PASTEBIN}
--troubleshoot Dashboard down? Run tests to identify the potential problem
--owner-login Login as owner
--use-external-dns Forwards all DNS requests to Google (8.8.8.8) and Cloudflare (1.1.1.1) DNS servers
--help Show this message
"
function enable_remote_access() {
local -r cloudron_support_public_key="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGWS+930b8QdzbchGljt3KSljH9wRhYvht8srrtQHdzg support@cloudron.io"
local -r ssh_user="cloudron-support"
local -r keys_file="/home/cloudron-support/.ssh/authorized_keys"
echo -n "Enabling Remote Access for the Cloudron support team..."
mkdir -p $(dirname "${keys_file}") # .ssh does not exist sometimes
touch "${keys_file}" # required for concat to work
if ! grep -q "${cloudron_support_public_key}" "${keys_file}"; then
echo -e "\n${cloudron_support_public_key}" >> "${keys_file}"
chmod 600 "${keys_file}"
chown "${ssh_user}" "${keys_file}"
fi
echo "Done"
}
function check_host_mysql() {
if ! systemctl is-active -q mysql; then
echo "MySQL is down. Maybe restarting fixes it"
systemctl restart mysql
if ! systemctl is-active -q mysql; then
echo "MySQL is still down, please investigate the error by inspecting /var/log/mysql/error.log"
exit 1
fi
fi
echo "MySQL is OK"
}
function owner_login() {
check_host_mysql
local -r owner_username=$(mysql -NB -uroot -ppassword -e "SELECT username FROM box.users WHERE role='owner' AND username IS NOT NULL AND active=1 ORDER BY creationTime LIMIT 1" 2>/dev/null)
local -r owner_password=$(pwgen -1s 12)
local -r dashboard_domain=$(mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='dashboard_domain'" 2>/dev/null)
mysql -NB -uroot -ppassword -e "INSERT INTO box.settings (name, value) VALUES ('ghosts_config', '{\"${owner_username}\":\"${owner_password}\"}') ON DUPLICATE KEY UPDATE name='ghosts_config', value='{\"${owner_username}\":\"${owner_password}\"}'" 2>/dev/null
echo "Login at https://my.${dashboard_domain} as ${owner_username} / ${owner_password} . This password may only be used once."
}
function send_diagnostics() {
local -r log="/tmp/cloudron-support.log"
echo -n "Generating Cloudron Support stats..."
rm -rf $log
echo -e $LINE"Linux"$LINE >> $log
uname -nar &>> $log
echo -e $LINE"Ubuntu"$LINE >> $log
lsb_release -a &>> $log
echo -e $LINE"Dashboard Domain"$LINE >> $log
mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='dashboard_domain'" &>> $log 2>/dev/null || true
echo -e $LINE"Docker containers"$LINE >> $log
if ! timeout --kill-after 10s 15s docker ps -a &>> $log 2>&1; then
echo -e "Docker is not responding" >> $log
fi
echo -e $LINE"Filesystem stats"$LINE >> $log
df -h &>> $log
echo -e $LINE"Appsdata stats"$LINE >> $log
du -hcsL /home/yellowtent/appsdata/* &>> $log || true
echo -e $LINE"Boxdata stats"$LINE >> $log
du -hcsL /home/yellowtent/boxdata/* &>> $log
echo -e $LINE"Backup stats (possibly misleading)"$LINE >> $log
du -hcsL /var/backups/* &>> $log || true
echo -e $LINE"System daemon status"$LINE >> $log
systemctl status --lines=100 box mysql unbound cloudron-syslog nginx collectd docker &>> $log
echo -e $LINE"Box logs"$LINE >> $log
tail -n 100 /home/yellowtent/platformdata/logs/box.log &>> $log
echo -e $LINE"Interface Info"$LINE >> $log
ip addr &>> $log
echo -e $LINE"Firewall chains"$LINE >> $log
iptables -L &>> $log
has_ipv6=$(cat /proc/net/if_inet6 >/dev/null 2>&1 && echo "yes" || echo "no")
echo -e "IPv6: ${has_ipv6}" >> $log
[[ "${has_ipv6}" == "yes" ]] && ip6tables -L &>> $log
echo "Done"
echo -n "Uploading information..."
paste_key=$(curl -X POST ${PASTEBIN}/documents --silent --data-binary "@$log" | python3 -c "import sys, json; print(json.load(sys.stdin)['key'])")
echo "Done"
echo -e "\nPlease email the following link to support@cloudron.io : ${PASTEBIN}/${paste_key}"
}
function check_unbound() {
if ! systemctl is-active -q unbound; then
echo "unbound is down. updating root anchor to see if it fixes it"
unbound-anchor -a /var/lib/unbound/root.key
systemctl restart unbound
if ! systemctl is-active -q unbound; then
echo "unbound is still down, please investigate the error using 'journalctl -u unbound'"
exit 1
fi
fi
test_resolve=$(dig cloudron.io @127.0.0.1 +short)
if [[ -z "test_resolve" ]]; then
echo "DNS is not resolving, maybe try forwarding all DNS requests using the --use-external-dns option"
exit 1
fi
echo "unbound is OK"
}
function check_nginx() {
if ! systemctl is-active -q nginx; then
echo "nginx is down. checking if this is because of invalid certs"
# TODO: delete config files that use invalid certs and restart nginx
fi
echo "nginx is OK"
}
function check_docker() {
if ! systemctl is-active -q docker; then
echo "Docker is down. Maybe restarting fixes it"
systemctl restart docker
if ! systemctl is-active -q docker; then
echo "Docker is still down, please investigate the error using 'journalctl -u docker'"
exit 1
fi
fi
echo "docker is OK"
}
function check_hairpin_nat() {
local -r dashboard_domain=$(mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='dashboard_domain'" 2>/dev/null)
if ! curl --fail -s https://my.${dashboard_domain} >/dev/null; then
echo "Could not query dashboard domain. Is Hairpin NAT functional?"
exit 1
fi
echo "Hairpin NAT is OK"
}
function check_expired_domain() {
local -r dashboard_domain=$(mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='dashboard_domain'" 2>/dev/null)
local -r expdate=$(whois ${dashboard_domain} | egrep -i 'Expiration Date:|Expires on|Expiry Date:' | head -1 | awk '{print $NF}')
local -r expdate_secs=$(date -d"$expdate" +%s)
local -r curdate_secs="$(date +%s)"
if (( curdate_secs > expdate_secs )); then
echo "Domain appears to be expired"
exit 1
fi
echo "Domain is valid and has not expired"
}
function use_external_dns() {
local -r $conf_file="/etc/unbound/unbound.conf.d/forward-everything.conf"
echo "Forwarding all DNS requests to Google/Cloudflare DNS. To remove the forwarding, please delete $conf and 'systemctl restart unbound'"
cat > $conf_file <<EOF
forward-zone:
name: "."
forward-addr: 1.1.1.1
forward-addr: 8.8.8.8
EOF
systemctl restart unbound
}
function disable_dnssec() {
local -r $conf_file="/etc/unbound/unbound.conf.d/disable-dnssec.conf"
echo "Disabling DNSSEC. To reenable it, please delete $conf and 'systemctl restart unbound'"
cat > $conf_file <<EOF
server:
val-permissive-mode: yes
EOF
systemctl restart unbound
}
function troubleshoot() {
# note: disk space test has already been run globally
check_nginx
check_docker
check_host_mysql
check_unbound
check_hairpin_nat # requires mysql to be checked
check_expired_domain
}
function check_disk_space() {
# check if at least 10mb root partition space is available
if [[ "`df --output="avail" / | sed -n 2p`" -lt "10240" ]]; then
echo "No more space left on /"
echo "This is likely the root case of the issue. Free up some space and also check other partitions below:"
echo ""
df -h
echo ""
echo "To recover from a full disk, follow the guide at https://docs.cloudron.io/troubleshooting/#recovery-after-disk-full"
exit 1
fi
# check for at least 5mb free /tmp space for the log file
if [[ "`df --output="avail" /tmp | sed -n 2p`" -lt "5120" ]]; then
echo "Not enough space left on /tmp"
echo "Free up some space first by deleting files from /tmp"
exit 1
fi
}
check_disk_space
args=$(getopt -o "" -l "admin-login,disable-dnssec,enable-ssh,enable-remote-access,help,owner-login,send-diagnostics,use-external-dns,troubleshoot" -n "$0" -- "$@")
eval set -- "${args}"
while true; do
case "$1" in
--enable-ssh)
# fall through
;&
--enable-remote-access) enable_remote_access; exit 0;;
--admin-login)
# fall through
;&
--owner-login) owner_login; exit 0;;
--send-diagnostics) send_diagnostics; exit 0;;
--troubleshoot) troubleshoot; exit 0;;
--no-dnssec) disable_dnssec; exit 0;;
--use-external-dns) use_external_dns; exit 0;;
--help) break;;
--) break;;
*) echo "Unknown option $1"; exit 1;;
esac
done
echo -e "${HELP_MESSAGE}"