mirror of https://github.com/wolfSSL/wolfssh.git
305 lines
11 KiB
YAML
305 lines
11 KiB
YAML
name: wolfSSH x509 Interop Test
|
|
|
|
on:
|
|
schedule:
|
|
# Weekly: Mondays at 06:00 UTC
|
|
- cron: '0 6 * * 1'
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
WOLFSSL_REF: v5.9.1-stable
|
|
PKIXSSH_VERSION: 14.4
|
|
|
|
jobs:
|
|
build_wolfssl:
|
|
name: Build wolfSSL
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Checking cache for wolfSSL
|
|
uses: actions/cache@v5
|
|
id: cache-wolfssl
|
|
with:
|
|
path: build-dir/
|
|
key: wolfssh-x509-interop-wolfssl-${{ env.WOLFSSL_REF }}-all-ubuntu-latest
|
|
lookup-only: true
|
|
|
|
- name: Checkout, build, and install wolfSSL
|
|
if: steps.cache-wolfssl.outputs.cache-hit != 'true'
|
|
uses: wolfSSL/actions-build-autotools-project@v1
|
|
with:
|
|
repository: wolfssl/wolfssl
|
|
ref: ${{ env.WOLFSSL_REF }}
|
|
path: wolfssl
|
|
# --enable-all defines WOLFSSL_FPKI, which compiles the UPN-vs-username
|
|
# binding in wolfSSHd (apps/wolfsshd/auth.c). The client cert carries
|
|
# UPN:fred@example, so user "fred" is bound to the certificate. The
|
|
# wolfSSHd build still passes -DWOLFSSH_NO_FPKI below so the strict
|
|
# FPKI profile (FASCN) is not required of the fred test certificate.
|
|
#
|
|
# Coverage note: this FPKI build exercises only the WOLFSSL_FPKI
|
|
# success/binding path of RequestAuthentication. The non-FPKI
|
|
# fail-closed reject branch (apps/wolfsshd/auth.c) is intentionally
|
|
# not run here; it is verified at compile time and would need a
|
|
# separate non-FPKI build to exercise at runtime.
|
|
configure: --enable-all
|
|
check: false
|
|
install: true
|
|
|
|
build_pkixssh:
|
|
name: Build PKIX-SSH
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checking cache for PKIX-SSH
|
|
uses: actions/cache@v5
|
|
id: cache-pkixssh
|
|
with:
|
|
path: build-dir/
|
|
key: wolfssh-x509-interop-pkixssh-${{ env.PKIXSSH_VERSION }}-ubuntu-latest
|
|
lookup-only: true
|
|
|
|
- name: Install build dependencies
|
|
if: steps.cache-pkixssh.outputs.cache-hit != 'true'
|
|
run: |
|
|
sudo apt-get -y update
|
|
sudo apt-get -y install libssl-dev zlib1g-dev
|
|
|
|
- name: Download, build, and install PKIX-SSH
|
|
if: steps.cache-pkixssh.outputs.cache-hit != 'true'
|
|
run: |
|
|
curl -L -o pkixssh.tar.gz \
|
|
"https://roumenpetrov.info/secsh/src/pkixssh-${PKIXSSH_VERSION}.tar.gz"
|
|
echo "3d3b34a3e60dcc69995aeea25a9dafb9a3abbb72a413ef0654b64f7103aa4928 pkixssh.tar.gz" \
|
|
| sha256sum -c -
|
|
tar xzf pkixssh.tar.gz
|
|
sudo mkdir -p /var/empty
|
|
cd pkixssh-${PKIXSSH_VERSION}
|
|
./configure \
|
|
--prefix=$PWD/../build-dir/ \
|
|
--with-privsep-path=/var/empty \
|
|
--with-privsep-user=nobody \
|
|
--disable-strip
|
|
make
|
|
make install
|
|
|
|
x509_interop:
|
|
name: Run x509 interop test
|
|
needs: [build_wolfssl, build_pkixssh]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Restore wolfSSL cache
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: build-dir/
|
|
key: wolfssh-x509-interop-wolfssl-${{ env.WOLFSSL_REF }}-all-ubuntu-latest
|
|
fail-on-cache-miss: true
|
|
|
|
- name: Restore PKIX-SSH cache
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: build-dir/
|
|
key: wolfssh-x509-interop-pkixssh-${{ env.PKIXSSH_VERSION }}-ubuntu-latest
|
|
fail-on-cache-miss: true
|
|
|
|
- name: Install test dependencies
|
|
run: |
|
|
sudo apt-get -y update
|
|
sudo apt-get -y install netcat-traditional
|
|
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
path: wolfssh/
|
|
|
|
- name: autogen
|
|
working-directory: ./wolfssh/
|
|
run: ./autogen.sh
|
|
|
|
- name: configure
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
./configure --enable-all --enable-certs \
|
|
LDFLAGS="-L${{ github.workspace }}/build-dir/lib" \
|
|
CPPFLAGS="-I${{ github.workspace }}/build-dir/include -DWOLFSSH_NO_FPKI"
|
|
|
|
- name: make
|
|
working-directory: ./wolfssh/
|
|
run: make
|
|
|
|
- name: Create test user fred
|
|
run: |
|
|
sudo useradd -m fred
|
|
|
|
- name: Prepare client cert in PKIX-SSH format
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
chmod 600 ./keys/fred-key.pem
|
|
cat ./keys/fred-cert.pem >> ./keys/fred-key.pem
|
|
../build-dir/bin/ssh-keygen -y -f ./keys/fred-key.pem \
|
|
> ./keys/fred-key.pem.pub
|
|
|
|
- name: Write PKIX-SSH client config
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
echo "CACertificateFile $PWD/keys/ca-cert-ecc.pem" \
|
|
> ssh-pkixssh-config
|
|
|
|
- name: Write wolfSSHd config
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
rm -f sshd_config
|
|
cat > sshd_config <<EOT
|
|
Port 22222
|
|
Protocol 2
|
|
LoginGraceTime 600
|
|
PermitRootLogin yes
|
|
PasswordAuthentication yes
|
|
PermitEmptyPasswords no
|
|
|
|
TrustedUserCAKeys $PWD/keys/ca-cert-ecc.pem
|
|
HostKey $PWD/keys/server-key.pem
|
|
HostCertificate $PWD/keys/server-cert.pem
|
|
EOT
|
|
|
|
- name: Start wolfSSHd
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
# wolfSSHd loads the host key, host cert, and user CA through the
|
|
# secure gate. The daemon runs under sudo (euid 0), so make all three
|
|
# root-owned. The secret host key must also be 0600; the public cert
|
|
# and CA stay 0644 (not group/world writable, still runner readable).
|
|
sudo chmod 600 $PWD/keys/server-key.pem
|
|
sudo chown 0:0 $PWD/keys/server-key.pem $PWD/keys/server-cert.pem \
|
|
$PWD/keys/ca-cert-ecc.pem
|
|
sudo ./apps/wolfsshd/wolfsshd -f sshd_config -d \
|
|
-E $PWD/wolfsshd-log.txt &
|
|
for i in $(seq 1 20); do
|
|
if nc -z 127.0.0.1 22222; then
|
|
echo "wolfSSHd is up"
|
|
exit 0
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
echo "wolfSSHd failed to start"
|
|
cat wolfsshd-log.txt || true
|
|
exit 1
|
|
|
|
- name: Test PKIX-SSH client exit
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
../build-dir/bin/ssh -o StrictHostKeyChecking=accept-new \
|
|
-o PreferredAuthentications=publickey \
|
|
-p 22222 -F ssh-pkixssh-config \
|
|
-i ./keys/fred-key.pem fred@127.0.0.1 exit
|
|
|
|
- name: Test PKIX-SSH client ls command
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
../build-dir/bin/ssh -o StrictHostKeyChecking=accept-new \
|
|
-o PreferredAuthentications=publickey \
|
|
-p 22222 -F ssh-pkixssh-config \
|
|
-i ./keys/fred-key.pem fred@127.0.0.1 ls
|
|
|
|
- name: Test PKIX-SSH sftp interop
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
../build-dir/bin/sftp -o StrictHostKeyChecking=accept-new \
|
|
-o PreferredAuthentications=publickey \
|
|
-P 22222 -F ssh-pkixssh-config \
|
|
-S ../build-dir/bin/ssh \
|
|
-i ./keys/fred-key.pem \
|
|
fred@127.0.0.1 <<EOF
|
|
exit
|
|
EOF
|
|
|
|
- name: Negative test - fred cert must not authenticate as another user
|
|
working-directory: ./wolfssh/
|
|
run: |
|
|
# Regression guard for the cert principal-binding fix: a certificate
|
|
# issued for "fred" (UPN:fred@example) must not be accepted for a
|
|
# different SSH username. PreferredAuthentications=publickey plus
|
|
# BatchMode keep this to a single publickey attempt with no password
|
|
# fallback. The preceding positive tests already proved connectivity
|
|
# and that fred's cert works; here we additionally require the failure
|
|
# to be an authentication denial, so an unrelated ssh error (transport,
|
|
# host-key, option change) cannot masquerade as a passing negative test.
|
|
sudo useradd -m otheruser
|
|
set +e
|
|
../build-dir/bin/ssh -o StrictHostKeyChecking=accept-new \
|
|
-o PreferredAuthentications=publickey \
|
|
-o BatchMode=yes -o NumberOfPasswordPrompts=0 \
|
|
-p 22222 -F ssh-pkixssh-config \
|
|
-i ./keys/fred-key.pem otheruser@127.0.0.1 exit \
|
|
> ssh-neg.out 2> ssh-neg.err
|
|
rc=$?
|
|
set -e
|
|
cat ssh-neg.err || true
|
|
if [ "$rc" -eq 0 ]; then
|
|
echo "SECURITY FAILURE: fred certificate authenticated as otheruser"
|
|
exit 1
|
|
fi
|
|
if ! grep -qi "permission denied" ssh-neg.err; then
|
|
echo "Negative test inconclusive: ssh failed but not with an auth"
|
|
echo "denial (possible transport/config error, not a binding reject)"
|
|
exit 1
|
|
fi
|
|
echo "OK: fred certificate correctly rejected for otheruser (auth denied)"
|
|
|
|
- name: Show wolfSSHd log on failure
|
|
if: failure()
|
|
working-directory: ./wolfssh/
|
|
run: cat wolfsshd-log.txt || true
|
|
|
|
- name: Stop wolfSSHd
|
|
if: always()
|
|
run: sudo pkill wolfsshd || true
|
|
|
|
notify_failure:
|
|
name: Open issue on scheduled failure
|
|
needs: [build_wolfssl, build_pkixssh, x509_interop]
|
|
if: failure() && github.event_name == 'schedule'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions:
|
|
issues: write
|
|
steps:
|
|
- uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const label = 'x509-interop-failure';
|
|
const runUrl = `${context.serverUrl}/${context.repo.owner}/` +
|
|
`${context.repo.repo}/actions/runs/${context.runId}`;
|
|
const body = [
|
|
'The weekly x509 interop workflow failed.',
|
|
'',
|
|
`Run: ${runUrl}`,
|
|
`Commit: ${context.sha}`,
|
|
].join('\n');
|
|
const existing = await github.rest.issues.listForRepo({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
state: 'open',
|
|
labels: label,
|
|
});
|
|
if (existing.data.length > 0) {
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: existing.data[0].number,
|
|
body: body,
|
|
});
|
|
} else {
|
|
await github.rest.issues.create({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
title: 'Weekly x509 interop test failed',
|
|
body: body,
|
|
labels: [label],
|
|
});
|
|
}
|