go x/crypto/ssh: 非标准的常量 maxAgentResponseBytes 应该可配置,

e0bqpujr  于 24天前  发布在  Go
关注(0)|答案(9)|浏览(18)

Go版本

go版本:go1.21.1 darwin/arm64

你正在使用什么操作系统和处理器架构(go env)?

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/cweagans/Library/Caches/go-build'
GOENV='/Users/cweagans/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/cweagans/Developer/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/cweagans/Developer/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.21.1/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.21.1/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.21.1'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/cweagans/Developer/github.com/swirldslabs/txauth/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/xh/xytgk13j0v33_8zj9qdtpyh80000gp/T/go-build3042814605=/tmp/go-build -gno-record-gcc-switches -fno-common'

你做了什么?

我们有一个代理协议扩展,有时可以通过代理协议发送相当多的数据 - 超过了由https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.17.0:ssh/agent/client.go;l=156施加的16kb限制。由于代理协议没有指定限制,我没想到Go库会施加一个限制,最终花了很多时间缩小问题的范围。

你期望看到什么?

我期望能够通过连接发送任意数量的任意数据。

你看到了什么?

我无法做到这一点,并在日志中得到了这个错误:agent 11: agent: client error: response too large

yb3bgrhw

yb3bgrhw2#

感谢cweagans报告此问题。您能否分享一个复现器并确认它与OpenSSH代理实现一起工作?

k4ymrczo

k4ymrczo3#

当然,给你!
OpenSSH似乎没有使用任何代理协议扩展,除了session-bind@openssh.com(用于设置key restrictions),所以我不确定如何将其与OpenSSH的任何实现进行验证。任何广泛使用的实现都不会真正关心自定义协议扩展。
我的特定用例与协议扩展(本质上是特定于我的项目)有关,但我在规范中没有看到指定最大代理回复大小的地方。关于maxAgentResponseBytes的注解也同意这一点。看起来client.List()依赖于这个值来判断代理回复中的密钥是否过多,而client.CallRaw以您所期望的方式使用它。我不太确定client.List()在使用该变量时是否做得正确。
只是在这里集思广益:有什么理由不让maxAgentResponseBytes成为一个公共变量,开发者可以简单地更改它?默认值可以保持不变——在大多数情况下,这是一个相当合理的限制。或者,我们可以添加一个额外的接口(具有方法GetMaxAgentResponseBytes() int或类似功能的ConfigurableAgent),可以根据每个代理的基础覆盖该限制(这样内部方法将使用maxAgentResponseBytes的值或返回的GetMaxAgentResponseBytes())。
我在项目中暂时通过简单地将补丁应用于agent包来解决这个问题,将值更改为16 << 24
testagent.go(展开源代码)

package main

