auth.go 8.1 KB

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