Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / github.com / docker / swarmkit / ca / auth.go
1 package ca
2
3 import (
4         "crypto/tls"
5         "crypto/x509/pkix"
6         "strings"
7
8         "github.com/Sirupsen/logrus"
9
10         "github.com/docker/swarmkit/api"
11         "github.com/docker/swarmkit/log"
12         "golang.org/x/net/context"
13         "google.golang.org/grpc"
14         "google.golang.org/grpc/codes"
15         "google.golang.org/grpc/credentials"
16         "google.golang.org/grpc/peer"
17 )
18
19 type localRequestKeyType struct{}
20
21 // LocalRequestKey is a context key to mark a request that originating on the
22 // local node. The associated value is a RemoteNodeInfo structure describing the
23 // local node.
24 var LocalRequestKey = localRequestKeyType{}
25
26 // LogTLSState logs information about the TLS connection and remote peers
27 func LogTLSState(ctx context.Context, tlsState *tls.ConnectionState) {
28         if tlsState == nil {
29                 log.G(ctx).Debugf("no TLS Chains found")
30                 return
31         }
32
33         peerCerts := []string{}
34         verifiedChain := []string{}
35         for _, cert := range tlsState.PeerCertificates {
36                 peerCerts = append(peerCerts, cert.Subject.CommonName)
37         }
38         for _, chain := range tlsState.VerifiedChains {
39                 subjects := []string{}
40                 for _, cert := range chain {
41                         subjects = append(subjects, cert.Subject.CommonName)
42                 }
43                 verifiedChain = append(verifiedChain, strings.Join(subjects, ","))
44         }
45
46         log.G(ctx).WithFields(logrus.Fields{
47                 "peer.peerCert": peerCerts,
48                 // "peer.verifiedChain": verifiedChain},
49         }).Debugf("")
50 }
51
52 // getCertificateSubject extracts the subject from a verified client certificate
53 func getCertificateSubject(tlsState *tls.ConnectionState) (pkix.Name, error) {
54         if tlsState == nil {
55                 return pkix.Name{}, grpc.Errorf(codes.PermissionDenied, "request is not using TLS")
56         }
57         if len(tlsState.PeerCertificates) == 0 {
58                 return pkix.Name{}, grpc.Errorf(codes.PermissionDenied, "no client certificates in request")
59         }
60         if len(tlsState.VerifiedChains) == 0 {
61                 return pkix.Name{}, grpc.Errorf(codes.PermissionDenied, "no verified chains for remote certificate")
62         }
63
64         return tlsState.VerifiedChains[0][0].Subject, nil
65 }
66
67 func tlsConnStateFromContext(ctx context.Context) (*tls.ConnectionState, error) {
68         peer, ok := peer.FromContext(ctx)
69         if !ok {
70                 return nil, grpc.Errorf(codes.PermissionDenied, "Permission denied: no peer info")
71         }
72         tlsInfo, ok := peer.AuthInfo.(credentials.TLSInfo)
73         if !ok {
74                 return nil, grpc.Errorf(codes.PermissionDenied, "Permission denied: peer didn't not present valid peer certificate")
75         }
76         return &tlsInfo.State, nil
77 }
78
79 // certSubjectFromContext extracts pkix.Name from context.
80 func certSubjectFromContext(ctx context.Context) (pkix.Name, error) {
81         connState, err := tlsConnStateFromContext(ctx)
82         if err != nil {
83                 return pkix.Name{}, err
84         }
85         return getCertificateSubject(connState)
86 }
87
88 // AuthorizeOrgAndRole takes in a context and a list of roles, and returns
89 // the Node ID of the node.
90 func AuthorizeOrgAndRole(ctx context.Context, org string, blacklistedCerts map[string]*api.BlacklistedCertificate, ou ...string) (string, error) {
91         certSubj, err := certSubjectFromContext(ctx)
92         if err != nil {
93                 return "", err
94         }
95         // Check if the current certificate has an OU that authorizes
96         // access to this method
97         if intersectArrays(certSubj.OrganizationalUnit, ou) {
98                 return authorizeOrg(certSubj, org, blacklistedCerts)
99         }
100
101         return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: remote certificate not part of OUs: %v", ou)
102 }
103
104 // authorizeOrg takes in a certificate subject and an organization, and returns
105 // the Node ID of the node.
106 func authorizeOrg(certSubj pkix.Name, org string, blacklistedCerts map[string]*api.BlacklistedCertificate) (string, error) {
107         if _, ok := blacklistedCerts[certSubj.CommonName]; ok {
108                 return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: node %s was removed from swarm", certSubj.CommonName)
109         }
110
111         if len(certSubj.Organization) > 0 && certSubj.Organization[0] == org {
112                 return certSubj.CommonName, nil
113         }
114
115         return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: remote certificate not part of organization: %s", org)
116 }
117
118 // AuthorizeForwardedRoleAndOrg checks for proper roles and organization of caller. The RPC may have
119 // been proxied by a manager, in which case the manager is authenticated and
120 // so is the certificate information that it forwarded. It returns the node ID
121 // of the original client.
122 func AuthorizeForwardedRoleAndOrg(ctx context.Context, authorizedRoles, forwarderRoles []string, org string, blacklistedCerts map[string]*api.BlacklistedCertificate) (string, error) {
123         if isForwardedRequest(ctx) {
124                 _, err := AuthorizeOrgAndRole(ctx, org, blacklistedCerts, forwarderRoles...)
125                 if err != nil {
126                         return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: unauthorized forwarder role: %v", err)
127                 }
128
129                 // This was a forwarded request. Authorize the forwarder, and
130                 // check if the forwarded role matches one of the authorized
131                 // roles.
132                 _, forwardedID, forwardedOrg, forwardedOUs := forwardedTLSInfoFromContext(ctx)
133
134                 if len(forwardedOUs) == 0 || forwardedID == "" || forwardedOrg == "" {
135                         return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: missing information in forwarded request")
136                 }
137
138                 if !intersectArrays(forwardedOUs, authorizedRoles) {
139                         return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: unauthorized forwarded role, expecting: %v", authorizedRoles)
140                 }
141
142                 if forwardedOrg != org {
143                         return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: organization mismatch, expecting: %s", org)
144                 }
145
146                 return forwardedID, nil
147         }
148
149         // There wasn't any node being forwarded, check if this is a direct call by the expected role
150         nodeID, err := AuthorizeOrgAndRole(ctx, org, blacklistedCerts, authorizedRoles...)
151         if err == nil {
152                 return nodeID, nil
153         }
154
155         return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: unauthorized peer role: %v", err)
156 }
157
158 // intersectArrays returns true when there is at least one element in common
159 // between the two arrays
160 func intersectArrays(orig, tgt []string) bool {
161         for _, i := range orig {
162                 for _, x := range tgt {
163                         if i == x {
164                                 return true
165                         }
166                 }
167         }
168         return false
169 }
170
171 // RemoteNodeInfo describes a node sending an RPC request.
172 type RemoteNodeInfo struct {
173         // Roles is a list of roles contained in the node's certificate
174         // (or forwarded by a trusted node).
175         Roles []string
176
177         // Organization is the organization contained in the node's certificate
178         // (or forwarded by a trusted node).
179         Organization string
180
181         // NodeID is the node's ID, from the CN field in its certificate
182         // (or forwarded by a trusted node).
183         NodeID string
184
185         // ForwardedBy contains information for the node that forwarded this
186         // request. It is set to nil if the request was received directly.
187         ForwardedBy *RemoteNodeInfo
188
189         // RemoteAddr is the address that this node is connecting to the cluster
190         // from.
191         RemoteAddr string
192 }
193
194 // RemoteNode returns the node ID and role from the client's TLS certificate.
195 // If the RPC was forwarded, the original client's ID and role is returned, as
196 // well as the forwarder's ID. This function does not do authorization checks -
197 // it only looks up the node ID.
198 func RemoteNode(ctx context.Context) (RemoteNodeInfo, error) {
199         // If we have a value on the context that marks this as a local
200         // request, we return the node info from the context.
201         localNodeInfo := ctx.Value(LocalRequestKey)
202
203         if localNodeInfo != nil {
204                 nodeInfo, ok := localNodeInfo.(RemoteNodeInfo)
205                 if ok {
206                         return nodeInfo, nil
207                 }
208         }
209
210         certSubj, err := certSubjectFromContext(ctx)
211         if err != nil {
212                 return RemoteNodeInfo{}, err
213         }
214
215         org := ""
216         if len(certSubj.Organization) > 0 {
217                 org = certSubj.Organization[0]
218         }
219
220         peer, ok := peer.FromContext(ctx)
221         if !ok {
222                 return RemoteNodeInfo{}, grpc.Errorf(codes.PermissionDenied, "Permission denied: no peer info")
223         }
224
225         directInfo := RemoteNodeInfo{
226                 Roles:        certSubj.OrganizationalUnit,
227                 NodeID:       certSubj.CommonName,
228                 Organization: org,
229                 RemoteAddr:   peer.Addr.String(),
230         }
231
232         if isForwardedRequest(ctx) {
233                 remoteAddr, cn, org, ous := forwardedTLSInfoFromContext(ctx)
234                 if len(ous) == 0 || cn == "" || org == "" {
235                         return RemoteNodeInfo{}, grpc.Errorf(codes.PermissionDenied, "Permission denied: missing information in forwarded request")
236                 }
237                 return RemoteNodeInfo{
238                         Roles:        ous,
239                         NodeID:       cn,
240                         Organization: org,
241                         ForwardedBy:  &directInfo,
242                         RemoteAddr:   remoteAddr,
243                 }, nil
244         }
245
246         return directInfo, nil
247 }