137 lines
4.2 KiB
Go
137 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/nats-io/jwt/v2"
|
|
"github.com/nats-io/nkeys"
|
|
)
|
|
|
|
// Keys is what the driver on machine A needs to act as the operator, the identity seat and a system user.
|
|
type Keys struct {
|
|
Op, Sys, Acc, Sig string // seeds
|
|
A, B, C string // addresses
|
|
}
|
|
|
|
func must[T any](v T, err error) T {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func pubOf(kp nkeys.KeyPair) string { return must(kp.PublicKey()) }
|
|
|
|
func fromSeed(s string) nkeys.KeyPair { return must(nkeys.FromSeed([]byte(s))) }
|
|
|
|
func seedOf(kp nkeys.KeyPair) string { return string(must(kp.Seed())) }
|
|
|
|
// accountJWT is the MESH account: JetStream limits, one signing key, a revocation list.
|
|
func accountJWT(op, acc, sig nkeys.KeyPair, revoked map[string]time.Time) string {
|
|
ac := jwt.NewAccountClaims(pubOf(acc))
|
|
ac.Name = "MESH"
|
|
ac.Limits.JetStreamLimits = jwt.JetStreamLimits{MemoryStorage: 64 << 20, DiskStorage: 1 << 30, Streams: 20, Consumer: 50}
|
|
ac.SigningKeys.Add(pubOf(sig))
|
|
if len(revoked) > 0 {
|
|
ac.Revocations = jwt.RevocationList{}
|
|
for k, t := range revoked {
|
|
ac.Revocations.Revoke(k, t)
|
|
}
|
|
}
|
|
return must(ac.Encode(op))
|
|
}
|
|
|
|
// userJWT: a user of the MESH account signed by the account's signing key, allowed everything.
|
|
func userJWT(acc, sig, ukp nkeys.KeyPair, name string) string {
|
|
uc := jwt.NewUserClaims(pubOf(ukp))
|
|
uc.Name = name
|
|
uc.IssuerAccount = pubOf(acc)
|
|
uc.Pub.Allow.Add(">")
|
|
uc.Sub.Allow.Add(">")
|
|
return must(uc.Encode(sig))
|
|
}
|
|
|
|
func creds(token string, ukp nkeys.KeyPair) []byte {
|
|
return must(jwt.FormatUserConfig(token, []byte(seedOf(ukp))))
|
|
}
|
|
|
|
func gen(args []string) {
|
|
fs := flag.NewFlagSet("gen", flag.ExitOnError)
|
|
out := fs.String("out", "", "output directory")
|
|
a := fs.String("a", "", "address of A")
|
|
b := fs.String("b", "", "address of B")
|
|
c := fs.String("c", "", "address of C")
|
|
fs.Parse(args)
|
|
os.MkdirAll(*out, 0o755)
|
|
op := must(nkeys.CreateOperator())
|
|
sys := must(nkeys.CreateAccount())
|
|
acc := must(nkeys.CreateAccount())
|
|
sig := must(nkeys.CreateAccount())
|
|
oc := jwt.NewOperatorClaims(pubOf(op))
|
|
oc.Name = "op"
|
|
oc.SystemAccount = pubOf(sys)
|
|
opJWT := must(oc.Encode(op))
|
|
sc := jwt.NewAccountClaims(pubOf(sys))
|
|
sc.Name = "SYS"
|
|
sysJWT := must(sc.Encode(op))
|
|
accJ := accountJWT(op, acc, sig, nil)
|
|
|
|
keys := Keys{seedOf(op), seedOf(sys), seedOf(acc), seedOf(sig), *a, *b, *c}
|
|
kj, _ := json.MarshalIndent(keys, "", " ")
|
|
w := func(name string, data []byte) { must(0, os.WriteFile(filepath.Join(*out, name), data, 0o600)) }
|
|
w("keys.json", kj)
|
|
w("op.jwt", []byte(opJWT))
|
|
|
|
// the legacy server joins as a leaf with one user in each of the two accounts
|
|
lk := must(nkeys.CreateUser())
|
|
w("leaf-mesh.creds", creds(userJWT(acc, sig, lk, "legacy-leaf"), lk))
|
|
sk := must(nkeys.CreateUser())
|
|
suc := jwt.NewUserClaims(pubOf(sk))
|
|
suc.Name = "legacy-leaf-sys"
|
|
w("leaf-sys.creds", creds(must(suc.Encode(sys)), sk))
|
|
|
|
common := func(name, domain string) string {
|
|
return fmt.Sprintf(`listen: 0.0.0.0:4222
|
|
server_name: %s
|
|
http_port: 8222
|
|
ping_interval: "2s"
|
|
ping_max: 2
|
|
log_file: "/var/log/q8-%s.log"
|
|
jetstream: {store_dir: "/var/lib/q8/%s/js", domain: %s}
|
|
`, name, name, name, domain)
|
|
}
|
|
hub := func(name, peer string) string {
|
|
return common(name, "hub") + fmt.Sprintf(`operator: "/root/q8/op.jwt"
|
|
system_account: %s
|
|
resolver: {type: full, dir: "/var/lib/q8/%s/accts", allow_delete: false}
|
|
resolver_preload: {%s: %q, %s: %q}
|
|
cluster { name: HUB, listen: 0.0.0.0:6222, routes: ["nats-route://%s:6222"] }
|
|
leafnodes { listen: 0.0.0.0:7422 }
|
|
`, pubOf(sys), name, pubOf(sys), sysJWT, pubOf(acc), accJ, peer)
|
|
}
|
|
w("B.conf", []byte(hub("B", *c)))
|
|
w("C.conf", []byte(hub("C", *b)))
|
|
w("A.conf", []byte(common("A", "legacy")+fmt.Sprintf(`accounts {
|
|
MESH { jetstream: enabled
|
|
users: [ {user: w, password: pw} ] }
|
|
SYS { users: [ {user: sys, password: sys} ] }
|
|
}
|
|
system_account: SYS
|
|
leafnodes { remotes [
|
|
{url: "nats-leaf://%s:7422", account: MESH, credentials: "/root/q8/leaf-mesh.creds"},
|
|
{url: "nats-leaf://%s:7422", account: SYS, credentials: "/root/q8/leaf-sys.creds"}
|
|
] }
|
|
`, *b, *b)))
|
|
}
|
|
|
|
func userClaimsFor(sys, ukp nkeys.KeyPair) string {
|
|
uc := jwt.NewUserClaims(pubOf(ukp))
|
|
uc.Name = "sys"
|
|
return must(uc.Encode(sys))
|
|
}
|