Skip to content

Instantly share code, notes, and snippets.

@subuk
Created September 23, 2016 10:48
Show Gist options
  • Save subuk/8c412d26639f1a41b109e3e76a81cfb7 to your computer and use it in GitHub Desktop.
Save subuk/8c412d26639f1a41b109e3e76a81cfb7 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
)
// PagerDuty API key
const PD_SERVICE_KEY = ""
// Checks configuration
var CHECKS = []CheckConfig{
{Host: "google.com", Port: 443},
}
const (
CHECK_OK = iota
CHECK_WARN = iota
CHECK_FAIL = iota
)
const (
PD_TRIGGER = iota
PD_RESOLVE = iota
)
type CheckConfig struct {
Host string
Port int
ServerName string
}
func (c *CheckConfig) SniName() string {
if c.ServerName != "" {
return c.ServerName
}
return c.Host
}
func (c *CheckConfig) String() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
func IsRevoked(cert *x509.Certificate) error {
for _, crlPoint := range cert.CRLDistributionPoints {
resp, err := http.Get(crlPoint)
if err != nil {
return fmt.Errorf("failed to fetch CRL: %s", err)
}
bodyReader := &io.LimitedReader{resp.Body, 10 * 1024 * 1024}
crlBytes, err := ioutil.ReadAll(bodyReader)
if err != nil {
return fmt.Errorf("failed to fetch CRL: %s", err)
}
crl, err := x509.ParseCRL(crlBytes)
if err != nil {
return fmt.Errorf("failed to parse CRL: %s", err)
}
for _, revokedCert := range crl.TBSCertList.RevokedCertificates {
if cert.SerialNumber.Cmp(revokedCert.SerialNumber) == 0 {
return fmt.Errorf("certificate revoked at %s", revokedCert.RevocationTime)
}
}
}
return nil
}
func Check(c CheckConfig) error {
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", c.Host, c.Port), &tls.Config{ServerName: c.SniName()})
if err != nil {
return err
}
cert := conn.ConnectionState().PeerCertificates[0]
if err := IsRevoked(cert); err != nil {
return err
}
if time.Now().Add(time.Hour * 24 * 30).After(cert.NotAfter) {
return fmt.Errorf("expire in next month")
}
if err := conn.Close(); err != nil {
return err
}
return nil
}
func SetPDIncidentState(eventId, description string, state int) {
data := map[string]string{
"service_key": PD_SERVICE_KEY,
"incident_key": eventId,
"description": description,
"client": "go-ssl-cert-checker",
}
switch state {
case PD_TRIGGER:
data["event_type"] = "trigger"
case PD_RESOLVE:
data["event_type"] = "resolve"
default:
panic(fmt.Errorf("unknown pagerduty state: %d", state))
}
body, err := json.Marshal(&data)
if err != nil {
panic(err)
}
resp, err := http.Post(
"https://events.pagerduty.com/generic/2010-04-15/create_event.json",
"application/json",
bytes.NewReader(body),
)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode > 399 {
bodyReader := &io.LimitedReader{resp.Body, 1024 * 1024}
data, err := ioutil.ReadAll(bodyReader)
if err != nil {
panic(err)
}
panic(fmt.Errorf("Bad response from pagerduty: %s", string(data)))
}
}
func main() {
for _, c := range CHECKS {
if err := Check(c); err != nil {
description := fmt.Sprintf("%s CRIT - %s", c.String(), err.Error())
fmt.Println(description)
SetPDIncidentState(c.String(), description, PD_TRIGGER)
} else {
description := fmt.Sprintf("%s OK", c.String())
fmt.Println(description)
SetPDIncidentState(c.String(), description, PD_RESOLVE)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment