diff --git a/ChangeLog b/ChangeLog index dbf58e9..a693a6c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ -0.5.0 - 2023-10-00 - * Access to /dev/kvm (for token users). +0.5.0 - 2023-11-00 + * Configurable access to /dev/kvm + * Reverse Port via curl sf/port + * Token via curl sf/set -dtoken= + * per LG traffic shaping 0.4.9p3 - 2023-09-20 * Helix (hx) diff --git a/config/etc/nginx/nginx-rpc.conf b/config/etc/nginx/nginx-rpc.conf index 7a70229..0e46595 100644 --- a/config/etc/nginx/nginx-rpc.conf +++ b/config/etc/nginx/nginx-rpc.conf @@ -73,7 +73,25 @@ http { rewrite /net /net/; rewrite /wg /wg/; rewrite /dmesg /dmesg/; + rewrite /port /port/; + rewrite /set /set/; + location ~* ^/set/.* { + fastcgi_param REMOTE_ADDR $remote_addr; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param REQUEST_BODY $request_body; + fastcgi_param FCGI_CMD set; + fastcgi_param SCRIPT_FILENAME /cgi-bin/rpc; + fastcgi_pass unix:/dev/shm/sf/master/fcgiwrap.socket; + } + location ~* ^/port/.* { + fastcgi_param REMOTE_ADDR $remote_addr; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param REQUEST_BODY $request_body; + fastcgi_param FCGI_CMD port; + fastcgi_param SCRIPT_FILENAME /cgi-bin/rpc; + fastcgi_pass unix:/dev/shm/sf/master/fcgiwrap.socket; + } location ~* ^/net/.* { fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REQUEST_URI $request_uri; diff --git a/config/etc/sf/sf.conf b/config/etc/sf/sf.conf index a0d8935..e52a0dd 100644 --- a/config/etc/sf/sf.conf +++ b/config/etc/sf/sf.conf @@ -13,20 +13,25 @@ #SF_USER_MEMORY_LIMIT=256m #SF_USER_MEMORY_AND_SWAP_LIMIT= # Not set=no swap. Example =4g #SF_USER_PIDS_LIMIT=128 -#SF_USER_CPU_SHARE=8 # 2..1024. docker's default is 1024. 2048 gives 2x and 512 half. +#SF_USER_CPU_SHARE=8 # 2..1024. docker's default is 1024. 2048 gives 2x and 512 half. #SF_USER_OOM_SCORE=500 -#SF_USER_NICE_SCORE=10 #-20 (most often scheduled) to 19 (least often scheduled) -#SF_ULIMIT_NOFILE="1024:8192" +#SF_USER_NICE_SCORE=10 #-20 (most often scheduled) to 19 (least often scheduled) +#SF_ULIMIT_NOFILE="8192" # Number of open files 16384:65536" _per_ container #SF_USER_BLKIO_WEIGHT=100 # Reduced to 10 during DoS #SF_MAX_STRAIN=100 #SF_SHM_SIZE= # Hard limit is USER_MEMORY_LIMIT #SF_CPUS= # automatic between 1..4 depending on host's cpu count +#SF_TOKEN_PROHIBITED= # Prohibit the use of TOKENS #SF_USER_SYN_BURST=8196 # Can send 8k tcp sync packets #SF_USER_SYN_LIMIT=1 # Thereafter refill with 1 syn/second, 0=unlimited +#SF_USER_UL_RATE= # Limit LG egress speed (10Mbit, 20Mbit, ...) #SF_SYN_BURST=10000 # Global limit. (0-10000) #SF_SYN_LIMIT=200 # Global Limit. 0=unlimited +#SF_RPORT=1 # Enable reverse ports for users. +#SF_RPORT_ON_LOGIN= # Auto-assign a reverse port on log in. Implies SF_RPORT=1. + ## Per user limit of root filesystem / #SF_USER_ROOT_FS_SIZE= # e.g. 16MB, 2GB, 0=unlimited. Not set=read-only #SF_USER_ROOT_FS_INODE=65536 # Inode Limit. Only enforced if FS_SIZE > 0 @@ -38,6 +43,7 @@ #SF_USER_DEV_KVM= # =1 to allow access to /dev/kvm (Warning: User can DoS PHY) #SF_ALLOW_SRC_TOR= # =1 to allow connections from TOR +#SF_USER_IMMUNE= # =1 to not ban user by lgban # Limit to 8 concurrently running servers per IP #SF_LIMIT_SERVER_BY_IP=8 diff --git a/contrib/db-sync.sh b/contrib/db-sync.sh index 91afb2c..4b9318e 100755 --- a/contrib/db-sync.sh +++ b/contrib/db-sync.sh @@ -15,10 +15,11 @@ while [[ $i -gt 0 ]]; do rsync -ral "${h}":/sf/config/db/banned "${h}":/sf/config/db/token "${h}":/sf/config/db/limits . done -echo "====================================================" +echo "==[DOWN done. Press Enter to start UP]==================================================" +read i=0 for h in "${HOSTS[@]}"; do echo "#$i Syncing ${h} UP" rsync -ral banned token limits "${h}":'/sf/config/db' ((i++)) -done \ No newline at end of file +done diff --git a/docker-compose.yml b/docker-compose.yml index 055e8b4..064a957 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,6 +44,7 @@ services: - "${SF_BASEDIR:-.}/config/etc/sf:/config/etc/sf:ro" - "${SF_BASEDIR:-.}/data:/encfs/raw" - "${SF_SHMDIR:-/dev/shm/sf}/encfs-sec:/encfs/sec:shared" + - "${SF_SHMDIR:-/dev/shm/sf}/run:/sf/run:ro" - "${SF_SHMDIR:-/dev/shm/sf}/run/encfsd/user:/sf/run/encfsd/user" - "${SF_BASEDIR:-.}/sfbin:/sf/bin:ro" - "${SF_OVERLAYDIR:-/var/lib/docker/overlay2}:/var/lib/docker/overlay2:ro" @@ -253,7 +254,7 @@ services: - "${SF_SHMDIR:-/dev/shm/sf}/config-for-guest:/config/guest" # vpn_status to guest - "${SF_SHMDIR:-/dev/shm/sf}/run/redis/sock:/redis-sock" - "${SF_BASEDIR:-.}/sfbin:/sf/bin:ro" - # entrypoint: sleep infinity # FIXME-2022 + # entrypoint: sleep infinity # FIXME-TESTING mullvad: @@ -421,6 +422,7 @@ services: - SF_NORDVPN_IP=${SF_NORDVPN_IP:?} - SF_CRYPTOSTORM_IP=${SF_CRYPTOSTORM_IP:?} - SF_MULLVAD_IP=${SF_MULLVAD_IP:?} + - SF_GUEST_MTU=${SF_GUEST_MTU:-1420} volumes: - "${SF_SHMDIR:-/dev/shm/sf}/run/vpn:/sf/run/vpn" - "${SF_BASEDIR:-.}/config/etc/sf:/config/host/etc/sf:ro" @@ -458,6 +460,7 @@ services: - SF_MULLVAD_ROUTE=${SF_MULLVAD_ROUTE:?} - SF_DNS=${SF_NET_VPN_DNS_IP} - SF_NET_LG_ROUTER_IP=${SF_NET_LG_ROUTER_IP:?} + - SF_HOST_MTU=${SF_HOST_MTU:-1500} volumes: - "${SF_SHMDIR:-/dev/shm/sf}:/dev/shm/sf" - "${SF_BASEDIR:-.}/config/db:/config/db" @@ -638,7 +641,7 @@ services: - SF_DIRECT - SF_DEBUG - SF_BACKING_FS - # - SF_DEBUG_SSHD=1 # FIXME-2022 sshd debug + # - SF_DEBUG_SSHD=1 # FIXME-TESTING sshd debug volumes: - "${SF_BASEDIR:-.}/config:/config/host" - "${SF_BASEDIR:-.}/data/share:/sf/share:ro" @@ -650,8 +653,8 @@ services: - "/var/run/docker.sock:/var/run/docker.sock" - "/var/lib/lxcfs:/var/lib/lxcfs:ro" - "${SF_SHMDIR:-/dev/shm/sf}/run/redis/sock:/redis-sock" - # - /research/segfault/host/fs-root/bin/segfaultsh:/bin/segfaultsh:ro # FIXME-2022 - # - /research/segfault/host:/host:ro # FIXME-2022 sshd debug + # - /research/segfault/host/fs-root/bin/segfaultsh:/bin/segfaultsh:ro # FIXME-TESTING + # - /research/segfault/host:/host:ro # FIXME-TESTING sshd debug nginx: image: nginx @@ -671,9 +674,17 @@ services: - "${SF_BASEDIR:-.}/config/etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro" networks: + # Force docker0 + default: + driver: bridge + driver_opts: + com.docker.network.driver.mtu: ${SF_HOST_MTU:-1500} + vpn-net: name: sf-vpn driver: bridge + driver_opts: + com.docker.network.driver.mtu: ${SF_HOST_MTU:-1500} ipam: config: - subnet: ${SF_NET_VPN:?} @@ -684,6 +695,8 @@ networks: # expects all host traffic to arrive at SF_NET_DIRECT_ROUTE_IP. name: A-sf-direct driver: bridge + driver_opts: + com.docker.network.driver.mtu: ${SF_HOST_MTU:-1500} ipam: config: - subnet: ${SF_NET_DIRECT:?} @@ -692,6 +705,8 @@ networks: # sf-host and gsnc name: sf-access driver: bridge + driver_opts: + com.docker.network.driver.mtu: ${SF_HOST_MTU:-1500} ipam: config: - subnet: ${SF_NET_ACCESS:?} @@ -699,6 +714,8 @@ networks: dmz-net: name: sf-dmz driver: bridge + driver_opts: + com.docker.network.driver.mtu: ${SF_HOST_MTU:-1500} ipam: config: - subnet: ${SF_NET_DMZ:?} @@ -706,6 +723,8 @@ networks: dns-doh-net: name: sf-dns-doh driver: bridge + driver_opts: + com.docker.network.driver.mtu: ${SF_HOST_MTU:-1500} ipam: config: - subnet: ${SF_NET_DOH:?} @@ -715,7 +734,7 @@ networks: name: sf-guest driver: bridge driver_opts: - com.docker.network.driver.mtu: 1420 + com.docker.network.driver.mtu: ${SF_GUEST_MTU:-1420} # Can not use 'internal'. This will only remvoe the host's bridge # but this also means we can not route via 10.11.0.* even if we can # ping the router. diff --git a/encfsd/destructor.sh b/encfsd/destructor.sh index a8fa65c..1bb6a10 100755 --- a/encfsd/destructor.sh +++ b/encfsd/destructor.sh @@ -31,6 +31,7 @@ stop_lg() rm -f "/sf/run/pids/lg-${lid}.pid" rm -f "/sf/run/ips/lg-${lid}.ip" rm -rf "/config/self-for-guest/lg-${lid}" + rm -rf "/sf/run/users/lg-${lid}" # Tear down container [[ -n $is_container ]] && docker stop "lg-$lid" &>/dev/nuill diff --git a/encfsd/encfsd.sh b/encfsd/encfsd.sh index 57db834..ab6b6ac 100755 --- a/encfsd/encfsd.sh +++ b/encfsd/encfsd.sh @@ -124,7 +124,6 @@ encfs_mount_server() load_limits() { local lid - local token lid="$1" unset SF_USER_FS_SIZE @@ -132,16 +131,7 @@ load_limits() unset SF_USER_ROOT_FS_SIZE unset SF_USER_ROOT_FS_INODE - # First source global - [[ -f "/config/etc/sf/sf.conf" ]] && eval "$(grep ^SF_ "/config/etc/sf/sf.conf")" - - # Then Token - [[ -f "/config/db/user/lg-${lid}/token" ]] && { - token=$(<"/config/db/user/lg-${lid}/token") - source "/config/db/token/token-${token,,}.conf" 2>/dev/null - } - # Then source user specific limits - [[ -f "/config/db/user/lg-${lid}/limits.conf" ]] && eval "$(grep ^SF_ "/config/db/user/lg-${lid}/limits.conf")" + source "/sf/run/users/lg-${lid}/limits.txt" } dir2prjid() diff --git a/guest/Dockerfile b/guest/Dockerfile index ea78b24..55e81b4 100644 --- a/guest/Dockerfile +++ b/guest/Dockerfile @@ -49,6 +49,7 @@ RUN /pkg-install.sh BASE apt-get install -y --no-install-recommends \ jq \ less \ openssh-sftp-server \ + pipx \ python3-pip \ screen \ sharutils \ @@ -227,7 +228,6 @@ RUN /pkg-install.sh LARGE apt-get install -y --no-install-recommends \ p7zip-full \ peass \ pip \ - pipx \ proxychains \ python2-minimal \ python-is-python3 \ diff --git a/guest/fs-root/etc/shellrc b/guest/fs-root/etc/shellrc index a6797a8..72a97cb 100644 --- a/guest/fs-root/etc/shellrc +++ b/guest/fs-root/etc/shellrc @@ -253,6 +253,9 @@ lsg() { ls -Alh --color=always | grep -i -E "$*" } +noansi() { sed -e 's/\x1b\[[0-9;]*m//g'; } +alias nocol=noansi + [[ -f /usr/bin/fdfind ]] && alias fd=fdfind [[ -z $LANG ]] && export LANG=en_US.UTF-8 diff --git a/guest/fs-root/etc/zsh_command_not_found b/guest/fs-root/etc/zsh_command_not_found index 5ce033b..7f1635b 100644 --- a/guest/fs-root/etc/zsh_command_not_found +++ b/guest/fs-root/etc/zsh_command_not_found @@ -18,13 +18,22 @@ function cnf_preexec() { cmd="$1" # Remove any variable like in `FOO=blah duf` + # Test: X="FOO BAR" Y="hello world" Z=mememe whoami while :; do cmd="${cmd#"${cmd%%[^[:space:]]*}"}" # remove leading whitespace characters [[ $cmd != *" "* ]] && break - # Check if first string before " " is a variable (contains '=') + # Check if first string before \s is a variable (contains '=') [[ ${cmd%% *} != *"="* ]] && break - # HERE: It's a variable. Remove it for 'cmd' - cmd=${cmd#* } + + # HERE: It's a variable. X=foo, X="foo" or X="foo bar". Remove it for 'cmd' + [[ ${cmd%% *} != *"=\""* ]] && { + # HERE: variable without quotes + cmd=${cmd#* } + continue; + } + # HERE: X="foo" or X="foo bar" + cmd=${cmd#*=\"} + cmd=${cmd#*\" } done typeset -g cnf_command="${cmd%% *}" @@ -32,7 +41,7 @@ function cnf_preexec() { # HERE: command not found [ -n "$cnf_once" ] && return typeset -g cnf_once="1" - echo -en "\e[0;31m" + echo -en "💥 \e[0;31m" } function cnf_precmd() { @@ -42,7 +51,7 @@ function cnf_precmd() { echo -en "\e[0m" (($cnf_ret)) && [ -n "$cnf_command" ] && { whence -- "${cnf_command}" >& /dev/null || - echo -e "\e[0;34m[\e[0;33mSF\e[0;34m]\e[0m ¯\_(⊙︿⊙)_/¯ Like us to install \e[0;36m${cnf_command}\e[0m?\n\e[0;34m[\e[0;33mSF\e[0;34m] \e[1;37mTell us at https://t.me/thcorg\e[0m" + echo -e "\e[0;34m[\e[0;33mSF\e[0;34m]\e[0m ¯\_(⊙︿⊙)_/¯ Like us to install \e[0;36m${cnf_command}\e[0m?\n\e[0;34m[\e[0;33mSF\e[0;34m] \e[1;37mTell us at https://t.me/thcorg\e[0m 🌈😘" unset cnf_command } } diff --git a/guest/fs-root/sf/bin/sf-motd.sh b/guest/fs-root/sf/bin/sf-motd.sh index 33ace45..c1fc89c 100755 --- a/guest/fs-root/sf/bin/sf-motd.sh +++ b/guest/fs-root/sf/bin/sf-motd.sh @@ -81,7 +81,8 @@ loc="${loc:0:15}" IPPORT="${CDY}$(/dev/null >>'/sf/run/logs/segfault.log'" + bash -c "{ echo -en '[$(date '+%F %T' -u)]${p:- }'; echo -e '[${CDM}${LID}${CN}] $str';} 2>/dev/null >>'${SF_RUN_DIR}/logs/segfault.log'" } LOG(){ _log "" "$@"; } @@ -197,6 +197,12 @@ exec_errnull() fi } +logpipe() { + [[ ! -e "${SF_RUN_DIR}/logpipe/logPipe.sock" ]] && return + + echo "$*" | exec_devnull unix-socket-client +} + # Overcoming a restricted shell. Write $1 to file in $2 # tofile "foobar \$HOME \"|';id;" world.txt tofile() @@ -319,7 +325,7 @@ init_vars() init_defaults init_emu - [[ -f "/sf/run/logs/segfault.log" ]] && IS_LOGGING=1 + [[ -f "${SF_RUN_DIR}/logs/segfault.log" ]] && IS_LOGGING=1 NOW="$(date +%s)" [[ -z $YOUR_IP ]] && { @@ -341,11 +347,13 @@ init_vars() SF_USER_DB_DIR="${db_dir}/user/lg-${LID}" SF_BLACKLIST_DIR="${db_dir}/banned" SF_TOKEN_DIR="${db_dir}/token" + SF_LIMITS_DIR="${db_dir}/limits" HNLID_DIR="${db_dir}/hn" - SF_RUN_DIR="/sf/run/" + SF_RUN_DIR="/sf/run" LG_PID_DIR="${SF_RUN_DIR}/pids" LG_PID_FILE="${LG_PID_DIR}/pid-${LID}.$$" + LG_RUN_DIR="${SF_RUN_DIR}/users/lg-${LID}" TS_LOGOUT_FILE="${SF_USER_DB_DIR}/ts_logout" TS_LOGIN_FILE="${SF_USER_DB_DIR}/ts_login" TS_RUN_FILE="${SF_USER_DB_DIR}/ts_run" @@ -365,6 +373,7 @@ init_vars() fi fi + xmkdir "${LG_RUN_DIR}" # Check if we are still in sshd's Network Namespace IS_SSHD_NS_NET=1 [[ ${SF_NS_NET:?} != "$(readlink /proc/self/ns/net)" ]] && unset IS_SSHD_NS_NET # Already inside LG's Network Namespace @@ -374,21 +383,6 @@ init_vars() trap cb_sighup SIGPIPE } -mk_portforward() -{ - local ipport - - ipport=$(echo -e "DEL portd:response-${LID}\"\n\ -RPUSH portd:blcmd \"getport ${LID}\"\n\ -BLPOP portd:response-${LID} 5" | redr) || return - # DEBUGF "ipport='$ipport'" - ipport="${ipport##*$'\n'}" - [[ ! "${ipport##*:}" -gt 0 ]] && { DEBUGF "Failed to get Reverse Port Forward (ipport='$ipport')"; return; } - - # The PortD add's a /sf/run/self/reverse_forward. - DEBUGF "Reverse Port Forward: $ipport" -} - # Called when a new server is created. print_disclaimer() { @@ -514,7 +508,7 @@ sshd_to_ns() # Load PID of container's init process (uid=1000) [[ -z $LG_PID ]] && { - LG_PID=$(<"/sf/run/pids/lg-${LID}.pid") + LG_PID=$(<"${LG_PID_DIR}/lg-${LID}.pid") [[ -z $LG_PID ]] && ERREXIT 222 "Init PID not found." } ln -sf "/proc/${LG_PID}/ns/net" "/dev/shm/ns-net-${PPID}" @@ -532,12 +526,16 @@ spawn_shell_exit() sem_release + # Add a log entry into elastisearch using logpipe + logpipe "Type:Login|LID:${LID}|Hostname:${SF_HOSTNAME}||C_ISO:${YOUR_COUNTRY_ISO}|CONTINENT=${YOUR_CONTINENT_CODE}|" + # Update current IP: tofile "${YOUR_IP_DISPLAY:?}" "/config/self-for-guest/lg-${LID}/ip" - tofile "${YOUR_IP:?}" "/sf/run/ips/lg-${LID}.ip" + tofile "${YOUR_IP:?}" "${SF_RUN_DIR}/ips/lg-${LID}.ip" [[ -n $YOUR_GEOIP ]] && tofile "${YOUR_GEOIP}" "/config/self-for-guest/lg-${LID}/geoip" # Request a reverse Port Forward - [[ ! -f "/config/self-for-guest/lg-${LID}/reverse_ip" ]] && mk_portforward "${LID}" + [[ -n $SF_RPORT_ON_LOGIN ]] && [[ -n $SF_RPORT ]] && [[ ! -f "/config/self-for-guest/lg-${LID}/reverse_ip" ]] && exec_devnull docker exec --user 0:0 "lg-${LID}" curl -s sf/port + # Warn user if this is the last server by IP (after semaphore has been released) [[ -n $IS_SHOW_LAST_SERVER ]] && show_last_server "$IS_SHOW_LAST_SERVER" @@ -606,13 +604,22 @@ BLPOP \"encfs-$$-${LID}-X\" 10" | red) || return 255 return 0 } +load_limits_fn() { + local fn=$1 + [[ ! -f "$fn" ]] && return + + eval "$(<"${fn}")" +} + load_limits() { # Set the default values. # No default for ROOT_FS limit. Should be set in sf.conf or if not set # then root is mounted read-only - #SF_USER_ROOT_FS_SIZE=2GB - SF_USER_ROOT_FS_INODE=65536 + # SF_USER_ROOT_FS_SIZE=8g + # SF_USER_ROOT_FS_INODE=65536 + # SF_USER_FS_SIZE=16g + # SF_USER_FS_INODE=65536 SF_USER_MEMORY_LIMIT=256m SF_USER_PIDS_LIMIT=128 SF_USER_CPU_SHARE=8 @@ -620,11 +627,12 @@ load_limits() SF_USER_NICE_SCORE=10 SF_LIMIT_SERVER_BY_IP=8 SF_USER_BLKIO_WEIGHT=100 - SF_ULIMIT_NOFILE="1024:8192" + SF_ULIMIT_NOFILE="8192" SF_USER_SYN_BURST=8196 SF_USER_SYN_LIMIT=1 SF_USER_DL_BURST=8gb SF_USER_UL_BURST=8gb + SF_RPORT=1 # No new shells until load goes below STRAIN*NPROC. # Should be larger than ContainerGuard's strain when CG starts killing @@ -634,7 +642,13 @@ load_limits() # dd bs=1M count=10024 if=/dev/zero of=/dump.dat oflag=direct status=progress # Source system wide limits - [[ -f "${SF_ETCSF_DIR}/sf.conf" ]] && eval "$(<"${SF_ETCSF_DIR}/sf.conf")" + load_limits_fn "${SF_ETCSF_DIR}/sf.conf" + + # Source continent specific limits + load_limits_fn "${SF_LIMITS_DIR}/limits-continent-${YOUR_CONTINENT_CODE}.conf" + + # Source country specific limits + load_limits_fn "${SF_LIMITS_DIR}/limits-country-${YOUR_COUNTRY_ISO}.conf" # Then source token specific limits (and write TOKEN information) if [[ -z $SF_TOKEN ]]; then @@ -666,10 +680,17 @@ load_limits() } # Then source user specific limits - [[ -f "${SF_USER_DB_DIR}/limits.conf" ]] && eval "$(<"${SF_USER_DB_DIR}/limits.conf")" + load_limits_fn "${SF_USER_DB_DIR}/limits.conf" # Then source IP specific limits - [[ -f "${SF_ETCSF_DIR}/sf-${YOUR_IP}.conf" ]] && eval "$(<"${SF_ETCSF_DIR}/sf-${YOUR_IP}.conf")" + load_limits_fn "${SF_ETCSF_DIR}/sf-${YOUR_IP}.conf" + + # Add SF docker args to LG container. + # DISABLED: otherwise, an attacker with write access to token/limits (e.g. through a web user-management interface) could own the PHY. + # [[ ${#SF_USER_DOCKER_ARGS[@]} -gt 0 ]] && DOCKER_ARGS+=("${SF_USER_DOCKER_ARGS[@]}") + + # User gets a reverse port on login + [[ -n $SF_RPORT_ON_LOGIN ]] && SF_RPORT=1 # Set swap limit if not set in sf.conf [[ -z $SF_USER_MEMORY_AND_SWAP_LIMIT ]] && SF_USER_MEMORY_AND_SWAP_LIMIT="$SF_USER_MEMORY_LIMIT" @@ -692,7 +713,7 @@ load_limits() DOCKER_ARGS+=("--oom-score-adj=${SF_USER_OOM_SCORE}") DOCKER_ARGS+=("--blkio-weight=${SF_USER_BLKIO_WEIGHT}") - [[ -n $SF_USER_DEV_KVM ]] && DOCKER_ARGS+=("--device=/dev/kvm") + [[ -n $SF_USER_DEV_KVM ]] && [[ -e /dev/kvm ]] && DOCKER_ARGS+=("--device=/dev/kvm") if [[ -z $SF_USER_ROOT_FS_SIZE ]]; then DOCKER_ARGS+=("--read-only") @@ -708,6 +729,8 @@ load_limits() } fi + write_lg_limits + # NOTE: This is no longer used because /dev/shm is now mounted as tmpfs to make UML work # [[ -n $SF_SHM_SIZE ]] && DOCKER_ARGS+=("--shm-size=$SF_SHM_SIZE") @@ -716,11 +739,15 @@ load_limits() setup_fs_limit || ERREXIT 202 "Can't configure XFS limit" } -# Publish user limits to self/limits +# Publish user limits to self/limits, human readable. write_guest_limits() { local is_token local is_ro + local tx; + + tx="${SF_USER_UL_RATE}" + [[ -z $SF_USER_UL_RATE ]] && tx="${SF_MAXOUT}" is_token="no" [[ -n $SF_TOKEN ]] && is_token="yes" @@ -737,7 +764,7 @@ SHM_SIZE=${SF_SHM_SIZE} PIDS=${SF_USER_PIDS_LIMIT} MEMORY=${SF_USER_MEMORY_LIMIT} NOFILE=${SF_ULIMIT_NOFILE} -TX=${SF_MAXOUT} +TX=${tx:-unlimited} RX=${SF_MAXIN:-unlimited} SYN_BURST=${SF_USER_SYN_BURST} SYN_RATE=${SF_USER_SYN_LIMIT}/sec @@ -745,6 +772,18 @@ SERVERS=${SF_LIMIT_SERVER_BY_IP} GREETINGS='${SF_SYSCOP_MSG}'" "/config/self-for-guest/lg-${LID}/limits" } +# Write limits to file that can be loaded by other processes (like rpc and encfsd) +write_lg_limits() { + tofile "\ +SF_USER_ROOT_FS_SIZE=\"$SF_USER_ROOT_FS_SIZE\" +SF_USER_ROOT_FS_INODE=\"$SF_USER_ROOT_FS_INODE\" +SF_USER_FS_SIZE=\"$SF_USER_FS_SIZE\" +SF_USER_FS_INODE=\"$SF_USER_FS_INODE\" +SF_USER_UL_RATE=\"$SF_USER_UL_RATE\" +SF_RPORT=\"$SF_RPORT\" +SF_USER_IMMUNE=\"$SF_USER_IMMUNE\"" "${LG_RUN_DIR}/limits.txt" +} + check_banned() { local blfn @@ -975,6 +1014,9 @@ check_limit_server_by_ip() mk_geoip() { local ip + local country + local country_iso + local continent_code ip="${1}" [[ ! -f /sf/share/GeoLite2-City.mmdb ]] && return [[ -z ${ip} ]] && return @@ -984,6 +1026,16 @@ mk_geoip() res=$(mmdbinspect --db /sf/share/GeoLite2-City.mmdb "${ip}") [[ -z $SF_HIDEIP ]] && city=$(echo "$res" | jq -r '.[0].Records[0].Record.city.names.en | select(. != null)') country=$(echo "$res" | jq -r '.[0].Records[0].Record.country.names.en | select(. != null)') + country_iso=$(echo "$res" | jq -r '.[0].Records[0].Record.country.iso_code | select(. != null)') + continent_code=$(echo "$res" | jq -r '.[0].Records[0].Record.country.iso_code | select(. != null)') + + country_iso="${country_iso,,}" + country_iso="${country_iso//[^a-z]}" + YOUR_COUNTRY_ISO="${country_iso:0:2}" + + continent_code="${continent_code,,}" + continent_code="${continent_code//[^a-z]}" + YOUR_CONTINENT_CODE="${continent_code:0:6}" unset YOUR_GEOIP if [[ -n $city ]] && [[ -n $country ]]; then @@ -1091,9 +1143,6 @@ export LID [[ -z $SF_SEED ]] && ERREXIT 244 "SF_SEED= is not set." -# Show system messages -sysmsg "/config/host/etc/loginmsg-all.sh" - # Call init_vars() after LID is set init_vars # Load CPU/PID/OOM limits (systemwide or user specific) @@ -1102,6 +1151,10 @@ load_limits check_banned mk_hostname + +# Show system messages +sysmsg "/config/host/etc/loginmsg-all.sh" + HNLID_FILE="${HNLID_DIR}/hn2lid-${SF_HOSTNAME}" LG_SEM="sema:lg-$(( (SF_NUM + SF_RAND_OFS) % SF_HM_SIZE_LG ))" @@ -1153,6 +1206,8 @@ else tofile "$SF_HOSTNAME" "${SF_USER_DB_DIR}/hostname" [[ -d "${HNLID_DIR}" ]] || exec_devnull mkdir "${HNLID_DIR}" tofile "$LID" "${HNLID_FILE}" || ERREXIT 231 "tofile: Failed to create hnlid_file" + # Add a log entry into elastisearch using logpipe + logpipe "Type:Create|LID:${LID}|Hostname:${SF_HOSTNAME}|C_ISO:${YOUR_COUNTRY_ISO}|CONTINENT=${YOUR_CONTINENT_CODE}|" fi DEBUGF "LID=${LID} SF_HOSTNAME=${SF_HOSTNAME}" @@ -1295,7 +1350,7 @@ CID=${arr[0]} LG_PID=${arr[1]} C_IP=${arr[2]} [[ -z $C_IP ]] && ERREXIT 249 "Could not get container's IP address." -tofile "${LG_PID:?}" "/sf/run/pids/lg-${LID}.pid" +tofile "${LG_PID:?}" "${LG_PID_DIR}/lg-${LID}.pid" # Set up Root FS / inode limits and move encfsd to lg's cgroup setup_encfsd || STOPEXIT "${LID}" 244 "Could not set FS quota." @@ -1308,7 +1363,7 @@ res=$(red SET "ip:${C_IP}" "${LID} ${CID} ${LG_PID}") || STOPEXIT "$LID" 252 "Fa exec_devnull docker exec sf-router /user-limit.sh "${YOUR_IP_HASH}" "${YOUR_IP}" "${C_IP}" "$SF_USER_SYN_LIMIT" "$SF_USER_SYN_BURST" "$SF_USER_DL_RATE" "$SF_USER_DL_BURST" "$SF_USER_UL_RATE" "$SF_USER_UL_BURST" || STOPEXIT "${LID}" 251 "Faild to set syn-limit..."; # Ready container -exec_devnull docker exec sf-master /ready-lg.sh "${LID}" "${C_IP}" "${LG_PID}" || STOPEXIT "${LID}" 246 "Failed-#3 to ready guest container..." +exec_devnull docker exec sf-master /ready-lg.sh "${LID}" "${C_IP}" "${LG_PID}" "${SF_USER_DL_RATE}" "${SF_USER_UL_RATE}" || STOPEXIT "${LID}" 246 "Failed-#3 to ready guest container..." # Setup container (within container's namespace) unset WGNAME_UP @@ -1319,9 +1374,6 @@ tofile "${C_IP:?}" "/config/self-for-guest/lg-${LID}/c_ip" echo_pty -e "....[${CG}OK${CN}]" -# Add a log entry into elastisearch using logpipe -echo "Type:Login|LID:${LID}|Hostname:${SF_HOSTNAME}|" | unix-socket-client &> /dev/null - # Spawn shell spawn_shell_exit "$@" # NOT REACHED diff --git a/master/cgi-bin/rpc b/master/cgi-bin/rpc index 474d2c8..4af53d9 100755 --- a/master/cgi-bin/rpc +++ b/master/cgi-bin/rpc @@ -63,6 +63,7 @@ GetFormVars() [[ ${key} == "privatekey" ]] && key="private" [[ ${key} == "private" ]] && R_WG_PRIVATE="${val//[^[:alnum:]+\/]}=" [[ ${key} == "name" ]] && { val="${val//[^[:alnum:]]}"; R_WT_NAME="${val:0:13}"; } + [[ ${key} == "token" ]] && { val="${val//[^a-zA-Z0-9@]}"; val="${val##*@}"; TOKEN_NAME="${val:0:64}"; } ### wgOUT [[ ${key} == "psk" ]] && R_OUT_PSK="${val//[^[:alnum:]+\/]}=" [[ ${key} == "public" ]] && key="peer" # Alias @@ -612,15 +613,22 @@ CheckGoodKey() BAIL "${R}ERROR${N}: Bad Key for ${opt}=" } -wg_net_init() -{ - local arr - local IFS - +# Load LG specific configuration (by source IP) +load_lg() { + local arr; # Retrieve (LID CID PID) arr=($(redr GET "ip:${REMOTE_ADDR}")) || BAIL "Bad Value" "Bad Value: " "ret=$?, ${#arr[@]}" [[ ${#arr[@]} -ne 3 ]] && BAIL "Value != 3" "Value != 3: " "${#arr[@]}" LID="${arr[0]}" + # CID="${arr[1]}" + PID="${arr[2]}" +} + +wg_net_init() +{ + local IFS + + load_lg LID_WGDIR="/config/db/user/lg-${LID}/wg" [[ ! -d "${LID_WGDIR}" ]] && mkdir "${LID_WGDIR}" LID_PROMPT_FN="/dev/shm/sf/self-for-guest/lg-${LID}/prompt" @@ -631,14 +639,8 @@ wg_net_init() USER_DB_WGNAME_UP_FN="/config/db/user/lg-${LID}/wg/name_up" # USER_DB_WGCLIENT_FN="/config/db/user/lg-${LID}/wg/client" - # CID="${arr[1]}" - PID="${arr[2]}" - # Split into arguments IFS=/ read -r -a ARGS <<< "${REQUEST_URI:1}" # Ignore first '/'. Split into arguements. - - # Load CLIENT config - # source "${USER_DB_WGCLIENT_FN}" 2>/dev/null } ERR_wg_help_exit() @@ -649,13 +651,50 @@ ERR_wg_help_exit() exit } +cmd_port() { + local ipport + + load_lg + source "/dev/shm/sf/run/users/lg-${LID}/limits.txt" + [[ -z $SF_RPORT ]] && BAIL "💥 ${CR}ERROR${CN}: ${CDM}Please contact a SysCop to enable this feature for you.${CN}" + + red RPUSH portd:cmd "remport ${lid}" >/dev/null + sleep 1 # Stop DoS attack: flood-requesting reverse ports + ipport=$(echo -e "DEL portd:response-${LID}\"\n\ +RPUSH portd:blcmd \"getport ${LID}\"\n\ +BLPOP portd:response-${LID} 5" | redr) || return + # DEBUGF "ipport='$ipport'" + ipport="${ipport##*$'\n'}" + [[ ! "${ipport##*:}" -gt 0 ]] && { DEBUGF "Failed to get Reverse Port Forward (ipport='$ipport')"; return; } + + # The PortD add's a /sf/run/self/reverse_forward. + echo -en "\ +${CDM}Tip${CN}: ${CDC}${CF}Type ${CDC}cat /config/self/reverse_*${CN}. +${CDG}👾 New reverse Port is ${CDY}${ipport}${CN}" + + # portd.sh automaticaly adds this to /config/self/reverse_* + exit +} + +cmd_token() { + local token_fn="/config/db/token/token-${TOKEN_NAME,,}.conf" + load_lg + source "/dev/shm/sf/run/users/lg-${LID}/limits.txt" + + [[ -n $SF_TOKEN_PROHIBITED ]] && BAIL "💥 ${CR}ERROR${CN}: ${CDM}Please contact a SysCop to enable this feature for you.${CN}" + [[ ! -f "${token_fn}" ]] && { sleep 1; BAIL "💥 ${CR}ERROR${CN}: ${CDM}Token '${TOKEN_NAME}' does not exist.${CN}"; } + + echo "${TOKEN_NAME}" >"/config/db/user/lg-${LID}/token" + + exit +} + # CLIENT cmd_wg_up() { local epip local args local err - local epport [[ ${R_OUT_ENDPOINT} != *:* ]] && R_OUT_ENDPOINT+=":51820" epip="${R_OUT_ENDPOINT%%:*}" @@ -684,7 +723,7 @@ cmd_wg_up() echo "$R_WG_PRIVATE" >"/dev/shm/private.$$" err=$(nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n wg set "$WG_DEV" private-key "/dev/shm/private.$$" peer "$R_OUT_PEER" "${args[@]}" endpoint "${R_OUT_ENDPOINT}" persistent-keepalive 25 allowed-ips 0.0.0.0/0,::/0 2>&1) || BAIL "${R}ERROR${N}: Failed: wg set (${err:0:128})" rm -f "/dev/shm/private.$$" "/dev/shm/psk.$$" - nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip link set mtu $((1500 - 80 - 80)) up dev "${WG_DEV}" + nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip link set mtu $((SF_HOST_MTU - 80 - 80)) up dev "${WG_DEV}" # Route to WG endpoint: nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${epip}" via "${SF_NET_LG_ROUTER_IP}" 2>/dev/null @@ -767,6 +806,12 @@ GetFormVars exit } +[[ "${FCGI_CMD}" == "port" ]] && cmd_port +[[ "${FCGI_CMD}" == "set" ]] && { + [[ -n $TOKEN_NAME ]] && cmd_token + BAIL "💥 ${CR}ERROR${CN}: ${CDM}Setting not found.${CN}" +} + [[ -n $SF_DEBUG ]] && [[ "${FCGI_CMD}" == "env" ]] && { env; exit; } wg_net_init @@ -883,7 +928,7 @@ wg_net_init # Configure interface after moving nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip -4 address add "${WG_IP}" dev "${WG_DEV}" err=$(nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip -6 address add "${WG_IP6}" dev "${WG_DEV}" 2>&1) || echo >&2 "${CR}ERROR${CN}: ip -6: $err" - nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip link set mtu 1420 up dev "${WG_DEV}" + nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip link set mtu $((SF_HOST_MTU - 80)) up dev "${WG_DEV}" set_route diff --git a/master/ready-lg.sh b/master/ready-lg.sh index 0195ba3..ed5f19a 100755 --- a/master/ready-lg.sh +++ b/master/ready-lg.sh @@ -14,6 +14,8 @@ source /dev/shm/config-lg.txt || exit 255 LID="$1" C_IP="$2" LG_PID="$3" +USER_DL_RATE="$4" +USER_UL_RATE="$5" LID_PROMPT_FN="/dev/shm/sf/self-for-guest/lg-${LID}/prompt" # Create 'empty' for ZSH's prompt to show WG EXIT @@ -30,7 +32,12 @@ nsenter.u1000 -t "${LG_PID:?}" --setuid 0 --setgid 0 -n arp -s "${SF_RPC_IP}" # 255.0.0.1 always points to guest's localhost: user can now set up a ssh -D1080 and connect with browser to # 255.0.0.1 and reach guest's 127.0.0.1. +# iptables is u+s and does not need --setuid nsenter.u1000 -t "${LG_PID}" -n iptables -t nat -A OUTPUT -p tcp --dst 255.0.0.1 -j DNAT --to-destination 127.0.0.1 + +# Set egress limits per LG +[[ -n $USER_UL_RATE ]] && nsenter.u1000 -t "${LG_PID:?}" --setuid 0 --setgid 0 -n tc qdisc add dev eth0 root cake bandwidth "${USER_UL_RATE}" dsthost + set +e exit 0 \ No newline at end of file diff --git a/provision/env.example b/provision/env.example index 27af7f9..b33c7fa 100644 --- a/provision/env.example +++ b/provision/env.example @@ -29,6 +29,7 @@ SF_BASEDIR=${HOME}/segfault ## Example: Germany:::