import (
	"crypto/rand"
	"errors"
	"flag"
	"io"
	"log"
	"net"
	"os"
	"os/signal"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

var socketPath = "/tmp/response-limit-tester-agent.sock"

var serveAgent bool

func main() {
	flag.BoolVar(&serveAgent, "serve", false, "run the testing agent (otherwise, will run the client)")
	flag.Parse()

	if serveAgent {
		runAgent()
	} else {
		runClient()
	}
}

func runClient() {
	c, err := net.Dial("unix", socketPath)
	if err != nil {
		log.Fatalf("failed to dial testing agent: %v", err)
	}

	client := agent.NewClient(c)

	res, err := client.Extension("responselimittester@golang.org", []byte{})
	if err != nil {
		log.Fatalf("failed to get response from testing agent: %v", err)
	}

	log.Printf("got response from testing agent: %d bytes", len(res))
}

func runAgent() {
	ln, err := net.Listen("unix", socketPath)
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()
	log.Printf("listening on %s", socketPath)

	// Handle interrupts.
	exit := make(chan os.Signal, 1)
	signal.Notify(exit, os.Interrupt)
	go func() {
		<-exit
		log.Println("received SIGINT; shutting down")
		ln.Close()
		os.Exit(0)
	}()

	log.Println("ready")

	for {
		c, err := ln.Accept()
		if errors.Is(err, net.ErrClosed) {
			return
		}
		if err != nil {
			log.Println("error accepting connection:", err)
		}

		go handleConnection(c)
	}
}

func handleConnection(c net.Conn) {
	log.Println("client connected; serving testing agent")
	defer c.Close()
	a := &TestingAgent{}
	err := agent.ServeAgent(a, c)
	if err == io.EOF {
		err = nil
	}
	if err != nil {
		log.Fatalf("failed to serve agent: %v", err)
	}
	log.Println("connection closed")
}

var ErrNotImplemented = errors.New("not implemented")

// TestingAgent only implements the protocol extension and just passes random data back to the client.
type TestingAgent struct{}

func (a *TestingAgent) List() ([]*agent.Key, error) {
	return nil, ErrNotImplemented
}

func (a *TestingAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
	return nil, ErrNotImplemented
}

func (a *TestingAgent) Add(key agent.AddedKey) error {
	return ErrNotImplemented
}

func (a *TestingAgent) Remove(key ssh.PublicKey) error {
	return ErrNotImplemented
}

func (a *TestingAgent) RemoveAll() error {
	return ErrNotImplemented
}

func (a *TestingAgent) Lock(passphrase []byte) error {
	return ErrNotImplemented
}

func (a *TestingAgent) Unlock(passphrase []byte) error {
	return ErrNotImplemented
}

func (a *TestingAgent) Signers() ([]ssh.Signer, error) {
	return nil, ErrNotImplemented
}

func (a *TestingAgent) SignWithFlags(key ssh.PublicKey, data []byte, flags agent.SignatureFlags) (*ssh.Signature, error) {
	return nil, ErrNotImplemented
}

func (a *TestingAgent) Extension(extensionType string, contents []byte) ([]byte, error) {
	log.Println("got extension request with type:", extensionType)

	if extensionType == "responselimittester@golang.org" {
		response := make([]byte, 16<<24)
		rand.Read(response)
		return response, nil
	}

	return nil, nil
}

更改 maxAgentResponseBytes 之前:

$ ./testagent -serve
2024/01/09 11:10:14 listening on /tmp/response-limit-tester-agent.sock
2024/01/09 11:10:14 ready
2024/01/09 11:10:16 client connected; serving testing agent
2024/01/09 11:10:16 got extension request with type: responselimittester@golang.org
2024/01/09 11:10:17 failed to serve agent: agent: reply too large: 268435456 bytes

# In another terminal:
$ ./testagent
2024/01/09 11:10:17 failed to get response from testing agent: agent: client error: EOF

更改 maxAgentResponseBytes 之后:

./testagent -serve
2024/01/09 11:09:26 listening on /tmp/response-limit-tester-agent.sock
2024/01/09 11:09:26 ready
2024/01/09 11:09:31 client connected; serving testing agent
2024/01/09 11:09:31 got extension request with type: responselimittester@golang.org
2024/01/09 11:09:32 connection closed
^C2024/01/09 11:10:02 received SIGINT; shutting down

# In another terminal:

$ ./testagent
2024/01/09 11:09:32 got response from testing agent: 268435456 bytes
mrwjdhj3

mrwjdhj34#

非常感谢您的回复。我之所以提出这个问题,是因为OpenSSH似乎也有一个类似的限制。

您的请求是有道理的,我们需要提出新的API并通过审核。

gijlo24d

gijlo24d5#

好的,我能帮什么忙?我应该写那份提案吗?你对哪个方向有想法?(公共变量vs新接口vs仅更改默认值)

sd2nnvve

sd2nnvve6#

在这段代码中,限制是在初始实现时引入的。它并没有为了解决特定的问题而添加,而只是作为一种防止消耗过多内存等的合理性检查。

也许我们可以增加这个限制。对于你的用例来说,什么是一个合适的值呢?例如,1MB的限制对于任何用例都是适当的,同时我们仍然保持现有的合理性检查不变。

我不认为使用公共变量是一个好选择。如果不能接受改变默认值,我们应该添加一个接口扩展或者一个选项(例如func NewClient(rw io.ReadWriter, options ...AgentOption) ExtendedAgent)。

我希望得到其他维护者的意见
cc @golang/security

jc3wubiy

jc3wubiy7#

我认为我们不应该仅仅更改默认值。这可能会解决我个人的问题,但限制的理想值确实取决于你如何使用库。
在我看来,它应该真正可配置,以便开发人员可以为他们的特定应用程序设置正确的值。

x0fgdtte

x0fgdtte8#

嗯,我想不出代理不可信的场景,也许我们可以移除这个限制,而不是让它可配置。

7kjnsjlb

7kjnsjlb9#

这对我来说似乎很合理。

相关问题