123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- package trust
- import (
- "crypto/x509"
- "errors"
- "io/ioutil"
- "net/http"
- "net/url"
- "os"
- "path"
- "path/filepath"
- "sync"
- "time"
- log "github.com/Sirupsen/logrus"
- "github.com/docker/libtrust/trustgraph"
- )
- type TrustStore struct {
- path string
- caPool *x509.CertPool
- graph trustgraph.TrustGraph
- expiration time.Time
- fetcher *time.Timer
- fetchTime time.Duration
- autofetch bool
- httpClient *http.Client
- baseEndpoints map[string]*url.URL
- sync.RWMutex
- }
- // defaultFetchtime represents the starting duration to wait between
- // fetching sections of the graph. Unsuccessful fetches should
- // increase time between fetching.
- const defaultFetchtime = 45 * time.Second
- var baseEndpoints = map[string]string{"official": "https://dvjy3tqbc323p.cloudfront.net/trust/official.json"}
- func NewTrustStore(path string) (*TrustStore, error) {
- abspath, err := filepath.Abs(path)
- if err != nil {
- return nil, err
- }
- // Create base graph url map
- endpoints := map[string]*url.URL{}
- for name, endpoint := range baseEndpoints {
- u, err := url.Parse(endpoint)
- if err != nil {
- return nil, err
- }
- endpoints[name] = u
- }
- // Load grant files
- t := &TrustStore{
- path: abspath,
- caPool: nil,
- httpClient: &http.Client{},
- fetchTime: time.Millisecond,
- baseEndpoints: endpoints,
- }
- err = t.reload()
- if err != nil {
- return nil, err
- }
- return t, nil
- }
- func (t *TrustStore) reload() error {
- t.Lock()
- defer t.Unlock()
- matches, err := filepath.Glob(filepath.Join(t.path, "*.json"))
- if err != nil {
- return err
- }
- statements := make([]*trustgraph.Statement, len(matches))
- for i, match := range matches {
- f, err := os.Open(match)
- if err != nil {
- return err
- }
- statements[i], err = trustgraph.LoadStatement(f, nil)
- if err != nil {
- f.Close()
- return err
- }
- f.Close()
- }
- if len(statements) == 0 {
- if t.autofetch {
- log.Debugf("No grants, fetching")
- t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
- }
- return nil
- }
- grants, expiration, err := trustgraph.CollapseStatements(statements, true)
- if err != nil {
- return err
- }
- t.expiration = expiration
- t.graph = trustgraph.NewMemoryGraph(grants)
- log.Debugf("Reloaded graph with %d grants expiring at %s", len(grants), expiration)
- if t.autofetch {
- nextFetch := expiration.Sub(time.Now())
- if nextFetch < 0 {
- nextFetch = defaultFetchtime
- } else {
- nextFetch = time.Duration(0.8 * (float64)(nextFetch))
- }
- t.fetcher = time.AfterFunc(nextFetch, t.fetch)
- }
- return nil
- }
- func (t *TrustStore) fetchBaseGraph(u *url.URL) (*trustgraph.Statement, error) {
- req := &http.Request{
- Method: "GET",
- URL: u,
- Proto: "HTTP/1.1",
- ProtoMajor: 1,
- ProtoMinor: 1,
- Header: make(http.Header),
- Body: nil,
- Host: u.Host,
- }
- resp, err := t.httpClient.Do(req)
- if err != nil {
- return nil, err
- }
- if resp.StatusCode == 404 {
- return nil, errors.New("base graph does not exist")
- }
- defer resp.Body.Close()
- return trustgraph.LoadStatement(resp.Body, t.caPool)
- }
- // fetch retrieves updated base graphs. This function cannot error, it
- // should only log errors
- func (t *TrustStore) fetch() {
- t.Lock()
- defer t.Unlock()
- if t.autofetch && t.fetcher == nil {
- // Do nothing ??
- return
- }
- fetchCount := 0
- for bg, ep := range t.baseEndpoints {
- statement, err := t.fetchBaseGraph(ep)
- if err != nil {
- log.Infof("Trust graph fetch failed: %s", err)
- continue
- }
- b, err := statement.Bytes()
- if err != nil {
- log.Infof("Bad trust graph statement: %s", err)
- continue
- }
- // TODO check if value differs
- err = ioutil.WriteFile(path.Join(t.path, bg+".json"), b, 0600)
- if err != nil {
- log.Infof("Error writing trust graph statement: %s", err)
- }
- fetchCount++
- }
- log.Debugf("Fetched %d base graphs at %s", fetchCount, time.Now())
- if fetchCount > 0 {
- go func() {
- err := t.reload()
- if err != nil {
- log.Infof("Reload of trust graph failed: %s", err)
- }
- }()
- t.fetchTime = defaultFetchtime
- t.fetcher = nil
- } else if t.autofetch {
- maxTime := 10 * defaultFetchtime
- t.fetchTime = time.Duration(1.5 * (float64)(t.fetchTime+time.Second))
- if t.fetchTime > maxTime {
- t.fetchTime = maxTime
- }
- t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
- }
- }
|