use DuckDNS for demo

This commit is contained in:
AJ ONeal 2020-05-06 11:11:13 -06:00
parent 5a68908e66
commit 776aabf879
4 changed files with 121 additions and 21 deletions

View File

@ -17,6 +17,7 @@ type Config struct {
Token string Token string
Insecure bool Insecure bool
Services RouteMap Services RouteMap
TLSConfig *tls.Config
} }
// Run establishes a connection with the RVPN server specified in the config. If the first attempt // Run establishes a connection with the RVPN server specified in the config. If the first attempt
@ -47,7 +48,7 @@ func Run(ctx context.Context, config *Config) error {
return fmt.Errorf(`service %s missing port for "*"`, name) return fmt.Errorf(`service %s missing port for "*"`, name)
} }
} }
handler := NewWsHandler(config.Services) handler := NewWsHandler(config.Services, config.TLSConfig)
authenticated := false authenticated := false
for { for {

View File

@ -2,6 +2,7 @@ package client
import ( import (
"context" "context"
"crypto/tls"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -28,15 +29,17 @@ type WsHandler struct {
ctx context.Context ctx context.Context
dataChan chan *packer.Packer dataChan chan *packer.Packer
tlsConfig *tls.Config
} }
// NewWsHandler creates a new handler ready to be given a websocket connection. The services // NewWsHandler creates a new handler ready to be given a websocket connection. The services
// argument specifies what port each service type should be directed to on the local interface. // argument specifies what port each service type should be directed to on the local interface.
func NewWsHandler(services RouteMap) *WsHandler { func NewWsHandler(services RouteMap, tlsConfig *tls.Config) *WsHandler {
h := new(WsHandler) return &WsHandler{
h.servicePorts = services servicePorts: services,
h.localConns = make(map[string]net.Conn) localConns: make(map[string]net.Conn),
return h tlsConfig: tlsConfig,
}
} }
// HandleConn handles all of the traffic on the provided websocket connection. The function // HandleConn handles all of the traffic on the provided websocket connection. The function
@ -81,7 +84,7 @@ func (h *WsHandler) HandleConn(ctx context.Context, conn *websocket.Conn) {
} }
} }
func (h *WsHandler) writeRemote(conn *websocket.Conn) { func (h *WsHandler) writeRemote(wsconn *websocket.Conn) {
defer h.closeConnections() defer h.closeConnections()
defer func() { h.dataChan = nil }() defer func() { h.dataChan = nil }()
@ -94,13 +97,13 @@ func (h *WsHandler) writeRemote(conn *websocket.Conn) {
// all errors if it doesn't work. // all errors if it doesn't work.
message := websocket.FormatCloseMessage(websocket.CloseGoingAway, "closing connection") message := websocket.FormatCloseMessage(websocket.CloseGoingAway, "closing connection")
deadline := time.Now().Add(10 * time.Second) deadline := time.Now().Add(10 * time.Second)
conn.WriteControl(websocket.CloseMessage, message, deadline) wsconn.WriteControl(websocket.CloseMessage, message, deadline)
conn.Close() wsconn.Close()
return return
case p := <-h.dataChan: case p := <-h.dataChan:
packed := p.PackV1() packed := p.PackV1()
conn.WriteMessage(websocket.BinaryMessage, packed.Bytes()) wsconn.WriteMessage(websocket.BinaryMessage, packed.Bytes())
} }
} }
} }
@ -139,6 +142,7 @@ func (h *WsHandler) getLocalConn(p *packer.Packer) net.Conn {
} }
var hostname string var hostname string
//var terminate bool
if service == "http" { if service == "http" {
if match := hostRegexp.FindSubmatch(p.Data.Data()); match != nil { if match := hostRegexp.FindSubmatch(p.Data.Data()); match != nil {
hostname = strings.Split(string(match[1]), ":")[0] hostname = strings.Split(string(match[1]), ":")[0]
@ -146,6 +150,7 @@ func (h *WsHandler) getLocalConn(p *packer.Packer) net.Conn {
} }
} else if service == "https" { } else if service == "https" {
hostname, _ = sni.GetHostname(p.Data.Data()) hostname, _ = sni.GetHostname(p.Data.Data())
//terminate = true
} else { } else {
hostname = "*" hostname = "*"
} }
@ -176,7 +181,14 @@ func (h *WsHandler) getLocalConn(p *packer.Packer) net.Conn {
return nil return nil
} }
h.localConns[key] = conn rconn := conn
/*
if terminate {
rconn = tls.Server(conn, h.tlsConfig)
//rconn = tls.Client(conn, h.tlsConfig)
}
*/
h.localConns[key] = rconn
loginfo.Printf("new client %q for %s:%d (%d clients)\n", key, hostname, term.Port, len(h.localConns)) loginfo.Printf("new client %q for %s:%d (%d clients)\n", key, hostname, term.Port, len(h.localConns))
go h.readLocal(key, &p.Header) go h.readLocal(key, &p.Header)
return conn return conn
@ -190,6 +202,7 @@ func (h *WsHandler) writeLocal(p *packer.Packer) {
} }
if p.Service() == "error" || p.Service() == "end" { if p.Service() == "error" || p.Service() == "end" {
// TODO XXX where's the opposite of this?
conn.Close() conn.Close()
return return
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"crypto/tls"
"flag" "flag"
"fmt" "fmt"
"log" "log"
@ -12,7 +13,9 @@ import (
"git.coolaj86.com/coolaj86/go-telebitd/client" "git.coolaj86.com/coolaj86/go-telebitd/client"
"github.com/caddyserver/certmagic"
jwt "github.com/dgrijalva/jwt-go" jwt "github.com/dgrijalva/jwt-go"
"github.com/go-acme/lego/v3/providers/dns/duckdns"
_ "github.com/joho/godotenv/autoload" _ "github.com/joho/godotenv/autoload"
) )
@ -264,17 +267,99 @@ func main() {
ctx, quit := context.WithCancel(context.Background()) ctx, quit := context.WithCancel(context.Background())
defer quit() defer quit()
acmeStorage := "./acme.d/"
acmeEmail := ""
acmeStaging := false
//
// CertMagic is Greenlock for Go
//
directory := certmagic.LetsEncryptProductionCA
if acmeStaging {
directory = certmagic.LetsEncryptStagingCA
}
magic, err := newCertMagic(directory, acmeEmail, &certmagic.FileStorage{Path: acmeStorage})
if nil != err {
fmt.Fprintf(os.Stderr, "failed to initialize certificate management (discovery url? local folder perms?): %s\n", err)
os.Exit(1)
}
tlsConfig := &tls.Config{
GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
return magic.GetCertificate(hello)
/*
if false {
_, _ = magic.GetCertificate(hello)
}
// TODO
// 1. call out to greenlock for validation
// 2. push challenges through http channel
// 3. receive certificates (or don't)
certbundleT, err := tls.LoadX509KeyPair("certs/fullchain.pem", "certs/privkey.pem")
certbundle := &certbundleT
if err != nil {
return nil, err
}
return certbundle, nil
*/
},
}
config := client.Config{ config := client.Config{
Insecure: insecure, Insecure: insecure,
Server: relay, Server: relay,
Services: servicePorts, Services: servicePorts,
Token: token, Token: token,
TLSConfig: tlsConfig,
} }
fmt.Printf("config:\n%#v\n", config) fmt.Printf("config:\n%#v\n", config)
log.Fatal(client.Run(ctx, &config)) log.Fatal(client.Run(ctx, &config))
} }
func newCertMagic(directory string, email string, storage certmagic.Storage) (*certmagic.Config, error) {
cache := certmagic.NewCache(certmagic.CacheOptions{
GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
// do whatever you need to do to get the right
// configuration for this certificate; keep in
// mind that this config value is used as a
// template, and will be completed with any
// defaults that are set in the Default config
return &certmagic.Config{}, nil
},
})
provider, err := newDuckDNSProvider(os.Getenv("DUCKDNS_TOKEN"))
if err != nil {
return nil, err
}
magic := certmagic.New(cache, certmagic.Config{
Storage: storage,
OnDemand: &certmagic.OnDemandConfig{
DecisionFunc: func(name string) error {
return nil
},
},
})
// Ummm... just a little confusing
magic.Issuer = certmagic.NewACMEManager(magic, certmagic.ACMEManager{
DNSProvider: provider,
CA: directory,
Email: email,
Agreed: true,
DisableHTTPChallenge: true,
DisableTLSALPNChallenge: true,
// plus any other customizations you need
})
return magic, nil
}
// newDuckDNSProvider is for the sake of demoing the tunnel
func newDuckDNSProvider(token string) (*duckdns.DNSProvider, error) {
config := duckdns.NewDefaultConfig()
config.Token = token
return duckdns.NewDNSProviderConfig(config)
}
func stringSlice(csv string) []string { func stringSlice(csv string) []string {
list := []string{} list := []string{}
for _, item := range strings.Split(csv, ", ") { for _, item := range strings.Split(csv, ", ") {

View File

@ -332,6 +332,7 @@ func (mx *MPlexy) routeToTarget(ctx context.Context, extConn *tunnel.WedgeConn,
} }
for { for {
fmt.Println("xxyyzz buffer")
buffer, err := extConn.PeekAll() buffer, err := extConn.PeekAll()
if err != nil { if err != nil {
loginfo.Println("unable to peekAll", err) loginfo.Println("unable to peekAll", err)