package main import ( "fmt" "os" "time" ssh "golang.org/x/crypto/ssh" ) func Signer(privateKeyFile string) (ssh.Signer, error) { priv, err := os.ReadFile(privateKeyFile) if err != nil { return nil, err } signer, err := ssh.ParsePrivateKey(priv) if err != nil { return nil, err } return signer, nil } func UnmarshalSSHPacketEnv(data []byte) (name, value string, ok bool) { // https://www.rfc-editor.org/rfc/rfc4254.html#section-6.4 var packet struct { Name, Value string } if ssh.Unmarshal(data, &packet) != nil { return } return packet.Name, packet.Value, true } func UnmarshalSSHPacketExec(data []byte) (command string, ok bool) { // https://www.rfc-editor.org/rfc/rfc4254.html#section-6.5 var packet struct { Command string } if ssh.Unmarshal(data, &packet) != nil { return } return packet.Command, true } func SendExitStatus(channel ssh.Channel, exitStatus int) error { // https://www.rfc-editor.org/rfc/rfc4254.html#section-6.10 // https://github.com/golang/go/issues/6235#issuecomment-66084526 data := ssh.Marshal(&struct{ExitStatus uint32}{uint32(exitStatus)}) _, err := channel.SendRequest("exit-status", false, data) return err } func UnnecessarilyMovieLikeRejection(channel ssh.Channel, requestC <-chan *ssh.Request) { done := make(chan struct{}, 0) go func() { for { req, ok := <-requestC if !ok { return } if req.WantReply { go func() { <-done req.Reply(false, nil) }() } } }() for _, r := range "=== ACCESS DENIED ===" { fmt.Fprintf(channel.Stderr(), "%c", r) time.Sleep(50 * time.Millisecond) } fmt.Fprintf(channel.Stderr(), "\r\n") close(done) } // TODO This *is* `(*ssh.Request).Reply`; see: https://cs.opensource.google/go/x/crypto/+/bc19a97f:ssh/channel.go;l=96 func reply(req *ssh.Request, ok bool) { if req.WantReply { req.Reply(ok, nil) } }