trusts.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package trust
  2. import (
  3. "crypto/x509"
  4. "errors"
  5. "io/ioutil"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "sync"
  12. "time"
  13. log "github.com/Sirupsen/logrus"
  14. "github.com/docker/libtrust/trustgraph"
  15. )
  16. type TrustStore struct {
  17. path string
  18. caPool *x509.CertPool
  19. graph trustgraph.TrustGraph
  20. expiration time.Time
  21. fetcher *time.Timer
  22. fetchTime time.Duration
  23. autofetch bool
  24. httpClient *http.Client
  25. baseEndpoints map[string]*url.URL
  26. sync.RWMutex
  27. }
  28. // defaultFetchtime represents the starting duration to wait between
  29. // fetching sections of the graph. Unsuccessful fetches should
  30. // increase time between fetching.
  31. const defaultFetchtime = 45 * time.Second
  32. var baseEndpoints = map[string]string{"official": "https://dvjy3tqbc323p.cloudfront.net/trust/official.json"}
  33. func NewTrustStore(path string) (*TrustStore, error) {
  34. abspath, err := filepath.Abs(path)
  35. if err != nil {
  36. return nil, err
  37. }
  38. // Create base graph url map
  39. endpoints := map[string]*url.URL{}
  40. for name, endpoint := range baseEndpoints {
  41. u, err := url.Parse(endpoint)
  42. if err != nil {
  43. return nil, err
  44. }
  45. endpoints[name] = u
  46. }
  47. // Load grant files
  48. t := &TrustStore{
  49. path: abspath,
  50. caPool: nil,
  51. httpClient: &http.Client{},
  52. fetchTime: time.Millisecond,
  53. baseEndpoints: endpoints,
  54. }
  55. err = t.reload()
  56. if err != nil {
  57. return nil, err
  58. }
  59. return t, nil
  60. }
  61. func (t *TrustStore) reload() error {
  62. t.Lock()
  63. defer t.Unlock()
  64. matches, err := filepath.Glob(filepath.Join(t.path, "*.json"))
  65. if err != nil {
  66. return err
  67. }
  68. statements := make([]*trustgraph.Statement, len(matches))
  69. for i, match := range matches {
  70. f, err := os.Open(match)
  71. if err != nil {
  72. return err
  73. }
  74. statements[i], err = trustgraph.LoadStatement(f, nil)
  75. if err != nil {
  76. f.Close()
  77. return err
  78. }
  79. f.Close()
  80. }
  81. if len(statements) == 0 {
  82. if t.autofetch {
  83. log.Debugf("No grants, fetching")
  84. t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
  85. }
  86. return nil
  87. }
  88. grants, expiration, err := trustgraph.CollapseStatements(statements, true)
  89. if err != nil {
  90. return err
  91. }
  92. t.expiration = expiration
  93. t.graph = trustgraph.NewMemoryGraph(grants)
  94. log.Debugf("Reloaded graph with %d grants expiring at %s", len(grants), expiration)
  95. if t.autofetch {
  96. nextFetch := expiration.Sub(time.Now())
  97. if nextFetch < 0 {
  98. nextFetch = defaultFetchtime
  99. } else {
  100. nextFetch = time.Duration(0.8 * (float64)(nextFetch))
  101. }
  102. t.fetcher = time.AfterFunc(nextFetch, t.fetch)
  103. }
  104. return nil
  105. }
  106. func (t *TrustStore) fetchBaseGraph(u *url.URL) (*trustgraph.Statement, error) {
  107. req := &http.Request{
  108. Method: "GET",
  109. URL: u,
  110. Proto: "HTTP/1.1",
  111. ProtoMajor: 1,
  112. ProtoMinor: 1,
  113. Header: make(http.Header),
  114. Body: nil,
  115. Host: u.Host,
  116. }
  117. resp, err := t.httpClient.Do(req)
  118. if err != nil {
  119. return nil, err
  120. }
  121. if resp.StatusCode == 404 {
  122. return nil, errors.New("base graph does not exist")
  123. }
  124. defer resp.Body.Close()
  125. return trustgraph.LoadStatement(resp.Body, t.caPool)
  126. }
  127. // fetch retrieves updated base graphs. This function cannot error, it
  128. // should only log errors
  129. func (t *TrustStore) fetch() {
  130. t.Lock()
  131. defer t.Unlock()
  132. if t.autofetch && t.fetcher == nil {
  133. // Do nothing ??
  134. return
  135. }
  136. fetchCount := 0
  137. for bg, ep := range t.baseEndpoints {
  138. statement, err := t.fetchBaseGraph(ep)
  139. if err != nil {
  140. log.Infof("Trust graph fetch failed: %s", err)
  141. continue
  142. }
  143. b, err := statement.Bytes()
  144. if err != nil {
  145. log.Infof("Bad trust graph statement: %s", err)
  146. continue
  147. }
  148. // TODO check if value differs
  149. err = ioutil.WriteFile(path.Join(t.path, bg+".json"), b, 0600)
  150. if err != nil {
  151. log.Infof("Error writing trust graph statement: %s", err)
  152. }
  153. fetchCount++
  154. }
  155. log.Debugf("Fetched %d base graphs at %s", fetchCount, time.Now())
  156. if fetchCount > 0 {
  157. go func() {
  158. err := t.reload()
  159. if err != nil {
  160. log.Infof("Reload of trust graph failed: %s", err)
  161. }
  162. }()
  163. t.fetchTime = defaultFetchtime
  164. t.fetcher = nil
  165. } else if t.autofetch {
  166. maxTime := 10 * defaultFetchtime
  167. t.fetchTime = time.Duration(1.5 * (float64)(t.fetchTime+time.Second))
  168. if t.fetchTime > maxTime {
  169. t.fetchTime = maxTime
  170. }
  171. t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
  172. }
  173. }