file reorganization
This commit is contained in:
parent
3502d67b06
commit
9b7c3f62db
|
@ -16,16 +16,20 @@ telebit-client-macos
|
||||||
telebit-client-windows-debug.exe
|
telebit-client-windows-debug.exe
|
||||||
telebit-client-windows.exe
|
telebit-client-windows.exe
|
||||||
|
|
||||||
/mplexer/cmd/dnsclient/dnsclient
|
/cmd/dnsclient/dnsclient
|
||||||
/mplexer/cmd/sqlstore/sqlstore
|
/cmd/sqlstore/sqlstore
|
||||||
/mplexer/mgmt/cmd/mgmt/mgmt
|
/cmd/mgmt/mgmt
|
||||||
/mplexer/cmd/signjwt/signjwt
|
/cmd/signjwt/signjwt
|
||||||
/mplexer/cmd/telebit/telebit
|
/cmd/telebit/telebit
|
||||||
|
|
||||||
/telebit
|
/telebit
|
||||||
/cmd/telebit/telebit
|
/cmd/telebit/telebit
|
||||||
/telebit-relay
|
/telebit-relay
|
||||||
|
/telebit-relay-linux
|
||||||
|
/telebit-relay-macos
|
||||||
/cmd/telebit-relay/telebit-relay
|
/cmd/telebit-relay/telebit-relay
|
||||||
|
/cmd/telebit-relay/telebit-relay-linux
|
||||||
|
/cmd/telebit-relay/telebit-relay-macos
|
||||||
|
|
||||||
.*.sw*
|
.*.sw*
|
||||||
log.txt
|
log.txt
|
||||||
|
|
|
@ -50,10 +50,10 @@ pushd mplexy/
|
||||||
|
|
||||||
go generate ./...
|
go generate ./...
|
||||||
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o mgmt-server-linux ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o mgmt-server-linux ./cmd/mgmt/*.go
|
||||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o mgmt-server-macos ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o mgmt-server-macos ./cmd/mgmt/*.go
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o mgmt-server-windows-debug.exe ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o mgmt-server-windows-debug.exe ./cmd/mgmt/*.go
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o mgmt-server-windows.exe ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o mgmt-server-windows.exe ./cmd/mgmt/*.go
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
go generate ./...
|
go generate ./...
|
||||||
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o mgmt-server-linux ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o mgmt-server-linux ./cmd/mgmt/*.go
|
||||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o mgmt-server-macos ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o mgmt-server-macos ./cmd/mgmt/*.go
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o mgmt-server-windows-debug.exe ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o mgmt-server-windows-debug.exe ./cmd/mgmt/*.go
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o mgmt-server-windows.exe ./mgmt/cmd/mgmt/*.go
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o mgmt-server-windows.exe ./cmd/mgmt/*.go
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v3/challenge"
|
"github.com/go-acme/lego/v3/challenge"
|
||||||
"github.com/go-acme/lego/v3/providers/dns/duckdns"
|
"github.com/go-acme/lego/v3/providers/dns/duckdns"
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/go-chi/chi/middleware"
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
|
||||||
|
|
||||||
"github.com/denisbrodbeck/machineid"
|
"github.com/denisbrodbeck/machineid"
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
|
@ -16,8 +16,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/log"
|
"git.coolaj86.com/coolaj86/go-telebitd/log"
|
||||||
|
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/dns01"
|
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/dns01"
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt"
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/relay"
|
"git.coolaj86.com/coolaj86/go-telebitd/relay"
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/relay/api"
|
"git.coolaj86.com/coolaj86/go-telebitd/relay/api"
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/relay/mplexy"
|
"git.coolaj86.com/coolaj86/go-telebitd/relay/mplexy"
|
||||||
|
@ -257,7 +257,7 @@ func main() {
|
||||||
tokenString = r.URL.Query().Get("access_token")
|
tokenString = r.URL.Query().Get("access_token")
|
||||||
}
|
}
|
||||||
|
|
||||||
grants, err := mgmt.Inspect(authURL, tokenString)
|
grants, err := telebit.Inspect(authURL, tokenString)
|
||||||
/*
|
/*
|
||||||
tok, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
tok, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
return []byte(secretKey), nil
|
return []byte(secretKey), nil
|
||||||
|
|
|
@ -1,356 +1,253 @@
|
||||||
|
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/client"
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt"
|
||||||
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
|
||||||
|
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
||||||
|
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/dns01"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
|
"github.com/denisbrodbeck/machineid"
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/go-acme/lego/v3/challenge"
|
||||||
"github.com/go-acme/lego/v3/providers/dns/duckdns"
|
"github.com/go-acme/lego/v3/providers/dns/duckdns"
|
||||||
|
"github.com/go-acme/lego/v3/providers/dns/godaddy"
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
)
|
)
|
||||||
|
|
||||||
var httpRegexp = regexp.MustCompile(`(?i)^http`)
|
var (
|
||||||
var locals string
|
// GitRev refers to the abbreviated commit hash
|
||||||
var domains string
|
GitRev = "0000000"
|
||||||
var insecure bool
|
// GitVersion refers to the most recent tag, plus any commits made since then
|
||||||
var relay string
|
GitVersion = "v0.0.0-pre0+0000000"
|
||||||
var secret string
|
// GitTimestamp refers to the timestamp of the most recent commit
|
||||||
var token string
|
GitTimestamp = "0000-00-00T00:00:00+0000"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
type Forward struct {
|
||||||
flag.StringVar(&locals, "locals", "", "comma separated list of <proto>:<port> or "+
|
scheme string
|
||||||
"<proto>:<hostname>:<port> to which matching incoming connections should forward. "+
|
pattern string
|
||||||
"Ex: smtps:8465,https:example.com:8443")
|
port string
|
||||||
flag.StringVar(&domains, "domains", "", "comma separated list of domain names to set to the tunnel")
|
|
||||||
flag.BoolVar(&insecure, "insecure", false, "Allow TLS connections to telebit-relay without valid certs")
|
|
||||||
flag.BoolVar(&insecure, "k", false, "alias of --insecure")
|
|
||||||
flag.StringVar(&relay, "relay", "", "the domain (or ip address) at which the relay server is running")
|
|
||||||
flag.StringVar(&secret, "secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
|
|
||||||
flag.StringVar(&token, "token", "", "a pre-generated token to give the server (instead of generating one with --secret)")
|
|
||||||
}
|
|
||||||
|
|
||||||
type proxy struct {
|
|
||||||
protocol string
|
|
||||||
hostname string
|
|
||||||
port int
|
|
||||||
}
|
|
||||||
|
|
||||||
func addLocals(proxies []proxy, location string) ([]proxy, error) {
|
|
||||||
parts := strings.Split(location, ":")
|
|
||||||
if len(parts) > 3 || "" == parts[0] {
|
|
||||||
return nil, fmt.Errorf("provided invalid --locals %q", location)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format can be any of
|
|
||||||
// <hostname> or <port> or <proto>:<port> or <proto>:<hostname>:<port>
|
|
||||||
|
|
||||||
n := len(parts)
|
|
||||||
i := n - 1
|
|
||||||
last := parts[i]
|
|
||||||
|
|
||||||
port, err := strconv.Atoi(last)
|
|
||||||
if nil != err {
|
|
||||||
// The last item is the hostname,
|
|
||||||
// which means it should be the only item
|
|
||||||
if n > 1 {
|
|
||||||
return nil, fmt.Errorf("provided invalid --locals %q", location)
|
|
||||||
}
|
|
||||||
// accepting all defaults
|
|
||||||
// If all that was provided as a "local" is the domain name we assume that domain
|
|
||||||
last = strings.ToLower(strings.Trim(last, "/"))
|
|
||||||
proxies = append(proxies, proxy{"http", last, 80})
|
|
||||||
proxies = append(proxies, proxy{"https", last, 443})
|
|
||||||
return proxies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// the last item is the port, and it must be a valid port
|
|
||||||
if port <= 0 || port > 65535 {
|
|
||||||
return nil, fmt.Errorf("local port forward must be between 1 and 65535, not %d", port)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch n {
|
|
||||||
case 1:
|
|
||||||
// <port>
|
|
||||||
proxies = append(proxies, proxy{"http", "*", port})
|
|
||||||
proxies = append(proxies, proxy{"https", "*", port})
|
|
||||||
case 2:
|
|
||||||
// <hostname>:<port>
|
|
||||||
// <scheme>:<port>
|
|
||||||
parts[0] = strings.ToLower(strings.Trim(parts[0], "/"))
|
|
||||||
if strings.Contains(parts[0], ".") {
|
|
||||||
hostname := parts[0]
|
|
||||||
proxies = append(proxies, proxy{"http", hostname, port})
|
|
||||||
proxies = append(proxies, proxy{"https", hostname, port})
|
|
||||||
} else {
|
|
||||||
scheme := parts[0]
|
|
||||||
proxies = append(proxies, proxy{scheme, "*", port})
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
// <scheme>:<hostname>:<port>
|
|
||||||
scheme := strings.ToLower(strings.Trim(parts[0], "/"))
|
|
||||||
hostname := strings.ToLower(strings.Trim(parts[1], "/"))
|
|
||||||
proxies = append(proxies, proxy{scheme, hostname, port})
|
|
||||||
}
|
|
||||||
return proxies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addDomains(proxies []proxy, location string) ([]proxy, error) {
|
|
||||||
parts := strings.Split(location, ":")
|
|
||||||
if len(parts) > 3 || "" == parts[0] {
|
|
||||||
return nil, fmt.Errorf("provided invalid --domains %q", location)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format is limited to
|
|
||||||
// <hostname> or <proto>:<hostname>:<port>
|
|
||||||
|
|
||||||
err := fmt.Errorf("invalid argument for --domains, use format <domainname> or <scheme>:<domainname>:<local-port>")
|
|
||||||
switch len(parts) {
|
|
||||||
case 1:
|
|
||||||
// TODO test that it's a valid pattern for a domain
|
|
||||||
hostname := parts[0]
|
|
||||||
if !strings.Contains(hostname, ".") {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
proxies = append(proxies, proxy{"http", hostname, 80})
|
|
||||||
proxies = append(proxies, proxy{"https", hostname, 443})
|
|
||||||
case 2:
|
|
||||||
return nil, err
|
|
||||||
case 3:
|
|
||||||
scheme := parts[0]
|
|
||||||
hostname := parts[1]
|
|
||||||
if "" == scheme {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !strings.Contains(hostname, ".") {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
port, _ := strconv.Atoi(parts[2])
|
|
||||||
if port <= 0 || port > 65535 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
proxies = append(proxies, proxy{scheme, hostname, port})
|
|
||||||
}
|
|
||||||
|
|
||||||
return proxies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractServicePorts(proxies []proxy) client.RouteMap {
|
|
||||||
result := make(client.RouteMap, 2)
|
|
||||||
|
|
||||||
for _, p := range proxies {
|
|
||||||
if p.protocol != "" && p.port != 0 {
|
|
||||||
hostPorts := result[p.protocol]
|
|
||||||
if hostPorts == nil {
|
|
||||||
result[p.protocol] = make(map[client.DomainName]*client.TerminalConfig)
|
|
||||||
hostPorts = result[p.protocol]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only HTTP and HTTPS allow us to determine the hostname from the request, so only
|
|
||||||
// those protocols support different ports for the same service.
|
|
||||||
if !httpRegexp.MatchString(p.protocol) || p.hostname == "" {
|
|
||||||
p.hostname = "*"
|
|
||||||
}
|
|
||||||
if port, ok := hostPorts[p.hostname]; ok && port.Port != p.port {
|
|
||||||
panic(fmt.Sprintf("duplicate ports for %s://%s", p.protocol, p.hostname))
|
|
||||||
}
|
|
||||||
hostPorts[p.hostname] = &client.TerminalConfig{
|
|
||||||
Port: p.port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we have defaults for HTTPS and HTTP.
|
|
||||||
if result["https"] == nil {
|
|
||||||
result["https"] = make(map[client.DomainName]*client.TerminalConfig, 1)
|
|
||||||
}
|
|
||||||
if result["https"]["*"] == nil {
|
|
||||||
result["https"]["*"] = &client.TerminalConfig{}
|
|
||||||
}
|
|
||||||
if result["https"]["*"].Port == 0 {
|
|
||||||
result["https"]["*"].Port = 8443
|
|
||||||
}
|
|
||||||
|
|
||||||
if result["http"] == nil {
|
|
||||||
result["http"] = make(map[client.DomainName]*client.TerminalConfig, 1)
|
|
||||||
}
|
|
||||||
if result["http"]["*"] == nil {
|
|
||||||
result["http"]["*"] = &client.TerminalConfig{}
|
|
||||||
}
|
|
||||||
if result["http"]["*"].Port == 0 {
|
|
||||||
result["http"]["*"] = result["https"]["*"]
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var err error
|
||||||
|
var provider challenge.Provider = nil
|
||||||
|
var domains []string
|
||||||
|
var forwards []Forward
|
||||||
|
|
||||||
|
// TODO replace the websocket connection with a mock server
|
||||||
|
appID := flag.String("app-id", "telebit.io", "a unique identifier for a deploy target environment")
|
||||||
|
email := flag.String("acme-email", "", "email to use for Let's Encrypt / ACME registration")
|
||||||
|
certpath := flag.String("acme-storage", "./acme.d/", "path to ACME storage directory")
|
||||||
|
acmeAgree := flag.Bool("acme-agree", false, "agree to the terms of the ACME service provider (required)")
|
||||||
|
acmeStaging := flag.Bool("acme-staging", false, "get fake certificates for testing")
|
||||||
|
acmeDirectory := flag.String("acme-directory", "", "ACME Directory URL")
|
||||||
|
enableHTTP01 := flag.Bool("acme-http-01", false, "enable HTTP-01 ACME challenges")
|
||||||
|
enableTLSALPN01 := flag.Bool("acme-tls-alpn-01", false, "enable TLS-ALPN-01 ACME challenges")
|
||||||
|
acmeRelay := flag.String("acme-relay", "", "the base url of the ACME DNS-01 relay, if not the same as the tunnel relay")
|
||||||
|
authURL := flag.String("auth-url", "", "the base url for authentication, if not the same as the tunnel relay")
|
||||||
|
relay := flag.String("relay", "", "the domain (or ip address) at which the relay server is running")
|
||||||
|
secret := flag.String("secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
|
||||||
|
token := flag.String("token", "", "a pre-generated token to give the server (instead of generating one with --secret)")
|
||||||
|
locals := flag.String("locals", "", "a list of <from-domain>:<to-port>")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var err error
|
if len(os.Args) >= 2 {
|
||||||
|
if "version" == os.Args[1] {
|
||||||
if "" == locals {
|
fmt.Printf("telebit %s %s %s", GitVersion, GitRev[:7], GitTimestamp)
|
||||||
locals = os.Getenv("LOCALS")
|
os.Exit(0)
|
||||||
}
|
|
||||||
|
|
||||||
proxies := make([]proxy, 0)
|
|
||||||
for _, option := range stringSlice(locals) {
|
|
||||||
for _, location := range strings.Split(option, ",") {
|
|
||||||
//fmt.Println("locals", location)
|
|
||||||
proxies, err = addLocals(proxies, location)
|
|
||||||
if nil != err {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//fmt.Println("proxies:")
|
|
||||||
//fmt.Printf("%+v\n\n", proxies)
|
|
||||||
for _, option := range stringSlice(domains) {
|
|
||||||
for _, location := range strings.Split(option, ",") {
|
|
||||||
proxies, err = addDomains(proxies, location)
|
|
||||||
if nil != err {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
servicePorts := extractServicePorts(proxies)
|
|
||||||
domainMap := make(map[string]bool)
|
|
||||||
for _, p := range proxies {
|
|
||||||
if p.hostname != "" && p.hostname != "*" {
|
|
||||||
domainMap[p.hostname] = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if relay == "" {
|
if "" != *acmeDirectory {
|
||||||
relay = os.Getenv("RELAY")
|
if *acmeStaging {
|
||||||
}
|
fmt.Fprintf(os.Stderr, "pick either acme-directory or acme-staging\n")
|
||||||
if relay == "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "must provide remote relay server to connect to\n")
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if secret == "" {
|
if *acmeStaging {
|
||||||
secret = os.Getenv("SECRET")
|
*acmeDirectory = certmagic.LetsEncryptStagingCA
|
||||||
}
|
}
|
||||||
|
|
||||||
if secret != "" {
|
if "" == *locals {
|
||||||
domains := make([]string, 0, len(domainMap))
|
*locals = os.Getenv("LOCALS")
|
||||||
for name := range domainMap {
|
|
||||||
domains = append(domains, name)
|
|
||||||
}
|
}
|
||||||
tokenData := jwt.MapClaims{"domains": domains}
|
for _, cfg := range strings.Fields(strings.ReplaceAll(*locals, ",", " ")) {
|
||||||
|
parts := strings.Split(cfg, ":")
|
||||||
|
last := len(parts) - 1
|
||||||
|
port := parts[last]
|
||||||
|
domain := parts[last-1]
|
||||||
|
scheme := ""
|
||||||
|
if len(parts) > 2 {
|
||||||
|
scheme = parts[0]
|
||||||
|
}
|
||||||
|
forwards = append(forwards, Forward{
|
||||||
|
scheme: scheme,
|
||||||
|
pattern: domain,
|
||||||
|
port: port,
|
||||||
|
})
|
||||||
|
|
||||||
secret := []byte(secret)
|
// don't load wildcard into jwt domains
|
||||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, tokenData)
|
if "*" == domain {
|
||||||
if tokenStr, err := jwtToken.SignedString(secret); err != nil {
|
continue
|
||||||
|
}
|
||||||
|
domains = append(domains, domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
ppid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", *appID, *secret))
|
||||||
|
if nil != err {
|
||||||
|
fmt.Fprintf(os.Stderr, "unauthorized device")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
ppidBytes, err := hex.DecodeString(ppid)
|
||||||
|
ppid = base64.RawURLEncoding.EncodeToString(ppidBytes)
|
||||||
|
|
||||||
|
if "" == *token {
|
||||||
|
if "" == *secret {
|
||||||
|
*secret = os.Getenv("SECRET")
|
||||||
|
}
|
||||||
|
*token, err = authstore.HMACToken(ppid)
|
||||||
|
}
|
||||||
|
if nil != err {
|
||||||
|
fmt.Fprintf(os.Stderr, "neither secret nor token provided")
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if "" == *relay {
|
||||||
|
*relay = os.Getenv("RELAY") // "wss://example.com:443"
|
||||||
|
}
|
||||||
|
if "" == *relay {
|
||||||
|
fmt.Fprintf(os.Stderr, "Missing relay url")
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if "" == *acmeRelay {
|
||||||
|
*acmeRelay = strings.Replace(*relay, "ws", "http", 1) // "https://example.com:443"
|
||||||
|
}
|
||||||
|
if "" == *authURL {
|
||||||
|
*authURL = strings.Replace(*relay, "ws", "http", 1) // "https://example.com:443"
|
||||||
|
}
|
||||||
|
|
||||||
|
if "" != os.Getenv("GODADDY_API_KEY") {
|
||||||
|
id := os.Getenv("GODADDY_API_KEY")
|
||||||
|
secret := os.Getenv("GODADDY_API_SECRET")
|
||||||
|
if provider, err = newGoDaddyDNSProvider(id, secret); nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
}
|
||||||
|
} else if "" != os.Getenv("DUCKDNS_TOKEN") {
|
||||||
|
if provider, err = newDuckDNSProvider(os.Getenv("DUCKDNS_TOKEN")); nil != err {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
token = tokenStr
|
endpoint := *acmeRelay
|
||||||
|
if strings.HasSuffix(endpoint, "/") {
|
||||||
|
endpoint = endpoint[:len(endpoint)-1]
|
||||||
|
}
|
||||||
|
//endpoint += "/api/dns/"
|
||||||
|
if provider, err = newAPIDNSProvider(endpoint, *token); nil != err {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
} else if token != "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "must provide either token or secret\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, quit := context.WithCancel(context.Background())
|
grants, err := telebit.Inspect(*authURL, *token)
|
||||||
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 {
|
if nil != err {
|
||||||
fmt.Fprintf(os.Stderr, "failed to initialize certificate management (discovery url? local folder perms?): %s\n", err)
|
_, err := mgmt.Register(*authURL, *secret, ppid)
|
||||||
|
if nil != err {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to register client: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
grants, err = telebit.Inspect(*authURL, *token)
|
||||||
|
if nil != err {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to authenticate after registering client: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("grants", grants)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
acme := &telebit.ACME{
|
||||||
GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
Email: *email,
|
||||||
return magic.GetCertificate(hello)
|
StoragePath: *certpath,
|
||||||
/*
|
Agree: *acmeAgree,
|
||||||
if false {
|
Directory: *acmeDirectory,
|
||||||
_, _ = 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{
|
|
||||||
Insecure: insecure,
|
|
||||||
Server: relay,
|
|
||||||
Services: servicePorts,
|
|
||||||
Token: token,
|
|
||||||
TLSConfig: tlsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("config:\n%#v\n", 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,
|
DNSProvider: provider,
|
||||||
CA: directory,
|
EnableHTTPChallenge: *enableHTTP01,
|
||||||
Email: email,
|
EnableTLSALPNChallenge: *enableTLSALPN01,
|
||||||
Agreed: true,
|
}
|
||||||
DisableHTTPChallenge: true,
|
|
||||||
DisableTLSALPNChallenge: true,
|
mux := telebit.NewRouteMux()
|
||||||
// plus any other customizations you need
|
mux.HandleTLS("*", acme, mux)
|
||||||
})
|
for _, fwd := range forwards {
|
||||||
return magic, nil
|
mux.ForwardTCP("*", "localhost:"+fwd.port, 120*time.Second)
|
||||||
|
//mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
connected := make(chan net.Conn)
|
||||||
|
go func() {
|
||||||
|
timeoutCtx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second))
|
||||||
|
defer cancel()
|
||||||
|
tun, err := telebit.DialWebsocketTunnel(timeoutCtx, *relay, *token)
|
||||||
|
if nil != err {
|
||||||
|
msg := ""
|
||||||
|
if strings.Contains(err.Error(), "bad handshake") {
|
||||||
|
msg = " (may be auth related)"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Error connecting to %s: %s%s\n", *relay, err, msg)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mgmt.Ping(*authURL, *token)
|
||||||
|
if nil != err {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to ping mgmt server: %s", err)
|
||||||
|
//os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
connected <- tun
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(10 * time.Minute)
|
||||||
|
err = mgmt.Ping(*authURL, *token)
|
||||||
|
if nil != err {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to ping mgmt server: %s", err)
|
||||||
|
//os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tun := <-connected
|
||||||
|
fmt.Printf("Listening at %s\n", *relay)
|
||||||
|
log.Fatal("Closed server: ", telebit.ListenAndServe(tun, mux))
|
||||||
|
}
|
||||||
|
|
||||||
|
type ACMEProvider struct {
|
||||||
|
BaseURL string
|
||||||
|
provider challenge.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ACMEProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
return p.provider.Present(domain, token, keyAuth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ACMEProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
return p.provider.CleanUp(domain, token, keyAuth)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDuckDNSProvider is for the sake of demoing the tunnel
|
// newDuckDNSProvider is for the sake of demoing the tunnel
|
||||||
|
@ -360,13 +257,58 @@ func newDuckDNSProvider(token string) (*duckdns.DNSProvider, error) {
|
||||||
return duckdns.NewDNSProviderConfig(config)
|
return duckdns.NewDNSProviderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringSlice(csv string) []string {
|
// newGoDaddyDNSProvider is for the sake of demoing the tunnel
|
||||||
list := []string{}
|
func newGoDaddyDNSProvider(id, secret string) (*godaddy.DNSProvider, error) {
|
||||||
for _, item := range strings.Split(csv, ", ") {
|
config := godaddy.NewDefaultConfig()
|
||||||
if 0 == len(item) {
|
config.APIKey = id
|
||||||
continue
|
config.APISecret = secret
|
||||||
|
return godaddy.NewDNSProviderConfig(config)
|
||||||
}
|
}
|
||||||
list = append(list, item)
|
|
||||||
|
// newAPIDNSProvider is for the sake of demoing the tunnel
|
||||||
|
func newAPIDNSProvider(baseURL string, token string) (*dns01.DNSProvider, error) {
|
||||||
|
config := dns01.NewDefaultConfig()
|
||||||
|
config.Token = token
|
||||||
|
endpoint, err := url.Parse(baseURL)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return list
|
config.Endpoint = endpoint
|
||||||
|
return dns01.NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// TODO for http proxy
|
||||||
|
return mplexer.TargetOptions {
|
||||||
|
Hostname // default localhost
|
||||||
|
Termination // default TLS
|
||||||
|
XFWD // default... no?
|
||||||
|
Port // default 0
|
||||||
|
Conn // should be dialed beforehand
|
||||||
|
}, nil
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
t := telebit.New(token)
|
||||||
|
mux := telebit.RouteMux{}
|
||||||
|
mux.HandleTLS("*", mux) // go back to itself
|
||||||
|
mux.HandleProxy("example.com", "localhost:3000")
|
||||||
|
mux.HandleTCP("example.com", func (c *telebit.Conn) {
|
||||||
|
return httpmux.Serve()
|
||||||
|
})
|
||||||
|
|
||||||
|
l := t.Listen("wss://example.com")
|
||||||
|
conn := l.Accept()
|
||||||
|
telebit.Serve(listener, mux)
|
||||||
|
t.ListenAndServe("wss://example.com", mux)
|
||||||
|
*/
|
||||||
|
|
||||||
|
func getToken(secret string, domains []string) (token string, err error) {
|
||||||
|
tokenData := jwt.MapClaims{"domains": domains}
|
||||||
|
|
||||||
|
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, tokenData)
|
||||||
|
if token, err = jwtToken.SignedString([]byte(secret)); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
|
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
|
||||||
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SuccessResponse struct {
|
type SuccessResponse struct {
|
|
@ -1,314 +0,0 @@
|
||||||
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/dns01"
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt"
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
|
||||||
"github.com/denisbrodbeck/machineid"
|
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/go-acme/lego/v3/challenge"
|
|
||||||
"github.com/go-acme/lego/v3/providers/dns/duckdns"
|
|
||||||
"github.com/go-acme/lego/v3/providers/dns/godaddy"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// GitRev refers to the abbreviated commit hash
|
|
||||||
GitRev = "0000000"
|
|
||||||
// GitVersion refers to the most recent tag, plus any commits made since then
|
|
||||||
GitVersion = "v0.0.0-pre0+0000000"
|
|
||||||
// GitTimestamp refers to the timestamp of the most recent commit
|
|
||||||
GitTimestamp = "0000-00-00T00:00:00+0000"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Forward struct {
|
|
||||||
scheme string
|
|
||||||
pattern string
|
|
||||||
port string
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var err error
|
|
||||||
var provider challenge.Provider = nil
|
|
||||||
var domains []string
|
|
||||||
var forwards []Forward
|
|
||||||
|
|
||||||
// TODO replace the websocket connection with a mock server
|
|
||||||
appID := flag.String("app-id", "telebit.io", "a unique identifier for a deploy target environment")
|
|
||||||
email := flag.String("acme-email", "", "email to use for Let's Encrypt / ACME registration")
|
|
||||||
certpath := flag.String("acme-storage", "./acme.d/", "path to ACME storage directory")
|
|
||||||
acmeAgree := flag.Bool("acme-agree", false, "agree to the terms of the ACME service provider (required)")
|
|
||||||
acmeStaging := flag.Bool("acme-staging", false, "get fake certificates for testing")
|
|
||||||
acmeDirectory := flag.String("acme-directory", "", "ACME Directory URL")
|
|
||||||
enableHTTP01 := flag.Bool("acme-http-01", false, "enable HTTP-01 ACME challenges")
|
|
||||||
enableTLSALPN01 := flag.Bool("acme-tls-alpn-01", false, "enable TLS-ALPN-01 ACME challenges")
|
|
||||||
acmeRelay := flag.String("acme-relay", "", "the base url of the ACME DNS-01 relay, if not the same as the tunnel relay")
|
|
||||||
authURL := flag.String("auth-url", "", "the base url for authentication, if not the same as the tunnel relay")
|
|
||||||
relay := flag.String("relay", "", "the domain (or ip address) at which the relay server is running")
|
|
||||||
secret := flag.String("secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
|
|
||||||
token := flag.String("token", "", "a pre-generated token to give the server (instead of generating one with --secret)")
|
|
||||||
locals := flag.String("locals", "", "a list of <from-domain>:<to-port>")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if len(os.Args) >= 2 {
|
|
||||||
if "version" == os.Args[1] {
|
|
||||||
fmt.Printf("telebit %s %s %s", GitVersion, GitRev[:7], GitTimestamp)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if "" != *acmeDirectory {
|
|
||||||
if *acmeStaging {
|
|
||||||
fmt.Fprintf(os.Stderr, "pick either acme-directory or acme-staging\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if *acmeStaging {
|
|
||||||
*acmeDirectory = certmagic.LetsEncryptStagingCA
|
|
||||||
}
|
|
||||||
|
|
||||||
if "" == *locals {
|
|
||||||
*locals = os.Getenv("LOCALS")
|
|
||||||
}
|
|
||||||
for _, cfg := range strings.Fields(strings.ReplaceAll(*locals, ",", " ")) {
|
|
||||||
parts := strings.Split(cfg, ":")
|
|
||||||
last := len(parts) - 1
|
|
||||||
port := parts[last]
|
|
||||||
domain := parts[last-1]
|
|
||||||
scheme := ""
|
|
||||||
if len(parts) > 2 {
|
|
||||||
scheme = parts[0]
|
|
||||||
}
|
|
||||||
forwards = append(forwards, Forward{
|
|
||||||
scheme: scheme,
|
|
||||||
pattern: domain,
|
|
||||||
port: port,
|
|
||||||
})
|
|
||||||
|
|
||||||
// don't load wildcard into jwt domains
|
|
||||||
if "*" == domain {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
domains = append(domains, domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
ppid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", *appID, *secret))
|
|
||||||
if nil != err {
|
|
||||||
fmt.Fprintf(os.Stderr, "unauthorized device")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
ppidBytes, err := hex.DecodeString(ppid)
|
|
||||||
ppid = base64.RawURLEncoding.EncodeToString(ppidBytes)
|
|
||||||
|
|
||||||
if "" == *token {
|
|
||||||
if "" == *secret {
|
|
||||||
*secret = os.Getenv("SECRET")
|
|
||||||
}
|
|
||||||
*token, err = authstore.HMACToken(ppid)
|
|
||||||
}
|
|
||||||
if nil != err {
|
|
||||||
fmt.Fprintf(os.Stderr, "neither secret nor token provided")
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if "" == *relay {
|
|
||||||
*relay = os.Getenv("RELAY") // "wss://example.com:443"
|
|
||||||
}
|
|
||||||
if "" == *relay {
|
|
||||||
fmt.Fprintf(os.Stderr, "Missing relay url")
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if "" == *acmeRelay {
|
|
||||||
*acmeRelay = strings.Replace(*relay, "ws", "http", 1) // "https://example.com:443"
|
|
||||||
}
|
|
||||||
if "" == *authURL {
|
|
||||||
*authURL = strings.Replace(*relay, "ws", "http", 1) // "https://example.com:443"
|
|
||||||
}
|
|
||||||
|
|
||||||
if "" != os.Getenv("GODADDY_API_KEY") {
|
|
||||||
id := os.Getenv("GODADDY_API_KEY")
|
|
||||||
secret := os.Getenv("GODADDY_API_SECRET")
|
|
||||||
if provider, err = newGoDaddyDNSProvider(id, secret); nil != err {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else if "" != os.Getenv("DUCKDNS_TOKEN") {
|
|
||||||
if provider, err = newDuckDNSProvider(os.Getenv("DUCKDNS_TOKEN")); nil != err {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
endpoint := *acmeRelay
|
|
||||||
if strings.HasSuffix(endpoint, "/") {
|
|
||||||
endpoint = endpoint[:len(endpoint)-1]
|
|
||||||
}
|
|
||||||
//endpoint += "/api/dns/"
|
|
||||||
if provider, err = newAPIDNSProvider(endpoint, *token); nil != err {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
grants, err := telebit.Inspect(*authURL, *token)
|
|
||||||
if nil != err {
|
|
||||||
_, err := mgmt.Register(*authURL, *secret, ppid)
|
|
||||||
if nil != err {
|
|
||||||
fmt.Fprintf(os.Stderr, "failed to register client: %s", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
grants, err = telebit.Inspect(*authURL, *token)
|
|
||||||
if nil != err {
|
|
||||||
fmt.Fprintf(os.Stderr, "failed to authenticate after registering client: %s", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("grants", grants)
|
|
||||||
|
|
||||||
acme := &telebit.ACME{
|
|
||||||
Email: *email,
|
|
||||||
StoragePath: *certpath,
|
|
||||||
Agree: *acmeAgree,
|
|
||||||
Directory: *acmeDirectory,
|
|
||||||
DNSProvider: provider,
|
|
||||||
EnableHTTPChallenge: *enableHTTP01,
|
|
||||||
EnableTLSALPNChallenge: *enableTLSALPN01,
|
|
||||||
}
|
|
||||||
|
|
||||||
mux := telebit.NewRouteMux()
|
|
||||||
mux.HandleTLS("*", acme, mux)
|
|
||||||
for _, fwd := range forwards {
|
|
||||||
mux.ForwardTCP("*", "localhost:"+fwd.port, 120*time.Second)
|
|
||||||
//mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
connected := make(chan net.Conn)
|
|
||||||
go func() {
|
|
||||||
timeoutCtx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second))
|
|
||||||
defer cancel()
|
|
||||||
tun, err := telebit.DialWebsocketTunnel(timeoutCtx, *relay, *token)
|
|
||||||
if nil != err {
|
|
||||||
msg := ""
|
|
||||||
if strings.Contains(err.Error(), "bad handshake") {
|
|
||||||
msg = " (may be auth related)"
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, "Error connecting to %s: %s%s\n", *relay, err, msg)
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = mgmt.Ping(*authURL, *token)
|
|
||||||
if nil != err {
|
|
||||||
fmt.Fprintf(os.Stderr, "failed to ping mgmt server: %s", err)
|
|
||||||
//os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
connected <- tun
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(10 * time.Minute)
|
|
||||||
err = mgmt.Ping(*authURL, *token)
|
|
||||||
if nil != err {
|
|
||||||
fmt.Fprintf(os.Stderr, "failed to ping mgmt server: %s", err)
|
|
||||||
//os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
tun := <-connected
|
|
||||||
fmt.Printf("Listening at %s\n", *relay)
|
|
||||||
log.Fatal("Closed server: ", telebit.ListenAndServe(tun, mux))
|
|
||||||
}
|
|
||||||
|
|
||||||
type ACMEProvider struct {
|
|
||||||
BaseURL string
|
|
||||||
provider challenge.Provider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ACMEProvider) Present(domain, token, keyAuth string) error {
|
|
||||||
return p.provider.Present(domain, token, keyAuth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ACMEProvider) CleanUp(domain, token, keyAuth string) error {
|
|
||||||
return p.provider.CleanUp(domain, token, keyAuth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newGoDaddyDNSProvider is for the sake of demoing the tunnel
|
|
||||||
func newGoDaddyDNSProvider(id, secret string) (*godaddy.DNSProvider, error) {
|
|
||||||
config := godaddy.NewDefaultConfig()
|
|
||||||
config.APIKey = id
|
|
||||||
config.APISecret = secret
|
|
||||||
return godaddy.NewDNSProviderConfig(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newAPIDNSProvider is for the sake of demoing the tunnel
|
|
||||||
func newAPIDNSProvider(baseURL string, token string) (*dns01.DNSProvider, error) {
|
|
||||||
config := dns01.NewDefaultConfig()
|
|
||||||
config.Token = token
|
|
||||||
endpoint, err := url.Parse(baseURL)
|
|
||||||
if nil != err {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Endpoint = endpoint
|
|
||||||
return dns01.NewDNSProviderConfig(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// TODO for http proxy
|
|
||||||
return mplexer.TargetOptions {
|
|
||||||
Hostname // default localhost
|
|
||||||
Termination // default TLS
|
|
||||||
XFWD // default... no?
|
|
||||||
Port // default 0
|
|
||||||
Conn // should be dialed beforehand
|
|
||||||
}, nil
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
t := telebit.New(token)
|
|
||||||
mux := telebit.RouteMux{}
|
|
||||||
mux.HandleTLS("*", mux) // go back to itself
|
|
||||||
mux.HandleProxy("example.com", "localhost:3000")
|
|
||||||
mux.HandleTCP("example.com", func (c *telebit.Conn) {
|
|
||||||
return httpmux.Serve()
|
|
||||||
})
|
|
||||||
|
|
||||||
l := t.Listen("wss://example.com")
|
|
||||||
conn := l.Accept()
|
|
||||||
telebit.Serve(listener, mux)
|
|
||||||
t.ListenAndServe("wss://example.com", mux)
|
|
||||||
*/
|
|
||||||
|
|
||||||
func getToken(secret string, domains []string) (token string, err error) {
|
|
||||||
tokenData := jwt.MapClaims{"domains": domains}
|
|
||||||
|
|
||||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, tokenData)
|
|
||||||
if token, err = jwtToken.SignedString([]byte(secret)); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue