github.com/christopherobin/docker@v1.6.2/trust/trusts.go (about)

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