mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 11:05:45 +00:00
ssh/tailssh: fix SSH on busybox systems
This involved the following: 1. Pass the su command path as first of args in call to unix.Exec to make sure that busybox sees the correct program name. Busybox is a single executable userspace that implements various core userspace commands in a single binary. You'll see it used via symlinking, so that for example /bin/su symlinks to /bin/busybox. Busybox knows that you're trying to execute /bin/su because argv[0] is '/bin/su'. When we called unix.Exec, we weren't including the program name for argv[0], which caused busybox to fail with 'applet not found', meaning that it didn't know which command it was supposed to run. 2. Tell su to whitelist the SSH_AUTH_SOCK environment variable in order to support ssh agent forwarding. 3. Run integration tests on alpine, which uses busybox. 4. Increment CurrentCapabilityVersion to allow turning on SSH V2 behavior from control. Fixes #12849 Signed-off-by: Percy Wegmann <percy@tailscale.com>
This commit is contained in:
parent
7675c3ebf2
commit
7d83056a1b
3
Makefile
3
Makefile
@ -117,7 +117,8 @@ sshintegrationtest: ## Run the SSH integration tests in various Docker container
|
|||||||
echo "Testing on ubuntu:focal" && docker build --build-arg="BASE=ubuntu:focal" -t ssh-ubuntu-focal ssh/tailssh/testcontainers && \
|
echo "Testing on ubuntu:focal" && docker build --build-arg="BASE=ubuntu:focal" -t ssh-ubuntu-focal ssh/tailssh/testcontainers && \
|
||||||
echo "Testing on ubuntu:jammy" && docker build --build-arg="BASE=ubuntu:jammy" -t ssh-ubuntu-jammy ssh/tailssh/testcontainers && \
|
echo "Testing on ubuntu:jammy" && docker build --build-arg="BASE=ubuntu:jammy" -t ssh-ubuntu-jammy ssh/tailssh/testcontainers && \
|
||||||
echo "Testing on ubuntu:mantic" && docker build --build-arg="BASE=ubuntu:mantic" -t ssh-ubuntu-mantic ssh/tailssh/testcontainers && \
|
echo "Testing on ubuntu:mantic" && docker build --build-arg="BASE=ubuntu:mantic" -t ssh-ubuntu-mantic ssh/tailssh/testcontainers && \
|
||||||
echo "Testing on ubuntu:noble" && docker build --build-arg="BASE=ubuntu:noble" -t ssh-ubuntu-noble ssh/tailssh/testcontainers
|
echo "Testing on ubuntu:noble" && docker build --build-arg="BASE=ubuntu:noble" -t ssh-ubuntu-noble ssh/tailssh/testcontainers && \
|
||||||
|
echo "Testing on alpine:latest" && docker build --build-arg="BASE=alpine:latest" -t ssh-alpine-latest ssh/tailssh/testcontainers
|
||||||
|
|
||||||
help: ## Show this help
|
help: ## Show this help
|
||||||
@echo "\nSpecify a command. The choices are:\n"
|
@echo "\nSpecify a command. The choices are:\n"
|
||||||
|
@ -399,7 +399,7 @@ func tryExecLogin(dlogf logger.Logf, ia incubatorArgs) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
loginArgs := ia.loginArgs(loginCmdPath)
|
loginArgs := ia.loginArgs(loginCmdPath)
|
||||||
dlogf("logging in with %s %+v", loginCmdPath, loginArgs)
|
dlogf("logging in with %+v", loginArgs)
|
||||||
|
|
||||||
// If Exec works, the Go code will not proceed past this:
|
// If Exec works, the Go code will not proceed past this:
|
||||||
err = unix.Exec(loginCmdPath, loginArgs, os.Environ())
|
err = unix.Exec(loginCmdPath, loginArgs, os.Environ())
|
||||||
@ -435,13 +435,18 @@ func trySU(dlogf logger.Logf, ia incubatorArgs) (handled bool, err error) {
|
|||||||
defer sessionCloser()
|
defer sessionCloser()
|
||||||
}
|
}
|
||||||
|
|
||||||
loginArgs := []string{"-l", ia.localUser}
|
loginArgs := []string{
|
||||||
|
su,
|
||||||
|
"-w", "SSH_AUTH_SOCK", // pass through SSH_AUTH_SOCK environment variable to support ssh agent forwarding
|
||||||
|
"-l",
|
||||||
|
ia.localUser,
|
||||||
|
}
|
||||||
if ia.cmd != "" {
|
if ia.cmd != "" {
|
||||||
// Note - unlike the login command, su allows using both -l and -c.
|
// Note - unlike the login command, su allows using both -l and -c.
|
||||||
loginArgs = append(loginArgs, "-c", ia.cmd)
|
loginArgs = append(loginArgs, "-c", ia.cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
dlogf("logging in with %s %q", su, loginArgs)
|
dlogf("logging in with %+v", loginArgs)
|
||||||
|
|
||||||
// If Exec works, the Go code will not proceed past this:
|
// If Exec works, the Go code will not proceed past this:
|
||||||
err = unix.Exec(su, loginArgs, os.Environ())
|
err = unix.Exec(su, loginArgs, os.Environ())
|
||||||
@ -473,9 +478,15 @@ func findSU(dlogf logger.Logf, ia incubatorArgs) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// First try to execute su -l <user> -c true to make sure su supports the
|
// First try to execute su -w SSH_AUTH_SOCK -l <user> -c true
|
||||||
// necessary arguments.
|
// to make sure su supports the necessary arguments.
|
||||||
err = exec.Command(su, "-l", ia.localUser, "-c", "true").Run()
|
err = exec.Command(
|
||||||
|
su,
|
||||||
|
"-w", "SSH_AUTH_SOCK",
|
||||||
|
"-l",
|
||||||
|
ia.localUser,
|
||||||
|
"-c", "true",
|
||||||
|
).Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dlogf("su check failed: %s", err)
|
dlogf("su check failed: %s", err)
|
||||||
return ""
|
return ""
|
||||||
|
@ -349,7 +349,7 @@ func TestSSHAgentForwarding(t *testing.T) {
|
|||||||
|
|
||||||
// Run tailscale SSH server and connect to it
|
// Run tailscale SSH server and connect to it
|
||||||
username := "testuser"
|
username := "testuser"
|
||||||
tailscaleAddr := testServer(t, username, false) // TODO: make this false to use V2 behavior
|
tailscaleAddr := testServer(t, username, false)
|
||||||
tcl, err := ssh.Dial("tcp", tailscaleAddr, &ssh.ClientConfig{
|
tcl, err := ssh.Dial("tcp", tailscaleAddr, &ssh.ClientConfig{
|
||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
})
|
})
|
||||||
@ -387,7 +387,7 @@ func TestSSHAgentForwarding(t *testing.T) {
|
|||||||
|
|
||||||
o, err := s.CombinedOutput(fmt.Sprintf(`ssh -T -o StrictHostKeyChecking=no -p %s upstreamuser@%s "true"`, upstreamPort, upstreamHost))
|
o, err := s.CombinedOutput(fmt.Sprintf(`ssh -T -o StrictHostKeyChecking=no -p %s upstreamuser@%s "true"`, upstreamPort, upstreamHost))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to call true command: %s\n%s", err, o)
|
t.Fatalf("unable to call true command: %s\n%s\n-------------------------", err, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
ARG BASE
|
ARG BASE
|
||||||
FROM ${BASE}
|
FROM ${BASE}
|
||||||
|
|
||||||
|
ARG BASE
|
||||||
|
|
||||||
RUN echo "Install openssh, needed for scp."
|
RUN echo "Install openssh, needed for scp."
|
||||||
RUN apt-get update -y && apt-get install -y openssh-client
|
RUN if echo "$BASE" | grep "ubuntu:"; then apt-get update -y && apt-get install -y openssh-client; fi
|
||||||
|
RUN if echo "$BASE" | grep "alpine:"; then apk add openssh; fi
|
||||||
|
|
||||||
RUN groupadd -g 10000 groupone
|
# Note - on Ubuntu, we do not create the user's home directory, pam_mkhomedir will do that
|
||||||
RUN groupadd -g 10001 grouptwo
|
|
||||||
# Note - we do not create the user's home directory, pam_mkhomedir will do that
|
|
||||||
# for us, and we want to test that PAM gets triggered by Tailscale SSH.
|
# for us, and we want to test that PAM gets triggered by Tailscale SSH.
|
||||||
RUN useradd -g 10000 -G 10001 -u 10002 testuser
|
RUN if echo "$BASE" | grep "ubuntu:"; then groupadd -g 10000 groupone && groupadd -g 10001 grouptwo && useradd -g 10000 -G 10001 -u 10002 testuser; fi
|
||||||
|
# On Alpine, we can't configure pam_mkhomdir, so go ahead and create home directory.
|
||||||
|
RUN if echo "$BASE" | grep "alpine:"; then addgroup -g 10000 groupone && addgroup -g 10001 grouptwo && adduser -u 10002 -D testuser && addgroup testuser groupone && addgroup testuser grouptwo; fi
|
||||||
|
|
||||||
RUN echo "Set up pam_mkhomedir."
|
RUN if echo "$BASE" | grep "ubuntu:"; then \
|
||||||
RUN sed -i -e 's/Default: no/Default: yes/g' /usr/share/pam-configs/mkhomedir || echo "might not be ubuntu"
|
echo "Set up pam_mkhomedir." && \
|
||||||
RUN cat /usr/share/pam-configs/mkhomedir
|
sed -i -e 's/Default: no/Default: yes/g' /usr/share/pam-configs/mkhomedir && \
|
||||||
RUN pam-auth-update --enable mkhomedir
|
cat /usr/share/pam-configs/mkhomedir && \
|
||||||
|
pam-auth-update --enable mkhomedir \
|
||||||
|
; fi
|
||||||
|
|
||||||
COPY tailscaled .
|
COPY tailscaled .
|
||||||
COPY tailssh.test .
|
COPY tailssh.test .
|
||||||
@ -22,11 +27,11 @@ RUN chmod 755 tailscaled
|
|||||||
|
|
||||||
# RUN echo "First run tests normally."
|
# RUN echo "First run tests normally."
|
||||||
RUN eval `ssh-agent -s` && TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestSSHAgentForwarding
|
RUN eval `ssh-agent -s` && TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestSSHAgentForwarding
|
||||||
RUN rm -Rf /home/testuser
|
RUN if echo "$BASE" | grep "ubuntu:"; then rm -Rf /home/testuser; fi
|
||||||
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSFTP
|
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSFTP
|
||||||
RUN rm -Rf /home/testuser
|
RUN if echo "$BASE" | grep "ubuntu:"; then rm -Rf /home/testuser; fi
|
||||||
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSCP
|
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSCP
|
||||||
RUN rm -Rf /home/testuser
|
RUN if echo "$BASE" | grep "ubuntu:"; then rm -Rf /home/testuser; fi
|
||||||
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSSH
|
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSSH
|
||||||
|
|
||||||
RUN echo "Then run tests as non-root user testuser and make sure tests still pass."
|
RUN echo "Then run tests as non-root user testuser and make sure tests still pass."
|
||||||
@ -35,28 +40,31 @@ RUN TAILSCALED_PATH=`pwd`tailscaled eval `su -m testuser -c ssh-agent -s` && su
|
|||||||
RUN TAILSCALED_PATH=`pwd`tailscaled su -m testuser -c "./tailssh.test -test.v -test.run TestIntegration TestDoDropPrivileges"
|
RUN TAILSCALED_PATH=`pwd`tailscaled su -m testuser -c "./tailssh.test -test.v -test.run TestIntegration TestDoDropPrivileges"
|
||||||
RUN chown root:root /tmp/tailscalessh.log
|
RUN chown root:root /tmp/tailscalessh.log
|
||||||
|
|
||||||
RUN echo "Then run tests in a system that's pretending to be SELinux in enforcing mode"
|
RUN if echo "$BASE" | grep "ubuntu:"; then \
|
||||||
RUN mv /usr/bin/login /tmp/login_orig
|
echo "Then run tests in a system that's pretending to be SELinux in enforcing mode" && \
|
||||||
# Use nonsense for /usr/bin/login so that it fails.
|
# Remove execute permissions for /usr/bin/login so that it fails.
|
||||||
# It's not the same failure mode as in SELinux, but failure is good enough for test.
|
mv /usr/bin/login /tmp/login_orig && \
|
||||||
RUN echo "adsfasdfasdf" > /usr/bin/login
|
# Use nonsense for /usr/bin/login so that it fails.
|
||||||
RUN chmod 755 /usr/bin/login
|
# It's not the same failure mode as in SELinux, but failure is good enough for test.
|
||||||
# Simulate getenforce command
|
echo "adsfasdfasdf" > /usr/bin/login && \
|
||||||
RUN printf "#!/bin/bash\necho 'Enforcing'" > /usr/bin/getenforce
|
chmod 755 /usr/bin/login && \
|
||||||
RUN chmod 755 /usr/bin/getenforce
|
# Simulate getenforce command
|
||||||
RUN eval `ssh-agent -s` && TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestSSHAgentForwarding
|
printf "#!/bin/bash\necho 'Enforcing'" > /usr/bin/getenforce && \
|
||||||
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegration
|
chmod 755 /usr/bin/getenforce && \
|
||||||
RUN mv /tmp/login_orig /usr/bin/login
|
eval `ssh-agent -s` && TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestSSHAgentForwarding && \
|
||||||
RUN rm /usr/bin/getenforce
|
TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegration && \
|
||||||
|
mv /tmp/login_orig /usr/bin/login && \
|
||||||
|
rm /usr/bin/getenforce \
|
||||||
|
; fi
|
||||||
|
|
||||||
RUN echo "Then remove the login command and make sure tests still pass."
|
RUN echo "Then remove the login command and make sure tests still pass."
|
||||||
RUN rm `which login`
|
RUN rm `which login`
|
||||||
RUN eval `ssh-agent -s` && TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestSSHAgentForwarding
|
RUN eval `ssh-agent -s` && TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestSSHAgentForwarding
|
||||||
RUN rm -Rf /home/testuser
|
RUN if echo "$BASE" | grep "ubuntu:"; then rm -Rf /home/testuser; fi
|
||||||
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSFTP
|
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSFTP
|
||||||
RUN rm -Rf /home/testuser
|
RUN if echo "$BASE" | grep "ubuntu:"; then rm -Rf /home/testuser; fi
|
||||||
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSCP
|
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSCP
|
||||||
RUN rm -Rf /home/testuser
|
RUN if echo "$BASE" | grep "ubuntu:"; then rm -Rf /home/testuser; fi
|
||||||
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSSH
|
RUN TAILSCALED_PATH=`pwd`tailscaled ./tailssh.test -test.v -test.run TestIntegrationSSH
|
||||||
|
|
||||||
RUN echo "Then remove the su command and make sure tests still pass."
|
RUN echo "Then remove the su command and make sure tests still pass."
|
||||||
|
@ -147,7 +147,8 @@
|
|||||||
// - 102: 2024-07-12: NodeAttrDisableMagicSockCryptoRouting support
|
// - 102: 2024-07-12: NodeAttrDisableMagicSockCryptoRouting support
|
||||||
// - 103: 2024-07-24: Client supports NodeAttrDisableCaptivePortalDetection
|
// - 103: 2024-07-24: Client supports NodeAttrDisableCaptivePortalDetection
|
||||||
// - 104: 2024-08-03: SelfNodeV6MasqAddrForThisPeer now works
|
// - 104: 2024-08-03: SelfNodeV6MasqAddrForThisPeer now works
|
||||||
const CurrentCapabilityVersion CapabilityVersion = 104
|
// - 105: 2024-08-05: Fixed SSH behavior on systems that use busybox (issue #12849)
|
||||||
|
const CurrentCapabilityVersion CapabilityVersion = 105
|
||||||
|
|
||||||
type StableID string
|
type StableID string
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user