Pass client SSH key fingerprint in upstream username

When using public key auth, include the client's key fingerprint
in the upstream username with format: fp:FINGERPRINT.originaluser

This allows upstream servers (like maldoror) to identify users
by their original SSH key even when sshpiper uses its own key
for upstream authentication.
This commit is contained in:
Russell Ballestrini 2025-12-21 08:43:18 -05:00
parent 17c0645dc6
commit e6667a6b8c

View file

@ -2,10 +2,13 @@ package skel
import (
"bytes"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"fmt"
"io"
"net"
"strings"
"time"
"github.com/patrickmn/go-cache"
@ -253,7 +256,8 @@ func (p *SkelPlugin) PublicKeyCallback(conn libplugin.ConnMetadata, publicKey []
return nil, err
}
u, err := p.createUpstream(conn, to, nil)
// Pass the client's public key so their fingerprint can be included in the upstream username
u, err := p.createUpstreamWithClientKey(conn, to, nil, publicKey)
if err != nil {
return nil, err
}
@ -262,6 +266,10 @@ func (p *SkelPlugin) PublicKeyCallback(conn libplugin.ConnMetadata, publicKey []
}
func (p *SkelPlugin) createUpstream(conn libplugin.ConnMetadata, to SkelPipeTo, originalPassword []byte) (*libplugin.Upstream, error) {
return p.createUpstreamWithClientKey(conn, to, originalPassword, nil)
}
func (p *SkelPlugin) createUpstreamWithClientKey(conn libplugin.ConnMetadata, to SkelPipeTo, originalPassword []byte, clientPublicKey []byte) (*libplugin.Upstream, error) {
host, port, err := libplugin.SplitHostPortForSSH(to.Host(conn))
if err != nil {
return nil, err
@ -272,6 +280,17 @@ func (p *SkelPlugin) createUpstream(conn libplugin.ConnMetadata, to SkelPipeTo,
user = conn.User()
}
// If client public key is provided, prepend fingerprint to username
// Format: "SHA256:xxxxx.originaluser" - maldoror/upstream can parse this
if clientPublicKey != nil {
hash := sha256.Sum256(clientPublicKey)
fingerprint := base64.RawStdEncoding.EncodeToString(hash[:])
// Replace any dots in fingerprint with underscores to avoid parsing issues
fingerprint = strings.ReplaceAll(fingerprint, ".", "_")
user = "fp:" + fingerprint + "." + user
log.Debugf("passing client fingerprint in username: %s", user)
}
p.cache.SetDefault(conn.UniqueID(), to)
u := &libplugin.Upstream{