Skip to content

Commit

Permalink
switch user with -a, docker adds entrypoint script, #876
Browse files Browse the repository at this point in the history
  • Loading branch information
zonyitoo committed Jun 28, 2022
1 parent fd4f1f1 commit 64ad58b
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 31 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/build-docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
bin:
bin:
- ssserver
- sslocal
steps:
Expand All @@ -17,7 +17,7 @@ jobs:
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
Expand All @@ -34,3 +34,5 @@ jobs:
target: ${{ matrix.bin }}
tags: ${{ steps.metadata.outputs.tags }}
push: true
context: .
file: ./docker/Dockerfile
15 changes: 0 additions & 15 deletions Dockerfile.v2ray

This file was deleted.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ docker pull ghcr.io/shadowsocks/ssserver-rust:latest
If you want to build the Docker image yourself, you need to use the [BuildX](https://docs.docker.com/buildx/working-with-buildx/).

```bash
docker buildx build -t shadowsocks/ssserver-rust:latest -t shadowsocks/ssserver-rust:v1.11.1 --target ssserver .
docker buildx build -t shadowsocks/sslocal-rust:latest -t shadowsocks/sslocal-rust:v1.11.1 --target sslocal .
docker buildx build -t shadowsocks/ssserver-rust:latest -t shadowsocks/ssserver-rust:v1.11.1 --target ssserver ./docker
docker buildx build -t shadowsocks/sslocal-rust:latest -t shadowsocks/sslocal-rust:v1.11.1 --target sslocal ./docker
```

#### Run the container
Expand Down
20 changes: 11 additions & 9 deletions Dockerfile → docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ ADD . .

RUN rustup install nightly && rustup default nightly && \
case "$TARGETARCH" in \
"386") \
"386") \
RUST_TARGET="i686-unknown-linux-musl" \
MUSL="i686-linux-musl" \
;; \
;; \
"amd64") \
RUST_TARGET="x86_64-unknown-linux-musl" \
MUSL="x86_64-linux-musl" \
;; \
;; \
"arm64") \
RUST_TARGET="aarch64-unknown-linux-musl" \
MUSL="aarch64-linux-musl" \
;; \
;; \
*) \
echo "Doesn't support $TARGETARCH architecture" \
exit 1 \
;; \
;; \
esac && \
wget -qO- "https://musl.cc/$MUSL-cross.tgz" | tar -xzC /root/ && \
CC=/root/$MUSL-cross/bin/$MUSL-gcc && \
Expand All @@ -36,19 +36,21 @@ RUN rustup install nightly && rustup default nightly && \
FROM alpine:3.14 AS sslocal

COPY --from=build /root/shadowsocks-rust/target/release/sslocal /usr/bin

COPY --from=build /root/shadowsocks-rust/examples/config.json /etc/shadowsocks-rust/
COPY --from=build /root/shadowsocks-rust/docker/docker-entrypoint.sh /

USER nobody

ENTRYPOINT [ "sslocal", "--log-without-time", "-c", "/etc/shadowsocks-rust/config.json" ]
ENTRYPOINT [ "/docker-entrypoint.sh" ]
CMD [ "sslocal", "--log-without-time", "-c", "/etc/shadowsocks-rust/config.json" ]

FROM alpine:3.14 AS ssserver

COPY --from=build /root/shadowsocks-rust/target/release/ssserver /usr/bin

COPY --from=build /root/shadowsocks-rust/examples/config.json /etc/shadowsocks-rust/
COPY --from=build /root/shadowsocks-rust/docker/docker-entrypoint.sh /

USER nobody

ENTRYPOINT [ "ssserver", "--log-without-time", "-c", "/etc/shadowsocks-rust/config.json" ]
ENTRYPOINT [ "/docker-entrypoint.sh" ]
CMD [ "ssserver", "--log-without-time", "-c", "/etc/shadowsocks-rust/config.json" ]
16 changes: 16 additions & 0 deletions docker/Dockerfile.v2ray
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM ghcr.io/shadowsocks/ssserver-rust:latest

USER root

RUN cd /tmp && \
TAG=$(wget -qO- https://api.github.com/repos/shadowsocks/v2ray-plugin/releases/latest | grep tag_name | cut -d '"' -f4) && \
wget https://github.com/shadowsocks/v2ray-plugin/releases/download/$TAG/v2ray-plugin-linux-amd64-$TAG.tar.gz && \
tar -xf *.gz && \
rm *.gz && \
mv v2ray* /usr/bin/v2ray-plugin && \
chmod +x /usr/bin/v2ray-plugin

USER nobody

ENTRYPOINT [ "/docker-entrypoint.sh" ]
CMD [ "ssserver", "--log-without-time", "-c", "/etc/shadowsocks-rust/config.json" ]
38 changes: 38 additions & 0 deletions docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh
# vim:sw=4:ts=4:et

set -e

if [ -z "${SS_ENTRYPOINT_QUIET_LOGS:-}" ]; then
exec 3>&1
else
exec 3>/dev/null
fi

if [ "$1" = "sslocal" -o "$1" = "ssserver" -o "$1" = "ssmanager" -o "$1" = "ssservice" ]; then
if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration"

echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/"
find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do
case "$f" in
*.sh)
if [ -x "$f" ]; then
echo >&3 "$0: Launching $f";
"$f"
else
# warn on shell scripts without exec bit
echo >&3 "$0: Ignoring $f, not executable";
fi
;;
*) echo >&3 "$0: Ignoring $f";;
esac
done

