Add support for self signed ntfy certificates
A fingerprint can be specified in the configuration file. If it matches the one from the server certificate, it will be accepted. Closes: https://todo.xenrox.net/~xenrox/ntfy-alertmanager/22
This commit is contained in:
parent
1bfb814f14
commit
25e65db8bd
4 changed files with 51 additions and 7 deletions
|
@ -51,6 +51,9 @@ ntfy {
|
||||||
# ntfy authentication via access tokens (https://docs.ntfy.sh/publish/#access-tokens)
|
# ntfy authentication via access tokens (https://docs.ntfy.sh/publish/#access-tokens)
|
||||||
# Either access-token or a user/password combination can be used - not both.
|
# Either access-token or a user/password combination can be used - not both.
|
||||||
access-token foobar
|
access-token foobar
|
||||||
|
# When using (self signed) certificates that cannot be verified, you can instead specify
|
||||||
|
# the SHA512 fingerprint.
|
||||||
|
certificate-fingerprint 136d2b889c5736d081b4b29c7909276292cfb86a6bd3ad4635cb7017eb996e28082ab8c6794bf62e817941981d53c807b35c245fb18eb6fb66b5ddb4d05c299
|
||||||
# Forward all messages to the specified email address.
|
# Forward all messages to the specified email address.
|
||||||
email-address foo@bar.com
|
email-address foo@bar.com
|
||||||
# Call the specified number for all alerts. Use `yes` to pick the first of your verified numbers.
|
# Call the specified number for all alerts. Use `yes` to pick the first of your verified numbers.
|
||||||
|
|
|
@ -40,6 +40,7 @@ type ntfyConfig struct {
|
||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
AccessToken string
|
AccessToken string
|
||||||
|
CertFingerprint string
|
||||||
EmailAddress string
|
EmailAddress string
|
||||||
Call string
|
Call string
|
||||||
}
|
}
|
||||||
|
@ -277,6 +278,13 @@ func ReadConfig(path string) (*Config, error) {
|
||||||
return nil, errors.New("ntfy: cannot use both an access-token and a user/password at the same time")
|
return nil, errors.New("ntfy: cannot use both an access-token and a user/password at the same time")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d = ntfyDir.Children.Get("certificate-fingerprint")
|
||||||
|
if d != nil {
|
||||||
|
if err := d.ParseParams(&config.Ntfy.CertFingerprint); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
d = ntfyDir.Children.Get("email-address")
|
d = ntfyDir.Children.Get("email-address")
|
||||||
if d != nil {
|
if d != nil {
|
||||||
if err := d.ParseParams(&config.Ntfy.EmailAddress); err != nil {
|
if err := d.ParseParams(&config.Ntfy.EmailAddress); err != nil {
|
||||||
|
|
|
@ -45,6 +45,7 @@ resolved {
|
||||||
|
|
||||||
ntfy {
|
ntfy {
|
||||||
topic https://ntfy.sh/alertmanager-alerts
|
topic https://ntfy.sh/alertmanager-alerts
|
||||||
|
certificate-fingerprint 136d2b889c5736d081b4b29c7909276292cfb86a6bd3ad4635cb7017eb996e28082ab8c6794bf62e817941981d53c807b35c245fb18eb6fb66b5ddb4d05c299
|
||||||
user user
|
user user
|
||||||
password pass
|
password pass
|
||||||
}
|
}
|
||||||
|
@ -71,7 +72,12 @@ cache {
|
||||||
AlertMode: Multi,
|
AlertMode: Multi,
|
||||||
User: "webhookUser",
|
User: "webhookUser",
|
||||||
Password: "webhookPass",
|
Password: "webhookPass",
|
||||||
Ntfy: ntfyConfig{Topic: "https://ntfy.sh/alertmanager-alerts", User: "user", Password: "pass"},
|
Ntfy: ntfyConfig{
|
||||||
|
Topic: "https://ntfy.sh/alertmanager-alerts",
|
||||||
|
User: "user",
|
||||||
|
Password: "pass",
|
||||||
|
CertFingerprint: "136d2b889c5736d081b4b29c7909276292cfb86a6bd3ad4635cb7017eb996e28082ab8c6794bf62e817941981d53c807b35c245fb18eb6fb66b5ddb4d05c299",
|
||||||
|
},
|
||||||
Labels: labels{Order: []string{"severity", "instance"},
|
Labels: labels{Order: []string{"severity", "instance"},
|
||||||
Label: map[string]labelConfig{
|
Label: map[string]labelConfig{
|
||||||
"severity:critical": {
|
"severity:critical": {
|
||||||
|
|
27
main.go
27
main.go
|
@ -5,9 +5,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
@ -329,6 +333,29 @@ func (br *bridge) publish(n *notification) error {
|
||||||
req.Header.Set("Actions", fmt.Sprintf("http, Silence, %s, method=POST, body=%s%s", url, n.silenceBody, authString))
|
req.Header.Set("Actions", fmt.Sprintf("http, Silence, %s, method=POST, body=%s%s", url, n.silenceBody, authString))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configFingerprint := br.cfg.Ntfy.CertFingerprint
|
||||||
|
if configFingerprint != "" {
|
||||||
|
tlsCfg := &tls.Config{}
|
||||||
|
tlsCfg.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
for _, rawCert := range rawCerts {
|
||||||
|
hash := sha512.Sum512(rawCert)
|
||||||
|
if hex.EncodeToString(hash[:]) == configFingerprint {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rawCerts) == 0 {
|
||||||
|
return errors.New("the ntfy server does not offer a certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha512.Sum512(rawCerts[0])
|
||||||
|
return fmt.Errorf("ntfy certificate fingerprint does not match: expected %q, got %q", hex.EncodeToString(hash[:]), configFingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsCfg.InsecureSkipVerify = true
|
||||||
|
br.client.Transport = &http.Transport{TLSClientConfig: tlsCfg}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := br.client.Do(req)
|
resp, err := br.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue