From 9f5e624edb5d6d0a3b8aa37a745d2b6c05fa4d35 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Tue, 11 Jun 2024 20:58:32 +0500 Subject: [PATCH 1/4] cmd: Add command to initialize basic router config --- cmd/xconn/config.yaml.in | 37 +++++++++++++++++++++ cmd/xconn/main.go | 69 +++++++++++++++++++++++++++++++++------- go.mod | 9 ++++-- go.sum | 18 ++++++++--- 4 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 cmd/xconn/config.yaml.in diff --git a/cmd/xconn/config.yaml.in b/cmd/xconn/config.yaml.in new file mode 100644 index 0000000..aaf1031 --- /dev/null +++ b/cmd/xconn/config.yaml.in @@ -0,0 +1,37 @@ +version: '1' + +realms: + - name: realm1 + +transports: + - type: websocket + port: 8080 + serializers: + - json + - cbor + - protobuf + +authenticators: + cryptosign: + - authid: john + realm: realm1 + role: anonymous + authorized_keys: + - 20e6ff0eb2552204fac19a15a61da586e437abd64a545bedce61a89b48184fcb + + wampcra: + - authid: john + realm: realm1 + role: anonymous + secret: hello + + ticket: + - authid: john + realm: realm1 + role: anonymous + ticket: hello + + anonymous: + - authid: john + realm: realm1 + role: anonymous diff --git a/cmd/xconn/main.go b/cmd/xconn/main.go index f38dc68..0607348 100644 --- a/cmd/xconn/main.go +++ b/cmd/xconn/main.go @@ -1,24 +1,71 @@ package main import ( + _ "embed" // nolint:gci + "fmt" "log" + "os" - "github.com/xconnio/wampproto-protobuf/go" - "github.com/xconnio/xconn-go" + "github.com/alecthomas/kingpin/v2" ) -func main() { - router := xconn.NewRouter() - router.AddRealm("realm1") +var ( + //go:embed config.yaml.in + sampleConfig []byte +) + +const ( + versionString = "0.1.0" + + ConfigDir = ".xconn" + ConfigFile = ConfigDir + "/config.yaml" +) + +type cmd struct { + parsedCommand string + + init *kingpin.CmdClause +} + +func parseCommand(args []string) (*cmd, error) { + app := kingpin.New(args[0], "XConn") + app.Version(versionString).VersionFlag.Short('v') + + c := &cmd{ + init: app.Command("init", "Initialize sample router config."), + } + + parsedCommand, err := app.Parse(args[1:]) + if err != nil { + return nil, err + } + c.parsedCommand = parsedCommand + + return c, nil +} - server := xconn.NewServer(router, nil) +func Run(args []string) error { + c, err := parseCommand(args) + if err != nil { + return err + } - serializer := &wampprotobuf.ProtobufSerializer{} - protobufSpec := xconn.NewWSSerializerSpec("wamp.2.protobuf", serializer) + switch c.parsedCommand { + case c.init.FullCommand(): + if err := os.MkdirAll(ConfigDir, os.ModePerm); err != nil { + return err + } - if err := server.RegisterSpec(protobufSpec); err != nil { - log.Fatal(err) + if err = os.WriteFile(ConfigFile, sampleConfig, 0600); err != nil { + return fmt.Errorf("unable to write config: %w", err) + } } - log.Fatal(server.Start("0.0.0.0", 8080)) + return nil +} + +func main() { + if err := Run(os.Args); err != nil { + log.Fatalln(err) + } } diff --git a/go.mod b/go.mod index b107ab4..4162356 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,19 @@ module github.com/xconnio/xconn-go go 1.20 require ( + github.com/alecthomas/kingpin/v2 v2.4.0 github.com/gammazero/nexus/v3 v3.2.2 github.com/gammazero/workerpool v1.1.3 github.com/gobwas/ws v1.4.0 github.com/stretchr/testify v1.8.4 - github.com/xconnio/wampproto-go v0.0.0-20240606221544-14a7a1771d92 - github.com/xconnio/wampproto-protobuf/go v0.0.0-20240607222929-52f6a1b22c87 + github.com/xconnio/wampproto-go v0.0.0-20240611083555-4bcae9e441a3 + github.com/xconnio/wampproto-protobuf/go v0.0.0-20240611092706-1e859744b5a2 golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d + gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fxamacker/cbor/v2 v2.6.0 // indirect github.com/gammazero/deque v0.2.0 // indirect @@ -24,8 +27,8 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/sys v0.20.0 // indirect google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 473900c..20d7b79 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,8 @@ +github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= +github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= @@ -19,6 +24,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -29,10 +36,12 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xconnio/wampproto-go v0.0.0-20240606221544-14a7a1771d92 h1:sQGmy7DEnqegkRvLqWNkz+BY7n1DiisfptURHk7Nh30= -github.com/xconnio/wampproto-go v0.0.0-20240606221544-14a7a1771d92/go.mod h1:/b7EyR1X9EkOHPQBJGz1KvdjClo1GsalBGIzjQU5+i4= -github.com/xconnio/wampproto-protobuf/go v0.0.0-20240607222929-52f6a1b22c87 h1:m7533sneh3nWbdPgWbQqkAOEZO2WSkdjaSO9rQZzTmY= -github.com/xconnio/wampproto-protobuf/go v0.0.0-20240607222929-52f6a1b22c87/go.mod h1:4mNLdXqx+2F43CRWaYqaMwZw0moLX3UURbFnaaCRtI8= +github.com/xconnio/wampproto-go v0.0.0-20240611083555-4bcae9e441a3 h1:iVd1el18AP1N3SnZy06ivkKcHtTuTD7cg3JhUM+xQbU= +github.com/xconnio/wampproto-go v0.0.0-20240611083555-4bcae9e441a3/go.mod h1:/b7EyR1X9EkOHPQBJGz1KvdjClo1GsalBGIzjQU5+i4= +github.com/xconnio/wampproto-protobuf/go v0.0.0-20240611092706-1e859744b5a2 h1:1WkQ68ICoin0wTerMn5FrWl+ZbK4XS3/W2Wr86jS9K8= +github.com/xconnio/wampproto-protobuf/go v0.0.0-20240611092706-1e859744b5a2/go.mod h1:SZAkbKSDucIUBrPgcKlT0SzVmktfrsD7OdP1chFR+vw= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= @@ -45,5 +54,6 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 4befaa068dd25186ab6ae0d2d547782e08c17522 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Tue, 11 Jun 2024 20:59:14 +0500 Subject: [PATCH 2/4] cmd: Add structure to read yaml config --- cmd/xconn/types.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 cmd/xconn/types.go diff --git a/cmd/xconn/types.go b/cmd/xconn/types.go new file mode 100644 index 0000000..bef56c1 --- /dev/null +++ b/cmd/xconn/types.go @@ -0,0 +1,55 @@ +package main + +type Config struct { + Version string `yaml:"version"` + Realms []Realm `yaml:"realms"` + Transports []Transport `yaml:"transports"` + Authenticators Authenticators `yaml:"authenticators"` +} + +type Realm struct { + Name string `yaml:"name"` +} + +type Transport struct { + Type string `yaml:"type"` + Port int `yaml:"port"` + Serializers []string `yaml:"serializers"` +} + +type CryptoSign struct { + AuthID string `yaml:"authid"` + Realm string `yaml:"realm"` + Role string `yaml:"role"` + AuthorizedKeys []string `yaml:"authorized_keys"` +} + +type WAMPCRA struct { + AuthID string `yaml:"authid"` + Realm string `yaml:"realm"` + Role string `yaml:"role"` + Secret string `yaml:"secret"` + Salt string `yaml:"salt"` + Iterations int `yaml:"iterations"` + KeyLen int `yaml:"keylen"` +} + +type Ticket struct { + AuthID string `yaml:"authid"` + Realm string `yaml:"realm"` + Role string `yaml:"role"` + Ticket string `yaml:"ticket"` +} + +type Anonymous struct { + AuthID string `yaml:"authid"` + Realm string `yaml:"realm"` + Role string `yaml:"role"` +} + +type Authenticators struct { + CryptoSign []CryptoSign `yaml:"cryptosign"` + WAMPCRA []WAMPCRA `yaml:"wampcra"` + Ticket []Ticket `yaml:"ticket"` + Anonymous []Anonymous `yaml:"anonymous"` +} From d7d9e612409b7a8b5a69d2fbe9a3c51fa28fa98d Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Tue, 11 Jun 2024 21:02:27 +0500 Subject: [PATCH 3/4] cmd: Add command to start server --- cmd/xconn/main.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/cmd/xconn/main.go b/cmd/xconn/main.go index 0607348..4fb85ae 100644 --- a/cmd/xconn/main.go +++ b/cmd/xconn/main.go @@ -1,12 +1,18 @@ package main import ( + "bytes" _ "embed" // nolint:gci "fmt" "log" "os" "github.com/alecthomas/kingpin/v2" + "golang.org/x/exp/slices" + "gopkg.in/yaml.v3" + + "github.com/xconnio/wampproto-protobuf/go" + "github.com/xconnio/xconn-go" ) var ( @@ -19,12 +25,16 @@ const ( ConfigDir = ".xconn" ConfigFile = ConfigDir + "/config.yaml" + + ProtobufSubProtocol = "wamp.2.protobuf" ) type cmd struct { parsedCommand string init *kingpin.CmdClause + + start *kingpin.CmdClause } func parseCommand(args []string) (*cmd, error) { @@ -32,7 +42,8 @@ func parseCommand(args []string) (*cmd, error) { app.Version(versionString).VersionFlag.Short('v') c := &cmd{ - init: app.Command("init", "Initialize sample router config."), + init: app.Command("init", "Initialize sample router config."), + start: app.Command("start", "Start the router."), } parsedCommand, err := app.Parse(args[1:]) @@ -59,6 +70,44 @@ func Run(args []string) error { if err = os.WriteFile(ConfigFile, sampleConfig, 0600); err != nil { return fmt.Errorf("unable to write config: %w", err) } + + case c.start.FullCommand(): + data, err := os.ReadFile(ConfigFile) + if err != nil { + return fmt.Errorf("unable to read config file: %w", err) + } + + var decoder = yaml.NewDecoder(bytes.NewBuffer(data)) + decoder.KnownFields(true) + + var config Config + if err := decoder.Decode(&config); err != nil { + return fmt.Errorf("unable to decode config file: %w", err) + } + + router := xconn.NewRouter() + + for _, realm := range config.Realms { + router.AddRealm(realm.Name) + } + + server := xconn.NewServer(router, nil) + + for _, transport := range config.Transports { + if slices.Contains(transport.Serializers, "protobuf") { + serializer := &wampprotobuf.ProtobufSerializer{} + protobufSpec := xconn.NewWSSerializerSpec(ProtobufSubProtocol, serializer) + + if err := server.RegisterSpec(protobufSpec); err != nil { + return err + } + } + + if err := server.Start("0.0.0.0", transport.Port); err != nil { + return err + } + } + } return nil From 1f33eae5510d498df3360ed7cde63ec89275eb65 Mon Sep 17 00:00:00 2001 From: Muzzammil Shahid Date: Wed, 12 Jun 2024 16:44:04 +0500 Subject: [PATCH 4/4] cmd: Add authenticator --- cmd/xconn/authenticator.go | 71 ++++++++++++++++++++++++++++++++++++++ cmd/xconn/main.go | 3 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 cmd/xconn/authenticator.go diff --git a/cmd/xconn/authenticator.go b/cmd/xconn/authenticator.go new file mode 100644 index 0000000..d406473 --- /dev/null +++ b/cmd/xconn/authenticator.go @@ -0,0 +1,71 @@ +package main + +import ( + "fmt" + + "golang.org/x/exp/slices" + + "github.com/xconnio/wampproto-go/auth" +) + +type Authenticator struct { + authenticator Authenticators +} + +func NewAuthenticator(authenticators Authenticators) *Authenticator { + return &Authenticator{authenticator: authenticators} +} + +func (a *Authenticator) Methods() []auth.Method { + return []auth.Method{auth.MethodAnonymous, auth.MethodTicket, auth.MethodCRA, auth.MethodCryptoSign} +} + +func (a *Authenticator) Authenticate(request auth.Request) (auth.Response, error) { + switch request.AuthMethod() { + case auth.MethodAnonymous: + for _, anonymous := range a.authenticator.Anonymous { + if anonymous.Realm == request.Realm() { + return auth.NewResponse(request.AuthID(), request.AuthRole(), 0) + } + } + return nil, fmt.Errorf("invalid realm") + + case auth.MethodTicket: + ticketRequest, ok := request.(*auth.TicketRequest) + if !ok { + return nil, fmt.Errorf("invalid request") + } + + for _, ticket := range a.authenticator.Ticket { + if ticket.Realm == ticketRequest.Realm() && ticket.Ticket == ticketRequest.Ticket() { + return auth.NewResponse(ticketRequest.AuthID(), ticketRequest.AuthRole(), 0) + } + } + return nil, fmt.Errorf("invalid ticket") + + case auth.MethodCRA: + for _, wampcra := range a.authenticator.WAMPCRA { + if wampcra.Realm == request.Realm() { + return auth.NewCRAResponse(request.AuthID(), request.AuthRole(), wampcra.Secret, 0), nil + } + } + return nil, fmt.Errorf("invalid realm") + + case auth.MethodCryptoSign: + cryptosignRequest, ok := request.(*auth.RequestCryptoSign) + if !ok { + return nil, fmt.Errorf("invalid request") + } + + for _, cryptosign := range a.authenticator.CryptoSign { + if cryptosign.Realm == cryptosignRequest.Realm() && + slices.Contains(cryptosign.AuthorizedKeys, cryptosignRequest.PublicKey()) { + return auth.NewResponse(cryptosignRequest.AuthID(), cryptosignRequest.AuthRole(), 0) + } + } + return nil, fmt.Errorf("unknown publickey") + + default: + return nil, fmt.Errorf("unknown authentication method: %v", request.AuthMethod()) + } +} diff --git a/cmd/xconn/main.go b/cmd/xconn/main.go index 4fb85ae..efbc787 100644 --- a/cmd/xconn/main.go +++ b/cmd/xconn/main.go @@ -91,7 +91,8 @@ func Run(args []string) error { router.AddRealm(realm.Name) } - server := xconn.NewServer(router, nil) + authenticator := NewAuthenticator(config.Authenticators) + server := xconn.NewServer(router, authenticator) for _, transport := range config.Transports { if slices.Contains(transport.Serializers, "protobuf") {