Skip to content
Open
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
6 changes: 5 additions & 1 deletion docker.Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ shellcheck: build_shell_validate_image ## run shellcheck validation
docker run -ti --rm $(ENVVARS) $(MOUNTS) $(VALIDATE_IMAGE_NAME) make shellcheck

.PHONY: test-e2e ## run e2e tests
test-e2e: test-e2e-non-experimental test-e2e-experimental test-e2e-connhelper-ssh
test-e2e: test-e2e-non-experimental test-e2e-experimental test-e2e-connhelper-ssh test-e2e-ssh-withpassword

.PHONY: test-e2e-experimental
test-e2e-experimental: build_e2e_image
Expand All @@ -119,6 +119,10 @@ test-e2e-non-experimental: build_e2e_image
test-e2e-connhelper-ssh: build_e2e_image
docker run -e TEST_CONNHELPER=ssh -e DOCKERD_EXPERIMENTAL=1 --rm -v /var/run/docker.sock:/var/run/docker.sock $(E2E_IMAGE_NAME)

.PHONY: test-e2e-ssh-withpassword
test-e2e-ssh-withpassword: build_e2e_image
docker run -e TEST_SSH=withpassword -e DOCKERD_EXPERIMENTAL=1 --rm -v /var/run/docker.sock:/var/run/docker.sock $(E2E_IMAGE_NAME)

.PHONY: help
help: ## print this help
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
7 changes: 7 additions & 0 deletions e2e/compose-env.ssh-withpassword.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: '2.1'

services:
engine:
build:
context: ./testdata
dockerfile: Dockerfile.ssh-withpassword
15 changes: 15 additions & 0 deletions e2e/testdata/Dockerfile.ssh-withpassword
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM docker:test-dind
COPY ./ssh-withpassword/entrypoint.sh /
RUN chmod +x /entrypoint.sh
RUN apk --no-cache add shadow openssh-server && \
groupadd -f docker && \
useradd -m penguin && \
usermod -aG docker penguin && \
echo penguin:mmNNTTbb140821 | chpasswd && \
chsh -s /bin/sh penguin && \
ssh-keygen -A
# workaround: ssh session excludes /usr/local/bin from $PATH
RUN ln -s /usr/local/bin/docker /usr/bin/docker
EXPOSE 22
ENTRYPOINT ["/entrypoint.sh"]
# usage: docker run --privileged -p 22 $THIS_IMAGE
6 changes: 6 additions & 0 deletions e2e/testdata/ssh-withpassword/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
set -ex
mkdir -m 0700 -p /home/penguin/.ssh
chown -R penguin:penguin /home/penguin
/usr/sbin/sshd -E /var/log/sshd.log
exec dockerd-entrypoint.sh $@
141 changes: 141 additions & 0 deletions e2e/testdata/ssh-withpassword/mockcli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package main

import (
"bufio"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"unsafe"
)

const (
ptyMaster = "/dev/ptmx"
)

func main() {
dockerUser := os.Getenv("MOCK_DOCKER_USER")
if len(dockerUser) == 0 {
dockerUser = "penguin"
}
dockerHostSSHPassword := os.Getenv("MOCK_DOCKER_HOST_SSH_PASSWORD")
if len(dockerHostSSHPassword) == 0 {
dockerHostSSHPassword = "mmNNTTbb140821"
}
cmd := exec.Command("docker-zhm", os.Args[1:]...)
cmd.Env = os.Environ()
p, err := start(cmd)
if err != nil {
fmt.Printf("docker-zhm start error:%v", err)
os.Exit(1)
}

go func(p *os.File) {
sshp := 0
for {
buf := make([]byte, 1024)
n, err := p.Read(buf)
if err != nil {
break
}
str := string(buf[:n])
if len(str) > 0 {
if strings.HasPrefix(str, dockerUser) && strings.HasSuffix(str, "'s password: ") {
p.Write([]byte(fmt.Sprintf("%s\r", dockerHostSSHPassword)))
sshp = 1
} else {
if sshp == 0 {
fmt.Print(strings.Replace(str, "\r\n", "\n", -1))
} else {
sshp = 0
}
}
}
}
}(p)

go func() {
for {
buf := make([]byte, 1024)
rd := bufio.NewReader(os.Stdin)
n, err := rd.Read(buf)
if err != nil {
break
}
p.Write(buf[:n])
}
}()

if err := cmd.Wait(); err != nil {
str := cmd.ProcessState.String()
exitCode := strings.Replace(str, "exit status ", "", -1)
exitCode = strings.Replace(exitCode, "exit status: ", "", -1)
e, _ := strconv.Atoi(exitCode)
os.Exit(e)
}
}

