add client connection whitelist

This commit is contained in:
AJ ONeal 2020-08-25 02:08:56 -06:00
parent b8cf135c45
commit e47ecba665
3 changed files with 47 additions and 5 deletions

View File

@ -21,6 +21,7 @@ import (
telebit "git.rootprojects.org/root/telebit" telebit "git.rootprojects.org/root/telebit"
"git.rootprojects.org/root/telebit/dbg" "git.rootprojects.org/root/telebit/dbg"
tbDns01 "git.rootprojects.org/root/telebit/dns01" tbDns01 "git.rootprojects.org/root/telebit/dns01"
"git.rootprojects.org/root/telebit/iplist"
"git.rootprojects.org/root/telebit/mgmt" "git.rootprojects.org/root/telebit/mgmt"
"git.rootprojects.org/root/telebit/mgmt/authstore" "git.rootprojects.org/root/telebit/mgmt/authstore"
"git.rootprojects.org/root/telebit/table" "git.rootprojects.org/root/telebit/table"
@ -81,6 +82,7 @@ func main() {
var forwards []Forward var forwards []Forward
var portForwards []Forward var portForwards []Forward
spfDomain := flag.String("spf-domain", "", "domain with SPF-like list of IP addresses which are allowed to connect to clients")
// TODO replace the websocket connection with a mock server // TODO replace the websocket connection with a mock server
vendorID := flag.String("vendor-id", "", "a unique identifier for a deploy target environment") vendorID := flag.String("vendor-id", "", "a unique identifier for a deploy target environment")
email := flag.String("acme-email", "", "email to use for Let's Encrypt / ACME registration") email := flag.String("acme-email", "", "email to use for Let's Encrypt / ACME registration")
@ -107,6 +109,14 @@ func main() {
dbg.Debug = *verbose dbg.Debug = *verbose
} }
spfRecords := iplist.Init(*spfDomain)
if len(spfRecords) > 0 {
fmt.Println(
"Allow client connections from:",
strings.Join(spfRecords, " "),
)
}
if len(os.Args) >= 2 { if len(os.Args) >= 2 {
if "version" == os.Args[1] { if "version" == os.Args[1] {
fmt.Printf("telebit %s %s %s\n", GitVersion, GitRev[:7], GitTimestamp) fmt.Printf("telebit %s %s %s\n", GitVersion, GitRev[:7], GitTimestamp)
@ -477,8 +487,6 @@ func muxAll(
fmt.Println(msg) fmt.Println(msg)
mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second, msg, "[Port Forward]") mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second, msg, "[Port Forward]")
} }
// TODO close connection on invalid hostname
mux.HandleTCP("*", telebit.HandlerFunc(routeSubscribersAndClients), "[Tun => Remote Servers]")
if 0 == len(*apiHostname) { if 0 == len(*apiHostname) {
*apiHostname = os.Getenv("API_HOSTNAME") *apiHostname = os.Getenv("API_HOSTNAME")
@ -506,6 +514,9 @@ func muxAll(
}), "[Admin API & Server Relays]") }), "[Admin API & Server Relays]")
} }
// TODO close connection on invalid hostname
mux.HandleTCP("*", telebit.HandlerFunc(routeSubscribersAndClients), "[Tun => Remote Servers]")
if nil != grants { if nil != grants {
for i, domainname := range grants.Domains { for i, domainname := range grants.Domains {
fmt.Printf("[%d] Will decrypt remote requests to %q\n", i, domainname) fmt.Printf("[%d] Will decrypt remote requests to %q\n", i, domainname)
@ -544,7 +555,7 @@ func routeSubscribersAndClients(client net.Conn) error {
case *telebit.ConnWrap: case *telebit.ConnWrap:
wconn = conn wconn = conn
default: default:
panic("HandleTun is special in that it must receive &ConnWrap{ Conn: conn }") panic("routeSubscribersAndClients is special in that it must receive &ConnWrap{ Conn: conn }")
} }
// We know this to be two parts "ip:port" // We know this to be two parts "ip:port"
@ -618,6 +629,32 @@ func tryToServeName(servername string, wconn *telebit.ConnWrap) bool {
} }
return false return false
} }
// Note: timing can reveal if the client exists
if allowAll, _ := iplist.IsAllowed(nil); !allowAll {
addr := wconn.RemoteAddr()
if nil == addr {
// handled by denial
wconn.Close()
return true
}
remoteAddr := addr.String()
if "127.0.0.1" != remoteAddr &&
"::1" != remoteAddr &&
"localhost" != remoteAddr {
ipAddr := net.ParseIP(remoteAddr)
if nil == ipAddr {
wconn.Close()
return true
}
if ok, err := iplist.IsAllowed(ipAddr); !ok || nil != err {
wconn.Close()
return true
}
}
}
// async so that the call stack can complete and be released // async so that the call stack can complete and be released
//srv.clients.Store(wconn.LocalAddr().String(), wconn) //srv.clients.Store(wconn.LocalAddr().String(), wconn)

View File

@ -92,6 +92,9 @@ func IsAllowed(remoteIP net.IP) (bool, error) {
if 0 == len(fields) { if 0 == len(fields) {
return true, nil return true, nil
} }
if nil == remoteIP {
return false, nil
}
for _, section := range fields { for _, section := range fields {
parts := strings.Split(section, ":") parts := strings.Split(section, ":")

View File

@ -186,14 +186,16 @@ func (wsw *WebsocketTunnel) Close() error {
func (wsw *WebsocketTunnel) LocalAddr() net.Addr { func (wsw *WebsocketTunnel) LocalAddr() net.Addr {
// TODO do we reverse this since the "local" address is that of the relay? // TODO do we reverse this since the "local" address is that of the relay?
// return wsw.wsconn.RemoteAddr() // return wsw.wsconn.RemoteAddr()
panic("no LocalAddr() implementation") fmt.Fprintf(os.Stderr, "no LocalAddr() implementation\n")
return nil
} }
// RemoteAddr is not implemented and will panic. Additionally, it wouldn't mean anything useful anyway. // RemoteAddr is not implemented and will panic. Additionally, it wouldn't mean anything useful anyway.
func (wsw *WebsocketTunnel) RemoteAddr() net.Addr { func (wsw *WebsocketTunnel) RemoteAddr() net.Addr {
// TODO do we reverse this since the "remote" address means nothing / is that of one of the clients? // TODO do we reverse this since the "remote" address means nothing / is that of one of the clients?
// return wsw.wsconn.LocalAddr() // return wsw.wsconn.LocalAddr()
panic("no RemoteAddr() implementation") fmt.Fprintf(os.Stderr, "no RemoteAddr() implementation\n")
return nil
} }
// SetDeadline sets the read and write deadlines associated // SetDeadline sets the read and write deadlines associated