Start implementing custom ssh server

Turns out SSH has robust semantics for opening generic channels.
Looks like I'll be able to set up tunnels without ever needing to
forward ports on the server, since I can connect the channels with
a custom protocol.

Of course I'll eventually want to support generic SSH clients, but
this makes starting much easier.
This commit is contained in:
Anders Pitman
2020-10-01 17:22:54 -06:00
parent 84b7c0828e
commit 59c824bfca
3 changed files with 123 additions and 27 deletions

View File

@@ -34,6 +34,7 @@ type BoringProxy struct {
tunMan *TunnelManager
adminListener *AdminListener
certConfig *certmagic.Config
sshServer *SshServer
}
func NewBoringProxy() *BoringProxy {
@@ -67,7 +68,9 @@ func NewBoringProxy() *BoringProxy {
auth := NewAuth()
p := &BoringProxy{config, auth, tunMan, adminListener, certConfig}
sshServer := NewSshServer()
p := &BoringProxy{config, auth, tunMan, adminListener, certConfig, sshServer}
http.HandleFunc("/", p.handleAdminRequest)
go http.Serve(adminListener, nil)

View File

@@ -2,8 +2,6 @@ package main
import (
"log"
//"bytes"
"net/http"
"fmt"
"golang.org/x/crypto/ssh"
"io/ioutil"
@@ -22,40 +20,45 @@ func (c *BoringProxyClient) Run() {
//var hostKey ssh.PublicKey
key, err := ioutil.ReadFile("/home/anders/.ssh/id_rsa_test")
if err != nil {
log.Fatalf("unable to read private key: %v", err)
}
// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
config := &ssh.ClientConfig{
User: "anders",
User: "user",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
ssh.Password("yolo"),
},
//HostKeyCallback: ssh.FixedHostKey(hostKey),
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial("tcp", "boringproxy.io:22", config)
client, err := ssh.Dial("tcp", "boringproxy.io:2022", config)
if err != nil {
log.Fatal("Failed to dial: ", err)
}
// Request the remote side to open port 8080 on all interfaces.
l, err := client.Listen("tcp", "0.0.0.0:9001")
if err != nil {
log.Fatal("unable to register tcp forward: ", err)
}
defer l.Close()
tunnelRequests := client.HandleChannelOpen("boringproxy-tunnel")
// Serve HTTP with your SSH server acting as a reverse proxy.
http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
fmt.Fprintf(resp, "Hi there\n")
}))
for req := range tunnelRequests {
go handleTunnelRequest(req)
}
}
func handleTunnelRequest(req ssh.NewChannel) error {
tun, reqs, err := req.Accept()
if err != nil {
return err
}
go ssh.DiscardRequests(reqs)
port := req.ExtraData()
fmt.Println(port)
data, err := ioutil.ReadAll(tun)
if err != nil {
return err
}
fmt.Println(string(data))
return nil
}

90
ssh_server.go Normal file
View File

@@ -0,0 +1,90 @@
package main
import (
"fmt"
"log"
"net"
"io/ioutil"
"golang.org/x/crypto/ssh"
)
type SshServer struct {
config *ssh.ServerConfig
listener net.Listener
}
func NewSshServer() *SshServer {
config := &ssh.ServerConfig{
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
if c.User() == "user" && string(pass) == "yolo" {
return nil, nil
}
return nil, fmt.Errorf("password rejected for %q", c.User())
},
}
privateBytes, err := ioutil.ReadFile("id_rsa_boringproxy")
if err != nil {
log.Fatal("Failed to load private key: ", err)
}
private, err := ssh.ParsePrivateKey(privateBytes)
if err != nil {
log.Fatal("Failed to parse private key: ", err)
}
config.AddHostKey(private)
listener, err := net.Listen("tcp", "0.0.0.0:2022")
if err != nil {
log.Fatal("failed to listen for connection: ", err)
}
server := &SshServer{config, listener}
go server.acceptAll()
return server
}
func (s *SshServer) acceptAll() {
for {
nConn, err := s.listener.Accept()
if err != nil {
log.Print("failed to accept incoming connection: ", err)
continue
}
go s.handleServerConn(nConn)
}
}
func (s *SshServer) handleServerConn(nConn net.Conn) {
conn, chans, reqs, err := ssh.NewServerConn(nConn, s.config)
if err != nil {
log.Print("failed to handshake: ", err)
return
}
go ssh.DiscardRequests(reqs)
go func() {
for newChannel := range chans {
newChannel.Reject(ssh.ResourceShortage, "too bad")
}
}()
ch, cReqs, err := conn.OpenChannel("boringproxy-tunnel", []byte{25, 25})
if err != nil {
log.Print(err)
return
}
go ssh.DiscardRequests(cReqs)
ch.Write([]byte("Hi there"))
ch.Close()
}