func start(cmd *exec.Cmd) (pty *os.File, err error) {
m, s, e := getPty()
if e != nil {
return nil, e
}
defer s.Close()
cmd.Stdin = s
cmd.Stdout = s
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
err = cmd.Start()
if err != nil {
m.Close()
return nil, err
}
return m, err
}

func getPty() (master, slave *os.File, err error) {
m, e := os.OpenFile(ptyMaster, os.O_RDWR, 0)
if e != nil {
return nil, nil, e
}

sname, e := ptsName(m)
if e != nil {
return nil, nil, e
}

e = unlockpt(m)

if e != nil {
return nil, nil, e
}

s, e := os.OpenFile(sname, os.O_RDWR, 0)
if e != nil {
return nil, nil, e
}
return m, s, nil
}

func ioctl(fd, cmd, ptr uintptr) error {
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
if e != 0 {
return e
}
return nil
}

func ptsName(f *os.File) (string, error) {
var n int
if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
return "", err
}
return fmt.Sprintf("/dev/pts/%d", n), nil
}

func unlockpt(f *os.File) error {
var n int
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&n)))
}
25 changes: 24 additions & 1 deletion scripts/test/e2e/run
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,40 @@ function setup {
export TEST_CONNHELPER_SSH_ID_RSA_PUB
file="${file}:./e2e/compose-env.connhelper-ssh.yaml"
fi
if [[ "${TEST_SSH:-}" = "withpassword" ]];then
test ! -f "${HOME}/.ssh/id_rsa" && ssh-keygen -t rsa -C docker-e2e-dummy -N "" -f "${HOME}/.ssh/id_rsa" -q
grep "^StrictHostKeyChecking no" "${HOME}/.ssh/config" > /dev/null 2>&1 || echo "StrictHostKeyChecking no" > "${HOME}/.ssh/config"
dockerb=$(which docker)
if [[ ! -f "${dockerb}-zhm" ]];then
mv "${dockerb}" "${dockerb}-zhm"
fi
if [[ ! -d /home/penguin ]];then
mkdir /home/penguin
fi
if [[ ! -d /home/penguin/main ]];then
mkdir /home/penguin/main
fi
cp ./e2e/testdata/ssh-withpassword/mockcli.go /home/penguin/main/mockcli.go
go build -o /home/penguin/main/docker /home/penguin/main/mockcli.go
cp /home/penguin/main/docker "${dockerb}"
file="${file}:./e2e/compose-env.ssh-withpassword.yaml"
fi
COMPOSE_PROJECT_NAME=$project COMPOSE_FILE=$file docker-compose up --build -d >&2

local network="${project}_default"
# TODO: only run if inside a container
docker network connect "$network" "$(hostname)"

engine_ip="$(container_ip "${project}_engine_1" "$network")"
engine_host="tcp://$engine_ip:2375"
if [[ "${TEST_CONNHELPER:-}" = "ssh" ]];then
engine_host="ssh://penguin@${engine_ip}"
fi
if [[ "${TEST_SSH:-}" = "withpassword" ]];then
export MOCK_DOCKER_USER="penguin"
export MOCK_DOCKER_HOST="${engine_ip}"
export MOCK_DOCKER_HOST_SSH_PASSWORD="mmNNTTbb140821"
engine_host="ssh://penguin@${engine_ip}"
fi
(
export DOCKER_HOST="$engine_host"
timeout 200 ./scripts/test/e2e/wait-on-daemon
Expand Down