fix(ssh): let pushes finish when the client disconnects early (#40105)

With the built-in SSH server, closing the client connection cancelled
the `gitea serv` process tree. go-git based clients close it right after
the push report, so post-receive was killed and branches, pull requests,
webhooks and Actions were not updated.

The command now runs under the server's hammer context like with
OpenSSH, where it ends once its pipes close or at shutdown.
This commit is contained in:
silverwind
2026-10-31 16:24:13 +01:00
committed by GitHub
parent bd5fbcfb29
commit fc4d490b35
2 changed files with 5 additions and 12 deletions

View File

@@ -28,7 +28,6 @@ type sshServer struct {
type sshSession struct { type sshSession struct {
gossh.Channel gossh.Channel
conn *gossh.ServerConn conn *gossh.ServerConn
ctx context.Context
rawCmd string rawCmd string
env []string env []string
} }
@@ -86,10 +85,7 @@ func (srv *sshServer) serve(listener net.Listener) error {
func (srv *sshServer) handleConn(netConn net.Conn) { func (srv *sshServer) handleConn(netConn net.Conn) {
defer netConn.Close() defer netConn.Close()
ctx, cancel := context.WithCancel(graceful.GetManager().HammerContext()) sshConn, sshChannels, sshReqs, err := gossh.NewServerConn(netConn, srv.newServerConfig(graceful.GetManager().HammerContext()))
defer cancel()
sshConn, sshChannels, sshReqs, err := gossh.NewServerConn(netConn, srv.newServerConfig(ctx))
if err != nil { if err != nil {
// OpenSSH's logs are something like: // OpenSSH's logs are something like:
// * disconnect without sending anything: "Connection closed by 1.2.3.4 port 5678" // * disconnect without sending anything: "Connection closed by 1.2.3.4 port 5678"
@@ -107,11 +103,11 @@ func (srv *sshServer) handleConn(netConn net.Conn) {
_ = newChan.Reject(gossh.UnknownChannelType, "unsupported channel type") _ = newChan.Reject(gossh.UnknownChannelType, "unsupported channel type")
continue continue
} }
go handleSessionChannel(ctx, sshConn, newChan) go handleSessionChannel(sshConn, newChan)
} }
} }
func handleSessionChannel(ctx context.Context, conn *gossh.ServerConn, newChan gossh.NewChannel) { func handleSessionChannel(conn *gossh.ServerConn, newChan gossh.NewChannel) {
channel, reqs, err := newChan.Accept() channel, reqs, err := newChan.Accept()
if err != nil { if err != nil {
log.Error("SSH: Accept session channel: %v", err) log.Error("SSH: Accept session channel: %v", err)
@@ -119,7 +115,7 @@ func handleSessionChannel(ctx context.Context, conn *gossh.ServerConn, newChan g
} }
defer channel.Close() defer channel.Close()
session := &sshSession{Channel: channel, conn: conn, ctx: ctx} session := &sshSession{Channel: channel, conn: conn}
for req := range reqs { for req := range reqs {
switch req.Type { switch req.Type {
case "env": case "env":

View File

@@ -64,9 +64,6 @@ func sessionHandler(session *sshSession) int {
args := []string{"--config=" + setting.CustomConf, "serv", "key-" + keyID} args := []string{"--config=" + setting.CustomConf, "serv", "key-" + keyID}
log.Trace("SSH: Arguments: %v", args) log.Trace("SSH: Arguments: %v", args)
ctx, cancel := context.WithCancel(session.ctx)
defer cancel()
gitProtocol := "" gitProtocol := ""
for _, env := range session.env { for _, env := range session.env {
if strings.HasPrefix(env, "GIT_PROTOCOL=") { if strings.HasPrefix(env, "GIT_PROTOCOL=") {
@@ -75,7 +72,7 @@ func sessionHandler(session *sshSession) int {
} }
} }
cmd := process.CommandContext(ctx, setting.AppPath, args...) cmd := process.CommandContext(graceful.GetManager().HammerContext(), setting.AppPath, args...)
cmd.Env = append( cmd.Env = append(
os.Environ(), os.Environ(),
"SSH_ORIGINAL_COMMAND="+session.rawCmd, "SSH_ORIGINAL_COMMAND="+session.rawCmd,