add token auth

This commit is contained in:
AJ ONeal 2020-05-26 03:05:39 -06:00
parent 10a9ed8e3a
commit 1f22f5f34f
3 changed files with 135 additions and 49 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ acme.d
xversion.go xversion.go
/mplexer/cmd/mgmt/mgmt /mplexer/cmd/mgmt/mgmt
/mplexer/cmd/signjwt/signjwt
/mplexer/cmd/telebit/telebit /mplexer/cmd/telebit/telebit
/telebit /telebit
/cmd/telebit/telebit /cmd/telebit/telebit

View File

@ -3,13 +3,16 @@
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"strings"
"time" "time"
jwt "github.com/dgrijalva/jwt-go"
"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"
"github.com/go-acme/lego/v3/providers/dns/godaddy" "github.com/go-acme/lego/v3/providers/dns/godaddy"
@ -27,6 +30,8 @@ var (
GitTimestamp = "0000-00-00T00:00:00+0000" GitTimestamp = "0000-00-00T00:00:00+0000"
) )
type MWKey string
func main() { func main() {
var err error var err error
var provider challenge.Provider = nil // TODO is this concurrency-safe? var provider challenge.Provider = nil // TODO is this concurrency-safe?
@ -35,12 +40,13 @@ func main() {
addr := flag.String("address", "", "IPv4 or IPv6 bind address") addr := flag.String("address", "", "IPv4 or IPv6 bind address")
port := flag.String("port", "3000", "port to listen to") port := flag.String("port", "3000", "port to listen to")
secret := flag.String("secret", "", "a >= 16-character random string for JWT key signing") // SECRET
flag.Parse() flag.Parse()
if "" != os.Getenv("GODADDY_API_KEY") { if "" != os.Getenv("GODADDY_API_KEY") {
id := os.Getenv("GODADDY_API_KEY") id := os.Getenv("GODADDY_API_KEY")
secret := os.Getenv("GODADDY_API_SECRET") apiSecret := os.Getenv("GODADDY_API_SECRET")
if provider, err = newGoDaddyDNSProvider(id, secret); nil != err { if provider, err = newGoDaddyDNSProvider(id, apiSecret); nil != err {
panic(err) panic(err)
} }
} else if "" != os.Getenv("DUCKDNS_TOKEN") { } else if "" != os.Getenv("DUCKDNS_TOKEN") {
@ -51,15 +57,49 @@ func main() {
panic("Must provide either DUCKDNS or GODADDY credentials") panic("Must provide either DUCKDNS or GODADDY credentials")
} }
if "" == *secret {
*secret = os.Getenv("SECRET")
}
if "" == *secret {
fmt.Fprintf(os.Stderr, "Usage: signjwt <secret>")
os.Exit(1)
return
}
r := chi.NewRouter() r := chi.NewRouter()
r.Use(middleware.Logger) r.Use(middleware.Logger)
r.Use(middleware.Timeout(15 * time.Second)) r.Use(middleware.Timeout(15 * time.Second))
r.Use(middleware.Recoverer) r.Use(middleware.Recoverer)
// TODO add authorization header and validation r.Route("/api/dns", func(r chi.Router) {
//r.Post("/api/dns/{domain:[a-z0-9\\.-]+}", func(w http.ResponseWriter, r *http.Request) { r.Use(func(next http.Handler) http.Handler {
//r.Post("/api/dns/*", func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Post("/api/dns/{domain}", func(w http.ResponseWriter, r *http.Request) { var tokenString string
if auth := strings.Split(r.Header.Get("Authorization"), " "); len(auth) > 1 {
// TODO handle Basic auth tokens as well
tokenString = auth[1]
}
if "" == tokenString {
tokenString = r.URL.Query().Get("access_token")
}
// TODO check expiration and such
tok, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(*secret), nil
})
if nil != err {
fmt.Println("validation error:", tokenString, err)
http.Error(w, "{\"error\":\"could not verify token\"}", http.StatusBadRequest)
return
}
ctx := context.WithValue(r.Context(), MWKey("token"), tok)
next.ServeHTTP(w, r.WithContext(ctx))
})
})
r.Post("/{domain}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
ch := Challenge{} ch := Challenge{}
@ -91,8 +131,8 @@ func main() {
w.Write([]byte("{\"success\":true}\n")) w.Write([]byte("{\"success\":true}\n"))
}) })
// TODO ugly Delete, but wahtever // TODO ugly Delete, but whatever
r.Delete("/api/dns/{domain}/{token}/{keyAuth}", func(w http.ResponseWriter, r *http.Request) { r.Delete("/{domain}/{token}/{keyAuth}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
ch := Challenge{ ch := Challenge{
@ -113,6 +153,7 @@ func main() {
w.Write([]byte("{\"success\":true}\n")) w.Write([]byte("{\"success\":true}\n"))
}) })
})
r.Get("/", func(w http.ResponseWriter, r *http.Request) { r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome\n")) w.Write([]byte("welcome\n"))

View File

@ -0,0 +1,44 @@
package main
import (
"fmt"
"os"
jwt "github.com/dgrijalva/jwt-go"
_ "github.com/joho/godotenv/autoload"
)
func main() {
var secret string
if len(os.Args) == 2 {
secret = os.Args[1]
}
if "" == secret {
secret = os.Getenv("SECRET")
}
if "" == secret {
fmt.Fprintf(os.Stderr, "Usage: signjwt <secret>")
os.Exit(1)
return
}
tok, err := getToken(secret, []string{})
if nil != err {
fmt.Fprintf(os.Stderr, "signing error: %s", err)
os.Exit(1)
return
}
fmt.Println(tok)
}
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
}