mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
*: Rename security
to auth
This commit is contained in:
parent
e20b487904
commit
64ec8af91b
@ -12,7 +12,7 @@ There are three types of resources in etcd
|
||||
#### 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 security 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.
|
||||
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.
|
||||
@ -35,7 +35,7 @@ A permission on `/foo` is for that exact key or directory, not its children or r
|
||||
|
||||
### Settings Resources
|
||||
|
||||
Specific settings for the cluster as a whole. This can include adding and removing cluster members, enabling or disabling security, replacing certificates, and any other dynamic configuration by the administrator (holder of the ROOT role).
|
||||
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
|
||||
|
||||
@ -43,7 +43,7 @@ Specific settings for the cluster as a whole. This can include adding and removi
|
||||
We only support [Basic Auth](http://en.wikipedia.org/wiki/Basic_access_authentication) 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/security
|
||||
Added to requests to /v2/keys, /v2/auth
|
||||
Add code 403 Forbidden to the set of responses from the v2 API
|
||||
Authorization: Basic {encoded string}
|
||||
|
||||
@ -86,7 +86,7 @@ Password is only passed when necessary. Last Modified is set by the server and i
|
||||
|
||||
**Get a list of users**
|
||||
|
||||
GET/HEAD /v2/security/user
|
||||
GET/HEAD /v2/auth/user
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -102,7 +102,7 @@ GET/HEAD /v2/security/user
|
||||
|
||||
**Get User Details**
|
||||
|
||||
GET/HEAD /v2/security/users/alice
|
||||
GET/HEAD /v2/auth/users/alice
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -122,7 +122,7 @@ GET/HEAD /v2/security/users/alice
|
||||
|
||||
A user can be created with initial roles, if filled in. However, no roles are required; only the username and password fields
|
||||
|
||||
PUT /v2/security/users/charlie
|
||||
PUT /v2/auth/users/charlie
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -138,7 +138,7 @@ PUT /v2/security/users/charlie
|
||||
|
||||
**Remove A User**
|
||||
|
||||
DELETE /v2/security/users/charlie
|
||||
DELETE /v2/auth/users/charlie
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -169,7 +169,7 @@ A full role structure may look like this. A Permission List structure is used fo
|
||||
|
||||
**Get a list of Roles**
|
||||
|
||||
GET/HEAD /v2/security/roles
|
||||
GET/HEAD /v2/auth/roles
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -186,7 +186,7 @@ GET/HEAD /v2/security/roles
|
||||
|
||||
**Get Role Details**
|
||||
|
||||
GET/HEAD /v2/security/roles/fleet
|
||||
GET/HEAD /v2/auth/roles/fleet
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -210,7 +210,7 @@ GET/HEAD /v2/security/roles/fleet
|
||||
|
||||
**Create Or Update A Role**
|
||||
|
||||
PUT /v2/security/roles/rocket
|
||||
PUT /v2/auth/roles/rocket
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -228,7 +228,7 @@ PUT /v2/security/roles/rocket
|
||||
|
||||
**Remove A Role**
|
||||
|
||||
DELETE /v2/security/roles/rocket
|
||||
DELETE /v2/auth/roles/rocket
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <BasicAuthString>
|
||||
@ -240,11 +240,11 @@ DELETE /v2/security/roles/rocket
|
||||
200 Body: (empty)
|
||||
|
||||
|
||||
#### Enable and Disable Security
|
||||
#### Enable and Disable Authentication
|
||||
|
||||
**Get security status**
|
||||
**Get auth status**
|
||||
|
||||
GET /v2/security/enable
|
||||
GET /v2/auth/enable
|
||||
|
||||
Sent Headers:
|
||||
Possible Status Codes:
|
||||
@ -255,9 +255,9 @@ GET /v2/security/enable
|
||||
}
|
||||
|
||||
|
||||
**Enable security**
|
||||
**Enable auth**
|
||||
|
||||
PUT /v2/security/enable
|
||||
PUT /v2/auth/enable
|
||||
|
||||
Sent Headers:
|
||||
Put Body: (empty)
|
||||
@ -266,9 +266,9 @@ PUT /v2/security/enable
|
||||
400 Bad Request (if not a root user)
|
||||
200 Body: (empty)
|
||||
|
||||
**Disable security**
|
||||
**Disable auth**
|
||||
|
||||
DELETE /v2/security/enable
|
||||
DELETE /v2/auth/enable
|
||||
|
||||
Sent Headers:
|
||||
Authorization: Basic <RootAuthString>
|
||||
@ -282,10 +282,10 @@ DELETE /v2/security/enable
|
||||
|
||||
Let's walk through an example to show two tenants (applications, in our case) using etcd permissions.
|
||||
|
||||
### Enable security
|
||||
### Enable auth
|
||||
|
||||
```
|
||||
PUT /v2/security/enable
|
||||
PUT /v2/auth/enable
|
||||
Headers:
|
||||
Put Body:
|
||||
{"user" : "root", "password": "root"}
|
||||
@ -295,7 +295,7 @@ PUT /v2/security/enable
|
||||
### Change root's password
|
||||
|
||||
```
|
||||
PUT /v2/security/users/root
|
||||
PUT /v2/auth/users/root
|
||||
Headers:
|
||||
Authorization: Basic <root:root>
|
||||
Put Body:
|
||||
@ -307,7 +307,7 @@ PUT /v2/security/users/root
|
||||
Create the rocket role fully specified:
|
||||
|
||||
```
|
||||
PUT /v2/security/roles/rocket
|
||||
PUT /v2/auth/roles/rocket
|
||||
Headers:
|
||||
Authorization: Basic <root:betterRootPW!>
|
||||
Body:
|
||||
@ -329,7 +329,7 @@ PUT /v2/security/roles/rocket
|
||||
But let's make fleet just a basic role for now:
|
||||
|
||||
```
|
||||
PUT /v2/security/roles/fleet
|
||||
PUT /v2/auth/roles/fleet
|
||||
Headers:
|
||||
Authorization: Basic <root:betterRootPW!>
|
||||
Body:
|
||||
@ -345,7 +345,7 @@ Well, we finally figured out where we want fleet to live. Let's fix it.
|
||||
|
||||
|
||||
```
|
||||
PUT /v2/security/roles/fleet
|
||||
PUT /v2/auth/roles/fleet
|
||||
Headers:
|
||||
Authorization: Basic <root:betterRootPW!>
|
||||
Put Body:
|
||||
@ -367,7 +367,7 @@ PUT /v2/security/roles/fleet
|
||||
Same as before, let's use rocket all at once and fleet separately
|
||||
|
||||
```
|
||||
PUT /v2/security/users/rocketuser
|
||||
PUT /v2/auth/users/rocketuser
|
||||
Headers:
|
||||
Authorization: Basic <root:betterRootPW!>
|
||||
Body:
|
||||
@ -375,7 +375,7 @@ PUT /v2/security/users/rocketuser
|
||||
```
|
||||
|
||||
```
|
||||
PUT /v2/security/users/fleetuser
|
||||
PUT /v2/auth/users/fleetuser
|
||||
Headers:
|
||||
Authorization: Basic <root:betterRootPW!>
|
||||
Body:
|
||||
@ -387,7 +387,7 @@ PUT /v2/security/users/fleetuser
|
||||
Likewise, let's explicitly grant fleetuser access.
|
||||
|
||||
```
|
||||
PUT /v2/security/users/fleetuser
|
||||
PUT /v2/auth/users/fleetuser
|
||||
Headers:
|
||||
Authorization: Basic <root:betterRootPW!>
|
||||
Body:
|
@ -47,15 +47,15 @@ const (
|
||||
ReadWritePermission
|
||||
)
|
||||
|
||||
// NewSecurityRoleAPI constructs a new SecurityRoleAPI that uses HTTP to
|
||||
// NewAuthRoleAPI constructs a new AuthRoleAPI that uses HTTP to
|
||||
// interact with etcd's role creation and modification features.
|
||||
func NewSecurityRoleAPI(c Client) SecurityRoleAPI {
|
||||
return &httpSecurityRoleAPI{
|
||||
func NewAuthRoleAPI(c Client) AuthRoleAPI {
|
||||
return &httpAuthRoleAPI{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
type SecurityRoleAPI interface {
|
||||
type AuthRoleAPI interface {
|
||||
// Add a role.
|
||||
AddRole(ctx context.Context, role string) error
|
||||
|
||||
@ -75,27 +75,27 @@ type SecurityRoleAPI interface {
|
||||
ListRoles(ctx context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
type httpSecurityRoleAPI struct {
|
||||
type httpAuthRoleAPI struct {
|
||||
client httpClient
|
||||
}
|
||||
|
||||
type securityRoleAPIAction struct {
|
||||
type authRoleAPIAction struct {
|
||||
verb string
|
||||
name string
|
||||
role *Role
|
||||
}
|
||||
|
||||
type securityRoleAPIList struct{}
|
||||
type authRoleAPIList struct{}
|
||||
|
||||
func (list *securityRoleAPIList) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2SecurityURL(ep, "roles", "")
|
||||
func (list *authRoleAPIList) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2AuthURL(ep, "roles", "")
|
||||
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return req
|
||||
}
|
||||
|
||||
func (l *securityRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2SecurityURL(ep, "roles", l.name)
|
||||
func (l *authRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2AuthURL(ep, "roles", l.name)
|
||||
if l.role == nil {
|
||||
req, _ := http.NewRequest(l.verb, u.String(), nil)
|
||||
return req
|
||||
@ -110,8 +110,8 @@ func (l *securityRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
return req
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
|
||||
resp, body, err := r.client.Do(ctx, &securityRoleAPIList{})
|
||||
func (r *httpAuthRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
|
||||
resp, body, err := r.client.Do(ctx, &authRoleAPIList{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -128,31 +128,31 @@ func (r *httpSecurityRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
|
||||
return userList.Roles, nil
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) AddRole(ctx context.Context, rolename string) error {
|
||||
func (r *httpAuthRoleAPI) AddRole(ctx context.Context, rolename string) error {
|
||||
role := &Role{
|
||||
Role: rolename,
|
||||
}
|
||||
return r.addRemoveRole(ctx, &securityRoleAPIAction{
|
||||
return r.addRemoveRole(ctx, &authRoleAPIAction{
|
||||
verb: "PUT",
|
||||
name: rolename,
|
||||
role: role,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) RemoveRole(ctx context.Context, rolename string) error {
|
||||
return r.addRemoveRole(ctx, &securityRoleAPIAction{
|
||||
func (r *httpAuthRoleAPI) RemoveRole(ctx context.Context, rolename string) error {
|
||||
return r.addRemoveRole(ctx, &authRoleAPIAction{
|
||||
verb: "DELETE",
|
||||
name: rolename,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) addRemoveRole(ctx context.Context, req *securityRoleAPIAction) error {
|
||||
func (r *httpAuthRoleAPI) addRemoveRole(ctx context.Context, req *authRoleAPIAction) error {
|
||||
resp, body, err := r.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
|
||||
var sec securityError
|
||||
var sec authError
|
||||
err := json.Unmarshal(body, &sec)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -162,8 +162,8 @@ func (r *httpSecurityRoleAPI) addRemoveRole(ctx context.Context, req *securityRo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) {
|
||||
return r.modRole(ctx, &securityRoleAPIAction{
|
||||
func (r *httpAuthRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) {
|
||||
return r.modRole(ctx, &authRoleAPIAction{
|
||||
verb: "GET",
|
||||
name: rolename,
|
||||
})
|
||||
@ -183,7 +183,7 @@ func buildRWPermission(prefixes []string, permType PermissionType) rwPermission
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
|
||||
func (r *httpAuthRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
|
||||
rwp := buildRWPermission(prefixes, permType)
|
||||
role := &Role{
|
||||
Role: rolename,
|
||||
@ -191,14 +191,14 @@ func (r *httpSecurityRoleAPI) GrantRoleKV(ctx context.Context, rolename string,
|
||||
KV: rwp,
|
||||
},
|
||||
}
|
||||
return r.modRole(ctx, &securityRoleAPIAction{
|
||||
return r.modRole(ctx, &authRoleAPIAction{
|
||||
verb: "PUT",
|
||||
name: rolename,
|
||||
role: role,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
|
||||
func (r *httpAuthRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
|
||||
rwp := buildRWPermission(prefixes, permType)
|
||||
role := &Role{
|
||||
Role: rolename,
|
||||
@ -206,20 +206,20 @@ func (r *httpSecurityRoleAPI) RevokeRoleKV(ctx context.Context, rolename string,
|
||||
KV: rwp,
|
||||
},
|
||||
}
|
||||
return r.modRole(ctx, &securityRoleAPIAction{
|
||||
return r.modRole(ctx, &authRoleAPIAction{
|
||||
verb: "PUT",
|
||||
name: rolename,
|
||||
role: role,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *httpSecurityRoleAPI) modRole(ctx context.Context, req *securityRoleAPIAction) (*Role, error) {
|
||||
func (r *httpAuthRoleAPI) modRole(ctx context.Context, req *authRoleAPIAction) (*Role, error) {
|
||||
resp, body, err := r.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||
var sec securityError
|
||||
var sec authError
|
||||
err := json.Unmarshal(body, &sec)
|
||||
if err != nil {
|
||||
return nil, err
|
@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
defaultV2SecurityPrefix = "/v2/security"
|
||||
defaultV2AuthPrefix = "/v2/auth"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
@ -36,50 +36,50 @@ type User struct {
|
||||
Revoke []string `json:"revoke,omitempty"`
|
||||
}
|
||||
|
||||
func v2SecurityURL(ep url.URL, action string, name string) *url.URL {
|
||||
func v2AuthURL(ep url.URL, action string, name string) *url.URL {
|
||||
if name != "" {
|
||||
ep.Path = path.Join(ep.Path, defaultV2SecurityPrefix, action, name)
|
||||
ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action, name)
|
||||
return &ep
|
||||
}
|
||||
ep.Path = path.Join(ep.Path, defaultV2SecurityPrefix, action)
|
||||
ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action)
|
||||
return &ep
|
||||
}
|
||||
|
||||
// NewSecurityAPI constructs a new SecurityAPI that uses HTTP to
|
||||
// interact with etcd's general security features.
|
||||
func NewSecurityAPI(c Client) SecurityAPI {
|
||||
return &httpSecurityAPI{
|
||||
// NewAuthAPI constructs a new AuthAPI that uses HTTP to
|
||||
// interact with etcd's general auth features.
|
||||
func NewAuthAPI(c Client) AuthAPI {
|
||||
return &httpAuthAPI{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
type SecurityAPI interface {
|
||||
// Enable security.
|
||||
type AuthAPI interface {
|
||||
// Enable auth.
|
||||
Enable(ctx context.Context) error
|
||||
|
||||
// Disable security.
|
||||
// Disable auth.
|
||||
Disable(ctx context.Context) error
|
||||
}
|
||||
|
||||
type httpSecurityAPI struct {
|
||||
type httpAuthAPI struct {
|
||||
client httpClient
|
||||
}
|
||||
|
||||
func (s *httpSecurityAPI) Enable(ctx context.Context) error {
|
||||
return s.enableDisable(ctx, &securityAPIAction{"PUT"})
|
||||
func (s *httpAuthAPI) Enable(ctx context.Context) error {
|
||||
return s.enableDisable(ctx, &authAPIAction{"PUT"})
|
||||
}
|
||||
|
||||
func (s *httpSecurityAPI) Disable(ctx context.Context) error {
|
||||
return s.enableDisable(ctx, &securityAPIAction{"DELETE"})
|
||||
func (s *httpAuthAPI) Disable(ctx context.Context) error {
|
||||
return s.enableDisable(ctx, &authAPIAction{"DELETE"})
|
||||
}
|
||||
|
||||
func (s *httpSecurityAPI) enableDisable(ctx context.Context, req httpAction) error {
|
||||
func (s *httpAuthAPI) enableDisable(ctx context.Context, req httpAction) error {
|
||||
resp, body, err := s.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
|
||||
var sec securityError
|
||||
var sec authError
|
||||
err := json.Unmarshal(body, &sec)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -89,34 +89,34 @@ func (s *httpSecurityAPI) enableDisable(ctx context.Context, req httpAction) err
|
||||
return nil
|
||||
}
|
||||
|
||||
type securityAPIAction struct {
|
||||
type authAPIAction struct {
|
||||
verb string
|
||||
}
|
||||
|
||||
func (l *securityAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2SecurityURL(ep, "enable", "")
|
||||
func (l *authAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2AuthURL(ep, "enable", "")
|
||||
req, _ := http.NewRequest(l.verb, u.String(), nil)
|
||||
return req
|
||||
}
|
||||
|
||||
type securityError struct {
|
||||
type authError struct {
|
||||
Message string `json:"message"`
|
||||
Code int `json:"-"`
|
||||
}
|
||||
|
||||
func (e securityError) Error() string {
|
||||
func (e authError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// NewSecurityUserAPI constructs a new SecurityUserAPI that uses HTTP to
|
||||
// NewAuthUserAPI constructs a new AuthUserAPI that uses HTTP to
|
||||
// interact with etcd's user creation and modification features.
|
||||
func NewSecurityUserAPI(c Client) SecurityUserAPI {
|
||||
return &httpSecurityUserAPI{
|
||||
func NewAuthUserAPI(c Client) AuthUserAPI {
|
||||
return &httpAuthUserAPI{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
type SecurityUserAPI interface {
|
||||
type AuthUserAPI interface {
|
||||
// Add a user.
|
||||
AddUser(ctx context.Context, username string, password string) error
|
||||
|
||||
@ -139,27 +139,27 @@ type SecurityUserAPI interface {
|
||||
ListUsers(ctx context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
type httpSecurityUserAPI struct {
|
||||
type httpAuthUserAPI struct {
|
||||
client httpClient
|
||||
}
|
||||
|
||||
type securityUserAPIAction struct {
|
||||
type authUserAPIAction struct {
|
||||
verb string
|
||||
username string
|
||||
user *User
|
||||
}
|
||||
|
||||
type securityUserAPIList struct{}
|
||||
type authUserAPIList struct{}
|
||||
|
||||
func (list *securityUserAPIList) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2SecurityURL(ep, "users", "")
|
||||
func (list *authUserAPIList) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2AuthURL(ep, "users", "")
|
||||
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return req
|
||||
}
|
||||
|
||||
func (l *securityUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2SecurityURL(ep, "users", l.username)
|
||||
func (l *authUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
u := v2AuthURL(ep, "users", l.username)
|
||||
if l.user == nil {
|
||||
req, _ := http.NewRequest(l.verb, u.String(), nil)
|
||||
return req
|
||||
@ -174,13 +174,13 @@ func (l *securityUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
return req
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) ListUsers(ctx context.Context) ([]string, error) {
|
||||
resp, body, err := u.client.Do(ctx, &securityUserAPIList{})
|
||||
func (u *httpAuthUserAPI) ListUsers(ctx context.Context) ([]string, error) {
|
||||
resp, body, err := u.client.Do(ctx, &authUserAPIList{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||
var sec securityError
|
||||
var sec authError
|
||||
err := json.Unmarshal(body, &sec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -197,32 +197,32 @@ func (u *httpSecurityUserAPI) ListUsers(ctx context.Context) ([]string, error) {
|
||||
return userList.Users, nil
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) AddUser(ctx context.Context, username string, password string) error {
|
||||
func (u *httpAuthUserAPI) AddUser(ctx context.Context, username string, password string) error {
|
||||
user := &User{
|
||||
User: username,
|
||||
Password: password,
|
||||
}
|
||||
return u.addRemoveUser(ctx, &securityUserAPIAction{
|
||||
return u.addRemoveUser(ctx, &authUserAPIAction{
|
||||
verb: "PUT",
|
||||
username: username,
|
||||
user: user,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) RemoveUser(ctx context.Context, username string) error {
|
||||
return u.addRemoveUser(ctx, &securityUserAPIAction{
|
||||
func (u *httpAuthUserAPI) RemoveUser(ctx context.Context, username string) error {
|
||||
return u.addRemoveUser(ctx, &authUserAPIAction{
|
||||
verb: "DELETE",
|
||||
username: username,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) addRemoveUser(ctx context.Context, req *securityUserAPIAction) error {
|
||||
func (u *httpAuthUserAPI) addRemoveUser(ctx context.Context, req *authUserAPIAction) error {
|
||||
resp, body, err := u.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
|
||||
var sec securityError
|
||||
var sec authError
|
||||
err := json.Unmarshal(body, &sec)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -232,56 +232,56 @@ func (u *httpSecurityUserAPI) addRemoveUser(ctx context.Context, req *securityUs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) GetUser(ctx context.Context, username string) (*User, error) {
|
||||
return u.modUser(ctx, &securityUserAPIAction{
|
||||
func (u *httpAuthUserAPI) GetUser(ctx context.Context, username string) (*User, error) {
|
||||
return u.modUser(ctx, &authUserAPIAction{
|
||||
verb: "GET",
|
||||
username: username,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) {
|
||||
func (u *httpAuthUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) {
|
||||
user := &User{
|
||||
User: username,
|
||||
Grant: roles,
|
||||
}
|
||||
return u.modUser(ctx, &securityUserAPIAction{
|
||||
return u.modUser(ctx, &authUserAPIAction{
|
||||
verb: "PUT",
|
||||
username: username,
|
||||
user: user,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) {
|
||||
func (u *httpAuthUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) {
|
||||
user := &User{
|
||||
User: username,
|
||||
Revoke: roles,
|
||||
}
|
||||
return u.modUser(ctx, &securityUserAPIAction{
|
||||
return u.modUser(ctx, &authUserAPIAction{
|
||||
verb: "PUT",
|
||||
username: username,
|
||||
user: user,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) {
|
||||
func (u *httpAuthUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) {
|
||||
user := &User{
|
||||
User: username,
|
||||
Password: password,
|
||||
}
|
||||
return u.modUser(ctx, &securityUserAPIAction{
|
||||
return u.modUser(ctx, &authUserAPIAction{
|
||||
verb: "PUT",
|
||||
username: username,
|
||||
user: user,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *httpSecurityUserAPI) modUser(ctx context.Context, req *securityUserAPIAction) (*User, error) {
|
||||
func (u *httpAuthUserAPI) modUser(ctx context.Context, req *authUserAPIAction) (*User, error) {
|
||||
resp, body, err := u.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||
var sec securityError
|
||||
var sec authError
|
||||
err := json.Unmarshal(body, &sec)
|
||||
if err != nil {
|
||||
return nil, err
|
@ -24,49 +24,49 @@ import (
|
||||
"github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
func NewSecurityCommands() cli.Command {
|
||||
func NewAuthCommands() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "security",
|
||||
Usage: "overall security controls",
|
||||
Name: "auth",
|
||||
Usage: "overall auth controls",
|
||||
Subcommands: []cli.Command{
|
||||
cli.Command{
|
||||
Name: "enable",
|
||||
Usage: "enable security access controls",
|
||||
Action: actionSecurityEnable,
|
||||
Usage: "enable auth access controls",
|
||||
Action: actionAuthEnable,
|
||||
},
|
||||
cli.Command{
|
||||
Name: "disable",
|
||||
Usage: "disable security access controls",
|
||||
Action: actionSecurityDisable,
|
||||
Usage: "disable auth access controls",
|
||||
Action: actionAuthDisable,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func actionSecurityEnable(c *cli.Context) {
|
||||
securityEnableDisable(c, true)
|
||||
func actionAuthEnable(c *cli.Context) {
|
||||
authEnableDisable(c, true)
|
||||
}
|
||||
|
||||
func actionSecurityDisable(c *cli.Context) {
|
||||
securityEnableDisable(c, false)
|
||||
func actionAuthDisable(c *cli.Context) {
|
||||
authEnableDisable(c, false)
|
||||
}
|
||||
|
||||
func mustNewSecurityAPI(c *cli.Context) client.SecurityAPI {
|
||||
func mustNewAuthAPI(c *cli.Context) client.AuthAPI {
|
||||
hc := mustNewClient(c)
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
}
|
||||
|
||||
return client.NewSecurityAPI(hc)
|
||||
return client.NewAuthAPI(hc)
|
||||
}
|
||||
|
||||
func securityEnableDisable(c *cli.Context, enable bool) {
|
||||
func authEnableDisable(c *cli.Context, enable bool) {
|
||||
if len(c.Args()) != 0 {
|
||||
fmt.Fprintln(os.Stderr, "No arguments accepted")
|
||||
os.Exit(1)
|
||||
}
|
||||
s := mustNewSecurityAPI(c)
|
||||
s := mustNewAuthAPI(c)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
|
||||
var err error
|
||||
if enable {
|
||||
@ -80,8 +80,8 @@ func securityEnableDisable(c *cli.Context, enable bool) {
|
||||
os.Exit(1)
|
||||
}
|
||||
if enable {
|
||||
fmt.Println("Security Enabled")
|
||||
fmt.Println("Authentication Enabled")
|
||||
} else {
|
||||
fmt.Println("Security Disabled")
|
||||
fmt.Println("Authentication Disabled")
|
||||
}
|
||||
}
|
@ -76,14 +76,14 @@ func NewRoleCommands() cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func mustNewSecurityRoleAPI(c *cli.Context) client.SecurityRoleAPI {
|
||||
func mustNewAuthRoleAPI(c *cli.Context) client.AuthRoleAPI {
|
||||
hc := mustNewClient(c)
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
}
|
||||
|
||||
return client.NewSecurityRoleAPI(hc)
|
||||
return client.NewAuthRoleAPI(hc)
|
||||
}
|
||||
|
||||
func actionRoleList(c *cli.Context) {
|
||||
@ -91,7 +91,7 @@ func actionRoleList(c *cli.Context) {
|
||||
fmt.Fprintln(os.Stderr, "No arguments accepted")
|
||||
os.Exit(1)
|
||||
}
|
||||
r := mustNewSecurityRoleAPI(c)
|
||||
r := mustNewAuthRoleAPI(c)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
|
||||
roles, err := r.ListRoles(ctx)
|
||||
cancel()
|
||||
@ -228,7 +228,7 @@ func actionRoleGet(c *cli.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func mustRoleAPIAndName(c *cli.Context) (client.SecurityRoleAPI, string) {
|
||||
func mustRoleAPIAndName(c *cli.Context) (client.AuthRoleAPI, string) {
|
||||
args := c.Args()
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintln(os.Stderr, "Please provide a role name")
|
||||
@ -236,6 +236,6 @@ func mustRoleAPIAndName(c *cli.Context) (client.SecurityRoleAPI, string) {
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
api := mustNewSecurityRoleAPI(c)
|
||||
api := mustNewAuthRoleAPI(c)
|
||||
return api, name
|
||||
}
|
||||
|
@ -73,14 +73,14 @@ func NewUserCommands() cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func mustNewSecurityUserAPI(c *cli.Context) client.SecurityUserAPI {
|
||||
func mustNewAuthUserAPI(c *cli.Context) client.AuthUserAPI {
|
||||
hc := mustNewClient(c)
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
}
|
||||
|
||||
return client.NewSecurityUserAPI(hc)
|
||||
return client.NewAuthUserAPI(hc)
|
||||
}
|
||||
|
||||
func actionUserList(c *cli.Context) {
|
||||
@ -88,7 +88,7 @@ func actionUserList(c *cli.Context) {
|
||||
fmt.Fprintln(os.Stderr, "No arguments accepted")
|
||||
os.Exit(1)
|
||||
}
|
||||
u := mustNewSecurityUserAPI(c)
|
||||
u := mustNewAuthUserAPI(c)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
|
||||
users, err := u.ListUsers(ctx)
|
||||
cancel()
|
||||
@ -229,14 +229,14 @@ func actionUserGet(c *cli.Context) {
|
||||
|
||||
}
|
||||
|
||||
func mustUserAPIAndName(c *cli.Context) (client.SecurityUserAPI, string) {
|
||||
func mustUserAPIAndName(c *cli.Context) (client.AuthUserAPI, string) {
|
||||
args := c.Args()
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintln(os.Stderr, "Please provide a username")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
api := mustNewSecurityUserAPI(c)
|
||||
api := mustNewAuthUserAPI(c)
|
||||
username := args[0]
|
||||
return api, username
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func main() {
|
||||
command.NewImportSnapCommand(),
|
||||
command.NewUserCommands(),
|
||||
command.NewRoleCommands(),
|
||||
command.NewSecurityCommands(),
|
||||
command.NewAuthCommands(),
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package security
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -44,7 +44,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver", "security")
|
||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver", "auth")
|
||||
)
|
||||
|
||||
var rootRole = Role{
|
||||
@ -108,11 +108,11 @@ type Error struct {
|
||||
func (se Error) Error() string { return se.errmsg }
|
||||
|
||||
func mergeErr(s string, v ...interface{}) Error {
|
||||
return Error{fmt.Sprintf("security: "+s, v...)}
|
||||
return Error{fmt.Sprintf("auth: "+s, v...)}
|
||||
}
|
||||
|
||||
func securityErr(s string, v ...interface{}) Error {
|
||||
return Error{fmt.Sprintf("security: "+s, v...)}
|
||||
func authErr(s string, v ...interface{}) Error {
|
||||
return Error{fmt.Sprintf("auth: "+s, v...)}
|
||||
}
|
||||
|
||||
func NewStore(server doer, timeout time.Duration) *Store {
|
||||
@ -147,7 +147,7 @@ func (s *Store) GetUser(name string) (User, error) {
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeKeyNotFound {
|
||||
return User{}, securityErr("User %s does not exist.", name)
|
||||
return User{}, authErr("User %s does not exist.", name)
|
||||
}
|
||||
}
|
||||
return User{}, err
|
||||
@ -197,7 +197,7 @@ func (s *Store) CreateUser(user User) (User, error) {
|
||||
|
||||
func (s *Store) createUserInternal(user User) (User, error) {
|
||||
if user.Password == "" {
|
||||
return user, securityErr("Cannot create user %s with an empty password", user.User)
|
||||
return user, authErr("Cannot create user %s with an empty password", user.User)
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
@ -209,7 +209,7 @@ func (s *Store) createUserInternal(user User) (User, error) {
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeNodeExist {
|
||||
return user, securityErr("User %s already exists.", user.User)
|
||||
return user, authErr("User %s already exists.", user.User)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -217,14 +217,14 @@ func (s *Store) createUserInternal(user User) (User, error) {
|
||||
}
|
||||
|
||||
func (s *Store) DeleteUser(name string) error {
|
||||
if s.SecurityEnabled() && name == "root" {
|
||||
return securityErr("Cannot delete root user while security is enabled.")
|
||||
if s.AuthEnabled() && name == "root" {
|
||||
return authErr("Cannot delete root user while auth is enabled.")
|
||||
}
|
||||
_, err := s.deleteResource("/users/" + name)
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeKeyNotFound {
|
||||
return securityErr("User %s does not exist", name)
|
||||
return authErr("User %s does not exist", name)
|
||||
}
|
||||
}
|
||||
return err
|
||||
@ -238,7 +238,7 @@ func (s *Store) UpdateUser(user User) (User, error) {
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeKeyNotFound {
|
||||
return user, securityErr("User %s doesn't exist.", user.User)
|
||||
return user, authErr("User %s doesn't exist.", user.User)
|
||||
}
|
||||
}
|
||||
return old, err
|
||||
@ -249,9 +249,9 @@ func (s *Store) UpdateUser(user User) (User, error) {
|
||||
}
|
||||
if reflect.DeepEqual(old, newUser) {
|
||||
if user.Revoke != nil || user.Grant != nil {
|
||||
return old, securityErr("User not updated. Grant/Revoke lists didn't match any current roles.")
|
||||
return old, authErr("User not updated. Grant/Revoke lists didn't match any current roles.")
|
||||
}
|
||||
return old, securityErr("User not updated. Use Grant/Revoke/Password to update the user.")
|
||||
return old, authErr("User not updated. Use Grant/Revoke/Password to update the user.")
|
||||
}
|
||||
_, err = s.updateResource("/users/"+user.User, newUser)
|
||||
if err == nil {
|
||||
@ -287,7 +287,7 @@ func (s *Store) GetRole(name string) (Role, error) {
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeKeyNotFound {
|
||||
return Role{}, securityErr("Role %s does not exist.", name)
|
||||
return Role{}, authErr("Role %s does not exist.", name)
|
||||
}
|
||||
}
|
||||
return Role{}, err
|
||||
@ -313,13 +313,13 @@ func (s *Store) CreateOrUpdateRole(r Role) (role Role, created bool, err error)
|
||||
|
||||
func (s *Store) CreateRole(role Role) error {
|
||||
if role.Role == RootRoleName {
|
||||
return securityErr("Cannot modify role %s: is root role.", role.Role)
|
||||
return authErr("Cannot modify role %s: is root role.", role.Role)
|
||||
}
|
||||
_, err := s.createResource("/roles/"+role.Role, role)
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeNodeExist {
|
||||
return securityErr("Role %s already exists.", role.Role)
|
||||
return authErr("Role %s already exists.", role.Role)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -331,13 +331,13 @@ func (s *Store) CreateRole(role Role) error {
|
||||
|
||||
func (s *Store) DeleteRole(name string) error {
|
||||
if name == RootRoleName {
|
||||
return securityErr("Cannot modify role %s: is superuser role.", name)
|
||||
return authErr("Cannot modify role %s: is superuser role.", name)
|
||||
}
|
||||
_, err := s.deleteResource("/roles/" + name)
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeKeyNotFound {
|
||||
return securityErr("Role %s doesn't exist.", name)
|
||||
return authErr("Role %s doesn't exist.", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -352,7 +352,7 @@ func (s *Store) UpdateRole(role Role) (Role, error) {
|
||||
if err != nil {
|
||||
if e, ok := err.(*etcderr.Error); ok {
|
||||
if e.ErrorCode == etcderr.EcodeKeyNotFound {
|
||||
return role, securityErr("Role %s doesn't exist.", role.Role)
|
||||
return role, authErr("Role %s doesn't exist.", role.Role)
|
||||
}
|
||||
}
|
||||
return old, err
|
||||
@ -363,9 +363,9 @@ func (s *Store) UpdateRole(role Role) (Role, error) {
|
||||
}
|
||||
if reflect.DeepEqual(old, newRole) {
|
||||
if role.Revoke != nil || role.Grant != nil {
|
||||
return old, securityErr("Role not updated. Grant/Revoke lists didn't match any current permissions.")
|
||||
return old, authErr("Role not updated. Grant/Revoke lists didn't match any current permissions.")
|
||||
}
|
||||
return old, securityErr("Role not updated. Use Grant/Revoke to update the role.")
|
||||
return old, authErr("Role not updated. Use Grant/Revoke to update the role.")
|
||||
}
|
||||
_, err = s.updateResource("/roles/"+role.Role, newRole)
|
||||
if err == nil {
|
||||
@ -374,45 +374,45 @@ func (s *Store) UpdateRole(role Role) (Role, error) {
|
||||
return newRole, err
|
||||
}
|
||||
|
||||
func (s *Store) SecurityEnabled() bool {
|
||||
return s.detectSecurity()
|
||||
func (s *Store) AuthEnabled() bool {
|
||||
return s.detectAuth()
|
||||
}
|
||||
|
||||
func (s *Store) EnableSecurity() error {
|
||||
if s.SecurityEnabled() {
|
||||
return securityErr("already enabled")
|
||||
func (s *Store) EnableAuth() error {
|
||||
if s.AuthEnabled() {
|
||||
return authErr("already enabled")
|
||||
}
|
||||
_, err := s.GetUser("root")
|
||||
if err != nil {
|
||||
return securityErr("No root user available, please create one")
|
||||
return authErr("No root user available, please create one")
|
||||
}
|
||||
_, err = s.GetRole(GuestRoleName)
|
||||
if err != nil {
|
||||
plog.Printf("no guest role access found, creating default")
|
||||
err := s.CreateRole(guestRole)
|
||||
if err != nil {
|
||||
plog.Errorf("error creating guest role. aborting security enable.")
|
||||
plog.Errorf("error creating guest role. aborting auth enable.")
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = s.enableSecurity()
|
||||
err = s.enableAuth()
|
||||
if err == nil {
|
||||
plog.Noticef("security: enabled security")
|
||||
plog.Noticef("auth: enabled auth")
|
||||
} else {
|
||||
plog.Errorf("error enabling security (%v)", err)
|
||||
plog.Errorf("error enabling auth (%v)", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) DisableSecurity() error {
|
||||
if !s.SecurityEnabled() {
|
||||
return securityErr("already disabled")
|
||||
func (s *Store) DisableAuth() error {
|
||||
if !s.AuthEnabled() {
|
||||
return authErr("already disabled")
|
||||
}
|
||||
err := s.disableSecurity()
|
||||
err := s.disableAuth()
|
||||
if err == nil {
|
||||
plog.Noticef("security: disabled security")
|
||||
plog.Noticef("auth: disabled auth")
|
||||
} else {
|
||||
plog.Errorf("error disabling security (%v)", err)
|
||||
plog.Errorf("error disabling auth (%v)", err)
|
||||
}
|
||||
return err
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package security
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -24,7 +24,7 @@ import (
|
||||
"github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
)
|
||||
|
||||
func (s *Store) ensureSecurityDirectories() error {
|
||||
func (s *Store) ensureAuthDirectories() error {
|
||||
if s.ensuredOnce {
|
||||
return nil
|
||||
}
|
||||
@ -45,7 +45,7 @@ func (s *Store) ensureSecurityDirectories() error {
|
||||
continue
|
||||
}
|
||||
}
|
||||
plog.Errorf("failed to create security directories in the store (%v)", err)
|
||||
plog.Errorf("failed to create auth directories in the store (%v)", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -72,16 +72,16 @@ func (s *Store) ensureSecurityDirectories() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) enableSecurity() error {
|
||||
func (s *Store) enableAuth() error {
|
||||
_, err := s.updateResource("/enabled", true)
|
||||
return err
|
||||
}
|
||||
func (s *Store) disableSecurity() error {
|
||||
func (s *Store) disableAuth() error {
|
||||
_, err := s.updateResource("/enabled", false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) detectSecurity() bool {
|
||||
func (s *Store) detectAuth() bool {
|
||||
if s.server == nil {
|
||||
return false
|
||||
}
|
||||
@ -92,7 +92,7 @@ func (s *Store) detectSecurity() bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
plog.Errorf("failed to detect security settings (%s)", err)
|
||||
plog.Errorf("failed to detect auth settings (%s)", err)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ func (s *Store) createResource(res string, value interface{}) (etcdserver.Respon
|
||||
return s.setResource(res, value, false)
|
||||
}
|
||||
func (s *Store) setResource(res string, value interface{}, prevexist bool) (etcdserver.Response, error) {
|
||||
err := s.ensureSecurityDirectories()
|
||||
err := s.ensureAuthDirectories()
|
||||
if err != nil {
|
||||
return etcdserver.Response{}, err
|
||||
}
|
||||
@ -145,7 +145,7 @@ func (s *Store) setResource(res string, value interface{}, prevexist bool) (etcd
|
||||
}
|
||||
|
||||
func (s *Store) deleteResource(res string) (etcdserver.Response, error) {
|
||||
err := s.ensureSecurityDirectories()
|
||||
err := s.ensureAuthDirectories()
|
||||
if err != nil {
|
||||
return etcdserver.Response{}, err
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package security
|
||||
package auth
|
||||
|
||||
import (
|
||||
"reflect"
|
@ -14,14 +14,14 @@ import (
|
||||
type capability string
|
||||
|
||||
const (
|
||||
securityCapability capability = "security"
|
||||
authCapability capability = "auth"
|
||||
)
|
||||
|
||||
var (
|
||||
// capabilityMap is a static map of version to capability map.
|
||||
// the base capabilities is the set of capability 2.0 supports.
|
||||
capabilityMaps = map[string]map[capability]bool{
|
||||
"2.1.0": {securityCapability: true},
|
||||
"2.1.0": {authCapability: true},
|
||||
}
|
||||
|
||||
enableMapMu sync.Mutex
|
||||
|
@ -32,9 +32,9 @@ import (
|
||||
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
"github.com/coreos/etcd/etcdserver"
|
||||
"github.com/coreos/etcd/etcdserver/auth"
|
||||
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
|
||||
"github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/etcdserver/security"
|
||||
"github.com/coreos/etcd/etcdserver/stats"
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
"github.com/coreos/etcd/raft"
|
||||
@ -43,7 +43,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
securityPrefix = "/v2/security"
|
||||
authPrefix = "/v2/auth"
|
||||
keysPrefix = "/v2/keys"
|
||||
deprecatedMachinesPrefix = "/v2/machines"
|
||||
membersPrefix = "/v2/members"
|
||||
@ -58,7 +58,7 @@ const (
|
||||
func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
|
||||
go capabilityLoop(server)
|
||||
|
||||
sec := security.NewStore(server, defaultServerTimeout)
|
||||
sec := auth.NewStore(server, defaultServerTimeout)
|
||||
|
||||
kh := &keysHandler{
|
||||
sec: sec,
|
||||
@ -83,7 +83,7 @@ func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
|
||||
cluster: server.Cluster(),
|
||||
}
|
||||
|
||||
sech := &securityHandler{
|
||||
sech := &authHandler{
|
||||
sec: sec,
|
||||
cluster: server.Cluster(),
|
||||
}
|
||||
@ -102,13 +102,13 @@ func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
|
||||
mux.Handle(membersPrefix, mh)
|
||||
mux.Handle(membersPrefix+"/", mh)
|
||||
mux.Handle(deprecatedMachinesPrefix, dmh)
|
||||
handleSecurity(mux, sech)
|
||||
handleAuth(mux, sech)
|
||||
|
||||
return requestLogger(mux)
|
||||
}
|
||||
|
||||
type keysHandler struct {
|
||||
sec *security.Store
|
||||
sec *auth.Store
|
||||
server etcdserver.Server
|
||||
cluster etcdserver.Cluster
|
||||
timer etcdserver.RaftTimer
|
||||
@ -170,7 +170,7 @@ func (h *deprecatedMachinesHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
|
||||
type membersHandler struct {
|
||||
sec *security.Store
|
||||
sec *auth.Store
|
||||
server etcdserver.Server
|
||||
cluster etcdserver.Cluster
|
||||
clock clockwork.Clock
|
||||
|
@ -21,29 +21,29 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/etcdserver"
|
||||
"github.com/coreos/etcd/etcdserver/auth"
|
||||
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
|
||||
"github.com/coreos/etcd/etcdserver/security"
|
||||
"github.com/coreos/etcd/pkg/netutil"
|
||||
)
|
||||
|
||||
type securityHandler struct {
|
||||
sec *security.Store
|
||||
type authHandler struct {
|
||||
sec *auth.Store
|
||||
cluster etcdserver.Cluster
|
||||
}
|
||||
|
||||
func hasWriteRootAccess(sec *security.Store, r *http.Request) bool {
|
||||
func hasWriteRootAccess(sec *auth.Store, r *http.Request) bool {
|
||||
if r.Method == "GET" || r.Method == "HEAD" {
|
||||
return true
|
||||
}
|
||||
return hasRootAccess(sec, r)
|
||||
}
|
||||
|
||||
func hasRootAccess(sec *security.Store, r *http.Request) bool {
|
||||
func hasRootAccess(sec *auth.Store, r *http.Request) bool {
|
||||
if sec == nil {
|
||||
// No store means no security available, eg, tests.
|
||||
// No store means no auth available, eg, tests.
|
||||
return true
|
||||
}
|
||||
if !sec.SecurityEnabled() {
|
||||
if !sec.AuthEnabled() {
|
||||
return true
|
||||
}
|
||||
username, password, ok := netutil.BasicAuth(r)
|
||||
@ -56,24 +56,24 @@ func hasRootAccess(sec *security.Store, r *http.Request) bool {
|
||||
}
|
||||
ok = rootUser.CheckPassword(password)
|
||||
if !ok {
|
||||
plog.Warningf("security: wrong password for user %s", username)
|
||||
plog.Warningf("auth: wrong password for user %s", username)
|
||||
return false
|
||||
}
|
||||
for _, role := range rootUser.Roles {
|
||||
if role == security.RootRoleName {
|
||||
if role == auth.RootRoleName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
plog.Warningf("security: user %s does not have the %s role for resource %s.", username, security.RootRoleName, r.URL.Path)
|
||||
plog.Warningf("auth: user %s does not have the %s role for resource %s.", username, auth.RootRoleName, r.URL.Path)
|
||||
return false
|
||||
}
|
||||
|
||||
func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string, recursive bool) bool {
|
||||
func hasKeyPrefixAccess(sec *auth.Store, r *http.Request, key string, recursive bool) bool {
|
||||
if sec == nil {
|
||||
// No store means no security available, eg, tests.
|
||||
// No store means no auth available, eg, tests.
|
||||
return true
|
||||
}
|
||||
if !sec.SecurityEnabled() {
|
||||
if !sec.AuthEnabled() {
|
||||
return true
|
||||
}
|
||||
username, password, ok := netutil.BasicAuth(r)
|
||||
@ -82,12 +82,12 @@ func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string, recurs
|
||||
}
|
||||
user, err := sec.GetUser(username)
|
||||
if err != nil {
|
||||
plog.Warningf("security: no such user: %s.", username)
|
||||
plog.Warningf("auth: no such user: %s.", username)
|
||||
return false
|
||||
}
|
||||
authAsUser := user.CheckPassword(password)
|
||||
if !authAsUser {
|
||||
plog.Warningf("security: incorrect password for user: %s.", username)
|
||||
plog.Warningf("auth: incorrect password for user: %s.", username)
|
||||
return false
|
||||
}
|
||||
writeAccess := r.Method != "GET" && r.Method != "HEAD"
|
||||
@ -101,20 +101,20 @@ func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string, recurs
|
||||
}
|
||||
return role.HasKeyAccess(key, writeAccess)
|
||||
}
|
||||
plog.Warningf("security: invalid access for user %s on key %s.", username, key)
|
||||
plog.Warningf("auth: invalid access for user %s on key %s.", username, key)
|
||||
return false
|
||||
}
|
||||
|
||||
func hasGuestAccess(sec *security.Store, r *http.Request, key string) bool {
|
||||
func hasGuestAccess(sec *auth.Store, r *http.Request, key string) bool {
|
||||
writeAccess := r.Method != "GET" && r.Method != "HEAD"
|
||||
role, err := sec.GetRole(security.GuestRoleName)
|
||||
role, err := sec.GetRole(auth.GuestRoleName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if role.HasKeyAccess(key, writeAccess) {
|
||||
return true
|
||||
}
|
||||
plog.Warningf("security: invalid access for unauthenticated user on resource %s.", key)
|
||||
plog.Warningf("auth: invalid access for unauthenticated user on resource %s.", key)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -123,15 +123,15 @@ func writeNoAuth(w http.ResponseWriter) {
|
||||
herr.WriteTo(w)
|
||||
}
|
||||
|
||||
func handleSecurity(mux *http.ServeMux, sh *securityHandler) {
|
||||
mux.HandleFunc(securityPrefix+"/roles", capabilityHandler(securityCapability, sh.baseRoles))
|
||||
mux.HandleFunc(securityPrefix+"/roles/", capabilityHandler(securityCapability, sh.handleRoles))
|
||||
mux.HandleFunc(securityPrefix+"/users", capabilityHandler(securityCapability, sh.baseUsers))
|
||||
mux.HandleFunc(securityPrefix+"/users/", capabilityHandler(securityCapability, sh.handleUsers))
|
||||
mux.HandleFunc(securityPrefix+"/enable", capabilityHandler(securityCapability, sh.enableDisable))
|
||||
func handleAuth(mux *http.ServeMux, sh *authHandler) {
|
||||
mux.HandleFunc(authPrefix+"/roles", capabilityHandler(authCapability, sh.baseRoles))
|
||||
mux.HandleFunc(authPrefix+"/roles/", capabilityHandler(authCapability, sh.handleRoles))
|
||||
mux.HandleFunc(authPrefix+"/users", capabilityHandler(authCapability, sh.baseUsers))
|
||||
mux.HandleFunc(authPrefix+"/users/", capabilityHandler(authCapability, sh.handleUsers))
|
||||
mux.HandleFunc(authPrefix+"/enable", capabilityHandler(authCapability, sh.enableDisable))
|
||||
}
|
||||
|
||||
func (sh *securityHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
|
||||
func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
|
||||
if !allowMethod(w, r.Method, "GET") {
|
||||
return
|
||||
}
|
||||
@ -160,8 +160,8 @@ func (sh *securityHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *securityHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
|
||||
subpath := path.Clean(r.URL.Path[len(securityPrefix):])
|
||||
func (sh *authHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
|
||||
subpath := path.Clean(r.URL.Path[len(authPrefix):])
|
||||
// Split "/roles/rolename/command".
|
||||
// First item is an empty string, second is "roles"
|
||||
pieces := strings.Split(subpath, "/")
|
||||
@ -176,7 +176,7 @@ func (sh *securityHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
|
||||
sh.forRole(w, r, pieces[2])
|
||||
}
|
||||
|
||||
func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role string) {
|
||||
func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role string) {
|
||||
if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
|
||||
return
|
||||
}
|
||||
@ -201,7 +201,7 @@ func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role
|
||||
}
|
||||
return
|
||||
case "PUT":
|
||||
var in security.Role
|
||||
var in auth.Role
|
||||
err := json.NewDecoder(r.Body).Decode(&in)
|
||||
if err != nil {
|
||||
writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
|
||||
@ -236,7 +236,7 @@ func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *securityHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
|
||||
func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
|
||||
if !allowMethod(w, r.Method, "GET") {
|
||||
return
|
||||
}
|
||||
@ -265,8 +265,8 @@ func (sh *securityHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *securityHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
subpath := path.Clean(r.URL.Path[len(securityPrefix):])
|
||||
func (sh *authHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
subpath := path.Clean(r.URL.Path[len(authPrefix):])
|
||||
// Split "/users/username".
|
||||
// First item is an empty string, second is "users"
|
||||
pieces := strings.Split(subpath, "/")
|
||||
@ -281,7 +281,7 @@ func (sh *securityHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
sh.forUser(w, r, pieces[2])
|
||||
}
|
||||
|
||||
func (sh *securityHandler) forUser(w http.ResponseWriter, r *http.Request, user string) {
|
||||
func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user string) {
|
||||
if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
|
||||
return
|
||||
}
|
||||
@ -308,7 +308,7 @@ func (sh *securityHandler) forUser(w http.ResponseWriter, r *http.Request, user
|
||||
}
|
||||
return
|
||||
case "PUT":
|
||||
var u security.User
|
||||
var u auth.User
|
||||
err := json.NewDecoder(r.Body).Decode(&u)
|
||||
if err != nil {
|
||||
writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
|
||||
@ -351,7 +351,7 @@ type enabled struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (sh *securityHandler) enableDisable(w http.ResponseWriter, r *http.Request) {
|
||||
func (sh *authHandler) enableDisable(w http.ResponseWriter, r *http.Request) {
|
||||
if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
|
||||
return
|
||||
}
|
||||
@ -361,22 +361,22 @@ func (sh *securityHandler) enableDisable(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String())
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
isEnabled := sh.sec.SecurityEnabled()
|
||||
isEnabled := sh.sec.AuthEnabled()
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
jsonDict := enabled{isEnabled}
|
||||
err := json.NewEncoder(w).Encode(jsonDict)
|
||||
if err != nil {
|
||||
plog.Warningf("error encoding security state on %s", r.URL)
|
||||
plog.Warningf("error encoding auth state on %s", r.URL)
|
||||
}
|
||||
case "PUT":
|
||||
err := sh.sec.EnableSecurity()
|
||||
err := sh.sec.EnableAuth()
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
case "DELETE":
|
||||
err := sh.sec.DisableSecurity()
|
||||
err := sh.sec.DisableAuth()
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
@ -23,8 +23,8 @@ import (
|
||||
|
||||
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
"github.com/coreos/etcd/etcdserver/auth"
|
||||
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
|
||||
"github.com/coreos/etcd/etcdserver/security"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -55,7 +55,7 @@ func writeError(w http.ResponseWriter, err error) {
|
||||
e.WriteTo(w)
|
||||
case *httptypes.HTTPError:
|
||||
e.WriteTo(w)
|
||||
case security.Error:
|
||||
case auth.Error:
|
||||
herr := httptypes.NewHTTPError(http.StatusBadRequest, e.Error())
|
||||
herr.WriteTo(w)
|
||||
default:
|
||||
|
Loading…
x
Reference in New Issue
Block a user