46 lines
1.6 KiB
Go
46 lines
1.6 KiB
Go
package crypto
|
||
|
||
import (
|
||
"crypto/tls"
|
||
"errors"
|
||
"fmt"
|
||
)
|
||
|
||
var (
|
||
// ErrUnknownServer: Kein Fingerprint bekannt – ein ungeprüfter Kontakt
|
||
// wird verweigert (kein blindes TOFU).
|
||
ErrUnknownServer = errors.New("crypto: unbekannter Server – Fingerprint aus der Einladung erforderlich")
|
||
// ErrFingerprintMismatch: Das Server-Zertifikat passt nicht zum Pin
|
||
// (möglicher Man-in-the-Middle).
|
||
ErrFingerprintMismatch = errors.New("crypto: Server-Fingerprint stimmt nicht – möglicher Man-in-the-Middle")
|
||
)
|
||
|
||
// PinnedTLSConfig liefert die TLS-Konfiguration des Agenten. Statt einer CA
|
||
// wird der Fingerprint des Server-Zertifikats geprüft – unabhängig von der
|
||
// Adresse, über die der Server erreicht wird (DNS, interne IP, NetBird):
|
||
// Derselbe Server hat überall denselben Fingerprint.
|
||
//
|
||
// Beim Erstkontakt kommt want aus der Einladung, danach aus node.json. Die
|
||
// Prüfung läuft im TLS-Handshake, also bevor irgendein Request-Byte (Token,
|
||
// Signatur, Body) gesendet wird. Nur TLS 1.3.
|
||
func PinnedTLSConfig(want Fingerprint) *tls.Config {
|
||
return &tls.Config{
|
||
MinVersion: tls.VersionTLS13,
|
||
// Die CA- und Hostnamen-Prüfung entfällt bewusst; VerifyConnection
|
||
// ersetzt sie durch die strengere Fingerprint-Prüfung.
|
||
InsecureSkipVerify: true,
|
||
VerifyConnection: func(cs tls.ConnectionState) error {
|
||
if want == "" {
|
||
return ErrUnknownServer
|
||
}
|
||
if len(cs.PeerCertificates) == 0 {
|
||
return fmt.Errorf("%w: kein Zertifikat", ErrFingerprintMismatch)
|
||
}
|
||
if got := FingerprintOf(cs.PeerCertificates[0]); got != want {
|
||
return fmt.Errorf("%w: Server präsentiert %s, erwartet %s", ErrFingerprintMismatch, got, want)
|
||
}
|
||
return nil
|
||
},
|
||
}
|
||
}
|