Skip to content

wolfSSH x509 Interop Test #7

wolfSSH x509 Interop Test

wolfSSH x509 Interop Test #7

Workflow file for this run

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],
});
}