ETCD RBAC功能简介
ETCD的介绍网上已经烂大街了,我就不赘述了。因为本文主要是使用ETCD的RBAC功能,所以重点介绍一下此功能。
RBAC及Role-based access control,直译就是基于角色的访问控制。核心就是三个部分,权限、角色、用户。为角色绑定权限,为用户分配角色,目的是简化权限的管理。相同角色的用户拥有相同的权限,不需要对每个用户分配权限。
ETCD 在版本2.1后实现了此功能。ETCD使用RBAC主要作用是隔离不同用户的访问空间,我们知道ETCD是一个键值储存系统用来存贮用户的关键信息。如果不开启RBAC,所用用户都可以访问及操作所有的内容,这对一个存贮关键信息的系统来说是有安全隐患的。使用RBAC可以为不同的角色分配不同的键值访问权限,这样就保证了关键信息的安全。
vault简介
vault是由HashiCorp开发的一个secrets管理工具。所谓secrets,按照vault官网的介绍就是指token、password、certificate 以及encryption key等私密信息。对于secrets的管理,vault主要使用一个叫做secrets engine的模块实现。 secrets engine能够对于不同后端的secrets进行存储、生成、加密等。通过HTTP-API对外提供统一的密码访问入口,并且提供权限控制以及详细的日志审计功能。vault原生实现了多种secrets engine,如aws、google cloud、consul等。
但是很遗憾,vault原生并不支持针对ETCD的secrets engine,这导致ETCD用户无法使用vault来管理etcd rbac用户角色及密码。好在vault提供了用户接口,用户可以实现这些接口来开发一个plugin,使vault具有与ETCD交互的能力。
plugin设计
plugin相对于vault是独立编译的,运行的plugin是一个grpc server,vault通过plugin与secrets engine交互及本文中的ETCD。因为使用的是grpc进行交互,所以按理说任何支持grpc的语言都可以用来开发grpc。我使用的是golang,因为vault本身就是golang开发的。
这里仅贴出部分代码对plugin运行流程做一个简单的介绍,以下为plugin的目录结构。
plugin
├── engine
│ ├── connection_Producer.go
│ ├── doc.go
│ ├── plugin.go
│ └── plugin_test.go
├── main.go
└── vendor.tgz
plugin/main.go
func main() {
apiClientMeta := &pluginutil.APIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(os.Args[1:])
err := engine.Run(apiClientMeta.GetTLSConfig())
if err != nil {
log.Println(apiClientMeta.GetTLSConfig())
os.Exit(1)
}
}
main函数用来接收flag,并调用engine中的run函数,启动运行plugin,实际就是运行了一个grpc server。
对于ETCD操作的实现主要在engine文件中,
plugin/engine/connection_Producer.go
func (c *etcdConnectionProducer) Connection(ctx context.Context) (interface{}, error)
if !c.Initialized {
return nil, connutil.ErrNotInitialized
}
if c.client != nil {
return c.client, nil
}
tlsInfo := transport.TLSInfo{
CertFile: c.CertFile,
KeyFile: c.KeyFIle,
TrustedCAFile: c.TrustedCAFile,
}
tlsConfig, err := tlsInfo.ClientConfig()
endpoints := strings.Split(c.Endpoints, ",")
cfg := clientv3.Config{
Endpoints: endpoints,
TLS: tlsConfig,
Username: "root",
Password: c.RootPassword,
DialTimeout: 5 * time.Second,
DialKeepAliveTime: 10 * time.Second,
DialKeepAliveTimeout: 3 * time.Second,
}
client, err := clientv3.New(cfg)
if err != nil {
return nil, err
}
c.client = client
return c.client, nil
}
Connection为与ETCD建立链接的函数,实际调用了etcd.io/etcd包中的clientv3模块生成了etcd client。
plugin/engine/plugin.go
通过plugin/engine/connection_Producer.go建立链接后,可以使用client实例对ETCD进行操作,并且需要实现vault提供的接口。
type Database interface {
Type() (string, error)
CreateUser(ctx context.Context, statements Statements, usernameConfig UsernameConfig, expiration time.Time)(username string, password string, err error)
RenewUser(ctx context.Context, statements Statements, username string, expiration time.Time) error
RevokeUser(ctx context.Context, statements Statements, username string) error
RotateRootCredentials(ctx context.Context, statements []string) (config map[string]interface{}, err error)
Init(ctx context.Context, config map[string]interface{}, verifyConnection bool) (saveConfig map[string]interface{}, err error)
Close() error
}
CreateUser 新建ETCD用户并分配权限,并且可以设置ttl
RenewUser 如果新建用户时设置ttl,此命令用于刷新ttl
RevokeUser 删除用户及角色
使用plugin
在vault的config文件中,添加plugin_directory及api_addr两字段。
plugin_directory = "/vault/plugin"
api_addr = "https://0.0.0.0:8200"
将build成功的plugin放在plugin_directory指定的目录下,进行以下操作,来通过vault获得ETCD用户名及密码。
注册plugin
./vault plugin register -sha256=4b03d14761de35910dfedb49b07bc026e94b8a866e2eee6d4af518b67dbff9a0 -command="plugin -tls-skip-verify" etcd-plugin
secrets enable database
./vault secrets enable database
write config //启动plugin
./vault write database/config/etcd-role plugin_name=etcd-plugin endpoints="http://xxx:2379"rootpassword="123" allowed_roles="my-role"
endpints是etcd的地址,rootpassword是ETCD RBAC的root用户密码
write role
./vault write database/roles/my-role db_name=etcd-role creation_statements='{"role_name":"role1", "roles":[{"role":"readwirte","dir":"/app1"},{"role":"readwrite","dir":"/services"}]}' revocation_statements='{"role_name":"role1"}' default_ttl="180"
read creds //执行create_user
./vault read database/creds/my-role