enable auth checking
This commit is contained in:
parent
6736d68446
commit
7303e36a16
|
@ -1,24 +0,0 @@
|
||||||
r.Use(func(next http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
valid, _ := ctx.Value(MWKey("valid")).(bool)
|
|
||||||
|
|
||||||
if !valid {
|
|
||||||
// misdirection
|
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
w.Write([]byte("{\"success\":true}\n"))
|
|
||||||
//http.Error(w, `{"error":"could not verify token"}`, http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if nil != err2 {
|
|
||||||
// a little misdirection there
|
|
||||||
msg := `{"error":"internal server error"}`
|
|
||||||
http.Error(w, msg, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,17 +1,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
||||||
|
|
||||||
"github.com/denisbrodbeck/machineid"
|
"github.com/denisbrodbeck/machineid"
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,14 +41,7 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b := make([]byte, 16)
|
tok, err := authstore.HMACToken(secret)
|
||||||
_, _ = rand.Read(b)
|
|
||||||
claims := &jwt.StandardClaims{
|
|
||||||
Id: base64.RawURLEncoding.EncodeToString(b),
|
|
||||||
IssuedAt: time.Now().Unix(),
|
|
||||||
ExpiresAt: time.Now().Add(5 * time.Minute).Unix(),
|
|
||||||
}
|
|
||||||
tok, err := getToken(secret, claims)
|
|
||||||
if nil != err {
|
if nil != err {
|
||||||
fmt.Fprintf(os.Stderr, "signing error: %s", err)
|
fmt.Fprintf(os.Stderr, "signing error: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -60,25 +50,3 @@ func main() {
|
||||||
|
|
||||||
fmt.Println(tok)
|
fmt.Println(tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getToken(secret string, tokenData *jwt.StandardClaims) (token string, err error) {
|
|
||||||
keyID := authstore.ToPublicKeyString(secret)
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "secret: %s\n", secret)
|
|
||||||
fmt.Fprintf(os.Stderr, "kid: %s\n", keyID)
|
|
||||||
|
|
||||||
jwtToken := &jwt.Token{
|
|
||||||
Header: map[string]interface{}{
|
|
||||||
"kid": keyID,
|
|
||||||
"typ": "JWT",
|
|
||||||
"alg": jwt.SigningMethodHS256.Alg(),
|
|
||||||
},
|
|
||||||
Claims: tokenData,
|
|
||||||
Method: jwt.SigningMethodHS256,
|
|
||||||
}
|
|
||||||
|
|
||||||
if token, err = jwtToken.SignedString([]byte(secret)); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ import (
|
||||||
|
|
||||||
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
||||||
dns01 "git.coolaj86.com/coolaj86/go-telebitd/mplexer/dns01"
|
dns01 "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/caddyserver/certmagic"
|
||||||
"github.com/denisbrodbeck/machineid"
|
"github.com/denisbrodbeck/machineid"
|
||||||
|
@ -58,23 +60,16 @@ func main() {
|
||||||
enableHTTP01 := flag.Bool("acme-http-01", false, "enable HTTP-01 ACME challenges")
|
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")
|
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")
|
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")
|
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)")
|
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)")
|
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>")
|
locals := flag.String("locals", "", "a list of <from-domain>:<to-port>")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
muid, err := machineid.ProtectedID(*appID)
|
|
||||||
if nil != err {
|
|
||||||
fmt.Fprintf(os.Stderr, "unauthorized device")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
muidb, err := hex.DecodeString(muid)
|
|
||||||
muid = base64.RawURLEncoding.EncodeToString(muidb)
|
|
||||||
|
|
||||||
if len(os.Args) >= 2 {
|
if len(os.Args) >= 2 {
|
||||||
if "version" == os.Args[1] {
|
if "version" == os.Args[1] {
|
||||||
fmt.Printf("telebit %s %s %s %s", GitVersion, GitRev[:7], GitTimestamp, muid[:24])
|
fmt.Printf("telebit %s %s %s %s", GitVersion, GitRev[:7], GitTimestamp)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,11 +109,19 @@ func main() {
|
||||||
domains = append(domains, domain)
|
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 "" == *token {
|
||||||
if "" == *secret {
|
if "" == *secret {
|
||||||
*secret = os.Getenv("SECRET")
|
*secret = os.Getenv("SECRET")
|
||||||
}
|
}
|
||||||
*token, err = getToken(*secret, domains)
|
*token, err = authstore.HMACToken(ppid)
|
||||||
}
|
}
|
||||||
if nil != err {
|
if nil != err {
|
||||||
fmt.Fprintf(os.Stderr, "neither secret nor token provided")
|
fmt.Fprintf(os.Stderr, "neither secret nor token provided")
|
||||||
|
@ -137,6 +140,9 @@ func main() {
|
||||||
if "" == *acmeRelay {
|
if "" == *acmeRelay {
|
||||||
*acmeRelay = strings.Replace(*relay, "ws", "http", 1) // "https://example.com:443"
|
*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") {
|
if "" != os.Getenv("GODADDY_API_KEY") {
|
||||||
id := os.Getenv("GODADDY_API_KEY")
|
id := os.Getenv("GODADDY_API_KEY")
|
||||||
|
@ -159,6 +165,21 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grants, err := mgmt.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 = mgmt.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{
|
acme := &telebit.ACME{
|
||||||
Email: *email,
|
Email: *email,
|
||||||
StoragePath: *certpath,
|
StoragePath: *certpath,
|
||||||
|
|
|
@ -20,4 +20,4 @@ echo "PPID: $my_ppid KeyID: $my_keyid"
|
||||||
|
|
||||||
TOKEN=$(go run cmd/signjwt/*.go $my_ppid)
|
TOKEN=$(go run cmd/signjwt/*.go $my_ppid)
|
||||||
curl -X POST http://localhost:3000/api/ping -H "Authorization: Bearer ${TOKEN}"
|
curl -X POST http://localhost:3000/api/ping -H "Authorization: Bearer ${TOKEN}"
|
||||||
curl -X POST http://localhost:3000/api/inspect -H "Authorization: Bearer ${TOKEN}"
|
curl http://localhost:3000/api/inspect -H "Authorization: Bearer ${TOKEN}"
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package mgmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
telebit "git.coolaj86.com/coolaj86/go-telebitd/mplexer"
|
||||||
|
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/mgmt/authstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Grants struct {
|
||||||
|
Domains []string `json:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Inspect(authURL, token string) (*Grants, error) {
|
||||||
|
msg, err := telebit.Request("GET", authURL+"/inspect", token, nil)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if nil == msg {
|
||||||
|
return nil, fmt.Errorf("invalid response")
|
||||||
|
}
|
||||||
|
|
||||||
|
grants := &Grants{}
|
||||||
|
err = json.NewDecoder(msg).Decode(grants)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return grants, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Register(authURL, secret, ppid string) (kid string, err error) {
|
||||||
|
pub := authstore.ToPublicKeyString(ppid)
|
||||||
|
jsonb := bytes.NewBuffer([]byte(
|
||||||
|
fmt.Sprintf(`{ "machine_ppid": "%s", "public_key": "%s" }`, ppid, pub),
|
||||||
|
))
|
||||||
|
msg, err := telebit.Request("POST", authURL+"/register-device/"+secret, "", jsonb)
|
||||||
|
if nil != err {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if nil == msg {
|
||||||
|
return "", fmt.Errorf("invalid response")
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := &authstore.Authorization{}
|
||||||
|
err = json.NewDecoder(msg).Decode(auth)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
msgBytes, _ := ioutil.ReadAll(msg)
|
||||||
|
if "" == auth.PublicKey {
|
||||||
|
return "", fmt.Errorf("unexpected server response: no public key: %s", string(msgBytes))
|
||||||
|
}
|
||||||
|
if pub != auth.PublicKey {
|
||||||
|
return "", fmt.Errorf("server disagrees about public key id: %s vs %s", kid, auth.PublicKey)
|
||||||
|
}
|
||||||
|
return auth.PublicKey, nil
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
package authstore
|
package authstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrExists = errors.New("token already exists")
|
var ErrExists = errors.New("token already exists")
|
||||||
|
@ -42,3 +45,30 @@ func ToPublicKeyString(secret string) string {
|
||||||
}
|
}
|
||||||
return pub
|
return pub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HMACToken(secret string) (token string, err error) {
|
||||||
|
keyID := ToPublicKeyString(secret)
|
||||||
|
|
||||||
|
b := make([]byte, 16)
|
||||||
|
_, _ = rand.Read(b)
|
||||||
|
claims := &jwt.StandardClaims{
|
||||||
|
Id: base64.RawURLEncoding.EncodeToString(b),
|
||||||
|
IssuedAt: time.Now().Unix(),
|
||||||
|
ExpiresAt: time.Now().Add(5 * time.Minute).Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
jwtToken := &jwt.Token{
|
||||||
|
Header: map[string]interface{}{
|
||||||
|
"kid": keyID,
|
||||||
|
"typ": "JWT",
|
||||||
|
"alg": jwt.SigningMethodHS256.Alg(),
|
||||||
|
},
|
||||||
|
Claims: claims,
|
||||||
|
Method: jwt.SigningMethodHS256,
|
||||||
|
}
|
||||||
|
|
||||||
|
if token, err = jwtToken.SignedString([]byte(secret)); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ func routeAll() chi.Router {
|
||||||
handleDNSRoutes(r)
|
handleDNSRoutes(r)
|
||||||
handleDeviceRoutes(r)
|
handleDeviceRoutes(r)
|
||||||
|
|
||||||
r.Post("/inspect", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/inspect", func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
claims, ok := ctx.Value(MWKey("claims")).(*MgmtClaims)
|
claims, ok := ctx.Value(MWKey("claims")).(*MgmtClaims)
|
||||||
if !ok {
|
if !ok {
|
|
@ -1,11 +1,14 @@
|
||||||
package telebit
|
package telebit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -254,3 +257,36 @@ func newCertMagic(acme *ACME) (*certmagic.Config, error) {
|
||||||
})
|
})
|
||||||
return magic, nil
|
return magic, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Request(method, fullurl, token string, payload io.Reader) (io.Reader, error) {
|
||||||
|
HTTPClient := &http.Client{
|
||||||
|
Timeout: 15 * time.Second,
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(method, fullurl, payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(token) > 0 {
|
||||||
|
req.Header.Set("Authorization", "Bearer "+token)
|
||||||
|
}
|
||||||
|
if nil != payload {
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := HTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%d: failed to read response body: %w", resp.StatusCode, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
|
return nil, fmt.Errorf("%d: request failed: %v", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.NewBuffer(body), nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue