refactor cmds for goreleaser
This commit is contained in:
parent
750f339f58
commit
ade7f0ba80
|
@ -0,0 +1,48 @@
|
|||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod download
|
||||
# you may remove this if you don't need go generate
|
||||
- go generate ./...
|
||||
builds:
|
||||
- id: telebit-client
|
||||
main: ./cmd/telebit/telebit.go
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- arm
|
||||
- id: telebit-mgmt
|
||||
main: ./cmd/mgmt/mgmt.go
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- arm
|
||||
archives:
|
||||
- replacements:
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
arm64: aarch64
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
352
README.md
352
README.md
|
@ -1,9 +1,45 @@
|
|||
# Telebit
|
||||
|
||||
| **Telebit Client** | [Telebit Relay](/tree/master/cmd/telebit) | [Telebit Mgmt](/tree/master/cmd/mgmt) |
|
||||
|
||||
A secure, end-to-end Encrypted tunnel.
|
||||
|
||||
Because friends don't let friends localhost.
|
||||
|
||||
# Usage
|
||||
|
||||
```bash
|
||||
telebit --env ./.env --verbose
|
||||
```
|
||||
|
||||
Command-line flags or `.env` may be used.
|
||||
|
||||
```bash
|
||||
# --acme-agree
|
||||
export ACME_AGREE=true
|
||||
# --acme-email
|
||||
export ACME_EMAIL=johndoe@example.com
|
||||
# --vendor-id
|
||||
export VENDOR_ID=example.com
|
||||
# --secret
|
||||
export SECRET=QQgPyfzVdxJTcUc1ceot3pgJFKtWSHMQ
|
||||
# --tunnel-relay
|
||||
export TUNNEL_RELAY_URL=https://tunnel.example.com/
|
||||
# --tls-locals
|
||||
export TLS_LOCALS=https:*:3000
|
||||
```
|
||||
|
||||
See `./telebit --help` for all options. \
|
||||
See [`examples/client.env`][client-env] for detail explanations.
|
||||
|
||||
[client-env]: /tree/master/examples/client.env
|
||||
|
||||
# Build
|
||||
|
||||
```bash
|
||||
goreleaser --rm-dist --skip-publish
|
||||
```
|
||||
|
||||
## Install Go
|
||||
|
||||
Installs Go to `~/.local/opt/go` for MacOS and Linux:
|
||||
|
@ -37,239 +73,6 @@ The binary can be built with `VENDOR_ID` and `CLIENT_SECRET` built into the bina
|
|||
You can also change the `serviceName` and `serviceDescription` at build time.
|
||||
See `examples/run-as-client.sh`.
|
||||
|
||||
### Configure
|
||||
|
||||
Command-line flags or `.env` may be used.
|
||||
|
||||
See `./telebit --help` for all options, and `examples/relay.env` for their corresponding ENVs.
|
||||
|
||||
### Example
|
||||
|
||||
Copy `examples/relay.env` as `.env` in the working directory.
|
||||
|
||||
```bash
|
||||
# For Tunnel Relay Server
|
||||
API_HOSTNAME=devices.example.com
|
||||
LISTEN=:443
|
||||
LOCALS=https:mgmt.devices.example.com:3010
|
||||
VERBOSE=false
|
||||
|
||||
# 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_HTTP_01_RELAY_URL=http://localhost:3010/api/http
|
||||
ACME_RELAY_URL=http://localhost:3010/api/dns
|
||||
SECRET=xxxxxxxxxxxxxxxx
|
||||
GODADDY_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
GODADDY_API_SECRET=xxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
Note: It is not necessary to specify the `--flags` when using the ENVs.
|
||||
|
||||
```bash
|
||||
./telebit \
|
||||
--api-hostname $API_HOSTNAME \
|
||||
--auth-url "$AUTH_URL" \
|
||||
--acme-agree "$ACME_AGREE" \
|
||||
--acme-email "$ACME_EMAIL" \
|
||||
--secret "$SECRET" \
|
||||
--listen "$LISTEN"
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
List all connected devices
|
||||
|
||||
```bash
|
||||
bash examples/admin-list-devices.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -L https://devices.example.com/api/subscribers -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"subscribers": [{ "since": "2020-07-22T08:20:40Z", "sub": "ruby", "sockets": ["73.228.72.97:50737"], "clients": 0 }]
|
||||
}
|
||||
```
|
||||
|
||||
Show connectivity, of a single device, if any
|
||||
|
||||
```bash
|
||||
curl -L https://devices.example.com/api/subscribers -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"subscribers": [{ "since": "2020-07-22T08:20:40Z", "sub": "ruby", "sockets": ["73.228.72.97:50737"], "clients": 0 }]
|
||||
}
|
||||
```
|
||||
|
||||
Force a device to disconnect:
|
||||
|
||||
```bash
|
||||
bash examples/admin-disconnect-device.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
my_subdomain="ruby"
|
||||
curl -X DELETE http://mgmt.example.com:3010/api/subscribers/ruby" -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{ "success": true }
|
||||
```
|
||||
|
||||
## Management Server
|
||||
|
||||
```bash
|
||||
go generate ./...
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o mgmt-server-linux ./cmd/mgmt/*.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o mgmt-server-macos ./cmd/mgmt/*.go
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o mgmt-server-windows-debug.exe ./cmd/mgmt/*.go
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o mgmt-server-windows.exe ./cmd/mgmt/*.go
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
./telebit-mgmt --domain devices.example.com --port 3010
|
||||
```
|
||||
|
||||
Copy `examples/mgmt.env` as `.env` in the working directory.
|
||||
|
||||
### Device Management API
|
||||
|
||||
Create a token with the same `SECRET` used with the `mgmt` server,
|
||||
and add a device by its `subdomain`.
|
||||
|
||||
To build `signjwt`:
|
||||
|
||||
```bash
|
||||
go build -mod=vendor -ldflags "-s -w" -o signjwt cmd/signjwt/*.go
|
||||
```
|
||||
|
||||
To generate an `admin` token:
|
||||
|
||||
```bash
|
||||
VENDOR_ID="test-id"
|
||||
SECRET="xxxxxxxxxxx"
|
||||
TOKEN=$(./signjwt \
|
||||
--expires-in 15m \
|
||||
--vendor-id $VENDOR_ID \
|
||||
--secret $SECRET \
|
||||
--machine-ppid $SECRET
|
||||
)
|
||||
```
|
||||
|
||||
Authorize a device:
|
||||
|
||||
```bash
|
||||
my_subdomain="xxxx"
|
||||
my_mgmt_host=http://mgmt.example.com:3010
|
||||
curl -X POST $my_mgmt_host/api/devices \
|
||||
-H "Authorization: Bearer ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "slug": "'$my_subdomain'" }'
|
||||
```
|
||||
|
||||
```json
|
||||
{ "shared_key": "ZZZZZZZZ" }
|
||||
```
|
||||
|
||||
Show data of a single device
|
||||
|
||||
```bash
|
||||
my_subdomain="xxxx"
|
||||
curl -L http://mgmt.example.com:3010/api/devices/${my_subdomain} -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{ "subdomain": "sub1", "updated_at": "2020-05-20T12:00:01Z" }
|
||||
```
|
||||
|
||||
Get a list of connected devices:
|
||||
|
||||
```bash
|
||||
curl -L http://mgmt.example.com:3010/api/devices -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
[{ "subdomain": "sub1", "updated_at": "2020-05-20T12:00:01Z" }]
|
||||
```
|
||||
|
||||
Get a list of disconnected devices:
|
||||
|
||||
```bash
|
||||
curl -L http://mgmt.example.com:3010/api/devices?inactive=true -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
Deauthorize a device:
|
||||
|
||||
```bash
|
||||
my_subdomain="xxxx"
|
||||
curl -L -X DELETE http://mgmt.example.com:3010/api/devices/${my_subdomain} -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
## Tunnel Client
|
||||
|
||||
The tunnel relay binary is also the client binary.
|
||||
|
||||
You do not need to build a separate client binary.
|
||||
|
||||
### Configure
|
||||
|
||||
Command-line flags or `.env` may be used.
|
||||
|
||||
See `./telebit --help` for all options, and `examples/client.env` for their corresponding ENVs.
|
||||
|
||||
### Example
|
||||
|
||||
Copy `examples/client.env` as `.env` in the working directory.
|
||||
|
||||
```bash
|
||||
# For Client
|
||||
VENDOR_ID=test-id
|
||||
CLIENT_SUBJECT=newieb
|
||||
CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxx
|
||||
AUTH_URL="https://mgmt.devices.example.com/api"
|
||||
TUNNEL_RELAY_URL=https://devices.example.com/
|
||||
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 \
|
||||
--vendor-id "$VENDOR_ID" \
|
||||
--secret "$CLIENT_SECRET" \
|
||||
--tunnel-relay-url $TUNNEL_RELAY_URL \
|
||||
--locals "$LOCALS" \
|
||||
--acme-agree="$ACME_AGREE" \
|
||||
--acme-email "$ACME_EMAIL" \
|
||||
--verbose=$VERBOSE
|
||||
```
|
||||
|
||||
## Local Web Application
|
||||
|
||||
Currently only raw TCP is tunneled.
|
||||
|
@ -288,20 +91,75 @@ EOF
|
|||
python3 -m http.server 3000
|
||||
```
|
||||
|
||||
## Glossary
|
||||
## Help
|
||||
|
||||
```
|
||||
--vendor-id $VENDOR_ID an arbitrary id used as part of authentication
|
||||
--secret $SECRET the secret for creating JWTs
|
||||
--tunnel-relay-url $TUNNEL_RELAY_URL the url of the tunnel server
|
||||
--auth-url $AUTH_URL use to override the server-provided auth url
|
||||
--acme-relay-url $ACME_RELAY_URL use to override the server-provided acme dns 01 proxy
|
||||
--locals $LOCALS a list of `scheme:domainname:port`
|
||||
for forwarding incoming `domainname` to local `port`
|
||||
--port-forwards $PORT_FORWARDS a list of `remote:local` tcp port-forwarding
|
||||
--verbose $VERBOSE logs everything, including abbreviated data (as hex)
|
||||
$VERBOSE_BYTES logs full data (as hex)
|
||||
$VERBOSE_RAW logs full data (as string)
|
||||
--acme-agree $ACME_AGREE agree to the ACME service agreement
|
||||
--acme-email $ACME_EMAIL the webmaster email for ACME notices
|
||||
Usage of telebit:
|
||||
ACME_AGREE
|
||||
--acme-agree
|
||||
agree to the terms of the ACME service provider (required)
|
||||
--acme-directory string
|
||||
ACME Directory URL
|
||||
ACME_EMAIL
|
||||
--acme-email string
|
||||
email to use for Let's Encrypt / ACME registration
|
||||
--acme-http-01
|
||||
enable HTTP-01 ACME challenges
|
||||
ACME_HTTP_01_RELAY_URL
|
||||
--acme-http-01-relay-url string
|
||||
the base url of the ACME HTTP-01 relay, if not the same as the DNS-01 relay
|
||||
--acme-relay-url string
|
||||
the base url of the ACME DNS-01 relay, if not the same as the tunnel relay
|
||||
--acme-staging
|
||||
get fake certificates for testing
|
||||
--acme-storage string
|
||||
path to ACME storage directory (default "./acme.d/")
|
||||
--acme-tls-alpn-01
|
||||
enable TLS-ALPN-01 ACME challenges
|
||||
API_HOSTNAME
|
||||
--api-hostname string
|
||||
the hostname used to manage clients
|
||||
--auth-url string
|
||||
the base url for authentication, if not the same as the tunnel relay
|
||||
DEBUG
|
||||
--debug
|
||||
show debug output (default true)
|
||||
--dns-01-delay duration
|
||||
add an extra delay after dns self-check to allow DNS-01 challenges to propagate
|
||||
--dns-resolvers string
|
||||
a list of resolvers in the format 8.8.8.8:53,8.8.4.4:53
|
||||
--env string
|
||||
path to .env file
|
||||
--leeway duration
|
||||
allow for time drift / skew (hard-coded to 15 minutes) (default 15m0s)
|
||||
LISTEN
|
||||
--listen string
|
||||
list of bind addresses on which to listen, such as localhost:80, or :443
|
||||
LOCALS
|
||||
--locals string
|
||||
a list of <from-domain>:<to-port>
|
||||
PORT_FORWARD
|
||||
--port-forward string
|
||||
a list of <from-port>:<to-port> for raw port-forwarding
|
||||
SECRET
|
||||
--secret string
|
||||
the same secret used by telebit-relay (used for JWT authentication)
|
||||
--spf-domain string
|
||||
domain with SPF-like list of IP addresses which are allowed to connect to clients
|
||||
TLS_LOCALS
|
||||
--tls-locals string
|
||||
like --locals, but TLS will be used to connect to the local port
|
||||
--token string
|
||||
an auth token for the server (instead of generating --secret); use --token=false to ignore any $TOKEN in env
|
||||
TUNNEL_RELAY_URL
|
||||
--tunnel-relay-url string
|
||||
the websocket url at which to connect to the tunnel relay
|
||||
VENDOR_ID
|
||||
--vendor-id string
|
||||
a unique identifier for a deploy target environment
|
||||
VERBOSE
|
||||
VERBOSE_BYTES
|
||||
VERBOSE_RAW
|
||||
--verbose
|
||||
log excessively
|
||||
```
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# MGMT Server
|
||||
# Telebit Mgmt
|
||||
|
||||
# Config
|
||||
|
||||
|
@ -18,6 +18,17 @@ NAMECOM_USERNAME=johndoe
|
|||
NAMECOM_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```bash
|
||||
my_subdomain="ruby"
|
||||
curl -X DELETE http://mgmt.example.com:3010/api/subscribers/ruby" -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{ "success": true }
|
||||
```
|
||||
|
||||
# Build
|
||||
|
||||
```bash
|
||||
|
@ -27,3 +38,95 @@ pushd cmd/mgmt
|
|||
go build -mod vendor -o telebit-mgmt
|
||||
popd
|
||||
```
|
||||
|
||||
## Management Server
|
||||
|
||||
```bash
|
||||
go generate ./...
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod vendor -o mgmt-server-linux ./cmd/mgmt/*.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -mod vendor -o mgmt-server-macos ./cmd/mgmt/*.go
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -o mgmt-server-windows-debug.exe ./cmd/mgmt/*.go
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H windowsgui" -o mgmt-server-windows.exe ./cmd/mgmt/*.go
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
./telebit-mgmt --domain devices.example.com --port 3010
|
||||
```
|
||||
|
||||
Copy `examples/mgmt.env` as `.env` in the working directory.
|
||||
|
||||
### Device Management API
|
||||
|
||||
Create a token with the same `SECRET` used with the `mgmt` server,
|
||||
and add a device by its `subdomain`.
|
||||
|
||||
To build `signjwt`:
|
||||
|
||||
```bash
|
||||
go build -mod=vendor -ldflags "-s -w" -o signjwt cmd/signjwt/*.go
|
||||
```
|
||||
|
||||
To generate an `admin` token:
|
||||
|
||||
```bash
|
||||
VENDOR_ID="test-id"
|
||||
SECRET="xxxxxxxxxxx"
|
||||
TOKEN=$(./signjwt \
|
||||
--expires-in 15m \
|
||||
--vendor-id $VENDOR_ID \
|
||||
--secret $SECRET \
|
||||
--machine-ppid $SECRET
|
||||
)
|
||||
```
|
||||
|
||||
Authorize a device:
|
||||
|
||||
```bash
|
||||
my_subdomain="xxxx"
|
||||
my_mgmt_host=http://mgmt.example.com:3010
|
||||
curl -X POST $my_mgmt_host/api/devices \
|
||||
-H "Authorization: Bearer ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "slug": "'$my_subdomain'" }'
|
||||
```
|
||||
|
||||
```json
|
||||
{ "shared_key": "ZZZZZZZZ" }
|
||||
```
|
||||
|
||||
Show data of a single device
|
||||
|
||||
```bash
|
||||
my_subdomain="xxxx"
|
||||
curl -L http://mgmt.example.com:3010/api/devices/${my_subdomain} -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{ "subdomain": "sub1", "updated_at": "2020-05-20T12:00:01Z" }
|
||||
```
|
||||
|
||||
Get a list of connected devices:
|
||||
|
||||
```bash
|
||||
curl -L http://mgmt.example.com:3010/api/devices -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
[{ "subdomain": "sub1", "updated_at": "2020-05-20T12:00:01Z" }]
|
||||
```
|
||||
|
||||
Get a list of disconnected devices:
|
||||
|
||||
```bash
|
||||
curl -L http://mgmt.example.com:3010/api/devices?inactive=true -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
Deauthorize a device:
|
||||
|
||||
```bash
|
||||
my_subdomain="xxxx"
|
||||
curl -L -X DELETE http://mgmt.example.com:3010/api/devices/${my_subdomain} -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
|
|
@ -10,12 +10,14 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"git.rootprojects.org/root/telebit/mgmt/authstore"
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt"
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt/authstore"
|
||||
|
||||
"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"
|
||||
"github.com/go-acme/lego/v3/providers/dns/namedotcom"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
)
|
||||
|
||||
|
@ -28,14 +30,8 @@ var (
|
|||
GitTimestamp = "0000-00-00T00:00:00+0000"
|
||||
)
|
||||
|
||||
// MWKey is a type guard
|
||||
type MWKey string
|
||||
|
||||
var store authstore.Store
|
||||
var provider challenge.Provider = nil // TODO is this concurrency-safe?
|
||||
var secret string
|
||||
var primaryDomain string
|
||||
var relayDomain string
|
||||
|
||||
func help() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: mgmt --domain <devices.example.com> --secret <128-bit secret>\n")
|
||||
|
@ -59,22 +55,22 @@ func main() {
|
|||
"database (postgres) connection url")
|
||||
flag.StringVar(&secret, "secret", "",
|
||||
"a >= 16-character random string for JWT key signing")
|
||||
flag.StringVar(&primaryDomain, "domain", "",
|
||||
flag.StringVar(&mgmt.DeviceDomain, "domain", "",
|
||||
"the base domain to use for all clients")
|
||||
flag.StringVar(&relayDomain, "tunnel-domain", "",
|
||||
flag.StringVar(&mgmt.RelayDomain, "tunnel-domain", "",
|
||||
"the domain name of the tunnel relay service, if different from base domain")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if 0 == len(primaryDomain) {
|
||||
primaryDomain = os.Getenv("DOMAIN")
|
||||
if 0 == len(mgmt.DeviceDomain) {
|
||||
mgmt.DeviceDomain = os.Getenv("DOMAIN")
|
||||
}
|
||||
|
||||
if 0 == len(relayDomain) {
|
||||
relayDomain = os.Getenv("TUNNEL_DOMAIN")
|
||||
if 0 == len(mgmt.RelayDomain) {
|
||||
mgmt.RelayDomain = os.Getenv("TUNNEL_DOMAIN")
|
||||
}
|
||||
if 0 == len(relayDomain) {
|
||||
relayDomain = primaryDomain
|
||||
if 0 == len(mgmt.RelayDomain) {
|
||||
mgmt.RelayDomain = mgmt.DeviceDomain
|
||||
}
|
||||
|
||||
if 0 == len(dbURL) {
|
||||
|
@ -99,6 +95,8 @@ func main() {
|
|||
lnAddr = "localhost:" + port
|
||||
}
|
||||
|
||||
// TODO are these concurrency-safe?
|
||||
var provider challenge.Provider = nil
|
||||
if len(os.Getenv("GODADDY_API_KEY")) > 0 {
|
||||
id := os.Getenv("GODADDY_API_KEY")
|
||||
apiSecret := os.Getenv("GODADDY_API_SECRET")
|
||||
|
@ -120,7 +118,7 @@ func main() {
|
|||
fmt.Println("DNS-01 relay disabled")
|
||||
}
|
||||
|
||||
if 0 == len(primaryDomain) || 0 == len(secret) || 0 == len(dbURL) {
|
||||
if 0 == len(mgmt.DeviceDomain) || 0 == len(secret) || 0 == len(dbURL) {
|
||||
help()
|
||||
os.Exit(1)
|
||||
return
|
||||
|
@ -134,7 +132,7 @@ func main() {
|
|||
connStr += "?sslmode=required"
|
||||
}
|
||||
|
||||
store, err = authstore.NewStore(connStr, initSQL)
|
||||
store, err = authstore.NewStore(connStr, mgmt.InitSQL)
|
||||
if nil != err {
|
||||
log.Fatal("connection error", err)
|
||||
return
|
||||
|
@ -142,16 +140,18 @@ func main() {
|
|||
_ = store.SetMaster(secret)
|
||||
defer store.Close()
|
||||
|
||||
mgmt.Init(store, provider)
|
||||
|
||||
go func() {
|
||||
fmt.Println("Listening for ACME challenges on :" + challengesPort)
|
||||
if err := http.ListenAndServe(":"+challengesPort, routeStatic()); nil != err {
|
||||
if err := http.ListenAndServe(":"+challengesPort, mgmt.RouteStatic()); nil != err {
|
||||
log.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Println("Listening on", lnAddr)
|
||||
fmt.Fprintf(os.Stderr, "failed: %s", http.ListenAndServe(lnAddr, routeAll()))
|
||||
fmt.Fprintf(os.Stderr, "failed: %s", http.ListenAndServe(lnAddr, mgmt.RouteAll()))
|
||||
}
|
||||
|
||||
// newNameDotComDNSProvider is for the sake of demoing the tunnel
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
package main
|
||||
|
||||
var initSQL = "./postgres.init.sql"
|
|
@ -0,0 +1,79 @@
|
|||
# Telebit Relay
|
||||
|
||||
| [Telebit Client](../../) | **Telebit Relay** | [Telebit Mgmt](../mgmt) |
|
||||
|
||||
### Example
|
||||
|
||||
Copy `examples/relay.env` as `.env` in the working directory.
|
||||
|
||||
```bash
|
||||
# For Tunnel Relay Server
|
||||
API_HOSTNAME=devices.example.com
|
||||
LISTEN=:443
|
||||
LOCALS=https:mgmt.devices.example.com:3010
|
||||
VERBOSE=false
|
||||
|
||||
# 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_HTTP_01_RELAY_URL=http://localhost:3010/api/http
|
||||
ACME_RELAY_URL=http://localhost:3010/api/dns
|
||||
SECRET=xxxxxxxxxxxxxxxx
|
||||
GODADDY_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
GODADDY_API_SECRET=xxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
Note: It is not necessary to specify the `--flags` when using the ENVs.
|
||||
|
||||
```bash
|
||||
./telebit \
|
||||
--api-hostname $API_HOSTNAME \
|
||||
--auth-url "$AUTH_URL" \
|
||||
--acme-agree "$ACME_AGREE" \
|
||||
--acme-email "$ACME_EMAIL" \
|
||||
--secret "$SECRET" \
|
||||
--listen "$LISTEN"
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
List all connected devices
|
||||
|
||||
```bash
|
||||
bash examples/admin-list-devices.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -L https://devices.example.com/api/subscribers -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"subscribers": [{ "since": "2020-07-22T08:20:40Z", "sub": "ruby", "sockets": ["73.228.72.97:50737"], "clients": 0 }]
|
||||
}
|
||||
```
|
||||
|
||||
Show connectivity, of a single device, if any
|
||||
|
||||
```bash
|
||||
curl -L https://devices.example.com/api/subscribers -H "Authorization: Bearer ${TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"subscribers": [{ "since": "2020-07-22T08:20:40Z", "sub": "ruby", "sockets": ["73.228.72.97:50737"], "clients": 0 }]
|
||||
}
|
||||
```
|
||||
|
||||
Force a device to disconnect:
|
||||
|
||||
```bash
|
||||
bash examples/admin-disconnect-device.sh
|
||||
```
|
|
@ -11,6 +11,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
|
@ -22,21 +23,23 @@ import (
|
|||
"git.rootprojects.org/root/telebit/dbg"
|
||||
"git.rootprojects.org/root/telebit/internal/dns01"
|
||||
"git.rootprojects.org/root/telebit/internal/http01"
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt"
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt/authstore"
|
||||
"git.rootprojects.org/root/telebit/internal/service"
|
||||
telebitX "git.rootprojects.org/root/telebit/internal/telebit"
|
||||
"git.rootprojects.org/root/telebit/iplist"
|
||||
"git.rootprojects.org/root/telebit/mgmt"
|
||||
"git.rootprojects.org/root/telebit/mgmt/authstore"
|
||||
"git.rootprojects.org/root/telebit/table"
|
||||
"git.rootprojects.org/root/telebit/tunnel"
|
||||
legoDNS01 "github.com/go-acme/lego/v3/challenge/dns01"
|
||||
|
||||
"github.com/coolaj86/certmagic"
|
||||
"github.com/denisbrodbeck/machineid"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
legoDNS01 "github.com/go-acme/lego/v3/challenge/dns01"
|
||||
"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/namedotcom"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/joho/godotenv"
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
)
|
||||
|
@ -80,8 +83,6 @@ type Forward struct {
|
|||
localTLS bool
|
||||
}
|
||||
|
||||
var authorizer telebit.Authorizer
|
||||
|
||||
var isHostname = regexp.MustCompile(`^[A-Za-z0-9_\.\-]+$`).MatchString
|
||||
|
||||
// VendorID may be baked in, or supplied via ENVs or --args
|
||||
|
@ -383,7 +384,6 @@ func main() {
|
|||
return
|
||||
}
|
||||
fmt.Println("Auth URL", *authURL)
|
||||
authorizer = NewAuthorizer(*authURL)
|
||||
|
||||
dns01Base := directory.DNS01Proxy.URL
|
||||
if 0 == len(*acmeRelay) {
|
||||
|
@ -428,7 +428,6 @@ func main() {
|
|||
fmt.Printf("[Grants]\n\t%#v\n", grants)
|
||||
*relay = grants.Audience
|
||||
}
|
||||
authorizer = NewAuthorizer(*authURL)
|
||||
|
||||
fmt.Printf("Email: %q\n", *email)
|
||||
|
||||
|
@ -590,9 +589,11 @@ func muxAll(
|
|||
}
|
||||
if "" != *apiHostname {
|
||||
// this is a generic net listener
|
||||
InitAdmin(*authURL)
|
||||
r := chi.NewRouter()
|
||||
telebitX.RouteAdmin(*authURL, r)
|
||||
apiListener := tunnel.NewListener()
|
||||
go func() {
|
||||
httpsrv := &http.Server{Handler: r}
|
||||
httpsrv.Serve(apiListener)
|
||||
}()
|
||||
fmt.Printf("Will respond to Websocket and API requests to %q\n", *apiHostname)
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
# VERBOSE
|
||||
# Show more output in the logs
|
||||
#VERBOSE=true
|
||||
|
||||
# DEBUG
|
||||
# Show binary output in the longs too
|
||||
#DEBUG=true
|
||||
|
||||
# Used for Let's Encrypt registration
|
||||
# ACME_AGREE
|
||||
ACME_AGREE=true
|
||||
# ACME_EMAIL
|
||||
ACME_EMAIL=johndoe@example.com
|
||||
|
||||
# TUNNEL_RELAY_URL
|
||||
# The URL of the Telebit Relay, of course.
|
||||
# Note that many client configuration details can be preassigned at
|
||||
|
@ -9,23 +23,53 @@ TUNNEL_RELAY_URL=https://devices.example.com/
|
|||
# It's just as well to generate a random ID for your organization.
|
||||
VENDOR_ID=
|
||||
|
||||
# Used for Let's Encrypt registration
|
||||
# ACME_AGREE
|
||||
ACME_AGREE=true
|
||||
# ACME_EMAIL
|
||||
ACME_EMAIL=johndoe@example.com
|
||||
# SECRET
|
||||
# This is the shared secret between the client device
|
||||
# and the device management server.
|
||||
SECRET=shared-secret
|
||||
|
||||
# CLIENT_SUBJECT (optional)
|
||||
# NOT used by Telebit.
|
||||
# This is for the Device Management & Authentication server.
|
||||
# This is for the example scripts
|
||||
# (for the Device Management & Authentication server)
|
||||
CLIENT_SUBJECT=newbie
|
||||
CLIENT_SECRET=xxxxxxxxxxxxxxxx
|
||||
LOCALS=https:$CLIENT_SUBJECT.devices.example.com:3000,https:*.$CLIENT_SUBJECT.devices.example.com:3000
|
||||
#ACME_HTTP_01_RELAY_URL=http://localhost:4200/api/http
|
||||
#PORT_FORWARDS=3443:3001,8443:3002
|
||||
#DUCKDNS_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
|
||||
# TLS_LOCALS
|
||||
# ReverseProxy any matching requests to the given local port.
|
||||
# This DOES NOT terminate TLS
|
||||
TLS_LOCALS=https:*:3000
|
||||
|
||||
# LOCALS
|
||||
# ReverseProxy any matching requests to the given local port.
|
||||
# This terminates TLS
|
||||
# Ex: LOCALS=https:$CLIENT_SUBJECT.devices.example.com:3000,https:*.$CLIENT_SUBJECT.devices.example.com:3000
|
||||
LOCALS=https:*:3000
|
||||
|
||||
# PORT_FORWARDS
|
||||
# ReverseProxy any matching TCP streams from the given remote incoming port,
|
||||
# directly to the given destination port.
|
||||
PORT_FORWARDS=3443:3001,8443:3002
|
||||
|
||||
# AUTH_URL
|
||||
# The client may receive this as `.authn.url`
|
||||
# through `https://$API_DOMAIN/.well-known/telebit.app/index.json`
|
||||
# Setting the ENV AUTH_URL or the flag --auth-url overrides the discovery endpoint
|
||||
|
||||
# ACME_HTTP_01_RELAY_URL
|
||||
# Overrides `.acme_http_01_relay.url`
|
||||
# from `https://$API_DOMAIN/.well-known/telebit.app/index.json`
|
||||
ACME_HTTP_01_RELAY_URL=https://tunnel.example.com/api/acme-relay
|
||||
|
||||
# ACME_RELAY_URL (deprecated)
|
||||
# Overrides `.acme_dns_01_relay.url`
|
||||
# from `https://$API_DOMAIN/.well-known/telebit.app/index.json`
|
||||
#ACME_RELAY_URL=https://tunnel.example.com/api/acme-relay
|
||||
|
||||
# ACME DNS-01 Challenge Strategies
|
||||
# Rather than use the http-01 or dns-01 relay you can set one of these
|
||||
#DUCKDNS_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
#NAMECOM_USERNAME=
|
||||
#NAMECOM_API_TOKEN=
|
||||
#GODADDY_API_KEY=
|
||||
#GODADDY_API_SECRET=
|
|
@ -11,8 +11,9 @@ CLIENT_SECRET="${CLIENT_SECRET:-}"
|
|||
#go build -mod=vendor -o ./telebit \
|
||||
# -ldflags="-X 'main.VendorID=$VENDOR_ID' -X 'main.ClientSecret=$CLIENT_SECRET' -X 'main.serviceName=telebit' -X 'main.serviceDesc=securely tunnel through telebit.io'" \
|
||||
# cmd/telebit/*.go
|
||||
go build -mod=vendor -o telebit \
|
||||
cmd/telebit/*.go
|
||||
pushd cmd/telebit
|
||||
go build -mod=vendor -o telebit .
|
||||
popd
|
||||
|
||||
# For Device Authorization across services
|
||||
#AUTH_URL=${AUTH_URL:-"https://devices.examples.com/api"}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package mgmt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
telebit "git.rootprojects.org/root/telebit"
|
||||
"git.rootprojects.org/root/telebit/dbg"
|
||||
"git.rootprojects.org/root/telebit/mgmt/authstore"
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt/authstore"
|
||||
)
|
||||
|
||||
type SuccessResponse struct {
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package mgmt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
@ -10,7 +10,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rootprojects.org/root/telebit/mgmt/authstore"
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt/authstore"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package mgmt
|
||||
|
||||
import (
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt/authstore"
|
||||
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
)
|
||||
|
||||
var store authstore.Store
|
||||
|
||||
var provider challenge.Provider = nil
|
||||
|
||||
// DeviceDomain is the base hostname used for devices, such as devices.example.com
|
||||
// which has devices as foo.devices.example.com
|
||||
var DeviceDomain string
|
||||
|
||||
// RelayDomain is the API hostname used for the tunnel
|
||||
// ( currently NOT used, but will be used for wss://RELAY_DOMAIN/ )
|
||||
var RelayDomain string
|
||||
|
||||
// MWKey is a type guard
|
||||
type MWKey string
|
||||
|
||||
// Init initializes some package variables
|
||||
func Init(s authstore.Store, p challenge.Provider) {
|
||||
store = s
|
||||
provider = p
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package mgmt
|
||||
|
||||
// InitSQL is the filepath to the SQL file used to initialize the database on each start
|
||||
var InitSQL = "./postgres.init.sql"
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package mgmt
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -12,7 +12,8 @@ import (
|
|||
"time"
|
||||
|
||||
"git.rootprojects.org/root/telebit/dbg"
|
||||
"git.rootprojects.org/root/telebit/mgmt/authstore"
|
||||
"git.rootprojects.org/root/telebit/internal/mgmt/authstore"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
|
@ -27,7 +28,7 @@ type MgmtClaims struct {
|
|||
var presenters = make(chan *Challenge)
|
||||
var cleanups = make(chan *Challenge)
|
||||
|
||||
func routeStatic() chi.Router {
|
||||
func RouteStatic() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(middleware.Logger)
|
||||
|
@ -49,7 +50,7 @@ func routeStatic() chi.Router {
|
|||
return r
|
||||
}
|
||||
|
||||
func routeAll() chi.Router {
|
||||
func RouteAll() chi.Router {
|
||||
|
||||
go func() {
|
||||
for {
|
||||
|
@ -125,8 +126,8 @@ func routeAll() chi.Router {
|
|||
return nil, fmt.Errorf("invalid jwt payload 'sub' (mismatch)")
|
||||
}
|
||||
claims.Subject = claims.Slug
|
||||
claims.Issuer = primaryDomain
|
||||
claims.Audience = fmt.Sprintf("wss://%s/ws", relayDomain)
|
||||
claims.Issuer = DeviceDomain
|
||||
claims.Audience = fmt.Sprintf("wss://%s/ws", RelayDomain)
|
||||
|
||||
/*
|
||||
// a little misdirection there
|
||||
|
@ -181,7 +182,7 @@ func routeAll() chi.Router {
|
|||
claims.Subject,
|
||||
claims.Audience,
|
||||
claims.Slug,
|
||||
primaryDomain,
|
||||
DeviceDomain,
|
||||
)))
|
||||
})
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package telebit
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -13,7 +13,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
telebit "git.rootprojects.org/root/telebit"
|
||||
"git.rootprojects.org/root/telebit"
|
||||
"git.rootprojects.org/root/telebit/admin"
|
||||
"git.rootprojects.org/root/telebit/dbg"
|
||||
"git.rootprojects.org/root/telebit/table"
|
||||
|
@ -23,10 +23,11 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var httpsrv *http.Server
|
||||
var authorizer telebit.Authorizer
|
||||
|
||||
func InitAdmin(authURL string) {
|
||||
r := chi.NewRouter()
|
||||
// RouteAdmin sets up the API, including the Mgmt proxy and ACME relay
|
||||
func RouteAdmin(authURL string, r chi.Router) {
|
||||
authorizer = NewAuthorizer(authURL)
|
||||
|
||||
r.Use(middleware.Logger)
|
||||
//r.Use(middleware.Timeout(120 * time.Second))
|
||||
|
@ -113,10 +114,6 @@ func InitAdmin(authURL string) {
|
|||
fmt.Println("Request Path:", r.URL.Path)
|
||||
adminUI.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
httpsrv = &http.Server{
|
||||
Handler: r,
|
||||
}
|
||||
}
|
||||
|
||||
var apiPingContent = []byte("{ \"success\": true, \"error\": \"\" }\n")
|
|
@ -1,11 +1,11 @@
|
|||
package main
|
||||
package telebit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
telebit "git.rootprojects.org/root/telebit"
|
||||
"git.rootprojects.org/root/telebit"
|
||||
)
|
||||
|
||||
func NewAuthorizer(authURL string) telebit.Authorizer {
|
Loading…
Reference in New Issue