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 }, } }