Skip to content

Commit

Permalink
Merge pull request #39 from DrmagicE/auth
Browse files Browse the repository at this point in the history
feat: add simple username/password auth plugin
  • Loading branch information
DrmagicE authored Dec 19, 2020
2 parents 215d372 + 6ed745a commit ca3b679
Show file tree
Hide file tree
Showing 30 changed files with 2,602 additions and 71 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,28 @@ persistence:
database: 0
```
## Authentication
Gmqtt provides a simple username/password authentication mechanism. (Provided by [auth](https://github.com/DrmagicE/gmqtt/blob/master/plugin/auth) plugin).
It is not enabled in default configuration, you can change the configuration to enable it:
```yaml
# plugin loading orders
plugin_order:
- auth
- prometheus
- admin
```
When auth plugin enabled, every clients need an account to get connected.You can add accounts through the HTTP API:
```bash
# Create: username = user1, password = user1pass
$ curl -X POST -d '{"password":"user1pass"}' 127.0.0.1:8083/v1/accounts/user1
{}
# Query
$ curl 127.0.0.1:8083/v1/accounts/user1
{"account":{"username":"user1","password":"20a0db53bc1881a7f739cd956b740039"}}
```
API Doc [swagger](https://github.com/DrmagicE/gmqtt/blob/master/plugin/auth/swagger)


## Docker
```
$ docker build -t gmqtt .
Expand Down
22 changes: 22 additions & 0 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,28 @@ persistence:
database: 0
```
## 配置鉴权
Gmqtt内置了基于username/password的简单鉴权机制。(由 [auth](https://github.com/DrmagicE/gmqtt/blob/master/plugin/auth) 插件提供)。
Gmqtt默认配置没有开启鉴权,可以通过修改配置文件来加载鉴权插件:
```yaml
# plugin loading orders
plugin_order:
- auth
- prometheus
- admin
```
加载后,需要添加账户才可以连接,可以通过HTTP接口来添加账户:
```bash
# 创建: username = user1, password = user1pass
$ curl -X POST -d '{"password":"user1pass"}' 127.0.0.1:8083/v1/accounts/user1
{}
# 查询:
$ curl 127.0.0.1:8083/v1/accounts/user1
{"account":{"username":"user1","password":"20a0db53bc1881a7f739cd956b740039"}}
```
API文档:[swagger](https://github.com/DrmagicE/gmqtt/blob/master/plugin/auth/swagger)


## Docker
```
$ docker build -t gmqtt .
Expand Down
33 changes: 23 additions & 10 deletions cmd/gmqttd/default_config.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
listeners:
# bind address
- address: ":1883"
# tls setting
# tls:
# cert_file: "path_to_cert_file"
# key_file: "path_to_key_file"
# bind address
- address: ":1883"
# tls setting
# tls:
# cert_file: "path_to_cert_file"
# key_file: "path_to_key_file"

- address: ":8883"
# websocket setting
websocket:
path: "/"
- address: ":8883"
# websocket setting
websocket:
path: "/"
mqtt:
session_expiry: 2h
session_expiry_check_timer: 20s
Expand Down Expand Up @@ -62,6 +62,19 @@ plugins:
addr: :8083
grpc:
addr: 8084
auth:
# Password hash type. (plain | md5 | sha256 | bcrypt)
# Default to MD5.
hash: md5
# The file to store password. Default to $HOME/gmqtt_password.yml
# password_file:

# plugin loading orders
plugin_order:
# Uncomment auth to enable authentication.
#- auth
- prometheus
- admin
log:
level: info # debug | info | warn | error
format: text # json | text
Expand Down
15 changes: 3 additions & 12 deletions cmd/gmqttd/plugins.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
package main

import (
"github.com/DrmagicE/gmqtt/plugin/admin"
"github.com/DrmagicE/gmqtt/plugin/prometheus"
"github.com/DrmagicE/gmqtt/server"
_ "github.com/DrmagicE/gmqtt/plugin/admin"
_ "github.com/DrmagicE/gmqtt/plugin/auth"
_ "github.com/DrmagicE/gmqtt/plugin/prometheus"
)

