Merge pull request #218 from thockin/e2e_ssh
Support ssh as arbitrary users, add e2e for SSH
This commit is contained in:
commit
705be50aa0
|
|
@ -22,7 +22,11 @@ RUN apt-get update \
|
||||||
openssh-client \
|
openssh-client \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# By default we will run as this user...
|
||||||
RUN echo "git-sync:x:65533:65533::/tmp:/sbin/nologin" >> /etc/passwd
|
RUN echo "git-sync:x:65533:65533::/tmp:/sbin/nologin" >> /etc/passwd
|
||||||
|
# ...but the user might choose a different UID and pass --add-user
|
||||||
|
# which needs to be able to write to /etc/passwd.
|
||||||
|
RUN chmod 0666 /etc/passwd
|
||||||
|
|
||||||
ADD bin/{ARG_OS}_{ARG_ARCH}/{ARG_BIN} /{ARG_BIN}
|
ADD bin/{ARG_OS}_{ARG_ARCH}/{ARG_BIN} /{ARG_BIN}
|
||||||
|
|
||||||
|
|
|
||||||
3
Makefile
3
Makefile
|
|
@ -177,6 +177,9 @@ test: $(BUILD_DIRS)
|
||||||
"
|
"
|
||||||
@./test_e2e.sh
|
@./test_e2e.sh
|
||||||
|
|
||||||
|
test-tools:
|
||||||
|
@docker build -t $(REGISTRY)/test/test-sshd _test_tools/sshd
|
||||||
|
|
||||||
$(BUILD_DIRS):
|
$(BUILD_DIRS):
|
||||||
@mkdir -p $@
|
@mkdir -p $@
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Stolen from https://github.com/linuxkit/linuxkit/tree/master/pkg/sshd/
|
||||||
|
|
||||||
|
FROM alpine AS base
|
||||||
|
|
||||||
|
RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/
|
||||||
|
RUN apk add --no-cache --initdb -p /out \
|
||||||
|
alpine-baselayout \
|
||||||
|
apk-tools \
|
||||||
|
busybox \
|
||||||
|
ca-certificates \
|
||||||
|
git \
|
||||||
|
musl \
|
||||||
|
openssh-server \
|
||||||
|
tini \
|
||||||
|
util-linux \
|
||||||
|
wireguard-tools \
|
||||||
|
&& true
|
||||||
|
|
||||||
|
###############
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
ENTRYPOINT []
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
COPY --from=base /out/ /
|
||||||
|
|
||||||
|
RUN mkdir -p /etc/ssh && rm /etc/motd
|
||||||
|
COPY sshd_config /etc/ssh/
|
||||||
|
COPY sshd.sh /
|
||||||
|
|
||||||
|
# Callers should mount a .ssh directory here. Our sshd.sh will copy it and
|
||||||
|
# manage permissions.
|
||||||
|
VOLUME /dot_ssh
|
||||||
|
|
||||||
|
# Callers can SSH as user "test"
|
||||||
|
RUN echo "test:x:65533:65533::/home/test:/usr/bin/git-shell" >> /etc/passwd
|
||||||
|
|
||||||
|
CMD ["/sbin/tini", "/sshd.sh"]
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
# An SSHD for tests git-over-ssh
|
||||||
|
|
||||||
|
DO NOT USE THIS FOR ANYTHING BUT TESTING GIT OVER SSH!!!
|
||||||
|
|
||||||
|
## How to use it
|
||||||
|
|
||||||
|
Build yourself a test image. We use example.com so you can't accidentally push
|
||||||
|
it.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker build -t example.com/test/test-sshd .
|
||||||
|
...lots of output...
|
||||||
|
Successfully tagged example.com/test/test-sshd:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate keys for a fake user named "test".
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir -p dot_ssh
|
||||||
|
|
||||||
|
$ ssh-keygen -f dot_ssh/id_test -P ""
|
||||||
|
Generating public/private rsa key pair.
|
||||||
|
Your identification has been saved in dot_ssh/id_test.
|
||||||
|
Your public key has been saved in dot_ssh/id_test.pub.
|
||||||
|
...lots of output...
|
||||||
|
|
||||||
|
$ cat dot_ssh/id_test.pub > dot_ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
Run it.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker run -d -v $(pwd)/dot_ssh:/dot_ssh:ro example.com/test/test-sshd
|
||||||
|
6d05b4111b03c66907031e3cd7587763f0e4fab6c50fac33c4a8284732b448ae
|
||||||
|
```
|
||||||
|
|
||||||
|
Find your IP.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker inspect 6d05b4111b03c66907031e3cd7587763f0e4fab6c50fac33c4a8284732b448ae | jq -r .[0].NetworkSettings.IPAddress
|
||||||
|
192.168.1.2
|
||||||
|
```
|
||||||
|
|
||||||
|
SSH to it.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ssh -i dot_ssh/id_test -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null test@192.168.9.2
|
||||||
|
Warning: Permanently added '192.168.9.2' (ECDSA) to the list of known hosts.
|
||||||
|
fatal: Interactive git shell is not enabled.
|
||||||
|
hint: ~/git-shell-commands should exist and have read and execute access.
|
||||||
|
Connection to 192.168.9.2 closed.
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
KEYS=$(find /etc/ssh -name 'ssh_host_*_key')
|
||||||
|
[ -z "$KEYS" ] && ssh-keygen -A >/dev/null 2>/dev/null
|
||||||
|
|
||||||
|
# Copy creds for the test user, so we don't have to bake them into the image
|
||||||
|
# and so users don't have to manage permissions.
|
||||||
|
mkdir -p /home/test/.ssh
|
||||||
|
cp -a /dot_ssh/* /home/test/.ssh
|
||||||
|
chown -R test /home/test/.ssh
|
||||||
|
chmod 0700 /home/test/.ssh
|
||||||
|
chmod 0600 /home/test/.ssh/*
|
||||||
|
|
||||||
|
exec /usr/sbin/sshd -D -e
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
# This is the sshd server system-wide configuration file. See
|
||||||
|
# sshd_config(5) for more information.
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# To disable tunneled clear text passwords, change to no here!
|
||||||
|
PasswordAuthentication no
|
||||||
|
|
||||||
|
# Change to no to disable s/key passwords
|
||||||
|
ChallengeResponseAuthentication no
|
||||||
|
|
@ -94,6 +94,8 @@ var flSSHKnownHosts = flag.Bool("ssh-known-hosts", envBool("GIT_KNOWN_HOSTS", tr
|
||||||
"enable SSH known_hosts verification")
|
"enable SSH known_hosts verification")
|
||||||
var flSSHKnownHostsFile = flag.String("ssh-known-hosts-file", envString("GIT_SSH_KNOWN_HOSTS_FILE", "/etc/git-secret/known_hosts"),
|
var flSSHKnownHostsFile = flag.String("ssh-known-hosts-file", envString("GIT_SSH_KNOWN_HOSTS_FILE", "/etc/git-secret/known_hosts"),
|
||||||
"the known_hosts file to use")
|
"the known_hosts file to use")
|
||||||
|
var flAddUser = flag.Bool("add-user", envBool("GIT_SYNC_ADD_USER", false),
|
||||||
|
"add a record to /etc/passwd for the current UID/GID (needed to use SSH with a different UID)")
|
||||||
|
|
||||||
var flCookieFile = flag.Bool("cookie-file", envBool("GIT_COOKIE_FILE", false),
|
var flCookieFile = flag.Bool("cookie-file", envBool("GIT_COOKIE_FILE", false),
|
||||||
"use git cookiefile")
|
"use git cookiefile")
|
||||||
|
|
@ -241,6 +243,13 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *flAddUser {
|
||||||
|
if err := addUser(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: can't write to /etc/passwd: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This context is used only for git credentials initialization. There are no long-running operations like
|
// This context is used only for git credentials initialization. There are no long-running operations like
|
||||||
// `git clone`, so initTimeout set to 30 seconds should be enough.
|
// `git clone`, so initTimeout set to 30 seconds should be enough.
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), initTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), initTimeout)
|
||||||
|
|
@ -390,6 +399,29 @@ func sleepForever() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put the current UID/GID into /etc/passwd so SSH can look it up. This
|
||||||
|
// assumes that we have the permissions to write to it.
|
||||||
|
func addUser() error {
|
||||||
|
home := os.Getenv("HOME")
|
||||||
|
if home == "" {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't get current working directory: %v", err)
|
||||||
|
}
|
||||||
|
home = cwd
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.OpenFile("/etc/passwd", os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
str := fmt.Sprintf("git-sync:x:%d:%d::%s:/sbin/nologin\n", os.Getuid(), os.Getgid(), home)
|
||||||
|
_, err = f.WriteString(str)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// updateSymlink atomically swaps the symlink to point at the specified
|
// updateSymlink atomically swaps the symlink to point at the specified
|
||||||
// directory and cleans up the previous worktree. If there was a previous
|
// directory and cleans up the previous worktree. If there was a previous
|
||||||
// worktree, this returns the path to it.
|
// worktree, this returns the path to it.
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ Secret (e.g. "git-creds" used in both above examples).
|
||||||
- name: git-secret
|
- name: git-secret
|
||||||
secret:
|
secret:
|
||||||
secretName: git-creds
|
secretName: git-creds
|
||||||
defaultMode: 288 # 0440
|
defaultMode: 0400
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -103,6 +103,11 @@ that this is a Pod-wide setting, unlike the container `securityContext` above.
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want git-sync to run as a different (non-root) UID and GID, you can
|
||||||
|
change these last blocks to any UID/GID you like. SSH demands that the current
|
||||||
|
UID be present in /etc/passwd, so in this case you will need to add the
|
||||||
|
`--add-user` flag to git-sync's args array.
|
||||||
|
|
||||||
**Note:** Kubernetes mounts the Secret with permissions 0444 by default (not
|
**Note:** Kubernetes mounts the Secret with permissions 0444 by default (not
|
||||||
restrictive enough to be used as an SSH key), so make sure you set the
|
restrictive enough to be used as an SSH key), so make sure you set the
|
||||||
`defaultMode`.
|
`defaultMode`.
|
||||||
|
|
@ -130,7 +135,7 @@ spec:
|
||||||
- name: git-secret
|
- name: git-secret
|
||||||
secret:
|
secret:
|
||||||
secretName: git-creds
|
secretName: git-creds
|
||||||
defaultMode: 0440
|
defaultMode: 0400
|
||||||
containers:
|
containers:
|
||||||
- name: git-sync
|
- name: git-sync
|
||||||
image: k8s.gcr.io/git-sync:v3.1.1
|
image: k8s.gcr.io/git-sync:v3.1.1
|
||||||
|
|
|
||||||
42
test_e2e.sh
42
test_e2e.sh
|
|
@ -66,6 +66,7 @@ function freencport() {
|
||||||
|
|
||||||
# Build it
|
# Build it
|
||||||
make container REGISTRY=e2e VERSION=$(make -s version)
|
make container REGISTRY=e2e VERSION=$(make -s version)
|
||||||
|
make test-tools REGISTRY=e2e
|
||||||
|
|
||||||
RUNID="${RANDOM}${RANDOM}"
|
RUNID="${RANDOM}${RANDOM}"
|
||||||
DIR=""
|
DIR=""
|
||||||
|
|
@ -95,6 +96,12 @@ function clean_root() {
|
||||||
mkdir -p "$ROOT"
|
mkdir -p "$ROOT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Init SSH for test cases.
|
||||||
|
DOT_SSH="$DIR/dot_ssh"
|
||||||
|
mkdir -p "$DOT_SSH"
|
||||||
|
ssh-keygen -f "$DOT_SSH/id_test" -P "" >/dev/null
|
||||||
|
cat "$DOT_SSH/id_test.pub" > "$DOT_SSH/authorized_keys"
|
||||||
|
|
||||||
function finish() {
|
function finish() {
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "The directory $DIR was not removed as it contains"\
|
echo "The directory $DIR was not removed as it contains"\
|
||||||
|
|
@ -118,8 +125,10 @@ function GIT_SYNC() {
|
||||||
-v "$DIR":"$DIR":rw \
|
-v "$DIR":"$DIR":rw \
|
||||||
-v "$(pwd)/slow_git.sh":"$SLOW_GIT":ro \
|
-v "$(pwd)/slow_git.sh":"$SLOW_GIT":ro \
|
||||||
-v "$(pwd)/askpass_git.sh":"$ASKPASS_GIT":ro \
|
-v "$(pwd)/askpass_git.sh":"$ASKPASS_GIT":ro \
|
||||||
|
-v "$DOT_SSH/id_test":"/etc/git-secret/ssh":ro \
|
||||||
--env XDG_CONFIG_HOME=$DIR \
|
--env XDG_CONFIG_HOME=$DIR \
|
||||||
e2e/git-sync:$(make -s version)__$(go env GOOS)_$(go env GOARCH) \
|
e2e/git-sync:$(make -s version)__$(go env GOOS)_$(go env GOARCH) \
|
||||||
|
--add-user \
|
||||||
"$@"
|
"$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -963,5 +972,38 @@ fi
|
||||||
rm -rf $SUBMODULE
|
rm -rf $SUBMODULE
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Test SSH
|
||||||
|
##############################################
|
||||||
|
testcase "ssh"
|
||||||
|
echo "$TESTCASE" > "$REPO"/file
|
||||||
|
# Run a git-over-SSH server
|
||||||
|
CTR=$(docker run \
|
||||||
|
-d \
|
||||||
|
--rm \
|
||||||
|
--label git-sync-e2e="$RUNID" \
|
||||||
|
-v "$DOT_SSH":/dot_ssh:ro \
|
||||||
|
-v "$REPO":/src:ro \
|
||||||
|
e2e/test/test-sshd)
|
||||||
|
IP=$(docker inspect "$CTR" | jq -r .[0].NetworkSettings.IPAddress)
|
||||||
|
git -C "$REPO" commit -qam "$TESTCASE"
|
||||||
|
GIT_SYNC \
|
||||||
|
--logtostderr \
|
||||||
|
--v=5 \
|
||||||
|
--one-time \
|
||||||
|
--ssh \
|
||||||
|
--ssh-known-hosts=false \
|
||||||
|
--repo="test@$IP:/src" \
|
||||||
|
--branch=master \
|
||||||
|
--rev=HEAD \
|
||||||
|
--root="$ROOT" \
|
||||||
|
--dest="link" \
|
||||||
|
> "$DIR"/log."$TESTCASE" 2>&1
|
||||||
|
assert_link_exists "$ROOT"/link
|
||||||
|
assert_file_exists "$ROOT"/link/file
|
||||||
|
assert_file_eq "$ROOT"/link/file "$TESTCASE"
|
||||||
|
# Wrap up
|
||||||
|
pass
|
||||||
|
|
||||||
echo "cleaning up $DIR"
|
echo "cleaning up $DIR"
|
||||||
rm -rf "$DIR"
|
rm -rf "$DIR"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue