Add ClientAddress

Allows tunnels to specify what address the client with connect to
for upstream connections.

Also changed the tunnel-adder UI.

Also fixed a bug where invalid domains would crash. Needed to
properly return an error dialog.
This commit is contained in:
Anders Pitman committed 2020-10-20 18:52:32 -06:00
1 parent ef660f61ae
commit 9882017d5a
8 files changed
+117 -49

No files matched your search

+20 -2
View File
@@ -72,7 +72,20 @@ func (a *Api) CreateTunnel(tokenData TokenData, params url.Values) (*Tunnel, err
return nil, errors.New("Invalid client-port parameter")
}
tunnel, err := a.tunMan.CreateTunnelForClient(domain, tokenData.Owner, clientName, clientPort)
clientAddr := params.Get("client-addr")
if clientAddr == "" {
clientAddr = "127.0.0.1"
}
request := Tunnel{
Domain: domain,
Owner: tokenData.Owner,
ClientName: clientName,
ClientPort: clientPort,
ClientAddress: clientAddr,
}
tunnel, err := a.tunMan.RequestCreateTunnel(request)
if err != nil {
return nil, err
}
@@ -162,7 +175,12 @@ func (a *Api) handleCreateTunnel(w http.ResponseWriter, r *http.Request) {
}
owner := query["owner"][0]
tunnel, err := a.tunMan.CreateTunnel(domain, owner)
request := Tunnel{
Domain: domain,
Owner: owner,
}
tunnel, err := a.tunMan.RequestCreateTunnel(request)
if err != nil {
w.WriteHeader(400)
io.WriteString(w, err.Error())
+3 -3
View File
@@ -190,18 +190,18 @@ func (c *BoringProxyClient) BoreTunnel(tunnel Tunnel) context.CancelFunc {
break
//continue
}
go c.handleConnection(conn, tunnel.ClientPort)
go c.handleConnection(conn, tunnel.ClientAddress, tunnel.ClientPort)
}
}()
return cancelFunc
}
func (c *BoringProxyClient) handleConnection(conn net.Conn, port int) {
func (c *BoringProxyClient) handleConnection(conn net.Conn, addr string, port int) {
defer conn.Close()
upstreamConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
upstreamConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
if err != nil {
log.Print(err)
return
+2
View File
@@ -25,6 +25,7 @@ type User struct {
type Tunnel struct {
Owner string `json:"owner"`
Domain string `json:"domain"`
ServerAddress string `json:"server_address"`
ServerPort int `json:"server_port"`
ServerPublicKey string `json:"server_public_key"`
@@ -32,6 +33,7 @@ type Tunnel struct {
TunnelPort int `json:"tunnel_port"`
TunnelPrivateKey string `json:"tunnel_private_key"`
ClientName string `json:"client_name"`
ClientAddress string `json:"client_address"`
ClientPort int `json:"client_port"`
CssId string `json:"css_id"`
}
+17 -21
View File
@@ -49,35 +49,27 @@ func (m *TunnelManager) GetTunnels() map[string]Tunnel {
return m.db.GetTunnels()
}
func (m *TunnelManager) CreateTunnelForClient(domain, owner, clientName string, clientPort int) (Tunnel, error) {
tun, err := m.CreateTunnel(domain, owner)
if err != nil {
return Tunnel{}, err
func (m *TunnelManager) RequestCreateTunnel(tunReq Tunnel) (Tunnel, error) {
if tunReq.Domain == "" {
return Tunnel{}, errors.New("Domain required")
}
tun.ClientName = clientName
tun.ClientPort = clientPort
if tunReq.Owner == "" {
return Tunnel{}, errors.New("Owner required")
}
// TODO: It's a bit hacky that we call db.SetTunnel in CreateTunnel and
// then again here
m.db.SetTunnel(domain, tun)
return tun, nil
}
func (m *TunnelManager) CreateTunnel(domain, owner string) (Tunnel, error) {
err := m.certConfig.ManageSync([]string{domain})
err := m.certConfig.ManageSync([]string{tunReq.Domain})
if err != nil {
log.Println(err)
return Tunnel{}, errors.New("Failed to get cert")
}
m.mutex.Lock()
defer m.mutex.Unlock()
_, exists := m.db.GetTunnel(domain)
_, exists := m.db.GetTunnel(tunReq.Domain)
if exists {
return Tunnel{}, errors.New("Tunnel exists for domain " + domain)
return Tunnel{}, errors.New("Tunnel exists for domain " + tunReq.Domain)
}
port, err := randomPort()
@@ -85,22 +77,26 @@ func (m *TunnelManager) CreateTunnel(domain, owner string) (Tunnel, error) {
return Tunnel{}, err
}
privKey, err := m.addToAuthorizedKeys(domain, port)
privKey, err := m.addToAuthorizedKeys(tunReq.Domain, port)
if err != nil {
return Tunnel{}, err
}
tunnel := Tunnel{
Owner: owner,
Owner: tunReq.Owner,
Domain: tunReq.Domain,
ServerAddress: m.config.WebUiDomain,
ServerPort: 22,
ServerPublicKey: "",
TunnelPort: port,
TunnelPrivateKey: privKey,
ClientName: tunReq.ClientName,
ClientPort: tunReq.ClientPort,
ClientAddress: tunReq.ClientAddress,
Username: m.user.Username,
}
m.db.SetTunnel(domain, tunnel)
m.db.SetTunnel(tunReq.Domain, tunnel)
return tunnel, nil
}
+25 -12
View File
@@ -21,10 +21,15 @@ type WebUiHandler struct {
tunMan *TunnelManager
box *rice.Box
headHtml template.HTML
pendingRequests map[string]chan string
pendingRequests map[string]chan ReqResult
mutex *sync.Mutex
}
type ReqResult struct {
err error
redirectUrl string
}
type IndexData struct {
Head template.HTML
Tunnels map[string]Tunnel
@@ -91,7 +96,7 @@ func NewWebUiHandler(config *BoringProxyConfig, db *Database, api *Api, auth *Au
api: api,
auth: auth,
tunMan: tunMan,
pendingRequests: make(map[string]chan string),
pendingRequests: make(map[string]chan ReqResult),
mutex: &sync.Mutex{},
}
}
@@ -427,7 +432,7 @@ func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request
h.alertDialog(w, r, err.Error(), "/#/tunnels")
}
doneSignal := make(chan string)
doneSignal := make(chan ReqResult)
h.mutex.Lock()
h.pendingRequests[pendingId] = doneSignal
h.mutex.Unlock()
@@ -437,12 +442,8 @@ func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request
r.ParseForm()
_, err := h.api.CreateTunnel(tokenData, r.Form)
if err != nil {
w.WriteHeader(400)
h.alertDialog(w, r, err.Error(), "/#/tunnels")
}
doneSignal <- "/#/tunnels"
doneSignal <- ReqResult{err, "/#/tunnels"}
}()
timeout := make(chan bool, 1)
@@ -469,8 +470,14 @@ func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request
tmpl.Execute(w, data)
case <-doneSignal:
http.Redirect(w, r, "/#/tunnels", 303)
case result := <-doneSignal:
if result.err != nil {
w.WriteHeader(400)
h.alertDialog(w, r, result.err.Error(), result.redirectUrl)
return
}
http.Redirect(w, r, result.redirectUrl, 303)
}
}
@@ -654,9 +661,15 @@ func (h *WebUiHandler) handleLoading(w http.ResponseWriter, r *http.Request) {
delete(h.pendingRequests, pendingId)
h.mutex.Unlock()
redirUrl := <-doneSignal
result := <-doneSignal
http.Redirect(w, r, redirUrl, 303)
if result.err != nil {
w.WriteHeader(400)
h.alertDialog(w, r, result.err.Error(), result.redirectUrl)
return
}
http.Redirect(w, r, result.redirectUrl, 303)
}
func (h *WebUiHandler) loadTemplate(name string) (*template.Template, error) {
+10 -3
View File
@@ -2,11 +2,18 @@
<html>
<head>
{{.Head}}
<style>
.dialog {
display: block;
}
</style>
</head>
<body>
<main>
<div class='dialog'>
<div class='dialog'>
<div class='dialog__overlay'>
<div class='dialog__content'>
<p>
{{.Message}}
</p>
@@ -16,6 +23,6 @@
</a>
</div>
</div>
</main>
</div>
</body>
</html>
+19 -8
View File
@@ -24,7 +24,7 @@
<input autocomplete='off' type='checkbox' class='toggle' id='toggle-tunnel-hide-deleted-{{$tunnel.CssId}}'>
<div class='list-item'>
<div>
<a href="https://{{$domain}}">{{$domain}}</a>:{{$tunnel.TunnelPort}} -> {{$tunnel.ClientName}}:{{$tunnel.ClientPort}}
<a href="https://{{$domain}}">{{$domain}}</a>:{{$tunnel.TunnelPort}} -> {{$tunnel.ClientName}} -> {{$tunnel.ClientAddress}}:{{$tunnel.ClientPort}}
</div>
<label class='button' for='toggle-tunnel-delete-dialog-{{$tunnel.CssId}}'>
@@ -54,14 +54,25 @@
</div>
<div class='tunnel-adder'>
<h1>Add Tunnel</h1>
<form action="/tunnels" method="POST">
<label for="domain">Domain:</label>
<input type="text" id="domain" name="domain" required>
<label for="client-name">Client Name:</label>
<input type="text" id="client-name" name="client-name" required>
<label for="client-port">Client Port:</label>
<input type="text" id="client-port" name="client-port" required>
<button class='button' type="submit">Add/Update Tunnel</button>
<div class='input'>
<label for="domain">Domain:</label>
<input type="text" id="domain" name="domain" required>
</div>
<div class='input'>
<label for="client-name">Client Name:</label>
<input type="text" id="client-name" name="client-name" required>
</div>
<div class='input'>
<label for="client-addr">Client Address:</label>
<input type="text" id="client-addr" name="client-addr" value='127.0.0.1' required>
</div>
<div class='input'>
<label for="client-port">Client Port:</label>
<input type="text" id="client-port" name="client-port" required>
</div>
<button class='button' type="submit">Submit</button>
</form>
</div>
</div>
+21
View File
@@ -140,7 +140,28 @@ main {
.tunnel-adder {
padding: 5px;
}
.tunnel-adder h1 {
margin: .2em;
}
.tunnel-adder form {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.input {
padding: .7em;
margin: .2em;
border: 1px solid var(--main-color);
display: flex;
flex-direction: column;
}
.input label {
padding: .2em;
font-weight: bold;
}
.token-adder {