var pluginOrder = []string{
prometheus.Name,
admin.Name,
}

func init() {
server.SetPluginOrder(pluginOrder)
}
16 changes: 10 additions & 6 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var (
defaultPluginConfig = make(map[string]Configuration)
)

// Configuration is the interface that enable the implementation can parse config from the global config file.
// Configuration is the interface that enable the implementation to parse config from the global config file.
// Plugin admin and prometheus are two examples.
type Configuration interface {
// Validate validates the configuration.
Expand Down Expand Up @@ -103,11 +103,15 @@ func (p pluginConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {

// Config is the configration for gmqttd.
type Config struct {
Listeners []*ListenerConfig `yaml:"listeners"`
MQTT MQTT `yaml:"mqtt,omitempty"`
Log LogConfig `yaml:"log"`
PidFile string `yaml:"pid_file"`
Plugins pluginConfig `yaml:"plugins"`
Listeners []*ListenerConfig `yaml:"listeners"`
MQTT MQTT `yaml:"mqtt,omitempty"`
Log LogConfig `yaml:"log"`
PidFile string `yaml:"pid_file"`
Plugins pluginConfig `yaml:"plugins"`
// PluginOrder is a slice that contains the name of the plugin which will be loaded.
// Giving a correct order to the slice is significant,
// because it represents the loading order which affect the behavior of the broker.
PluginOrder []string `yaml:"plugin_order"`
Persistence Persistence `yaml:"persistence"`
TopicAliasManager TopicAliasManager `yaml:"topic_alias_manager"`
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.6.1
go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
golang.org/x/text v0.3.2 // indirect
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
9 changes: 8 additions & 1 deletion plugin/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"

"github.com/DrmagicE/gmqtt/config"
"github.com/DrmagicE/gmqtt/server"
Expand Down Expand Up @@ -110,7 +112,12 @@ func (a *Admin) Load(service server.Server) error {
log = server.LoggerWithField(zap.String("plugin", Name))
s := grpc.NewServer(
grpc.ChainUnaryInterceptor(
grpc_zap.UnaryServerInterceptor(log),
grpc_zap.UnaryServerInterceptor(log, grpc_zap.WithLevels(func(code codes.Code) zapcore.Level {
if code == codes.OK {
return zapcore.DebugLevel
}
return grpc_zap.DefaultClientCodeToLevel(code)
})),
grpc_prometheus.UnaryServerInterceptor),
)
a.grpcServer = s
Expand Down
4 changes: 2 additions & 2 deletions plugin/admin/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ func (c *Config) Validate() error {
var DefaultConfig = Config{
HTTP: HTTPConfig{
Enable: true,
Addr: ":8083",
Addr: "127.0.0.1:8083",
},
GRPC: GRPCConfig{
Addr: ":8084",
Addr: "127.0.0.1:8084",
},
}

Expand Down
6 changes: 6 additions & 0 deletions plugin/admin/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,17 @@ func (i *Indexer) Remove(id string) *list.Element {

// GetByID returns the value for the given id.
// Return nil if not found.
// Notice: Any access to the return *list.Element also require the mutex,
// because the Set method can modify the Value for *list.Element when updating the Value for the same id.
// If the caller needs the Value in *list.Element, it must get the Value before the next Set is called.
func (i *Indexer) GetByID(id string) *list.Element {
return i.index[id]
}

// Iterate iterates at most n elements in the list begin from offset.
// Notice: Any access to the *list.Element in fn also require the mutex,
// because the Set method can modify the Value for *list.Element when updating the Value for the same id.
// If the caller needs the Value in *list.Element, it must get the Value before the next Set is called.
func (i *Indexer) Iterate(fn func(elem *list.Element), offset, n uint) {
if i.rows.Len() < int(offset) {
return
Expand Down
7 changes: 7 additions & 0 deletions plugin/auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Auth

Auth plugin provides a simple username/password authentication mechanism.

# API Doc

See [swagger](https://github.com/DrmagicE/gmqtt/blob/master/plugin/auth/swagger)
Loading

0 comments on commit ca3b679

Please sign in to comment.