Customize sshd with sshd config.d (#525)

* Update OpenSSH server image version and add no penalties configuration

* Refactor SSH configuration paths and update test scripts for public key handling

* Update Docker Compose volumes to remove read-only flag for shared directory

* Remove end-to-end test files and related Docker configuration for CA public cert authentication
This commit is contained in:
Boshi Lian 2025-02-16 23:19:41 -08:00 committed by GitHub
parent 3050a85e6e
commit e96e3f6a3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 24 additions and 191 deletions

View file

@ -33,7 +33,7 @@ SSHPIPERD_DEBUG=1 docker-compose up --force-recreate --build -d
you will have two sshd:
* `host-password:2222`: a password only sshd server (user: `user`, password: `pass`)
* `host-publickey:2222`: a public key only sshd server (put your public key in `/sshconfig_publickey/.config/authorized_keys`)
* `host-publickey:2222`: a public key only sshd server (put your public key in `/publickey_authorized_keys/authorized_keys`)
more settings: <https://github.com/linuxserver/docker-openssh-server>

View file

@ -1,60 +0,0 @@
package e2e_test
import (
"fmt"
"testing"
"time"
"github.com/google/uuid"
)
func TestCa(t *testing.T) {
piperaddr, piperport := nextAvailablePiperAddress()
piper, _, _, err := runCmd("/sshpiperd/sshpiperd",
"-p",
piperport,
"/sshpiperd/plugins/testcaplugin",
"--target",
"host-capublickey:2222",
)
if err != nil {
t.Errorf("failed to run sshpiperd: %v", err)
}
defer killCmd(piper)
waitForEndpointReady(piperaddr)
randtext := uuid.New().String()
targetfie := uuid.New().String()
c, stdin, stdout, err := runCmd(
"ssh",
"-v",
"-o",
"StrictHostKeyChecking=no",
"-o",
"UserKnownHostsFile=/dev/null",
"-p",
piperport,
"-l",
"client_123",
"127.0.0.1",
fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie),
)
if err != nil {
t.Errorf("failed to ssh to piper-fixed, %v", err)
}
defer killCmd(c)
enterPassword(stdin, stdout, "pass")
time.Sleep(time.Second) // wait for file flush
checkSharedFileContent(t, targetfie, randtext)
}

View file

