transport.txt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. Using the transport package
  2. ===========================
  3. The transport package is designed to provide automated mutually-
  4. authenticated and server-only TLS security with proper security
  5. settings for Go programs. Mutually-authenticated means that clients
  6. will strictly validate the server's certificate, and servers will
  7. require that clients present a valid client authentication
  8. certificate.
  9. Adding the transport package to a project consists of a few steps:
  10. 1. Planning the right communications model.
  11. 2. Determining the configuration.
  12. 3. Adding the transport package to the project.
  13. Each of these steps will be covered in sequence, with the examples
  14. under transport/example/ used as illustrations. Some terminology:
  15. certificate provider: an interface to a CA that will sign CSRs and
  16. return certificates. The only available certificate provider at
  17. this time is CFSSL.
  18. key provider: mechanism for providing keys and signing certificate
  19. signing requests (CSRs). The only available key provider at this
  20. time is a disk-backed key set.
  21. root: the public certificate for a certificate authority (CA). This
  22. certificate is used to verify the certificate of a remote system: a
  23. client authentication root specifies the CA that a server uses to
  24. verify clients. The unqualified term root usually refers to a CA
  25. certificate that a client uses to verify a server's certificate.
  26. Transport package communication models
  27. --------------------------------------
  28. There are three models for communications:
  29. 1. A general TLS listener, such as a public HTTPS server. In this
  30. model, the server does not expect clients to authenticate
  31. themselves to the server using client authentication
  32. certificates. This listener doesn't need to configure any roots.
  33. 2. A mutually-authenticated TCP server. This can be an HTTPS server
  34. that requires client authentication, or any other TCP server that
  35. wants to set up mutually-authenticated communications. A server
  36. will construct a `Listener` and call the `Listen` method on that
  37. structure (this will be useful to remember later).
  38. 3. A mutually-authenticated TCP client. This can be an HTTPS client
  39. that supplies a client authentication certificate, or any other TCP
  40. client setting up a mutually-authenticated connection. A client
  41. will call `Dial` from a `Transport` structure.
  42. Once the model is determined, the configuration can be built.
  43. Transport package configuration
  44. -------------------------------
  45. In general, the transport package is build around the `core.Identity`
  46. type. This contains several top-level fields:
  47. + `Request` contains a `CertificateRequest` from the CFSSL csr package
  48. (e.g. https://godoc.org/github.com/cloudflare/cfssl/csr#CertificateRequest).
  49. The JSON tag for this field is "request".
  50. + `Profiles` contains profiles for certificate and key providers. The
  51. JSON tag for this field is "profiles".
  52. + `Roots` specifies roots that are used by clients to verify server
  53. certificates. The JSON tag for this field is "roots".
  54. + `ClientRoots` specifies roots that are used by servers to verify
  55. client certificates. The JSON tag for this field is "client_roots".
  56. The `Identity` structure is set up so that it could be integrated into
  57. a current configuration set up, or it can be present as a standalone
  58. configuration. The example programs use the code
  59. // conf is a string contain the path to a JSON configuration
  60. // file.
  61. var id = new(core.Identity)
  62. data, err := os.ReadFile(conf)
  63. if err != nil {
  64. exlib.Err(1, err, "reading config file")
  65. }
  66. err = json.Unmarshal(data, id)
  67. if err != nil {
  68. exlib.Err(1, err, "parsing config file")
  69. }
  70. to load a standalone transport configuration file in JSON.
  71. The `Profiles` field configures both key providers and certificate
  72. authorities.
  73. Key providers use the key request in the `Request` field to determine
  74. what sort of key to generate, and the rest of the field to determine
  75. the certificate signing request to generate.
  76. The only key-provider supported right now is the "path" provider,
  77. which would be configured something like
  78. // id is a core.Identity value.
  79. id.Profiles["path"] = map[string]string{
  80. "private_key": "/path/to/key.pem",
  81. "certificate": "/path/to/cert.pem",
  82. }
  83. The path provider determines whether a private key exists at the path
  84. provided; if it does not, a key is generated. If a certificate exists
  85. at the configured path, it is loaded --- if it's valid, it's
  86. kept. Otherwise, when the transport setup occurs, a new certificate
  87. will be requested. If the path fields are empty, then the keys will
  88. never be stored on disk; the "path" key must still be present though.
  89. // A path configuration for keys that are never stored on
  90. // disk.
  91. id["profiles"]["path"] = map[string]string{}
  92. When the key provider determines that its certificate is out of date
  93. (or, in the case of auto-updating, at some interval before the
  94. certificate expires), it will generate a CSR and pass it to a
  95. certificate provider.
  96. A CFSSL certificate provider points to a CFSSL server. It supports the
  97. following keys:
  98. + "remote" provides the hostname/IP and port for the CFSSL server.
  99. + "label" identifies which signer in a multiroot CFSSL should be
  100. used. An empty or missing label assumes the remote's default
  101. label will be used.
  102. + "profile" identifies the signing profile. An empty or missing
  103. profile assumes the remote's default profile will be used.
  104. + "auth-type" should be present if the remote CFSSL needs
  105. authentication. It tells the transport package what type of
  106. authentication to use. The authentication system in CFSSL
  107. is documented in "doc/authentication.txt"; for now, the
  108. only available authentication type is "standard".
  109. + "auth-key" specifies the authentication key in the case where the
  110. remote CFSSL requires authentication. Details are in
  111. "doc/authentication.txt", particularly the section covering key
  112. specification. As of now, The key may be specified in one of three
  113. ways:
  114. * hex-encoded string (e.g. "000102030405060708")
  115. * an environment variable prefixed with "env:"
  116. (e.g. "env:AUTH_KEY") that contains a hex-encoded string.
  117. * a path to a file containing the hex-encoded key, prefixed with
  118. "file:" (e.g. "file:/path/to/auth.key")
  119. A configuration that talks to the CFSSL instance running on
  120. ca.example.org might look like
  121. id["profiles"]["cfssl"] = map[string]string{
  122. "remote": "ca.example.org:8888",
  123. "profile": "short-lived",
  124. "auth-type": "standard",
  125. "auth-key": "env:TRANSPORT_CA_AUTH_KEY",
  126. }
  127. where the auth key would be set up as
  128. $ TRANSPORT_CA_AUTH_KEY="000102030405060708" ./some-program
  129. The `Roots` and `ClientRoots` fields are set up the same way; they
  130. differ only in how they are used. The are an array of root
  131. structures. There are three supported types of roots, each specified
  132. with the "type" key:
  133. + system roots use the operating system's default set of roots
  134. + file load PEM-encoded certificates from a file
  135. + cfssl retrieves the CA certificate from a remote CFSSL instance
  136. The file and cfssl types should contain a "metadata" key that contains
  137. a `map[string]string` with further information. The file type looks
  138. for the "source" metadata key, which should contain a path to the file
  139. to be loaded. The cfssl type accepts the same arguments as the CFSSL
  140. certificate provider; note that CFSSL servers don't authenticate the
  141. info endpoint. If the metadata contains authentication information
  142. (e.g. because it was copied from the certificate provider
  143. specification), the authentication keys will be ignored.
  144. The following example loads the system roots, a set of root
  145. certificates stored in a "custom.pem" file, and the same CFSSL
  146. instance used above; they are used for server authentication in this
  147. example.
  148. id["roots"] = []*core.Root{
  149. {
  150. Type: "system",
  151. },
  152. {
  153. Type: "file",
  154. Metadata: map[string]string{
  155. "source": "/etc/ssl/custom.pem",
  156. },
  157. },
  158. {
  159. Type: "cfssl",
  160. Metadata: map[string]string{
  161. "remote": "ca.example.org:8888",
  162. "profile": "short-lived",
  163. },
  164. },
  165. }
  166. If the above configuration was placed into a JSON file, it would look
  167. like:
  168. {
  169. "request": {
  170. "CN": "Example Service Client",
  171. "hosts": [
  172. "svc-client.example.org"
  173. ]
  174. },
  175. "profiles": {
  176. "path": {
  177. "private_key": "/path/to/key.pem",
  178. "certificate": "/path/to/cert.pem"
  179. },
  180. "cfssl": {
  181. "remote": "ca.example.org:8888",
  182. "profile": "short-lived",
  183. "auth-type": "standard",
  184. "auth-key": "env:TRANSPORT_CA_AUTH_KEY"
  185. }
  186. },
  187. "roots": [
  188. {
  189. "type": "system"
  190. },
  191. {
  192. "type": "file",
  193. "metadata": {
  194. "source": "/etc/ssl/custom.pem"
  195. }
  196. },
  197. {
  198. "type": "cfssl",
  199. "metadata": {
  200. "remote": "ca.example.org:8888",
  201. "profile": "short-lived"
  202. }
  203. }
  204. ]
  205. }
  206. This configuration would be used for a system using the third
  207. communications model discussed above. It could also be integrated into
  208. an existing configuration; an example of such a configuration would be
  209. type Configuration struct {
  210. Remote string // Server to connect to.
  211. Port int // Server's port.
  212. // Additional configuration fields follow
  213. Transport *core.Identity
  214. }
  215. Now that the service has a configuration, the transport package can be
  216. integrated into the code.
  217. Adding the transport package to a project
  218. -----------------------------------------
  219. Somehow, the program needs to load the `Identity` described in the
  220. previous section. For the sake of this discussion, it's assumed to
  221. be in the `id` variable:
  222. var id core.Identity
  223. The next step is to build a `Transport` from this. A `Transport` is
  224. set up with a "before" time (how long before the certificate expires
  225. should the service attempt to update the certificate) and a
  226. `*Identity`.
  227. // The default is to get a new certificate one day prior to
  228. // its expiration.
  229. tr, err := transport.New(core.DefaultBefore, &id)
  230. if err != nil {
  231. log.Fatalf("failed to configure a new TLS transport: %s", err)
  232. }
  233. The auto-updater must be configured explicitly. It takes two
  234. arguments: the update channel and an error channel. If the update
  235. channel is non-nil, it will receive `time.Time` values indicating when
  236. the certificate was renewed. If the error channel is non-nil, it will
  237. receive `error` values from the auto updater.
  238. If an error in updating occurs, the updater will use a backoff to keep
  239. retrying. If the backoff isn't configured, a default backoff (using
  240. an interval of 5 minutes and a max delay of six hours) will be
  241. used. The values for the default interval and maximum duration are
  242. found in the `DefaultInterval` and `DefaultMaxDuration` variables in
  243. the `backoff` package; these can be changed to suit the program's needs.
  244. (c.f https://godoc.org/github.com/cloudflare/backoff#Backoff).
  245. Clients will call `AutoUpdate` on the `Transport` itself; servers
  246. should call `AutoUpdate` on the listener (discussed below).
  247. The following example logs update timestamps and errors for a client:
  248. updates := make(chan time.Time, 0)
  249. go func(updatesc <-chan time.Time) {
  250. for {
  251. t, ok := <-updatesc
  252. if !ok {
  253. return
  254. }
  255. log.Printf("certificate auto-updated at %s",
  256. t.Format(time.RFC3339))
  257. }
  258. }
  259. errs := make(chan error, 0)
  260. go func(errsc <-chan error) {
  261. for {
  262. err, ok := <-errsc
  263. if !ok {
  264. return
  265. }
  266. log.Printf("certificate auto-update error: %s", err)
  267. }
  268. }
  269. go tr.AutoUpdate(updates, errs)
  270. A client may not want to start the auto-updater if the connection is
  271. expected to be short-lived. The package will check the certificate
  272. before the connection occurs, making sure it's still valid.
  273. At this point, the client can call the `Dial` function:
  274. conn, err := transport.Dial(address, tr)
  275. if err != nil {
  276. log.Fatalf("failed to dial remote host: %s", err)
  277. }
  278. The returned `conn` is a `*tls.Conn`, which is an implementation of
  279. `net.Conn`.
  280. Servers need one extra step before they are ready:
  281. l, err := transport.Listen(address, tr)
  282. if err != nil {
  283. loglFatalf("error setting up listener: %s", err)
  284. }
  285. // The same update channels
  286. go l.AutoUpdate(nil, nil)
  287. defer l.Close()
  288. for {
  289. conn, err := l.Accept()
  290. if err != nil {
  291. log.Printf("connection failed: %s", err)
  292. continue
  293. }
  294. log.Printf("connection from %s", conn.RemoteAddr())
  295. go serveClient(conn)
  296. }
  297. Extending the transport package
  298. ===============================
  299. The package is set up to deliver a useful set of defaults, but these
  300. defaults won't be appropriate for everyone. There are several places
  301. where the behaviour can be altered.
  302. Additional root providers may be set up by adding the relevant entries
  303. to `roots.Providers`. This is a map of string names (e.g. the `Type`
  304. field) to a function that accepts the `Metadata` field (a
  305. `map[string]string`), and which returns a list of `*x509.Certificate`:
  306. var Providers map[string]func(map[string]string) ([]*x509.Certificate, error)
  307. The `NewKeyProvider` and `NewCA` functions provide a mechanism for
  308. choosing a key provider and CA from an identity. The default is to
  309. attempt to load the standard ("path") key provider and a CFSSL CA:
  310. var (
  311. // NewKeyProvider is the function used to build key providers
  312. // from some identity.
  313. NewKeyProvider = func(id *core.Identity) (kp.KeyProvider, error) {
  314. return kp.NewStandardProvider(id)
  315. }
  316. // NewCA is used to load a configuration for a certificate
  317. // authority.
  318. NewCA = func(id *core.Identity) (ca.CertificateAuthority, error) {
  319. return ca.NewCFSSLProvider(id, nil)
  320. }
  321. )
  322. By default, `AutoUpdate` checks the expiry on the certificate every
  323. thirty seconds. This behaviour may be changed by changing
  324. `transport.PollInterval`. If set to 0, the updater will just wait for
  325. the lifespan of the certificate.