sftpd: add support for OpenPubkey SSH

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino
2025-11-19 09:16:56 +01:00
parent 74f8539247
commit 22c875c0a1
6 changed files with 201 additions and 30 deletions

View File

@@ -16,6 +16,7 @@ package sftpd
import (
"bytes"
"context"
"errors"
"fmt"
"io"
@@ -1818,6 +1819,7 @@ func TestCanReadSymlink(t *testing.T) {
}
func TestAuthenticationErrors(t *testing.T) {
sftpAuthError := newAuthenticationError(nil, "", "")
loginMethod := dataprovider.SSHLoginMethodPassword
username := "test user"
err := newAuthenticationError(fmt.Errorf("cannot validate credentials: %w", util.NewRecordNotFoundError("not found")),
@@ -1842,3 +1844,42 @@ func TestAuthenticationErrors(t *testing.T) {
assert.ErrorIs(t, err, sftpAuthError)
assert.NotErrorIs(t, err, util.ErrNotFound)
}
type mockCommandExecutor struct {
Output []byte
Err error
}
func (f mockCommandExecutor) CombinedOutput(ctx context.Context, name string, args ...string) ([]byte, error) {
return f.Output, f.Err
}
func TestVerifyWithOPKSSH(t *testing.T) {
sshCert := []byte(`ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg4+hKHVPKv183MU/Q7XD/mzDBFSc2YY3eraltxLMGJo0AAAADAQABAAABAQCe6jMoy1xCQgiZkZJ7gi6NLj4uRqz2OaUGK/OJYZTfBqK+SlS9iymAluHu9K+cc4+0qxx0gn7dRTJWINSgzvca6ayYe995EKgD1hE5krh9BH0bRrXB+hGqyslcZOgLNO+v8jYojClQbRtET2tS+xb4k33GCuL5wgla2790ZgOQgs7huQUjG0S8c1W+EYt6fI4cWE/DeEBnv9sqryS8rOb0PbM6WUd7XBadwySFWYQUX0ei56GNt12Z4gADEGlFQV/OnV0PvnTcAMGUl0rfToPgJ4jgogWKoTVWuZ9wyA/x+2LRLRvgm2a969ig937/AH0i0Wq+FzqfK7EXQ99Yf5K/AAAAAAAAAAAAAAACAAAAFGhvc3QuZXhhbXBsZS5jb20ta2V5AAAAFAAAABBob3N0LmV4YW1wbGUuY29tAAAAAGXEzYAAAAAAd8sP4wAAAAAAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAL4PXUPSERufZWCW/hhEnylk3IeMgaa+2HcNY5Cur77a8fYy6OYZAPF+vhJUT0akwGUpTeXAZumAgHECDrJlw1J+jo9ZVT0AKDo0wU77IzNzYxob7+dpB02NJ7DLAXmPauQ07Zc5pWJFVKtmuh7YH9pjYtNXSMOXye7k06PBGzX+ztIt7nPWvD9fR2mZeTSoljeBCGZHwdlnV2ESQlQbBoEI93RPxqxJh/UCDatQPhpDbyverr2ZvB9Y45rqsx6ZVmu5RXl3MfBU1U21W/4ia2di3PybyD4rSmVoam0efcqxo6cBKSHe26OFoTuS9zgdH0iCWL37vqOFmJ7eH91M3nMAAAEUAAAADHJzYS1zaGEyLTI1NgAAAQA/ByIegNZYJRRl413S/8LxGvTZnbxsPwaluoJ/54niGZV9P28THz7d9jXfSHPjalhH93jNPfTYXvI4opnDC37ua1Nu8KKfk40IWXnnDdZLWraUxEidIzhmfVtz8kGdGoFQ8H0EzubL7zKNOTlfSfOoDlmQVOuxT/+eh2mEp4ri0/+8J1mLfLBr8tREX0/iaNjK+RKdcyTMicKursAYMCDdu8vlaphxea+ocyHM9izSX/l33t44V13ueTqIOh2Zbl2UE2k+jk+0dc1CmV0SEoiWiIyt8TRM4yQry1vPlQLsrf28sYM/QMwnhCVhyZO3vs5F25aQWrB9d51VEzBW9/fd host.example.com`)
key, _, _, _, err := ssh.ParseAuthorizedKey(sshCert) //nolint:dogsled
require.NoError(t, err)
cert, ok := key.(*ssh.Certificate)
require.True(t, ok)
c := Configuration{}
c.executor = mockCommandExecutor{
Err: errors.New("test error"),
}
err = c.verifyWithOPKSSH("user", cert)
assert.Error(t, err)
c.executor = mockCommandExecutor{}
err = c.verifyWithOPKSSH("", cert)
assert.Error(t, err)
c.executor = mockCommandExecutor{
Output: ssh.MarshalAuthorizedKey(cert),
}
err = c.verifyWithOPKSSH("", cert)
assert.Error(t, err)
c.executor = mockCommandExecutor{
Output: ssh.MarshalAuthorizedKey(cert.SignatureKey),
}
err = c.verifyWithOPKSSH("", cert)
assert.NoError(t, err)
}