echo >&3 "$0: Configuration complete; ready for start up"
else
echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration"
fi
fi

exec "$@"
20 changes: 19 additions & 1 deletion src/service/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ use shadowsocks_service::{
use crate::logging;
use crate::{
config::{Config as ServiceConfig, RuntimeMode},
monitor, validator,
monitor,
validator,
};

/// Defines command line options
Expand Down Expand Up @@ -371,6 +372,17 @@ pub fn define_command_line_options(mut app: Command<'_>) -> Command<'_> {
);
}

#[cfg(unix)]
{
app = app.arg(
Arg::new("USER")
.long("user")
.short('a')
.takes_value(true)
.help("Run as another user"),
);
}

app
}

Expand Down Expand Up @@ -776,6 +788,12 @@ pub fn main(matches: &ArgMatches) -> ExitCode {
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
}

#[cfg(unix)]
if let Some(uname) = matches.value_of("USER") {
crate::sys::run_as_user(uname);
}
crate::sys::check_run_from_root();

info!("shadowsocks local {} build {}", crate::VERSION, crate::BUILD_TIME);

let mut builder = match service_config.runtime.mode {
Expand Down
20 changes: 19 additions & 1 deletion src/service/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use shadowsocks_service::{
use crate::logging;
use crate::{
config::{Config as ServiceConfig, RuntimeMode},
monitor, validator,
monitor,
validator,
};

/// Defines command line options
Expand Down Expand Up @@ -204,6 +205,17 @@ pub fn define_command_line_options(mut app: Command<'_>) -> Command<'_> {
);
}

#[cfg(unix)]
{
app = app.arg(
Arg::new("USER")
.long("user")
.short('a')
.takes_value(true)
.help("Run as another user"),
);
}

app
}

Expand Down Expand Up @@ -448,6 +460,12 @@ pub fn main(matches: &ArgMatches) -> ExitCode {
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
}

#[cfg(unix)]
if let Some(uname) = matches.value_of("USER") {
crate::sys::run_as_user(uname);
}
crate::sys::check_run_from_root();

info!("shadowsocks manager {} build {}", crate::VERSION, crate::BUILD_TIME);

let mut worker_count = 1;
Expand Down
20 changes: 19 additions & 1 deletion src/service/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ use shadowsocks_service::{
use crate::logging;
use crate::{
config::{Config as ServiceConfig, RuntimeMode},
monitor, validator,
monitor,
validator,
};

/// Defines command line options
Expand Down Expand Up @@ -216,6 +217,17 @@ pub fn define_command_line_options(mut app: Command<'_>) -> Command<'_> {
);
}

#[cfg(unix)]
{
app = app.arg(
Arg::new("USER")
.long("user")
.short('a')
.takes_value(true)
.help("Run as another user"),
);
}

app
}

Expand Down Expand Up @@ -461,6 +473,12 @@ pub fn main(matches: &ArgMatches) -> ExitCode {
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
}

#[cfg(unix)]
if let Some(uname) = matches.value_of("USER") {
crate::sys::run_as_user(uname);
}
crate::sys::check_run_from_root();

info!("shadowsocks server {} build {}", crate::VERSION, crate::BUILD_TIME);

let mut worker_count = 1;
Expand Down
78 changes: 78 additions & 0 deletions src/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,81 @@ pub fn adjust_nofile() {
}
}
}

/// setuid(), setgid() for a specific user or uid
#[cfg(unix)]
pub fn run_as_user(uname: &str) {
use log::warn;
use std::{
ffi::{CStr, CString},
io::Error,
};

unsafe {
let pwd = match uname.parse::<u32>() {
Ok(uid) => libc::getpwuid(uid),
Err(..) => {
let uname = CString::new(uname).expect("username");
libc::getpwnam(uname.as_ptr())
}
};

if pwd.is_null() {
warn!("user {} not found", uname);
return;
}

let pwd = &*pwd;

// setgid first, because we may not allowed to do it anymore after setuid
if libc::setgid(pwd.pw_gid as libc::gid_t) != 0 {
let err = Error::last_os_error();

warn!(
"could not change group id to user {:?}'s gid: {}, uid: {}, error: {}",
CStr::from_ptr(pwd.pw_name),
pwd.pw_gid,
pwd.pw_uid,
err
);
return;
}

if libc::initgroups(pwd.pw_name, pwd.pw_gid.try_into().unwrap()) != 0 {
let err = Error::last_os_error();
warn!(
"could not change supplementary groups to user {:?}'s gid: {}, uid: {}, error: {}",
CStr::from_ptr(pwd.pw_name),
pwd.pw_gid,
pwd.pw_uid,
err
);
return;
}

if libc::setuid(pwd.pw_uid) != 0 {
let err = Error::last_os_error();
warn!(
"could not change user id to user {:?}'s gid: {}, uid: {}, error: {}",
CStr::from_ptr(pwd.pw_name),
pwd.pw_gid,
pwd.pw_uid,
err
);
return;
}
}
}

/// Check if running from a root user
#[inline(always)]
pub fn check_run_from_root() {
#[cfg(unix)]
unsafe {
use log::warn;

if libc::geteuid() == 0 {
warn!("running from root user");
}
}
}

0 comments on commit 64ad58b

Please sign in to comment.