...

Source file src/github.com/google/certificate-transparency-go/submission/proxy.go

Documentation: github.com/google/certificate-transparency-go/submission

     1  // Copyright 2019 Google LLC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package submission contains code and structs for certificates submission proxy.
    16  package submission
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	ct "github.com/google/certificate-transparency-go"
    25  	"github.com/google/certificate-transparency-go/asn1"
    26  	"github.com/google/certificate-transparency-go/ctpolicy"
    27  	"github.com/google/certificate-transparency-go/loglist3"
    28  	"github.com/google/certificate-transparency-go/schedule"
    29  	"github.com/google/certificate-transparency-go/tls"
    30  	"github.com/google/certificate-transparency-go/x509util"
    31  	"github.com/google/trillian/monitoring"
    32  	"k8s.io/klog/v2"
    33  )
    34  
    35  // CTPolicyType indicates CT-policy used for certificate submission.
    36  type CTPolicyType int
    37  
    38  // Policy type values:
    39  const (
    40  	ChromeCTPolicy CTPolicyType = iota
    41  	AppleCTPolicy
    42  )
    43  
    44  var (
    45  	proxyOnce      sync.Once
    46  	logListUpdates monitoring.Counter
    47  	rspLatency     monitoring.Histogram // ep => value
    48  )
    49  
    50  // proxyInitMetrics initializes all the exported metrics.
    51  func proxyInitMetrics(mf monitoring.MetricFactory) {
    52  	logListUpdates = mf.NewCounter("log_list_updates", "Number of Log-list updates")
    53  	rspLatency = mf.NewHistogram("http_latency", "Latency of policy-multiplexed add-responses in seconds", "ep")
    54  }
    55  
    56  // DistributorBuilder builds distributor instance for a given Log list.
    57  type DistributorBuilder func(*loglist3.LogList) (*Distributor, error)
    58  
    59  // GetDistributorBuilder given CT-policy type and Log-client builder produces
    60  // Distributor c-tor.
    61  func GetDistributorBuilder(plc CTPolicyType, lcBuilder LogClientBuilder, mf monitoring.MetricFactory) DistributorBuilder {
    62  	if plc == AppleCTPolicy {
    63  		return func(ll *loglist3.LogList) (*Distributor, error) {
    64  			return NewDistributor(ll, ctpolicy.AppleCTPolicy{}, lcBuilder, mf)
    65  		}
    66  	}
    67  	return func(ll *loglist3.LogList) (*Distributor, error) {
    68  		return NewDistributor(ll, ctpolicy.ChromeCTPolicy{}, lcBuilder, mf)
    69  	}
    70  }
    71  
    72  // ASN1MarshalSCTs serializes list of AssignedSCTs according to RFC6962 3.3
    73  func ASN1MarshalSCTs(scts []*AssignedSCT) ([]byte, error) {
    74  	if len(scts) == 0 {
    75  		return nil, fmt.Errorf("ASN1MarshalSCTs requires positive number of SCTs, 0 provided")
    76  	}
    77  	unassignedSCTs := make([]*ct.SignedCertificateTimestamp, 0, len(scts))
    78  	for _, sct := range scts {
    79  		unassignedSCTs = append(unassignedSCTs, sct.SCT)
    80  	}
    81  	sctList, err := x509util.MarshalSCTsIntoSCTList(unassignedSCTs)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	encdSCTList, err := tls.Marshal(*sctList)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	encoded, err := asn1.Marshal(encdSCTList)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	return encoded, nil
    94  }
    95  
    96  // Proxy wraps Log List updates watcher and Distributor running on fresh Log List.
    97  type Proxy struct {
    98  	Init chan bool
    99  
   100  	llRefreshInterval    time.Duration
   101  	rootsRefreshInterval time.Duration
   102  
   103  	llWatcher          *LogListManager
   104  	distributorBuilder DistributorBuilder
   105  
   106  	distMu     sync.RWMutex // guards the distributor
   107  	dist       *Distributor
   108  	distCancel context.CancelFunc // used to cancel distributor updates
   109  }
   110  
   111  // NewProxy creates an inactive Proxy instance. Call Run() to activate.
   112  func NewProxy(llm *LogListManager, db DistributorBuilder, mf monitoring.MetricFactory) *Proxy {
   113  	var p Proxy
   114  	p.llWatcher = llm
   115  	p.distributorBuilder = db
   116  	p.Init = make(chan bool, 1)
   117  	p.rootsRefreshInterval = 24 * time.Hour
   118  
   119  	if mf == nil {
   120  		mf = monitoring.InertMetricFactory{}
   121  	}
   122  	proxyOnce.Do(func() { proxyInitMetrics(mf) })
   123  
   124  	return &p
   125  }
   126  
   127  // Run starts regular LogList checks and associated Distributor initialization.
   128  // Sends true via Init channel when init is complete.
   129  // Terminates upon context cancellation.
   130  func (p *Proxy) Run(ctx context.Context, llRefresh time.Duration, rootsRefresh time.Duration) {
   131  	init := false
   132  	p.llRefreshInterval = llRefresh
   133  	p.rootsRefreshInterval = rootsRefresh
   134  	p.llWatcher.Run(ctx, llRefresh)
   135  
   136  	go func() {
   137  		for {
   138  			select {
   139  			case <-ctx.Done():
   140  				if !init {
   141  					close(p.Init)
   142  				}
   143  				return
   144  			case llData := <-p.llWatcher.LLUpdates:
   145  				logListUpdates.Inc()
   146  				if err := p.restartDistributor(ctx, llData.List); err != nil {
   147  					klog.Errorf("Unable to use Log-list:\n %v\n %v", err, llData.JSON)
   148  				} else if !init {
   149  					init = true
   150  					p.Init <- true
   151  					close(p.Init)
   152  				}
   153  			case err := <-p.llWatcher.Errors:
   154  				klog.Error(err)
   155  			}
   156  		}
   157  	}()
   158  }
   159  
   160  // restartDistributor activates new Distributor instance with Log List provided
   161  // and sets it as active.
   162  func (p *Proxy) restartDistributor(ctx context.Context, ll *loglist3.LogList) error {
   163  	d, err := p.distributorBuilder(ll)
   164  	if err != nil {
   165  		// losing ll info. No good.
   166  		return err
   167  	}
   168  
   169  	// Start refreshing roots periodically so they stay up-to-date.
   170  	refreshCtx, refreshCancel := context.WithCancel(ctx)
   171  	go schedule.Every(refreshCtx, p.rootsRefreshInterval, func(ectx context.Context) {
   172  		if errs := d.RefreshRoots(ectx); len(errs) > 0 {
   173  			for _, err := range errs {
   174  				klog.Warning(err)
   175  			}
   176  		}
   177  	})
   178  
   179  	p.distMu.Lock()
   180  	defer p.distMu.Unlock()
   181  	if p.distCancel != nil {
   182  		p.distCancel()
   183  	}
   184  	p.dist = d
   185  	p.distCancel = refreshCancel
   186  	return nil
   187  }
   188  
   189  // AddPreChain passes call to underlying Distributor instance.
   190  func (p *Proxy) AddPreChain(ctx context.Context, rawChain [][]byte, loadPendingLogs bool) ([]*AssignedSCT, error) {
   191  	if p.dist == nil {
   192  		return []*AssignedSCT{}, fmt.Errorf("proxy distributor is not initialized. call Run()")
   193  	}
   194  
   195  	defer func(start time.Time) {
   196  		rspLatency.Observe(time.Since(start).Seconds(), "add-pre-chain")
   197  	}(time.Now())
   198  	return p.dist.AddPreChain(ctx, rawChain, loadPendingLogs)
   199  }
   200  
   201  // AddChain passes call to underlying Distributor instance.
   202  func (p *Proxy) AddChain(ctx context.Context, rawChain [][]byte, loadPendingLogs bool) ([]*AssignedSCT, error) {
   203  	if p.dist == nil {
   204  		return []*AssignedSCT{}, fmt.Errorf("proxy distributor is not initialized. call Run()")
   205  	}
   206  	defer func(start time.Time) {
   207  		rspLatency.Observe(time.Since(start).Seconds(), "add-chain")
   208  	}(time.Now())
   209  	return p.dist.AddChain(ctx, rawChain, loadPendingLogs)
   210  }
   211  

View as plain text