From e6d3bb694b6ed7f26dbe2f55c3c3a9786bf8bb88 Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Mon, 19 May 2025 12:31:04 -0700 Subject: [PATCH] Add upstream banner mode and refactor banner callbacks (#594) * feat: add upstream banner mode and refactor banner callbacks * chore: update crypto submodule to latest commit * fix: improve usage description for upstream banner mode flag * Update cmd/sshpiperd/daemon.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cmd/sshpiperd/daemon.go | 14 +++++++++++++- cmd/sshpiperd/internal/plugin/chain.go | 10 +++++----- cmd/sshpiperd/internal/plugin/grpc.go | 8 ++++---- cmd/sshpiperd/main.go | 7 +++++++ crypto | 2 +- e2e/sshdconfig/banner | 2 +- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/cmd/sshpiperd/daemon.go b/cmd/sshpiperd/daemon.go index 711f255c..954dbaf5 100644 --- a/cmd/sshpiperd/daemon.go +++ b/cmd/sshpiperd/daemon.go @@ -134,7 +134,7 @@ func newDaemon(ctx *cli.Context) (*daemon, error) { bannerfile := ctx.String("banner-file") if bannertext != "" || bannerfile != "" { - config.BannerCallback = func(_ ssh.ConnMetadata, _ ssh.ChallengeContext) string { + config.DownstreamBannerCallback = func(_ ssh.ConnMetadata, _ ssh.ChallengeContext) string { if bannerfile != "" { text, err := os.ReadFile(bannerfile) if err != nil { @@ -147,6 +147,18 @@ func newDaemon(ctx *cli.Context) (*daemon, error) { } } + switch ctx.String("upstream-banner-mode") { + case "passthrough": + // library will handle the banner to client + case "ignore": + config.UpstreamBannerCallback = func(downconn ssh.ServerPreAuthConn, banner string, challengeCtx ssh.ChallengeContext) error { + return nil + } + // case "dedup": + default: + return nil, fmt.Errorf("unknown upstream banner mode %q; allowed: 'passthrough' or 'ignore'", ctx.String("upstream-banner-mode")) + } + return &daemon{ config: config, lis: lis, diff --git a/cmd/sshpiperd/internal/plugin/chain.go b/cmd/sshpiperd/internal/plugin/chain.go index 5a85ebd7..7b7a2bc0 100644 --- a/cmd/sshpiperd/internal/plugin/chain.go +++ b/cmd/sshpiperd/internal/plugin/chain.go @@ -54,7 +54,7 @@ type chainConnMeta struct { current int } -func (cp *ChainPlugins) CreateChallengeContext(conn ssh.ConnMetadata) (ssh.ChallengeContext, error) { +func (cp *ChainPlugins) CreateChallengeContext(conn ssh.ServerPreAuthConn) (ssh.ChallengeContext, error) { uiq, err := uuid.NewRandom() if err != nil { return nil, err @@ -109,7 +109,7 @@ func (cp *ChainPlugins) NextAuthMethods(conn ssh.ConnMetadata, challengeCtx ssh. func (cp *ChainPlugins) InstallPiperConfig(config *GrpcPluginConfig) error { - config.CreateChallengeContext = func(conn ssh.ConnMetadata) (ssh.ChallengeContext, error) { + config.CreateChallengeContext = func(conn ssh.ServerPreAuthConn) (ssh.ChallengeContext, error) { ctx, err := cp.CreateChallengeContext(conn) if err != nil { log.Errorf("cannot create challenge context %v", err) @@ -143,10 +143,10 @@ func (cp *ChainPlugins) InstallPiperConfig(config *GrpcPluginConfig) error { } } - config.BannerCallback = func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) string { + config.DownstreamBannerCallback = func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) string { cur := cp.pluginsCallback[challengeCtx.(*chainConnMeta).current] - if cur.BannerCallback != nil { - return cur.BannerCallback(conn, challengeCtx) + if cur.DownstreamBannerCallback != nil { + return cur.DownstreamBannerCallback(conn, challengeCtx) } return "" diff --git a/cmd/sshpiperd/internal/plugin/grpc.go b/cmd/sshpiperd/internal/plugin/grpc.go index d052bb1c..0c3a721a 100644 --- a/cmd/sshpiperd/internal/plugin/grpc.go +++ b/cmd/sshpiperd/internal/plugin/grpc.go @@ -56,7 +56,7 @@ func (g *GrpcPlugin) InstallPiperConfig(config *GrpcPluginConfig) error { return err } - config.CreateChallengeContext = func(conn ssh.ConnMetadata) (ssh.ChallengeContext, error) { + config.CreateChallengeContext = func(conn ssh.ServerPreAuthConn) (ssh.ChallengeContext, error) { ctx, err := g.CreateChallengeContext(conn) if err != nil { log.Errorf("cannot create challenge context %v", err) @@ -121,7 +121,7 @@ func (g *GrpcPlugin) InstallPiperConfig(config *GrpcPluginConfig) error { g.UpstreamAuthFailureCallbackRemote(conn, method, err, challengeCtx) } case "Banner": - config.BannerCallback = g.BannerCallback + config.DownstreamBannerCallback = g.DownstreamBannerCallback case "VerifyHostKey": // ignore case "PipeStart": @@ -155,7 +155,7 @@ func (m *connMeta) Meta() interface{} { return m } -func (g *GrpcPlugin) CreateChallengeContext(conn ssh.ConnMetadata) (ssh.ChallengeContext, error) { +func (g *GrpcPlugin) CreateChallengeContext(conn ssh.ServerPreAuthConn) (ssh.ChallengeContext, error) { uiq, err := uuid.NewRandom() if err != nil { return nil, err @@ -494,7 +494,7 @@ func (g *GrpcPlugin) KeyboardInteractiveCallback(conn ssh.ConnMetadata, client s } } -func (g *GrpcPlugin) BannerCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) string { +func (g *GrpcPlugin) DownstreamBannerCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) string { meta := toMeta(challengeCtx, conn) reply, err := g.client.Banner(context.Background(), &libplugin.BannerRequest{ Meta: meta, diff --git a/cmd/sshpiperd/main.go b/cmd/sshpiperd/main.go index 1f9aa120..3f5bfbe8 100644 --- a/cmd/sshpiperd/main.go +++ b/cmd/sshpiperd/main.go @@ -166,6 +166,12 @@ func main() { Usage: "display a banner from file before authentication", EnvVars: []string{"SSHPIPERD_BANNERFILE"}, }, + &cli.StringFlag{ + Name: "upstream-banner-mode", + Value: "passthrough", + Usage: "upstream banner mode, allowed values: 'passthrough' (pass the banner from upstream to client) or 'ignore' (ignore the banner from upstream)", + EnvVars: []string{"SSHPIPERD_UPSTREAM_BANNER_MODE"}, + }, &cli.BoolFlag{ Name: "drop-hostkeys-message", Value: false, @@ -299,6 +305,7 @@ func main() { log.Errorf("plugin %v recv logs error: %v", p.Name, err) } }() + plugins = append(plugins, p) } diff --git a/crypto b/crypto index 2a4b9c24..ff1e56f4 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit 2a4b9c2448bc0257714a950e5d61a55d3633eb65 +Subproject commit ff1e56f4b7ef0bf248a7bba9c996414504ffa09a diff --git a/e2e/sshdconfig/banner b/e2e/sshdconfig/banner index e0b8fdc5..3de2e879 100644 --- a/e2e/sshdconfig/banner +++ b/e2e/sshdconfig/banner @@ -1 +1 @@ -sshpiper banner from upstream test \ No newline at end of file +sshpiper banner from upstream test