Skip to content

Commit

Permalink
Create .dockerignore
Browse files Browse the repository at this point in the history
  • Loading branch information
dogruis committed Dec 11, 2024
1 parent e83b04f commit 2de655b
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 122 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.gitignore
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends binfmt-support qemu-user-static

- run: ./build.sh
- run: ./test.sh shellsu-amd64
- run: ./test.sh shellsu-i386
- run: ./test.sh --debian shellsu-amd64
- run: ./test.sh --debian shellsu-i386
- run: ./test.sh --alpine shellsu
- run: ./test.sh --debian shellsu
- run: docker build --pull --file hub/Dockerfile.alpine hub
- run: docker build --pull --file hub/Dockerfile.debian hub
43 changes: 9 additions & 34 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,25 @@ FROM debian:bookworm-slim
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
arch-test \
file \
bash \
login \
; \
rm -rf /var/lib/apt/lists/*

# Set environment variable for build flags
ENV BUILD_FLAGS="-v"

# Prepare the `shellsu` build and test script
RUN set -eux; \
{ \
echo '#!/usr/bin/env bash'; \
echo 'set -Eeuo pipefail -x'; \
echo 'ARCH="${ARCH:-$(uname -m)}"'; \
echo 'cp /usr/local/bin/shellsu /usr/local/bin/shellsu-$ARCH'; \
echo 'file "/usr/local/bin/shellsu-$ARCH"'; \
echo 'if arch-test "$ARCH"; then'; \
echo ' try() { for (( i = 0; i < 30; i++ )); do if timeout 1s "$@"; then return 0; fi; done; return 1; }'; \
echo ' try "/usr/local/bin/shellsu-$ARCH" --version'; \
echo ' try "/usr/local/bin/shellsu-$ARCH" nobody id'; \
echo ' try "/usr/local/bin/shellsu-$ARCH" nobody ls -l /proc/self/fd'; \
echo 'fi'; \
} > /usr/local/bin/shellsu-build-and-test.sh; \
chmod +x /usr/local/bin/shellsu-build-and-test.sh

# Disable CGO (not relevant for Bash, but keeping for alignment with original pattern)
ENV CGO_ENABLED 0

# Copy `shellsu` script into the container
WORKDIR /usr/local/bin
COPY shellsu /usr/local/bin/shellsu
COPY shellsu.sh /usr/local/bin/shellsu
RUN chmod +x /usr/local/bin/shellsu

# Test `shellsu` for various architectures
RUN ARCH=amd64 shellsu-build-and-test.sh
RUN ARCH=i386 shellsu-build-and-test.sh
RUN ARCH=armel shellsu-build-and-test.sh
RUN ARCH=armhf shellsu-build-and-test.sh
RUN ARCH=arm64 shellsu-build-and-test.sh
RUN ARCH=mips64el shellsu-build-and-test.sh
RUN ARCH=ppc64el shellsu-build-and-test.sh
RUN ARCH=riscv64 shellsu-build-and-test.sh
RUN ARCH=s390x shellsu-build-and-test.sh
# Test `shellsu` to verify functionality
RUN set -eux; \
# Run the script with basic tests
./shellsu --help; \
./shellsu --version

# Final verification step
RUN set -eux; ls -lAFh /usr/local/bin/shellsu-*; file /usr/local/bin/shellsu-*
# Final verification step (list files and check permissions)
RUN set -eux; ls -lAFh /usr/local/bin/shellsu; file /usr/local/bin/shellsu
17 changes: 13 additions & 4 deletions Dockerfile.test-alpine
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
FROM alpine:3.20

RUN apk add --no-cache bash coreutils shadow

# Add "nobody" to ALL groups to create edge cases for testing
RUN cut -d: -f1 /etc/group | xargs -rtn1 addgroup nobody

Expand All @@ -11,30 +13,37 @@ RUN { \
echo 'spec="$1"; shift'; \
echo; \
echo 'expec="$1"; shift'; \
echo 'real="$(shellsu "$spec" id -u):$(shellsu "$spec" id -g):$(shellsu "$spec" id -G)"'; \
echo 'real="$(/usr/local/bin/shellsu "$spec" id -u):$(/usr/local/bin/shellsu "$spec" id -g):$(/usr/local/bin/shellsu "$spec" id -G)"'; \
echo '[ "$expec" = "$real" ]'; \
echo; \
echo 'expec="$1"; shift'; \
echo 'real="$(shellsu "$spec" id -un):$(shellsu "$spec" id -gn):$(shellsu "$spec" id -Gn)" || true'; \
echo 'real="$(/usr/local/bin/shellsu "$spec" id -un):$(/usr/local/bin/shellsu "$spec" id -gn):$(/usr/local/bin/shellsu "$spec" id -Gn)" || true'; \
echo '[ "$expec" = "$real" ]'; \
} > /usr/local/bin/shellsu-t \
&& chmod +x /usr/local/bin/shellsu-t

# Copy the `shellsu` binary/script into the image
COPY shellsu /usr/local/bin/
RUN ls -la
RUN pwd
COPY shellsu /usr/local/bin/shellsu
RUN chmod +x /usr/local/bin/shellsu
RUN ls -la /usr/local/bin/
RUN echo $PATH
RUN ls -l $(which su)

# Adjust permissions for testing unusual cases
RUN chgrp nobody /usr/local/bin/shellsu \
&& chmod +s /usr/local/bin/shellsu

# Configure an environment for testing
ENV SHELLSU_INSECURE_MODE="I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhäuser Gate. All those moments will be lost in time, like tears in rain. Time to die."
ENV SHELLSU_INSECURE_FLAG="I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhäuser Gate. All those moments will be lost in time, like tears in rain. Time to die."
USER nobody
ENV HOME /omg/really/shellsu/nowhere

# Validate initial state
RUN id
RUN cat /etc/passwd
RUN cat /etc/group

# Test various user/group configurations
RUN shellsu-t 0 "0:0:$(id -G root)" "root:root:$(id -Gn root)"
Expand Down
27 changes: 23 additions & 4 deletions Dockerfile.test-debian
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
FROM debian:bookworm-slim

# Install necessary dependencies
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
file \
bash \
login \
; \
rm -rf /var/lib/apt/lists/*


# Add "nobody" to ALL groups to create edge cases for testing
RUN cut -d: -f1 /etc/group | xargs -rtI'{}' usermod -aG '{}' nobody
# Emulate Alpine's "games" user, which is part of the "users" group
Expand All @@ -13,30 +24,38 @@ RUN { \
echo 'spec="$1"; shift'; \
echo; \
echo 'expec="$1"; shift'; \
echo 'real="$(shellsu "$spec" id -u):$(shellsu "$spec" id -g):$(shellsu "$spec" id -G)"'; \
echo 'real="$(/usr/local/bin/shellsu "$spec" id -u):$(/usr/local/bin/shellsu "$spec" id -g):$(/usr/local/bin/shellsu "$spec" id -G)"'; \
echo '[ "$expec" = "$real" ]'; \
echo; \
echo 'expec="$1"; shift'; \
echo 'real="$(shellsu "$spec" id -un):$(shellsu "$spec" id -gn):$(shellsu "$spec" id -Gn)" || true'; \
echo 'real="$(/usr/local/bin/shellsu "$spec" id -un):$(/usr/local/bin/shellsu "$spec" id -gn):$(/usr/local/bin/shellsu "$spec" id -Gn)" || true'; \
echo '[ "$expec" = "$real" ]'; \
} > /usr/local/bin/shellsu-t \
&& chmod +x /usr/local/bin/shellsu-t

# Copy the `shellsu` binary/script into the image
COPY shellsu /usr/local/bin/
RUN ls -la
RUN pwd
COPY shellsu /usr/local/bin/shellsu
COPY shellsu /usr/local/bin/shellsu
RUN chmod +x /usr/local/bin/shellsu
RUN ls -la /usr/local/bin/
RUN echo $PATH
RUN ls -l $(which su)

# Adjust permissions for testing unusual cases
RUN chgrp nogroup /usr/local/bin/shellsu \
&& chmod +s /usr/local/bin/shellsu

# Configure an environment for testing
ENV SHELLSU_INSECURE_MODE="I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhäuser Gate. All those moments will be lost in time, like tears in rain. Time to die."
ENV SHELLSU_INSECURE_FLAG="I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhäuser Gate. All those moments will be lost in time, like tears in rain. Time to die."
USER nobody
ENV HOME /omg/really/shellsu/nowhere

# Validate initial state
RUN id
RUN cat /etc/passwd
RUN cat /etc/group

# Test various user/group configurations
RUN shellsu-t 0 "0:0:$(id -G root)" "root:root:$(id -Gn root)"
Expand Down
21 changes: 9 additions & 12 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e

# Navigate to the directory containing this script
Expand All @@ -7,20 +7,17 @@ cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
set -x

# Build the Docker image
docker build --pull -t shellsu .
docker build --no-cache --pull -t shellsu .

# Clean up any pre-existing artifacts
rm -f shellsu* SHA256SUMS*

# Extract the `shellsu` binaries from the built image
docker run --rm shellsu sh -c 'cd /go/bin && tar -c shellsu*' | tar -xv
docker run --rm --entrypoint cat shellsu /usr/local/bin/shellsu > shellsu

# Generate SHA256 checksums for the extracted files
sha256sum shellsu* | tee SHA256SUMS
chmod +x shellsu

# Inspect the extracted files
file shellsu*
ls -lFh shellsu* SHA256SUMS*
sha256sum shellsu | tee SHA256SUMS

# Run the built binary to verify functionality
"./shellsu-$(dpkg --print-architecture)" --help
file shellsu
ls -lFh shellsu SHA256SUMS

"./shellsu" --help
2 changes: 1 addition & 1 deletion hub/Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM alpine:3.20

# https://github.com/tianon/shellsu/releases
# https://github.com/dogruis/shellsu/releases
ENV SHELLSU_VERSION 1.17

RUN set -eux; \
Expand Down
2 changes: 1 addition & 1 deletion hub/Dockerfile.debian
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM debian:bookworm-slim

# https://github.com/tianon/shellsu/releases
# https://github.com/dogruis/shellsu/releases
ENV SHELLSU_VERSION 1.17

RUN set -eux; \
Expand Down
115 changes: 71 additions & 44 deletions shellsu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ set -Eeuo pipefail
# Script Metadata
VERSION="1.0"
SCRIPT_NAME="$(basename "$0")"
LICENSE_TEXT="Apache-2.0 (see https://github.com/tianon/gosu)"
LICENSE_TEXT="MIT (see https://github.com/dogruis/shellsu)"

# Helper Functions
usage() {
cat <<EOF
Usage: $SCRIPT_NAME user-spec command [args...]
eg: $SCRIPT_NAME tianon bash
eg: $SCRIPT_NAME dogruis bash
$SCRIPT_NAME nobody:root bash -c 'whoami && id'
$SCRIPT_NAME 1000:1 id
Options:
--help, -h Show this help message
--version, -v Show version information
$SCRIPT_NAME version: $VERSION
$SCRIPT_NAME license: $LICENSE_TEXT
EOF
Expand All @@ -29,61 +33,84 @@ error_exit() {
exit 1
}

# Parse and validate user:group spec
parse_user_spec() {
local spec="$1"
USER="${spec%%:*}"
GROUP="${spec#*:}"
[[ "$USER" == "$GROUP" ]] && GROUP=""

id "$USER" &>/dev/null || error_exit "User '$USER' does not exist."
if [[ -n "$GROUP" ]]; then
getent group "$GROUP" &>/dev/null || error_exit "Group '$GROUP' does not exist."
# Check if the script is insecure (no setuid/setgid bit)
check_insecure() {
if [[ "${SHELLSU_INSECURE_FLAG:-}" != "I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhäuser Gate. All those moments will be lost in time, like tears in rain. Time to die." ]]; then
# Check if the script has setuid or setgid permissions
if [[ -u "$0" ]]; then
error_exit "$0 appears to be installed with the 'setuid' bit set, which is extremely insecure and unsupported! Use 'sudo' or 'su' instead."
elif [[ -g "$0" ]]; then
error_exit "$0 appears to be installed with the 'setgid' bit set, which is not great and unsupported! Use 'sudo' or 'su' instead."
fi
fi
}

# Switch user and group, then execute the command
execute_as_user() {
local user="$1"
local group="$2"
shift 2
local cmd=("$@")
# Function to switch to user and group and execute command
run_cmd_as_user() {
spec="$1"
shift
cmd=("$@")

IFS=':' read -r user group <<< "$spec"

# Resolve the user if numeric
if [[ "$user" =~ ^[0-9]+$ ]]; then
user=$(getent passwd "$user" | cut -d: -f1)
fi

# Resolve the group if numeric
if [[ -n "$group" && "$group" =~ ^[0-9]+$ ]]; then
group=$(getent group "$group" | cut -d: -f1)
fi

# Validate user
if ! id "$user" &>/dev/null; then
error_exit "User '$user' does not exist."
fi

# Resolve group ID if specified
# Validate group
if [[ -n "$group" ]]; then
GROUP_ID=$(getent group "$group" | cut -d: -f3)
sg "$group" <<EOF
exec su -s /bin/bash -c "$(printf '%q ' "${cmd[@]}")" "$user"
EOF
if ! getent group "$group" &>/dev/null; then
error_exit "Group '$group' does not exist."
fi
fi

# Clear HOME environment variable so SetupUser can set it
unset HOME

# Switch to the group first, then the user
# Switch user and execute command without su
if [[ -n "$group" ]]; then
# Set the group and user ID for the command
exec env "USER=$user" "GROUP=$group" "HOME=/home/$user" "LOGNAME=$user" id -u "$user" "${cmd[@]}"
else
exec su -s /bin/bash -c "$(printf '%q ' "${cmd[@]}")" "$user"
# If no group is provided, just switch to the user
exec env "USER=$user" "HOME=/home/$user" "LOGNAME=$user" id -u "$user" "${cmd[@]}"
fi
}

# Main Script Logic
# Main function to execute the script logic
main() {
if [[ "$#" -lt 2 ]]; then
check_insecure

# Handle the command-line arguments
if [[ $# -ge 2 ]]; then
case "$1" in
"--help" | "-h" | "-?")
usage
;;
"--version" | "-v")
version
exit 0
;;
esac
else
usage
exit 1
fi

case "$1" in
--help | -h)
usage
exit 0
;;
--version | -v)
version
exit 0
;;
esac

USER_SPEC="$1"
shift
COMMAND=("$@")

parse_user_spec "$USER_SPEC"
execute_as_user "$USER" "$GROUP" "${COMMAND[@]}"
# Call the user switching and command execution function
run_cmd_as_user "$@"
}

# Call the main function
main "$@"
Loading

0 comments on commit 2de655b

Please sign in to comment.