...

Source file src/github.com/letsencrypt/boulder/observer/probers/tls/tls_conf.go

Documentation: github.com/letsencrypt/boulder/observer/probers/tls

     1  package probers
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"strings"
     7  
     8  	"github.com/letsencrypt/boulder/observer/probers"
     9  	"github.com/letsencrypt/boulder/strictyaml"
    10  	"github.com/prometheus/client_golang/prometheus"
    11  )
    12  
    13  const (
    14  	notAfterName  = "obs_tls_not_after"
    15  	notBeforeName = "obs_tls_not_before"
    16  	reasonName    = "obs_tls_reason"
    17  )
    18  
    19  // TLSConf is exported to receive YAML configuration.
    20  type TLSConf struct {
    21  	Hostname string `yaml:"hostname"`
    22  	RootOrg  string `yaml:"rootOrg"`
    23  	RootCN   string `yaml:"rootCN"`
    24  	Response string `yaml:"response"`
    25  }
    26  
    27  // Kind returns a name that uniquely identifies the `Kind` of `Configurer`.
    28  func (c TLSConf) Kind() string {
    29  	return "TLS"
    30  }
    31  
    32  // UnmarshalSettings takes YAML as bytes and unmarshals it to the to an TLSConf
    33  // object.
    34  func (c TLSConf) UnmarshalSettings(settings []byte) (probers.Configurer, error) {
    35  	var conf TLSConf
    36  	err := strictyaml.Unmarshal(settings, &conf)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	return conf, nil
    42  }
    43  
    44  func (c TLSConf) validateHostname() error {
    45  	url, err := url.Parse(c.Hostname)
    46  	if err != nil {
    47  		return fmt.Errorf(
    48  			"invalid 'hostname', got %q, expected a valid hostname: %s", c.Hostname, err)
    49  	}
    50  
    51  	if url.Scheme != "" {
    52  		return fmt.Errorf(
    53  			"invalid 'hostname', got: %q, should not include scheme", c.Hostname)
    54  	}
    55  
    56  	return nil
    57  }
    58  
    59  func (c TLSConf) validateResponse() error {
    60  	acceptable := []string{"valid", "expired", "revoked"}
    61  	for _, a := range acceptable {
    62  		if strings.ToLower(c.Response) == a {
    63  			return nil
    64  		}
    65  	}
    66  
    67  	return fmt.Errorf(
    68  		"invalid `response`, got %q. Must be one of %s", c.Response, acceptable)
    69  }
    70  
    71  // MakeProber constructs a `TLSProbe` object from the contents of the bound
    72  // `TLSConf` object. If the `TLSConf` cannot be validated, an error appropriate
    73  // for end-user consumption is returned instead.
    74  func (c TLSConf) MakeProber(collectors map[string]prometheus.Collector) (probers.Prober, error) {
    75  	// Validate `hostname`
    76  	err := c.validateHostname()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	// Valid `response`
    82  	err = c.validateResponse()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	// Validate the Prometheus collectors that were passed in
    88  	coll, ok := collectors[notAfterName]
    89  	if !ok {
    90  		return nil, fmt.Errorf("tls prober did not receive collector %q", notAfterName)
    91  	}
    92  
    93  	notAfterColl, ok := coll.(*prometheus.GaugeVec)
    94  	if !ok {
    95  		return nil, fmt.Errorf("tls prober received collector %q of wrong type, got: %T, expected *prometheus.GaugeVec", notAfterName, coll)
    96  	}
    97  
    98  	coll, ok = collectors[notBeforeName]
    99  	if !ok {
   100  		return nil, fmt.Errorf("tls prober did not receive collector %q", notBeforeName)
   101  	}
   102  
   103  	notBeforeColl, ok := coll.(*prometheus.GaugeVec)
   104  	if !ok {
   105  		return nil, fmt.Errorf("tls prober received collector %q of wrong type, got: %T, expected *prometheus.GaugeVec", notBeforeName, coll)
   106  	}
   107  
   108  	coll, ok = collectors[reasonName]
   109  	if !ok {
   110  		return nil, fmt.Errorf("tls prober did not receive collector %q", reasonName)
   111  	}
   112  
   113  	reasonColl, ok := coll.(*prometheus.CounterVec)
   114  	if !ok {
   115  		return nil, fmt.Errorf("tls prober received collector %q of wrong type, got: %T, expected *prometheus.CounterVec", reasonName, coll)
   116  	}
   117  
   118  	return TLSProbe{c.Hostname, c.RootOrg, c.RootCN, strings.ToLower(c.Response), notAfterColl, notBeforeColl, reasonColl}, nil
   119  }
   120  
   121  // Instrument constructs any `prometheus.Collector` objects the `TLSProbe` will
   122  // need to report its own metrics. A map is returned containing the constructed
   123  // objects, indexed by the name of the Promtheus metric.  If no objects were
   124  // constructed, nil is returned.
   125  func (c TLSConf) Instrument() map[string]prometheus.Collector {
   126  	notBefore := prometheus.Collector(prometheus.NewGaugeVec(
   127  		prometheus.GaugeOpts{
   128  			Name: notBeforeName,
   129  			Help: "Certificate notBefore value as a Unix timestamp in seconds",
   130  		}, []string{"hostname"},
   131  	))
   132  	notAfter := prometheus.Collector(prometheus.NewGaugeVec(
   133  		prometheus.GaugeOpts{
   134  			Name: notAfterName,
   135  			Help: "Certificate notAfter value as a Unix timestamp in seconds",
   136  		}, []string{"hostname"},
   137  	))
   138  	reason := prometheus.Collector(prometheus.NewCounterVec(
   139  		prometheus.CounterOpts{
   140  			Name: reasonName,
   141  			Help: fmt.Sprintf("Reason for TLS Prober check failure. Can be one of %s", getReasons()),
   142  		}, []string{"hostname", "reason"},
   143  	))
   144  	return map[string]prometheus.Collector{
   145  		notAfterName:  notAfter,
   146  		notBeforeName: notBefore,
   147  		reasonName:    reason,
   148  	}
   149  }
   150  
   151  // init is called at runtime and registers `TLSConf`, a `Prober` `Configurer`
   152  // type, as "TLS".
   153  func init() {
   154  	probers.Register(TLSConf{})
   155  }
   156  

View as plain text