Created
July 12, 2025 03:46
-
-
Save KewbitXMR/a5a781978f636aece211c63bc0bd958b to your computer and use it in GitHub Desktop.
Kewnix Installer
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
| # Author: Kewbit.org | |
| # Description: Hardened TOR-based orchestrator for Docker containers with isolated networking, circuit-level Tor routing, and optional container access control. | |
| set -e | |
| # === GLOBALS === | |
| TOR_GW_NAME="tor-gateway" | |
| DEFAULT_TOR_IP="192.168.100.2" | |
| BRIDGE_PREFIX="kewnet" # Changed for consistent prefix | |
| BASE_SUBNET="192.168" | |
| SUBNET_OFFSET=110 | |
| COMPOSE_FILE="docker-compose.yml" | |
| SERVICES=() | |
| SERVICE_NETWORKS=() | |
| SERVICE_IMAGES=() | |
| ACCESS_DIR=".access" | |
| STATE_DIR=".state" | |
| # === FUNCTIONS === | |
| function check_docker() { | |
| if ! command -v docker &>/dev/null; then | |
| echo "[!] Docker not found. Installing..." | |
| curl -fsSL https://get.docker.com | sh | |
| systemctl enable docker --now | |
| fi | |
| if ! command -v docker compose &>/dev/null; then | |
| echo "[!] docker compose not found. Installing..." | |
| apt install docker-compose -y | |
| fi | |
| } | |
| function reserve_network() { | |
| local index=$1 | |
| local service_name=${SERVICES[$index]} | |
| local net_name="${BRIDGE_PREFIX}-${service_name//[^a-zA-Z0-9]/_}" | |
| local subnet="${BASE_SUBNET}.$((SUBNET_OFFSET + index)).0/24" | |
| echo "[+] Creating isolated Docker network: $net_name ($subnet)" | |
| docker network create \ | |
| --subnet=$subnet \ | |
| --gateway=${subnet%0/24}1 \ | |
| --driver=bridge \ | |
| $net_name || true | |
| SERVICE_NETWORKS[$index]="$net_name" | |
| } | |
| function setup_custom_chains() { | |
| iptables -N TOR_ORCH 2>/dev/null || true | |
| iptables -t nat -N TOR_ORCH_NAT 2>/dev/null || true | |
| iptables -C FORWARD -j TOR_ORCH 2>/dev/null || iptables -A FORWARD -j TOR_ORCH | |
| iptables -t nat -C PREROUTING -j TOR_ORCH_NAT 2>/dev/null || iptables -t nat -A PREROUTING -j TOR_ORCH_NAT | |
| } | |
| function ask_service_details() { | |
| local service_name=$1 | |
| local index=$2 | |
| declare -A ACCESS_RULES | |
| echo "\n=== Configuring service: $service_name ===" | |
| read -p "[*] Use registry image for $service_name? (y/n) [default: y]: " use_registry | |
| use_registry=${use_registry:-y} | |
| if [[ "$use_registry" == "y" ]]; then | |
| read -p " Enter image name (e.g. alpine, nginx:latest): " image_name | |
| SERVICE_IMAGES[$index]="$image_name" | |
| else | |
| SERVICE_IMAGES[$index]="build: ./$(basename "$PWD")/${service_name}" | |
| mkdir -p "${service_name}" | |
| echo -e "FROM alpine\nCMD [\"sleep\", \"infinity\"]" > "${service_name}/Dockerfile" | |
| echo "[+] Created local Dockerfile scaffold at ./${service_name}/Dockerfile" | |
| fi | |
| for other_service in "${SERVICES[@]}"; do | |
| if [[ "$other_service" == "$service_name" ]]; then continue; fi | |
| read -p "[*] Should $service_name be able to talk to $other_service? (y/n) [default: n]: " ans | |
| ans=${ans:-n} | |
| ACCESS_RULES[$other_service]=$ans | |
| done | |
| mkdir -p "$ACCESS_DIR" "$STATE_DIR" | |
| touch "$STATE_DIR/$service_name.state" | |
| for target in "${!ACCESS_RULES[@]}"; do | |
| echo "${ACCESS_RULES[$target]}" > "$ACCESS_DIR/${service_name}_to_${target}" | |
| done | |
| reserve_network $index | |
| } | |
| function generate_compose() { | |
| echo "[+] Generating docker-compose.yml..." | |
| cat > $COMPOSE_FILE <<EOF | |
| version: '3.8' | |
| services: | |
| $TOR_GW_NAME: | |
| image: dperson/torproxy | |
| container_name: $TOR_GW_NAME | |
| restart: unless-stopped | |
| command: "-a -S" # Isolate by source IP | |
| networks: | |
| tor-gw: | |
| ipv4_address: $DEFAULT_TOR_IP | |
| read_only: true | |
| dns: | |
| - 127.0.0.1 | |
| EOF | |
| echo "networks:" >> $COMPOSE_FILE | |
| echo " tor-gw:" >> $COMPOSE_FILE | |
| echo " driver: bridge" >> $COMPOSE_FILE | |
| echo " ipam:" >> $COMPOSE_FILE | |
| echo " config:" >> $COMPOSE_FILE | |
| echo " - subnet: 192.168.100.0/24" >> $COMPOSE_FILE | |
| echo " gateway: 192.168.100.1" >> $COMPOSE_FILE | |
| for i in "${!SERVICES[@]}"; do | |
| local svc=${SERVICES[$i]} | |
| local net="${BRIDGE_PREFIX}-${svc//[^a-zA-Z0-9]/_}" | |
| local ip="${BASE_SUBNET}.$((SUBNET_OFFSET+i)).10" | |
| local image=${SERVICE_IMAGES[$i]} | |
| echo " $svc:" >> $COMPOSE_FILE | |
| if [[ "$image" == build:* ]]; then | |
| echo " build: ${image#build: }" >> $COMPOSE_FILE | |
| else | |
| echo " image: $image" >> $COMPOSE_FILE | |
| fi | |
| cat >> $COMPOSE_FILE <<EOF | |
| container_name: $svc | |
| read_only: true | |
| command: ["sleep", "infinity"] | |
| networks: | |
| $net: | |
| ipv4_address: $ip | |
| dns: | |
| - $DEFAULT_TOR_IP | |
| EOF | |
| echo " $net:" >> $COMPOSE_FILE | |
| echo " driver: bridge" >> $COMPOSE_FILE | |
| echo " ipam:" >> $COMPOSE_FILE | |
| echo " config:" >> $COMPOSE_FILE | |
| echo " - subnet: ${BASE_SUBNET}.$((SUBNET_OFFSET+i)).0/24" >> $COMPOSE_FILE | |
| echo " gateway: ${BASE_SUBNET}.$((SUBNET_OFFSET+i)).1" >> $COMPOSE_FILE | |
| done | |
| echo "[✓] Docker Compose file generated." | |
| } | |
| function setup_host_iptables() { | |
| echo "[+] Applying hardened iptables rules..." | |
| setup_custom_chains | |
| iptables -F TOR_ORCH | |
| iptables -t nat -F TOR_ORCH_NAT | |
| for i in "${!SERVICES[@]}"; do | |
| ip="${BASE_SUBNET}.$((SUBNET_OFFSET+i)).10" | |
| svc="${SERVICES[$i]}" | |
| iptables -t nat -A TOR_ORCH_NAT -s $ip -p udp --dport 53 -j DNAT --to-destination $DEFAULT_TOR_IP:5353 -m comment --comment "tor-orch:dns:$svc" | |
| iptables -t nat -A TOR_ORCH_NAT -s $ip -p tcp --syn -j DNAT --to-destination $DEFAULT_TOR_IP:9040 -m comment --comment "tor-orch:tcp:$svc" | |
| iptables -A TOR_ORCH -s $ip -d $DEFAULT_TOR_IP -j ACCEPT -m comment --comment "tor-orch:allow-out:$svc" | |
| iptables -A TOR_ORCH -d $ip -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT -m comment --comment "tor-orch:allow-in:$svc" | |
| iptables -A TOR_ORCH -s $ip -j DROP -m comment --comment "tor-orch:block-other:$svc" | |
| done | |
| iptables -A TOR_ORCH -s $DEFAULT_TOR_IP -p udp -j DROP -m comment --comment "tor-orch:block-udp:tor" | |
| iptables-save > /etc/iptables/rules.v4 || true | |
| echo "[✓] Host iptables hardened." | |
| } | |
| function remove_service() { | |
| local svc="$1" | |
| docker compose stop "$svc" || true | |
| docker compose rm -f "$svc" || true | |
| sed -i "/^ $svc:/,/^ [^ ]/d" $COMPOSE_FILE || true | |
| rm -f $ACCESS_DIR/${svc}_to_* $STATE_DIR/$svc.state | |
| echo "[✓] Removed $svc and cleaned up access rules." | |
| } | |
| function clean_all() { | |
| echo "[!] This will remove all containers, networks, iptables rules, and local files created by the orchestrator." | |
| read -p "Are you sure? (y/n): " confirm | |
| [[ "$confirm" != "y" ]] && exit 1 | |
| echo "[+] Stopping and removing containers..." | |
| docker ps -a --format '{{.Names}}' | grep "^$BRIDGE_PREFIX-" | xargs -r docker rm -f | |
| echo "[+] Removing networks..." | |
| docker network ls --format '{{.Name}}' | grep "^$BRIDGE_PREFIX-" | xargs -r docker network rm || true | |
| echo "[+] Removing iptables rules..." | |
| iptables -F TOR_ORCH || true | |
| iptables -t nat -F TOR_ORCH_NAT || true | |
| iptables -D FORWARD -j TOR_ORCH || true | |
| iptables -t nat -D PREROUTING -j TOR_ORCH_NAT || true | |
| iptables -X TOR_ORCH || true | |
| iptables -t nat -X TOR_ORCH_NAT || true | |
| iptables-save > /etc/iptables/rules.v4 || true | |
| echo "[+] Removing local files..." | |
| rm -rf "$COMPOSE_FILE" "$ACCESS_DIR" "$STATE_DIR" | |
| find . -maxdepth 1 -type d -name "$BRIDGE_PREFIX-*" -exec rm -rf {} + | |
| echo "[✓] All resources cleaned." | |
| } | |
| # === ENTRYPOINT === | |
| if [[ "$1" == "clean" ]]; then clean_all; exit 0; fi | |
| if [[ "$1" == "remove-service" ]]; then remove_service "$2"; exit 0; fi | |
| if [[ -f "$COMPOSE_FILE" ]]; then | |
| echo "Usage: $0 [add-service|remove-service <name>|list-services|clean]" | |
| exit 0 | |
| fi | |
| check_docker | |
| read -p "Enter comma-separated list of service names (e.g., app1,app2,db): " svc_list | |
| IFS=',' read -ra SERVICES <<< "$svc_list" | |
| for i in "${!SERVICES[@]}"; do ask_service_details "${SERVICES[$i]}" "$i"; echo; done | |
| generate_compose | |
| docker compose up -d | |
| setup_host_iptables | |
| echo "[✓] Setup complete. Containers are now transparently routed through Tor." |
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
| # Author: Kewbit.org | |
| # Modular companion script for adding, listing, tailing, and managing services | |
| set -e | |
| COMPOSE_FILE="docker-compose.yml" | |
| ACCESS_DIR=".access" | |
| STATE_DIR=".state" | |
| BASE_SUBNET="192.168" | |
| SUBNET_OFFSET=110 | |
| BRIDGE_PREFIX="kewnet" | |
| TOR_GW_IP="192.168.100.2" | |
| mkdir -p "$ACCESS_DIR" "$STATE_DIR" | |
| function list_services() { | |
| echo "[+] Listing configured services:" | |
| if [[ ! -d "$STATE_DIR" ]]; then | |
| echo "[!] No services found." | |
| return | |
| fi | |
| for state in "$STATE_DIR"/*.state; do | |
| [[ -e "$state" ]] || continue | |
| svc=$(basename "$state" .state) | |
| echo " - $svc" | |
| done | |
| } | |
| function get_next_index() { | |
| local max=0 | |
| for f in "$STATE_DIR"/*.state; do | |
| [[ -e "$f" ]] || continue | |
| idx=$(grep '^index=' "$f" | cut -d= -f2) | |
| (( idx > max )) && max=$idx | |
| done | |
| echo $((max + 1)) | |
| } | |
| function add_service() { | |
| echo "[+] Adding new service..." | |
| read -p "Enter name of new service: " svc_name | |
| if [[ -f "$STATE_DIR/$svc_name.state" ]]; then | |
| echo "[!] Service $svc_name already exists." | |
| return 1 | |
| fi | |
| index=$(get_next_index) | |
| net_name="$BRIDGE_PREFIX-$svc_name" | |
| subnet="$BASE_SUBNET.$((SUBNET_OFFSET + index)).0/24" | |
| ip="$BASE_SUBNET.$((SUBNET_OFFSET + index)).10" | |
| gateway="$BASE_SUBNET.$((SUBNET_OFFSET + index)).1" | |
| read -p "[*] Use registry image for $svc_name? (y/n) [y]: " use_registry | |
| use_registry=${use_registry:-y} | |
| if [[ "$use_registry" == "y" ]]; then | |
| read -p " Enter image name (e.g. alpine, nginx:latest): " image_name | |
| image_directive="image: $image_name" | |
| else | |
| mkdir -p "$svc_name" | |
| echo -e "FROM alpine\nCMD [\"sleep\", \"infinity\"]" > "$svc_name/Dockerfile" | |
| echo "[+] Created $svc_name/Dockerfile" | |
| image_directive="build: ./$svc_name" | |
| fi | |
| for other_state in "$STATE_DIR"/*.state; do | |
| [[ -e "$other_state" ]] || continue | |
| other_svc=$(basename "$other_state" .state) | |
| [[ "$other_svc" == "$svc_name" ]] && continue | |
| read -p "[*] Should $svc_name be able to talk to $other_svc? (y/n) [n]: " ans | |
| echo "${ans:-n}" > "$ACCESS_DIR/${svc_name}_to_${other_svc}" | |
| done | |
| cat > "$STATE_DIR/$svc_name.state" <<EOF | |
| index=$index | |
| subnet=$subnet | |
| ip=$ip | |
| gateway=$gateway | |
| net=$net_name | |
| EOF | |
| docker network create \ | |
| --subnet="$subnet" \ | |
| --gateway="$gateway" \ | |
| --driver=bridge "$net_name" || true | |
| echo "[+] Updating $COMPOSE_FILE..." | |
| if ! grep -q "$svc_name:" "$COMPOSE_FILE" 2>/dev/null; then | |
| cat >> "$COMPOSE_FILE" <<EOF | |
| $svc_name: | |
| $image_directive | |
| container_name: $svc_name | |
| command: ["sleep", "infinity"] | |
| read_only: true | |
| networks: | |
| $net_name: | |
| ipv4_address: $ip | |
| dns: | |
| - $TOR_GW_IP | |
| EOF | |
| echo " $net_name:" >> "$COMPOSE_FILE" | |
| echo " driver: bridge" >> "$COMPOSE_FILE" | |
| echo " ipam:" >> "$COMPOSE_FILE" | |
| echo " config:" >> "$COMPOSE_FILE" | |
| echo " - subnet: $subnet" >> "$COMPOSE_FILE" | |
| echo " gateway: $gateway" >> "$COMPOSE_FILE" | |
| fi | |
| docker compose up -d "$svc_name" | |
| echo "[✓] Service $svc_name added and started." | |
| } | |
| function tail_service_logs() { | |
| echo "[+] Tailing iptables logs for TOR_ORCH chains..." | |
| journalctl -f -k | grep --line-buffered -Ei 'TOR_ORCH' | |
| } | |
| function usage() { | |
| echo "Usage: $0 [list-services | add-service | tail-service-logs]" | |
| exit 1 | |
| } | |
| case "$1" in | |
| list-services) | |
| list_services | |
| ;; | |
| add-service) | |
| add_service | |
| ;; | |
| tail-service-logs) | |
| tail_service_logs | |
| ;; | |
| *) | |
| usage | |
| ;; | |
| esac |
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
| #!/bin/bash | |
| set -e | |
| SCRIPT_PREFIX="__REPLACE_WITH_RAW_GIST_URL__" | |
| FILES=( | |
| kewnix | |
| .tor_gateway_docker_orchestrator.sh | |
| .tor_gateway_service_manager.sh | |
| ) | |
| BIN_DIR="$HOME/.local/bin" | |
| mkdir -p "$BIN_DIR" | |
| echo "[+] Installing Kewnix scripts into $BIN_DIR..." | |
| for f in "${FILES[@]}"; do | |
| curl -fsSL "$SCRIPT_PREFIX/$f" -o "$BIN_DIR/$f" | |
| chmod +x "$BIN_DIR/$f" | |
| done | |
| echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc | |
| echo "[✓] Kewnix installed. Restart shell or run: source ~/.bashrc" |
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
| #!/bin/bash | |
| # kewnix - Unified Tor Gateway Orchestrator CLI | |
| # Author: Kewbit.org | |
| # Description: Central command router for all orchestrator operations | |
| set -e | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| ORCH_SCRIPT="$SCRIPT_DIR/tor_gateway_docker_orchestrator.sh" | |
| MANAGER_SCRIPT="$SCRIPT_DIR/tor_gateway_service_manager.sh" | |
| function usage() { | |
| cat <<EOF | |
| Usage: kewnix <command> | |
| Commands: | |
| init Set up TOR gateway and containers (first time only) | |
| add-service Add a new container routed through TOR | |
| remove-service NAME Remove a container by name | |
| list-services Show all configured services | |
| tail-logs Tail logs related to TOR_ORCH iptables | |
| clean Remove all kewnix-managed containers, networks, rules, and files | |
| help Show this help | |
| EOF | |
| exit 0 | |
| } | |
| case "$1" in | |
| init) | |
| shift | |
| "$ORCH_SCRIPT" "$@" | |
| ;; | |
| add-service) | |
| "$MANAGER_SCRIPT" add-service | |
| ;; | |
| remove-service) | |
| shift | |
| "$ORCH_SCRIPT" remove-service "$@" | |
| ;; | |
| list-services) | |
| "$MANAGER_SCRIPT" list-services | |
| ;; | |
| tail-logs) | |
| "$MANAGER_SCRIPT" tail-service-logs | |
| ;; | |
| clean) | |
| "$ORCH_SCRIPT" clean | |
| ;; | |
| help|--help|-h) | |
| usage | |
| ;; | |
| *) | |
| echo "[!] Unknown command: $1" | |
| usage | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment