Skip to content

Instantly share code, notes, and snippets.

@Danielk84
Created January 27, 2026 15:32
Show Gist options
  • Select an option

  • Save Danielk84/b203e9dfeef52d25be50fd2112483ba2 to your computer and use it in GitHub Desktop.

Select an option

Save Danielk84/b203e9dfeef52d25be50fd2112483ba2 to your computer and use it in GitHub Desktop.
simple tor, torsocks, proxychains, nyx config generator
#!/usr/bin/env bash
## Global Settings
_TORRC="/etc/tor/torrc"
_PROXYCHAINS="/etc/proxychains.conf"
_MAX_PASSWORD_SIZE="64"
## Use colors, but only if connected to a terminal, and that terminal supports them.
if type -P tput >"/dev/null" 2>&1; then
_ncolors=$(tput colors)
fi
if [[ -t 1 ]]; then
if [[ -n "${_ncolors}" ]] && [[ "${_ncolors}" -ge 8 ]]; then
_RED="$(tput setaf 1)"
_GREEN="$(tput setaf 2)"
_YELLOW="$(tput setaf 3)"
_BOLD="$(tput bold)"
_NORMAL="$(tput sgr0)"
elif [[ ${TERM:-} != "dumb" ]]; then
_RED=$'\033[31m'
_GREEN=$'\033[32m'
_YELLOW=$'\033[33m'
_BOLD=$'\033[1m'
_NORMAL=$'\033[0m'
fi
else
_RED=""
_GREEN=""
_YELLOW=""
_BOLD=""
_NORMAL=""
fi
function _colorize()
{
local arg text color
arg="${1}"
text="${2}"
case "${arg}" in
--red) color="${_RED}" ;;
--green) color="${_GREEN}" ;;
--yellow) color="${_YELLOW}" ;;
*) color="" ;;
esac
printf "%s" "${color}${_BOLD}${text}${_NORMAL}"
}
## Print logs and notify level.
_INFO="$(_colorize --green 'Info')"
_WARN="$(_colorize --yellow 'Warn')"
_ERROR="$(_colorize --red 'Error')"
function _log-msg()
{
local level location msg output
OPTIND=1
while getopts "l:a:m:" arg 2>/dev/null; do
case "${arg}" in
l) level="${OPTARG}" ;;
a) location="${OPTARG}" ;;
m) msg="${OPTARG}" ;;
?) echo "Invalid option: -${OPTARG}" >&2; return 1 ;;
esac
done
if [[ -z "${msg}" ]]; then
echo "${_ERROR} [ ${0} ]: at least -m <message> is required."
return 1
fi
# Formatting log.
[[ -n "${level}" ]] && output="${level}"
[[ -n "${location}" ]] && output="${output} [ ${location} ]"
[[ -n "${output}" ]] && output="${output}: "
[[ -n "${msg}" ]] && output="${output}${msg}"
echo "${output}"
}
## User permission and info.
function _check-file-and-path-permission()
{
local target path
target="${1}"
path="${target%/*}"
[[ ! -d "${path}" ]] && mkdir -p "${path}"
if [[ ! -w "${path}" ]]; then
_log-msg -l "${_ERROR}" -a "${0}" -m "Folder ${path@Q} does not accessible."
return 1
fi
if [[ ! -e "${target}" ]]; then
_log-msg -l "${_WARN}" -a "${0}" -m "Target ${target@Q} does not exist."
elif [[ ! -w "${target}" || ! -r "${target}" ]]; then
_log-msg -l "${_ERROR}" -a "${0}" -m "Target ${target@Q} does not accessible."
return 1
fi
}
# Finding username of who run this script with allow permission access.
function _issuser()
{
printf "%s" "${SUDO_USER:-${USER:-$(id -un)}}"
}
# Finding home dir from username
# This part is from "https://stackoverflow.com/questions/1521462/looping-through-the-content-of-a-file-in-bash"
function _home-folder-path()
{
local user user_home
user="$(_issuser)"
user_home=$(bash -c "cd ~$(printf %q "$user") && pwd")
if [[ -z "${user_home}" || ! -d "${user_home}" ]]; then
_log-msg -l "${_ERROR}" -a "${0}" -m "Failed to determine home directory for user=${user@Q}, user_home=${user_home@Q}"
return 1
fi
printf "%s" "${user_home}"
}
## File and executable binary managing.
function _check-binary-exist()
{
local target="${1}"
if command -v "${target}" >/dev/null 2>&1; then
_log-msg -l "${_INFO}" -m "The ${target@Q} already installed."
return
else
_log-msg -l "${_WARN}" -m "The ${target@Q} does not installed."
return 1
fi
}
function _check-binary-installed()
{
local target cmd pkgs pkg
target="${1}"
cmd="${2:-${target}}"
# If target installed return.
_check-binary-exist "${cmd}" && return
# If target does not installed,
# try to install it with supported package manager.
pkgs=( "apt update; apt install"
"dnf install --refresh"
"pacman -Sy"
"apk update; apk add")
IFS=""; for manager in "${pkgs[@]}"; do
if _check-binary-exist "${manager%% *}" >/dev/null; then
pkg="${manager}"
break
fi
done
if [[ -z "${pkg}" ]]; then
_log-msg -l "${_WARN}" -m "I could not install package ${target@Q}, You need install it manually!"
return 1
fi
bash -c "${pkg} ${target}" 2>&1
}
function _copy-file()
{
local src dst
src="${1}"
dst="${2}"
if [[ -z "${src}" || -z "${dst}" ]]; then
_log-msg -l "${_ERROR}" -a "${0}" -m "Source or destination missing."
return 1
fi
{
while IFS= read -r line; do
printf '%s\n' "$line"
done
} < "$src" > "$dst"
}
# You need to check permission before running it.
function _create-or-backup-file()
{
local target bak
target="${1}"
bak="${target}.bak"
if [[ -z "${target}" ]]; then
_log-msg -l "${_WARN}" -a "${0}" -m "No file path provided."
return 1
fi
if [[ -f "${target}" && ! -e "${bak}" ]]; then
_log-msg -l "${_INFO}" -m "Trying to copy ${target@Q} to ${bak@Q}."
_copy-file "${target}" "${bak}"
else
_log-msg -l "${_INFO}" -m "Trying to create ${target@Q}"
:>"${target}"
fi
}
# Example: _check_binary_and_config_file "tor" "/etc/tor"
function _check-binary-and-config-file()
{
local target config_file
target="${1}"
config_file="${2}"
_check-binary-installed "${target}" || return 1
_check-file-and-path-permission "${config_file}" || return 1
_create-or-backup-file "${config_file}"
}
# You need to check permission and existence of target before running it.
# Exmaple: { arr=("a", b"); _write_to_file_from_array "/path/to/file" "${arr[@]}"; }
function _write-to-file-from-array()
{
local target=${1}
shift
if [[ -z "${target}" || ! -w "${target}" ]]; then
_log-msg -l "${_ERROR}" -a "${0}" -m "File ${target@Q} does not found, you should specify valid file."
return 1
fi
{
IFS=""; for line in "$@"; do
printf "%s\n" "${line}"
done
} > "${target}"
}
# Return the first option value that find base on tor config syntax.
# Example: _get_option_value_from_file "/etc/tor/torrc" "ControlPort"
function _get-option-value-from-file()
{
local target target_option option value line
target="${1}"
target_option="${2}"
_check-file-and-path-permission "${target}" >&2 && return 1
while read -r -s line; do
option="${line%% *}"
if [[ "${target_option}" == "${option}" ]]; then
value="${line#"${option} "}"
break
fi
done <"${target}"
printf "%s" "${value}"
}
## Generating config file for tools.
### Validators.
function _addr-validator()
{
local addr ip port
addr="${1}"
IFS=":"; read -r ip port <<< "${addr}"
IFS=""
if [[ -n "${ip}" && ! "${ip}" =~ ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$ ]]; then
_log-msg -l "${_ERROR}" -m "Invalid ip=${ip@Q}"
return 1
fi
if [[ -n "${port}" && ! "${port}" =~ ^([1-9][0-9]{0,3}|[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
_log-msg -l "${_ERROR}" -m "Invalid port=${port@Q}"
return 1
fi
}
### Utils.
# "fn" name for sub function is reserved,
# because i did not found better way for seperating return value from question strings. sorry!
function _get-generic()
{
local issue max input
issue="${1}"
max="${2:-${_MAX_PASSWORD_SIZE}}"
function fn()
{
read -r -n "${max}" -p "${issue} ( keep empty for Ignore, max=${max} )? " input
}
fn >&2
printf "%s" "${input}"
}
function _get-password()
{
local issue mode max password
issue="${1}"
mode="${2:-"1"}"
max="${3:-"${_MAX_PASSWORD_SIZE}"}"
case "${mode}" in
"1") function fn() {
read -r -s -n "${max}" -p "Enter ${issue@Q} Password ( optional, keep empty for ignore, max=${max} ): " password
echo
} ;;
"2") function fn() {
local repeat
for (( i=1; i <= 3; i++ )); do
read -r -s -n "${max}" -p "Set password for ${issue@Q} ( optional, keep empty for ignore, max=${max} ): " password
echo
[[ -z "${password}" ]] && return
read -r -s -n "${max}" -p "Repeat password: " repeat
echo
[[ "${password}" == "${repeat}" ]] && return
password=""
echo "Passwords not equals (${i})."
done
} ;;
*) function fn() {
_log-msg -l "${_WARN}" -a "${0}" -m "Invalid mode ${mode@Q}"
} ;;
esac
fn >&2
printf "%s" "${password}"
}
### Tor client config.
function _get-password-hash()
{
local issue hash
issue="${1}"
hash=$(tor --hash-password "$(_get-password "${issue}" "2")")
printf "%s" "${hash##*$'\n'}"
}
function _get-addr()
{
local issue default_addr addr
issue="${1}"
default_addr="${2}"
function fn() {
read -r -p "${issue} default is ${default_addr@Q} ( keep empty for ignore or change 'host:port' )? " addr
if [[ -n "${addr}" ]]; then
_addr-validator "${addr}" || addr=""
fi
}
fn >&2
printf "%s" "${addr:-${default_addr}}"
}
function _get-https-authenticator()
{
local username password
function fn() {
read -r -p "Do you want set username for https basic-auth ( keep empty for Ignore )? " username
}
fn >&2
if [[ -n ${username} ]]; then
password=$(_get-password "HTTPS Authenticator")
printf "%s" "${username}:${password}"
fi
}
function generate-torrc-config()
{
local target service_type port
target="tor"
# The below processes should happen before config generation.
_check-binary-and-config-file "${target}" "${_TORRC}" >&2 || return 1
port=$(_get-generic "Socks port ( default='9050' )")
# basic configs.
declare -A configs_template=(
["HashedControlPassword"]=$(_get-password-hash "Control port")
["SocksPort"]="${port:-"9050"} IsolateDestPort IsolateDestAddr IsolateClientAddr"
)
read -r -p "Do you have other tor proxy ( keep empty for ignore, 'socks5', 'https' )? " service_type
case "${service_type}" in
"https")
configs_template["HTTPSProxy"]=$(_get-addr "HTTPS-proxy" "127.0.0.1:9445")
configs_template["HTTPSProxyAuthenticator"]=$(_get-https-authenticator)
;;
"socks5")
configs_template["Socks5Proxy"]=$(_get-addr "Socks5-Proxy" "127.0.0.1:9050")
configs_template["Socks5ProxyUsername"]=$(_get-generic "Socks5-proxy username" "255")
[[ -n "${configs_template["Socks5ProxyUsername"]}" ]] &&
configs_template["Socks5ProxyPassword"]=$(_get-password "Socks5-proxy" "1" "255")
;;
*) ;;
esac
declare -a configs=(
"User tor"
"DataDirectory /var/lib/tor"
# Log options
"Log notice stdout"
"LogMessageDomains 1"
"ControlPort 127.0.0.1:9051" # For using on nyx.
"CookieAuthentication 1"
"SafeLogging 1"
)
IFS=""; for key in "${!configs_template[@]}"; do
[[ -n "${configs_template[${key}]}" ]] && configs+=("${key} ${configs_template[${key}]}")
done
_write-to-file-from-array "${_TORRC}" "${configs[@]}"
# The below processes should happen after config generation.
tor --verify-config
}
## torsocks config.
function generate-torsocks-config()
{
local target config_file port
target="torsocks"
config_file="/etc/tor/torsocks.conf"
_check-binary-and-config-file "${target}" "${config_file}" || return 1
port=$(_get-option-value-from-file "${_TORRC}" "SocksPort")
[[ -z "${port}" ]] && return 1
declare -A configs_template=(
["TorPort"]="${port%% *}"
)
declare -a configs=(
"TorAddress 127.0.0.1"
)
IFS=""; for key in "${!configs_template[@]}"; do
[[ -n "${configs_template[${key}]}" ]] && configs+=("${key} ${configs_template[${key}]}")
done
_write-to-file-from-array "${config_file}" "${configs[@]}"
}
## proxychains-ng config.
function generate-proxychains-config()
{
local cmd target port username password
cmd="proxychains4"
if _check-binary-exist "apt" >"/dev/null"; then
target="${cmd}"
else
target="proxychains-ng"
fi
_check-binary-installed "${target}" "${cmd}" || return 1
_check-file-and-path-permission "${_PROXYCHAINS}" || return 1
_create-or-backup-file "${_PROXYCHAINS}" || return 1
port=$(_get-option-value-from-file "${_TORRC}" "SocksPort")
[[ -z "${port}" ]] && return 1
declare -A configs_template=(
["socks5"]="127.0.0.1 ${port%% *}"
)
declare -a configs=(
"strict_chain"
"proxy_dns"
"tcp_read_time_out 15000"
"tcp_connect_time_out 8000"
"[ProxyList]"
)
IFS=""; for key in "${!configs_template[@]}"; do
[[ -n "${configs_template[${key}]}" ]] && configs+=("${key} ${configs_template[${key}]}")
done
_write-to-file-from-array "${_PROXYCHAINS}" "${configs[@]}"
}
## nyx config.
function generate-nyx-config()
{
local target user_home config_file
target="nyx"
user_home=$(_home-folder-path) || return 1
config_file="${user_home}/.nyx/nyxrc"
_check-binary-and-config-file "${target}" "${config_file}" || return 1
declare -A configs_template=(
["data_directory"]="${target%/*}"
["password"]=$(_get-password "Control Port")
)
declare -a configs=(
"confirm_quit false"
"max_log_size 2000"
)
IFS=""; for key in "${!configs_template[@]}"; do
[[ -n "${configs_template[${key}]}" ]] && configs+=("${key} ${configs_template[${key}]}")
done
_write-to-file-from-array "${config_file}" "${configs[@]}"
}
function main()
{
generate-torrc-config
generate-torsocks-config
generate-proxychains-config
generate-nyx-config
}
main "$@"
@NavidVafaeiMajd
Copy link

This could be very useful for someone doing automation for their Tor proxy etc.....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment