forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 0
/
auth.go
146 lines (123 loc) · 4.61 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2015 The Cockroach Authors.
//
// 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 security
import (
"crypto/tls"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
)
const (
// NodeUser is used by nodes for intra-cluster traffic.
NodeUser = "node"
// RootUser is the default cluster administrator.
RootUser = "root"
)
// UserAuthHook authenticates a user based on their username and whether their
// connection originates from a client or another node in the cluster.
type UserAuthHook func(string, bool) error
// GetCertificateUser extract the username from a client certificate.
func GetCertificateUser(tlsState *tls.ConnectionState) (string, error) {
if tlsState == nil {
return "", errors.Errorf("request is not using TLS")
}
if len(tlsState.PeerCertificates) == 0 {
return "", errors.Errorf("no client certificates in request")
}
// The go server handshake code verifies the first certificate, using
// any following certificates as intermediates. See:
// https://github.com/golang/go/blob/go1.8.1/src/crypto/tls/handshake_server.go#L723:L742
return tlsState.PeerCertificates[0].Subject.CommonName, nil
}
// RequestWithUser must be implemented by `roachpb.Request`s which are
// arguments to methods that are not permitted to skip user checks.
type RequestWithUser interface {
GetUser() string
}
// ProtoAuthHook builds an authentication hook based on the security
// mode and client certificate.
// The proto.Message passed to the hook must implement RequestWithUser.
func ProtoAuthHook(
insecureMode bool, tlsState *tls.ConnectionState,
) (func(proto.Message, bool) error, error) {
userHook, err := UserAuthCertHook(insecureMode, tlsState)
if err != nil {
return nil, err
}
return func(request proto.Message, clientConnection bool) error {
// RequestWithUser must be implemented.
requestWithUser, ok := request.(RequestWithUser)
if !ok {
return errors.Errorf("unknown request type: %T", request)
}
if err := userHook(requestWithUser.GetUser(), clientConnection); err != nil {
return errors.Errorf("%s error in request: %s", err, request)
}
return nil
}, nil
}
// UserAuthCertHook builds an authentication hook based on the security
// mode and client certificate.
func UserAuthCertHook(insecureMode bool, tlsState *tls.ConnectionState) (UserAuthHook, error) {
var certUser string
if !insecureMode {
var err error
certUser, err = GetCertificateUser(tlsState)
if err != nil {
return nil, err
}
}
return func(requestedUser string, clientConnection bool) error {
// TODO(marc): we may eventually need stricter user syntax rules.
if len(requestedUser) == 0 {
return errors.New("user is missing")
}
if !clientConnection && requestedUser != NodeUser {
return errors.Errorf("user %s is not allowed", requestedUser)
}
// If running in insecure mode, we have nothing to verify it against.
if insecureMode {
return nil
}
// The client certificate user must match the requested user,
// except if the certificate user is NodeUser, which is allowed to
// act on behalf of all other users.
if !(certUser == NodeUser || certUser == requestedUser) {
return errors.Errorf("requested user is %s, but certificate is for %s", requestedUser, certUser)
}
return nil
}, nil
}
// UserAuthPasswordHook builds an authentication hook based on the security
// mode, password, and its potentially matching hash.
func UserAuthPasswordHook(insecureMode bool, password string, hashedPassword []byte) UserAuthHook {
return func(requestedUser string, clientConnection bool) error {
if len(requestedUser) == 0 {
return errors.New("user is missing")
}
if !clientConnection {
return errors.New("password authentication is only available for client connections")
}
if insecureMode {
return nil
}
if requestedUser == RootUser {
return errors.Errorf("user %s must use certificate authentication instead of password authentication", RootUser)
}
// If the requested user has an empty password, disallow authentication.
if len(password) == 0 || CompareHashAndPassword(hashedPassword, password) != nil {
return errors.New("invalid password")
}
return nil
}
}