mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-24 00:44:33 +00:00
75 lines
2.1 KiB
Go
75 lines
2.1 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/henrygd/beszel/internal/entities/monitor"
|
|
)
|
|
|
|
const (
|
|
certCheckInterval = 24 * time.Hour
|
|
certCheckRetryInterval = time.Hour
|
|
)
|
|
|
|
// certChecker fetches the leaf certificate for an HTTPS target.
|
|
type certChecker func(context.Context, string) (monitor.CertInfo, error)
|
|
|
|
// certCheckEnabled reports whether a monitor's certificate is checked, which is
|
|
// the case for every HTTP monitor with an https target.
|
|
func certCheckEnabled(config monitor.Config) bool {
|
|
return config.Protocol == "http" && len(config.Target) > 8 && strings.EqualFold(config.Target[:8], "https://")
|
|
}
|
|
|
|
// checkCert reads the leaf certificate presented by an HTTPS target. The chain is
|
|
// not verified, so expired or self-signed certificates are still reported.
|
|
func checkCert(ctx context.Context, target string) (monitor.CertInfo, error) {
|
|
address, host, err := certAddress(target)
|
|
if err != nil {
|
|
return monitor.CertInfo{}, err
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, monitor.MaxProbeTimeout)
|
|
defer cancel()
|
|
dialer := tls.Dialer{Config: &tls.Config{ServerName: host, InsecureSkipVerify: true}}
|
|
conn, err := dialer.DialContext(ctx, "tcp", address)
|
|
if err != nil {
|
|
return monitor.CertInfo{}, err
|
|
}
|
|
defer conn.Close()
|
|
certs := conn.(*tls.Conn).ConnectionState().PeerCertificates
|
|
if len(certs) == 0 {
|
|
return monitor.CertInfo{}, errors.New("no peer certificates")
|
|
}
|
|
leaf := certs[0]
|
|
return monitor.CertInfo{
|
|
Expires: leaf.NotAfter.UnixMilli(),
|
|
Issuer: leaf.Issuer.CommonName,
|
|
}, nil
|
|
}
|
|
|
|
// certAddress returns the dial address and server name for an HTTPS URL.
|
|
func certAddress(target string) (address, host string, err error) {
|
|
u, err := url.Parse(target)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if !strings.EqualFold(u.Scheme, "https") {
|
|
return "", "", fmt.Errorf("certificate check requires an https target: %s", target)
|
|
}
|
|
host = u.Hostname()
|
|
if host == "" {
|
|
return "", "", fmt.Errorf("missing host in target: %s", target)
|
|
}
|
|
port := u.Port()
|
|
if port == "" {
|
|
port = "443"
|
|
}
|
|
return net.JoinHostPort(host, port), host, nil
|
|
}
|