cfssl_provider.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. package ca
  2. import (
  3. "crypto/x509"
  4. "encoding/json"
  5. "encoding/pem"
  6. "errors"
  7. "fmt"
  8. "net"
  9. "path/filepath"
  10. "github.com/cloudflare/cfssl/api/client"
  11. "github.com/cloudflare/cfssl/auth"
  12. "github.com/cloudflare/cfssl/config"
  13. "github.com/cloudflare/cfssl/helpers"
  14. "github.com/cloudflare/cfssl/info"
  15. "github.com/cloudflare/cfssl/signer"
  16. "github.com/cloudflare/cfssl/transport/core"
  17. )
  18. type authError struct {
  19. authType string
  20. }
  21. func (err *authError) Error() string {
  22. return fmt.Sprintf("transport: unsupported CFSSL authentication method %s", err.authType)
  23. }
  24. // This approach allows us to quickly add other providers later, such
  25. // as the TPM.
  26. var authTypes = map[string]func(config.AuthKey, []byte) (auth.Provider, error){
  27. "standard": newStandardProvider,
  28. }
  29. // Create a standard provider without providing any additional data.
  30. func newStandardProvider(ak config.AuthKey, ad []byte) (auth.Provider, error) {
  31. return auth.New(ak.Key, ad)
  32. }
  33. // Create a new provider from an authentication key and possibly
  34. // additional data.
  35. func newProvider(ak config.AuthKey, ad []byte) (auth.Provider, error) {
  36. // If no auth key was provided, use unauthenticated
  37. // requests. This is useful when a local CFSSL is being used.
  38. if ak.Type == "" && ak.Key == "" {
  39. return nil, nil
  40. }
  41. f, ok := authTypes[ak.Type]
  42. if !ok {
  43. return nil, &authError{authType: ak.Type}
  44. }
  45. return f(ak, ad)
  46. }
  47. // ErrNoAuth is returned when a client is talking to a CFSSL remote
  48. // that is not on a loopback address and doesn't have an
  49. // authentication provider set.
  50. var ErrNoAuth = errors.New("transport: authentication is required for non-local remotes")
  51. var v4Loopback = net.IPNet{
  52. IP: net.IP{127, 0, 0, 0},
  53. Mask: net.IPv4Mask(255, 0, 0, 0),
  54. }
  55. func ipIsLocal(ip net.IP) bool {
  56. if ip.To4() == nil {
  57. return ip.Equal(net.IPv6loopback)
  58. }
  59. return v4Loopback.Contains(ip)
  60. }
  61. // The only time a client should be doing unauthenticated requests is
  62. // when a local CFSSL is being used.
  63. func (cap *CFSSL) validateAuth() error {
  64. // The client is using some form of authentication, and the best way
  65. // to figure out that the auth is invalid is when it's used. Therefore,
  66. // we'll delay checking the credentials until that time.
  67. if cap.provider != nil {
  68. return nil
  69. }
  70. hosts := cap.remote.Hosts()
  71. for i := range hosts {
  72. ips, err := net.LookupIP(hosts[i])
  73. if err != nil {
  74. return err
  75. }
  76. for _, ip := range ips {
  77. if !ipIsLocal(ip) {
  78. return ErrNoAuth
  79. }
  80. }
  81. }
  82. return nil
  83. }
  84. var cfsslConfigDirs = []string{
  85. "/usr/local/cfssl",
  86. "/etc/cfssl",
  87. "/state/etc/cfssl",
  88. }
  89. // The CFSSL standard is to have a configuration file for a label as
  90. // <config>.json.
  91. func findLabel(label string) *config.Config {
  92. for _, dir := range cfsslConfigDirs {
  93. cfgFile := filepath.Join(dir, label+".json")
  94. cfg, err := config.LoadFile(cfgFile)
  95. if err == nil {
  96. return cfg
  97. }
  98. }
  99. return nil
  100. }
  101. func getProfile(cfg *config.Config, profileName string) (*config.SigningProfile, bool) {
  102. if cfg == nil || cfg.Signing == nil || cfg.Signing.Default == nil {
  103. return nil, false
  104. }
  105. var ok bool
  106. profile := cfg.Signing.Default
  107. if profileName != "" {
  108. if cfg.Signing.Profiles == nil {
  109. return nil, false
  110. }
  111. profile, ok = cfg.Signing.Profiles[profileName]
  112. if !ok {
  113. return nil, false
  114. }
  115. }
  116. return profile, true
  117. }
  118. // loadAuth loads an authentication provider from the client config's
  119. // explicitly set auth key.
  120. func (cap *CFSSL) loadAuth() error {
  121. var err error
  122. cap.provider, err = newProvider(cap.DefaultAuth, nil)
  123. return err
  124. }
  125. func getRemote(cfg *config.Config, profile *config.SigningProfile) (string, bool) {
  126. // NB: Loading the config will validate that the remote is
  127. // present in the config's remote section.
  128. if profile.RemoteServer != "" {
  129. return profile.RemoteServer, true
  130. }
  131. return "", false
  132. }
  133. func (cap *CFSSL) setRemoteAndAuth() error {
  134. if cap.Label != "" {
  135. cfsslConfig := findLabel(cap.Label)
  136. profile, ok := getProfile(cfsslConfig, cap.Profile)
  137. if ok {
  138. remote, ok := getRemote(cfsslConfig, profile)
  139. if ok {
  140. cap.remote = client.NewServerTLS(remote, helpers.CreateTLSConfig(profile.RemoteCAs, profile.ClientCert))
  141. cap.provider = profile.Provider
  142. return nil
  143. }
  144. // The profile may not have a remote set, but
  145. // it may have an authentication provider.
  146. cap.provider = profile.Provider
  147. }
  148. }
  149. cap.remote = cap.DefaultRemote
  150. if cap.provider != nil {
  151. return nil
  152. }
  153. return cap.loadAuth()
  154. }
  155. // CFSSL provides support for signing certificates via
  156. // CFSSL.
  157. type CFSSL struct {
  158. remote client.Remote
  159. provider auth.Provider
  160. Profile string
  161. Label string
  162. DefaultRemote client.Remote
  163. DefaultAuth config.AuthKey
  164. }
  165. // SignCSR requests a certificate from a CFSSL signer.
  166. func (cap *CFSSL) SignCSR(csrPEM []byte) (cert []byte, err error) {
  167. p, _ := pem.Decode(csrPEM)
  168. if p == nil || p.Type != "CERTIFICATE REQUEST" {
  169. return nil, errors.New("transport: invalid PEM-encoded certificate signing request")
  170. }
  171. csr, err := x509.ParseCertificateRequest(p.Bytes)
  172. if err != nil {
  173. return nil, err
  174. }
  175. hosts := make([]string, len(csr.DNSNames), len(csr.DNSNames)+len(csr.IPAddresses)+len(csr.URIs))
  176. copy(hosts, csr.DNSNames)
  177. for i := range csr.IPAddresses {
  178. hosts = append(hosts, csr.IPAddresses[i].String())
  179. }
  180. for i := range csr.URIs {
  181. hosts = append(hosts, csr.URIs[i].String())
  182. }
  183. sreq := &signer.SignRequest{
  184. Hosts: hosts,
  185. Request: string(csrPEM),
  186. Profile: cap.Profile,
  187. Label: cap.Label,
  188. }
  189. out, err := json.Marshal(sreq)
  190. if err != nil {
  191. return nil, err
  192. }
  193. if cap.provider != nil {
  194. return cap.remote.AuthSign(out, nil, cap.provider)
  195. }
  196. return cap.remote.Sign(out)
  197. }
  198. // CACertificate returns the certificate for a CFSSL CA.
  199. func (cap *CFSSL) CACertificate() ([]byte, error) {
  200. req := &info.Req{
  201. Label: cap.Label,
  202. Profile: cap.Profile,
  203. }
  204. out, err := json.Marshal(req)
  205. if err != nil {
  206. return nil, err
  207. }
  208. resp, err := cap.remote.Info(out)
  209. if err != nil {
  210. return nil, err
  211. }
  212. return []byte(resp.Certificate), nil
  213. }
  214. // NewCFSSLProvider takes the configuration information from an
  215. // Identity (and an optional default remote), returning a CFSSL
  216. // instance. There should be a profile in id called "cfssl", which
  217. // should contain label and profile fields as needed.
  218. func NewCFSSLProvider(id *core.Identity, defaultRemote client.Remote) (*CFSSL, error) {
  219. if id == nil {
  220. return nil, errors.New("transport: the identity hasn't been initialised. Has it been loaded from disk?")
  221. }
  222. cap := &CFSSL{
  223. DefaultRemote: defaultRemote,
  224. }
  225. cfssl := id.Profiles["cfssl"]
  226. if cfssl != nil {
  227. cap.Label = cfssl["label"]
  228. cap.Profile = cfssl["profile"]
  229. if cap.DefaultRemote == nil {
  230. cert, err := helpers.LoadClientCertificate(cfssl["mutual-tls-cert"], cfssl["mutual-tls-key"])
  231. if err != nil {
  232. return nil, err
  233. }
  234. remoteCAs, err := helpers.LoadPEMCertPool(cfssl["tls-remote-ca"])
  235. if err != nil {
  236. return nil, err
  237. }
  238. cap.DefaultRemote = client.NewServerTLS(cfssl["remote"], helpers.CreateTLSConfig(remoteCAs, cert))
  239. }
  240. cap.DefaultAuth.Type = cfssl["auth-type"]
  241. cap.DefaultAuth.Key = cfssl["auth-key"]
  242. }
  243. err := cap.setRemoteAndAuth()
  244. if err != nil {
  245. return nil, err
  246. }
  247. return cap, nil
  248. }