update VENDOR_ID, fix examples/mgmt-*, update README

This commit is contained in:
AJ ONeal 2020-07-20 09:27:31 -06:00
джерело 360f800a30
коміт b08f1d7e88
13 змінених файлів з 284 додано та 76 видалено

1
.gitignore сторонній

@ -16,6 +16,7 @@ telebit-client-macos
telebit-client-windows-debug.exe
telebit-client-windows.exe
/cmd/machineid/machineid
/cmd/dnsclient/dnsclient
/cmd/sqlstore/sqlstore
/cmd/wsserve/wsserve

106
README.md

@ -9,10 +9,14 @@ Because friends don't let friends localhost.
Installs Go to `~/.local/opt/go` for MacOS and Linux:
```bash
curl https://webinstall.dev/golang | bash
curl -fsS https://webinstall.dev/golang | bash
```
For Windows, see https://golang.org/dl
Windows 10:
```bash
curl.exe -fsSA "MS" https://webinstall.dev/golang | powershell
```
**Note**: The _minimum required go version_ is shown in `go.mod`. DO NOT use with `GOPATH`!
@ -23,12 +27,15 @@ All dependencies are included, at the correct version in the `./vendor` director
```bash
go generate ./...
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o telebit-relay-linux ./cmd/telebit-relay/*.go
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o telebit-relay-macos ./cmd/telebit-relay/*.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o telebit-relay-windows-debug.exe ./cmd/telebit-relay/*.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o telebit-relay-windows.exe ./cmd/telebit-relay/*.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o telebit-relay-linux ./cmd/telebit/*.go
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o telebit-relay-macos ./cmd/telebit/*.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o telebit-relay-windows-debug.exe ./cmd/telebit/*.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o telebit-relay-windows.exe ./cmd/telebit/*.go
```
The binary can be built with `VENDOR_ID` and `CLIENT_SECRET` built into the binary.
See `examples/run-as-client.sh`.
### Configure
Command-line flags or `.env` may be used.
@ -37,17 +44,45 @@ See `./telebit-relay --help` for all options, and `examples/relay.env` for their
### Example
Copy `examples/relay.env` as `.env` in the working directory.
```bash
./telebit-relay --acme-agree=true --auth-url=http://localhost:3010/api
# For Tunnel Relay Server
API_HOSTNAME=devices.example.com
LISTEN=:80,:443
LOCALS=https:mgmt.devices.example.com:3010
VERBOSE=true
# For Device Management & Authentication
AUTH_URL=http://localhost:3010/api
# For Let's Encrypt / ACME registration
ACME_AGREE=true
ACME_EMAIL=letsencrypt@example.com
# For Let's Encrypt / ACME challenges
ACME_RELAY_URL=http://localhost:3010/api/dns
SECRET=xxxxxxxxxxxxxxxx
GODADDY_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
GODADDY_API_SECRET=xxxxxxxxxxxxxxxxxxxxxx
```
Copy `examples/relay.env` as `.env` in the working directory.
Note: It is not necessary to specify the `--flags` when using the ENVs.
```bash
./telebit-relay \
--api-hostname $API_HOSTNAME \
--auth-url "$AUTH_URL" \
--acme-agree "$ACME_AGREE" \
--acme-email "$ACME_EMAIL" \
--acme-relay-url "$ACME_RELAY_URL" \
--secret "$SECRET" \
--listen "$LISTEN"
```
## Management Server
```bash
pushd mplexy/
go generate ./...
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o mgmt-server-linux ./cmd/mgmt/*.go
@ -70,8 +105,13 @@ Create a token with the same `SECRET` used with the `mgmt` server,
and add a device by its `subdomain`.
```bash
VERDOR_ID="test-id"
SECRET="xxxxxxxxxxx"
TOKEN=$(go run -mod=vendor cmd/signjwt/*.go $SECRET)
TOKEN=$(go run -mod=vendor cmd/signjwt/*.go \
--vendor-id $VENDOR_ID \
--secret $SECRET \
--machine-id $SECRET
)
```
Authorize a device:
@ -128,8 +168,6 @@ curl -L -X DELETE http://mgmt.example.com:3010/api/devices/${my_subdomain} -H "A
All dependencies are included, at the correct version in the `./vendor` directory.
```bash
pushd mplexy/
go generate ./...
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o telebit-client-linux ./cmd/telebit/*.go
@ -146,13 +184,43 @@ See `./telebit-client --help` for all options, and `examples/client.env` for the
### Example
Copy `examples/client.env` as `.env` in the working directory.
```bash
./telebit-client --acme-agree=true \
--relay wss://devices.example.com \
--app-id test-id --secret ZR2rxYmcKJcmtKgmH9D5Qw \
--acme-relay http://mgmt.example.com:3010/api/dns \
--auth-url http://mgmt.example.com:3010/api \
--locals http://xxx.devices.example.com:8080,https://xxx.devices.example.com:8080
# For Client
VENDOR_ID=test-id
CLIENT_SUBJECT=newieb
CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxx
AUTH_URL="https://mgmt.devices.example.com/api"
TUNNEL_RELAY_URL=wss://devices.example.com/ws
LOCALS=https:newbie.devices.example.com:3000,http:newbie.devices.example.com:3000
#PORT_FORWARDS=3443:3001,8443:3002
# For Debugging
VERBOSE=true
#VERBOSE_BYTES=true
#VERBOSE_RAW=true
# For Let's Encrypt / ACME registration
ACME_AGREE=true
ACME_EMAIL=letsencrypt@example.com
# For Let's Encrypt / ACME challenges
ACME_RELAY_URL="https://mgmt.devices.example.com/api/dns"
```
```bash
./telebit-client \
--auth-url $AUTH_URL \
--vendor-id "$VENDOR_ID" \
--secret "$CLIENT_SECRET" \
--tunnel-relay-url $TUNNEL_RELAY_URL \
--listen "$LISTEN" \
--locals "$LOCALS" \
--acme-agree="$ACME_AGREE" \
--acme-email "$ACME_EMAIL" \
--acme-relay-url $ACME_RELAY_URL \
--verbose=$VERBOSE
```
## Local Web Application

@ -0,0 +1,83 @@
package main
import (
"encoding/base64"
"encoding/hex"
"flag"
"fmt"
"os"
"git.coolaj86.com/coolaj86/go-telebitd/mgmt/authstore"
"github.com/denisbrodbeck/machineid"
_ "github.com/joho/godotenv/autoload"
)
func main() {
var secret, clientSecret, relaySecret string
var raw bool
flag.BoolVar(&raw, "raw", false, "output the raw machine id")
vendorID := flag.String("vendor-id", "", "a unique identifier for a deploy target environment")
flag.StringVar(&secret, "secret", "", "either the remote server or the tunnel relay secret (used for JWT authentication)")
flag.Parse()
if 0 == len(*vendorID) {
*vendorID = os.Getenv("VENDOR_ID")
}
if 0 == len(*vendorID) {
*vendorID = "telebit.io"
}
if 0 == len(secret) {
clientSecret = os.Getenv("CLIENT_SECRET")
relaySecret = os.Getenv("RELAY_SECRET")
if 0 == len(relaySecret) {
relaySecret = os.Getenv("SECRET")
}
}
if 0 == len(secret) {
secret = clientSecret
}
if 0 == len(secret) {
secret = relaySecret
}
if 0 == len(secret) && 0 == len(clientSecret) && 0 == len(relaySecret) {
fmt.Fprintf(os.Stderr, "See usage: machineid --help\n")
os.Exit(1)
return
} else if 0 != len(clientSecret) && 0 != len(relaySecret) {
fmt.Fprintf(os.Stderr, "Use only one of $SECRET or --relay-secret or --client-secret\n")
os.Exit(1)
return
}
if raw {
rawID, err := machineid.ID()
if nil != err {
fmt.Fprintf(os.Stderr, "Error: %q", err)
os.Exit(1)
return
}
fmt.Println("Raw Machine ID:", rawID)
}
fmt.Println("Vendor ID:", *vendorID)
fmt.Println("Secret:", secret)
var ppid string
muid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", *vendorID, secret))
//muid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", VendorID, ClientSecret))
if nil != err {
fmt.Fprintf(os.Stderr, "unauthorized device: %s\n", err)
os.Exit(1)
return
}
muidBytes, _ := hex.DecodeString(muid)
ppid = base64.RawURLEncoding.EncodeToString(muidBytes)
fmt.Println("PPID:", ppid)
pub := authstore.ToPublicKeyString(ppid)
fmt.Println("Pub:", pub)
}

@ -1,6 +1,8 @@
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"flag"
@ -17,9 +19,11 @@ import (
func main() {
var secret, clientSecret, relaySecret string
appID := flag.String("app-id", "", "a unique identifier for a deploy target environment")
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")
authURL := flag.String("auth-url", "", "the base url for authentication, if not the same as the tunnel relay")
machinePPID := flag.Bool("machine-ppid", false, "just print the machine ppid, not the token")
getMachinePPID := flag.Bool("machine-ppid-only", false, "just print the machine ppid, not the token")
flag.StringVar(&secret, "secret", "", "either the remote server or the tunnel relay secret (used for JWT authentication)")
flag.Parse()
@ -27,14 +31,11 @@ func main() {
*authURL = os.Getenv("AUTH_URL")
}
if 0 == len(*appID) {
*appID = os.Getenv("APP_ID")
if 0 == len(*vendorID) {
*vendorID = os.Getenv("VENDOR_ID")
}
if 0 == len(*appID) {
*appID = os.Getenv("CLIENT_ID")
}
if 0 == len(*appID) {
*appID = "telebit.io"
if 0 == len(*vendorID) {
*vendorID = "telebit.io"
}
if 0 == len(secret) {
@ -61,22 +62,30 @@ func main() {
return
}
var ppid string
muid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", *appID, secret))
//muid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", ClientID, ClientSecret))
if nil != err {
fmt.Fprintf(os.Stderr, "unauthorized device: %s\n", err)
os.Exit(1)
return
ppid := *machinePPID
if 0 == len(ppid) {
appID := fmt.Sprintf("%s|%s", *vendorID, secret)
var muid string
var err error
if 0 == len(*machineID) {
muid, err = machineid.ProtectedID(appID)
if nil != err {
fmt.Fprintf(os.Stderr, "unauthorized device: %s\n", err)
os.Exit(1)
return
}
} else {
muid = ProtectMachineID(appID, *machineID)
}
muidBytes, _ := hex.DecodeString(muid)
ppid = base64.RawURLEncoding.EncodeToString(muidBytes)
}
muidBytes, _ := hex.DecodeString(muid)
ppid = base64.RawURLEncoding.EncodeToString(muidBytes)
fmt.Fprintf(os.Stderr, "[debug] appID = %s\n", *appID)
fmt.Fprintf(os.Stderr, "[debug] vendorID = %s\n", *vendorID)
fmt.Fprintf(os.Stderr, "[debug] secret = %s\n", secret)
pub := authstore.ToPublicKeyString(ppid)
if *machinePPID {
if *getMachinePPID {
fmt.Fprintf(os.Stderr, "[debug]: <ppid> <pub>\n")
fmt.Fprintf(
os.Stdout,
@ -110,3 +119,9 @@ func main() {
fmt.Fprintf(os.Stderr, "%+v\n", grants)
}
}
func ProtectMachineID(appID, machineID string) string {
mac := hmac.New(sha256.New, []byte(machineID))
mac.Write([]byte(appID))
return hex.EncodeToString(mac.Sum(nil))
}

@ -55,8 +55,8 @@ var authorizer telebit.Authorizer
var isHostname = regexp.MustCompile(`^[A-Za-z0-9_\.\-]+$`).MatchString
// ClientID may be baked in, or supplied via ENVs or --args
var ClientID string
// VendorID may be baked in, or supplied via ENVs or --args
var VendorID string
// ClientSecret may be baked in, or supplied via ENVs or --args
var ClientSecret string
@ -67,7 +67,7 @@ func main() {
var portForwards []Forward
// TODO replace the websocket connection with a mock server
appID := flag.String("app-id", "", "a unique identifier for a deploy target environment")
vendorID := flag.String("vendor-id", "", "a unique identifier for a deploy target environment")
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)")
@ -163,19 +163,19 @@ func main() {
}
// Baked-in takes precedence
if 0 == len(ClientID) {
ClientID = *appID
} else if 0 != len(*appID) {
if ClientID != *appID {
fmt.Fprintf(os.Stderr, "invalid --app-id\n")
if 0 == len(VendorID) {
VendorID = *vendorID
} else if 0 != len(*vendorID) {
if VendorID != *vendorID {
fmt.Fprintf(os.Stderr, "invalid --vendor-id\n")
os.Exit(1)
}
}
if 0 == len(ClientID) {
ClientID = os.Getenv("APP_ID")
} else if 0 != len(os.Getenv("APP_ID")) {
if ClientID != os.Getenv("APP_ID") {
fmt.Fprintf(os.Stderr, "invalid APP_ID\n")
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")
os.Exit(1)
}
}
@ -195,7 +195,7 @@ func main() {
os.Exit(1)
}
}
ppid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", ClientID, ClientSecret))
ppid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", VendorID, ClientSecret))
if nil != err {
fmt.Fprintf(os.Stderr, "unauthorized device\n")
os.Exit(1)
@ -210,7 +210,7 @@ func main() {
if 0 == len(*token) {
*token, err = authstore.HMACToken(ppid)
if dbg.Debug {
fmt.Printf("[debug] app_id: %q\n", ClientID)
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")

@ -24,23 +24,23 @@ var authorizer telebit.Authorizer
func main() {
// TODO replace the websocket connection with a mock server
appID := flag.String("app-id", "", "a unique identifier for a deploy target environment")
vendorID := flag.String("vendor-id", "", "a unique identifier for a deploy target environment")
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")
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)")
flag.Parse()
if 0 == len(*appID) {
*appID = os.Getenv("APP_ID")
if 0 == len(*vendorID) {
*vendorID = os.Getenv("VENDOR_ID")
}
if 0 == len(*appID) {
*appID = "telebit.io"
if 0 == len(*vendorID) {
*vendorID = "telebit.io"
}
if 0 == len(*secret) {
*secret = os.Getenv("CLIENT_SECRET")
}
ppid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", *appID, *secret))
ppid, err := machineid.ProtectedID(fmt.Sprintf("%s|%s", *vendorID, *secret))
if nil != err {
fmt.Fprintf(os.Stderr, "unauthorized device\n")
os.Exit(1)
@ -48,7 +48,7 @@ func main() {
}
ppidBytes, err := hex.DecodeString(ppid)
ppid = base64.RawURLEncoding.EncodeToString(ppidBytes)
fmt.Println("[debug] app-id, secret, ppid", *appID, *secret, ppid)
fmt.Println("[debug] vendor-id, secret, ppid", *vendorID, *secret, ppid)
if 0 == len(*token) {
*token, err = authstore.HMACToken(ppid)
if nil != err {

@ -0,0 +1,37 @@
#!/bin/bash
set -e
set -u
source .env
AUTH_URL="${AUTH_URL:-"http://localhost:3000/api"}"
# 1. (srv) create a new shared key for a given slug
# 2. (dev) try to update via ping
# 3. (dev) use key to exchange machine id
# 4. (dev) use key to connect to remote
# 5. (dev) ping occasionally
echo "RELAY_SECRET: $RELAY_SECRET"
TOKEN=$(go run cmd/signjwt/*.go \
--vendor-id "$VENDOR_ID" \
--secret "$RELAY_SECRET" \
--machine-ppid "$RELAY_SECRET"
)
echo "TOKEN 2: '$TOKEN'"
my_parts=$(go run cmd/signjwt/*.go \
--vendor-id "$VENDOR_ID" \
--secret $RELAY_SECRET \
--machine-ppid "$RELAY_SECRET" \
--machine-ppid-only
)
my_ppid=$(echo $my_parts | cut -d' ' -f1)
my_keyid=$(echo $my_parts | cut -d' ' -f2)
echo "PPID: $my_ppid KeyID: $my_keyid"
echo "Auth URL: $AUTH_URL"
curl -X POST "$AUTH_URL/ping" -H "Authorization: Bearer ${TOKEN}"
echo ""
curl "$AUTH_URL/inspect" -H "Authorization: Bearer ${TOKEN}"
echo ""

@ -13,13 +13,16 @@ AUTH_URL="${AUTH_URL:-"http://localhost:3000/api"}"
# 5. (dev) ping occasionally
echo "CLIENT_SECRET: $CLIENT_SECRET"
TOKEN=$(go run cmd/signjwt/*.go --app-id "$APP_ID" --secret "$CLIENT_SECRET")
echo "TOKEN 1: $TOKEN"
TOKEN=$(go run cmd/signjwt/*.go --vendor-id "$VENDOR_ID" --secret "$CLIENT_SECRET")
echo "TOKEN 1: '$TOKEN'"
my_parts=$(go run cmd/signjwt/*.go --secret $CLIENT_SECRET --machine-ppid)
my_parts=$(go run cmd/signjwt/*.go --vendor-id "$VENDOR_ID" --secret $CLIENT_SECRET --machine-ppid-only)
my_ppid=$(echo $my_parts | cut -d' ' -f1)
my_keyid=$(echo $my_parts | cut -d' ' -f2)
echo "PPID: $my_ppid KeyID: $my_keyid"
curl -X POST "$AUTH_URL/ping" -H "Authorization: Bearer ${TOKEN}"
echo "$AUTH_URL"
curl -X POST "$AUTH_URL/ping" -H "Authorization: Bearer ${TOKEN}"
echo ""
curl "$AUTH_URL/inspect" -H "Authorization: Bearer ${TOKEN}"
echo ""

@ -1,7 +1,11 @@
# For Tunnel Relay Service
VERBOSE=true
API_HOSTNAME=devices.example.com
LISTEN=":80 :443"
# To proxy incoming requests for 'https://mgmt.devices.example.com' to localhost:3010
LOCALS=https:mgmt.devices.example.com:3010
# For Device Management & Authorization Server
AUTH_URL=http://localhost:4200/api

@ -6,17 +6,17 @@ set -u
source .env
#go generate -mod=vendor ./...
CLIENT_ID="${CLIENT_ID:-"${APP_ID:-"test-id"}"}"
VENDOR_ID="${VENDOR_ID:-"${VENDOR_ID:-"test-id"}"}"
CLIENT_SECRET="${CLIENT_SECRET:-}"
go build -mod=vendor -o ./telebit \
-ldflags="-X 'main.ClientID=$CLIENT_ID' -X 'main.ClientSecret=$CLIENT_SECRET'" \
-ldflags="-X 'main.VendorID=$VENDOR_ID' -X 'main.ClientSecret=$CLIENT_SECRET'" \
cmd/telebit/*.go
#go build -mod=vendor -o telebit \
# cmd/telebit/*.go
# For Device Authorization across services
AUTH_URL=${AUTH_URL:-"https://devices.examples.com/api"}
APP_ID="$CLIENT_ID"
VENDOR_ID="$VENDOR_ID"
SECRET="${CLIENT_SECRET:-"xxxxxxxxxxxxxxxx"}"
#CLIENT_SECRET=${CLIENT_SECRET:-"yyyyyyyyyyyyyyyy"}
LOCALS="${LOCALS:-"https:newbie.devices.examples.com:3000,http:newbie.devices.examples.com:3000"}"
@ -39,7 +39,7 @@ VERBOSE_RAW=${VERBOSE_RAW:-}
./telebit \
--auth-url $AUTH_URL \
--app-id "$APP_ID" \
--vendor-id "$VENDOR_ID" \
--secret "$CLIENT_SECRET" \
--tunnel-relay-url $TUNNEL_RELAY_URL \
--listen "$LISTEN" \

@ -6,6 +6,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"os"
"time"
"git.coolaj86.com/coolaj86/go-telebitd/dbg"
@ -53,7 +54,7 @@ func ToPublicKeyString(secret string) string {
func HMACToken(secret string) (token string, err error) {
keyID := ToPublicKeyString(secret)
if dbg.Debug {
fmt.Printf("[debug] keyID=%s\n", keyID)
fmt.Fprintf(os.Stderr, "[debug] keyID=%s\n", keyID)
}
b := make([]byte, 16)

@ -2,9 +2,7 @@ package authstore
import (
"context"
"crypto/sha256"
"database/sql"
"encoding/base64"
"fmt"
"io/ioutil"
"time"
@ -55,9 +53,7 @@ func (s *PGStore) SetMaster(secret string) error {
ctx, done := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
defer done()
pubBytes := sha256.Sum256([]byte(secret))
pub := base64.RawURLEncoding.EncodeToString(pubBytes[:])
pub = pub[:24]
pub := ToPublicKeyString(secret)
auth := &Authorization{
Slug: "*",
SharedKey: secret,

@ -343,7 +343,7 @@ func Inspect(authURL, token string) (*Grants, error) {
return nil, err
}
if "" == grants.Subject {
fmt.Println("TODO update mgmt server to show Subject:", msg)
fmt.Fprintf(os.Stderr, "TODO update mgmt server to show Subject: %q", msg)
grants.Subject = strings.Split(grants.Domains[0], ".")[0]
}
return grants, nil