Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
if: ${{ github.event_name == 'release' || (github.event_name == 'pull_request' && github.event.label.name == 'build-release-container') }}
steps:
- name: Setup BuildX
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Checkout
uses: actions/checkout@v3
with:
Expand Down Expand Up @@ -40,6 +40,7 @@ jobs:
cache-to: type=gha,mode=max
build-contexts: |
git=.git
docker=docker
target: run
push: ${{ github.event_name == 'release' && 'true' || 'false' }}
tags: ${{ steps.meta.outputs.tags }}
Expand Down
174 changes: 115 additions & 59 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
# syntax=docker/dockerfile:1.4-labs

ARG ALPINE_VERSION=3.17

FROM alpine:${ALPINE_VERSION} as rust-base

RUN apk add --no-cache ca-certificates gcc musl-dev

ENV RUSTUP_HOME=/usr/local/rustup
ENV CARGO_HOME=/usr/local/cargo
ENV PATH=/usr/local/cargo/bin:${PATH}

ARG RUSTUP_VERSION=1.25.1
ARG RUST_VERSION=1.61.0
ARG RUST_ARCH=x86_64-unknown-linux-musl

# https://github.com/sfackler/rust-openssl/issues/1462
ENV RUSTFLAGS="-Ctarget-feature=-crt-static"

FROM rust:${RUST_VERSION} as dev-planner
ADD --chmod=755 https://static.rust-lang.org/rustup/archive/${RUSTUP_VERSION}/${RUST_ARCH}/rustup-init /tmp
RUN /tmp/rustup-init \
-y \
--no-modify-path \
--profile minimal \
--default-toolchain ${RUST_VERSION} \
--default-host ${RUST_ARCH}

FROM rust-base as dev-planner

RUN cargo install --version 0.1.35 cargo-chef

Expand All @@ -12,37 +35,32 @@ COPY . .
ENV CARGO_TARGET_DIR=/opt/cargo-target
RUN cargo chef prepare --recipe-path recipe.json

FROM rust:${RUST_VERSION} as dev
FROM rust-base as dev

RUN <<EOF
set -e
apt-get update
apt-get remove --yes git
apt-get install --yes --no-install-recommends \
RUN apk add --no-cache \
zlib-dev \
openssl-dev \
curl-dev

WORKDIR /usr/src/josh
RUN rustup component add rustfmt
RUN cargo install --version 0.1.35 cargo-chef
RUN cargo install --verbose --version 0.10.0 graphql_client_cli

