extract skel into its own package (#596)

This breaks the dependency of libplugin
on x/crypto, enabling people to use libplugin
without having conflicts over the crypto module.
This commit is contained in:
Josh Bleecher Snyder 2025-05-23 12:42:44 -07:00 • committed by GitHub
parent e6d3bb694b
commit 297b8c60e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 91 additions and 99 deletions

2
go.mod
View file

@ -2,7 +2,7 @@ module github.com/tg123/sshpiper
go 1.24.0 go 1.24.0
toolchain go1.24.1 toolchain go1.24.3
replace golang.org/x/crypto => ./crypto replace golang.org/x/crypto => ./crypto

View file

@ -1,23 +1,27 @@
package libplugin package skel
import ( import (
"bytes" "bytes"
"crypto/subtle" "crypto/subtle"
"fmt" "fmt"
"io"
"net"
"time" "time"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
"github.com/tg123/sshpiper/libplugin"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/knownhosts"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
type SkelPlugin struct { type SkelPlugin struct {
cache *cache.Cache cache *cache.Cache
listPipe func(ConnMetadata) ([]SkelPipe, error) listPipe func(libplugin.ConnMetadata) ([]SkelPipe, error)
} }
func NewSkelPlugin(listPipe func(ConnMetadata) ([]SkelPipe, error)) *SkelPlugin { func NewSkelPlugin(listPipe func(libplugin.ConnMetadata) ([]SkelPipe, error)) *SkelPlugin {
return &SkelPlugin{ return &SkelPlugin{
cache: cache.New(1*time.Minute, 10*time.Minute), cache: cache.New(1*time.Minute, 10*time.Minute),
listPipe: listPipe, listPipe: listPipe,
@ -29,43 +33,43 @@ type SkelPipe interface {
} }
type SkelPipeFrom interface { type SkelPipeFrom interface {
MatchConn(conn ConnMetadata) (SkelPipeTo, error) MatchConn(conn libplugin.ConnMetadata) (SkelPipeTo, error)
} }
type SkelPipeFromPassword interface { type SkelPipeFromPassword interface {
SkelPipeFrom SkelPipeFrom
TestPassword(conn ConnMetadata, password []byte) (bool, error) TestPassword(conn libplugin.ConnMetadata, password []byte) (bool, error)
} }
type SkelPipeFromPublicKey interface { type SkelPipeFromPublicKey interface {
SkelPipeFrom SkelPipeFrom
AuthorizedKeys(conn ConnMetadata) ([]byte, error) AuthorizedKeys(conn libplugin.ConnMetadata) ([]byte, error)
TrustedUserCAKeys(conn ConnMetadata) ([]byte, error) TrustedUserCAKeys(conn libplugin.ConnMetadata) ([]byte, error)
} }
type SkelPipeTo interface { type SkelPipeTo interface {
Host(conn ConnMetadata) string Host(conn libplugin.ConnMetadata) string
User(conn ConnMetadata) string User(conn libplugin.ConnMetadata) string
IgnoreHostKey(conn ConnMetadata) bool IgnoreHostKey(conn libplugin.ConnMetadata) bool
KnownHosts(conn ConnMetadata) ([]byte, error) KnownHosts(conn libplugin.ConnMetadata) ([]byte, error)
} }
type SkelPipeToPassword interface { type SkelPipeToPassword interface {
SkelPipeTo SkelPipeTo
OverridePassword(conn ConnMetadata) ([]byte, error) OverridePassword(conn libplugin.ConnMetadata) ([]byte, error)
} }
type SkelPipeToPrivateKey interface { type SkelPipeToPrivateKey interface {
SkelPipeTo SkelPipeTo
PrivateKey(conn ConnMetadata) ([]byte, []byte, error) PrivateKey(conn libplugin.ConnMetadata) ([]byte, []byte, error)
} }
func (p *SkelPlugin) CreateConfig() *SshPiperPluginConfig { func (p *SkelPlugin) CreateConfig() *libplugin.SshPiperPluginConfig {
return &SshPiperPluginConfig{ return &libplugin.SshPiperPluginConfig{
NextAuthMethodsCallback: p.SupportedMethods, NextAuthMethodsCallback: p.SupportedMethods,
PasswordCallback: p.PasswordCallback, PasswordCallback: p.PasswordCallback,
PublicKeyCallback: p.PublicKeyCallback, PublicKeyCallback: p.PublicKeyCallback,
@ -73,7 +77,7 @@ func (p *SkelPlugin) CreateConfig() *SshPiperPluginConfig {
} }
} }
func (p *SkelPlugin) SupportedMethods(conn ConnMetadata) ([]string, error) { func (p *SkelPlugin) SupportedMethods(conn libplugin.ConnMetadata) ([]string, error) {
set := make(map[string]bool) set := make(map[string]bool)
pipes, err := p.listPipe(conn) pipes, err := p.listPipe(conn)
@ -105,7 +109,7 @@ func (p *SkelPlugin) SupportedMethods(conn ConnMetadata) ([]string, error) {
return methods, nil return methods, nil
} }
func (p *SkelPlugin) VerifyHostKeyCallback(conn ConnMetadata, hostname, netaddr string, key []byte) error { func (p *SkelPlugin) VerifyHostKeyCallback(conn libplugin.ConnMetadata, hostname, netaddr string, key []byte) error {
item, found := p.cache.Get(conn.UniqueID()) item, found := p.cache.Get(conn.UniqueID())
if !found { if !found {
log.Warnf("connection expired when verifying host key for conn [%v]", conn.UniqueID()) log.Warnf("connection expired when verifying host key for conn [%v]", conn.UniqueID())
@ -122,7 +126,7 @@ func (p *SkelPlugin) VerifyHostKeyCallback(conn ConnMetadata, hostname, netaddr
return VerifyHostKeyFromKnownHosts(bytes.NewBuffer(data), hostname, netaddr, key) return VerifyHostKeyFromKnownHosts(bytes.NewBuffer(data), hostname, netaddr, key)
} }
func (p *SkelPlugin) match(conn ConnMetadata, verify func(SkelPipeFrom) (bool, error)) (SkelPipeFrom, SkelPipeTo, error) { func (p *SkelPlugin) match(conn libplugin.ConnMetadata, verify func(SkelPipeFrom) (bool, error)) (SkelPipeFrom, SkelPipeTo, error) {
pipes, err := p.listPipe(conn) pipes, err := p.listPipe(conn)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -154,7 +158,7 @@ func (p *SkelPlugin) match(conn ConnMetadata, verify func(SkelPipeFrom) (bool, e
return nil, nil, fmt.Errorf("no matching pipe for username [%v] found", conn.User()) return nil, nil, fmt.Errorf("no matching pipe for username [%v] found", conn.User())
} }
func (p *SkelPlugin) PasswordCallback(conn ConnMetadata, password []byte) (*Upstream, error) { func (p *SkelPlugin) PasswordCallback(conn libplugin.ConnMetadata, password []byte) (*libplugin.Upstream, error) {
_, to, err := p.match(conn, func(from SkelPipeFrom) (bool, error) { _, to, err := p.match(conn, func(from SkelPipeFrom) (bool, error) {
frompass, ok := from.(SkelPipeFromPassword) frompass, ok := from.(SkelPipeFromPassword)
@ -176,8 +180,7 @@ func (p *SkelPlugin) PasswordCallback(conn ConnMetadata, password []byte) (*Upst
return u, nil return u, nil
} }
func (p *SkelPlugin) PublicKeyCallback(conn ConnMetadata, publicKey []byte) (*Upstream, error) { func (p *SkelPlugin) PublicKeyCallback(conn libplugin.ConnMetadata, publicKey []byte) (*libplugin.Upstream, error) {
pubKey, err := ssh.ParsePublicKey(publicKey) pubKey, err := ssh.ParsePublicKey(publicKey)
if err != nil { if err != nil {
return nil, err return nil, err
@ -246,7 +249,6 @@ func (p *SkelPlugin) PublicKeyCallback(conn ConnMetadata, publicKey []byte) (*Up
return verified, nil return verified, nil
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -259,8 +261,8 @@ func (p *SkelPlugin) PublicKeyCallback(conn ConnMetadata, publicKey []byte) (*Up
return u, nil return u, nil
} }
func (p *SkelPlugin) createUpstream(conn ConnMetadata, to SkelPipeTo, originalPassword []byte) (*Upstream, error) { func (p *SkelPlugin) createUpstream(conn libplugin.ConnMetadata, to SkelPipeTo, originalPassword []byte) (*libplugin.Upstream, error) {
host, port, err := SplitHostPortForSSH(to.Host(conn)) host, port, err := libplugin.SplitHostPortForSSH(to.Host(conn))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -272,7 +274,7 @@ func (p *SkelPlugin) createUpstream(conn ConnMetadata, to SkelPipeTo, originalPa
p.cache.SetDefault(conn.UniqueID(), to) p.cache.SetDefault(conn.UniqueID(), to)
u := &Upstream{ u := &libplugin.Upstream{
Host: host, Host: host,
Port: int32(port), // port is already checked to be within int32 range in SplitHostPortForSSH Port: int32(port), // port is already checked to be within int32 range in SplitHostPortForSSH
UserName: user, UserName: user,
@ -287,9 +289,9 @@ func (p *SkelPlugin) createUpstream(conn ConnMetadata, to SkelPipeTo, originalPa
} }
if overridepassword != nil { if overridepassword != nil {
u.Auth = CreatePasswordAuth(overridepassword) u.Auth = libplugin.CreatePasswordAuth(overridepassword)
} else { } else {
u.Auth = CreatePasswordAuth(originalPassword) u.Auth = libplugin.CreatePasswordAuth(originalPassword)
} }
case SkelPipeToPrivateKey: case SkelPipeToPrivateKey:
@ -298,10 +300,29 @@ func (p *SkelPlugin) createUpstream(conn ConnMetadata, to SkelPipeTo, originalPa
return nil, err return nil, err
} }
u.Auth = CreatePrivateKeyAuth(priv, cert) u.Auth = libplugin.CreatePrivateKeyAuth(priv, cert)
default: default:
return nil, fmt.Errorf("pipe to does not support any auth method") return nil, fmt.Errorf("pipe to does not support any auth method")
} }
return u, err return u, err
} }
func VerifyHostKeyFromKnownHosts(knownhostsData io.Reader, hostname, netaddr string, key []byte) error {
hostKeyCallback, err := knownhosts.NewFromReader(knownhostsData)
if err != nil {
return err
}
pub, err := ssh.ParsePublicKey(key)
if err != nil {
return err
}
addr, err := net.ResolveTCPAddr("tcp", netaddr)
if err != nil {
return err
}
return hostKeyCallback(hostname, addr, pub)
}

View file

@ -7,8 +7,6 @@ import (
"strconv" "strconv"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/knownhosts"
) )
func AuthMethodTypeToName(a AuthMethod) string { func AuthMethodTypeToName(a AuthMethod) string {
@ -89,7 +87,6 @@ func SplitHostPortForSSH(addr string) (host string, port int, err error) {
// DialForSSH is the modified version of net.Dial, would add ":22" automaticlly // DialForSSH is the modified version of net.Dial, would add ":22" automaticlly
func DialForSSH(addr string) (net.Conn, error) { func DialForSSH(addr string) (net.Conn, error) {
if _, _, err := net.SplitHostPort(addr); err != nil && addr != "" { if _, _, err := net.SplitHostPort(addr); err != nil && addr != "" {
// test valid after concat :22 // test valid after concat :22
if _, _, err := net.SplitHostPort(addr + ":22"); err == nil { if _, _, err := net.SplitHostPort(addr + ":22"); err == nil {
@ -154,22 +151,3 @@ func CreateRetryCurrentPluginAuth(meta map[string]string) *Upstream_RetryCurrent
}, },
} }
} }
func VerifyHostKeyFromKnownHosts(knownhostsData io.Reader, hostname, netaddr string, key []byte) error {
hostKeyCallback, err := knownhosts.NewFromReader(knownhostsData)
if err != nil {
return err
}
pub, err := ssh.ParsePublicKey(key)
if err != nil {
return err
}
addr, err := net.ResolveTCPAddr("tcp", netaddr)
if err != nil {
return err
}
return hostKeyCallback(hostname, addr, pub)
}

View file

@ -4,11 +4,11 @@ package main
import ( import (
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
func main() { func main() {
libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{ libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{
Name: "docker", Name: "docker",
Usage: "sshpiperd docker plugin, see config in https://github.com/tg123/sshpiper/tree/master/plugin/docker", Usage: "sshpiperd docker plugin, see config in https://github.com/tg123/sshpiper/tree/master/plugin/docker",
@ -18,7 +18,7 @@ func main() {
return nil, err return nil, err
} }
skel := libplugin.NewSkelPlugin(plugin.listPipe) skel := skel.NewSkelPlugin(plugin.listPipe)
return skel.CreateConfig(), nil return skel.CreateConfig(), nil
}, },
}) })

View file

@ -6,6 +6,7 @@ import (
"encoding/base64" "encoding/base64"
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
) )
type skelpipeWrapper struct { type skelpipeWrapper struct {
@ -40,18 +41,17 @@ type skelpipeToPrivateKeyWrapper struct {
skelpipeToWrapper skelpipeToWrapper
} }
func (s *skelpipeWrapper) From() []libplugin.SkelPipeFrom { func (s *skelpipeWrapper) From() []skel.SkelPipeFrom {
w := skelpipeFromWrapper{ w := skelpipeFromWrapper{
skelpipeWrapper: *s, skelpipeWrapper: *s,
} }
if s.pipe.PrivateKey != "" || s.pipe.AuthorizedKeys != "" { if s.pipe.PrivateKey != "" || s.pipe.AuthorizedKeys != "" {
return []libplugin.SkelPipeFrom{&skelpipePublicKeyWrapper{ return []skel.SkelPipeFrom{&skelpipePublicKeyWrapper{
skelpipeFromWrapper: w, skelpipeFromWrapper: w,
}} }}
} else { } else {
return []libplugin.SkelPipeFrom{&skelpipePasswordWrapper{ return []skel.SkelPipeFrom{&skelpipePasswordWrapper{
skelpipeFromWrapper: w, skelpipeFromWrapper: w,
}} }}
} }
@ -73,7 +73,7 @@ func (s *skelpipeToWrapper) KnownHosts(conn libplugin.ConnMetadata) ([]byte, err
return nil, nil // TODO support this return nil, nil // TODO support this
} }
func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (libplugin.SkelPipeTo, error) { func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (skel.SkelPipeTo, error) {
user := conn.User() user := conn.User()
matched := s.pipe.ClientUsername == user || s.pipe.ClientUsername == "" matched := s.pipe.ClientUsername == user || s.pipe.ClientUsername == ""
@ -130,13 +130,13 @@ func (s *skelpipeToPasswordWrapper) OverridePassword(conn libplugin.ConnMetadata
return nil, nil return nil, nil
} }
func (p *plugin) listPipe(_ libplugin.ConnMetadata) ([]libplugin.SkelPipe, error) { func (p *plugin) listPipe(_ libplugin.ConnMetadata) ([]skel.SkelPipe, error) {
dpipes, err := p.list() dpipes, err := p.list()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var pipes []libplugin.SkelPipe var pipes []skel.SkelPipe
for _, pipe := range dpipes { for _, pipe := range dpipes {
wrapper := &skelpipeWrapper{ wrapper := &skelpipeWrapper{
plugin: p, plugin: p,

View file

@ -2,6 +2,7 @@ package main
import ( import (
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -28,7 +29,7 @@ func main() {
if err != nil { if err != nil {
return nil, err return nil, err
} }
skel := libplugin.NewSkelPlugin(plugin.listPipe) skel := skel.NewSkelPlugin(plugin.listPipe)
return skel.CreateConfig(), nil return skel.CreateConfig(), nil
}, },
}) })

View file

@ -10,6 +10,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/tg123/go-htpasswd" "github.com/tg123/go-htpasswd"
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
piperv1beta1 "github.com/tg123/sshpiper/plugin/kubernetes/apis/sshpiper/v1beta1" piperv1beta1 "github.com/tg123/sshpiper/plugin/kubernetes/apis/sshpiper/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
@ -52,8 +53,8 @@ type skelpipeToPrivateKeyWrapper struct {
skelpipeToWrapper skelpipeToWrapper
} }
func (s *skelpipeWrapper) From() []libplugin.SkelPipeFrom { func (s *skelpipeWrapper) From() []skel.SkelPipeFrom {
var froms []libplugin.SkelPipeFrom var froms []skel.SkelPipeFrom
for _, f := range s.pipe.Spec.From { for _, f := range s.pipe.Spec.From {
w := &skelpipeFromWrapper{ w := &skelpipeFromWrapper{
@ -92,7 +93,7 @@ func (s *skelpipeToWrapper) KnownHosts(conn libplugin.ConnMetadata) ([]byte, err
return base64.StdEncoding.DecodeString(s.to.KnownHostsData) return base64.StdEncoding.DecodeString(s.to.KnownHostsData)
} }
func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (libplugin.SkelPipeTo, error) { func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (skel.SkelPipeTo, error) {
user := conn.User() user := conn.User()
matched := s.from.Username == user matched := s.from.Username == user
@ -142,7 +143,6 @@ func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (libplugin.
} }
func (s *skelpipePasswordWrapper) TestPassword(conn libplugin.ConnMetadata, password []byte) (bool, error) { func (s *skelpipePasswordWrapper) TestPassword(conn libplugin.ConnMetadata, password []byte) (bool, error) {
pwds, err := loadStringAndFile(s.from.HtpasswdData, s.from.HtpasswdFile) pwds, err := loadStringAndFile(s.from.HtpasswdData, s.from.HtpasswdFile)
if err != nil { if err != nil {
return false, err return false, err
@ -179,7 +179,6 @@ func (s *skelpipePublicKeyWrapper) TrustedUserCAKeys(conn libplugin.ConnMetadata
} }
func (s *skelpipeToPrivateKeyWrapper) PrivateKey(conn libplugin.ConnMetadata) ([]byte, []byte, error) { func (s *skelpipeToPrivateKeyWrapper) PrivateKey(conn libplugin.ConnMetadata) ([]byte, []byte, error) {
log.Debugf("mapping to %v private key using secret %v", s.to.Host, s.to.PrivateKeySecret.Name) log.Debugf("mapping to %v private key using secret %v", s.to.Host, s.to.PrivateKeySecret.Name)
secret, err := s.plugin.k8sclient.Secrets(s.pipe.Namespace).Get(context.Background(), s.to.PrivateKeySecret.Name, metav1.GetOptions{}) secret, err := s.plugin.k8sclient.Secrets(s.pipe.Namespace).Get(context.Background(), s.to.PrivateKeySecret.Name, metav1.GetOptions{})
if err != nil { if err != nil {
@ -237,7 +236,6 @@ func (s *skelpipeToPasswordWrapper) OverridePassword(conn libplugin.ConnMetadata
} }
func loadStringAndFile(base64orraw string, filepath string) ([][]byte, error) { func loadStringAndFile(base64orraw string, filepath string) ([][]byte, error) {
all := make([][]byte, 0, 2) all := make([][]byte, 0, 2)
if base64orraw != "" { if base64orraw != "" {
@ -261,13 +259,13 @@ func loadStringAndFile(base64orraw string, filepath string) ([][]byte, error) {
return all, nil return all, nil
} }
func (p *plugin) listPipe(_ libplugin.ConnMetadata) ([]libplugin.SkelPipe, error) { func (p *plugin) listPipe(_ libplugin.ConnMetadata) ([]skel.SkelPipe, error) {
kpipes, err := p.list() kpipes, err := p.list()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var pipes []libplugin.SkelPipe var pipes []skel.SkelPipe
for _, pipe := range kpipes { for _, pipe := range kpipes {
wrapper := &skelpipeWrapper{ wrapper := &skelpipeWrapper{
plugin: p, plugin: p,

View file

@ -7,11 +7,11 @@ import (
"github.com/pquerna/otp/totp" "github.com/pquerna/otp/totp"
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
func main() { func main() {
libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{ libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{
Name: "workingdir", Name: "workingdir",
Usage: "sshpiperd workingdir plugin", Usage: "sshpiperd workingdir plugin",
@ -54,7 +54,6 @@ func main() {
}, },
}, },
CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) { CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) {
fac := workdingdirFactory{ fac := workdingdirFactory{
root: c.String("root"), root: c.String("root"),
allowBadUsername: c.Bool("allow-baduser-name"), allowBadUsername: c.Bool("allow-baduser-name"),
@ -66,10 +65,9 @@ func main() {
checktotp := c.Bool("check-totp") checktotp := c.Bool("check-totp")
skel := libplugin.NewSkelPlugin(fac.listPipe) skel := skel.NewSkelPlugin(fac.listPipe)
config := skel.CreateConfig() config := skel.CreateConfig()
config.NextAuthMethodsCallback = func(conn libplugin.ConnMetadata) ([]string, error) { config.NextAuthMethodsCallback = func(conn libplugin.ConnMetadata) ([]string, error) {
auth := []string{"publickey"} auth := []string{"publickey"}
if !fac.noPasswordAuth { if !fac.noPasswordAuth {
@ -80,7 +78,6 @@ func main() {
if conn.GetMeta("totp") != "checked" { if conn.GetMeta("totp") != "checked" {
auth = []string{"keyboard-interactive"} auth = []string{"keyboard-interactive"}
} }
} }
return auth, nil return auth, nil

View file

@ -8,6 +8,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
) )
type workdingdirFactory struct { type workdingdirFactory struct {
@ -50,18 +51,17 @@ type skelpipeToPrivateKeyWrapper struct {
skelpipeToWrapper skelpipeToWrapper
} }
func (s *skelpipeWrapper) From() []libplugin.SkelPipeFrom { func (s *skelpipeWrapper) From() []skel.SkelPipeFrom {
w := skelpipeFromWrapper{ w := skelpipeFromWrapper{
skelpipeWrapper: *s, skelpipeWrapper: *s,
} }
if s.dir.Exists(userAuthorizedKeysFile) && s.dir.Exists(userKeyFile) { if s.dir.Exists(userAuthorizedKeysFile) && s.dir.Exists(userKeyFile) {
return []libplugin.SkelPipeFrom{&skelpipePublicKeyWrapper{ return []skel.SkelPipeFrom{&skelpipePublicKeyWrapper{
skelpipeFromWrapper: w, skelpipeFromWrapper: w,
}} }}
} else { } else {
return []libplugin.SkelPipeFrom{&skelpipePasswordWrapper{ return []skel.SkelPipeFrom{&skelpipePasswordWrapper{
skelpipeFromWrapper: w, skelpipeFromWrapper: w,
}} }}
} }
@ -83,8 +83,7 @@ func (s *skelpipeToWrapper) KnownHosts(conn libplugin.ConnMetadata) ([]byte, err
return s.dir.Readfile(userKnownHosts) return s.dir.Readfile(userKnownHosts)
} }
func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (libplugin.SkelPipeTo, error) { func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (skel.SkelPipeTo, error) {
if s.dir.Exists(userKeyFile) { if s.dir.Exists(userKeyFile) {
return &skelpipeToPrivateKeyWrapper{ return &skelpipeToPrivateKeyWrapper{
skelpipeToWrapper: skelpipeToWrapper(*s), skelpipeToWrapper: skelpipeToWrapper(*s),
@ -121,8 +120,7 @@ func (s *skelpipeToPasswordWrapper) OverridePassword(conn libplugin.ConnMetadata
return nil, nil return nil, nil
} }
func (wf *workdingdirFactory) listPipe(conn libplugin.ConnMetadata) ([]libplugin.SkelPipe, error) { func (wf *workdingdirFactory) listPipe(conn libplugin.ConnMetadata) ([]skel.SkelPipe, error) {
user := conn.User() user := conn.User()
if !wf.allowBadUsername { if !wf.allowBadUsername {
@ -131,11 +129,10 @@ func (wf *workdingdirFactory) listPipe(conn libplugin.ConnMetadata) ([]libplugin
} }
} }
var pipes []libplugin.SkelPipe var pipes []skel.SkelPipe
userdir := path.Join(wf.root, conn.User()) userdir := path.Join(wf.root, conn.User())
_ = filepath.Walk(userdir, func(path string, info os.FileInfo, err error) (stop error) { _ = filepath.Walk(userdir, func(path string, info os.FileInfo, err error) (stop error) {
log.Infof("search upstreams in path: %v", path) log.Infof("search upstreams in path: %v", path)
if err != nil { if err != nil {
log.Infof("error walking path: %v", err) log.Infof("error walking path: %v", err)

View file

@ -17,12 +17,10 @@ type workingdir struct {
Strict bool Strict bool
} }
var ( // Base username validation on Debians default: https://sources.debian.net/src/adduser/3.113%2Bnmu3/adduser.conf/#L85
// Base username validation on Debians default: https://sources.debian.net/src/adduser/3.113%2Bnmu3/adduser.conf/#L85 // -> NAME_REGEX="^[a-z][-a-z0-9_]*\$"
// -> NAME_REGEX="^[a-z][-a-z0-9_]*\$" // The length is limited to 32 characters. See man 8 useradd: https://linux.die.net/man/8/useradd
// The length is limited to 32 characters. See man 8 useradd: https://linux.die.net/man/8/useradd var usernameRule *regexp.Regexp = regexp.MustCompile("^[a-z_][-a-z0-9_]{0,31}$")
usernameRule *regexp.Regexp = regexp.MustCompile("^[a-z_][-a-z0-9_]{0,31}$")
)
const ( const (
userAuthorizedKeysFile = "authorized_keys" userAuthorizedKeysFile = "authorized_keys"
@ -52,7 +50,7 @@ func (w *workingdir) checkPerm(file string) error {
return nil return nil
} }
if fi.Mode().Perm()&0077 != 0 { if fi.Mode().Perm()&0o077 != 0 {
return fmt.Errorf("%v's perm is too open", filename) return fmt.Errorf("%v's perm is too open", filename)
} }

View file

@ -4,6 +4,7 @@ package main
import ( import (
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -29,7 +30,7 @@ func main() {
}, },
}, },
CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) { CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) {
skel := libplugin.NewSkelPlugin(plugin.listPipe) skel := skel.NewSkelPlugin(plugin.listPipe)
return skel.CreateConfig(), nil return skel.CreateConfig(), nil
}, },
}) })

View file

@ -4,12 +4,14 @@ package main
import ( import (
"errors" "errors"
log "github.com/sirupsen/logrus"
"os/user" "os/user"
"regexp" "regexp"
"slices" "slices"
log "github.com/sirupsen/logrus"
"github.com/tg123/sshpiper/libplugin" "github.com/tg123/sshpiper/libplugin"
"github.com/tg123/sshpiper/libplugin/skel"
) )
type skelpipeWrapper struct { type skelpipeWrapper struct {
@ -45,8 +47,8 @@ type skelpipeToPrivateKeyWrapper struct {
skelpipeToWrapper skelpipeToWrapper
} }
func (s *skelpipeWrapper) From() []libplugin.SkelPipeFrom { func (s *skelpipeWrapper) From() []skel.SkelPipeFrom {
var froms []libplugin.SkelPipeFrom var froms []skel.SkelPipeFrom
for _, f := range s.pipe.From { for _, f := range s.pipe.From {
w := &skelpipeFromWrapper{ w := &skelpipeFromWrapper{
@ -87,7 +89,7 @@ func (s *skelpipeToWrapper) KnownHosts(conn libplugin.ConnMetadata) ([]byte, err
}) })
} }
func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (libplugin.SkelPipeTo, error) { func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (skel.SkelPipeTo, error) {
username := conn.User() username := conn.User()
targetuser := s.to.Username targetuser := s.to.Username
@ -175,7 +177,6 @@ func (s *skelpipeToPrivateKeyWrapper) PrivateKey(conn libplugin.ConnMetadata) ([
"DOWNSTREAM_USER": conn.User(), "DOWNSTREAM_USER": conn.User(),
"UPSTREAM_USER": s.username, "UPSTREAM_USER": s.username,
}) })
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -187,13 +188,13 @@ func (s *skelpipeToPasswordWrapper) OverridePassword(conn libplugin.ConnMetadata
return nil, nil return nil, nil
} }
func (p *plugin) listPipe(_ libplugin.ConnMetadata) ([]libplugin.SkelPipe, error) { func (p *plugin) listPipe(_ libplugin.ConnMetadata) ([]skel.SkelPipe, error) {
configs, err := p.loadConfig() configs, err := p.loadConfig()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var pipes []libplugin.SkelPipe var pipes []skel.SkelPipe
for _, config := range configs { for _, config := range configs {
for _, pipe := range config.Pipes { for _, pipe := range config.Pipes {
wrapper := &skelpipeWrapper{ wrapper := &skelpipeWrapper{