@ -1,19 +0,0 @@
FROM debian:bookworm-slim
EXPOSE 2222
ENV DEBIAN_FRONTEND=noninteractive
COPY ca.pub /etc/ssh/trusted-ca.pub
RUN apt -y update \
&& apt -y install --no-install-recommends --no-install-suggests openssh-server \
&& mkdir -p /run/sshd \
&& adduser --disabled-password --gecos "" client_123 \
&& passwd -d client_123 \
&& rm -rf /var/lib/apt/lists/*
COPY sshd_config /etc/ssh/sshd_config
ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e", "-f", "/etc/ssh/sshd_config"]

View file

@ -1,23 +0,0 @@
Port 2222
# Trust our CA
TrustedUserCAKeys /etc/ssh/trusted-ca.pub
# Allow pubkey
PubkeyAuthentication yes
# Don't allow passwords
PasswordAuthentication no
# Don't allow host auth
HostbasedAuthentication no
# No challenge response
ChallengeResponseAuthentication no
# Allow client to pass locale environment variables
AcceptEnv LANG LC_*
# override default of no subsystems
Subsystem sftp /usr/lib/openssh/sftp-server
# Change this if you have issues
LogLevel INFO

View file

@ -2,7 +2,7 @@ version: '3.4'
services:
host-password:
image: lscr.io/linuxserver/openssh-server:9.7_p1-r4-ls184
image: lscr.io/linuxserver/openssh-server:9.9_p1-r2-ls190
environment:
- PASSWORD_ACCESS=true
- USER_PASSWORD=pass
@ -15,13 +15,13 @@ services:
- sshpiper.network=e2e_default
volumes:
- shared:/shared
- sshconfig_password:/config
- ./sshdconfig/no_penalties.conf:/config/sshd/sshd_config.d/no_penalties.conf:ro
networks:
- default
- netdistract
host-publickey:
image: lscr.io/linuxserver/openssh-server:9.7_p1-r4-ls184
image: lscr.io/linuxserver/openssh-server:9.9_p1-r2-ls190
environment:
- USER_NAME=user
- LOG_STDOUT=true
@ -31,18 +31,20 @@ services:
- sshpiper.authorized_keys=c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSU5SR1RIMzI1ckRVcDEydHBsd3VrSG1SOHl0YkM5VFBaODg2Z0NzdHluUDEgdGVzdEB0ZXN0Cg==
- sshpiper.private_key=LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNEVVJreDk5dWF3MUtkZHJhWmNMcEI1a2ZNcld3dlV6MmZQT29BckxjcHo5UUFBQUpDK2owK1N2bzlQCmtnQUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDRFVSa3g5OXVhdzFLZGRyYVpjTHBCNWtmTXJXd3ZVejJmUE9vQXJMY3B6OVEKQUFBRURjUWdkaDJ6MnIvNmJscTB6aUoxbDZzNklBWDhDKzlRSGZBSDkzMWNITk85UkdUSDMyNXJEVXAxMnRwbHd1a0htUgo4eXRiQzlUUFo4ODZnQ3N0eW5QMUFBQUFEV0p2YkdsaGJrQjFZblZ1ZEhVPQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K
volumes:
- shared:/shared
- sshconfig_publickey:/config
- shared:/shared
- publickey_authorized_keys:/config/.ssh/
- ./sshdconfig/no_penalties.conf:/config/sshd/sshd_config.d/no_penalties.conf:ro
host-capublickey:
build: ./cahost
labels:
- sshpiper.port=2222
- sshpiper.network=e2e_default
image: lscr.io/linuxserver/openssh-server:9.9_p1-r2-ls190
environment:
- USER_NAME=ca_user
- LOG_STDOUT=true
volumes:
- shared:/shared
networks:
- default
- ./sshdconfig/no_penalties.conf:/config/sshd/sshd_config.d/no_penalties.conf:ro
- ./sshdconfig/trusted-ca.conf:/config/sshd/sshd_config.d/trusted-ca.conf:ro
- ./sshdconfig/trusted-ca.pub:/config/sshd/trusted-ca.pub:ro
host-k8s-proxy:
@ -95,8 +97,7 @@ services:
volumes:
- ..:/src
- shared:/shared
- sshconfig_publickey:/sshconfig_publickey
- sshconfig_password:/sshconfig_password
- publickey_authorized_keys:/publickey_authorized_keys
- /var/run/docker.sock:/var/run/docker.sock
- kubeconfig:/root/.kube:ro
command: ["./e2eentry.sh"]
@ -121,9 +122,7 @@ volumes:
type: tmpfs
device: tmpfs
sshconfig_publickey:
sshconfig_password:
publickey_authorized_keys:
kubeconfig:

View file

@ -72,7 +72,7 @@ func TestDocker(t *testing.T) {
t.Errorf("failed to write to test key: %v", err)
}
if err := os.WriteFile("/sshconfig_publickey/.ssh/authorized_keys", []byte(`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINRGTH325rDUp12tplwukHmR8ytbC9TPZ886gCstynP1`), 0400); err != nil {
if err := os.WriteFile("/publickey_authorized_keys/authorized_keys", []byte(`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINRGTH325rDUp12tplwukHmR8ytbC9TPZ886gCstynP1`), 0400); err != nil {
t.Errorf("failed to write to authorized_keys: %v", err)
}

View file

@ -4,11 +4,6 @@ set -x
# use entrypoint.sh to generate the ssh_host_ed25519_key
PLUGIN="dummy_badname/" bash /sshpiperd/entrypoint.sh 2>/dev/null
# Create an ssh key pair and then sign the public key with the ca cert
chmod 600 cahost/ca
ssh-keygen -t ssh-ed25519 -f /etc/ssh/ssh_user -N ""
ssh-keygen -s cahost/ca -I ssh_user -n client_123 /etc/ssh/ssh_user.pub
if [ "${SSHPIPERD_DEBUG}" == "1" ]; then
echo "enter debug on hold mode"
echo "run [docker exec -ti e2e_testrunner_1 bash] to run to attach"

View file

@ -255,7 +255,7 @@ stringData:
8ytbC9TPZ886gCstynP1AAAADWJvbGlhbkB1YnVudHU=
-----END OPENSSH PRIVATE KEY-----
ssh-publickey-cert: |
ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIMIr7E6Il5fvTpixHHDCszKhlRbzXrB0fnWtxYAYdyVlAAAAINRGTH325rDUp12tplwukHmR8ytbC9TPZ886gCstynP1AAAAAAAAAAAAAAABAAAAC2s4c2UyZV90ZXN0AAAADgAAAApjbGllbnRfMTIzAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIJrzCeQ3a+m8PnA/KUmK+JAfC1tM7SG2gkGmW29nXj1nAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDICm0JT8fakTkWTRjDtbjjMx0dUrvGvPb+Qzi2592/39CqBj8cEqZAF/DesCJhKmO+hyN4jDxTcKPhPM0y2/sH
ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIAf6qglkclnZFuSIlW4ClXcwq+SCYJn7rCYcUpbVVaKrAAAAINRGTH325rDUp12tplwukHmR8ytbC9TPZ886gCstynP1AAAAAAAAAAAAAAABAAAACHNzaF91c2VyAAAACwAAAAdjYV91c2VyAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIJrzCeQ3a+m8PnA/KUmK+JAfC1tM7SG2gkGmW29nXj1nAAAAUwAAAAtzc2gtZWQyNTUxOQAAAECM8mKjWLhTKmhj4sb6r0CVaTz/vO8oy1o/7OzmQyUMMa1ex4mo+HB3RHa5eUGOdcFAJu6O8r6GBEah+O0maH8A
kind: Secret
metadata:
name: host-publickey-key-ca
@ -271,7 +271,7 @@ spec:
authorized_keys_file: /files/authorized_keys
to:
host: host-capublickey:2222
username: "client_123"
username: "ca_user"
private_key_secret:
name: host-publickey-key-ca
ignore_hostkey: true

View file

@ -56,7 +56,7 @@ func TestKubernetes(t *testing.T) {
t.Errorf("failed to write to test key: %v", err)
}
if err := os.WriteFile("/sshconfig_publickey/.ssh/authorized_keys", []byte(`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINRGTH325rDUp12tplwukHmR8ytbC9TPZ886gCstynP1`), 0400); err != nil {
if err := os.WriteFile("/publickey_authorized_keys/authorized_keys", []byte(`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINRGTH325rDUp12tplwukHmR8ytbC9TPZ886gCstynP1`), 0400); err != nil {
t.Errorf("failed to write to authorized_keys: %v", err)
}

View file

@ -0,0 +1 @@
PerSourcePenaltyExemptList 0.0.0.0/0

View file

@ -0,0 +1 @@
TrustedUserCAKeys /config/sshd/trusted-ca.pub

View file

@ -1,61 +0,0 @@
//go:build full || e2e
package main
import (
"fmt"
"os"
"github.com/tg123/sshpiper/libplugin"
"github.com/urfave/cli/v2"
)
func main() {
libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{
Name: "testcaplugin",
Usage: "Plugin to test ca public cert auth",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "target",
Usage: "target ssh endpoint address",
EnvVars: []string{"SSHPIPERD_FIXED_TARGET"},
Required: true,
},
},
CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) {
target := c.String("target")
host, port, err := libplugin.SplitHostPortForSSH(target)
if err != nil {
return nil, err
}
return &libplugin.SshPiperPluginConfig{
PasswordCallback: func(conn libplugin.ConnMetadata, password []byte) (*libplugin.Upstream, error) {
if string(password) != "pass" {
return nil, fmt.Errorf("Password is incorrect")
}
// Load the private key
signer, err := os.ReadFile("/etc/ssh/ssh_user")
if err != nil {
return nil, fmt.Errorf("unable to read private key: %v", err)
}
// Load the public ca certificate
capublickey, err := os.ReadFile("/etc/ssh/ssh_user-cert.pub")
if err != nil {
return nil, fmt.Errorf("unable to read ca cert: %v", err)
}
return &libplugin.Upstream{
Host: host,
Port: int32(port),
IgnoreHostKey: true,
Auth: libplugin.CreatePrivateKeyAuth(signer, capublickey),
}, nil
},
}, nil
},
})
}

View file

@ -188,7 +188,7 @@ func TestWorkingDirectory(t *testing.T) {
if err := runCmdAndWait(
"/bin/cp",
path.Join(userdir, "id_rsa.pub"),
"/sshconfig_publickey/.ssh/authorized_keys",
"/publickey_authorized_keys/authorized_keys",
); err != nil {
t.Errorf("failed to copy public key: %v", err)
}

View file

@ -129,7 +129,7 @@ func TestYaml(t *testing.T) {
if err := runCmdAndWait(
"/bin/cp",
path.Join(yamldir, "id_rsa.pub"),
"/sshconfig_publickey/.ssh/authorized_keys",
"/publickey_authorized_keys/authorized_keys",
); err != nil {
t.Errorf("failed to copy public key: %v", err)
}