2016-05-13 06:51:14 +03:00
|
|
|
// Copyright 2016 The etcd Authors
|
2016-03-08 11:31:48 +03:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2016-04-12 08:14:15 +03:00
|
|
|
"bytes"
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
"encoding/binary"
|
2016-03-28 07:00:10 +03:00
|
|
|
"errors"
|
2016-06-09 03:11:26 +03:00
|
|
|
"fmt"
|
2016-04-11 08:34:13 +03:00
|
|
|
"sort"
|
2016-11-24 10:34:29 +03:00
|
|
|
"strconv"
|
2016-04-05 09:42:38 +03:00
|
|
|
"strings"
|
2016-05-17 09:20:50 +03:00
|
|
|
"sync"
|
2016-03-28 07:00:10 +03:00
|
|
|
|
2016-03-23 07:25:48 +03:00
|
|
|
"github.com/coreos/etcd/auth/authpb"
|
|
|
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
2016-04-25 22:32:58 +03:00
|
|
|
"github.com/coreos/etcd/mvcc/backend"
|
2016-03-23 03:10:28 +03:00
|
|
|
"github.com/coreos/pkg/capnslog"
|
2016-03-23 07:25:48 +03:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2016-06-09 03:11:26 +03:00
|
|
|
"golang.org/x/net/context"
|
2016-11-21 07:47:29 +03:00
|
|
|
"google.golang.org/grpc/credentials"
|
2016-11-24 10:34:29 +03:00
|
|
|
"google.golang.org/grpc/metadata"
|
2016-11-21 07:47:29 +03:00
|
|
|
"google.golang.org/grpc/peer"
|
2016-03-08 11:31:48 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2016-04-01 05:07:43 +03:00
|
|
|
enableFlagKey = []byte("authEnabled")
|
2016-06-10 19:37:37 +03:00
|
|
|
authEnabled = []byte{1}
|
|
|
|
authDisabled = []byte{0}
|
2016-04-01 05:07:43 +03:00
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
revisionKey = []byte("authRevision")
|
|
|
|
|
2016-03-23 07:25:48 +03:00
|
|
|
authBucketName = []byte("auth")
|
|
|
|
authUsersBucketName = []byte("authUsers")
|
2016-04-01 05:07:43 +03:00
|
|
|
authRolesBucketName = []byte("authRoles")
|
2016-03-08 11:31:48 +03:00
|
|
|
|
|
|
|
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "auth")
|
2016-03-28 07:00:10 +03:00
|
|
|
|
2016-06-08 07:44:54 +03:00
|
|
|
ErrRootUserNotExist = errors.New("auth: root user does not exist")
|
|
|
|
ErrRootRoleNotExist = errors.New("auth: root user does not have root role")
|
2016-06-05 10:41:10 +03:00
|
|
|
ErrUserAlreadyExist = errors.New("auth: user already exists")
|
2016-11-02 07:37:37 +03:00
|
|
|
ErrUserEmpty = errors.New("auth: user name is empty")
|
2016-06-05 10:41:10 +03:00
|
|
|
ErrUserNotFound = errors.New("auth: user not found")
|
|
|
|
ErrRoleAlreadyExist = errors.New("auth: role already exists")
|
|
|
|
ErrRoleNotFound = errors.New("auth: role not found")
|
|
|
|
ErrAuthFailed = errors.New("auth: authentication failed, invalid user ID or password")
|
|
|
|
ErrPermissionDenied = errors.New("auth: permission denied")
|
|
|
|
ErrRoleNotGranted = errors.New("auth: role is not granted to the user")
|
|
|
|
ErrPermissionNotGranted = errors.New("auth: permission is not granted to the role")
|
2016-06-20 11:09:13 +03:00
|
|
|
ErrAuthNotEnabled = errors.New("auth: authentication is not enabled")
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
ErrAuthOldRevision = errors.New("auth: revision in header is old")
|
2016-11-24 10:34:29 +03:00
|
|
|
ErrInvalidAuthToken = errors.New("auth: invalid auth token")
|
2016-07-07 09:01:09 +03:00
|
|
|
|
|
|
|
// BcryptCost is the algorithm cost / strength for hashing auth passwords
|
|
|
|
BcryptCost = bcrypt.DefaultCost
|
2016-03-08 11:31:48 +03:00
|
|
|
)
|
|
|
|
|
2016-06-08 07:44:54 +03:00
|
|
|
const (
|
|
|
|
rootUser = "root"
|
|
|
|
rootRole = "root"
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
|
|
|
|
revBytesLen = 8
|
2016-06-08 07:44:54 +03:00
|
|
|
)
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
type AuthInfo struct {
|
|
|
|
Username string
|
|
|
|
Revision uint64
|
|
|
|
}
|
|
|
|
|
2016-03-08 11:31:48 +03:00
|
|
|
type AuthStore interface {
|
2016-04-27 19:15:23 +03:00
|
|
|
// AuthEnable turns on the authentication feature
|
2016-06-08 07:44:54 +03:00
|
|
|
AuthEnable() error
|
2016-03-21 16:43:41 +03:00
|
|
|
|
2016-05-07 21:24:43 +03:00
|
|
|
// AuthDisable turns off the authentication feature
|
|
|
|
AuthDisable()
|
|
|
|
|
2016-06-09 03:11:26 +03:00
|
|
|
// Authenticate does authentication based on given user name and password
|
|
|
|
Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error)
|
2016-04-13 07:44:53 +03:00
|
|
|
|
2016-03-21 16:43:41 +03:00
|
|
|
// Recover recovers the state of auth store from the given backend
|
|
|
|
Recover(b backend.Backend)
|
2016-03-23 07:25:48 +03:00
|
|
|
|
|
|
|
// UserAdd adds a new user
|
2016-03-29 08:32:19 +03:00
|
|
|
UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
|
2016-03-31 05:29:47 +03:00
|
|
|
|
|
|
|
// UserDelete deletes a user
|
|
|
|
UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
|
2016-03-31 08:31:07 +03:00
|
|
|
|
|
|
|
// UserChangePassword changes a password of a user
|
|
|
|
UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
|
2016-04-01 05:07:43 +03:00
|
|
|
|
2016-06-07 06:17:28 +03:00
|
|
|
// UserGrantRole grants a role to the user
|
|
|
|
UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error)
|
2016-04-11 08:34:13 +03:00
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
// UserGet gets the detailed information of a users
|
2016-05-30 11:21:11 +03:00
|
|
|
UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error)
|
|
|
|
|
2016-06-05 10:57:23 +03:00
|
|
|
// UserRevokeRole revokes a role of a user
|
|
|
|
UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error)
|
2016-06-03 10:38:59 +03:00
|
|
|
|
2016-04-01 05:07:43 +03:00
|
|
|
// RoleAdd adds a new role
|
|
|
|
RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
|
2016-04-05 09:42:38 +03:00
|
|
|
|
2016-06-07 06:17:28 +03:00
|
|
|
// RoleGrantPermission grants a permission to a role
|
|
|
|
RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error)
|
2016-05-17 09:20:50 +03:00
|
|
|
|
2016-06-02 17:25:15 +03:00
|
|
|
// RoleGet gets the detailed information of a role
|
|
|
|
RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error)
|
|
|
|
|
2016-06-05 10:57:23 +03:00
|
|
|
// RoleRevokePermission gets the detailed information of a role
|
|
|
|
RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error)
|
2016-06-03 11:10:08 +03:00
|
|
|
|
2016-06-03 11:33:05 +03:00
|
|
|
// RoleDelete gets the detailed information of a role
|
|
|
|
RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error)
|
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
// UserList gets a list of all users
|
|
|
|
UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error)
|
|
|
|
|
|
|
|
// RoleList gets a list of all roles
|
|
|
|
RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error)
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
// AuthInfoFromToken gets a username from the given Token and current revision number
|
|
|
|
// (The revision number is used for preventing the TOCTOU problem)
|
|
|
|
AuthInfoFromToken(token string) (*AuthInfo, bool)
|
2016-05-17 09:20:50 +03:00
|
|
|
|
|
|
|
// IsPutPermitted checks put permission of the user
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
IsPutPermitted(authInfo *AuthInfo, key []byte) error
|
2016-05-17 09:20:50 +03:00
|
|
|
|
|
|
|
// IsRangePermitted checks range permission of the user
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error
|
2016-06-09 23:40:26 +03:00
|
|
|
|
2016-06-14 03:19:59 +03:00
|
|
|
// IsDeleteRangePermitted checks delete-range permission of the user
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error
|
2016-06-14 03:19:59 +03:00
|
|
|
|
2016-06-09 23:40:26 +03:00
|
|
|
// IsAdminPermitted checks admin permission of the user
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
IsAdminPermitted(authInfo *AuthInfo) error
|
2016-06-09 03:11:26 +03:00
|
|
|
|
|
|
|
// GenSimpleToken produces a simple random string
|
|
|
|
GenSimpleToken() (string, error)
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
|
|
|
|
// Revision gets current revision of authStore
|
|
|
|
Revision() uint64
|
2016-10-13 13:30:35 +03:00
|
|
|
|
|
|
|
// CheckPassword checks a given pair of username and password is correct
|
|
|
|
CheckPassword(username, password string) (uint64, error)
|
2016-11-14 22:17:55 +03:00
|
|
|
|
|
|
|
// Close does cleanup of AuthStore
|
|
|
|
Close() error
|
2016-11-24 10:34:29 +03:00
|
|
|
|
|
|
|
// AuthInfoFromCtx gets AuthInfo from gRPC's context
|
|
|
|
AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error)
|
2016-11-21 07:47:29 +03:00
|
|
|
|
|
|
|
// AuthInfoFromTLS gets AuthInfo from TLS info of gRPC's context
|
|
|
|
AuthInfoFromTLS(ctx context.Context) *AuthInfo
|
2016-03-08 11:31:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type authStore struct {
|
2016-05-17 09:20:50 +03:00
|
|
|
be backend.Backend
|
|
|
|
enabled bool
|
|
|
|
enabledMu sync.RWMutex
|
2016-06-07 21:45:29 +03:00
|
|
|
|
|
|
|
rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions
|
2016-06-09 03:11:26 +03:00
|
|
|
|
2016-11-14 22:17:55 +03:00
|
|
|
simpleTokensMu sync.RWMutex
|
|
|
|
simpleTokens map[string]string // token -> username
|
|
|
|
simpleTokenKeeper *simpleTokenTTLKeeper
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
|
|
|
|
revision uint64
|
2016-11-24 10:34:29 +03:00
|
|
|
|
|
|
|
indexWaiter func(uint64) <-chan struct{}
|
2016-03-08 11:31:48 +03:00
|
|
|
}
|
|
|
|
|
2017-02-01 11:47:09 +03:00
|
|
|
func newDeleterFunc(as *authStore) func(string) {
|
|
|
|
return func(t string) {
|
|
|
|
as.simpleTokensMu.Lock()
|
|
|
|
defer as.simpleTokensMu.Unlock()
|
|
|
|
if username, ok := as.simpleTokens[t]; ok {
|
|
|
|
plog.Infof("deleting token %s for user %s", t, username)
|
|
|
|
delete(as.simpleTokens, t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-08 07:44:54 +03:00
|
|
|
func (as *authStore) AuthEnable() error {
|
2016-11-14 22:17:55 +03:00
|
|
|
as.enabledMu.Lock()
|
|
|
|
defer as.enabledMu.Unlock()
|
|
|
|
if as.enabled {
|
|
|
|
plog.Noticef("Authentication already enabled")
|
|
|
|
return nil
|
|
|
|
}
|
2016-03-21 16:43:41 +03:00
|
|
|
b := as.be
|
2016-03-08 11:31:48 +03:00
|
|
|
tx := b.BatchTx()
|
|
|
|
tx.Lock()
|
2016-06-08 07:44:54 +03:00
|
|
|
defer func() {
|
|
|
|
tx.Unlock()
|
|
|
|
b.ForceCommit()
|
|
|
|
}()
|
|
|
|
|
|
|
|
u := getUser(tx, rootUser)
|
|
|
|
if u == nil {
|
|
|
|
return ErrRootUserNotExist
|
|
|
|
}
|
|
|
|
|
2016-06-09 23:40:26 +03:00
|
|
|
if !hasRootRole(u) {
|
2016-06-08 07:44:54 +03:00
|
|
|
return ErrRootRoleNotExist
|
|
|
|
}
|
|
|
|
|
2016-06-10 19:37:37 +03:00
|
|
|
tx.UnsafePut(authBucketName, enableFlagKey, authEnabled)
|
2016-03-08 11:31:48 +03:00
|
|
|
|
2016-05-17 09:20:50 +03:00
|
|
|
as.enabled = true
|
2016-11-14 22:17:55 +03:00
|
|
|
|
2017-02-01 11:47:09 +03:00
|
|
|
as.simpleTokenKeeper = NewSimpleTokenTTLKeeper(newDeleterFunc(as))
|
2016-05-17 09:20:50 +03:00
|
|
|
|
2016-06-07 21:45:29 +03:00
|
|
|
as.rangePermCache = make(map[string]*unifiedRangePermissions)
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.revision = getRevision(tx)
|
|
|
|
|
2016-03-08 11:31:48 +03:00
|
|
|
plog.Noticef("Authentication enabled")
|
2016-06-08 07:44:54 +03:00
|
|
|
|
|
|
|
return nil
|
2016-03-08 11:31:48 +03:00
|
|
|
}
|
|
|
|
|
2016-05-07 21:24:43 +03:00
|
|
|
func (as *authStore) AuthDisable() {
|
2016-11-14 22:17:55 +03:00
|
|
|
as.enabledMu.Lock()
|
|
|
|
defer as.enabledMu.Unlock()
|
|
|
|
if !as.enabled {
|
|
|
|
return
|
|
|
|
}
|
2016-05-07 21:24:43 +03:00
|
|
|
b := as.be
|
|
|
|
tx := b.BatchTx()
|
|
|
|
tx.Lock()
|
2016-06-10 19:37:37 +03:00
|
|
|
tx.UnsafePut(authBucketName, enableFlagKey, authDisabled)
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
2016-05-07 21:24:43 +03:00
|
|
|
tx.Unlock()
|
|
|
|
b.ForceCommit()
|
|
|
|
|
2016-05-17 09:20:50 +03:00
|
|
|
as.enabled = false
|
|
|
|
|
2016-06-22 09:43:43 +03:00
|
|
|
as.simpleTokensMu.Lock()
|
|
|
|
as.simpleTokens = make(map[string]string) // invalidate all tokens
|
|
|
|
as.simpleTokensMu.Unlock()
|
2016-11-14 22:17:55 +03:00
|
|
|
if as.simpleTokenKeeper != nil {
|
|
|
|
as.simpleTokenKeeper.stop()
|
|
|
|
as.simpleTokenKeeper = nil
|
|
|
|
}
|
2016-06-22 09:43:43 +03:00
|
|
|
|
2016-05-07 21:24:43 +03:00
|
|
|
plog.Noticef("Authentication disabled")
|
|
|
|
}
|
|
|
|
|
2016-11-14 22:17:55 +03:00
|
|
|
func (as *authStore) Close() error {
|
|
|
|
as.enabledMu.Lock()
|
|
|
|
defer as.enabledMu.Unlock()
|
|
|
|
if !as.enabled {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if as.simpleTokenKeeper != nil {
|
|
|
|
as.simpleTokenKeeper.stop()
|
|
|
|
as.simpleTokenKeeper = nil
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-09 03:11:26 +03:00
|
|
|
func (as *authStore) Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) {
|
2016-06-20 11:09:13 +03:00
|
|
|
if !as.isAuthEnabled() {
|
|
|
|
return nil, ErrAuthNotEnabled
|
|
|
|
}
|
|
|
|
|
2016-06-09 03:11:26 +03:00
|
|
|
// TODO(mitake): after adding jwt support, branching based on values of ctx is required
|
|
|
|
index := ctx.Value("index").(uint64)
|
|
|
|
simpleToken := ctx.Value("simpleToken").(string)
|
|
|
|
|
2016-04-13 07:44:53 +03:00
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-09 03:11:26 +03:00
|
|
|
user := getUser(tx, username)
|
2016-06-10 23:23:28 +03:00
|
|
|
if user == nil {
|
|
|
|
return nil, ErrAuthFailed
|
2016-04-13 07:44:53 +03:00
|
|
|
}
|
|
|
|
|
2016-06-09 03:11:26 +03:00
|
|
|
token := fmt.Sprintf("%s.%d", simpleToken, index)
|
|
|
|
as.assignSimpleTokenToUser(username, token)
|
2016-04-13 07:44:53 +03:00
|
|
|
|
2016-06-09 03:11:26 +03:00
|
|
|
plog.Infof("authorized %s, token is %s", username, token)
|
2016-04-13 07:44:53 +03:00
|
|
|
return &pb.AuthenticateResponse{Token: token}, nil
|
|
|
|
}
|
|
|
|
|
2016-10-13 13:30:35 +03:00
|
|
|
func (as *authStore) CheckPassword(username, password string) (uint64, error) {
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
|
|
|
user := getUser(tx, username)
|
|
|
|
if user == nil {
|
|
|
|
return 0, ErrAuthFailed
|
|
|
|
}
|
|
|
|
|
|
|
|
if bcrypt.CompareHashAndPassword(user.Password, []byte(password)) != nil {
|
|
|
|
plog.Noticef("authentication failed, invalid password for user %s", username)
|
|
|
|
return 0, ErrAuthFailed
|
|
|
|
}
|
|
|
|
|
|
|
|
return getRevision(tx), nil
|
|
|
|
}
|
|
|
|
|
2016-03-21 16:43:41 +03:00
|
|
|
func (as *authStore) Recover(be backend.Backend) {
|
2016-06-10 19:37:37 +03:00
|
|
|
enabled := false
|
2016-03-21 16:43:41 +03:00
|
|
|
as.be = be
|
2016-06-10 19:37:37 +03:00
|
|
|
tx := be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
_, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0)
|
|
|
|
if len(vs) == 1 {
|
|
|
|
if bytes.Equal(vs[0], authEnabled) {
|
|
|
|
enabled = true
|
|
|
|
}
|
|
|
|
}
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
|
|
|
|
as.revision = getRevision(tx)
|
|
|
|
|
2016-06-10 19:37:37 +03:00
|
|
|
tx.Unlock()
|
|
|
|
|
|
|
|
as.enabledMu.Lock()
|
|
|
|
as.enabled = enabled
|
|
|
|
as.enabledMu.Unlock()
|
2016-03-21 16:43:41 +03:00
|
|
|
}
|
|
|
|
|
2016-03-29 08:32:19 +03:00
|
|
|
func (as *authStore) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) {
|
2016-11-02 07:37:37 +03:00
|
|
|
if len(r.Name) == 0 {
|
|
|
|
return nil, ErrUserEmpty
|
|
|
|
}
|
|
|
|
|
2016-07-07 09:01:09 +03:00
|
|
|
hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), BcryptCost)
|
2016-03-23 07:25:48 +03:00
|
|
|
if err != nil {
|
|
|
|
plog.Errorf("failed to hash password: %s", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
user := getUser(tx, r.Name)
|
|
|
|
if user != nil {
|
|
|
|
return nil, ErrUserAlreadyExist
|
2016-03-23 07:25:48 +03:00
|
|
|
}
|
|
|
|
|
2016-06-10 21:27:42 +03:00
|
|
|
newUser := &authpb.User{
|
2016-03-23 07:25:48 +03:00
|
|
|
Name: []byte(r.Name),
|
|
|
|
Password: hashed,
|
|
|
|
}
|
|
|
|
|
2016-06-10 21:27:42 +03:00
|
|
|
putUser(tx, newUser)
|
2016-03-23 07:25:48 +03:00
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
2016-03-23 07:25:48 +03:00
|
|
|
plog.Noticef("added a new user: %s", r.Name)
|
|
|
|
|
2016-03-29 08:32:19 +03:00
|
|
|
return &pb.AuthUserAddResponse{}, nil
|
2016-03-23 07:25:48 +03:00
|
|
|
}
|
|
|
|
|
2016-03-31 05:29:47 +03:00
|
|
|
func (as *authStore) UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) {
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
user := getUser(tx, r.Name)
|
|
|
|
if user == nil {
|
|
|
|
return nil, ErrUserNotFound
|
2016-03-31 05:29:47 +03:00
|
|
|
}
|
|
|
|
|
2016-06-11 00:11:00 +03:00
|
|
|
delUser(tx, r.Name)
|
2016-03-31 05:29:47 +03:00
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
|
|
|
as.invalidateCachedPerm(r.Name)
|
|
|
|
as.invalidateUser(r.Name)
|
|
|
|
|
2016-03-31 05:29:47 +03:00
|
|
|
plog.Noticef("deleted a user: %s", r.Name)
|
|
|
|
|
|
|
|
return &pb.AuthUserDeleteResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2016-03-31 08:31:07 +03:00
|
|
|
func (as *authStore) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) {
|
|
|
|
// TODO(mitake): measure the cost of bcrypt.GenerateFromPassword()
|
|
|
|
// If the cost is too high, we should move the encryption to outside of the raft
|
2016-07-07 09:01:09 +03:00
|
|
|
hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), BcryptCost)
|
2016-03-31 08:31:07 +03:00
|
|
|
if err != nil {
|
|
|
|
plog.Errorf("failed to hash password: %s", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
user := getUser(tx, r.Name)
|
|
|
|
if user == nil {
|
|
|
|
return nil, ErrUserNotFound
|
2016-03-31 08:31:07 +03:00
|
|
|
}
|
|
|
|
|
2016-06-10 21:27:42 +03:00
|
|
|
updatedUser := &authpb.User{
|
2016-03-31 08:31:07 +03:00
|
|
|
Name: []byte(r.Name),
|
2016-06-10 21:27:42 +03:00
|
|
|
Roles: user.Roles,
|
2016-03-31 08:31:07 +03:00
|
|
|
Password: hashed,
|
|
|
|
}
|
|
|
|
|
2016-06-10 21:27:42 +03:00
|
|
|
putUser(tx, updatedUser)
|
2016-03-31 08:31:07 +03:00
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
|
|
|
as.invalidateCachedPerm(r.Name)
|
|
|
|
as.invalidateUser(r.Name)
|
|
|
|
|
2016-03-31 08:31:07 +03:00
|
|
|
plog.Noticef("changed a password of a user: %s", r.Name)
|
|
|
|
|
|
|
|
return &pb.AuthUserChangePasswordResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2016-06-07 06:17:28 +03:00
|
|
|
func (as *authStore) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) {
|
2016-04-11 08:34:13 +03:00
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
user := getUser(tx, r.User)
|
|
|
|
if user == nil {
|
2016-04-11 08:34:13 +03:00
|
|
|
return nil, ErrUserNotFound
|
|
|
|
}
|
|
|
|
|
2016-06-08 07:44:54 +03:00
|
|
|
if r.Role != rootRole {
|
2016-06-10 23:23:28 +03:00
|
|
|
role := getRole(tx, r.Role)
|
|
|
|
if role == nil {
|
2016-06-08 07:44:54 +03:00
|
|
|
return nil, ErrRoleNotFound
|
|
|
|
}
|
2016-04-11 08:34:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
idx := sort.SearchStrings(user.Roles, r.Role)
|
|
|
|
if idx < len(user.Roles) && strings.Compare(user.Roles[idx], r.Role) == 0 {
|
|
|
|
plog.Warningf("user %s is already granted role %s", r.User, r.Role)
|
2016-06-07 06:17:28 +03:00
|
|
|
return &pb.AuthUserGrantRoleResponse{}, nil
|
2016-04-11 08:34:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
user.Roles = append(user.Roles, r.Role)
|
|
|
|
sort.Sort(sort.StringSlice(user.Roles))
|
|
|
|
|
2016-06-10 21:27:42 +03:00
|
|
|
putUser(tx, user)
|
2016-04-11 08:34:13 +03:00
|
|
|
|
2016-06-07 21:45:29 +03:00
|
|
|
as.invalidateCachedPerm(r.User)
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
2016-04-11 08:34:13 +03:00
|
|
|
plog.Noticef("granted role %s to user %s", r.Role, r.User)
|
2016-06-07 06:17:28 +03:00
|
|
|
return &pb.AuthUserGrantRoleResponse{}, nil
|
2016-04-11 08:34:13 +03:00
|
|
|
}
|
|
|
|
|
2016-05-30 11:21:11 +03:00
|
|
|
func (as *authStore) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
var resp pb.AuthUserGetResponse
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
user := getUser(tx, r.Name)
|
|
|
|
if user == nil {
|
2016-05-30 11:21:11 +03:00
|
|
|
return nil, ErrUserNotFound
|
|
|
|
}
|
2016-12-09 23:05:19 +03:00
|
|
|
resp.Roles = append(resp.Roles, user.Roles...)
|
2016-05-30 11:21:11 +03:00
|
|
|
return &resp, nil
|
|
|
|
}
|
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
func (as *authStore) UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) {
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
|
|
|
var resp pb.AuthUserListResponse
|
|
|
|
|
|
|
|
users := getAllUsers(tx)
|
|
|
|
|
|
|
|
for _, u := range users {
|
|
|
|
resp.Users = append(resp.Users, string(u.Name))
|
|
|
|
}
|
|
|
|
|
|
|
|
return &resp, nil
|
|
|
|
}
|
|
|
|
|
2016-06-05 10:57:23 +03:00
|
|
|
func (as *authStore) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) {
|
2016-06-03 10:38:59 +03:00
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
user := getUser(tx, r.Name)
|
|
|
|
if user == nil {
|
2016-06-03 10:38:59 +03:00
|
|
|
return nil, ErrUserNotFound
|
|
|
|
}
|
|
|
|
|
2016-06-11 00:19:29 +03:00
|
|
|
updatedUser := &authpb.User{
|
|
|
|
Name: user.Name,
|
|
|
|
Password: user.Password,
|
|
|
|
}
|
2016-06-03 10:38:59 +03:00
|
|
|
|
|
|
|
for _, role := range user.Roles {
|
|
|
|
if strings.Compare(role, r.Role) != 0 {
|
|
|
|
updatedUser.Roles = append(updatedUser.Roles, role)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-11 00:19:29 +03:00
|
|
|
if len(updatedUser.Roles) == len(user.Roles) {
|
2016-06-05 10:41:10 +03:00
|
|
|
return nil, ErrRoleNotGranted
|
|
|
|
}
|
|
|
|
|
2016-06-10 21:27:42 +03:00
|
|
|
putUser(tx, updatedUser)
|
2016-06-03 10:38:59 +03:00
|
|
|
|
2016-06-07 21:45:29 +03:00
|
|
|
as.invalidateCachedPerm(r.Name)
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
2016-06-03 10:38:59 +03:00
|
|
|
plog.Noticef("revoked role %s from user %s", r.Role, r.Name)
|
2016-06-05 10:57:23 +03:00
|
|
|
return &pb.AuthUserRevokeRoleResponse{}, nil
|
2016-06-03 10:38:59 +03:00
|
|
|
}
|
|
|
|
|
2016-06-02 17:25:15 +03:00
|
|
|
func (as *authStore) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) {
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
var resp pb.AuthRoleGetResponse
|
|
|
|
|
2016-06-10 20:59:34 +03:00
|
|
|
role := getRole(tx, r.Role)
|
|
|
|
if role == nil {
|
2016-06-02 17:25:15 +03:00
|
|
|
return nil, ErrRoleNotFound
|
|
|
|
}
|
2016-12-09 23:05:19 +03:00
|
|
|
resp.Perm = append(resp.Perm, role.KeyPermission...)
|
2016-06-02 17:25:15 +03:00
|
|
|
return &resp, nil
|
|
|
|
}
|
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
func (as *authStore) RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) {
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
|
|
|
var resp pb.AuthRoleListResponse
|
|
|
|
|
|
|
|
roles := getAllRoles(tx)
|
|
|
|
|
|
|
|
for _, r := range roles {
|
|
|
|
resp.Roles = append(resp.Roles, string(r.Name))
|
|
|
|
}
|
|
|
|
|
|
|
|
return &resp, nil
|
|
|
|
}
|
|
|
|
|
2016-06-05 10:57:23 +03:00
|
|
|
func (as *authStore) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) {
|
2016-06-03 11:10:08 +03:00
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-10 20:59:34 +03:00
|
|
|
role := getRole(tx, r.Role)
|
|
|
|
if role == nil {
|
2016-06-03 11:10:08 +03:00
|
|
|
return nil, ErrRoleNotFound
|
|
|
|
}
|
|
|
|
|
2016-06-11 00:19:29 +03:00
|
|
|
updatedRole := &authpb.Role{
|
|
|
|
Name: role.Name,
|
|
|
|
}
|
2016-06-03 11:10:08 +03:00
|
|
|
|
|
|
|
for _, perm := range role.KeyPermission {
|
2016-06-08 21:52:57 +03:00
|
|
|
if !bytes.Equal(perm.Key, []byte(r.Key)) || !bytes.Equal(perm.RangeEnd, []byte(r.RangeEnd)) {
|
2016-06-03 11:10:08 +03:00
|
|
|
updatedRole.KeyPermission = append(updatedRole.KeyPermission, perm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-11 00:19:29 +03:00
|
|
|
if len(role.KeyPermission) == len(updatedRole.KeyPermission) {
|
2016-06-05 10:41:10 +03:00
|
|
|
return nil, ErrPermissionNotGranted
|
|
|
|
}
|
|
|
|
|
2016-06-10 23:20:48 +03:00
|
|
|
putRole(tx, updatedRole)
|
2016-06-03 11:10:08 +03:00
|
|
|
|
2016-06-07 21:45:29 +03:00
|
|
|
// TODO(mitake): currently single role update invalidates every cache
|
|
|
|
// It should be optimized.
|
|
|
|
as.clearCachedPerm()
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
2016-06-03 11:10:08 +03:00
|
|
|
plog.Noticef("revoked key %s from role %s", r.Key, r.Role)
|
2016-06-05 10:57:23 +03:00
|
|
|
return &pb.AuthRoleRevokePermissionResponse{}, nil
|
2016-06-03 11:10:08 +03:00
|
|
|
}
|
|
|
|
|
2016-06-03 11:33:05 +03:00
|
|
|
func (as *authStore) RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) {
|
|
|
|
// TODO(mitake): current scheme of role deletion allows existing users to have the deleted roles
|
|
|
|
//
|
|
|
|
// Assume a case like below:
|
|
|
|
// create a role r1
|
|
|
|
// create a user u1 and grant r1 to u1
|
|
|
|
// delete r1
|
|
|
|
//
|
|
|
|
// After this sequence, u1 is still granted the role r1. So if admin create a new role with the name r1,
|
|
|
|
// the new r1 is automatically granted u1.
|
|
|
|
// In some cases, it would be confusing. So we need to provide an option for deleting the grant relation
|
|
|
|
// from all users.
|
|
|
|
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-10 20:59:34 +03:00
|
|
|
role := getRole(tx, r.Role)
|
|
|
|
if role == nil {
|
2016-06-03 11:33:05 +03:00
|
|
|
return nil, ErrRoleNotFound
|
|
|
|
}
|
|
|
|
|
2016-06-11 00:11:00 +03:00
|
|
|
delRole(tx, r.Role)
|
2016-06-03 11:33:05 +03:00
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
2016-06-03 11:33:05 +03:00
|
|
|
plog.Noticef("deleted role %s", r.Role)
|
|
|
|
return &pb.AuthRoleDeleteResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2016-04-01 05:07:43 +03:00
|
|
|
func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-10 20:59:34 +03:00
|
|
|
role := getRole(tx, r.Name)
|
|
|
|
if role != nil {
|
2016-04-01 05:07:43 +03:00
|
|
|
return nil, ErrRoleAlreadyExist
|
|
|
|
}
|
|
|
|
|
|
|
|
newRole := &authpb.Role{
|
|
|
|
Name: []byte(r.Name),
|
|
|
|
}
|
|
|
|
|
2016-06-10 23:20:48 +03:00
|
|
|
putRole(tx, newRole)
|
2016-04-01 05:07:43 +03:00
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
2016-04-01 05:07:43 +03:00
|
|
|
plog.Noticef("Role %s is created", r.Name)
|
|
|
|
|
|
|
|
return &pb.AuthRoleAddResponse{}, nil
|
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
func (as *authStore) AuthInfoFromToken(token string) (*AuthInfo, bool) {
|
2016-06-09 03:11:26 +03:00
|
|
|
as.simpleTokensMu.RLock()
|
|
|
|
defer as.simpleTokensMu.RUnlock()
|
|
|
|
t, ok := as.simpleTokens[token]
|
2016-11-14 22:17:55 +03:00
|
|
|
if ok {
|
|
|
|
as.simpleTokenKeeper.resetSimpleToken(token)
|
|
|
|
}
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return &AuthInfo{Username: t, Revision: as.revision}, ok
|
2016-05-17 09:20:50 +03:00
|
|
|
}
|
|
|
|
|
2016-04-12 08:14:15 +03:00
|
|
|
type permSlice []*authpb.Permission
|
|
|
|
|
|
|
|
func (perms permSlice) Len() int {
|
|
|
|
return len(perms)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (perms permSlice) Less(i, j int) bool {
|
|
|
|
return bytes.Compare(perms[i].Key, perms[j].Key) < 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (perms permSlice) Swap(i, j int) {
|
|
|
|
perms[i], perms[j] = perms[j], perms[i]
|
|
|
|
}
|
|
|
|
|
2016-06-07 06:17:28 +03:00
|
|
|
func (as *authStore) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) {
|
2016-04-05 09:42:38 +03:00
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-10 20:59:34 +03:00
|
|
|
role := getRole(tx, r.Name)
|
|
|
|
if role == nil {
|
2016-04-05 09:42:38 +03:00
|
|
|
return nil, ErrRoleNotFound
|
|
|
|
}
|
|
|
|
|
2016-04-12 08:14:15 +03:00
|
|
|
idx := sort.Search(len(role.KeyPermission), func(i int) bool {
|
|
|
|
return bytes.Compare(role.KeyPermission[i].Key, []byte(r.Perm.Key)) >= 0
|
|
|
|
})
|
|
|
|
|
2016-06-08 21:52:57 +03:00
|
|
|
if idx < len(role.KeyPermission) && bytes.Equal(role.KeyPermission[idx].Key, r.Perm.Key) && bytes.Equal(role.KeyPermission[idx].RangeEnd, r.Perm.RangeEnd) {
|
2016-04-12 08:14:15 +03:00
|
|
|
// update existing permission
|
|
|
|
role.KeyPermission[idx].PermType = r.Perm.PermType
|
|
|
|
} else {
|
|
|
|
// append new permission to the role
|
2016-04-05 09:42:38 +03:00
|
|
|
newPerm := &authpb.Permission{
|
|
|
|
Key: []byte(r.Perm.Key),
|
2016-06-08 21:52:57 +03:00
|
|
|
RangeEnd: []byte(r.Perm.RangeEnd),
|
2016-04-05 09:42:38 +03:00
|
|
|
PermType: r.Perm.PermType,
|
|
|
|
}
|
|
|
|
|
|
|
|
role.KeyPermission = append(role.KeyPermission, newPerm)
|
2016-04-12 08:14:15 +03:00
|
|
|
sort.Sort(permSlice(role.KeyPermission))
|
2016-04-05 09:42:38 +03:00
|
|
|
}
|
|
|
|
|
2016-06-10 23:20:48 +03:00
|
|
|
putRole(tx, role)
|
2016-04-05 09:42:38 +03:00
|
|
|
|
2016-06-07 21:45:29 +03:00
|
|
|
// TODO(mitake): currently single role update invalidates every cache
|
|
|
|
// It should be optimized.
|
|
|
|
as.clearCachedPerm()
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as.commitRevision(tx)
|
|
|
|
|
2016-04-05 09:42:38 +03:00
|
|
|
plog.Noticef("role %s's permission of key %s is updated as %s", r.Name, r.Perm.Key, authpb.Permission_Type_name[int32(r.Perm.PermType)])
|
|
|
|
|
2016-06-07 06:17:28 +03:00
|
|
|
return &pb.AuthRoleGrantPermissionResponse{}, nil
|
2016-04-05 09:42:38 +03:00
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
func (as *authStore) isOpPermitted(userName string, revision uint64, key, rangeEnd []byte, permTyp authpb.Permission_Type) error {
|
2016-05-17 09:20:50 +03:00
|
|
|
// TODO(mitake): this function would be costly so we need a caching mechanism
|
|
|
|
if !as.isAuthEnabled() {
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-01-10 02:27:09 +03:00
|
|
|
// only gets rev == 0 when passed AuthInfo{}; no user given
|
|
|
|
if revision == 0 {
|
|
|
|
return ErrUserEmpty
|
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
if revision < as.revision {
|
|
|
|
return ErrAuthOldRevision
|
2016-05-17 09:20:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
user := getUser(tx, userName)
|
|
|
|
if user == nil {
|
2016-05-17 09:20:50 +03:00
|
|
|
plog.Errorf("invalid user name %s for permission checking", userName)
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return ErrPermissionDenied
|
2016-05-17 09:20:50 +03:00
|
|
|
}
|
|
|
|
|
2016-09-06 05:32:25 +03:00
|
|
|
// root role should have permission on all ranges
|
|
|
|
if hasRootRole(user) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-11 05:18:07 +03:00
|
|
|
if as.isRangeOpPermitted(tx, userName, key, rangeEnd, permTyp) {
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return nil
|
2016-06-07 21:45:29 +03:00
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return ErrPermissionDenied
|
2016-05-17 09:20:50 +03:00
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
func (as *authStore) IsPutPermitted(authInfo *AuthInfo, key []byte) error {
|
|
|
|
return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, nil, authpb.WRITE)
|
2016-05-17 09:20:50 +03:00
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
func (as *authStore) IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error {
|
|
|
|
return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.READ)
|
2016-05-17 09:20:50 +03:00
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
func (as *authStore) IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error {
|
|
|
|
return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.WRITE)
|
2016-06-14 03:19:59 +03:00
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
func (as *authStore) IsAdminPermitted(authInfo *AuthInfo) error {
|
2016-06-09 23:40:26 +03:00
|
|
|
if !as.isAuthEnabled() {
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return nil
|
2016-06-09 23:40:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
tx := as.be.BatchTx()
|
|
|
|
tx.Lock()
|
|
|
|
defer tx.Unlock()
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
u := getUser(tx, authInfo.Username)
|
2016-06-09 23:40:26 +03:00
|
|
|
if u == nil {
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return ErrUserNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
if !hasRootRole(u) {
|
|
|
|
return ErrPermissionDenied
|
2016-06-09 23:40:26 +03:00
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
return nil
|
2016-06-09 23:40:26 +03:00
|
|
|
}
|
|
|
|
|
2016-06-08 08:42:39 +03:00
|
|
|
func getUser(tx backend.BatchTx, username string) *authpb.User {
|
|
|
|
_, vs := tx.UnsafeRange(authUsersBucketName, []byte(username), nil, 0)
|
|
|
|
if len(vs) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
user := &authpb.User{}
|
|
|
|
err := user.Unmarshal(vs[0])
|
|
|
|
if err != nil {
|
|
|
|
plog.Panicf("failed to unmarshal user struct (name: %s): %s", username, err)
|
|
|
|
}
|
|
|
|
return user
|
|
|
|
}
|
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
func getAllUsers(tx backend.BatchTx) []*authpb.User {
|
|
|
|
_, vs := tx.UnsafeRange(authUsersBucketName, []byte{0}, []byte{0xff}, -1)
|
|
|
|
if len(vs) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var users []*authpb.User
|
|
|
|
|
|
|
|
for _, v := range vs {
|
|
|
|
user := &authpb.User{}
|
|
|
|
err := user.Unmarshal(v)
|
|
|
|
if err != nil {
|
|
|
|
plog.Panicf("failed to unmarshal user struct: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
users = append(users, user)
|
|
|
|
}
|
|
|
|
|
|
|
|
return users
|
|
|
|
}
|
|
|
|
|
2016-06-10 21:27:42 +03:00
|
|
|
func putUser(tx backend.BatchTx, user *authpb.User) {
|
|
|
|
b, err := user.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
plog.Panicf("failed to marshal user struct (name: %s): %s", user.Name, err)
|
|
|
|
}
|
|
|
|
tx.UnsafePut(authUsersBucketName, user.Name, b)
|
|
|
|
}
|
|
|
|
|
2016-06-11 00:11:00 +03:00
|
|
|
func delUser(tx backend.BatchTx, username string) {
|
|
|
|
tx.UnsafeDelete(authUsersBucketName, []byte(username))
|
|
|
|
}
|
|
|
|
|
2016-06-10 20:59:34 +03:00
|
|
|
func getRole(tx backend.BatchTx, rolename string) *authpb.Role {
|
|
|
|
_, vs := tx.UnsafeRange(authRolesBucketName, []byte(rolename), nil, 0)
|
|
|
|
if len(vs) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
role := &authpb.Role{}
|
|
|
|
err := role.Unmarshal(vs[0])
|
|
|
|
if err != nil {
|
|
|
|
plog.Panicf("failed to unmarshal role struct (name: %s): %s", rolename, err)
|
|
|
|
}
|
|
|
|
return role
|
|
|
|
}
|
|
|
|
|
2016-06-15 09:11:27 +03:00
|
|
|
func getAllRoles(tx backend.BatchTx) []*authpb.Role {
|
|
|
|
_, vs := tx.UnsafeRange(authRolesBucketName, []byte{0}, []byte{0xff}, -1)
|
|
|
|
if len(vs) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var roles []*authpb.Role
|
|
|
|
|
|
|
|
for _, v := range vs {
|
|
|
|
role := &authpb.Role{}
|
|
|
|
err := role.Unmarshal(v)
|
|
|
|
if err != nil {
|
|
|
|
plog.Panicf("failed to unmarshal role struct: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
roles = append(roles, role)
|
|
|
|
}
|
|
|
|
|
|
|
|
return roles
|
|
|
|
}
|
|
|
|
|
2016-06-10 23:20:48 +03:00
|
|
|
func putRole(tx backend.BatchTx, role *authpb.Role) {
|
|
|
|
b, err := role.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
plog.Panicf("failed to marshal role struct (name: %s): %s", role.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.UnsafePut(authRolesBucketName, []byte(role.Name), b)
|
|
|
|
}
|
|
|
|
|
2016-06-11 00:11:00 +03:00
|
|
|
func delRole(tx backend.BatchTx, rolename string) {
|
|
|
|
tx.UnsafeDelete(authRolesBucketName, []byte(rolename))
|
|
|
|
}
|
|
|
|
|
2016-05-17 09:20:50 +03:00
|
|
|
func (as *authStore) isAuthEnabled() bool {
|
|
|
|
as.enabledMu.RLock()
|
|
|
|
defer as.enabledMu.RUnlock()
|
|
|
|
return as.enabled
|
|
|
|
}
|
|
|
|
|
2016-11-24 10:34:29 +03:00
|
|
|
func NewAuthStore(be backend.Backend, indexWaiter func(uint64) <-chan struct{}) *authStore {
|
2016-03-21 16:43:41 +03:00
|
|
|
tx := be.BatchTx()
|
2016-03-08 11:31:48 +03:00
|
|
|
tx.Lock()
|
2016-03-23 07:25:48 +03:00
|
|
|
|
2016-03-08 11:31:48 +03:00
|
|
|
tx.UnsafeCreateBucket(authBucketName)
|
2016-03-23 07:25:48 +03:00
|
|
|
tx.UnsafeCreateBucket(authUsersBucketName)
|
2016-04-01 05:07:43 +03:00
|
|
|
tx.UnsafeCreateBucket(authRolesBucketName)
|
2016-03-23 07:25:48 +03:00
|
|
|
|
2017-02-01 11:47:09 +03:00
|
|
|
enabled := false
|
|
|
|
_, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0)
|
|
|
|
if len(vs) == 1 {
|
|
|
|
if bytes.Equal(vs[0], authEnabled) {
|
|
|
|
enabled = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
as := &authStore{
|
2017-02-01 11:47:09 +03:00
|
|
|
be: be,
|
|
|
|
simpleTokens: make(map[string]string),
|
|
|
|
revision: getRevision(tx),
|
|
|
|
indexWaiter: indexWaiter,
|
|
|
|
enabled: enabled,
|
|
|
|
rangePermCache: make(map[string]*unifiedRangePermissions),
|
|
|
|
}
|
|
|
|
|
|
|
|
if enabled {
|
|
|
|
as.simpleTokenKeeper = NewSimpleTokenTTLKeeper(newDeleterFunc(as))
|
2016-03-08 11:31:48 +03:00
|
|
|
}
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
|
2017-02-22 01:37:15 +03:00
|
|
|
if as.revision == 0 {
|
|
|
|
as.commitRevision(tx)
|
|
|
|
}
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
|
|
|
|
tx.Unlock()
|
|
|
|
be.ForceCommit()
|
|
|
|
|
|
|
|
return as
|
2016-03-08 11:31:48 +03:00
|
|
|
}
|
2016-06-09 23:40:26 +03:00
|
|
|
|
|
|
|
func hasRootRole(u *authpb.User) bool {
|
|
|
|
for _, r := range u.Roles {
|
|
|
|
if r == rootRole {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
|
|
|
|
func (as *authStore) commitRevision(tx backend.BatchTx) {
|
|
|
|
as.revision++
|
|
|
|
revBytes := make([]byte, revBytesLen)
|
|
|
|
binary.BigEndian.PutUint64(revBytes, as.revision)
|
|
|
|
tx.UnsafePut(authBucketName, revisionKey, revBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRevision(tx backend.BatchTx) uint64 {
|
|
|
|
_, vs := tx.UnsafeRange(authBucketName, []byte(revisionKey), nil, 0)
|
|
|
|
if len(vs) != 1 {
|
2017-02-01 11:47:09 +03:00
|
|
|
// this can happen in the initialization phase
|
|
|
|
return 0
|
auth, etcdserver: introduce revision of authStore for avoiding TOCTOU problem
This commit introduces revision of authStore. The revision number
represents a version of authStore that is incremented by updating auth
related information.
The revision is required for avoiding TOCTOU problems. Currently there
are two types of the TOCTOU problems in v3 auth.
The first one is in ordinal linearizable requests with a sequence like
below ():
1. Request from client CA is processed in follower FA. FA looks up the
username (let it U) for the request from a token of the request. At
this time, the request is authorized correctly.
2. Another request from client CB is processed in follower FB. CB
is for changing U's password.
3. FB forwards the request from CB to the leader before FA. Now U's
password is updated and the request from CA should be rejected.
4. However, the request from CA is processed by the leader because
authentication is already done in FA.
For avoiding the above sequence, this commit lets
etcdserverpb.RequestHeader have a member revision. The member is
initialized during authentication by followers and checked in a
leader. If the revision in RequestHeader is lower than the leader's
authStore revision, it means a sequence like above happened. In such a
case, the state machine returns auth.ErrAuthRevisionObsolete. The
error code lets nodes retry their requests.
The second one, a case of serializable range and txn, is more
subtle. Because these requests are processed in follower directly. The
TOCTOU problem can be caused by a sequence like below:
1. Serializable request from client CA is processed in follower FA. At
first, FA looks up the username (let it U) and its permission
before actual access to KV.
2. Another request from client CB is processed in follower FB and
forwarded to the leader. The cluster including FA now commits a log
entry of the request from CB. Assume the request changed the
permission or password of U.
3. Now the serializable request from CA is accessing to KV. Even if
the access is allowed at the point of 1, now it can be invalid
because of the change introduced in 2.
For avoiding the above sequence, this commit lets the functions of
serializable requests (EtcdServer.Range() and EtcdServer.Txn())
compare the revision in the request header with the latest revision of
authStore after the actual access. If the saved revision is lower than
the latest one, it means the permission can be changed. Although it
would introduce false positives (e.g. changing other user's password),
it prevents the TOCTOU problem. This idea is an implementation of
Anthony's comment:
https://github.com/coreos/etcd/pull/5739#issuecomment-228128254
2016-06-23 12:31:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return binary.BigEndian.Uint64(vs[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *authStore) Revision() uint64 {
|
|
|
|
return as.revision
|
|
|
|
}
|
2016-11-24 10:34:29 +03:00
|
|
|
|
|
|
|
func (as *authStore) isValidSimpleToken(token string, ctx context.Context) bool {
|
|
|
|
splitted := strings.Split(token, ".")
|
|
|
|
if len(splitted) != 2 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
index, err := strconv.Atoi(splitted[1])
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-as.indexWaiter(uint64(index)):
|
|
|
|
return true
|
|
|
|
case <-ctx.Done():
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-11-21 07:47:29 +03:00
|
|
|
func (as *authStore) AuthInfoFromTLS(ctx context.Context) *AuthInfo {
|
|
|
|
peer, ok := peer.FromContext(ctx)
|
|
|
|
if !ok || peer == nil || peer.AuthInfo == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
tlsInfo := peer.AuthInfo.(credentials.TLSInfo)
|
|
|
|
for _, chains := range tlsInfo.State.VerifiedChains {
|
|
|
|
for _, chain := range chains {
|
|
|
|
cn := chain.Subject.CommonName
|
|
|
|
plog.Debugf("found common name %s", cn)
|
|
|
|
|
|
|
|
return &AuthInfo{
|
|
|
|
Username: cn,
|
|
|
|
Revision: as.Revision(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-24 10:34:29 +03:00
|
|
|
func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) {
|
|
|
|
md, ok := metadata.FromContext(ctx)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ts, tok := md["token"]
|
|
|
|
if !tok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
token := ts[0]
|
|
|
|
if !as.isValidSimpleToken(token, ctx) {
|
|
|
|
return nil, ErrInvalidAuthToken
|
|
|
|
}
|
|
|
|
|
|
|
|
authInfo, uok := as.AuthInfoFromToken(token)
|
|
|
|
if !uok {
|
|
|
|
plog.Warningf("invalid auth token: %s", token)
|
|
|
|
return nil, ErrInvalidAuthToken
|
|
|
|
}
|
|
|
|
return authInfo, nil
|
|
|
|
}
|