feat: support tls configuration
This commit is contained in:
parent
44d57f9d88
commit
c81b4df0f1
42
README.md
42
README.md
|
@ -70,6 +70,48 @@ then it's up and healthy. If the value is 0, then it's down.
|
|||
`uptime_latency_seconds` indicates the time it took to do a HTTP request to that specified `endpoint_address`
|
||||
field in second.
|
||||
|
||||
## Configuration file schema
|
||||
|
||||
If you want to do config.json.
|
||||
|
||||
```json
|
||||
{
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "string",
|
||||
"address": "https://url",
|
||||
"method": "GET",
|
||||
"timeout": 123,
|
||||
"successful_status_code": "2xx",
|
||||
"inverse_status": false,
|
||||
"tls_configuration": {
|
||||
"certificate_authority_path": "/path/to/certificate_authority.pem",
|
||||
"client_certificate_path": "/path/to/client_certificate.pem",
|
||||
"client_key_path": "/path/to/client_key.pem",
|
||||
"insecure_skip_verify": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you want to do config.yaml (or .yml if you prefer).
|
||||
|
||||
```yaml
|
||||
endpoints:
|
||||
- name: "string"
|
||||
address: "https://url"
|
||||
method: "GET"
|
||||
timeout: 123
|
||||
successful_status_code: 2xx
|
||||
inverse_status: false
|
||||
tls_configuration:
|
||||
certificate_authority_path: "/path/to/certificate_authority.pem",
|
||||
client_certificate_path: "/path/to/client_certificate.pem",
|
||||
client_key_path: "/path/to/client_key.pem",
|
||||
insecure_skip_verify: true
|
||||
```
|
||||
|
||||
## Build from source
|
||||
|
||||
Install [Go](https://go.dev/dl).
|
||||
|
|
94
config.go
94
config.go
|
@ -22,6 +22,13 @@
|
|||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Configuration struct {
|
||||
Endpoints []Endpoint `required:"true"`
|
||||
}
|
||||
|
@ -31,6 +38,89 @@ type Endpoint struct {
|
|||
Address string `required:"true"`
|
||||
Method string `required:"false" default:"GET"`
|
||||
Timeout uint16 `required:"false" default:"30"`
|
||||
SuccessfulStatusCode string `required:"false" default:"2xx"`
|
||||
InverseStatus bool `required:"false" default:"false"`
|
||||
SuccessfulStatusCode string `required:"false" default:"2xx" yaml:"successful_status_code" json:"successful_status_code" toml:"successful_status_code"`
|
||||
InverseStatus bool `required:"false" default:"false" yaml:"inverse_status" json:"inverse_status" toml:"inverse_status"`
|
||||
TLSConfiguration TLSConfiguration `required:"false" yaml:"tls_configuration" json:"tls_configuration" toml:"tls_configuration"`
|
||||
}
|
||||
|
||||
type TLSConfiguration struct {
|
||||
CertificateAuthorityPath string `required:"false" yaml:"certificate_authority_path" json:"certificate_authority_path" toml:"certificate_authority_path"`
|
||||
ClientCertificatePath string `required:"false" yaml:"client_certificate_path" json:"client_certificate_path" toml:"client_certificate_path"`
|
||||
ClientKeyPath string `required:"false" yaml:"client_key_path" json:"client_key_path" toml:"client_key_path"`
|
||||
InsecureSkipVerify bool `required:"false" default:"false" yaml:"insecure_skip_verify" json:"insecure_skip_verify" toml:"insecure_skip_verify"`
|
||||
}
|
||||
|
||||
type PreprocessedEndpoint struct {
|
||||
Name string
|
||||
Address string
|
||||
Method string
|
||||
Timeout uint16
|
||||
SuccessfulStatusCode string
|
||||
InverseStatus bool
|
||||
TLSConfiguration PreproccessedTLSConfiguration
|
||||
}
|
||||
|
||||
type PreproccessedTLSConfiguration struct {
|
||||
Certificates []tls.Certificate
|
||||
RootCA *x509.CertPool
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
func processConfiguration(endpoints []Endpoint) ([]PreprocessedEndpoint, error) {
|
||||
var preprocessedEndpoints []PreprocessedEndpoint
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
var certificates []tls.Certificate = nil
|
||||
var rootCA *x509.CertPool = nil
|
||||
|
||||
if endpoint.TLSConfiguration.CertificateAuthorityPath != "" {
|
||||
// Read CA file
|
||||
file, err := os.ReadFile(endpoint.TLSConfiguration.CertificateAuthorityPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading certificate authority file: %s", err.Error())
|
||||
}
|
||||
|
||||
rootCA = x509.NewCertPool()
|
||||
ok := rootCA.AppendCertsFromPEM(file)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid pem format for certificate authority")
|
||||
}
|
||||
}
|
||||
|
||||
if endpoint.TLSConfiguration.ClientCertificatePath != "" && endpoint.TLSConfiguration.ClientKeyPath != "" {
|
||||
// Read certificate file
|
||||
certificateFile, err := os.ReadFile(endpoint.TLSConfiguration.ClientCertificatePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading certificate file: %s", err.Error())
|
||||
}
|
||||
|
||||
keyFile, err := os.ReadFile(endpoint.TLSConfiguration.ClientKeyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading key file: %s", err.Error())
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(certificateFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating x509 key pair: %s", err.Error())
|
||||
}
|
||||
|
||||
certificates = append(certificates, certificate)
|
||||
}
|
||||
|
||||
preprocessedEndpoints = append(preprocessedEndpoints, PreprocessedEndpoint{
|
||||
Name: endpoint.Name,
|
||||
Address: endpoint.Address,
|
||||
Method: endpoint.Method,
|
||||
Timeout: endpoint.Timeout,
|
||||
SuccessfulStatusCode: endpoint.SuccessfulStatusCode,
|
||||
InverseStatus: endpoint.InverseStatus,
|
||||
TLSConfiguration: PreproccessedTLSConfiguration{
|
||||
Certificates: certificates,
|
||||
RootCA: rootCA,
|
||||
InsecureSkipVerify: endpoint.TLSConfiguration.InsecureSkipVerify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return preprocessedEndpoints, nil
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
endpoints:
|
||||
- name: "GitHub"
|
||||
address: https://github.com/healthz
|
||||
tls_configuration:
|
||||
insecure_skip_verify: true
|
||||
|
||||
- name: "Reinaldy"
|
||||
address: https://code.reinaldyrafli.com
|
||||
timeout: 60
|
||||
|
|
12
exporter.go
12
exporter.go
|
@ -24,6 +24,7 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
@ -54,7 +55,7 @@ var (
|
|||
)
|
||||
|
||||
type Exporter struct {
|
||||
Endpoints []Endpoint
|
||||
Endpoints []PreprocessedEndpoint
|
||||
Logger *onelog.Logger
|
||||
}
|
||||
|
||||
|
@ -106,7 +107,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|||
wg.Add(len(e.Endpoints))
|
||||
|
||||
for _, endpoint := range e.Endpoints {
|
||||
go func(endpoint Endpoint) {
|
||||
go func(endpoint PreprocessedEndpoint) {
|
||||
defer wg.Done()
|
||||
|
||||
// Create HTTP calls
|
||||
|
@ -118,6 +119,13 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|||
|
||||
httpClient := http.Client{
|
||||
Timeout: time.Duration(uint64(endpoint.Timeout) * 1e+9),
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
Certificates: endpoint.TLSConfiguration.Certificates,
|
||||
RootCAs: endpoint.TLSConfiguration.RootCA,
|
||||
InsecureSkipVerify: endpoint.TLSConfiguration.InsecureSkipVerify,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
response, err := httpClient.Do(request)
|
||||
|
|
11
main.go
11
main.go
|
@ -49,14 +49,21 @@ func main() {
|
|||
var config Configuration
|
||||
err := configor.Load(&config, *configFile)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
|
||||
preprocessedConfig, err := processConfiguration(config.Endpoints)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
|
||||
log.Printf("%+v", preprocessedConfig)
|
||||
|
||||
// Initiate the logger instance. It should be zero-allocated, so we'll use onelog
|
||||
logger := onelog.New(os.Stdout, onelog.ALL)
|
||||
|
||||
exporter := &Exporter{
|
||||
Endpoints: config.Endpoints,
|
||||
Endpoints: preprocessedConfig,
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue