Influxdb授權驗證流程分析
Qurey語句權限驗證
- 關于Influxdb的驗證邏輯,我們可以先參考官方文檔: Authentication and authorization in InfluxDB
1.1 在配置文件時,[http] auth-enabled = true將開啟授權驗證 - 我們從
appendHTTPDService(cmd/influxdb/run/server.go)入手,在處理http request時要作授權驗證,先要創(chuàng)建授權驗證對象:
srv.Handler.QueryAuthorizer = meta.NewQueryAuthorizer(s.MetaClient)
srv.Handler.WriteAuthorizer = meta.NewWriteAuthorizer(s.MetaClient)
- Query驗證
meta.QueryAuthorizer(services/meta/data.go)
// QueryAuthorizer determines whether a user is authorized to execute a given query.
type QueryAuthorizer struct {
Client *Client // MetaClient
}
-
驗證流程圖:
influxdb_authorizer.png 通過上面這張圖我們看到授權驗證所有的接口都定義在
type Authorizer interface中
type Authorizer interface {
// AuthorizeDatabase indicates whether the given Privilege is authorized on the database with the given name.
AuthorizeDatabase(p influxql.Privilege, name string) bool
// AuthorizeQuery returns an error if the query cannot be executed
AuthorizeQuery(database string, query *influxql.Query) error
// AuthorizeSeriesRead determines if a series is authorized for reading
AuthorizeSeriesRead(database string, measurement []byte, tags models.Tags) bool
// AuthorizeSeriesWrite determines if a series is authorized for writing
AuthorizeSeriesWrite(database string, measurement []byte, tags models.Tags) bool
}
- 首先看一下 `QueryAuthorizer.AuthorizeQuery, 以注釋的方式給出說明
func (a *QueryAuthorizer) AuthorizeQuery(u User, query *influxql.Query, database string) error {
// Special case if no users exist.
if n := a.Client.UserCount(); n == 0 {
// Ensure there is at least one statement.
if len(query.Statements) > 0 {
// First statement in the query must create a user with admin privilege.
// 如果沒有任何的用戶,query的一個請求必須是Create Admin User
cu, ok := query.Statements[0].(*influxql.CreateUserStatement)
if ok && cu.Admin {
return nil
}
}
//Inlufxdb的驗證流程規(guī)定,如果開啟了授權驗證,但還沒有創(chuàng)建任何用戶,則返回用戶下面這個錯誤
return &ErrAuthorize{
Query: query,
Database: database,
Message: "create admin user first or disable authentication",
}
}
//請求中沒有user信息,則返回用戶下面這個錯誤
if u == nil {
return &ErrAuthorize{
Query: query,
Database: database,
Message: "no user provided",
}
}
// 調用User.AuthorizeQuery繼續(xù)驗證
return u.AuthorizeQuery(database, query)
}
- 接著看一下
UserInfo.AuthorizeQuery
func (u *UserInfo) AuthorizeQuery(database string, query *influxql.Query) error {
// Admin privilege allows the user to execute all statements.
// Admin用戶有一切授權,不用再繼續(xù)驗證
if u.Admin {
return nil
}
// query里的每條statement的所需的所有privilege都需要逐一驗證
for _, stmt := range query.Statements {
privs, err := stmt.RequiredPrivileges()
for _, p := range privs {
if p.Admin {
// 走到這里說明當前用戶一定不是admin用戶,但當前statement又需要admin privilege, 則返回如下錯
return &ErrAuthorize{
Query: query,
User: u.Name,
Database: database,
Message: fmt.Sprintf("statement '%s', requires admin privilege", stmt),
}
}
db := p.Name
if db == "" {
db = database
}
// 調用u.AuthorizeDatabase進一步驗證
if !u.AuthorizeDatabase(p.Privilege, db) {
return &ErrAuthorize{
Query: query,
User: u.Name,
Database: database,
Message: fmt.Sprintf("statement '%s', requires %s on %s", stmt, p.Privilege.String(), db),
}
}
}
}
return nil
}
- 最后看一下
UserInfo.AuthorizeDatabase和UserInfo的定義:
type UserInfo struct {
// User's name.
Name string
// Hashed password.
Hash string
// Whether the user is an admin, i.e. allowed to do everything.
Admin bool
// Map of database name to granted privilege.
// 針對不同的database的privilege都存在這個map中
Privileges map[string]influxql.Privilege
}
func (ui *UserInfo) AuthorizeDatabase(privilege influxql.Privilege, database string) bool {
if ui.Admin || privilege == influxql.NoPrivileges {
return true
}
p, ok := ui.Privileges[database]
// 查Privileges這個map
return ok && (p == privilege || p == influxql.AllPrivileges)
}
寫語句權限驗證
-
srv.Handler.WriteAuthorizer = meta.NewWriteAuthorizer(s.MetaClient),創(chuàng)建了寫權限驗證對象 - 具體實現(xiàn)
WriteAuthorizer(在 services/meta/write_authorizer.go中), 實現(xiàn)比較簡單, 最終也是調用UserInfo.AuthorizeDatabase來驗證
// WriteAuthorizer determines whether a user is authorized to write to a given database.
type WriteAuthorizer struct {
Client *Client
}
// AuthorizeWrite returns nil if the user has permission to write to the database.
func (a WriteAuthorizer) AuthorizeWrite(username, database string) error {
u, err := a.Client.User(username)
if err != nil || u == nil || !u.AuthorizeDatabase(influxql.WritePrivilege, database) {
return &ErrAuthorize{
Database: database,
Message: fmt.Sprintf("%s not authorized to write to %s", username, database),
}
}
return nil
}
Open權限驗證
- 不需要授權驗證的情況,Influxdb實現(xiàn)了
openAuthorizer, 它同樣實現(xiàn)了Authorizer interface
// OpenAuthorizer is the Authorizer used when authorization is disabled.
// It allows all operations.
type openAuthorizer struct{}
// OpenAuthorizer can be shared by all goroutines.
var OpenAuthorizer = openAuthorizer{}
