This commit is contained in:
rootTHC 2022-05-10 16:39:52 +01:00
parent cb925b32ca
commit 9b49809433
25 changed files with 2876 additions and 92 deletions

1
Makefile Normal file → Executable file

@ -1,3 +1,4 @@
all:
make -C guest
make -C host
make -C encfs

@ -1,4 +1,4 @@
# l0pht
# segfault
Regional Cluster design:
```mermaid

@ -1,4 +1,4 @@
version: "3.1"
version: "3.7"
services:
dns-doh:
container_name: cloudflared
@ -25,6 +25,7 @@ services:
tor:
image: osminogin/tor-simple
container_name: sf-tor
networks:
guest-net:
ipv4_address: 172.24.0.4
@ -33,29 +34,23 @@ services:
depends_on:
- dnsmasq
l0pht:
container_name: l0pht-host
segfault:
container_name: sf-host
build: host
depends_on:
- dnsmasq
restart: always
init: true
dns: 255.255.255.255
ports:
- "2222:2222"
- "${PORT}:2222"
env_file:
.env
volumes:
- "~/l0pht/cfg/etc/ssh:/etc/ssh/l0pht:ro"
- "${SF_BASEDIR}/config:/config:ro"
- "${SF_BASEDIR}/config/db:/config/db"
- "/var/run/docker.sock:/var/run/docker.sock"
# init: true
# secrets:
# volumes:
# guest-data:
# type: bind
# - driver: local
# device: ~/research/l0pht/guest/l0pht-guest
# external: true
networks:
dns-doh-net:
driver: bridge
@ -64,6 +59,7 @@ networks:
- subnet: 172.23.0.0/24
guest-net:
name: sf_guest-net
driver: bridge
ipam:
config:

12
encfs/Dockerfile Normal file

@ -0,0 +1,12 @@
FROM alpine:latest
RUN \
apk add --no-cache --upgrade && \
apk add --no-cache \
docker-cli \
bash \
encfs
ADD mount.sh /
CMD ["/mount.sh"]

3
encfs/Makefile Executable file

@ -0,0 +1,3 @@
all: Dockerfile
docker build -t sf-encfs .

33
encfs/mount.sh Executable file

@ -0,0 +1,33 @@
#! /bin/bash
RAWDIR="/encfs/raw/user-${LID}"
SECDIR="/encfs/sec/user-${LID}"
mkdir -p "${RAWDIR}" "${SECDIR}" 2>/dev/null
if [[ -n $MARKFILE ]]; then
if [[ -n $CHECKFILE ]]; then
n=0
while [[ -f "${SECDIR}/${MARKFILE}" ]]; do
[[ $n -gt 0 ]] && sleep 2 || sleep 0.1
n=$((n+1))
[[ $n -gt 5 ]] && exit 253 # "Could not create /sec..."
done
# echo "encrypted" >/sec/.encrypted
exit 0 # /sec created
fi
touch "${SECDIR}/${MARKFILE}" || exit 255
echo "Failed to set up encrypted drive. DO NOT USE THIS DIRECTORY" >"${SECDIR}/${MARKFILE}"
exit 0
fi
encfs --standard -o nonempty -o allow_other --extpass="echo \"${LENCFS_PASS}\"" "${RAWDIR}" "${SECDIR}"
# Unmount when no instance is running anymore.
sleep 5
while :; do
docker container inspect "lg-${LID}" -f '{{.State.Status}}' || break
sleep 10
done
echo "Unmounting lg-${LID} [${SECDIR}]"
fusermount -zuq /encfs/sec || echo "fusermount: Error ($?)"
echo "DONE"

@ -30,10 +30,9 @@ RUN apt install -y --no-install-recommends \
RUN apt install -y --no-install-recommends \
zsh zsh-syntax-highlighting zsh-autosuggestions
COPY /fs-root/ /
COPY setup.sh /tmp
RUN /tmp/setup.sh && \
rm -f /tmp/setup.sh
COPY setup.sh /
RUN /setup.sh && \
rm -f /setup.sh
CMD ["zsh", "-il"]

@ -1,3 +1,3 @@
all: Dockerfile
docker build -t l0pht-guest .
docker build -t sf-guest .

@ -1,5 +0,0 @@
# source'd during interactive shell login.
# Trampoline to this script:
[[ -e /usr/local/l0pht-guest/bin/l0pht-motd.sh ]] && /usr/local/l0pht-guest/bin/l0pht-motd.sh

@ -0,0 +1,5 @@
# source'd during interactive shell login to SF-GUEST.
# Trampoline to this script:
[[ -e /usr/local/sf-guest/bin/sf-motd.sh ]] && /usr/local/sf-guest/bin/sf-motd.sh

5
guest/fs-root/sbin/halt Executable file

@ -0,0 +1,5 @@
#! /bin/sh
# In docker this will 'halt' (hard crash) the instance.
kill 1

@ -1,7 +1,19 @@
FROM alpine:latest
ENV LUSER=user
ENV LGUESTDIR=/research/l0pht/guest/l0pht-guest
# SF-HOST
# SSHD clears all environment variable before spwaning shell.
# Any variable that needs to be available inside shell 'segfaultsh'
# needs to be added to docker_sshd.sh
ENV LUSER="${LUSER:-root}"
ENV LUSERPASSWORD="${LUSERPASSWORD:-segfault}"
ENV LDNS="${LDNS:-172.24.0.2}"
ENV SF_FQDN="${SF_FQDN:-segfault.thc.org}"
ENV LENCFS_SECDIR="${LENCFS_SECDIR:-/dev/shm/encfs-sec}"
ENV LENCFS_RAWDIR="${LENCFS_RAWDIR:-/dev/shm}"
ENV SF_SRCDIR="${SF_SRCDIR:-/dev/null}"
ENV SF_DEBUG="${SF_DEBUG}"
ENV SF_BASEDIR="${SF_BASEDIR:-/dev/null}"
RUN \
apk add --no-cache --upgrade && \
@ -12,9 +24,9 @@ RUN \
COPY /fs-root/ /
COPY setup.sh /tmp
RUN /tmp/setup.sh && \
rm -f /tmp/setup.sh
COPY setup.sh /
RUN /setup.sh && \
rm -f /setup.sh
CMD ["/bin/docker_sshd.sh"]

@ -1,3 +1,3 @@
all: Dockerfile
docker build -t l0pht-host .
docker build -t sf-host .

@ -1,26 +1,92 @@
#! /bin/bash
# This is the entry point for L0PHT-HOST (e.g. host/Dockerfile)
# Fix ownership if mounted from within vbox
[[ -e /etc/ssh/l0pht/ssh_host_rsa_key ]] || {
echo -e \
"\033[1;31mSSH Key not found in /etc/ssh/l0pht\033[00m. You must create them first and the
start docker with the additional '-v' option below:
CY="\033[1;33m" # yellow
CR="\033[1;31m" # red
CC="\033[1;36m" # cyan
CN="\033[0m" # none
mkdir -p ~/l0pht/cfg/etc/ssh && ssh-keygen -A ~/l0pht/cfg && \\
docker run --r -p 22:2222 -v /var/run/docker.sock:/var/run/docker.sock \\
-v ~/l0pht/cfg/etc/ssh:/etc/ssh/l0pht:ro \\
--name l0pht-host -it l0pth-host"
[[ -d /config ]] || {
echo -e "${CR}Not found: /config${CN}
--> Try -v ~/segfault/config:ro -v ~/segfault/config/db:/config/db"
sleep 5
exit 255
}
[[ -d /config/db ]] || {
echo -e "${CR}Not found: /config/db${CN}
--> Try -v ~/segfault/config:ro -v ~/segfault/config/db:/config/db"
sleep 5
exit 255
}
# This is the entry point for SF-HOST (e.g. host/Dockerfile)
# Fix ownership if mounted from within vbox
[[ -e /config/etc/ssh/ssh_host_rsa_key ]] || {
echo -e "\
${CR}SSH Key not found in /config/etc/ssh/${CN}. You must create them first:
--> ${CC}mkdir -p ~/segfault/config/etc/ssh && ssh-keygen -A -f ~/segfault/config${CN}"
sleep 5
exit 255
}
[[ -e /config/etc/ssh/id_ed25519 ]] || {
echo -e "\
${CR}SSH Login Key not found in /config/etc/ssh/id_ed25519${CN}. You must create them first:
--> ${CC}ssh-keygen -q -t ed25519 -C \"\" -N \"\" -f ~/segfault/config/etc/ssh/id_ed25519${CN}"
sleep 5
exit 255
}
# Copy login-key to fake root's home directory
[[ -e /home/root/.ssh/authorized_keys ]] || {
[[ -d /home/root/.ssh ]] || { mkdir /home/root/.ssh; chown root:nobody /home/root/.ssh; }
cp /config/etc/ssh/id_ed25519.pub /home/root/.ssh/authorized_keys
chown root:nobody /home/root/.ssh/authorized_keys
}
# Make a copy so that sf-hosts's root(uid=1000) can access the file.
cp /config/etc/ssh/id_ed25519 /var/run/id_ed25519.luser
chmod 444 /var/run/id_ed25519.luser
# 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
# variables to the user's docker instance (sf-guest)
echo "LDNS=\"${LDNS}\"
LENCFS_SECDIR=\"${LENCFS_SECDIR}\"
LENCFS_RAWDIR=\"${LENCFS_RAWDIR}\"
SF_SRCDIR=\"${SF_SRCDIR}\"
LUSER=\"${LUSER}\"
SF_DEBUG=\"${SF_DEBUG}\"
SF_BASEDIR=\"${SF_BASEDIR}\"
SF_FQDN=\"${SF_FQDN}\"" >/var/run/lhost-config.txt
# The owner of the original socket is not known at 'docker build' time. Thus
# we need to dynamically add it so that the shell started by SSHD can
# spwan ther L0PHT-GUEST docker.
[[ ! -e /var/run/docker.sock ]] && { echo "Not found: /var/run/docker.sock"; echo "Try -v -v /var/run/docker.sock:/var/run/docker.sock"; exit 255; }
# spwan ther SF-GUEST instance.
[[ ! -e /var/run/docker.sock ]] && { echo "Not found: /var/run/docker.sock"; echo "Try -v /var/run/docker.sock:/var/run/docker.sock"; exit 255; }
echo "docker:x:$(stat -c %g /var/run/docker.sock):${LUSER}" >>/etc/group && \
chmod 770 /var/run/docker.sock && \
/usr/sbin/sshd -u0 -p 2222
exec sleep infinite
# SSHD's user (normally "root" with uid 1000) needs write access to /config/db
# That directory is mounted from the outside and we have no clue what the
# group owner or permission is. Need to add our root(uid=1000) to that group:
dbgid="$(stat -c %g /config/db)"
[[ "$dbgid" -eq 0 ]] && {
echo -e "${CY}WARNING:${CN} /config/db has group owner of 'root'.
--> Better try: ${CC}chgrp nogroup ~/segfault/config/db${CN}"
}
addgroup -g $(stat -c %g /config/db) sf-dbrw 2>/dev/null # Ignore if already exists.
addgroup root sf-dbrw 2>/dev/null # Ignore if already exists.
chmod g+wx /config/db || exit $?
# This will execute 'segfaultsh' on login
/usr/sbin/sshd -u0 -p 2222 -D
# /usr/sbin/sshd -u0 -p 2222
tail -f /dev/null

@ -1,31 +0,0 @@
#! /bin/bash -r
# This is called by SSHD inside L0PHT-HOST docker.
# Find out if SSHD allocated a TTY
# - Redirects not allowed in restricted shells.
# - Execute `tty' in unrestricted shell (one day a clever hacker will exploit this)
# - Set docker arguments if this is a TTY session.
bash -c "tty >/dev/null" && { ARG="-it"; PARAM=("-il"); } || { ARG="-i"; PARAM=(); }
# Connect to existing session
[[ -n $LID ]] && [[ ${#LID} -eq 12 ]] && {
docker exec -it "lg-${LID}" zsh "${PARAM[@]}" "$@" || { echo "SESSION GONE"; exit 255; }
}
LID="$(head -c 1024 /dev/urandom | tr -dc '[:alpha:]' | head -c 12)"
HID="$(head -c 1024 /dev/urandom | tr -dc '[:alpha:]' | head -c 6)"
LVER="1.1"
docker run \
--hostname "l0pht-${HID}" \
--rm \
"$ARG" \
--name "lg-${LID}" \
--init \
--net l0pht_guest-net \
--dns 172.24.0.2 \
--env LID="${LID}" \
--env LVER="${LVER}" \
--log-driver none \
-v "l0pht_guest-data:/usr/local/l0pht-guest:ro" \
l0pht-guest zsh "${PARAM[@]}" "$@"

284
host/fs-root/bin/segfaultsh Executable file

@ -0,0 +1,284 @@
#! /bin/bash -r
# This is called by SSHD inside SF-HOST docker.
# - All environments have been cleared by SSHD.
# - Redirects not allowed in restricted shells.
# Load/restore environment variables from file
# `source' is prohibited in restricted bash shell (bash -r)
# use `eval' trick to load environemnt variables into a restricted shell
# 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 /var/run/lhost-config.txt ]] && eval "$(cat /var/run/lhost-config.txt)"
[[ -z $SF_DEBUG ]] && SF_DEBUG="${SSH_SF_DEBUG}"
unset SSH_SF_DEBUG
CY="\033[1;33m" # yellow
CR="\033[1;31m" # red
CC="\033[1;36m" # cyan
CG="\033[1;32m" # green
CDC="\033[0;36m" # cyan
CDR="\033[0;31m" # red
CN="\033[0m" # none
CW="\033[1;37m" # white
CF="\033[2m" # faint
ERREXIT()
{
local code
code="$1"
[[ -z $code ]] && code=99
shift 1
[[ -n $1 ]] && echo -e >&2 "${CR}ERROR:${CN} $*"
exit "$code"
}
if [[ -z $SF_DEBUG ]]; then
DEBUGF(){ :;}
SF_DOCKER_LOG="none"
else
DEBUGF(){ echo -e 1>&2 "${CY}DEBUG:${CN} $*";}
SF_DOCKER_LOG="local"
fi
# The current 'restricted shell' does not allow for stdout redirection to /dev/null.
# Thus execute in unrestricted shell with 'exec_devnull' and redirect stdout to /dev/null.
exec_devnull()
{
if [[ -z $SF_DEBUG ]]; then
bash -c "exec $* 2>/dev/null >/dev/null"
else
# HERE: DEBUG is enabled. Show STDOUT/STDERR
$*
fi
}
exec_errnull()
{
if [[ -z $SF_DEBUG ]]; then
bash -c "exec $* 2>/dev/null"
else
# HERE: DEBUG is enabled. Show STDOUT/STDERR
$*
fi
}
print_ssh_access()
{
echo 1>&2 -e "\
:Cut & Paste these lines to your workstation's shell to retain access:
######################################################################
${CDC}cat >~/.ssh/id_ed25519-lg-${LID} ${CDR}<<__EOF__
${CN}${CF}$(cat /var/run/id_ed25519.luser)
${CDR}__EOF__
${CDC}cat >>~/.ssh/config ${CDR}<<${CDR}__EOF__
${CN}${CF}host ${HOSTNAME,,}.${SF_FQDN//./-}
HostName ${SF_FQDN}
IdentityFile ~/.ssh/id_ed25519-lg-${LID}
SetEnv SECRET=${LSEC}
${CDR}__EOF__
${CDC}chmod 600 ~/.ssh/config ~/.ssh/id_ed25519-lg-${LID}${CN}
######################################################################
Thereafter use this command to connect to your server:
--> ${CDC}ssh root@${HOSTNAME,,}.${SF_FQDN//./-}${CN}
----------------------------------------------------------------------"
}
echo_pty()
{
[[ -n $IS_PTY ]] || return
echo $@
}
# Find out if SSHD allocated a TTY
# - Execute `tty' in unrestricted shell (one day a clever hacker will exploit this)
# - Set docker arguments to login-shell if this is a TTY session.
bash -c "tty >/dev/null" && { ARG="-it"; PARAM=("-il"); IS_PTY=1; } || { ARG="-i"; PARAM=(); }
# Connect to existing session
if [[ -n $SECRET ]] && [[ ${#SECRET} -eq 12 ]] && [[ ! "${SECRET}" =~ [^a-zA-Z0-9] ]]; then
IS_TRY_EXISTING=1
LSEC="${SECRET}"
else
LSEC="$(head -c 1024 /dev/urandom | tr -dc '[:alpha:]' | head -c 12)"
fi
LID=$(echo -n "LID ${LSEC}" | sha512sum | base64 | tr -dc '[:alpha:]' | head -c 10)
LVER="1.1"
# MARKFILE="THIS-DIRECTORY-IS-NOT-ENCRYPTED--DO-NOT-USE-$(date +%s-%N).txt"
MARKFILE="THIS-DIRECTORY-IS-NOT-ENCRYPTED--DO-NOT-USE-$(date +%s-%N).txt"
if [[ -d "/config/db/db-${LID}" ]]; then
echo_pty -n "Connecting to server. Please wait..."
else
echo_pty -n "Creating new server. Please wait...."
IS_NEW_SERVER=1
mkdir -p "/config/db/db-${LID}" || ERREXIT
touch "/config/db/db-${LID}/created.txt" || ERREXIT
fi
DEBUGF "LID=${LID}"
if [[ "$(exec_errnull docker container inspect "encfs-${LID}" -f '{{.State.Status}}')" != "running" ]]; then
LENCFS_PASS=$(echo -n "EncFS-PASS ${LSEC}" | sha512sum | base64 | tr -dc '[:alpha:]' | head -c 16)
# HERE: encfs is not already running for this instance...
# 1st EncFS to put warning into mount-point that it is cleartext (until it's mounted!).
DEBUGF "EncFS-${LID} is not yet running..."
docker run \
--rm \
--env MARKFILE="${MARKFILE}" \
--env LID="${LID}" \
--env SF_DEBUG="${SF_DEBUG}" \
-v "${LENCFS_SECDIR}:/encfs/sec:shared" \
sf-encfs || ERREXIT 250 "Could not create /sec..."
# 2nd EncFS to mount encrypted to mount-point.
# Run in background (-d)
# Use exec_devnull to redirect Container-ID to /dev/null (docker run -d).
exec_devnull docker run \
--rm \
--name "encfs-${LID}" \
--cap-add SYS_ADMIN \
--device /dev/fuse \
--security-opt apparmor:unconfined \
--env LENCFS_PASS="${LENCFS_PASS}" \
--env LID="${LID}" \
--env SF_DEBUG="${SF_DEBUG}" \
--log-driver "${SF_DOCKER_LOG}" \
-v "${LENCFS_RAWDIR}:/encfs/raw" \
-v "${LENCFS_SECDIR}:/encfs/sec:shared" \
-v "/var/run/docker.sock:/var/run/docker.sock" \
-d \
sf-encfs || ERREXIT 251 "Could not create /sec..."
fi
echo_pty -n ".."
# Wait until sf-encf has mounted the secure drive...or otherwise this
# will show an insecure directory:
# ssh user@segfault.thc.org "ls -al /sec"
# This will loop until the MARKFILE is gone (e.g. encfs mounted a directory over it)
# NOTE: It would be quicker to have /encfs/sec available in this sf-host instance
# but this may also pose a security risk: A vulnerability in SSHD would
# expose all encrypted drives. Doing it with 'docker run' means that
# the hacker would to to break docker - which is easy enough given that the
# docker-socket is available in this context but still a step harder.
DEBUGF "Waiting until /sec becomes available..."
docker run \
--rm \
--env MARKFILE="${MARKFILE:-UNKNOWN.TXT}" \
--env CHECKFILE="1" \
--env LID="${LID}" \
--env SF_DEBUG="${SF_DEBUG}" \
-v "${LENCFS_SECDIR}:/encfs/sec:shared" \
sf-encfs || ERREXIT 244 "Failed to set up /sec..."
echo_pty -n ".."
# Generate a mnemonic hostname from LID (e.g. ButterflyCat)
NUM=$(echo "ibase=16; $(echo "$LID" | md5sum | cut -f1 -d" " | tr 'a-z' A-Z)" | bc)
readarray -t english </etc/english.txt
HOSTNAME="UnknownUnknown"
if [[ "${#english[@]}" -eq 2048 ]]; then
HOSTNAME="${english[$((NUM % 2048))]}"
HOSTNAME+="${english[$(( (NUM / 2048) % 2048 ))]}"
fi
DEBUGF "HOSTNAME=$HOSTNAME"
unset NUM
unset english
# Attach to instance if already running
[[ -n $IS_TRY_EXISTING ]] && {
DEBUGF "Attaching to existing instance lg-${LID}..."
exec_devnull docker container inspect "lg-${LID}" -f '{{.State.Status}}' && {
echo_pty -e "..........................[${CG}OK${CN}]"
docker exec "$ARG" "lg-${LID}" zsh "${PARAM[@]}" "$@"
exit $?
}
echo_pty -n ".."
DEBUGF "FAILED to attached to lg-${LID}"
# HERE: Instance does not exists.
}
# Starting GUEST shell
# Challenge: Keep user processes running that got spawned in the background
# even when first instance terminates. Also do not terminate instance
# when there are still shells using it ('docker exec').
# Solution: Spawn a docker in the background that monitors the number of
# processes and use 'docker exec' for every connection.
exec_devnull docker run \
--hostname "sf-${HOSTNAME}" \
--rm \
--init \
--name "lg-${LID}" \
--init \
--net sf_guest-net \
--dns "${LDNS:-BAD}" \
--env LSEC="${LSEC}" \
--env LVER="${LVER}" \
--env LUSER="${LUSER}" \
--env SF_FQDN="${SF_FQDN}" \
--env SF_DEBUG="${SF_DEBUG}" \
-e SSH_CONNECTION \
-e SSH_CLIENT \
--log-driver "${SF_DOCKER_LOG}" \
--mount type=bind,source="${SF_BASEDIR}/config/etc/ssh/id_ed25519,target=/config/id_ed25519,readonly" \
-v "${SF_BASEDIR}/guest/sf-guest:/usr/local/sf-guest:ro" \
-v "${LENCFS_SECDIR}/user-${LID}:/sec:shared" \
-d \
sf-guest /usr/local/sf-guest/bin/sf-destructor.sh || ERREXIT 251 "Failed to set up guest instance..."
[[ -n $IS_PTY ]] && echo -n ".."
DEBUGF "Instance started..."
# Wait for detached docker shell to become available...
n=0
while :; do
str="$(exec_errnull docker container inspect "lg-${LID}" -f '{{.State.Status}}')"
rc="$?"
DEBUGF "rc=$rc status=$str"
[[ "$rc" -eq 0 ]] && [[ "$str" = "running" ]] && break
DEBUGF "#${n} Waiting for sf-guest to be ready..."
[[ $n -gt 0 ]] && sleep 5 || sleep 0.1
n=$((n+1))
[[ $n -gt 2 ]] && ERREXIT 253 "Could not create instance..."
done
[[ -n $IS_PTY ]] && echo -n ".."
# Setup instance
docker exec "lg-${LID}" /usr/local/sf-guest/bin/sf-setup.sh || ERREXIT 252 "Failed to set up guest instance..."
echo_pty -e "....................[${CG}OK${CN}]"
# Output help of how to connect elegantly
[[ -n $IS_NEW_SERVER ]] && print_ssh_access
# Spawn shell
docker exec "$ARG" "lg-${LID}" zsh "${PARAM[@]}" "$@"
ret="$?" # save return value and exit this script later with same return value.
# Output GOODBYE message with infos how to connect back to this shell
if [[ -n $IS_PTY ]]; then
# Restricted shell (-r) wont let us redirect stderr - use a bash-exec trick
n="$(bash -c "docker exec \"lg-${LID}\" ps --no-headers aux 2>/dev/null|wc -l")"
DEBUGF "Processes running: $n"
if [[ "$n" -gt 4 ]]; then
echo -e "\
${CY}WARNING: Another shell or background process is still running.${CN}
-------> The encrypted filesystem in /sec will remain mounted until
-------> the last shell exits and all background processes terminate.
-------> Type ${CC}halt${CN} instead to unmount the encrypted filesystem
-------> in /sec and to halt this instance."
fi
echo -e "\
Access with : ${CDC}ssh -o \"SetEnv SECRET=${LSEC:-UNKNOWN}\" ${LUSER}@${SF_FQDN:-UNKNOWN}${CN}
GOODBYE : ${CW}Join us on Telegram - https://t.me/thcorg${CN}"
fi
exit "$ret"

2048
host/fs-root/etc/english.txt Normal file

File diff suppressed because it is too large Load Diff

18
host/fs-root/etc/ssh/sshd_config Normal file → Executable file

@ -15,9 +15,9 @@
#ListenAddress 0.0.0.0
#ListenAddress ::
HostKey /etc/ssh/l0pht/ssh_host_rsa_key
HostKey /etc/ssh/l0pht/ssh_host_ecdsa_key
HostKey /etc/ssh/l0pht/ssh_host_ed25519_key
HostKey /config/etc/ssh/ssh_host_rsa_key
HostKey /config/etc/ssh/ssh_host_ecdsa_key
HostKey /config/etc/ssh/ssh_host_ed25519_key
# Ciphers and keying
#RekeyLimit default none
@ -30,7 +30,9 @@ HostKey /etc/ssh/l0pht/ssh_host_ed25519_key
#LoginGraceTime 2m
#PermitRootLogin prohibit-password
#StrictModes yes
# FIXME: Disable strict mode to allow sshd to read ssh keys from vboxfs (rwxrwx---)
StrictModes no
##StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
@ -39,6 +41,7 @@ HostKey /etc/ssh/l0pht/ssh_host_ed25519_key
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys
#AuthorizedKeysFile /config/etc/ssh/id_ed25519.pub
#AuthorizedPrincipalsFile none
@ -90,13 +93,14 @@ X11Forwarding no
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
AcceptEnv LID
AcceptEnv SF_DEBUG
AcceptEnv SECRET
#PrintLastLog yes
#TCPKeepAlive yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
ClientAliveInterval 15
ClientAliveCountMax 3
#UseDNS no
#PidFile /run/sshd.pid
#MaxStartups 10:30:100

25
host/setup.sh Normal file → Executable file

@ -1,18 +1,27 @@
#! /bin/bash
echo "/bin/l0phtsh" >>/etc/shells && \
adduser -D ${LUSER} -s /bin/l0phtsh && \
sed -i 's/user\:!/user\:$6$nND1o68YSDG8heUr$wx\/FpC3\/TCZlhs3LsJ7ll5YVlPfICNN7yHmyppR6MqedvZ9Vgbq6SV3TlMFaFZAiYePNFaY477Xrb0fOMo24p0/g' /etc/shadow && \
# Docker sf-host setup script (docker build)
ROOTUSER="root"
# Default is for user to use 'ssh root@segfault.net' but this can be changed
# in .env to any other user name. In case it is 'root' then we need to move
# the true root out of the way for the docker-sshd to work.
if [[ "$LUSER" = "root" ]]; then
# rename root user
sed -i 's/^root/toor/' /etc/passwd
sed -i 's/^root/toor/' /etc/shadow
fi
echo "/bin/segfaultsh" >>/etc/shells && \
adduser -D ${LUSER} -G nobody -s /bin/segfaultsh && \
echo "${LUSER}:${LUSERPASSWORD}" | chpasswd
# sed -i 's/#PermitEmptyPasswords no/PermitEmptyPasswords yes/g' /etc/ssh/sshd_config && \
# sed -i 's/#PrintMotd yes/ no/PermitEmptyPasswords yes/g' /etc/ssh/sshd_config && \
# Need to set correct permission which may have gotten skewed when building
# docker inside vmbox from shared host drive (rwxrwx--- root:vobxsf)
chown -R root:root /etc/ssh && \
chown -R "${ROOTUSER}":"${ROOTUSER}" /etc/ssh && \
chmod 700 /etc/ssh && \
chown root:root /bin/l0phtsh && \
chmod 755 /bin/l0phtsh && \
chown "${ROOTUSER}":"${ROOTUSER}" /bin/segfaultsh && \
chmod 755 /bin/segfaultsh && \
chmod 755 /bin /etc && \
echo DONE

14
provision/env.example Executable file

@ -0,0 +1,14 @@
# EXAMPLE docker-compose .env file
SF_BASEDIR=${HOME}/segfault
# Source
SF_SRCDIR=${HOME}/segfault
# SF_SRCDIR=${SF_BASEDIR}
#LENCFS_RAWDIR=/home/sf-user/segfault/encfs-raw
LENCFS_RAWDIR=${SF_BASEDIR}/encfs-raw
PORT=22
#SF_DEBUG=1
#LENCFS_SECDIR=/dev/shm/encfs-sec
#LUSER=root
#LUSERPASSWORD=segfault
#SF_FQDN=CHANGEME.segfault-net
#LDNS=172.24.0.2

71
provision/funcs Normal file

@ -0,0 +1,71 @@
#! /usr/bin/env bash
# shellcheck disable=SC2034 # unused variable warning for ansi colors
CY="\033[1;33m" # yellow
CG="\033[1;32m" # green
CR="\033[1;31m" # red
CC="\033[1;36m" # cyan
CM="\033[1;35m" # magenta
CW="\033[1;37m" # magenta
CF="\033[2m" # faint
CN="\033[0m" # none
CBG="\033[42;1m" # Background Green
# night-mode
CDY="\033[0;33m" # yellow
CDG="\033[0;32m" # green
CDR="\033[0;31m" # red
CDC="\033[0;36m" # cyan
CDM="\033[0;35m" # magenta
# Clear from cursor to end of line
CL="\033[0K"
if [[ -z $SF_DEBUG ]]; then
DEBUGF(){ :;}
DEBUGF_R(){ :;}
else
DEBUGF(){ echo -e "${CY}DEBUG:${CN} $*";}
DEBUGF_R(){ echo -e "${CY}DEBUG:${CN} ${CR}$*${CN}";}
fi
ERREXIT()
{
local code
code="$1"
[[ $? -ne 0 ]] && code="$?"
[[ -z $code ]] && code=99
shift 1
[[ -n $1 ]] && echo -e >&2 "${CR}ERROR:${CN} $*"
exit "$code"
}
WARN()
{
local code
code="$1"
[[ -z $code ]] && code=255
shift 1
echo -e >&2 "${CY}WARNING(${code}):${CN} $*"
}
INFO()
{
echo -e "--> ${CM}$*${CN}"
}
NEED_ROOT()
{
[[ "$(id -u)" -ne 0 ]] && ERREXIT 255 "Error: Run this scrpt as root"
}
IS_APT_INSTALLED()
{
[[ "$(apt -qq list "$*" 2>/dev/null)" = *"[installed]" ]] && return 0 || return 1
}

69
provision/init-nordvpn.sh Executable file

@ -0,0 +1,69 @@
#! /bin/bash
# Install NordVPN and configure it.
SFI_BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)"
source "${SFI_BASEDIR}/funcs"
NEED_ROOT
# Exit if already installed (nordvpn status return TRUE even when not connected)
nordvpn status 2>/dev/null && { WARN 1 "NordVPN already installed. SKIPPING."; exit 0; }
# command -v nordvpn >/dev/null && { WARN 23 "NordVPN already installed. SKIPPING"; exit 0; }
[[ -z $SF_VPN_LOGIN ]] && ERREXIT 255 "SF_VPN_LOGIN= not set"
[[ -z $SF_VPN_PASSWORD ]] && ERREXIT 254 "SF_VPN_PASSWORD= not set"
[[ -z $SF_HOST_USER ]] && ERREXIT 253 "SF_HOST_USER= not set"
# AWS ubuntu 22 doesnt have this symlink
[[ ! -e /usr/bin/systemd-resolve ]] && [[ /usr/bin/resolvectl ]] && ln -s resolvectl /usr/bin/systemd-resolv
if ! command -v nordvpn >/dev/null; then
BASE_URL=https://repo.nordvpn.com/
KEY_PATH=/gpg/nordvpn_public.asc
REPO_PATH_DEB=/deb/nordvpn/debian
RELEASE="stable main"
PUB_KEY=${BASE_URL}${KEY_PATH}
REPO_URL_DEB=${BASE_URL}${REPO_PATH_DEB}
apt update -y
apt -y install --no-install-recommends ca-certificates \
curl \
gnupg \
lsb-release \
apt-transport-https
[[ -s /etc/apt/trusted.gpg.d/nordvpn_public.asc ]] || {
curl -s "${PUB_KEY}" | tee /etc/apt/trusted.gpg.d/nordvpn_public.asc >/dev/null
[[ -s /etc/apt/trusted.gpg.d/nordvpn_public.asc ]] || ERREXIT
}
[[ -s /etc/apt/sources.list.d/nordvpn.list ]] || {
echo "deb ${REPO_URL_DEB} ${RELEASE}" | tee /etc/apt/sources.list.d/nordvpn.list >/dev/null
[[ -s /etc/apt/sources.list.d/nordvpn.list ]] || ERREXIT
}
apt -y update
apt -y install --no-install-recommends nordvpn \
net-tools
fi
usermod -aG nordvpn "${SF_HOST_USER}"
nordvpn login --username "${SF_VPN_LOGIN}" --password "${SF_VPN_PASSWORD:-PASSWORD-MISSING}"
# Find out source IP to whitelist
[[ -z $SF_IP_WHITELIST ]] && {
SF_IP_WHITELIST="$(echo "$SSH_CONNECTION" | cut -f1 -d" ")"
[[ -z $SF_IP_WHITELIST ]] && ERREXIT 253 "SF_VPN_WHITELIST= not set."
}
nordvpn whitelist add subnet "${SF_IP_WHITELIST}/32" || ERREXIT 254 "SF_IP_WHITELIST=${SF_IP_WHITELIST}/32"
# We poll in sf-fw.sh until NordVPN is connected.
# nordvpn set autoconnect on
# Do not allow NordVPN to manage my firewall
nordvpn set firewall off

166
provision/init-ubuntu.sh Executable file

@ -0,0 +1,166 @@
#! /bin/bash
# Installs & Bootstraps 'Segfault Hosting Solution' onto a vanilla Linux Server.
#
# See https://www.thc.org/segfault/deploy to install with a single command.
#
# Environment variables:
# SF_HOST_USER - The user on the server under which 'segfault' is installed. (e.g. /home/ubuntu)
SFI_SRCDIR="$(cd "$(dirname "${0}")/.." || exit; pwd)"
source "${SFI_SRCDIR}/provision/funcs" || exit 255
NEED_ROOT
DEBUGF "SFI_SRCDIR=${SFI_SRCDIR}"
SUDO_SF()
{
sudo -u "${SF_HOST_USER}" bash -c "$*"
}
# INIT: Find valid user to use for installation
[[ -z $SF_HOST_USER ]] && {
# EC2 default is 'ubuntu'. Fall-back to 'sf-user' otherwise.
id -u ubuntu &>/dev/null && SF_HOST_USER="ubuntu" || SF_HOST_USER="sf-user"
}
export SF_HOST_USER # init-nordvpn.sh etc needs this.
# Create user if it does not exist
useradd "${SF_HOST_USER}" -s /bin/bash 2>/dev/null
[[ -d "/home/${SF_HOST_USER}" ]] || {
cp -a /etc/skel "/home/${SF_HOST_USER}"
chown -R "${SF_HOST_USER}:${SF_HOST_USER}" "/home/${SF_HOST_USER}"
}
SF_HOST_USER_ID="$(id -u "$SF_HOST_USER")"
DEBUGF "SF_HOST_USER_ID=${SF_HOST_USER_ID} (SF_HOST_USER=${SF_HOST_USER})"
# INIT: Find good location for dynamic configuration
[[ -z $SF_BASEDIR ]] && {
SUDO_SF "mkdir -p ~/segfault"
SF_BASEDIR="$(cd "/home/${SF_HOST_USER}/segfault" || exit; pwd)"
}
DEBUGF "SF_BASEDIR=${SF_BASEDIR}"
[[ ! -d "${SF_BASEDIR}/config" ]] && SUDO_SF "mkdir \"${SF_BASEDIR}/config\""
[[ ! -d "${SF_BASEDIR}/config/db" ]] && SUDO_SF "mkdir \"${SF_BASEDIR}/config/db\""
# Configure SSHD
[[ -z $SF_SSH_PORT ]] && SF_SSH_PORT=22
[[ -z $SF_SSH_PORT_MASTER ]] && SF_SSH_PORT_MASTER=64222
# Move original SSH server out of the way...
[[ "$SF_SSH_PORT" -eq 22 ]] && {
sed -i "s/#Port ${SF_SSH_PORT}/Port ${SF_SSH_PORT_MASTER}/g" /etc/ssh/sshd_config
DEBUGF "Restarting SSHD"
service sshd restart
}
# Add docker repository to APT
[[ -s /usr/share/keyrings/docker-archive-keyring.gpg ]] || {
apt update -y
apt -y install --no-install-recommends ca-certificates \
curl \
gnupg \
lsb-release \
apt-transport-https
[[ -s /usr/share/keyrings/docker-archive-keyring.gpg ]] || {
rm -rf /usr/share/keyrings/docker-archive-keyring.gpg 2>/dev/null # Delete in case it is zero bytes
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
}
apt-get update -y
}
apt-get install -y --no-install-recommends \
docker-ce docker-ce-cli containerd.io docker-compose \
net-tools make || ERREXIT
# SSHD's login user (normally 'root' with uid 1000) needs to start docker instances
usermod -a -G docker "${SF_HOST_USER}"
# NOTE: Only needed if source is mounted into vmbox (for testing)
[[ "$(stat -c %G /segfault 2>/dev/null)" = "vboxsf" ]] && usermod -a -G vboxsf "${SF_HOST_USER}"
# SNAPSHOT #2 (2022-05-09)
# Create guest, encfs and other docker images.
SUDO_SF "cd ${SFI_SRCDIR} && make" || exit
# Create SSH-KEYS and directories.
[[ -d "${SF_BASEDIR}/config/db" ]] || SUDO_SF "mkdir -p \"${SF_BASEDIR}/config/db\""
[[ -d "${SF_BASEDIR}/config/etc/ssh" ]] || SUDO_SF "mkdir -p \"${SF_BASEDIR}/config/etc/ssh\" && ssh-keygen -A -f ~/segfault/config"
[[ -f "${SF_BASEDIR}/config/etc/ssh/id_ed25519" ]] || SUDO_SF "ssh-keygen -q -t ed25519 -C \"\" -N \"\" -f \"${SF_BASEDIR}/config/etc/ssh/id_ed25519\""
# Find out my own hostname unless SF_FQDN is set (before NordVPN is runnning)
[[ -z $SF_FQDN ]] && {
# Find out my own hostname
IP="$(curl ifconfig.me 2>/dev/null)"
HOST="$(host "$IP")" && HOST="$(echo "$HOST" | sed -E 's/.*name pointer (.*)\.$/\1/g')" || HOST="$(hostname -f)"
# To short or contains illegal characters?
[[ "$HOST" =~ ^[a-zA-Z0-9.-]{4,61}$ ]] || unset HOST
SF_FQDN="${HOST:-UNKNOWN}"
unset ip
unset HOST
}
DEBUGF "SF_FQDN=${SF_FQDN}"
# Create '.env' file for docker-compose
SF_BASEDIR_ESC="${SF_BASEDIR//\//\\/}"
SF_SRCDIR_ESC="${SFI_SRCDIR//\//\\/}"
SF_FQDN_ESC="${SF_FQDN//\//\\/}"
ENV="${SFI_SRCDIR}/.env"
[[ -e "${ENV}" ]] && { WARN 4 "Using existing .env file"; } || {
SUDO_SF "cp \"${SFI_SRCDIR}/provision/env.example\" \"${ENV}\" && \
sed -i 's/^SF_BASEDIR.*/SF_BASEDIR=${SF_BASEDIR_ESC}/' \"${ENV}\" && \
sed -i 's/^SF_SRCDIR.*/SF_SRCDIR=${SF_SRCDIR_ESC}/' \"${ENV}\" && \
sed -i 's/.*SF_FQDN.*/SF_FQDN=${SF_FQDN_ESC}/' \"${ENV}\" && \
sed -i 's/PORT=.*/PORT=${SF_SSH_PORT}/' \"${ENV}\"" || ERREXIT 120 failed
}
# SUDO_SF "cd ${SF_BASEDIR} && docker-compose -f \"${SFI_SRCDIR}/docker-compose.yml\" up -d --build --force-recreate --quiet-pull" || ERREXIT
cd ${SFI_SRCDIR} && docker-compose up -d --build --force-recreate --quiet-pull || ERREXIT
# This directory will be mounted[read-only] into sf-guest (user's shell)
[[ -d "${SF_BASEDIR}/guest" ]] || SUDO_SF "mkdir -p \"${SF_BASEDIR}/guest\"" || ERREXIT
# Copy supporting files that sf-guest needs (like /etc/skel, vpn_status, sf-motd, etc).
SUDO_SF "cp -r \"${SFI_SRCDIR}/guest/sf-guest\" \"${SF_BASEDIR}/guest\""
# Set up NordVPN
${SFI_SRCDIR}/provision/init-nordvpn.sh
command -v nordvpn >/dev/null && {
DEBUGF "Installing Segfault Services..."
[[ -z $SF_BASEDIR_ESC ]] && ERREXIT 11 "SF_BASEDIR_ESC not set???"
# Create supporting directories
mkdir "${SF_BASEDIR}/system" 2>/dev/null
### Set up NordVPN Status-Update/Monitoring script
cp "${SFI_SRCDIR}/system/sf-monitor.sh" "${SF_BASEDIR}/system/sf-monitor.sh"
chmod 750 "${SF_BASEDIR}/system/sf-monitor.sh"
cp "${SFI_SRCDIR}/provision/sf-monitor.service" /etc/systemd/system/sf-monitor.service
chmod 640 /etc/systemd/system/sf-monitor.service
sed -i "s/@SF_BASEDIR@/${SF_BASEDIR_ESC}/" /etc/systemd/system/sf-monitor.service
systemctl enable sf-monitor
systemctl start sf-monitor
### Set up firewall script (for NordVPN)
cp "${SFI_SRCDIR}/system/sf-fw.sh" "${SF_BASEDIR}/system/sf-fw.sh"
chmod 750 "${SF_BASEDIR}/system/sf-fw.sh"
cp "${SFI_SRCDIR}/provision/sf-fw.service" /etc/systemd/system/sf-fw.service
chmod 640 /etc/systemd/system/sf-fw.service
sed -i "s/@SF_BASEDIR@/${SF_BASEDIR_ESC}/" /etc/systemd/system/sf-fw.service
systemctl enable sf-fw
systemctl start sf-fw
} || WARN 2 "Skipping NordVPN"

12
provision/sf-fw.service Executable file

@ -0,0 +1,12 @@
[Unit]
Description=Segfault Firewall Script
Requires=nordvpnd.service
[Service]
Type=oneshot
Environment="SF_BASEDIR=@SF_BASEDIR@"
ExecStart=@SF_BASEDIR@/system/sf-fw.sh
[Install]
WantedBy=default.target

11
provision/sf-monitor.service Executable file

@ -0,0 +1,11 @@
[Unit]
Description=Segfault Monitoring and VPN Status Updater
After=network.target
[Service]
Environment="SF_BASEDIR=@SF_BASEDIR@"
ExecStart=@SF_BASEDIR@/system/sf-monitor.sh
[Install]
WantedBy=default.target