github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/trust/trusts.go (about)

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