2020-11-13 11:53:01 +00:00
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2
2020-06-03 07:47:06 +00:00
2017-03-29 23:49:02 +00:00
package main
import (
2017-03-31 00:04:28 +00:00
"context"
2020-06-03 07:47:06 +00:00
"encoding/base64"
"encoding/hex"
2020-07-22 08:09:06 +00:00
"encoding/json"
2020-05-05 06:44:21 +00:00
"flag"
2017-04-04 18:09:20 +00:00
"fmt"
2020-06-22 06:34:42 +00:00
"io"
2020-11-15 09:04:03 +00:00
"log"
2020-06-03 07:47:06 +00:00
"net"
2020-11-13 09:43:17 +00:00
"net/http"
2020-06-03 07:47:06 +00:00
"net/url"
2020-05-05 06:44:21 +00:00
"os"
2020-11-15 09:04:03 +00:00
"path/filepath"
2020-06-22 06:34:42 +00:00
"regexp"
2020-06-09 00:58:52 +00:00
"strconv"
2017-04-04 18:09:20 +00:00
"strings"
2020-06-03 07:47:06 +00:00
"time"
2017-03-31 00:04:28 +00:00
2020-11-13 12:19:12 +00:00
"git.rootprojects.org/root/telebit/internal/dbg"
2020-11-12 13:30:52 +00:00
"git.rootprojects.org/root/telebit/internal/dns01"
2020-11-05 09:11:17 +00:00
"git.rootprojects.org/root/telebit/internal/http01"
2020-11-18 09:05:36 +00:00
"git.rootprojects.org/root/telebit/internal/http01proxy"
2020-11-13 12:19:12 +00:00
"git.rootprojects.org/root/telebit/internal/iplist"
2020-11-13 09:43:17 +00:00
"git.rootprojects.org/root/telebit/internal/mgmt"
"git.rootprojects.org/root/telebit/internal/mgmt/authstore"
2020-11-05 22:07:01 +00:00
"git.rootprojects.org/root/telebit/internal/service"
2020-11-13 12:19:12 +00:00
"git.rootprojects.org/root/telebit/internal/telebit"
"git.rootprojects.org/root/telebit/internal/tunnel"
2020-05-05 06:44:21 +00:00
2020-11-03 07:56:03 +00:00
"github.com/coolaj86/certmagic"
2020-06-03 07:47:06 +00:00
"github.com/denisbrodbeck/machineid"
"github.com/go-acme/lego/v3/challenge"
2020-11-13 09:43:17 +00:00
legoDNS01 "github.com/go-acme/lego/v3/challenge/dns01"
2020-05-06 17:11:13 +00:00
"github.com/go-acme/lego/v3/providers/dns/duckdns"
2020-06-03 07:47:06 +00:00
"github.com/go-acme/lego/v3/providers/dns/godaddy"
2020-11-12 13:30:52 +00:00
"github.com/go-acme/lego/v3/providers/dns/namedotcom"
2020-11-13 09:43:17 +00:00
"github.com/go-chi/chi"
2020-11-05 23:52:58 +00:00
"github.com/joho/godotenv"
2020-11-15 09:04:03 +00:00
"github.com/judwhite/go-svc/svc"
2020-05-05 06:44:21 +00:00
_ "github.com/joho/godotenv/autoload"
2017-03-29 23:49:02 +00:00
)
2020-08-17 17:35:00 +00:00
const (
// exitOk is for normal exits, such as a graceful disconnect or shutdown
exitOk = 0
// exitBadArguments is for positive failures as a result of arguments
exitBadArguments = 1
// exitBadConfig is for positive failures from an external service
exitBadConfig = 2
2020-09-16 10:29:16 +00:00
// exitRetry is for potentially false negative failures from temporary
// conditions such as a DNS resolution or network failure for which it would
// be reasonable to wait 10 seconds and try again
2020-08-17 17:35:00 +00:00
exitRetry = 29
)
2020-06-03 07:47:06 +00:00
var (
2020-11-13 11:53:01 +00:00
// commit refers to the abbreviated commit hash
commit = "0000000"
// version refers to the most recent tag, plus any commits made since then
version = "v0.0.0-pre0+0000000"
2020-06-03 07:47:06 +00:00
// GitTimestamp refers to the timestamp of the most recent commit
2020-11-13 11:53:01 +00:00
date = "0000-00-00T00:00:00+0000"
2020-11-05 22:07:01 +00:00
// serviceName is the service name
serviceName = "telebit"
// serviceDesc
2020-11-17 19:26:48 +00:00
serviceDesc = "Telebit Secure Proxy"
2020-11-15 09:04:03 +00:00
// defaultRelay should be set when compiled for the client
defaultRelay = "" //"https://telebit.app"
2020-06-03 07:47:06 +00:00
)
2017-04-04 18:09:20 +00:00
2020-11-15 09:04:03 +00:00
var bindAddrs [ ] string
2020-11-05 22:07:01 +00:00
// Forward describes how to route a network connection
2020-06-03 07:47:06 +00:00
type Forward struct {
2020-08-13 08:34:39 +00:00
scheme string
pattern string
port string
localTLS bool
2017-04-04 18:09:20 +00:00
}
2020-06-22 06:34:42 +00:00
var isHostname = regexp . MustCompile ( ` ^[A-Za-z0-9_\.\-]+$ ` ) . MatchString
2020-07-20 15:27:31 +00:00
// VendorID may be baked in, or supplied via ENVs or --args
var VendorID string
2020-07-18 03:51:48 +00:00
// ClientSecret may be baked in, or supplied via ENVs or --args
2020-07-17 07:50:55 +00:00
var ClientSecret string
2020-11-15 09:04:03 +00:00
// Config describes how to run
type Config struct {
acme * telebit . ACME
acmeRelay string
acmeDNS01Relay string
acmeHTTP01Relay string
enableHTTP01 bool
enableTLSALPN01 bool
forwards [ ] Forward
portForwards [ ] Forward
apiHostname string
authURL string
tunnelRelay string // api directory
wsTunnel string // ws tunnel
token string
leeway time . Duration
pairwiseSecret string // secret ppid
logPath string
}
var config Config
2020-11-17 19:26:48 +00:00
func ver ( ) string {
return fmt . Sprintf ( "%s v%s (%s) %s" , serviceName , version , commit [ : 7 ] , date )
}
2020-06-03 07:47:06 +00:00
func main ( ) {
2020-11-15 09:04:03 +00:00
parseFlagsAndENVs ( )
prg := program { }
defer func ( ) {
if prg . LogFile != nil {
if closeErr := prg . LogFile . Close ( ) ; closeErr != nil {
log . Printf ( "error closing '%s': %v\n" , prg . LogFile . Name ( ) , closeErr )
}
}
} ( )
// call svc.Run to start your program/service
// svc.Run will call Init, Start, and Stop
if err := svc . Run ( & prg ) ; err != nil {
log . Fatal ( err )
}
}
// implements svc.Service
type program struct {
LogFile * os . File
}
func ( p * program ) Init ( env svc . Environment ) error {
// write to "telebit.log" when running as a Windows Service
if env . IsWindowsService ( ) && 0 == len ( config . logPath ) {
dir , err := filepath . Abs ( filepath . Dir ( os . Args [ 0 ] ) )
if err != nil {
return err
}
config . logPath = filepath . Join ( dir , "telebit.log" )
}
if len ( config . logPath ) > 0 {
_ = os . MkdirAll ( filepath . Dir ( config . logPath ) , 0750 )
f , err := os . OpenFile ( config . logPath , os . O_RDWR | os . O_CREATE | os . O_APPEND , 0640 )
if err != nil {
return err
}
os . Stdout = f
os . Stderr = f
2020-11-17 01:19:46 +00:00
dbg . OutFile = f
dbg . ErrFile = f
2020-11-15 09:04:03 +00:00
log . SetOutput ( f )
}
return nil
}
2020-11-17 01:19:46 +00:00
var started bool
2020-11-15 09:04:03 +00:00
func ( p * program ) Start ( ) error {
log . Printf ( "Starting...\n" )
2020-11-17 01:19:46 +00:00
if ! started {
started = true
go fetchDirectivesAndRun ( )
}
2020-11-15 09:04:03 +00:00
return nil
}
func ( p * program ) Stop ( ) error {
2020-11-17 01:19:46 +00:00
log . Printf ( "Can't stop. Doing nothing instead.\n" )
2020-11-15 09:04:03 +00:00
return nil
}
func parseFlagsAndENVs ( ) {
2020-11-05 22:07:01 +00:00
if len ( os . Args ) >= 2 {
if "version" == strings . TrimLeft ( os . Args [ 1 ] , "-" ) {
2020-11-17 19:26:48 +00:00
fmt . Printf ( "%s\n" , ver ( ) )
2020-11-05 22:07:01 +00:00
os . Exit ( exitOk )
return
}
}
if len ( os . Args ) >= 2 {
if "install" == os . Args [ 1 ] {
2020-11-05 23:05:47 +00:00
if err := service . Install ( serviceName , serviceDesc ) ; nil != err {
2020-11-05 22:07:01 +00:00
fmt . Fprintf ( os . Stderr , "%v" , err )
}
return
}
}
2020-06-03 07:47:06 +00:00
var domains [ ] string
2020-09-16 10:29:16 +00:00
var resolvers [ ] string
2020-11-15 09:04:03 +00:00
var dnsPropagationDelay time . Duration
2020-06-03 07:47:06 +00:00
2020-11-17 01:19:46 +00:00
debug := flag . Bool ( "debug" , false , "show debug output" )
2020-11-15 09:04:03 +00:00
verbose := flag . Bool ( "verbose" , false , "log excessively" )
2020-08-25 08:08:56 +00:00
spfDomain := flag . String ( "spf-domain" , "" , "domain with SPF-like list of IP addresses which are allowed to connect to clients" )
2020-07-20 15:27:31 +00:00
vendorID := flag . String ( "vendor-id" , "" , "a unique identifier for a deploy target environment" )
2020-11-05 23:52:58 +00:00
envpath := flag . String ( "env" , "" , "path to .env file" )
2020-06-03 07:47:06 +00:00
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" )
2020-11-15 09:04:03 +00:00
clientSecret := flag . String ( "secret" , "" , "the same secret used by telebit-relay (used for JWT authentication)" )
2020-09-16 10:29:16 +00:00
resolverList := flag . String ( "dns-resolvers" , "" , "a list of resolvers in the format 8.8.8.8:53,8.8.4.4:53" )
2020-11-18 09:05:36 +00:00
proxyHTTP01 := flag . String ( "proxy-http-01" , "" , "listen on port 80 and forward .well-known/acme-challenge traffic to this url" )
2020-11-15 09:04:03 +00:00
flag . DurationVar ( & dnsPropagationDelay , "dns-01-delay" , 0 , "add an extra delay after dns self-check to allow DNS-01 challenges to propagate" )
flag . BoolVar ( & config . enableHTTP01 , "acme-http-01" , false , "enable HTTP-01 ACME challenges" )
flag . BoolVar ( & config . enableTLSALPN01 , "acme-tls-alpn-01" , false , "enable TLS-ALPN-01 ACME challenges" )
flag . StringVar ( & config . logPath , "outfile" , "" , "where to direct output (default system logger or OS stdout)" )
flag . StringVar ( & config . acmeRelay , "acme-relay-url" , "" , "the base url of the ACME relay, if different from relay's directives" )
flag . StringVar ( & config . acmeDNS01Relay , "acme-dns-01-relay-url" , "" , "the base url of the ACME DNS-01 relay, if different from ACME relay" )
flag . StringVar ( & config . acmeHTTP01Relay , "acme-http-01-relay-url" , "" , "the base url of the ACME HTTP-01 relay, if different from ACME relay" )
flag . StringVar ( & config . authURL , "auth-url" , "" , "the base url for authentication, if not the same as the tunnel relay" )
flag . StringVar ( & config . tunnelRelay , "tunnel-relay-url" , "" , "the websocket url at which to connect to the tunnel relay" )
flag . StringVar ( & config . apiHostname , "api-hostname" , "" , "the hostname used to manage clients" )
flag . StringVar ( & config . token , "token" , "" , "an auth token for the server (instead of generating --secret); use --token=false to ignore any $TOKEN in env" )
flag . DurationVar ( & config . leeway , "leeway" , 15 * time . Minute , "allow for time drift / skew (hard-coded to 15 minutes)" )
2020-06-09 00:58:52 +00:00
bindAddrsStr := flag . String ( "listen" , "" , "list of bind addresses on which to listen, such as localhost:80, or :443" )
2020-08-13 08:34:39 +00:00
tlsLocals := flag . String ( "tls-locals" , "" , "like --locals, but TLS will be used to connect to the local port" )
2020-06-03 07:47:06 +00:00
locals := flag . String ( "locals" , "" , "a list of <from-domain>:<to-port>" )
2020-06-09 08:42:56 +00:00
portToPorts := flag . String ( "port-forward" , "" , "a list of <from-port>:<to-port> for raw port-forwarding" )
2020-11-15 09:04:03 +00:00
2020-06-03 07:47:06 +00:00
flag . Parse ( )
2020-05-01 08:18:47 +00:00
2020-11-05 23:52:58 +00:00
if len ( * envpath ) > 0 {
if err := godotenv . Load ( * envpath ) ; nil != err {
fmt . Fprintf ( os . Stderr , "%v" , err )
os . Exit ( exitBadArguments )
return
}
}
2020-11-17 01:19:46 +00:00
dbg . Init ( )
if ! dbg . Verbose {
if * verbose {
dbg . Verbose = true
dbg . Printf ( "--verbose: extra output enabled" )
}
}
if ! dbg . Debug {
if * debug {
dbg . Verbose = true
dbg . Debug = true
dbg . Printf ( "--debug: byte output will be printed in full as hex" )
}
}
2020-11-05 23:52:58 +00:00
2020-08-25 08:08:56 +00:00
spfRecords := iplist . Init ( * spfDomain )
if len ( spfRecords ) > 0 {
fmt . Println (
"Allow client connections from:" ,
strings . Join ( spfRecords , " " ) ,
)
}
2020-06-09 00:58:52 +00:00
if len ( * acmeDirectory ) > 0 {
2020-06-03 07:47:06 +00:00
if * acmeStaging {
fmt . Fprintf ( os . Stderr , "pick either acme-directory or acme-staging\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-06-09 00:58:52 +00:00
return
2020-05-01 08:18:47 +00:00
}
2017-04-04 18:09:20 +00:00
}
2020-06-03 07:47:06 +00:00
if * acmeStaging {
* acmeDirectory = certmagic . LetsEncryptStagingCA
2017-04-04 18:09:20 +00:00
}
2020-07-09 09:37:05 +00:00
if ! * acmeAgree {
if "true" == os . Getenv ( "ACME_AGREE" ) {
* acmeAgree = true
}
}
2020-11-15 09:04:03 +00:00
if 0 == len ( config . acmeRelay ) {
config . acmeRelay = os . Getenv ( "ACME_RELAY_URL" )
}
if 0 == len ( config . acmeHTTP01Relay ) {
config . acmeHTTP01Relay = os . Getenv ( "ACME_HTTP_01_RELAY_URL" )
}
if 0 == len ( config . acmeHTTP01Relay ) {
config . acmeHTTP01Relay = config . acmeRelay
2020-07-22 04:42:21 +00:00
}
2020-11-15 09:04:03 +00:00
if 0 == len ( config . acmeDNS01Relay ) {
config . acmeDNS01Relay = os . Getenv ( "ACME_DNS_01_RELAY_URL" )
}
if 0 == len ( config . acmeDNS01Relay ) {
config . acmeDNS01Relay = config . acmeRelay
2020-11-05 09:11:17 +00:00
}
2020-07-22 04:42:21 +00:00
2020-07-09 09:37:05 +00:00
if 0 == len ( * email ) {
* email = os . Getenv ( "ACME_EMAIL" )
}
2017-04-04 18:09:20 +00:00
2020-06-09 00:58:52 +00:00
if 0 == len ( * locals ) {
2020-06-03 07:47:06 +00:00
* 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 ]
2020-05-01 08:18:47 +00:00
}
2020-11-15 09:04:03 +00:00
config . forwards = append ( config . forwards , Forward {
2020-06-03 07:47:06 +00:00
scheme : scheme ,
pattern : domain ,
port : port ,
} )
// don't load wildcard into jwt domains
if "*" == domain {
continue
2020-05-01 08:18:47 +00:00
}
2020-06-03 07:47:06 +00:00
domains = append ( domains , domain )
2017-04-04 18:09:20 +00:00
}
2020-05-01 08:18:47 +00:00
2020-08-13 08:34:39 +00:00
if 0 == len ( * tlsLocals ) {
* tlsLocals = os . Getenv ( "TLS_LOCALS" )
}
for _ , cfg := range strings . Fields ( strings . ReplaceAll ( * tlsLocals , "," , " " ) ) {
parts := strings . Split ( cfg , ":" )
last := len ( parts ) - 1
port := parts [ last ]
domain := parts [ last - 1 ]
scheme := ""
if len ( parts ) > 2 {
scheme = parts [ 0 ]
}
2020-11-15 09:04:03 +00:00
config . forwards = append ( config . forwards , Forward {
2020-08-13 08:34:39 +00:00
scheme : scheme ,
pattern : domain ,
port : port ,
localTLS : true ,
} )
// don't load wildcard into jwt domains
if "*" == domain {
continue
}
domains = append ( domains , domain )
}
2020-09-16 10:29:16 +00:00
var err error
if 0 == dnsPropagationDelay {
dnsPropagationDelay , err = time . ParseDuration ( os . Getenv ( "DNS_01_DELAY" ) )
}
if 0 == dnsPropagationDelay {
dnsPropagationDelay = 5 * time . Second
}
if 0 == len ( * resolverList ) {
* resolverList = os . Getenv ( "DNS_RESOLVERS" )
}
if len ( * resolverList ) > 0 {
for _ , resolver := range strings . Fields ( strings . ReplaceAll ( * resolverList , "," , " " ) ) {
resolvers = append ( resolvers , resolver )
}
2020-11-12 13:30:52 +00:00
legoDNS01 . AddRecursiveNameservers ( resolvers )
2020-09-16 10:29:16 +00:00
}
2020-06-09 08:42:56 +00:00
if 0 == len ( * portToPorts ) {
* portToPorts = os . Getenv ( "PORT_FORWARDS" )
}
2020-11-15 09:04:03 +00:00
config . portForwards , err = parsePortForwards ( portToPorts )
2020-06-09 08:42:56 +00:00
if nil != err {
fmt . Fprintf ( os . Stderr , "%s" , err )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-06-09 08:42:56 +00:00
return
}
2020-07-17 05:41:04 +00:00
if 0 == len ( * bindAddrsStr ) {
* bindAddrsStr = os . Getenv ( "LISTEN" )
}
2020-11-15 09:04:03 +00:00
bindAddrs , err = parseBindAddrs ( * bindAddrsStr )
2020-06-03 07:47:06 +00:00
if nil != err {
2020-06-09 00:58:52 +00:00
fmt . Fprintf ( os . Stderr , "invalid bind address(es) given to --listen\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-06-09 00:58:52 +00:00
return
2017-04-04 18:09:20 +00:00
}
2020-11-15 09:04:03 +00:00
if dbg . Debug {
fmt . Println ( "[debug] bindAddrs" , bindAddrs , * bindAddrsStr )
}
2017-04-04 18:09:20 +00:00
2020-07-17 07:50:55 +00:00
// Baked-in takes precedence
2020-07-20 15:27:31 +00:00
if 0 == len ( VendorID ) {
VendorID = * vendorID
} else if 0 != len ( * vendorID ) {
if VendorID != * vendorID {
fmt . Fprintf ( os . Stderr , "invalid --vendor-id\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-07-17 08:32:23 +00:00
}
2020-07-17 07:50:55 +00:00
}
2020-07-20 15:27:31 +00:00
if 0 == len ( VendorID ) {
VendorID = os . Getenv ( "VENDOR_ID" )
} else if 0 != len ( os . Getenv ( "VENDOR_ID" ) ) {
if VendorID != os . Getenv ( "VENDOR_ID" ) {
fmt . Fprintf ( os . Stderr , "invalid VENDOR_ID\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-07-17 08:32:23 +00:00
}
2020-07-17 07:50:55 +00:00
}
if 0 == len ( ClientSecret ) {
2020-11-15 09:04:03 +00:00
ClientSecret = * clientSecret
} else if 0 != len ( * clientSecret ) {
if ClientSecret != * clientSecret {
2020-07-17 08:32:23 +00:00
fmt . Fprintf ( os . Stderr , "invalid --secret\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-07-17 08:32:23 +00:00
}
2017-04-04 18:09:20 +00:00
}
2020-07-17 07:50:55 +00:00
if 0 == len ( ClientSecret ) {
ClientSecret = os . Getenv ( "SECRET" )
2020-07-17 08:32:23 +00:00
} else if 0 != len ( os . Getenv ( "SECRET" ) ) {
if ClientSecret != os . Getenv ( "SECRET" ) {
fmt . Fprintf ( os . Stderr , "invalid SECRET\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-07-17 08:32:23 +00:00
}
2020-07-17 07:50:55 +00:00
}
2020-11-15 09:04:03 +00:00
config . pairwiseSecret , err = machineid . ProtectedID ( fmt . Sprintf ( "%s|%s" , VendorID , ClientSecret ) )
2020-06-03 07:47:06 +00:00
if nil != err {
2020-06-09 00:58:52 +00:00
fmt . Fprintf ( os . Stderr , "unauthorized device\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadConfig )
2020-06-03 07:47:06 +00:00
return
2017-04-04 18:09:20 +00:00
}
2020-11-15 09:04:03 +00:00
ppidBytes , _ := hex . DecodeString ( config . pairwiseSecret )
config . pairwiseSecret = base64 . RawURLEncoding . EncodeToString ( ppidBytes )
2017-04-04 18:09:20 +00:00
2020-11-15 09:04:03 +00:00
if 0 == len ( config . token ) {
config . token = os . Getenv ( "TOKEN" )
2020-07-17 07:50:55 +00:00
}
2020-11-15 09:04:03 +00:00
if "false" == config . token {
config . token = ""
2020-09-03 19:41:15 +00:00
}
2020-11-15 09:04:03 +00:00
2020-11-18 09:05:36 +00:00
if 0 == len ( * proxyHTTP01 ) {
* proxyHTTP01 = os . Getenv ( "PROXY_HTTP_01" )
}
2020-11-15 09:04:03 +00:00
if 0 == len ( config . tunnelRelay ) {
config . tunnelRelay = os . Getenv ( "TUNNEL_RELAY_URL" ) // "wss://example.com:443"
2020-06-09 00:58:52 +00:00
}
2020-11-15 09:04:03 +00:00
if 0 == len ( config . tunnelRelay ) {
config . tunnelRelay = defaultRelay
2017-04-04 18:09:20 +00:00
}
2020-11-15 09:04:03 +00:00
if 0 == len ( config . tunnelRelay ) {
2020-07-06 09:56:29 +00:00
if len ( bindAddrs ) > 0 {
fmt . Fprintf ( os . Stderr , "Acting as Relay\n" )
} else {
2020-07-17 05:41:04 +00:00
fmt . Fprintf ( os . Stderr , "error: must provide Relay, or act as Relay\n" )
2020-08-17 17:35:00 +00:00
os . Exit ( exitBadArguments )
2020-07-06 09:56:29 +00:00
return
}
2020-05-05 06:44:21 +00:00
}
2020-11-15 09:04:03 +00:00
if 0 == len ( config . authURL ) {
config . authURL = os . Getenv ( "AUTH_URL" )
2020-07-06 09:56:29 +00:00
}
2020-07-22 04:42:21 +00:00
2020-11-15 09:04:03 +00:00
fmt . Printf ( "Email: %q\n" , * email )
2020-07-22 08:09:06 +00:00
2020-11-15 09:04:03 +00:00
config . acme = & telebit . ACME {
Email : * email ,
StoragePath : * certpath ,
Agree : * acmeAgree ,
Directory : * acmeDirectory ,
EnableTLSALPNChallenge : config . enableTLSALPN01 ,
}
2020-07-22 08:09:06 +00:00
2020-11-15 09:04:03 +00:00
//
// Telebit Relay Server
//
if 0 == len ( config . apiHostname ) {
config . apiHostname = os . Getenv ( "API_HOSTNAME" )
}
2020-11-18 09:05:36 +00:00
// Proxy for HTTP-01 requests
// TODO needs to be limited to .well-known/acme-challenges
if len ( * proxyHTTP01 ) > 0 {
go func ( ) {
fmt . Printf ( "Proxying HTTP-01 on port 80 to %s\n" , * proxyHTTP01 )
log . Fatalf ( "%v" , http01proxy . ListenAndServe ( * proxyHTTP01 , 10 * time . Second ) )
} ( )
}
2020-11-15 09:04:03 +00:00
}
func tokener ( ) string {
token := config . token
if 0 == len ( token ) {
var err error
token , err = authstore . HMACToken ( config . pairwiseSecret , config . leeway )
if dbg . Debug {
fmt . Printf ( "[debug] app_id: %q\n" , VendorID )
//fmt.Printf("[debug] client_secret: %q\n", ClientSecret)
//fmt.Printf("[debug] ppid: %q\n", ppid)
//fmt.Printf("[debug] ppid: [redacted]\n")
fmt . Printf ( "[debug] token: %q\n" , token )
}
2020-06-09 00:58:52 +00:00
if nil != err {
2020-11-15 09:04:03 +00:00
fmt . Fprintf ( os . Stderr , "neither client secret nor token provided\n" )
os . Exit ( exitBadArguments )
return ""
2017-04-04 18:09:20 +00:00
}
}
2020-11-15 09:04:03 +00:00
return token
}
2017-04-04 18:09:20 +00:00
2020-11-15 09:04:03 +00:00
func fetchDirectivesAndRun ( ) {
token := tokener ( )
2020-11-12 13:30:52 +00:00
2020-11-15 09:04:03 +00:00
var grants * telebit . Grants
if len ( config . tunnelRelay ) > 0 {
grants = fetchDirectives ( & config , token )
2020-11-12 13:30:52 +00:00
}
// TODO
// Blog about the stupidity of this typing
// var dns01Solver *dns01.Solver = nil
2020-11-15 09:04:03 +00:00
if len ( config . acmeDNS01Relay ) > 0 {
provider , err := getACMEDNS01Provider ( config . acmeDNS01Relay , tokener )
2020-11-05 09:11:17 +00:00
if nil != err {
fmt . Fprintf ( os . Stderr , "%s\n" , err )
// it's possible for some providers this could be a failed network request,
// but I think in the case of what we specifically support it's bad arguments
os . Exit ( exitBadArguments )
return
}
2020-11-15 09:04:03 +00:00
// TODO Use libdns DNS01Solver instead.
// See https://pkg.go.dev/github.com/caddyserver/certmagic#DNS01Solver
// DNS01Solver{ DNSProvider: libdnsprovider, PropagationTimeout: dnsPropagationDelay, Resolvesr: resolvers }
config . acme . DNS01Solver = dns01 . NewSolver ( provider )
2020-11-12 13:30:52 +00:00
fmt . Println ( "Using DNS-01 solver for ACME Challenges" )
2020-05-05 06:44:21 +00:00
}
2020-11-05 09:11:17 +00:00
2020-11-15 09:04:03 +00:00
if config . enableHTTP01 {
config . acme . EnableHTTPChallenge = true
2020-11-12 13:30:52 +00:00
}
2020-11-15 09:04:03 +00:00
if len ( config . acmeHTTP01Relay ) > 0 {
config . acme . EnableHTTPChallenge = true
endpoint , err := url . Parse ( config . acmeHTTP01Relay )
2020-11-05 09:11:17 +00:00
if nil != err {
fmt . Fprintf ( os . Stderr , "%s\n" , err )
os . Exit ( exitBadArguments )
return
}
2020-11-12 13:30:52 +00:00
http01Solver , err := http01 . NewSolver ( & http01 . Config {
2020-11-05 09:11:17 +00:00
Endpoint : endpoint ,
2020-11-15 09:04:03 +00:00
Tokener : tokener ,
2020-11-05 09:11:17 +00:00
} )
2020-11-15 09:04:03 +00:00
config . acme . HTTP01Solver = http01Solver
2020-11-12 13:30:52 +00:00
fmt . Println ( "Using HTTP-01 solver for ACME Challenges" )
}
2020-11-05 09:11:17 +00:00
2020-11-15 09:04:03 +00:00
if nil == config . acme . HTTP01Solver && nil == config . acme . DNS01Solver {
2020-11-12 13:30:52 +00:00
fmt . Fprintf ( os . Stderr , "Neither ACME HTTP 01 nor DNS 01 proxy URL detected, nor supplied\n" )
2020-11-15 09:04:03 +00:00
os . Exit ( exitBadArguments )
2020-11-12 13:30:52 +00:00
return
2017-04-04 18:09:20 +00:00
}
2020-11-15 09:04:03 +00:00
mux := muxAll ( config . portForwards , config . forwards , config . acme , config . apiHostname , config . authURL , grants )
2020-05-05 06:44:21 +00:00
2020-06-09 00:58:52 +00:00
done := make ( chan error )
for _ , addr := range bindAddrs {
2020-06-29 08:43:46 +00:00
go func ( addr string ) {
2020-06-09 00:58:52 +00:00
fmt . Printf ( "Listening on %s\n" , addr )
ln , err := net . Listen ( "tcp" , addr )
if nil != err {
fmt . Fprintf ( os . Stderr , "failed to bind to %q: %s" , addr , err )
done <- err
return
}
if err := telebit . Serve ( ln , mux ) ; nil != err {
fmt . Fprintf ( os . Stderr , "failed to bind to %q: %s" , addr , err )
done <- err
return
}
2020-06-29 08:43:46 +00:00
} ( addr )
2020-06-09 00:58:52 +00:00
}
//connected := make(chan net.Conn)
2020-06-03 07:47:06 +00:00
go func ( ) {
2020-11-15 09:04:03 +00:00
if 0 == len ( config . wsTunnel ) {
2020-06-09 00:58:52 +00:00
return
}
2020-06-03 07:47:06 +00:00
timeoutCtx , cancel := context . WithDeadline ( context . Background ( ) , time . Now ( ) . Add ( 10 * time . Second ) )
defer cancel ( )
2020-11-15 09:04:03 +00:00
tun , err := telebit . DialWebsocketTunnel ( timeoutCtx , config . wsTunnel , token )
2020-06-03 07:47:06 +00:00
if nil != err {
msg := ""
if strings . Contains ( err . Error ( ) , "bad handshake" ) {
msg = " (may be auth related)"
}
2020-11-15 09:04:03 +00:00
fmt . Fprintf ( os . Stderr , "Error connecting to %s: %s%s\n" , config . wsTunnel , err , msg )
2020-08-17 17:35:00 +00:00
os . Exit ( exitRetry )
2020-06-03 07:47:06 +00:00
return
2017-04-04 18:09:20 +00:00
}
2020-11-15 09:04:03 +00:00
err = mgmt . Ping ( config . authURL , token )
2020-06-03 07:47:06 +00:00
if nil != err {
2020-06-09 00:58:52 +00:00
fmt . Fprintf ( os . Stderr , "failed to ping mgmt server: %s\n" , err )
2020-08-17 17:35:00 +00:00
//os.Exit(exitRetry)
2017-04-04 18:09:20 +00:00
}
2017-03-29 23:49:02 +00:00
2020-06-09 00:58:52 +00:00
go func ( ) {
for {
time . Sleep ( 10 * time . Minute )
2020-11-15 09:04:03 +00:00
if len ( ClientSecret ) > 0 {
2020-07-22 05:47:47 +00:00
// re-create token unless no secret was supplied
2020-11-15 09:04:03 +00:00
token , err = authstore . HMACToken ( config . pairwiseSecret , config . leeway )
2020-07-22 05:47:47 +00:00
}
2020-11-15 09:04:03 +00:00
err = mgmt . Ping ( config . authURL , token )
2020-06-09 00:58:52 +00:00
if nil != err {
fmt . Fprintf ( os . Stderr , "failed to ping mgmt server: %s\n" , err )
2020-08-17 17:35:00 +00:00
//os.Exit(exitRetry)
2020-06-09 00:58:52 +00:00
}
}
} ( )
2020-11-15 09:04:03 +00:00
2020-06-09 00:58:52 +00:00
//connected <- tun
//tun := <-connected
2020-11-15 09:04:03 +00:00
fmt . Printf ( "Listening through %s\n" , config . wsTunnel )
2020-06-09 00:58:52 +00:00
err = telebit . ListenAndServe ( tun , mux )
2020-08-17 21:19:17 +00:00
fmt . Fprintf ( os . Stderr , "Closed server: %s\n" , err )
os . Exit ( exitRetry )
2020-06-09 00:58:52 +00:00
done <- err
2020-06-03 07:47:06 +00:00
} ( )
2020-05-06 17:11:13 +00:00
2020-06-09 00:58:52 +00:00
if err := <- done ; nil != err {
2020-08-17 17:35:00 +00:00
os . Exit ( exitRetry )
2020-06-09 00:58:52 +00:00
}
}
2020-11-15 09:04:03 +00:00
func fetchDirectives ( config * Config , token string ) * telebit . Grants {
var grants * telebit . Grants
directory , err := tunnel . Discover ( config . tunnelRelay )
if nil != err {
fmt . Fprintf ( os . Stderr , "Error: invalid Tunnel Relay URL %q: %s\n" , config . tunnelRelay , err )
os . Exit ( exitRetry )
}
fmt . Printf ( "[Directory] %s\n" , config . tunnelRelay )
jsonb , _ := json . Marshal ( directory )
fmt . Printf ( "\t%s\n" , string ( jsonb ) )
// TODO trimming this should no longer be necessary, but I need to double check
authBase := strings . TrimSuffix ( directory . Authenticate . URL , "/inspect" )
if "" == config . authURL {
config . authURL = authBase
} else {
fmt . Println ( "Suggested Auth URL:" , authBase )
fmt . Println ( "--auth-url Auth URL:" , config . authURL )
}
if "" == config . authURL {
fmt . Fprintf ( os . Stderr , "Discovered Directory Endpoints: %+v\n" , directory )
fmt . Fprintf ( os . Stderr , "No Auth URL detected, nor supplied\n" )
os . Exit ( exitBadConfig )
return nil
}
fmt . Println ( "Auth URL" , config . authURL )
acmeDNS01Relay := directory . DNS01Proxy . URL
if 0 == len ( config . acmeDNS01Relay ) {
config . acmeDNS01Relay = acmeDNS01Relay
} else {
fmt . Println ( "Suggested ACME DNS 01 Proxy URL:" , acmeDNS01Relay )
fmt . Println ( "--acme-relay-url ACME DNS 01 Proxy URL:" , config . acmeDNS01Relay )
}
acmeHTTP01Relay := directory . HTTP01Proxy . URL
if 0 == len ( config . acmeHTTP01Relay ) {
config . acmeHTTP01Relay = acmeHTTP01Relay
} else {
fmt . Println ( "Suggested ACME HTTP 01 Proxy URL:" , acmeHTTP01Relay )
fmt . Println ( "--acme-http-01-relay-url ACME HTTP 01 Proxy URL:" , config . acmeHTTP01Relay )
}
// backwards compat
if 0 == len ( config . acmeRelay ) {
if ! config . enableHTTP01 && len ( config . acmeDNS01Relay ) > 0 {
config . acmeRelay = config . acmeDNS01Relay
}
}
grants , err = telebit . Inspect ( config . authURL , token )
if nil != err {
if dbg . Debug {
fmt . Fprintf ( os . Stderr , "failed to inspect token: %s\n" , err )
}
_ , err := mgmt . Register ( config . authURL , ClientSecret , config . pairwiseSecret )
if nil != err {
if strings . Contains ( err . Error ( ) , ` "E_NOT_FOUND" ` ) {
fmt . Fprintf ( os . Stderr , "invalid client credentials: %s\n" , err )
// the server confirmed that the client is bad
os . Exit ( exitBadConfig )
} else {
// there may have been a network error
fmt . Fprintf ( os . Stderr , "failed to register client: %s\n" , err )
os . Exit ( exitRetry )
}
return nil
}
grants , err = telebit . Inspect ( config . authURL , token )
if nil != err {
fmt . Fprintf ( os . Stderr , "failed to authenticate after registering client: %s\n" , err )
// there was no error registering the client, yet there was one authenticating
// therefore this may be an error that will be resolved
os . Exit ( exitRetry )
return nil
}
}
fmt . Printf ( "[Grants]\n\t%#v\n" , grants )
config . wsTunnel = grants . Audience
return grants
}
2020-07-19 06:52:17 +00:00
func muxAll (
portForwards , forwards [ ] Forward ,
acme * telebit . ACME ,
2020-11-15 09:04:03 +00:00
apiHostname , authURL string ,
2020-07-19 06:52:17 +00:00
grants * telebit . Grants ,
) * telebit . RouteMux {
2020-07-18 06:18:09 +00:00
//mux := telebit.NewRouteMux(acme)
mux := telebit . NewRouteMux ( )
// Port forward without TerminatingTLS
for _ , fwd := range portForwards {
msg := fmt . Sprintf ( "Fwd: %s %s" , fwd . pattern , fwd . port )
fmt . Println ( msg )
mux . ForwardTCP ( fwd . pattern , "localhost:" + fwd . port , 120 * time . Second , msg , "[Port Forward]" )
}
2020-11-15 09:04:03 +00:00
//
// Telebit Relay Server
//
if len ( config . apiHostname ) > 0 {
2020-07-22 04:42:21 +00:00
// this is a generic net listener
2020-11-13 09:43:17 +00:00
r := chi . NewRouter ( )
2020-11-17 19:26:48 +00:00
r . Get ( "/version" , func ( w http . ResponseWriter , r * http . Request ) {
w . Write ( [ ] byte ( ver ( ) + "\n" ) )
} )
2020-11-15 09:04:03 +00:00
telebit . RouteAdmin ( config . authURL , r )
2020-07-22 04:42:21 +00:00
apiListener := tunnel . NewListener ( )
2020-07-18 06:18:09 +00:00
go func ( ) {
2020-11-13 09:43:17 +00:00
httpsrv := & http . Server { Handler : r }
2020-07-21 09:29:49 +00:00
httpsrv . Serve ( apiListener )
2020-07-18 06:18:09 +00:00
} ( )
2020-11-15 09:04:03 +00:00
fmt . Printf ( "Will respond to Websocket and API requests to %q\n" , config . apiHostname )
mux . HandleTLS ( config . apiHostname , acme , mux , "[Terminate TLS & Recurse] for " + config . apiHostname )
mux . HandleTCP ( config . apiHostname , telebit . HandlerFunc ( func ( client net . Conn ) error {
2020-07-18 06:18:09 +00:00
if dbg . Debug {
2020-11-15 09:04:03 +00:00
fmt . Printf ( "[debug] Accepting API or WebSocket client %q\n" , config . apiHostname )
2020-07-18 06:18:09 +00:00
}
2020-07-21 09:29:49 +00:00
apiListener . Feed ( client )
2020-07-18 06:18:09 +00:00
if dbg . Debug {
2020-11-15 09:04:03 +00:00
fmt . Printf ( "[debug] done with %q client\n" , config . apiHostname )
2020-07-18 06:18:09 +00:00
}
// nil now means handler in-progress (go routine)
// EOF now means handler finished
return nil
} ) , "[Admin API & Server Relays]" )
}
2020-07-19 06:52:17 +00:00
2020-08-25 08:08:56 +00:00
// TODO close connection on invalid hostname
mux . HandleTCP ( "*" , telebit . HandlerFunc ( routeSubscribersAndClients ) , "[Tun => Remote Servers]" )
2020-07-19 06:52:17 +00:00
if nil != grants {
2020-07-19 08:16:11 +00:00
for i , domainname := range grants . Domains {
fmt . Printf ( "[%d] Will decrypt remote requests to %q\n" , i , domainname )
2020-07-19 06:52:17 +00:00
mux . HandleTLS ( domainname , acme , mux , "[Terminate TLS & Recurse] for (tunnel) " + domainname )
}
}
2020-07-19 08:16:11 +00:00
for i , fwd := range forwards {
2020-07-22 04:42:21 +00:00
fmt . Printf ( "[%d] Will decrypt local requests to \"%s://%s\"\n" , i , fwd . scheme , fwd . pattern )
2020-07-19 06:52:17 +00:00
mux . HandleTLS ( fwd . pattern , acme , mux , "[Terminate TLS & Recurse] for (local) " + fwd . pattern )
}
//mux.HandleTLSFunc(func (sni) bool {
// // do whatever
// return false
//}, acme, mux, "[Terminate TLS & Recurse]")
2020-07-18 06:18:09 +00:00
for _ , fwd := range forwards {
//mux.ForwardTCP("*", "localhost:"+fwd.port, 120*time.Second)
2020-07-21 09:29:49 +00:00
if "https" == fwd . scheme {
2020-08-13 08:34:39 +00:00
if fwd . localTLS {
// this doesn't make much sense, but... security theatre
mux . ReverseProxyHTTPS ( fwd . pattern , "localhost:" + fwd . port , 120 * time . Second , "[Servername Reverse Proxy TLS]" )
} else {
mux . ReverseProxyHTTP ( fwd . pattern , "localhost:" + fwd . port , 120 * time . Second , "[Servername Reverse Proxy]" )
}
2020-07-21 09:29:49 +00:00
}
2020-07-18 06:18:09 +00:00
mux . ForwardTCP ( fwd . pattern , "localhost:" + fwd . port , 120 * time . Second , "[Servername Forward]" )
}
return mux
}
2020-06-22 06:34:42 +00:00
func routeSubscribersAndClients ( client net . Conn ) error {
var wconn * telebit . ConnWrap
switch conn := client . ( type ) {
case * telebit . ConnWrap :
wconn = conn
default :
2020-08-25 08:08:56 +00:00
panic ( "routeSubscribersAndClients is special in that it must receive &ConnWrap{ Conn: conn }" )
2020-06-22 06:34:42 +00:00
}
// We know this to be two parts "ip:port"
dstParts := strings . Split ( client . LocalAddr ( ) . String ( ) , ":" )
//dstAddr := dstParts[0]
dstPort , _ := strconv . Atoi ( dstParts [ 1 ] )
2020-07-18 03:51:48 +00:00
if dbg . Debug {
fmt . Printf ( "[debug] wconn.LocalAddr() %+v\n" , wconn . LocalAddr ( ) )
fmt . Printf ( "[debug] wconn.RemoteAddr() %+v\n" , wconn . RemoteAddr ( ) )
}
2020-07-12 05:59:34 +00:00
2020-06-22 06:34:42 +00:00
if 80 != dstPort && 443 != dstPort {
// TODO handle by port without peeking at Servername / Hostname
// if tryToServePort(client.LocalAddr().String(), wconn) {
// return io.EOF
// }
}
// TODO hostname for plain http?
servername := strings . ToLower ( wconn . Servername ( ) )
if "" != servername && ! isHostname ( servername ) {
_ = client . Close ( )
2020-07-18 03:51:48 +00:00
if dbg . Debug {
fmt . Println ( "[debug] invalid servername" )
}
2020-06-22 06:34:42 +00:00
return fmt . Errorf ( "invalid servername" )
}
2020-07-18 03:51:48 +00:00
if dbg . Debug {
fmt . Printf ( "[debug] wconn.Servername() %+v\n" , servername )
}
2020-06-22 06:34:42 +00:00
// Match full servername "sub.domain.example.com"
if tryToServeName ( servername , wconn ) {
// TODO better non-error
2020-07-09 09:03:04 +00:00
return nil
2020-06-22 06:34:42 +00:00
}
// Match wild names
// - "*.domain.example.com"
// - "*.example.com"
// - (skip)
labels := strings . Split ( servername , "." )
n := len ( labels )
if n < 3 {
2020-07-08 10:28:32 +00:00
// skip
return telebit . ErrNotHandled
2020-06-22 06:34:42 +00:00
}
for i := 1 ; i < n - 1 ; i ++ {
2020-07-18 03:51:48 +00:00
wildname := "*." + strings . Join ( labels [ i : ] , "." )
2020-06-22 06:34:42 +00:00
if tryToServeName ( wildname , wconn ) {
return io . EOF
}
}
// skip
2020-07-08 10:28:32 +00:00
return telebit . ErrNotHandled
2020-06-22 06:34:42 +00:00
}
// tryToServeName picks the server tunnel with the least connections, if any
func tryToServeName ( servername string , wconn * telebit . ConnWrap ) bool {
2020-11-13 12:19:12 +00:00
srv , ok := telebit . GetServer ( servername )
2020-07-17 05:41:04 +00:00
if ! ok || nil == srv {
if ok {
// TODO BUG: Sometimes srv=nil & ok=true, which should not be possible
fmt . Println ( "[bug] found 'srv=nil'" , servername , srv )
}
2020-07-18 03:51:48 +00:00
if dbg . Debug {
fmt . Println ( "[debug] no server to server" , servername )
}
2020-06-22 06:34:42 +00:00
return false
}
2020-08-25 08:08:56 +00:00
// Note: timing can reveal if the client exists
if allowAll , _ := iplist . IsAllowed ( nil ) ; ! allowAll {
addr := wconn . RemoteAddr ( )
if nil == addr {
// handled by denial
wconn . Close ( )
return true
}
2020-08-25 08:34:55 +00:00
// 192.168.1.100:2345
// [::fe12]:2345
remoteIP := addr . String ( )
index := strings . LastIndex ( remoteIP , ":" )
if index < 1 {
// TODO how to handle unexpected invalid address?
wconn . Close ( )
return true
}
remoteIP = remoteIP [ : index ]
fmt . Println ( "remote addr:" , remoteIP )
if "127.0.0.1" != remoteIP &&
"::1" != remoteIP &&
"localhost" != remoteIP {
ipAddr := net . ParseIP ( remoteIP )
2020-08-25 08:08:56 +00:00
if nil == ipAddr {
wconn . Close ( )
return true
}
if ok , err := iplist . IsAllowed ( ipAddr ) ; ! ok || nil != err {
wconn . Close ( )
return true
}
}
}
2020-06-22 06:34:42 +00:00
// async so that the call stack can complete and be released
//srv.clients.Store(wconn.LocalAddr().String(), wconn)
go func ( ) {
2020-07-18 03:51:48 +00:00
if dbg . Debug {
fmt . Printf ( "[debug] found server to handle client:\n%#v\n" , srv )
}
2020-06-22 06:34:42 +00:00
err := srv . Serve ( wconn )
2020-07-18 03:51:48 +00:00
if dbg . Debug {
fmt . Printf ( "[debug] a browser client stream is done: %v\n" , err )
}
2020-06-22 06:34:42 +00:00
} ( )
return true
}
2020-06-09 08:42:56 +00:00
func parsePortForwards ( portToPorts * string ) ( [ ] Forward , error ) {
var portForwards [ ] Forward
for _ , cfg := range strings . Fields ( strings . ReplaceAll ( * portToPorts , "," , " " ) ) {
parts := strings . Split ( cfg , ":" )
if 2 != len ( parts ) {
return nil , fmt . Errorf ( "--port-forward should be in the format 1234:5678, not %q" , cfg )
}
if _ , err := strconv . Atoi ( parts [ 0 ] ) ; nil != err {
return nil , fmt . Errorf ( "couldn't parse port %q of %q" , parts [ 0 ] , cfg )
}
if _ , err := strconv . Atoi ( parts [ 1 ] ) ; nil != err {
return nil , fmt . Errorf ( "couldn't parse port %q of %q" , parts [ 1 ] , cfg )
}
portForwards = append ( portForwards , Forward {
pattern : ":" + parts [ 0 ] ,
port : parts [ 1 ] ,
} )
}
return portForwards , nil
}
2020-06-09 00:58:52 +00:00
func parseBindAddrs ( bindAddrsStr string ) ( [ ] string , error ) {
bindAddrs := [ ] string { }
for _ , addr := range strings . Fields ( strings . ReplaceAll ( bindAddrsStr , "," , " " ) ) {
parts := strings . Split ( addr , ":" )
if len ( parts ) > 2 {
return nil , fmt . Errorf ( "too many colons (:) in bind address %s" , addr )
2020-06-03 07:47:06 +00:00
}
2020-07-17 05:41:04 +00:00
if "" == addr {
2020-06-09 00:58:52 +00:00
continue
}
var hostname , port string
if 2 == len ( parts ) {
hostname = parts [ 0 ]
port = parts [ 1 ]
} else {
port = parts [ 0 ]
}
if _ , err := strconv . Atoi ( port ) ; nil != err {
return nil , fmt . Errorf ( "couldn't parse port of %q" , addr )
}
bindAddrs = append ( bindAddrs , hostname + ":" + port )
}
return bindAddrs , nil
}
2020-11-15 09:04:03 +00:00
func getACMEDNS01Provider ( acmeRelay string , token func ( ) string ) ( challenge . Provider , error ) {
2020-06-09 00:58:52 +00:00
var err error
var provider challenge . Provider = nil
if "" != os . Getenv ( "GODADDY_API_KEY" ) {
id := os . Getenv ( "GODADDY_API_KEY" )
apiSecret := os . Getenv ( "GODADDY_API_SECRET" )
if provider , err = newGoDaddyDNSProvider ( id , apiSecret ) ; nil != err {
return nil , err
}
2020-11-12 13:30:52 +00:00
} 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
}
2020-06-09 00:58:52 +00:00
} else if "" != os . Getenv ( "DUCKDNS_TOKEN" ) {
if provider , err = newDuckDNSProvider ( os . Getenv ( "DUCKDNS_TOKEN" ) ) ; nil != err {
return nil , err
}
} else {
2020-11-15 09:04:03 +00:00
if "" == acmeRelay {
2020-07-17 05:41:04 +00:00
return nil , fmt . Errorf ( "No relay for ACME DNS-01 challenges given to --acme-relay-url" )
2020-06-09 00:58:52 +00:00
}
2020-11-15 09:04:03 +00:00
endpoint := acmeRelay
2020-07-17 08:16:18 +00:00
if ! strings . HasSuffix ( endpoint , "/" ) {
endpoint += "/"
2020-06-09 00:58:52 +00:00
}
2020-07-17 08:16:18 +00:00
/ *
if strings . HasSuffix ( endpoint , "/" ) {
endpoint = endpoint [ : len ( endpoint ) - 1 ]
}
endpoint += "/api/dns/"
* /
2020-11-15 09:04:03 +00:00
if provider , err = newAPIDNSProvider ( endpoint , tokener ) ; nil != err {
2020-06-09 00:58:52 +00:00
return nil , err
}
}
2020-05-06 17:11:13 +00:00
2020-06-09 00:58:52 +00:00
return provider , nil
2020-06-03 07:47:06 +00:00
}
2020-05-05 06:44:21 +00:00
2020-11-12 13:30:52 +00:00
// 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 )
}
2020-05-06 17:11:13 +00:00
// 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 )
}
2020-06-03 07:47:06 +00:00
// 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
2020-11-15 09:04:03 +00:00
func newAPIDNSProvider ( baseURL string , tokener func ( ) string ) ( * dns01 . DNSProvider , error ) {
2020-11-12 13:30:52 +00:00
config := dns01 . NewDefaultConfig ( )
2020-11-15 09:04:03 +00:00
config . Tokener = tokener
2020-06-03 07:47:06 +00:00
endpoint , err := url . Parse ( baseURL )
if nil != err {
return nil , err
}
config . Endpoint = endpoint
2020-11-12 13:30:52 +00:00
return dns01 . NewDNSProviderConfig ( config )
2020-06-03 07:47:06 +00:00
}
/ *
// 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 )
* /