Added support for a Listener Manager
- I anticipated having to bring up new listeners as tunnel-clients connect allowing different port use for a single domain - completed the code, however did not see any port information coming back from WSS client. - opened an issue with AJ. - all listeners are now dynamically generated. The run method takes an initial port, once functioning, the runner send down the channel a registration for the initial port.
This commit is contained in:
parent
b122a6091b
commit
493477179b
266
README.md
266
README.md
|
@ -1,190 +1,134 @@
|
|||
# RVPN Server
|
||||
|
||||
Admin Interface
|
||||
---------------
|
||||
Developing the API calls and buliding the structure behind it.
|
||||
## restructured-http
|
||||
|
||||
- build get mux router package `go get "github.com/gorilla/mux"`
|
||||
- run `go-rvpn-server`
|
||||
- execute stunnel (as below)
|
||||
- browse to <https://127.0.0.2:8000/api/servers>
|
||||
- connection handling has been totally rewritten.
|
||||
- on a specific port RVPN can determine the following:
|
||||
- if a connection is encrypted or not encrypted
|
||||
- if a request is a wss_client
|
||||
- if a request is an admin/api request
|
||||
- if a request is a plain (to be forwarded) http request
|
||||
- or if a request is a different protocol (perhaps SSH)
|
||||
|
||||
```json
|
||||
{"Servers":[{"ServerName":"0xc420019500","Domains":[{"Domain":"127.0.0.1","BytesIn":0,"BytesOut":2607},{"Domain":"hfc.daplie.me","BytesIn":0,"BytesOut":0},{"Domain":"test.hfc.daplie.me","BytesIn":0,"BytesOut":0}],"Duration":372.34454292,"BytesIn":65,"BytesOut":2607}]}
|
||||
To accomplish the above RVPN uses raw TCP sockets, buffered readers, and a custom Listener. This allows protocol detection (multiple services on one port)
|
||||
If we expose 443 and 80 to maximize the ability for tunnel clients and south bound traffic, the RVPN is able to deal with this traffic on a limited number of ports, and the most popular ports.
|
||||
It is possible now to meter any point of the connection (not Interface Level, rather TCP)
|
||||
|
||||
There is now a connection manager that dynamically allows new GenericListeners to start on different ports when needed....
|
||||
|
||||
```go
|
||||
newListener := NewListenerRegistration(initialPort)
|
||||
gl.register <- newListener
|
||||
```
|
||||
|
||||
The above is telemetry from the RVPN server. (now supports domain byte tracking)
|
||||
A new listener is created by sending a NewListenerRegistration on the channel.
|
||||
|
||||
Work is continuing, please review make sure it is what you are looking for.
|
||||
```go
|
||||
|
||||
We will need to deal with server name, I am placing the point address for now, not sure how to handle the name.
|
||||
ln, err := net.ListenTCP("tcp", listenAddr)
|
||||
if err != nil {
|
||||
loginfo.Println("unable to bind", err)
|
||||
listenerRegistration.status = listenerFault
|
||||
listenerRegistration.err = err
|
||||
listenerRegistration.commCh <- listenerRegistration
|
||||
return
|
||||
}
|
||||
|
||||
listenerRegistration.status = listenerAdded
|
||||
listenerRegistration.commCh <- listenerRegistration
|
||||
|
||||
```
|
||||
|
||||
Once the lister is fired up, I sends back a regisration status to the manager along with the new Listener and status.
|
||||
|
||||
|
||||
|
||||
Traffic Testing
|
||||
---------------
|
||||
* run go-rvpn-server
|
||||
* execute stunnel.js
|
||||
### Build
|
||||
|
||||
```bash
|
||||
bin/stunnel.js --locals http://hfc.daplie.me:8443,http://test.hfc.daplie.me:3001,http://127.0.0.1:8080 --stunneld wss://localhost.daplie.me:3502 --secret abc123
|
||||
```
|
||||
|
||||
* Browse to http://127.0.0.1:8080
|
||||
hcamacho@Hanks-MBP:go-rvpn-server $ go get
|
||||
|
||||
```
|
||||
INFO: connection: 2017/02/13 20:46:07.815340 connection.go:95: &{0xc420017350 0xc420181540 0xc42014acc0 127.0.0.1:49412 91 3390 0xc42014ad20 [hfc.daplie.me test.hfc.daplie.me 127.0.0.1]}
|
||||
```
|
||||
91, and 3390 are bytes in and bytes out.
|
||||
|
||||
### Execute RVPN
|
||||
|
||||
```bash
|
||||
|
||||
hcamacho@Hanks-MBP:go-rvpn-server $ ./go-rvpn-server
|
||||
INFO: packer: 2017/02/26 12:43:53.978133 run.go:48: startup
|
||||
-=-=-=-=-=-=-=-=-=-=
|
||||
INFO: connection: 2017/02/26 12:43:53.978959 connection_table.go:67: ConnectionTable starting
|
||||
INFO: connection: 2017/02/26 12:43:53.979000 connection_table.go:50: Reaper waiting for 300 seconds
|
||||
|
||||
|
||||
```
|
||||
INFO: external: 2017/02/13 20:46:07.814294 listener_webrequest.go:75: connState
|
||||
&{{0xc42015e690}} 127.0.0.1:8080 127.0.0.1:49416
|
||||
active
|
||||
INFO: external: 2017/02/13 20:46:07.814327 listener_webrequest.go:24: handlerWebRequestExternal
|
||||
INFO: external: 2017/02/13 20:46:07.814358 listener_webrequest.go:30: "GET /favicon.ico HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nAccept: image/webp,image/*,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, sdch, br\r\nAccept-Language: en-US,en;q=0.8\r\nCache-Control: no-cache\r\nConnection: keep-alive\r\nPragma: no-cache\r\nReferer: http://127.0.0.1:8080/\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\r\n\r\n"
|
||||
INFO: external: 2017/02/13 20:46:07.814372 listener_webrequest.go:50: &{0xc420017350 0xc420181540 0xc42014acc0 127.0.0.1:49412 78 2963 0xc42014ad20 [hfc.daplie.me test.hfc.daplie.me 127.0.0.1]} 127.0.0.1 49416
|
||||
header: ipv4,127.0.0.2,49416,398,na
|
||||
meta: {[254 27] 0 [0 0 0 0] [254 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 0}
|
||||
Data: GET /favicon.ico HTTP/1.1
|
||||
Host: 127.0.0.1:8080
|
||||
Accept: image/webp,image/*,*/*;q=0.8
|
||||
|
||||
### Connect Tunnel client
|
||||
|
||||
```bash
|
||||
|
||||
hcamacho@Hanks-MBP:node-tunnel-client $ bin/stunnel.js --locals http://hfc.daplie.me:8443,http://test.hfc.daplie.me:3001,http://127.0.0.1:8080 --stunneld wss://localhost.daplie.me:8443 --secret abc123
|
||||
[local proxy] http://hfc.daplie.me:8443
|
||||
[local proxy] http://test.hfc.daplie.me:3001
|
||||
[local proxy] http://127.0.0.1:8080
|
||||
[connect] 'wss://localhost.daplie.me:8443'
|
||||
[open] connected to 'wss://localhost.daplie.me:8443'
|
||||
|
||||
```
|
||||
|
||||
### Connect Admin
|
||||
|
||||
- add a host entry
|
||||
|
||||
```
|
||||
|
||||
127.0.0.1 tunnel.example.com rvpn.daplie.invalid
|
||||
|
||||
```
|
||||
|
||||
```bash
|
||||
browse https://rvpn.daplie.invalid:8443
|
||||
|
||||
```
|
||||
|
||||
### Send some traffic
|
||||
|
||||
- run the RVPN (as above)
|
||||
- run the tunnel client (as above)
|
||||
- browse http://127.0.0.1:8443 && https://127.0.0.1:8443
|
||||
- observe
|
||||
|
||||
```bash
|
||||
|
||||
hcamacho@Hanks-MBP:node-tunnel-client $ bin/stunnel.js --locals http://hfc.daplie.me:8443,http://test.hfc.daplie.me:3001,http://127.0.0.1:8080 --stunneld wss://localhost.daplie.me:8443 --secret abc123
|
||||
[local proxy] http://hfc.daplie.me:8443
|
||||
[local proxy] http://test.hfc.daplie.me:3001
|
||||
[local proxy] http://127.0.0.1:8080
|
||||
[connect] 'wss://localhost.daplie.me:8443'
|
||||
[open] connected to 'wss://localhost.daplie.me:8443'
|
||||
hello
|
||||
fe1c495076342c3132372e302e302e312c383038302c3431332c68747470474554202f20485454502f312e310d0a486f73743a203132372e302e302e313a383434330d0a436f6e6e656374696f6e3a206b6565702d616c6976650d0a43616368652d436f6e74726f6c3a206d61782d6167653d300d0a557067726164652d496e7365637572652d52657175657374733a20310d0a557365722d4167656e743a204d6f7a696c6c612f352e3020284d6163696e746f73683b20496e74656c204d6163204f5320582031305f31325f3329204170706c655765624b69742f3533372e333620284b48544d4c2c206c696b65204765636b6f29204368726f6d652f35362e302e323932342e3837205361666172692f3533372e33360d0a4163636570743a20746578742f68746d6c2c6170706c69636174696f6e2f7868746d6c2b786d6c2c6170706c69636174696f6e2f786d6c3b713d302e392c696d6167652f776562702c2a2f2a3b713d302e380d0a4163636570742d456e636f64696e673a20677a69702c206465666c6174652c20736463682c2062720d0a4163636570742d4c616e67756167653a20656e2d55532c656e3b713d302e380d0a0d0a
|
||||
<EFBFBD>IPv4,127.0.0.1,8080,413,httpGET / HTTP/1.1
|
||||
Host: 127.0.0.1:8443
|
||||
Connection: keep-alive
|
||||
Cache-Control: max-age=0
|
||||
Upgrade-Insecure-Requests: 1
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
|
||||
Accept-Encoding: gzip, deflate, sdch, br
|
||||
Accept-Language: en-US,en;q=0.8
|
||||
Cache-Control: no-cache
|
||||
Connection: keep-alive
|
||||
Pragma: no-cache
|
||||
Referer: http://127.0.0.1:8080/
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
|
||||
```
|
||||
|
||||
|
||||
[exit] loop closed 0
|
||||
|
||||
```
|
||||
[error] closing ',undefined,undefined' because 'unsupported service 'undefined'' (-1 clients)
|
||||
[_onLocalClose]
|
||||
<Buffer fe 0a 2c 2c 2c 31 2c 65 72 72 6f 72 00>
|
||||
not v1 (or data is corrupt)
|
||||
[error] closing ',undefined,undefined' because 'unsupported service 'undefined'' (-1 clients)
|
||||
[_onLocalClose]
|
||||
<Buffer fe 0a 2c 2c 2c 31 2c 65 72 72 6f 72 00>
|
||||
not v1 (or data is corrupt)
|
||||
```
|
||||
Above is an attempt to connection...
|
||||
|
||||
```
|
||||
INFO: external: 2017/02/13 20:50:45.883267 listener_webrequest.go:75: connState
|
||||
&{{0xc42015e690}} 127.0.0.1:8080 127.0.0.1:49416
|
||||
active
|
||||
INFO: external: 2017/02/13 20:50:45.883298 listener_webrequest.go:24: handlerWebRequestExternal
|
||||
INFO: external: 2017/02/13 20:50:45.883331 listener_webrequest.go:30: "GET /favicon.ico HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nAccept: image/webp,image/*,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, sdch, br\r\nAccept-Language: en-US,en;q=0.8\r\nConnection: keep-alive\r\nReferer: http://127.0.0.1:8080/\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\r\n\r\n"
|
||||
INFO: external: 2017/02/13 20:50:45.883338 listener_webrequest.go:46: unable to match 127.0.0.1 to an existing connection
|
||||
INFO: external: 2017/02/13 20:50:45.883362 listener_webrequest.go:75: connState
|
||||
&{{0xc42015e690}} 127.0.0.1:8080 127.0.0.1:49416
|
||||
```
|
||||
The above is a connection to a domain that was not registered.
|
||||
Looks like it aborts for some reaon. I have this problem on on a new installation as well.
|
||||
|
||||
|
||||
Branch: restructured
|
||||
--------------------
|
||||
* restructure into various packages, removing items from package "main"
|
||||
* used caddy source as a guide
|
||||
* weird debugging issue introduced, will not halt, break point unverified.
|
||||
* although a test project debugs just fin
|
||||
|
||||
listener_client — the WSS client
|
||||
--------------------------------
|
||||
- removed support for anything admin
|
||||
- injected the domains from the claim
|
||||
- domains are now included as initialDomains
|
||||
- registration performans as normal but includes adding the domains to a map of domains, and a collection of domains on the connection.
|
||||
- the system now supports look up fast in either direction, not sure if it will be needed.
|
||||
- reads a chan during registration before allowing traffic, making sure all is well.
|
||||
- registration returns a true on the channel if all is well. If it is not, false. Likely will add some text to pass back.
|
||||
|
||||
Connection
|
||||
----------
|
||||
- added support for boolean channel
|
||||
- support for initial domains in a slice, these are brought back from the JWT as a interface and then are type asserted into the map
|
||||
- removed all the old timer sender dwell stuff as a POC for traffic counts.
|
||||
|
||||
ConnectionTable
|
||||
---------------
|
||||
- added support for domain announcement after the WSS is connection. Not sure if we will need these. They have not been implemented.
|
||||
- I assume all domains are registered with JWT unless I hear differently which would require a new WSS session
|
||||
- expanded NewTable constructor
|
||||
- populating domains into the domain map, and into the connection slice.
|
||||
- added support for removing domains when a connection is removed.
|
||||
|
||||
Packer
|
||||
------
|
||||
- added support for a PackerHeader type and PackerData type
|
||||
- these are connected in a Packer type
|
||||
- support for calculated address family based on ip address property
|
||||
- service field is set to "na"
|
||||
|
||||
Logging
|
||||
-------
|
||||
- unified package logging based on a package init. Will likely need to remove this
|
||||
|
||||
Tests
|
||||
-----
|
||||
- stared to structure project for tests.
|
||||
|
||||
|
||||
Build Instructions
|
||||
------------------
|
||||
|
||||
Create a subinterface:
|
||||
```bash
|
||||
sudo ifconfig lo0 alias 127.0.0.2 up
|
||||
```
|
||||
The above creates an alias the code is able to bind against for admin. Admin is still in progress.
|
||||
|
||||
Get the dependencies
|
||||
|
||||
```bash
|
||||
go get github.com/gorilla/websocket
|
||||
go get github.com/dgrijalva/jwt-go
|
||||
|
||||
git clone git@git.daplie.com:Daplie/localhost.daplie.me-certificates.git
|
||||
ln -s localhost.daplie.me-certificates/certs/localhost.daplie.me certs
|
||||
```
|
||||
|
||||
Run the VPN
|
||||
```bash
|
||||
go build && ./go-rvpn-server
|
||||
```
|
||||
|
||||
In another terminal execute the client
|
||||
``` bash
|
||||
bin/stunnel.js --locals http:hfc.daplie.me:3000,http://test.hfc.daplie.me:3001 --stunneld wss://localhost.daplie.me:8000 --secret abc123
|
||||
```
|
||||
|
||||
A good authentication
|
||||
```
|
||||
INFO: 2017/02/02 21:22:22 vpn-server.go:88: startup
|
||||
INFO: 2017/02/02 21:22:22 vpn-server.go:90: :8000
|
||||
INFO: 2017/02/02 21:22:22 vpn-server.go:73: starting Listener
|
||||
INFO: 2017/02/02 21:22:22 connection_table.go:19: ConnectionTable starting
|
||||
INFO: 2017/02/02 21:22:24 connection.go:113: websocket opening 127.0.0.1:55469
|
||||
INFO: 2017/02/02 21:22:24 connection.go:127: access_token valid
|
||||
INFO: 2017/02/02 21:22:24 connection.go:130: processing domains [hfc.daplie.me test.hfc.daplie.me]
|
||||
```
|
||||
|
||||
Change the key on the tunnel client to test a valid secret
|
||||
``` bash
|
||||
INFO: 2017/02/02 21:24:13 vpn-server.go:88: startup
|
||||
INFO: 2017/02/02 21:24:13 vpn-server.go:90: :8000
|
||||
INFO: 2017/02/02 21:24:13 vpn-server.go:73: starting Listener
|
||||
INFO: 2017/02/02 21:24:13 connection_table.go:19: ConnectionTable starting
|
||||
INFO: 2017/02/02 21:24:15 connection.go:113: websocket opening 127.0.0.1:55487
|
||||
INFO: 2017/02/02 21:24:15 connection.go:123: access_token invalid...closing connection
|
||||
```
|
||||
|
||||
Connection to the External Interface.
|
||||
http://127.0.0.1:8080
|
||||
|
||||
The request is dumped to stdio. This is in preparation of taking that request and sending it back to the designated WSS connection
|
||||
The system needs to track the response coming back, decouple it, and place it back on the wire in the form of a response stream. Since
|
||||
-=-=-=-=-=-=
|
||||
|
||||
A Poor Man's Reverse VPN written in Go
|
||||
|
||||
|
|
|
@ -30,14 +30,12 @@ func NewWedgeConnSize(c net.Conn, size int) (p *WedgeConn) {
|
|||
//Peek - Get a number of bytes outof the buffer, but allow the buffer to be replayed once read
|
||||
func (w *WedgeConn) Peek(n int) ([]byte, error) {
|
||||
loginfo.Println("Peek")
|
||||
loginfo.Println("buffered=", w.reader.Buffered())
|
||||
return w.reader.Peek(n)
|
||||
|
||||
}
|
||||
|
||||
//Read -- A normal reader.
|
||||
func (w *WedgeConn) Read(p []byte) (int, error) {
|
||||
loginfo.Println("read", w.Conn)
|
||||
loginfo.Println("Read")
|
||||
cnt, err := w.reader.Read(p)
|
||||
return cnt, err
|
||||
}
|
||||
|
|
|
@ -5,8 +5,12 @@ import (
|
|||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
|
@ -17,15 +21,19 @@ import (
|
|||
|
||||
"bufio"
|
||||
|
||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/admin"
|
||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
|
||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/packer"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
ctxSecretKey contextKey = "secretKey"
|
||||
ctxConnectionTable contextKey = "connectionTable"
|
||||
ctxConfig contextKey = "config"
|
||||
ctxSecretKey contextKey = "secretKey"
|
||||
ctxConnectionTable contextKey = "connectionTable"
|
||||
ctxConfig contextKey = "config"
|
||||
ctxDeadTime contextKey = "deadtime"
|
||||
ctxListenerRegistration contextKey = "listenerRegistration"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -42,14 +50,13 @@ const (
|
|||
// - leaverage the wedgeConn to peek into the buffer.
|
||||
// - if TLS, consume connection with TLS certbundle, pass to request identifier
|
||||
// - else, just pass to the request identififer
|
||||
func GenericListenAndServe(ctx context.Context, connectionTable *connection.Table, secretKey string, serverBinding string, certbundle tls.Certificate, deadTime int) {
|
||||
config := &tls.Config{Certificates: []tls.Certificate{certbundle}}
|
||||
func GenericListenAndServe(ctx context.Context, listenerRegistration *ListenerRegistration) {
|
||||
|
||||
ctx = context.WithValue(ctx, ctxSecretKey, secretKey)
|
||||
ctx = context.WithValue(ctx, ctxConnectionTable, connectionTable)
|
||||
ctx = context.WithValue(ctx, ctxConfig, config)
|
||||
loginfo.Println(":" + string(listenerRegistration.port))
|
||||
|
||||
listenAddr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(listenerRegistration.port))
|
||||
deadTime := ctx.Value(ctxDeadTime).(int)
|
||||
|
||||
listenAddr, err := net.ResolveTCPAddr("tcp", serverBinding)
|
||||
if nil != err {
|
||||
loginfo.Println(err)
|
||||
return
|
||||
|
@ -58,9 +65,15 @@ func GenericListenAndServe(ctx context.Context, connectionTable *connection.Tabl
|
|||
ln, err := net.ListenTCP("tcp", listenAddr)
|
||||
if err != nil {
|
||||
loginfo.Println("unable to bind", err)
|
||||
listenerRegistration.status = listenerFault
|
||||
listenerRegistration.err = err
|
||||
listenerRegistration.commCh <- listenerRegistration
|
||||
return
|
||||
}
|
||||
|
||||
listenerRegistration.status = listenerAdded
|
||||
listenerRegistration.commCh <- listenerRegistration
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
@ -188,9 +201,7 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
|
|||
return
|
||||
}
|
||||
|
||||
loginfo.Println(r)
|
||||
|
||||
//now we have a request. Check to see if it is one of our own
|
||||
// do we have a valid wss_client?
|
||||
secretKey := ctx.Value(ctxSecretKey).(string)
|
||||
tokenString := r.URL.Query().Get("access_token")
|
||||
result, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
|
@ -201,8 +212,19 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
|
|||
loginfo.Println("Valid WSS dected...sending to handler")
|
||||
oneConn := &oneConnListener{wConn}
|
||||
handleWssClient(ctx, oneConn)
|
||||
|
||||
//do we have a invalid domain indicating Admin?
|
||||
//if yes, prep the oneConn and send it to the handler
|
||||
} else if strings.Contains(r.Host, "rvpn.daplie.invalid") {
|
||||
loginfo.Println("admin")
|
||||
oneConn := &oneConnListener{wConn}
|
||||
handleAdminClient(ctx, oneConn)
|
||||
return
|
||||
|
||||
} else {
|
||||
loginfo.Println("not a valid WSS connection")
|
||||
loginfo.Println("default connection")
|
||||
handleExternalHTTPRequest(ctx, wConn)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,6 +232,132 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
|
|||
loginfo.Println(hex.Dump(peek[0:]))
|
||||
}
|
||||
|
||||
//handleExternalHTTPRequest -
|
||||
// - get a wConn and start processing requests
|
||||
func handleExternalHTTPRequest(ctx context.Context, conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
connectionTable := ctx.Value(ctxConnectionTable).(*connection.Table)
|
||||
|
||||
var buffer [512]byte
|
||||
|
||||
for {
|
||||
cnt, err := conn.Read(buffer[0:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
loginfo.Println("conn ", conn)
|
||||
loginfo.Println("byte read", cnt)
|
||||
|
||||
readBuffer := bytes.NewBuffer(buffer[0:cnt])
|
||||
reader := bufio.NewReader(readBuffer)
|
||||
r, err := http.ReadRequest(reader)
|
||||
|
||||
loginfo.Println(r)
|
||||
|
||||
if err != nil {
|
||||
loginfo.Println("error parsing request")
|
||||
return
|
||||
}
|
||||
|
||||
hostname := r.Host
|
||||
loginfo.Println("Host: ", hostname)
|
||||
|
||||
if strings.Contains(hostname, ":") {
|
||||
arr := strings.Split(hostname, ":")
|
||||
hostname = arr[0]
|
||||
}
|
||||
|
||||
loginfo.Println("Remote: ", conn.RemoteAddr().String())
|
||||
|
||||
remoteSplit := strings.Split(conn.RemoteAddr().String(), ":")
|
||||
rAddr := remoteSplit[0]
|
||||
rPort := remoteSplit[1]
|
||||
|
||||
if conn, ok := connectionTable.ConnByDomain(hostname); !ok {
|
||||
//matching connection can not be found based on ConnByDomain
|
||||
loginfo.Println("unable to match ", hostname, " to an existing connection")
|
||||
//http.Error(, "Domain not supported", http.StatusBadRequest)
|
||||
|
||||
} else {
|
||||
|
||||
loginfo.Println("Domain Accepted")
|
||||
loginfo.Println(conn, rAddr, rPort)
|
||||
p := packer.NewPacker()
|
||||
p.Header.SetAddress(rAddr)
|
||||
p.Header.Port, err = strconv.Atoi(rPort)
|
||||
p.Header.Port = 8080
|
||||
p.Header.Service = "http"
|
||||
p.Data.AppendBytes(buffer[0:cnt])
|
||||
buf := p.PackV1()
|
||||
|
||||
sendTrack := connection.NewSendTrack(buf.Bytes(), hostname)
|
||||
conn.SendCh() <- sendTrack
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//handleAdminClient -
|
||||
// - expecting an existing oneConnListener with a qualified wss client connected.
|
||||
// - auth will happen again since we were just peeking at the token.
|
||||
func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
|
||||
connectionTable := ctx.Value(ctxConnectionTable).(*connection.Table)
|
||||
|
||||
router := mux.NewRouter().StrictSlash(true)
|
||||
|
||||
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
loginfo.Println("HandleFunc /")
|
||||
switch url := r.URL.Path; url {
|
||||
case "/":
|
||||
// check to see if we are using the administrative Host
|
||||
if strings.Contains(r.Host, "rvpn.daplie.invalid") {
|
||||
http.Redirect(w, r, "/admin", 301)
|
||||
}
|
||||
|
||||
default:
|
||||
http.Error(w, "Not Found", 404)
|
||||
}
|
||||
})
|
||||
|
||||
router.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
fmt.Fprintln(w, "<html>Welcome..press <a href=/api/servers>Servers</a> to access stats</html>")
|
||||
})
|
||||
|
||||
router.HandleFunc("/api/servers", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("here")
|
||||
serverContainer := admin.NewServerAPIContainer()
|
||||
|
||||
for c := range connectionTable.Connections() {
|
||||
serverAPI := admin.NewServerAPI(c)
|
||||
serverContainer.Servers = append(serverContainer.Servers, serverAPI)
|
||||
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
json.NewEncoder(w).Encode(serverContainer)
|
||||
|
||||
})
|
||||
|
||||
s := &http.Server{
|
||||
Addr: ":80",
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
err := s.Serve(oneConn)
|
||||
if err != nil {
|
||||
loginfo.Println("Serve error: ", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
loginfo.Println("Cancel signal hit")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//handleWssClient -
|
||||
// - expecting an existing oneConnListener with a qualified wss client connected.
|
||||
// - auth will happen again since we were just peeking at the token.
|
||||
|
@ -241,6 +389,8 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
|
|||
claims := result.Claims.(jwt.MapClaims)
|
||||
domains, ok := claims["domains"].([]interface{})
|
||||
|
||||
loginfo.Println("domains", domains)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
|
@ -283,5 +433,4 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
|
|||
loginfo.Println("Cancel signal hit")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
package genericlistener
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
|
||||
)
|
||||
|
||||
//ListenerRegistrationStatus - post registration status
|
||||
type ListenerRegistrationStatus int
|
||||
|
||||
const (
|
||||
listenerAdded ListenerRegistrationStatus = iota
|
||||
listenerExists
|
||||
listenerFault
|
||||
)
|
||||
|
||||
//ListenerRegistration -- A connection registration structure used to bring up a connection
|
||||
//connection table will then handle additing and sdtarting up the various readers
|
||||
//else error.
|
||||
type ListenerRegistration struct {
|
||||
// The websocket connection.
|
||||
listener *net.Listener
|
||||
|
||||
// The listener port
|
||||
port int
|
||||
|
||||
// The status
|
||||
status ListenerRegistrationStatus
|
||||
|
||||
// The error
|
||||
err error
|
||||
|
||||
// communications channel between go routines
|
||||
commCh chan *ListenerRegistration
|
||||
}
|
||||
|
||||
//NewListenerRegistration -- Constructor
|
||||
func NewListenerRegistration(port int) (p *ListenerRegistration) {
|
||||
p = new(ListenerRegistration)
|
||||
p.port = port
|
||||
p.commCh = make(chan *ListenerRegistration)
|
||||
return
|
||||
}
|
||||
|
||||
//GenericListeners -
|
||||
type GenericListeners struct {
|
||||
listeners map[*net.Listener]int
|
||||
ctx context.Context
|
||||
connnectionTable *connection.Table
|
||||
secretKey string
|
||||
certbundle tls.Certificate
|
||||
deadTime int
|
||||
register chan *ListenerRegistration
|
||||
genericListeners *GenericListeners
|
||||
}
|
||||
|
||||
//NewGenerListeners --
|
||||
func NewGenerListeners(ctx context.Context, connectionTable *connection.Table, secretKey string, certbundle tls.Certificate, deadTime int) (p *GenericListeners) {
|
||||
p = new(GenericListeners)
|
||||
p.listeners = make(map[*net.Listener]int)
|
||||
p.ctx = ctx
|
||||
p.connnectionTable = connectionTable
|
||||
p.secretKey = secretKey
|
||||
p.certbundle = certbundle
|
||||
p.deadTime = deadTime
|
||||
p.register = make(chan *ListenerRegistration)
|
||||
return
|
||||
}
|
||||
|
||||
//Run -- Execute
|
||||
// - execute the GenericLister
|
||||
// - pass initial port, we'll announce that
|
||||
func (gl *GenericListeners) Run(ctx context.Context, initialPort int) {
|
||||
loginfo.Println("ConnectionTable starting")
|
||||
|
||||
config := &tls.Config{Certificates: []tls.Certificate{gl.certbundle}}
|
||||
|
||||
ctx = context.WithValue(ctx, ctxSecretKey, gl.secretKey)
|
||||
ctx = context.WithValue(ctx, ctxConnectionTable, gl.connnectionTable)
|
||||
ctx = context.WithValue(ctx, ctxConfig, config)
|
||||
ctx = context.WithValue(ctx, ctxDeadTime, gl.deadTime)
|
||||
ctx = context.WithValue(ctx, ctxListenerRegistration, gl.register)
|
||||
|
||||
go func(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
|
||||
case <-ctx.Done():
|
||||
loginfo.Println("Cancel signal hit")
|
||||
return
|
||||
|
||||
case registration := <-gl.register:
|
||||
loginfo.Println("register fired", registration.port)
|
||||
|
||||
// check to see if port is already running
|
||||
for listener := range gl.listeners {
|
||||
if gl.listeners[listener] == registration.port {
|
||||
loginfo.Println("listener already running")
|
||||
registration.status = listenerExists
|
||||
registration.commCh <- registration
|
||||
}
|
||||
}
|
||||
loginfo.Println("listener starting up ", registration.port)
|
||||
go GenericListenAndServe(ctx, registration)
|
||||
|
||||
status := <-registration.commCh
|
||||
if status.status == listenerAdded {
|
||||
gl.listeners[status.listener] = status.port
|
||||
} else if status.status == listenerFault {
|
||||
loginfo.Println("Unable to create a new listerer", registration.port)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}(ctx)
|
||||
|
||||
newListener := NewListenerRegistration(initialPort)
|
||||
gl.register <- newListener
|
||||
}
|
|
@ -70,9 +70,12 @@ func Run() {
|
|||
// - if tls, establish, protocol peek buffer, else decrypted
|
||||
// - match protocol
|
||||
|
||||
go genericlistener.GenericListenAndServe(ctx, connectionTable, secretKey, argGenericBinding, certbundle, argDeadTime)
|
||||
genericListeners := genericlistener.NewGenerListeners(ctx, connectionTable, secretKey, certbundle, argDeadTime)
|
||||
go genericListeners.Run(ctx, 8443)
|
||||
|
||||
time.Sleep(20 * time.Second)
|
||||
//go genericlistener.GenericListenAndServe(ctx, connectionTable, secretKey, argGenericBinding, certbundle, argDeadTime)
|
||||
|
||||
time.Sleep(300 * time.Second)
|
||||
cancelContext()
|
||||
time.Sleep(60 * time.Second)
|
||||
|
||||
|
|
Loading…
Reference in New Issue