mirror of
https://github.com/boringproxy/boringproxy.git
synced 2025-02-25 18:55:29 -06:00
I had been moving in the direction of implementing a custom SSH server in golang. That would be pretty easy if using a custom application protocol, but I want to support tcpip-forward which looks like it would be a lot more work. It also would be nice to support generic CLI clients like OpenSSH. The point of using SSH in the first place is that it's known to be a solid tunneling solution. To that end, I've decided to rely on OpenSSH for now, since that program may have tunneled more bits than any other since the dawn of time. This requires a bit of hackery to generate SSH keys and place them in authorized_keys (as well as shipping the private key to the client), but I think this will work well for now. Plus OpenSSH is already installed on pretty much every server I'd expect to run boringproxy.
108 lines
2.7 KiB
Go
108 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
//"fmt"
|
|
//"strings"
|
|
"net/http"
|
|
"io"
|
|
"encoding/json"
|
|
)
|
|
|
|
type Api struct {
|
|
auth *Auth
|
|
tunMan *TunnelManager
|
|
mux *http.ServeMux
|
|
}
|
|
|
|
type CreateTunnelResponse struct {
|
|
ServerAddress string `json:"server_address"`
|
|
ServerPort int `json:"server_port"`
|
|
ServerPublicKey string `json:"server_public_key"`
|
|
TunnelPort int `json:"tunnel_port"`
|
|
TunnelPrivateKey string `json:"tunnel_private_key"`
|
|
}
|
|
|
|
|
|
func NewApi(auth *Auth, tunMan *TunnelManager) *Api {
|
|
|
|
api := &Api{auth, tunMan, nil}
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.Handle("/tunnels", http.StripPrefix("/tunnels", http.HandlerFunc(api.handleTunnels)))
|
|
|
|
api.mux = mux
|
|
|
|
return api
|
|
}
|
|
|
|
|
|
func (a *Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
a.mux.ServeHTTP(w, r)
|
|
}
|
|
|
|
func (a *Api) handleTunnels(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case "POST":
|
|
a.validateSession(http.HandlerFunc(a.handleCreateTunnel)).ServeHTTP(w, r)
|
|
default:
|
|
w.WriteHeader(405)
|
|
w.Write([]byte("Invalid method for /tunnels"))
|
|
}
|
|
}
|
|
|
|
func (a *Api) handleCreateTunnel(w http.ResponseWriter, r *http.Request) {
|
|
query := r.URL.Query()
|
|
|
|
if len(query["domain"]) != 1 {
|
|
w.WriteHeader(400)
|
|
w.Write([]byte("Invalid domain parameter"))
|
|
return
|
|
}
|
|
domain := query["domain"][0]
|
|
|
|
port, privKey, err := a.tunMan.CreateTunnel(domain)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
io.WriteString(w, err.Error())
|
|
return
|
|
}
|
|
|
|
response := &CreateTunnelResponse{
|
|
ServerAddress: "anders.boringproxy.io",
|
|
ServerPort: 22,
|
|
ServerPublicKey: "",
|
|
TunnelPort: port,
|
|
TunnelPrivateKey: privKey,
|
|
}
|
|
|
|
responseJson, err := json.MarshalIndent(response, "", " ")
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
io.WriteString(w, "Error encoding response")
|
|
return
|
|
}
|
|
|
|
w.Write(responseJson)
|
|
}
|
|
|
|
func (a *Api) validateSession(h http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
token, err := extractToken("access_token", r)
|
|
if err != nil {
|
|
w.WriteHeader(401)
|
|
w.Write([]byte("No token provided"))
|
|
return
|
|
}
|
|
|
|
if !a.auth.Authorized(token) {
|
|
w.WriteHeader(403)
|
|
w.Write([]byte("Not authorized"))
|
|
return
|
|
}
|
|
|
|
h.ServeHTTP(w, r)
|
|
})
|
|
}
|