telebit/cmd/mgmt/mgmt.go

179 lines
4.7 KiB
Go
Raw Normal View History

2020-05-26 07:47:22 +00:00
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver
package main
import (
"flag"
"fmt"
2020-05-30 23:14:40 +00:00
"log"
2020-05-26 07:47:22 +00:00
"net/http"
"os"
2020-05-26 09:05:39 +00:00
"strings"
2020-05-26 07:47:22 +00:00
2020-07-20 22:20:59 +00:00
"git.rootprojects.org/root/telebit/mgmt/authstore"
2020-05-30 23:14:40 +00:00
2020-05-26 07:47:22 +00:00
"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"
2020-11-12 13:30:52 +00:00
"github.com/go-acme/lego/v3/providers/dns/namedotcom"
2020-05-26 07:47:22 +00:00
_ "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"
)
// MWKey is a type guard
2020-05-26 09:05:39 +00:00
type MWKey string
2020-05-30 23:14:40 +00:00
var store authstore.Store
var provider challenge.Provider = nil // TODO is this concurrency-safe?
2020-07-22 03:56:46 +00:00
var secret string
2020-05-31 13:02:46 +00:00
var primaryDomain string
2020-07-22 03:56:46 +00:00
var relayDomain string
2020-05-31 13:02:46 +00:00
func help() {
2020-07-22 05:47:47 +00:00
fmt.Fprintf(os.Stderr, "Usage: mgmt --domain <devices.example.com> --secret <128-bit secret>\n")
2020-05-31 13:02:46 +00:00
}
2020-05-30 23:14:40 +00:00
2020-05-26 07:47:22 +00:00
func main() {
var err error
2020-11-12 13:30:52 +00:00
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")
2020-05-26 07:47:22 +00:00
flag.Parse()
2020-11-12 13:30:52 +00:00
if 0 == len(primaryDomain) {
primaryDomain = os.Getenv("DOMAIN")
2020-05-31 13:02:46 +00:00
}
2020-11-12 13:30:52 +00:00
if 0 == len(relayDomain) {
relayDomain = os.Getenv("TUNNEL_DOMAIN")
}
if 0 == len(relayDomain) {
2020-07-22 05:47:47 +00:00
relayDomain = primaryDomain
}
2020-05-31 13:02:46 +00:00
2020-11-12 13:30:52 +00:00
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 {
2020-05-26 07:47:22 +00:00
id := os.Getenv("GODADDY_API_KEY")
2020-05-26 09:05:39 +00:00
apiSecret := os.Getenv("GODADDY_API_SECRET")
if provider, err = newGoDaddyDNSProvider(id, apiSecret); nil != err {
2020-05-26 07:47:22 +00:00
panic(err)
}
2020-11-12 13:30:52 +00:00
} else if len(os.Getenv("DUCKDNS_TOKEN")) > 0 {
2020-05-26 07:47:22 +00:00
if provider, err = newDuckDNSProvider(os.Getenv("DUCKDNS_TOKEN")); nil != err {
panic(err)
}
2020-11-12 13:30:52 +00:00
} 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)
}
2020-05-26 07:47:22 +00:00
} else {
2020-11-12 13:30:52 +00:00
fmt.Println("DNS-01 relay disabled")
2020-05-26 07:47:22 +00:00
}
2020-11-12 13:30:52 +00:00
if 0 == len(primaryDomain) || 0 == len(secret) || 0 == len(dbURL) {
2020-05-31 13:02:46 +00:00
help()
2020-05-26 09:05:39 +00:00
os.Exit(1)
return
}
2020-11-12 13:30:52 +00:00
connStr := dbURL
2020-05-30 23:14:40 +00:00
// TODO url.Parse
2020-06-03 06:17:30 +00:00
if strings.Contains(connStr, "@localhost/") || strings.Contains(connStr, "@localhost:") {
2020-05-30 23:14:40 +00:00
connStr += "?sslmode=disable"
} else {
connStr += "?sslmode=required"
}
store, err = authstore.NewStore(connStr, initSQL)
if nil != err {
log.Fatal("connection error", err)
return
}
2020-07-22 03:56:46 +00:00
_ = store.SetMaster(secret)
2020-05-30 23:14:40 +00:00
defer store.Close()
2020-05-26 07:47:22 +00:00
go func() {
2020-11-12 13:30:52 +00:00
fmt.Println("Listening for ACME challenges on :" + challengesPort)
if err := http.ListenAndServe(":"+challengesPort, routeStatic()); nil != err {
2020-11-05 09:19:27 +00:00
log.Fatal(err)
os.Exit(1)
}
}()
2020-11-12 13:30:52 +00:00
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)
2020-05-26 07:47:22 +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)
}
// 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)
}