mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-01-09 01:11:04 +01:00
237 lines
8.0 KiB
Plaintext
Executable File
237 lines
8.0 KiB
Plaintext
Executable File
#!/command/with-contenv bashio
|
|
# shellcheck shell=bash
|
|
# ==============================================================================
|
|
# Home Assistant Community Add-on: Tor
|
|
# Prepares the add-on for startup
|
|
# ==============================================================================
|
|
declare address
|
|
declare clientname
|
|
declare host
|
|
declare key
|
|
declare log_level
|
|
declare port
|
|
declare private_key
|
|
declare public_key
|
|
declare target_port
|
|
declare virtual_port
|
|
|
|
readonly torrc='/etc/tor/torrc'
|
|
readonly hidden_service_dir='/ssl/tor/hidden_service'
|
|
readonly authorized_clients_dir="${hidden_service_dir}/authorized_clients"
|
|
readonly clients_dir="${hidden_service_dir}/clients"
|
|
readonly hostname_file="${hidden_service_dir}/hostname"
|
|
|
|
# A hidden service without any ports is kinda useless
|
|
if bashio::config.true 'hidden_services' \
|
|
&& ! bashio::config.has_value 'ports'; then
|
|
bashio::log.fatal
|
|
bashio::log.fatal 'Add-on configuration is incomplete.'
|
|
bashio::log.fatal
|
|
bashio::log.fatal 'Hidden services where enabled, using the'
|
|
bashio::log.fatal '"hidden_services" add-on configuration option,'
|
|
bashio::log.fatal 'But the "port" option does not contain any values!'
|
|
bashio::log.fatal
|
|
bashio::log.fatal 'Please configure the "ports" option.'
|
|
bashio::exit.nok
|
|
fi
|
|
|
|
# Checks if client names where configured when using stealth mode
|
|
if bashio::config.true 'hidden_services' \
|
|
&& bashio::config.true 'stealth' \
|
|
&& ! bashio::config.has_value 'client_names';
|
|
then
|
|
bashio::log.fatal
|
|
bashio::log.fatal 'Add-on configuration is incomplete.'
|
|
bashio::log.fatal
|
|
bashio::log.fatal 'Stealth mode is enabled, using the "stealth" add-on'
|
|
bashio::log.fatal 'configuration option, but there are no client names'
|
|
bashio::log.fatal 'configured in the "client_names" add-on option.'
|
|
bashio::log.fatal
|
|
bashio::log.fatal 'Please configure the "client_names" option.'
|
|
bashio::exit.nok
|
|
fi
|
|
|
|
# Created needed directories
|
|
mkdir -p \
|
|
"${authorized_clients_dir}" \
|
|
"${clients_dir}" \
|
|
"${hidden_service_dir}" \
|
|
|| bashio::exit.nok 'Could not create tor data directories'
|
|
chmod -R 0700 /ssl/tor
|
|
|
|
# Find the matching Tor log level
|
|
if bashio::config.has_value 'log_level'; then
|
|
case "$(bashio::string.lower "$(bashio::config 'log_level')")" in
|
|
all|trace)
|
|
log_level="debug"
|
|
;;
|
|
debug)
|
|
log_level="info"
|
|
;;
|
|
info|notice)
|
|
log_level="notice"
|
|
;;
|
|
warning)
|
|
log_level="warn"
|
|
;;
|
|
error|fatal|off)
|
|
log_level="err"
|
|
;;
|
|
esac
|
|
|
|
echo "Log ${log_level} stdout" >> "${torrc}"
|
|
fi
|
|
|
|
# Configure Socks proxy
|
|
if bashio::config.true 'socks'; then
|
|
echo 'SOCKSPort 0.0.0.0:9050' >> "${torrc}"
|
|
else
|
|
echo 'SOCKSPort 127.0.0.1:9050' >> "${torrc}"
|
|
fi
|
|
|
|
# Configure hidden services
|
|
if bashio::config.true 'hidden_services'; then
|
|
echo "HiddenServiceDir ${hidden_service_dir}" >> "${torrc}"
|
|
|
|
for port in $(bashio::config 'ports'); do
|
|
count=$(echo "${port}" | sed 's/[^:]//g'| awk '{ print length }')
|
|
if [[ "${count}" == 0 ]]; then
|
|
host='homeassistant'
|
|
virtual_port="${port}"
|
|
target_port="${port}"
|
|
elif [[ "${count}" == 1 ]]; then
|
|
# Check if format is hostname/ip:port or port:port
|
|
first=$(echo "${port}" | cut -f1 -d:)
|
|
if [[ "${first}" =~ ^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]) ]]; then
|
|
host='homeassistant'
|
|
virtual_port=$(echo "${port}" | cut -f1 -d:)
|
|
target_port=$(echo "${port}" | cut -f2 -d:)
|
|
else
|
|
host=$(echo "${port}" | cut -f1 -d:)
|
|
virtual_port=$(echo "${port}" | cut -f2 -d:)
|
|
target_port=$(echo "${port}" | cut -f2 -d:)
|
|
fi
|
|
elif [[ "${count}" == 2 ]]; then
|
|
host=$(echo "${port}" | cut -f1 -d:)
|
|
virtual_port=$(echo "${port}" | cut -f2 -d:)
|
|
target_port=$(echo "${port}" | cut -f3 -d:)
|
|
else
|
|
bashio::log.warning "$port Are not correct format, skipping..."
|
|
fi
|
|
if [[ "${count}" -le 2 ]]; then
|
|
echo "HiddenServicePort ${target_port} ${host}:${virtual_port}" \
|
|
>> "${torrc}"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Configure bridges
|
|
if bashio::config.exists 'bridges' \
|
|
&& ! bashio::config.is_empty 'bridges';
|
|
then
|
|
bashio::log.info 'Use bridges:'
|
|
echo "UseBridges 1" >> "${torrc}"
|
|
|
|
# Add client for OBFS transport
|
|
echo "ClientTransportPlugin obfs2,obfs3,obfs4,scramblesuit exec /usr/local/bin/obfs4proxy managed" >> "${torrc}"
|
|
|
|
# Add client for Snowflake transport
|
|
echo "ClientTransportPlugin snowflake exec /usr/local/bin/snowflake" >> "${torrc}"
|
|
|
|
# Add client for WebTunnel transport
|
|
echo "ClientTransportPlugin webtunnel exec /usr/local/bin/webtunnel" >> "${torrc}"
|
|
|
|
# Add bridges
|
|
while read -r bridge; do
|
|
bashio::log.info "Bridge ${bridge}"
|
|
echo "Bridge ${bridge}" >> "${torrc}"
|
|
done <<< "$(bashio::config 'bridges')"
|
|
fi
|
|
|
|
# Figure out the address
|
|
if bashio::config.true 'hidden_services'; then
|
|
bashio::log.info 'Starting Tor temporarly...'
|
|
|
|
exec 3< <(tor)
|
|
|
|
until bashio::fs.file_exists "${hostname_file}"; do
|
|
bashio::log.info "Waiting for service to start..."
|
|
sleep 1
|
|
done
|
|
|
|
address=$(<"${hostname_file}")
|
|
grep -m 1 "Bootstrapped 100% (done): Done" <&3 >/dev/null 2>&1
|
|
|
|
kill "$(pgrep tor)" >/dev/null 2>&1
|
|
|
|
bashio::log.info '---------------------------------------------------------'
|
|
bashio::log.info 'Your Home Assistant instance is available on Tor!'
|
|
bashio::log.info "Address: ${address}"
|
|
bashio::log.info '---------------------------------------------------------'
|
|
fi
|
|
|
|
# Configure stealth mode
|
|
if bashio::config.true 'hidden_services' && bashio::config.true 'stealth';
|
|
then
|
|
# Following the documentation at:
|
|
# https://community.torproject.org/onion-services/advanced/client-auth/
|
|
while read -r clientname; do
|
|
# Generate key is they do not exist yet
|
|
if ! bashio::fs.file_exists "${authorized_clients_dir}/${clientname}.auth"
|
|
then
|
|
key=$(openssl genpkey -algorithm x25519)
|
|
|
|
private_key=$(
|
|
sed \
|
|
-e '/----.*PRIVATE KEY----\|^[[:space:]]*$/d' \
|
|
<<< "${key}" \
|
|
| base64 -d \
|
|
| tail -c 32 \
|
|
| base32 \
|
|
| sed 's/=//g'
|
|
)
|
|
|
|
public_key=$(
|
|
openssl pkey -pubout \
|
|
<<< "${key}" \
|
|
| sed -e '/----.*PUBLIC KEY----\|^[[:space:]]*$/d' \
|
|
| base64 -d \
|
|
| tail -c 32 \
|
|
| base32 \
|
|
| sed 's/=//g'
|
|
)
|
|
|
|
# Create authorized client file
|
|
echo "descriptor:x25519:${public_key}" \
|
|
> "${clients_dir}/${clientname}.auth"
|
|
echo "descriptor:x25519:${public_key}" \
|
|
> "${authorized_clients_dir}/${clientname}.auth"
|
|
|
|
# Create private key file
|
|
echo "${private_key}" \
|
|
> "${clients_dir}/${clientname}.key.txt"
|
|
echo "${address%.onion}:descriptor:x25519:${private_key}" \
|
|
> "${clients_dir}/${clientname}.auth_private"
|
|
|
|
bashio::log.red
|
|
bashio::log.red
|
|
bashio::log.red "Created keys for ${clientname}!"
|
|
bashio::log.red
|
|
bashio::log.red "Keys are stored in:"
|
|
bashio::log.red "${clients_dir}"
|
|
bashio::log.red
|
|
bashio::log.red "Public key":
|
|
bashio::log.red "${public_key}"
|
|
bashio::log.red
|
|
bashio::log.red "Private key:"
|
|
bashio::log.red "${private_key}"
|
|
bashio::log.red
|
|
bashio::log.red
|
|
else
|
|
bashio::log.info "Keys for ${clientname} already exists; skipping..."
|
|
fi
|
|
done <<< "$(bashio::config 'client_names')"
|
|
|
|
echo 'HiddenServiceAllowUnknownPorts 0' >> "${torrc}"
|
|
fi
|