bugfix HTTP-01 relay and update docs
This commit is contained in:
parent
adb12c43b2
commit
8c8d5b150f
|
@ -24,6 +24,7 @@ telebit-client-windows.exe
|
|||
/cmd/wsconnect/wsconnect
|
||||
/cmd/mgmt/mgmt
|
||||
/cmd/signjwt/signjwt
|
||||
/signjwt
|
||||
/cmd/telebit/telebit
|
||||
|
||||
/telebit
|
||||
|
|
|
@ -161,7 +161,7 @@ go build -mod=vendor -ldflags "-s -w" -o signjwt cmd/signjwt/*.go
|
|||
To generate an `admin` token:
|
||||
|
||||
```bash
|
||||
VERDOR_ID="test-id"
|
||||
VENDOR_ID="test-id"
|
||||
SECRET="xxxxxxxxxxx"
|
||||
TOKEN=$(./signjwt \
|
||||
--expires-in 15m \
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# MGMT Server
|
||||
|
||||
# Config
|
||||
|
||||
```bash
|
||||
VERBOSE=
|
||||
|
||||
PORT=6468
|
||||
|
||||
# JWT Verification Secret
|
||||
#SECRET=XxxxxxxxxxxxxxxX
|
||||
|
||||
DB_URL=postgres://postgres:postgres@localhost:5432/postgres
|
||||
DOMAIN=mgmt.example.com
|
||||
TUNNEL_DOMAIN=tunnel.example.com
|
||||
|
||||
NAMECOM_USERNAME=johndoe
|
||||
NAMECOM_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
# Build
|
||||
|
||||
```bash
|
||||
go generate -mod vendor ./...
|
||||
|
||||
pushd cmd/mgmt
|
||||
go build -mod vendor -o telebit-mgmt
|
||||
popd
|
||||
```
|
|
@ -58,6 +58,9 @@ func handleDNSRoutes(r chi.Router) {
|
|||
r.Delete("/{domain}/{token}/{keyAuth}/{challengeType}", deleteChallenge)
|
||||
}
|
||||
|
||||
// TODO pick one and stick with it
|
||||
r.Route("/acme-relay", handleACMEChallenges)
|
||||
r.Route("/acme-solver", handleACMEChallenges)
|
||||
r.Route("/dns", handleACMEChallenges)
|
||||
r.Route("/http", handleACMEChallenges)
|
||||
}
|
||||
|
|
102
cmd/mgmt/mgmt.go
102
cmd/mgmt/mgmt.go
|
@ -15,6 +15,7 @@ import (
|
|||
"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/go-acme/lego/v3/providers/dns/namedotcom"
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
)
|
||||
|
||||
|
@ -43,51 +44,89 @@ func help() {
|
|||
func main() {
|
||||
var err error
|
||||
|
||||
addr := flag.String("address", "", "IPv4 or IPv6 bind address")
|
||||
port := flag.String("port", "3000", "port to listen to")
|
||||
challengesPort := flag.String("challenges-port", "80", "port to use to respond to .well-known/acme-challenge tokens")
|
||||
dbURL := flag.String(
|
||||
"db-url",
|
||||
"postgres://postgres:postgres@localhost/postgres",
|
||||
"database (postgres) connection url",
|
||||
)
|
||||
flag.StringVar(&secret, "secret", "", "a >= 16-character random string for JWT key signing")
|
||||
flag.StringVar(&primaryDomain, "domain", "", "the base domain to use for all clients")
|
||||
flag.StringVar(&relayDomain, "tunnel-domain", "", "the domain name of the tunnel relay service, if different from base domain")
|
||||
var port string
|
||||
var lnAddr string
|
||||
var dbURL string
|
||||
var challengesPort string
|
||||
|
||||
flag.StringVar(&port, "port", "",
|
||||
"port to listen to (default localhost 3000)")
|
||||
flag.StringVar(&lnAddr, "listen", "",
|
||||
"IPv4 or IPv6 bind address + port (instead of --port)")
|
||||
flag.StringVar(&challengesPort, "challenges-port", "80",
|
||||
"port to use to respond to .well-known/acme-challenge tokens")
|
||||
flag.StringVar(&dbURL, "db-url", "postgres://postgres:postgres@localhost:5432/postgres",
|
||||
"database (postgres) connection url")
|
||||
flag.StringVar(&secret, "secret", "",
|
||||
"a >= 16-character random string for JWT key signing")
|
||||
flag.StringVar(&primaryDomain, "domain", "",
|
||||
"the base domain to use for all clients")
|
||||
flag.StringVar(&relayDomain, "tunnel-domain", "",
|
||||
"the domain name of the tunnel relay service, if different from base domain")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if "" == primaryDomain {
|
||||
help()
|
||||
os.Exit(1)
|
||||
if 0 == len(primaryDomain) {
|
||||
primaryDomain = os.Getenv("DOMAIN")
|
||||
}
|
||||
if "" == relayDomain {
|
||||
|
||||
if 0 == len(relayDomain) {
|
||||
relayDomain = os.Getenv("TUNNEL_DOMAIN")
|
||||
}
|
||||
if 0 == len(relayDomain) {
|
||||
relayDomain = primaryDomain
|
||||
}
|
||||
|
||||
if "" != os.Getenv("GODADDY_API_KEY") {
|
||||
if 0 == len(dbURL) {
|
||||
dbURL = os.Getenv("DB_URL")
|
||||
}
|
||||
|
||||
if 0 == len(secret) {
|
||||
secret = os.Getenv("SECRET")
|
||||
}
|
||||
|
||||
// prefer --listen (with address) over --port (localhost only)
|
||||
if 0 == len(lnAddr) {
|
||||
lnAddr = os.Getenv("LISTEN")
|
||||
}
|
||||
if 0 == len(lnAddr) {
|
||||
if 0 == len(port) {
|
||||
port = os.Getenv("PORT")
|
||||
}
|
||||
if 0 == len(port) {
|
||||
port = "3000"
|
||||
}
|
||||
lnAddr = "localhost:" + port
|
||||
}
|
||||
|
||||
if len(os.Getenv("GODADDY_API_KEY")) > 0 {
|
||||
id := os.Getenv("GODADDY_API_KEY")
|
||||
apiSecret := os.Getenv("GODADDY_API_SECRET")
|
||||
if provider, err = newGoDaddyDNSProvider(id, apiSecret); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
} else if "" != os.Getenv("DUCKDNS_TOKEN") {
|
||||
} else if len(os.Getenv("DUCKDNS_TOKEN")) > 0 {
|
||||
if provider, err = newDuckDNSProvider(os.Getenv("DUCKDNS_TOKEN")); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
} else if len(os.Getenv("NAMECOM_API_TOKEN")) > 0 {
|
||||
if provider, err = newNameDotComDNSProvider(
|
||||
os.Getenv("NAMECOM_USERNAME"),
|
||||
os.Getenv("NAMECOM_API_TOKEN"),
|
||||
); nil != err {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic("Must provide either DUCKDNS or GODADDY credentials")
|
||||
fmt.Println("DNS-01 relay disabled")
|
||||
}
|
||||
|
||||
if "" == secret {
|
||||
secret = os.Getenv("SECRET")
|
||||
}
|
||||
if "" == secret {
|
||||
if 0 == len(primaryDomain) || 0 == len(secret) || 0 == len(dbURL) {
|
||||
help()
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
connStr := *dbURL
|
||||
connStr := dbURL
|
||||
// TODO url.Parse
|
||||
if strings.Contains(connStr, "@localhost/") || strings.Contains(connStr, "@localhost:") {
|
||||
connStr += "?sslmode=disable"
|
||||
|
@ -104,16 +143,23 @@ func main() {
|
|||
defer store.Close()
|
||||
|
||||
go func() {
|
||||
fmt.Println("Listening for ACME challenges on :" + *challengesPort)
|
||||
if err := http.ListenAndServe(":"+*challengesPort, routeStatic()); nil != err {
|
||||
fmt.Println("Listening for ACME challenges on :" + challengesPort)
|
||||
if err := http.ListenAndServe(":"+challengesPort, routeStatic()); nil != err {
|
||||
log.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
bind := *addr + ":" + *port
|
||||
fmt.Println("Listening on", bind)
|
||||
fmt.Fprintf(os.Stderr, "failed: %s", http.ListenAndServe(bind, routeAll()))
|
||||
fmt.Println("Listening on", lnAddr)
|
||||
fmt.Fprintf(os.Stderr, "failed: %s", http.ListenAndServe(lnAddr, routeAll()))
|
||||
}
|
||||
|
||||
// newNameDotComDNSProvider is for the sake of demoing the tunnel
|
||||
func newNameDotComDNSProvider(username, apitoken string) (*namedotcom.DNSProvider, error) {
|
||||
config := namedotcom.NewDefaultConfig()
|
||||
config.Username = username
|
||||
config.APIToken = apitoken
|
||||
return namedotcom.NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// newDuckDNSProvider is for the sake of demoing the tunnel
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/go-chi/chi/middleware"
|
||||
)
|
||||
|
||||
// MgmtClaims includes a Slug, for backwards compatibility
|
||||
type MgmtClaims struct {
|
||||
Slug string `json:"slug"`
|
||||
jwt.StandardClaims
|
||||
|
@ -197,7 +198,7 @@ func routeAll() chi.Router {
|
|||
http.Error(w, msg, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if "" != original.MachinePPID {
|
||||
if len(original.MachinePPID) > 0 {
|
||||
msg := `{"error":"the presented key has already been used", "code":"E_EXIST"}`
|
||||
log.Printf("/api/register-device/\n")
|
||||
log.Println(err)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
telebit "git.rootprojects.org/root/telebit"
|
||||
"git.rootprojects.org/root/telebit/dbg"
|
||||
|
@ -30,6 +31,7 @@ var durAbbrs = map[byte]bool{
|
|||
func main() {
|
||||
var secret, clientSecret, relaySecret string
|
||||
|
||||
debug := flag.Bool("debug", true, "show more debug output")
|
||||
machinePPID := flag.String("machine-ppid", "", "spoof the machine ppid")
|
||||
machineID := flag.String("machine-id", "", "spoof the raw machine id")
|
||||
vendorID := flag.String("vendor-id", "", "a unique identifier for a deploy target environment")
|
||||
|
@ -39,6 +41,10 @@ func main() {
|
|||
flag.StringVar(&secret, "secret", "", "either the remote server or the tunnel relay secret (used for JWT authentication)")
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
dbg.Debug = *debug
|
||||
}
|
||||
|
||||
if 0 == len(*authURL) {
|
||||
*authURL = os.Getenv("AUTH_URL")
|
||||
}
|
||||
|
@ -145,7 +151,7 @@ func main() {
|
|||
fmt.Fprintf(os.Stderr, "[debug] pub = %s\n", pub)
|
||||
}
|
||||
|
||||
tok, err := authstore.HMACToken(ppid, expNum)
|
||||
tok, err := authstore.HMACToken(ppid, 15*time.Minute, expNum)
|
||||
if nil != err {
|
||||
fmt.Fprintf(os.Stderr, "signing error: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
|
|
@ -66,6 +66,14 @@ func InitAdmin(authURL string) {
|
|||
|
||||
// Proxy mgmt server ACME DNS 01 Challenges
|
||||
r.Get("/api/dns/*", proxyHandleFunc)
|
||||
r.Post("/api/dns/*", proxyHandleFunc)
|
||||
r.Delete("/api/dns/*", proxyHandleFunc)
|
||||
r.Get("/api/http/*", proxyHandleFunc)
|
||||
r.Post("/api/http/*", proxyHandleFunc)
|
||||
r.Delete("/api/http/*", proxyHandleFunc)
|
||||
r.Get("/api/acme-relay/*", proxyHandleFunc)
|
||||
r.Post("/api/acme-relay/*", proxyHandleFunc)
|
||||
r.Delete("/api/acme-relay/*", proxyHandleFunc)
|
||||
|
||||
r.Route("/api", func(r chi.Router) {
|
||||
// TODO token needs a globally unique subject
|
||||
|
|
|
@ -18,9 +18,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
telebit "git.rootprojects.org/root/telebit"
|
||||
"git.rootprojects.org/root/telebit"
|
||||
"git.rootprojects.org/root/telebit/dbg"
|
||||
tbDns01 "git.rootprojects.org/root/telebit/internal/dns01"
|
||||
"git.rootprojects.org/root/telebit/internal/dns01"
|
||||
"git.rootprojects.org/root/telebit/internal/http01"
|
||||
"git.rootprojects.org/root/telebit/internal/service"
|
||||
"git.rootprojects.org/root/telebit/iplist"
|
||||
|
@ -28,7 +28,7 @@ import (
|
|||
"git.rootprojects.org/root/telebit/mgmt/authstore"
|
||||
"git.rootprojects.org/root/telebit/table"
|
||||
"git.rootprojects.org/root/telebit/tunnel"
|
||||
legoDns01 "github.com/go-acme/lego/v3/challenge/dns01"
|
||||
legoDNS01 "github.com/go-acme/lego/v3/challenge/dns01"
|
||||
|
||||
"github.com/coolaj86/certmagic"
|
||||
"github.com/denisbrodbeck/machineid"
|
||||
|
@ -36,6 +36,7 @@ import (
|
|||
"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/go-acme/lego/v3/providers/dns/namedotcom"
|
||||
"github.com/joho/godotenv"
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
)
|
||||
|
@ -112,6 +113,7 @@ func main() {
|
|||
var portForwards []Forward
|
||||
var resolvers []string
|
||||
|
||||
debug := flag.Bool("debug", true, "show debug output")
|
||||
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
|
||||
vendorID := flag.String("vendor-id", "", "a unique identifier for a deploy target environment")
|
||||
|
@ -133,7 +135,7 @@ func main() {
|
|||
apiHostname := flag.String("api-hostname", "", "the hostname used to manage clients")
|
||||
secret := flag.String("secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
|
||||
token := flag.String("token", "", "an auth token for the server (instead of generating --secret); use --token=false to ignore any $TOKEN in env")
|
||||
_ = flag.String("leeway", "", "(reserved for future use) allow for time drift / skew (hard-coded to 15 minutes)")
|
||||
leeway := flag.Duration("leeway", 15*time.Minute, "allow for time drift / skew (hard-coded to 15 minutes)")
|
||||
bindAddrsStr := flag.String("listen", "", "list of bind addresses on which to listen, such as localhost:80, or :443")
|
||||
tlsLocals := flag.String("tls-locals", "", "like --locals, but TLS will be used to connect to the local port")
|
||||
locals := flag.String("locals", "", "a list of <from-domain>:<to-port>")
|
||||
|
@ -142,7 +144,7 @@ func main() {
|
|||
flag.Parse()
|
||||
|
||||
if !dbg.Debug {
|
||||
dbg.Debug = *verbose
|
||||
dbg.Debug = *verbose || *debug
|
||||
}
|
||||
|
||||
if len(*envpath) > 0 {
|
||||
|
@ -253,7 +255,7 @@ func main() {
|
|||
for _, resolver := range strings.Fields(strings.ReplaceAll(*resolverList, ",", " ")) {
|
||||
resolvers = append(resolvers, resolver)
|
||||
}
|
||||
legoDns01.AddRecursiveNameservers(resolvers)
|
||||
legoDNS01.AddRecursiveNameservers(resolvers)
|
||||
}
|
||||
|
||||
if 0 == len(*portToPorts) {
|
||||
|
@ -325,7 +327,7 @@ func main() {
|
|||
*token = ""
|
||||
}
|
||||
if 0 == len(*token) {
|
||||
*token, err = authstore.HMACToken(ppid)
|
||||
*token, err = authstore.HMACToken(ppid, *leeway)
|
||||
if dbg.Debug {
|
||||
fmt.Printf("[debug] app_id: %q\n", VendorID)
|
||||
//fmt.Printf("[debug] client_secret: %q\n", ClientSecret)
|
||||
|
@ -384,19 +386,18 @@ func main() {
|
|||
authorizer = NewAuthorizer(*authURL)
|
||||
|
||||
dns01Base := directory.DNS01Proxy.URL
|
||||
if "" == *acmeRelay {
|
||||
if 0 == len(*acmeRelay) {
|
||||
*acmeRelay = dns01Base
|
||||
} else {
|
||||
fmt.Println("Suggested ACME DNS 01 Proxy URL:", dns01Base)
|
||||
fmt.Println("--acme-relay-url ACME DNS 01 Proxy URL:", *acmeRelay)
|
||||
}
|
||||
if "" == *acmeRelay {
|
||||
fmt.Fprintf(os.Stderr, "Discovered Directory Endpoints: %+v\n", directory)
|
||||
fmt.Fprintf(os.Stderr, "No ACME DNS 01 Proxy URL detected, nor supplied\n")
|
||||
os.Exit(exitBadConfig)
|
||||
return
|
||||
if 0 == len(*acmeHTTP01Relay) {
|
||||
*acmeHTTP01Relay = directory.HTTP01Proxy.URL
|
||||
} else {
|
||||
fmt.Println("Suggested ACME HTTP 01 Proxy URL:", dns01Base)
|
||||
fmt.Println("--acme-http-01-relay-url ACME DNS 01 Proxy URL:", *acmeRelay)
|
||||
}
|
||||
fmt.Println("DNS 01 URL", *acmeRelay)
|
||||
|
||||
grants, err = telebit.Inspect(*authURL, *token)
|
||||
if nil != err {
|
||||
|
@ -429,7 +430,19 @@ func main() {
|
|||
}
|
||||
authorizer = NewAuthorizer(*authURL)
|
||||
|
||||
var dns01Solver *tbDns01.Solver
|
||||
fmt.Printf("Email: %q\n", *email)
|
||||
|
||||
acme := &telebit.ACME{
|
||||
Email: *email,
|
||||
StoragePath: *certpath,
|
||||
Agree: *acmeAgree,
|
||||
Directory: *acmeDirectory,
|
||||
EnableTLSALPNChallenge: *enableTLSALPN01,
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Blog about the stupidity of this typing
|
||||
// var dns01Solver *dns01.Solver = nil
|
||||
if len(*acmeRelay) > 0 {
|
||||
provider, err := getACMEProvider(acmeRelay, token)
|
||||
if nil != err {
|
||||
|
@ -439,33 +452,8 @@ func main() {
|
|||
os.Exit(exitBadArguments)
|
||||
return
|
||||
}
|
||||
dns01Solver = tbDns01.NewSolver(provider)
|
||||
}
|
||||
|
||||
var http01Solver *http01.Solver
|
||||
if len(*acmeHTTP01Relay) > 0 {
|
||||
endpoint, err := url.Parse(*acmeHTTP01Relay)
|
||||
if nil != err {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(exitBadArguments)
|
||||
return
|
||||
}
|
||||
http01Solver, err = http01.NewSolver(&http01.Config{
|
||||
Endpoint: endpoint,
|
||||
Token: *token,
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Printf("Email: %q\n", *email)
|
||||
|
||||
acme := &telebit.ACME{
|
||||
Email: *email,
|
||||
StoragePath: *certpath,
|
||||
Agree: *acmeAgree,
|
||||
Directory: *acmeDirectory,
|
||||
DNS01Solver: dns01Solver,
|
||||
/*
|
||||
options: legoDns01.WrapPreCheck(func(domain, fqdn, value string, orig legoDns01.PreCheckFunc) (bool, error) {
|
||||
options: legoDNS01.WrapPreCheck(func(domain, fqdn, value string, orig legoDNS01.PreCheckFunc) (bool, error) {
|
||||
ok, err := orig(fqdn, value)
|
||||
if ok && dnsPropagationDelay > 0 {
|
||||
fmt.Printf("[Telebit-ACME-DNS] sleeping an additional %s\n", dnsPropagationDelay)
|
||||
|
@ -474,10 +462,35 @@ func main() {
|
|||
return ok, err
|
||||
}),
|
||||
*/
|
||||
HTTP01Solver: http01Solver,
|
||||
//DNSChallengeOption: legoDns01.DNSProviderOption,
|
||||
EnableHTTPChallenge: *enableHTTP01,
|
||||
EnableTLSALPNChallenge: *enableTLSALPN01,
|
||||
//DNSChallengeOption: legoDNS01.DNSProviderOption,
|
||||
acme.DNS01Solver = dns01.NewSolver(provider)
|
||||
fmt.Println("Using DNS-01 solver for ACME Challenges")
|
||||
}
|
||||
|
||||
if *enableHTTP01 {
|
||||
acme.EnableHTTPChallenge = true
|
||||
}
|
||||
if len(*acmeHTTP01Relay) > 0 {
|
||||
acme.EnableHTTPChallenge = true
|
||||
endpoint, err := url.Parse(*acmeHTTP01Relay)
|
||||
if nil != err {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(exitBadArguments)
|
||||
return
|
||||
}
|
||||
http01Solver, err := http01.NewSolver(&http01.Config{
|
||||
Endpoint: endpoint,
|
||||
Token: *token,
|
||||
})
|
||||
|
||||
acme.HTTP01Solver = http01Solver
|
||||
fmt.Println("Using HTTP-01 solver for ACME Challenges")
|
||||
}
|
||||
|
||||
if nil == acme.HTTP01Solver && nil == acme.DNS01Solver {
|
||||
fmt.Fprintf(os.Stderr, "Neither ACME HTTP 01 nor DNS 01 proxy URL detected, nor supplied\n")
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
mux := muxAll(portForwards, forwards, acme, apiHostname, authURL, grants)
|
||||
|
@ -533,7 +546,7 @@ func main() {
|
|||
time.Sleep(10 * time.Minute)
|
||||
if "" != ClientSecret {
|
||||
// re-create token unless no secret was supplied
|
||||
*token, err = authstore.HMACToken(ppid)
|
||||
*token, err = authstore.HMACToken(ppid, *leeway)
|
||||
}
|
||||
err = mgmt.Ping(*authURL, *token)
|
||||
if nil != err {
|
||||
|
@ -831,6 +844,13 @@ func getACMEProvider(acmeRelay, token *string) (challenge.Provider, error) {
|
|||
if provider, err = newGoDaddyDNSProvider(id, apiSecret); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else if "" != os.Getenv("NAMECOM_API_TOKEN") {
|
||||
if provider, err = newNameDotComDNSProvider(
|
||||
os.Getenv("NAMECOM_USERNAME"),
|
||||
os.Getenv("NAMECOM_API_TOKEN"),
|
||||
); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else if "" != os.Getenv("DUCKDNS_TOKEN") {
|
||||
if provider, err = newDuckDNSProvider(os.Getenv("DUCKDNS_TOKEN")); nil != err {
|
||||
return nil, err
|
||||
|
@ -857,6 +877,14 @@ func getACMEProvider(acmeRelay, token *string) (challenge.Provider, error) {
|
|||
return provider, nil
|
||||
}
|
||||
|
||||
// newNameDotComDNSProvider is for the sake of demoing the tunnel
|
||||
func newNameDotComDNSProvider(username, apitoken string) (*namedotcom.DNSProvider, error) {
|
||||
config := namedotcom.NewDefaultConfig()
|
||||
config.Username = username
|
||||
config.APIToken = apitoken
|
||||
return namedotcom.NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// newDuckDNSProvider is for the sake of demoing the tunnel
|
||||
func newDuckDNSProvider(token string) (*duckdns.DNSProvider, error) {
|
||||
config := duckdns.NewDefaultConfig()
|
||||
|
@ -873,15 +901,15 @@ func newGoDaddyDNSProvider(id, secret string) (*godaddy.DNSProvider, error) {
|
|||
}
|
||||
|
||||
// newAPIDNSProvider is for the sake of demoing the tunnel
|
||||
func newAPIDNSProvider(baseURL string, token string) (*tbDns01.DNSProvider, error) {
|
||||
config := tbDns01.NewDefaultConfig()
|
||||
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 tbDns01.NewDNSProviderConfig(config)
|
||||
return dns01.NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,7 +1,31 @@
|
|||
CLIENT_SUBJECT=newbie
|
||||
# TUNNEL_RELAY_URL
|
||||
# The URL of the Telebit Relay, of course.
|
||||
# Note that many client configuration details can be preassigned at
|
||||
# https://devices.example.com/.well-known/telebit.app/index.json
|
||||
TUNNEL_RELAY_URL=https://devices.example.com/
|
||||
|
||||
# VENDOR_ID
|
||||
# Used to distinguish between different white-labeled Telebit binaries.
|
||||
# It's just as well to generate a random ID for your organization.
|
||||
VENDOR_ID=
|
||||
|
||||
# Used for Let's Encrypt registration
|
||||
# ACME_AGREE
|
||||
ACME_AGREE=true
|
||||
# ACME_EMAIL
|
||||
ACME_EMAIL=johndoe@example.com
|
||||
|
||||
# CLIENT_SUBJECT (optional)
|
||||
# NOT used by Telebit.
|
||||
# This is for the Device Management & Authentication server.
|
||||
CLIENT_SUBJECT=newbie
|
||||
CLIENT_SECRET=xxxxxxxxxxxxxxxx
|
||||
LOCALS=https:$CLIENT_SUBJECT.devices.example.com:3000,https:*.$CLIENT_SUBJECT.devices.example.com:3000
|
||||
#ACME_HTTP_01_RELAY_URL=http://localhost:4200/api/http
|
||||
#PORT_FORWARDS=3443:3001,8443:3002
|
||||
#DUCKDNS_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
|
||||
# AUTH_URL
|
||||
# The client may receive this as `.authn.url`
|
||||
# through `https://$API_DOMAIN/.well-known/telebit.app/index.json`
|
||||
# Setting the ENV AUTH_URL or the flag --auth-url overrides the discovery endpoint
|
||||
|
|
|
@ -1,8 +1,32 @@
|
|||
# For bash tests
|
||||
MGMT_SECRET=xxxxxxxxxxxxxxxx
|
||||
MGMT_URL=https://devices.example.com
|
||||
# DOMAIN
|
||||
# This is the base domain from which all devices
|
||||
# will be given a subdomain (ex: foobar.devices.example.com).
|
||||
DOMAIN=devices.example.com
|
||||
|
||||
# TUNNEL_DOMAIN
|
||||
# This is the domain that will be used for the wss:// connection URL.
|
||||
TUNNEL_DOMAIN=new.telebit.cloud
|
||||
|
||||
# For mgmt server itself
|
||||
#SECRET=XxxxxxxxxxxxxxxX
|
||||
DB_URL=postgres://postgres:postgres@localhost:5432/postgres
|
||||
|
||||
# PORT
|
||||
# the localhost port on which to listen
|
||||
PORT=6468
|
||||
|
||||
# LISTEN
|
||||
# alternative to PORT, including address
|
||||
#LISTEN=localhost:6468
|
||||
|
||||
DUCKDNS_TOKEN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
GODADDY_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
GODADDY_API_SECRET=XXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
#NAMECOM_USERNAME=johndoe
|
||||
#NAMECOM_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
#GODADDY_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
#GODADDY_API_SECRET=XXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
# For bash tests
|
||||
MGMT_SECRET=XxxxxxxxxxxxxxxX
|
||||
MGMT_URL=https://devices.example.com
|
||||
|
|
|
@ -1,21 +1,46 @@
|
|||
# For Tunnel Relay Service
|
||||
|
||||
# SECRET
|
||||
# This should be the same as the MGMT server secret
|
||||
# It is used for JWT token creation and verification
|
||||
SECRET=xxxxxxxxxxxxxxxx
|
||||
|
||||
# VERBOSE=true
|
||||
# This will cause more verbose logs
|
||||
VERBOSE=true
|
||||
|
||||
# API_HOSTNAME
|
||||
# This is the domain name that should be captured for the API
|
||||
# (as opposed to being routed downstream)
|
||||
# If this is not set, the relay will not be active.
|
||||
API_HOSTNAME=devices.example.com
|
||||
|
||||
# LISTEN
|
||||
# This is the addr:port combo to which telebit should bind and listen.
|
||||
# Note: a tunnel client can itself still be a relay through the tunnel.
|
||||
LISTEN=":443"
|
||||
|
||||
# To proxy incoming requests for 'https://mgmt.devices.example.com' to localhost:3010
|
||||
LOCALS=https:mgmt.devices.example.com:3010
|
||||
# LOCALS
|
||||
# Act as a reverse proxy for matching incoming requests
|
||||
# LOCALS=<source-proto>:<source-name>:<destination-port>
|
||||
# Example: 'https://mgmt.devices.example.com' to localhost:6468
|
||||
LOCALS=https:mgmt.devices.example.com:6468
|
||||
|
||||
# For Device Management & Authorization Server
|
||||
AUTH_URL=http://localhost:4200/api
|
||||
# AUTH_URL
|
||||
# Telebit is narrowly scoped to handle network connections
|
||||
# The concerns of Device Management & Authorization should
|
||||
# be handled per each specific use case.
|
||||
AUTH_URL=http://localhost:6468/api
|
||||
|
||||
# For Let's Encrypt ACME registration
|
||||
# For Let's Encrypt ACME registration of the API_HOSTNAME
|
||||
# and LOCALS (reverse-proxied traffic).
|
||||
# This is NOT for the remote telebit clients!
|
||||
ACME_AGREE=true
|
||||
ACME_EMAIL=jon.doe@example.com
|
||||
|
||||
# For Let's Encrypt ACME Challenges (pick one)
|
||||
ACME_RELAY_URL=http://localhost:4200/api/dns
|
||||
SECRET=xxxxxxxxxxxxxxxx
|
||||
#DUCKDNS_TOKEN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
ACME_RELAY_URL=http://localhost:6468/api/acme-relay
|
||||
# (pick ONLY ONE DNS-01 provider)
|
||||
DUCKDNS_TOKEN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
#NAMECOM_USERNAME=johndoe
|
||||
#NAMECOM_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
#GODADDY_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
#GODADDY_API_SECRET=XXXXXXXXXXXXXXXXXXXXXX
|
||||
|
|
1
go.sum
1
go.sum
|
@ -239,6 +239,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
@ -202,11 +203,13 @@ type Solver struct {
|
|||
|
||||
// Present creates a DNS-01 Challenge Token
|
||||
func (s *Solver) Present(ctx context.Context, ch acme.Challenge) error {
|
||||
log.Println("Present DNS-01 challenge solution for", ch.Identifier.Value)
|
||||
return s.provider.Present(ch.Identifier.Value, ch.Token, ch.KeyAuthorization)
|
||||
}
|
||||
|
||||
// CleanUp deletes a DNS-01 Challenge Token
|
||||
func (s *Solver) CleanUp(ctx context.Context, ch acme.Challenge) error {
|
||||
log.Println("CleanUp DNS-01 challenge solution for", ch.Identifier.Value)
|
||||
c := make(chan error)
|
||||
go func() {
|
||||
c <- s.provider.CleanUp(ch.Identifier.Value, ch.Token, ch.KeyAuthorization)
|
||||
|
@ -222,6 +225,7 @@ func (s *Solver) CleanUp(ctx context.Context, ch acme.Challenge) error {
|
|||
// Wait blocks until the TXT record created in Present() appears in
|
||||
// authoritative lookups, i.e. until it has propagated, or until
|
||||
// timeout, whichever is first.
|
||||
func (s *Solver) Wait(ctx context.Context, challenge acme.Challenge) error {
|
||||
return s.dnsChecker.Wait(ctx, challenge)
|
||||
func (s *Solver) Wait(ctx context.Context, ch acme.Challenge) error {
|
||||
log.Println("Wait on DNS-01 challenge self-verification for", ch.Identifier.Value)
|
||||
return s.dnsChecker.Wait(ctx, ch)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
@ -60,8 +61,9 @@ func NewSolver(config *Config) (*Solver, error) {
|
|||
return &Solver{config: config}, nil
|
||||
}
|
||||
|
||||
// Present creates a DNS-01 Challenge Token
|
||||
// Present creates a HTTP-01 Challenge Token
|
||||
func (s *Solver) Present(ctx context.Context, ch acme.Challenge) error {
|
||||
log.Println("Present HTTP-01 challenge solution for", ch.Identifier.Value)
|
||||
msg := &Challenge{
|
||||
Type: "http-01",
|
||||
Token: ch.Token,
|
||||
|
@ -81,6 +83,7 @@ func (s *Solver) Present(ctx context.Context, ch acme.Challenge) error {
|
|||
|
||||
// CleanUp deletes an HTTP-01 Challenge Token
|
||||
func (s *Solver) CleanUp(ctx context.Context, ch acme.Challenge) error {
|
||||
log.Println("CleanUp HTTP-01 challenge solution for", ch.Identifier.Value)
|
||||
msg := &Challenge{
|
||||
Type: "http-01",
|
||||
Token: ch.Token,
|
||||
|
|
|
@ -52,7 +52,7 @@ func ToPublicKeyString(secret string) string {
|
|||
return pub
|
||||
}
|
||||
|
||||
func HMACToken(secret string, maybeExp ...int) (token string, err error) {
|
||||
func HMACToken(secret string, leeway time.Duration, maybeExp ...int) (token string, err error) {
|
||||
keyID := ToPublicKeyString(secret)
|
||||
if dbg.Debug {
|
||||
fmt.Fprintf(os.Stderr, "[debug] keyID=%s\n", keyID)
|
||||
|
@ -67,7 +67,6 @@ func HMACToken(secret string, maybeExp ...int) (token string, err error) {
|
|||
|
||||
b := make([]byte, 16)
|
||||
_, _ = rand.Read(b)
|
||||
leeway := 15 * time.Minute
|
||||
claims := &jwt.StandardClaims{
|
||||
Id: base64.RawURLEncoding.EncodeToString(b),
|
||||
Subject: "", // TODO
|
||||
|
|
|
@ -443,7 +443,7 @@ func NewCertMagic(acme *ACME) (*certmagic.Config, error) {
|
|||
})
|
||||
// yes, a circular reference, passing `magic` to its own Issuer
|
||||
fmt.Printf("ACME Email: %q\n", acme.Email)
|
||||
magic.Issuer = certmagic.NewACMEManager(magic, certmagic.ACMEManager{
|
||||
manager := certmagic.ACMEManager{
|
||||
DNS01Solver: acme.DNS01Solver,
|
||||
HTTP01Solver: acme.HTTP01Solver,
|
||||
CA: acme.Directory,
|
||||
|
@ -452,7 +452,9 @@ func NewCertMagic(acme *ACME) (*certmagic.Config, error) {
|
|||
DisableHTTPChallenge: !acme.EnableHTTPChallenge,
|
||||
DisableTLSALPNChallenge: !acme.EnableTLSALPNChallenge,
|
||||
// plus any other customizations you need
|
||||
})
|
||||
}
|
||||
|
||||
magic.Issuer = certmagic.NewACMEManager(magic, manager)
|
||||
return magic, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,9 @@ func Discover(relay string) (*Endpoints, error) {
|
|||
if len(directives.ChallengeProxy.Pathname) > 0 {
|
||||
directives.ChallengeProxy.URL = endpointToURLString(directives.APIHost, directives.ChallengeProxy)
|
||||
}
|
||||
directives.DNS01Proxy.URL = endpointToURLString(directives.APIHost, directives.DNS01Proxy)
|
||||
if len(directives.DNS01Proxy.Pathname) > 0 {
|
||||
directives.DNS01Proxy.URL = endpointToURLString(directives.APIHost, directives.DNS01Proxy)
|
||||
}
|
||||
if len(directives.HTTP01Proxy.Pathname) > 0 {
|
||||
directives.HTTP01Proxy.URL = endpointToURLString(directives.APIHost, directives.HTTP01Proxy)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ github.com/go-acme/lego/v3/platform/tester
|
|||
github.com/go-acme/lego/v3/platform/wait
|
||||
github.com/go-acme/lego/v3/providers/dns/duckdns
|
||||
github.com/go-acme/lego/v3/providers/dns/godaddy
|
||||
github.com/go-acme/lego/v3/providers/dns/namedotcom
|
||||
# github.com/go-chi/chi v4.1.1+incompatible
|
||||
## explicit
|
||||
github.com/go-chi/chi
|
||||
|
@ -116,8 +117,12 @@ github.com/mholt/acmez
|
|||
github.com/mholt/acmez/acme
|
||||
# github.com/miekg/dns v1.1.30
|
||||
github.com/miekg/dns
|
||||
# github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04
|
||||
github.com/namedotcom/go/namecom
|
||||
# github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e
|
||||
## explicit
|
||||
# github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/errors
|
||||
# github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/pmezard/go-difflib/difflib
|
||||
# github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
|
||||
|
|
Loading…
Reference in New Issue