mirror of
https://github.com/boringproxy/boringproxy.git
synced 2025-02-25 18:55:29 -06:00
Added IP restriction
This commit is contained in:
parent
658a8841d7
commit
74abbe8294
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/dist
|
||||
boringproxy_db.json
|
||||
|
30
api.go
30
api.go
@ -7,8 +7,10 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Api struct {
|
||||
@ -379,6 +381,33 @@ func (a *Api) CreateTunnel(tokenData TokenData, params url.Values) (*Tunnel, err
|
||||
}
|
||||
}
|
||||
|
||||
ipRestricted := params.Get("ip-restricted") == "on"
|
||||
|
||||
var ips []string
|
||||
if ipRestricted {
|
||||
ipsAsString := params.Get("allowed-ips")
|
||||
if len(ipsAsString) == 0 {
|
||||
return nil, errors.New("At least one allowed IP is required")
|
||||
}
|
||||
ipsRaw := strings.Split(strings.ReplaceAll(ipsAsString, "\r\n", "\n"), "\n")
|
||||
for _, ipAllowed := range ipsRaw {
|
||||
addr, err := netip.ParseAddr(ipAllowed)
|
||||
if err == nil {
|
||||
prefix, err := addr.Prefix(addr.BitLen())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid IP '%s' : %w", ipAllowed, err)
|
||||
}
|
||||
ips = append(ips, prefix.String())
|
||||
} else {
|
||||
_, err = netip.ParsePrefix(ipAllowed)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid IP '%s' : %w", ipAllowed, err)
|
||||
}
|
||||
ips = append(ips, ipAllowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tlsTerm := params.Get("tls-termination")
|
||||
if tlsTerm != "server" && tlsTerm != "client" && tlsTerm != "passthrough" && tlsTerm != "client-tls" && tlsTerm != "server-tls" {
|
||||
return nil, errors.New("Invalid tls-termination parameter")
|
||||
@ -413,6 +442,7 @@ func (a *Api) CreateTunnel(tokenData TokenData, params url.Values) (*Tunnel, err
|
||||
TlsTermination: tlsTerm,
|
||||
ServerAddress: sshServerAddr,
|
||||
ServerPort: sshServerPort,
|
||||
IPsAllowed: ips,
|
||||
}
|
||||
|
||||
tunnel, err := a.tunMan.RequestCreateTunnel(request)
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -352,12 +353,29 @@ func (p *Server) handleConnection(clientConn net.Conn, certConfig *certmagic.Con
|
||||
clientHello, clientReader, err := peekClientHello(clientConn)
|
||||
if err != nil {
|
||||
log.Println("peekClientHello error", err)
|
||||
clientConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
passConn := NewProxyConn(clientConn, clientReader)
|
||||
|
||||
tunnel, exists := p.db.GetTunnel(clientHello.ServerName)
|
||||
if exists && len(tunnel.IPsAllowed) != 0 {
|
||||
allowed, err := validateIpRestriction(clientConn.RemoteAddr(), &tunnel)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
clientConn.Close()
|
||||
return
|
||||
}
|
||||
if !allowed {
|
||||
log.Printf("Closing the connection to tunnel '%s' since the IP '%s' does not match the IP restriction.\n", tunnel.Domain, clientConn.RemoteAddr().String())
|
||||
err := clientConn.Close()
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
passConn := NewProxyConn(clientConn, clientReader)
|
||||
|
||||
if exists && (tunnel.TlsTermination == "client" || tunnel.TlsTermination == "passthrough") || tunnel.TlsTermination == "client-tls" {
|
||||
p.passthroughRequest(passConn, tunnel)
|
||||
@ -373,6 +391,25 @@ func (p *Server) handleConnection(clientConn net.Conn, certConfig *certmagic.Con
|
||||
}
|
||||
}
|
||||
|
||||
func validateIpRestriction(remoteAddr net.Addr, tunnel *Tunnel) (bool, error) {
|
||||
addrPort, err := netip.ParseAddrPort(remoteAddr.String())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, ipAllowed := range tunnel.IPsAllowed {
|
||||
network, err := netip.ParsePrefix(ipAllowed)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if network.Contains(addrPort.Addr()) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (p *Server) passthroughRequest(conn net.Conn, tunnel Tunnel) {
|
||||
|
||||
upstreamAddr := fmt.Sprintf("localhost:%d", tunnel.TunnelPort)
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -237,7 +238,7 @@ func (c *Client) SyncTunnels(ctx context.Context, serverTunnels map[string]Tunne
|
||||
log.Println("New tunnel", k)
|
||||
c.tunnels[k] = newTun
|
||||
bore = true
|
||||
} else if newTun != tun {
|
||||
} else if !reflect.DeepEqual(newTun, tun) {
|
||||
log.Println("Restart tunnel", k)
|
||||
c.cancelFuncsMutex.Lock()
|
||||
c.cancelFuncs[k]()
|
||||
|
11
database.go
11
database.go
@ -56,10 +56,11 @@ type Tunnel struct {
|
||||
|
||||
// TODO: These are not used by clients and possibly shouldn't be
|
||||
// returned in API calls.
|
||||
Owner string `json:"owner"`
|
||||
ClientName string `json:"client_name"`
|
||||
AuthUsername string `json:"auth_username"`
|
||||
AuthPassword string `json:"auth_password"`
|
||||
Owner string `json:"owner"`
|
||||
ClientName string `json:"client_name"`
|
||||
AuthUsername string `json:"auth_username"`
|
||||
AuthPassword string `json:"auth_password"`
|
||||
IPsAllowed []string `json:"ips_allowed"`
|
||||
}
|
||||
|
||||
func NewDatabase(path string) (*Database, error) {
|
||||
@ -158,7 +159,7 @@ func (d *Database) AddToken(owner, client string) (string, error) {
|
||||
|
||||
token, err := genRandomCode(32)
|
||||
if err != nil {
|
||||
return "", errors.New("Could not generat token")
|
||||
return "", errors.New("Could not generate token")
|
||||
}
|
||||
|
||||
d.Tokens[token] = TokenData{
|
||||
|
@ -58,6 +58,16 @@
|
||||
<input type="password" id="password" name="password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='input'>
|
||||
<label for="ip-restricted">IP restriction:</label>
|
||||
<input type="checkbox" id="ip-restricted" name="ip-restricted">
|
||||
|
||||
<div id='allowed-ips-input'>
|
||||
<label for="allowed-ips">Allowed IPs (one per line):</label>
|
||||
<textarea id="allowed-ips" name="allowed-ips" cols="40" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='input'>
|
||||
<label for="ssh-server-addr">Override SSH Server Address:</label>
|
||||
|
@ -215,6 +215,14 @@ main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#allowed-ips-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#ip-restricted:checked ~ #allowed-ips-input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.token-adder {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
|
Loading…
Reference in New Issue
Block a user