mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
518 lines
13 KiB
Markdown
518 lines
13 KiB
Markdown
---
|
|
title: v2 Auth and Security
|
|
---
|
|
|
|
**This is the documentation for etcd2 releases. Read [etcd3 doc][v3-docs] for etcd3 releases.**
|
|
|
|
[v3-docs]: ../docs.md#documentation
|
|
|
|
## etcd Resources
|
|
There are three types of resources in etcd
|
|
|
|
1. permission resources: users and roles in the user store
|
|
2. key-value resources: key-value pairs in the key-value store
|
|
3. settings resources: security settings, auth settings, and dynamic etcd cluster settings (election/heartbeat)
|
|
|
|
### Permission Resources
|
|
|
|
#### Users
|
|
A user is an identity to be authenticated. Each user can have multiple roles. The user has a capability (such as reading or writing) on the resource if one of the roles has that capability.
|
|
|
|
A user named `root` is required before authentication can be enabled, and it always has the ROOT role. The ROOT role can be granted to multiple users, but `root` is required for recovery purposes.
|
|
|
|
#### Roles
|
|
Each role has exact one associated Permission List. An permission list exists for each permission on key-value resources.
|
|
|
|
The special static ROOT (named `root`) role has a full permissions on all key-value resources, the permission to manage user resources and settings resources. Only the ROOT role has the permission to manage user resources and modify settings resources. The ROOT role is built-in and does not need to be created.
|
|
|
|
There is also a special GUEST role, named 'guest'. These are the permissions given to unauthenticated requests to etcd. This role will be created automatically, and by default allows access to the full keyspace due to backward compatibility. (etcd did not previously authenticate any actions.). This role can be modified by a ROOT role holder at any time, to reduce the capabilities of unauthenticated users.
|
|
|
|
#### Permissions
|
|
|
|
There are two types of permissions, `read` and `write`. All management and settings require the ROOT role.
|
|
|
|
A Permission List is a list of allowed patterns for that particular permission (read or write). Only ALLOW prefixes are supported. DENY becomes more complicated and is TBD.
|
|
|
|
### Key-Value Resources
|
|
A key-value resource is a key-value pairs in the store. Given a list of matching patterns, permission for any given key in a request is granted if any of the patterns in the list match.
|
|
|
|
Only prefixes or exact keys are supported. A prefix permission string ends in `*`.
|
|
A permission on `/foo` is for that exact key or directory, not its children or recursively. `/foo*` is a prefix that matches `/foo` recursively, and all keys thereunder, and keys with that prefix (eg. `/foobar`. Contrast to the prefix `/foo/*`). `*` alone is permission on the full keyspace.
|
|
|
|
### Settings Resources
|
|
|
|
Specific settings for the cluster as a whole. This can include adding and removing cluster members, enabling or disabling authentication, replacing certificates, and any other dynamic configuration by the administrator (holder of the ROOT role).
|
|
|
|
## v2 Auth
|
|
|
|
### Basic Auth
|
|
We only support [Basic Auth][basic-auth] for the first version. Client needs to attach the basic auth to the HTTP Authorization Header.
|
|
|
|
### Authorization field for operations
|
|
Added to requests to /v2/keys, /v2/auth
|
|
Add code 401 Unauthorized to the set of responses from the v2 API
|
|
Authorization: Basic {encoded string}
|
|
|
|
### Future Work
|
|
Other types of auth can be considered for the future (eg, signed certs, public keys) but the `Authorization:` header allows for other such types
|
|
|
|
### Things out of Scope for etcd Permissions
|
|
|
|
* Pluggable AUTH backends like LDAP (other Authorization tokens generated by LDAP et al may be a possibility)
|
|
* Very fine-grained access controls (eg: users modifying keys outside work hours)
|
|
|
|
|
|
|
|
## API endpoints
|
|
|
|
An Error JSON corresponds to:
|
|
{
|
|
"name": "ErrErrorName",
|
|
"description" : "The longer helpful description of the error."
|
|
}
|
|
|
|
#### Enable and Disable Authentication
|
|
|
|
**Get auth status**
|
|
|
|
GET /v2/auth/enable
|
|
|
|
Sent Headers:
|
|
Possible Status Codes:
|
|
200 OK
|
|
200 Body:
|
|
{
|
|
"enabled": true
|
|
}
|
|
|
|
|
|
**Enable auth**
|
|
|
|
PUT /v2/auth/enable
|
|
|
|
Sent Headers:
|
|
Put Body: (empty)
|
|
Possible Status Codes:
|
|
200 OK
|
|
400 Bad Request (if root user has not been created)
|
|
409 Conflict (already enabled)
|
|
200 Body: (empty)
|
|
|
|
**Disable auth**
|
|
|
|
DELETE /v2/auth/enable
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <RootAuthString>
|
|
Possible Status Codes:
|
|
200 OK
|
|
401 Unauthorized (if not a root user)
|
|
409 Conflict (already disabled)
|
|
200 Body: (empty)
|
|
|
|
|
|
#### Users
|
|
|
|
The User JSON object is formed as follows:
|
|
|
|
```
|
|
{
|
|
"user": "userName",
|
|
"password": "password",
|
|
"roles": [
|
|
"role1",
|
|
"role2"
|
|
],
|
|
"grant": [],
|
|
"revoke": []
|
|
}
|
|
```
|
|
|
|
Password is only passed when necessary.
|
|
|
|
**Get a List of Users**
|
|
|
|
GET/HEAD /v2/auth/users
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Possible Status Codes:
|
|
200 OK
|
|
401 Unauthorized
|
|
200 Headers:
|
|
Content-type: application/json
|
|
200 Body:
|
|
{
|
|
"users": [
|
|
{
|
|
"user": "alice",
|
|
"roles": [
|
|
{
|
|
"role": "root",
|
|
"permissions": {
|
|
"kv": {
|
|
"read": ["/*"],
|
|
"write": ["/*"]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"user": "bob",
|
|
"roles": [
|
|
{
|
|
"role": "guest",
|
|
"permissions": {
|
|
"kv": {
|
|
"read": ["/*"],
|
|
"write": ["/*"]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
**Get User Details**
|
|
|
|
GET/HEAD /v2/auth/users/alice
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Possible Status Codes:
|
|
200 OK
|
|
401 Unauthorized
|
|
404 Not Found
|
|
200 Headers:
|
|
Content-type: application/json
|
|
200 Body:
|
|
{
|
|
"user" : "alice",
|
|
"roles" : [
|
|
{
|
|
"role": "fleet",
|
|
"permissions" : {
|
|
"kv" : {
|
|
"read": [ "/fleet/" ],
|
|
"write": [ "/fleet/" ]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"role": "etcd",
|
|
"permissions" : {
|
|
"kv" : {
|
|
"read": [ "/*" ],
|
|
"write": [ "/*" ]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
**Create Or Update A User**
|
|
|
|
A user can be created with initial roles, if filled in. However, no roles are required; only the username and password fields
|
|
|
|
PUT /v2/auth/users/charlie
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Put Body:
|
|
JSON struct, above, matching the appropriate name
|
|
* Starting password and roles when creating.
|
|
* Grant/Revoke/Password filled in when updating (to grant roles, revoke roles, or change the password).
|
|
Possible Status Codes:
|
|
200 OK
|
|
201 Created
|
|
400 Bad Request
|
|
401 Unauthorized
|
|
404 Not Found (update non-existent users)
|
|
409 Conflict (when granting duplicated roles or revoking non-existent roles)
|
|
200 Headers:
|
|
Content-type: application/json
|
|
200 Body:
|
|
JSON state of the user
|
|
|
|
**Remove A User**
|
|
|
|
DELETE /v2/auth/users/charlie
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Possible Status Codes:
|
|
200 OK
|
|
401 Unauthorized
|
|
403 Forbidden (remove root user when auth is enabled)
|
|
404 Not Found
|
|
200 Headers:
|
|
200 Body: (empty)
|
|
|
|
#### Roles
|
|
|
|
A full role structure may look like this. A Permission List structure is used for the "permissions", "grant", and "revoke" keys.
|
|
```
|
|
{
|
|
"role" : "fleet",
|
|
"permissions" : {
|
|
"kv" : {
|
|
"read" : [ "/fleet/" ],
|
|
"write": [ "/fleet/" ]
|
|
}
|
|
},
|
|
"grant" : {"kv": {...}},
|
|
"revoke": {"kv": {...}}
|
|
}
|
|
```
|
|
|
|
**Get Role Details**
|
|
|
|
GET/HEAD /v2/auth/roles/fleet
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Possible Status Codes:
|
|
200 OK
|
|
401 Unauthorized
|
|
404 Not Found
|
|
200 Headers:
|
|
Content-type: application/json
|
|
200 Body:
|
|
{
|
|
"role" : "fleet",
|
|
"permissions" : {
|
|
"kv" : {
|
|
"read": [ "/fleet/" ],
|
|
"write": [ "/fleet/" ]
|
|
}
|
|
}
|
|
}
|
|
|
|
**Get a list of Roles**
|
|
|
|
GET/HEAD /v2/auth/roles
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Possible Status Codes:
|
|
200 OK
|
|
401 Unauthorized
|
|
200 Headers:
|
|
Content-type: application/json
|
|
200 Body:
|
|
{
|
|
"roles": [
|
|
{
|
|
"role": "fleet",
|
|
"permissions": {
|
|
"kv": {
|
|
"read": ["/fleet/"],
|
|
"write": ["/fleet/"]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"role": "etcd",
|
|
"permissions": {
|
|
"kv": {
|
|
"read": ["/*"],
|
|
"write": ["/*"]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"role": "quay",
|
|
"permissions": {
|
|
"kv": {
|
|
"read": ["/*"],
|
|
"write": ["/*"]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
**Create Or Update A Role**
|
|
|
|
PUT /v2/auth/roles/rkt
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Put Body:
|
|
Initial desired JSON state, including the role name for verification and:
|
|
* Starting permission set if creating
|
|
* Granted/Revoked permission set if updating
|
|
Possible Status Codes:
|
|
200 OK
|
|
201 Created
|
|
400 Bad Request
|
|
401 Unauthorized
|
|
404 Not Found (update non-existent roles)
|
|
409 Conflict (when granting duplicated permission or revoking non-existent permission)
|
|
200 Body:
|
|
JSON state of the role
|
|
|
|
**Remove A Role**
|
|
|
|
DELETE /v2/auth/roles/rkt
|
|
|
|
Sent Headers:
|
|
Authorization: Basic <BasicAuthString>
|
|
Possible Status Codes:
|
|
200 OK
|
|
401 Unauthorized
|
|
403 Forbidden (remove root)
|
|
404 Not Found
|
|
200 Headers:
|
|
200 Body: (empty)
|
|
|
|
|
|
## Example Workflow
|
|
|
|
Let's walk through an example to show two tenants (applications, in our case) using etcd permissions.
|
|
|
|
### Create root role
|
|
|
|
```
|
|
PUT /v2/auth/users/root
|
|
Put Body:
|
|
{"user" : "root", "password": "betterRootPW!"}
|
|
```
|
|
|
|
### Enable auth
|
|
|
|
```
|
|
PUT /v2/auth/enable
|
|
```
|
|
|
|
### Modify guest role (revoke write permission)
|
|
|
|
```
|
|
PUT /v2/auth/roles/guest
|
|
Headers:
|
|
Authorization: Basic <root:betterRootPW!>
|
|
Put Body:
|
|
{
|
|
"role" : "guest",
|
|
"revoke" : {
|
|
"kv" : {
|
|
"write": [
|
|
"/*"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
|
|
### Create Roles for the Applications
|
|
|
|
Create the rkt role fully specified:
|
|
|
|
```
|
|
PUT /v2/auth/roles/rkt
|
|
Headers:
|
|
Authorization: Basic <root:betterRootPW!>
|
|
Body:
|
|
{
|
|
"role" : "rkt",
|
|
"permissions" : {
|
|
"kv": {
|
|
"read": [
|
|
"/rkt/*"
|
|
],
|
|
"write": [
|
|
"/rkt/*"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
But let's make fleet just a basic role for now:
|
|
|
|
```
|
|
PUT /v2/auth/roles/fleet
|
|
Headers:
|
|
Authorization: Basic <root:betterRootPW!>
|
|
Body:
|
|
{
|
|
"role" : "fleet"
|
|
}
|
|
```
|
|
|
|
### Optional: Grant some permissions to the roles
|
|
|
|
Well, we finally figured out where we want fleet to live. Let's fix it.
|
|
(Note that we avoided this in the rkt case. So this step is optional.)
|
|
|
|
|
|
```
|
|
PUT /v2/auth/roles/fleet
|
|
Headers:
|
|
Authorization: Basic <root:betterRootPW!>
|
|
Put Body:
|
|
{
|
|
"role" : "fleet",
|
|
"grant" : {
|
|
"kv" : {
|
|
"read": [
|
|
"/rkt/fleet",
|
|
"/fleet/*"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Create Users
|
|
|
|
Same as before, let's use rocket all at once and fleet separately
|
|
|
|
```
|
|
PUT /v2/auth/users/rktuser
|
|
Headers:
|
|
Authorization: Basic <root:betterRootPW!>
|
|
Body:
|
|
{"user" : "rktuser", "password" : "rktpw", "roles" : ["rkt"]}
|
|
```
|
|
|
|
```
|
|
PUT /v2/auth/users/fleetuser
|
|
Headers:
|
|
Authorization: Basic <root:betterRootPW!>
|
|
Body:
|
|
{"user" : "fleetuser", "password" : "fleetpw"}
|
|
```
|
|
|
|
### Optional: Grant Roles to Users
|
|
|
|
Likewise, let's explicitly grant fleetuser access.
|
|
|
|
```
|
|
PUT /v2/auth/users/fleetuser
|
|
Headers:
|
|
Authorization: Basic <root:betterRootPW!>
|
|
Body:
|
|
{"user": "fleetuser", "grant": ["fleet"]}
|
|
```
|
|
|
|
#### Start to use fleetuser and rktuser
|
|
|
|
|
|
For example:
|
|
|
|
```
|
|
PUT /v2/keys/rkt/RktData
|
|
Headers:
|
|
Authorization: Basic <rktuser:rktpw>
|
|
Body:
|
|
value=launch
|
|
```
|
|
|
|
Reads and writes outside the prefixes granted will fail with a 401 Unauthorized.
|
|
|
|
[basic-auth]: https://en.wikipedia.org/wiki/Basic_access_authentication
|