RUN apk add --no-cache \
bash \
curl \
cmake \
gcc \
make \
libz-dev \
libssl-dev \
libcurl4-openssl-dev \
libexpat1-dev \
expat-dev \
gettext \
python3 \
python3-pip \
gettext \
python3-dev \
py3-pip \
tree \
autoconf \
libgit2-dev \
psmisc
rm -rf /var/lib/apt/lists/*
EOF

ARG GO_VERSION=1.19.3
WORKDIR /opt
RUN <<EOF
set -e
wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz
tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
EOF
ENV PATH=${PATH}:/usr/local/go/bin

ARG GIT_VERSION=2.36.1
WORKDIR /usr/src/git
Expand All @@ -52,32 +70,25 @@ wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-${GIT_VERSION}.tar
tar --extract --gzip --file git-${GIT_VERSION}.tar.gz
cd git-${GIT_VERSION}
make configure
./configure --without-tcltk --prefix=/opt/git-install --exec-prefix=/opt/git-install
./configure \
--without-tcltk \
--prefix=/opt/git-install \
--exec-prefix=/opt/git-install
make -j$(nproc)
make install
EOF

RUN mkdir /opt/git-install/etc

ENV PATH=${PATH}:/opt/git-install/bin
RUN mkdir /opt/git-install/etc
RUN git config -f /opt/git-install/etc/gitconfig --add safe.directory "*"

ARG CRAM_VERSION=d245cca
ARG PYGIT2_VERSION=1.9.1
ARG PYGIT2_VERSION=1.11.1
RUN pip3 install \
git+https://github.com/brodie/cram.git@${CRAM_VERSION} \
pygit2==${PYGIT2_VERSION}

RUN <<EOF
curl --fail --show-error --silent --location https://deb.nodesource.com/setup_18.x | bash - && \
apt-get install --yes nodejs
EOF

WORKDIR /usr/src/josh
RUN rustup component add rustfmt
RUN cargo install --version 0.1.35 cargo-chef
RUN cargo install --verbose --version 0.10.0 graphql_client_cli

RUN apk add --no-cache go nodejs npm openssh-client

FROM dev as dev-local

Expand All @@ -98,14 +109,15 @@ ARG USER_UID
RUN \
if [ ! $(getent group ${USER_GID}) ] ; then \
addgroup \
--gid ${USER_GID} dev ; \
-g ${USER_GID} dev ; \
fi

RUN adduser \
--uid ${USER_UID} \
--gid ${USER_GID} \
--disabled-login \
--gecos '' \
-u ${USER_UID} \
-G $(getent group ${USER_GID} | cut -d: -f1) \
-D \
-H \
-g '' \
dev

FROM dev as dev-ci
Expand All @@ -120,28 +132,72 @@ RUN cd josh-ui && npm install

FROM dev as build

WORKDIR /usr/src/josh
COPY . .
RUN cargo build -p josh-ui --release
RUN --mount=target=.git,from=git \
cargo build -p josh-proxy --release

FROM debian:bullseye as run

RUN <<EOF
apt-get update
apt-get install --yes --no-install-recommends \
zlib1g \
libexpat1 \
libcurl4 \
ca-certificates
rm -rf /var/lib/apt/lists/*
EOF
cargo build -p josh-proxy -p josh-ssh-shell --release

ARG ALPINE_VERSION
FROM alpine:${ALPINE_VERSION} as run

RUN apk add --no-cache \
zlib \
openssl \
libexpat \
libgit2 \
libgcc \
libcurl \
ca-certificates \
openssh \
bash \
xz \
shadow \
gettext

COPY --from=dev --link=false /opt/git-install /opt/git-install
ENV PATH=${PATH}:/opt/git-install/bin

COPY --from=build --link=false /usr/src/josh/target/release/josh-proxy /usr/bin/
COPY --from=build --link=false /usr/src/josh/run-josh.sh /usr/bin/
COPY --from=build --link=false /usr/src/josh/target/release/josh-ssh-shell /usr/bin/
COPY --from=build --link=false /usr/src/josh/static/ /josh/static/

CMD sh /usr/bin/run-josh.sh
ARG S6_OVERLAY_VERSION=3.1.2.1
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz

ARG GIT_GID_UID=2001

RUN addgroup -g ${GIT_GID_UID} git
RUN adduser \
-h /home/git \
-s /usr/bin/josh-ssh-shell \
-G git \
-D \
-u ${GIT_GID_UID} \
git

# https://unix.stackexchange.com/a/193131/336647
RUN usermod -p '*' git

COPY --from=docker --link=false etc/ssh/sshd_config.template /etc/ssh/sshd_config.template

ARG RC6_D=/etc/s6-overlay/s6-rc.d

COPY --from=docker --link=false \
josh-auth-key \
josh-ensure-dir \
josh-ensure-mode \
josh-ensure-owner \
/opt/josh-scripts/
COPY --from=docker --link=false s6-rc.d/. ${RC6_D}/
COPY --from=docker --link=false finish ${RC6_D}/josh/
COPY --from=docker --link=false finish ${RC6_D}/sshd/

WORKDIR /
ENV S6_KEEP_ENV=1
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=2
ENV PATH=${PATH}:/opt/josh-scripts
ENTRYPOINT ["/init"]
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Base server parameters

Port 23186
Port ${JOSH_SSH_PORT}
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::
Expand Down Expand Up @@ -43,5 +43,5 @@ AllowUsers git
PubkeyAuthentication yes
AuthorizedKeysFile none

AuthorizedKeysCommand /opt/scripts/josh-auth-key.sh %t %k
AuthorizedKeysCommand /opt/josh-scripts/josh-auth-key %t %k
AuthorizedKeysCommandUser nobody
File renamed without changes.
File renamed without changes.
17 changes: 17 additions & 0 deletions docker/josh-ensure-dir
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/command/execlineb

# Usage: josh-ensure-dir -p path program
# Ensures that `path` exists, and then executes `program`

elgetopt "p:"
multisubstitute
{
importas -i path ELGETOPT_p
elgetpositionals
}
foreground
{
if { s6-test ! -d ${path} }
mkdir -p ${path}
}
$@
21 changes: 21 additions & 0 deletions docker/josh-ensure-mode
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/command/execlineb

# Usage: josh-ensure-mode -p path -m mode program
# Ensures that `path` permissions equal `mode`, and then executes `program`
# Permissions should be in `NNN` format where N is an octal literal

elgetopt "p:m:"
multisubstitute
{
importas -i path ELGETOPT_p
importas -i mode ELGETOPT_m
elgetpositionals
}
backtick CURRENT_MODE { stat -c "%a" ${path} }
importas current_mode CURRENT_MODE
foreground
{
if { s6-test ${mode} != ${current_mode} }
chmod ${mode} ${target}
}
$@
21 changes: 21 additions & 0 deletions docker/josh-ensure-owner
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/command/execlineb

# Usage: josh-ensure-owner -p path -o owner program
# Ensures that `path` is owned by `owner`, and then executes `program`
# `owner` should be specified as `user:group`

elgetopt "p:o:"
multisubstitute
{
importas -i path ELGETOPT_p
importas -i owner ELGETOPT_o
elgetpositionals
}
backtick CURRENT_OWNER { stat -c "%G:%U" ${path} }
importas current_owner CURRENT_OWNER
foreground
{
if { s6-test ${owner} != ${current_owner} }
chown ${owner} ${path}
}
$@
1 change: 1 addition & 0 deletions docker/s6-rc.d/josh-create-local/type
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
oneshot
7 changes: 7 additions & 0 deletions docker/s6-rc.d/josh-create-local/up
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/command/execlineb

define data_dir /data/git

josh-ensure-dir -p ${data_dir}
josh-ensure-owner -p ${data_dir} -o git:git
josh-ensure-mode -p ${data_dir} -m 640
1 change: 1 addition & 0 deletions docker/s6-rc.d/josh-generate-keys/type
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
oneshot
28 changes: 28 additions & 0 deletions docker/s6-rc.d/josh-generate-keys/up
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/command/execlineb

define key_dir /data/keys/.ssh
define key_type ed25519

josh-ensure-dir -p ${key_dir}
josh-ensure-owner -p ${key_dir} -o git:git
josh-ensure-mode -p ${key_dir} -m 700

foreground
{
if { s6-test ( ! -f ${key_dir}/id_${key_type} ) -o ( ! -f ${key_dir}/id_${key_type}.pub ) }
foreground
{
fdmove -c 1 2
echo "Generating new SSH server key"
}
foreground
{
ssh-keygen -t ${key_type} -N "" -f ${key_dir}/id_${key_type} -C git
}

josh-ensure-owner -p ${key_dir}/id_${key_type} -o git:git
josh-ensure-mode -p ${key_dir}/id_${key_type} -m 600

josh-ensure-owner -p ${key_dir}/id_${key_type}.pub -o git:git
josh-ensure-mode -p ${key_dir}/id_${key_type}.pub -m 644
}
2 changes: 2 additions & 0 deletions docker/s6-rc.d/josh/dependencies
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
josh-generate-keys
josh-create-local
21 changes: 21 additions & 0 deletions docker/s6-rc.d/josh/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/command/execlineb -S0

importas josh_http_port JOSH_HTTP_PORT
importas josh_remote_http JOSH_REMOTE
importas josh_remote_ssh JOSH_REMOTE_SSH
s6-setuidgid git
backtick RUST_BACKTRACE { echo 1 }
backtick HOME { homeof git }
foreground
{
# importas returns "no word" when the variable isn't set,
# and if it's used in the middle of a word during expansion,
# the whole word will be deleted. Therefore it's okay to use
# potentially empty variables with arguments
josh-proxy \
--gc \
--local=/data/git/ \
--port=${josh_http_port} \
--remote=${josh_remote_http} \
--remote=${josh_remote_ssh}
}
Loading