-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
132 lines (116 loc) · 3.59 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
)
type Config struct {
Domains []string
ListenAddress string
IgnoreInvalidTLS bool
}
var config Config
func loadCertificates() (map[string]*x509.Certificate, string) {
startTime := time.Now().UnixNano()
var certs = make(map[string]*x509.Certificate)
var fetchMetrics = ""
for _, d := range config.Domains {
log.Println("Loading certificate for '" + d + "'...")
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: !config.IgnoreInvalidTLS}
client := http.Client{
Timeout: 1 * time.Second,
}
resp, err := client.Get("https://" + d + "/")
if err != nil {
log.Println("Error fetching '"+d+"'!", err)
fetchMetrics += "cert_fetch_success{domain=\"" + d + "\"} 0\n"
} else {
if resp.TLS != nil {
certificates := resp.TLS.PeerCertificates
if len(certificates) > 0 {
certs[d] = certificates[0]
fetchMetrics += "cert_fetch_success{domain=\"" + d + "\"} 1\n"
} else {
log.Println("No certificates given for '" + d + "'!")
fetchMetrics += "cert_fetch_success{domain=\"" + d + "\"} 0\n"
}
} else {
log.Println("TLS properties nil for '"+d+"'!", err)
fetchMetrics += "cert_fetch_success{domain=\"" + d + "\"} 0\n"
}
err1 := resp.Body.Close()
if err1 != nil {
log.Println("Error closing connection '"+d+"'!", err)
}
}
fetchMetrics += "cert_fetch_duration{domain=\"" + d + "\"} " + strconv.FormatInt(time.Now().UnixNano()-startTime, 10) + "\n"
}
return certs, fetchMetrics
}
func renderMetricsResponse() (string, error) {
certs, metrics := loadCertificates()
res := "# HELP cert_not_before The primary certificates NotBefore date as unix time'.\n" +
"# TYPE cert_not_before gauge\n" +
"# HELP cert_not_after The primary certificates NotAfter date as unix time'.\n" +
"# TYPE cert_not_after gauge\n" +
"# HELP cert_fetch_duration Duration of the http call in nanoseconds.\n" +
"# TYPE cert_fetch_duration gauge\n" +
"# HELP cert_fetch_success Success of the http call as a 0/1 boolean.\n" +
"# TYPE cert_fetch_success gauge\n" + metrics
for domain, crt := range certs {
for _, dnsName := range crt.DNSNames {
res += `cert_not_before{domain="` + domain + `",dnsname="` + dnsName + `"} ` + strconv.FormatInt(crt.NotBefore.Unix(), 10) + "\n"
res += `cert_not_after{domain="` + domain + `",dnsname="` + dnsName + `"} ` + strconv.FormatInt(crt.NotAfter.Unix(), 10) + "\n"
}
}
return res, nil
}
func handleMetrics(w http.ResponseWriter, r *http.Request) {
if r.RequestURI == "/metrics" {
response, err := renderMetricsResponse()
if err != nil {
log.Println("Error fetching metrics!", err)
w.WriteHeader(500)
return
}
_, _ = fmt.Fprint(w, response)
} else {
log.Println("Not found: '" + r.RequestURI)
w.WriteHeader(404)
}
}
func main() {
var configPath = flag.String("config", "config.json", "path to the config file")
flag.Parse()
file, err := os.Open(*configPath)
defer file.Close()
if err != nil {
log.Fatalln("Unable to open config file!", err)
return
}
decoder := json.NewDecoder(file)
config = Config{}
err1 := decoder.Decode(&config)
if err1 != nil {
log.Fatalln("Unable to read config!", err1)
return
}
server := &http.Server{
Addr: config.ListenAddress,
Handler: http.HandlerFunc(handleMetrics),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Println("Starting server...")
err2 := server.ListenAndServe()
if err2 != nil {
log.Fatalln("Unable to start server!", err2)
}
}