Created
January 27, 2026 15:32
-
-
Save Danielk84/b203e9dfeef52d25be50fd2112483ba2 to your computer and use it in GitHub Desktop.
simple tor, torsocks, proxychains, nyx config generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This could be very useful for someone doing automation for their Tor proxy etc.....