software steht so weit.
jetzt build + docker build und testen
This commit is contained in:
1 parent
42f0c0accb
commit
ca6cdcca63
90 files changed
+10298
-480
No files matched your search
+33
-134
@@ -7,78 +7,10 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newKnownServers(t *testing.T) *KnownServers {
|
||||
t.Helper()
|
||||
return &KnownServers{Path: filepath.Join(t.TempDir(), "known_servers")}
|
||||
}
|
||||
|
||||
func TestKnownServers(t *testing.T) {
|
||||
ks := newKnownServers(t)
|
||||
sc, _ := LoadOrCreateServerCert(t.TempDir())
|
||||
other, _ := LoadOrCreateServerCert(t.TempDir())
|
||||
|
||||
if _, ok, err := ks.Lookup("ingress:8443"); ok || err != nil {
|
||||
t.Fatalf("leere Datei: ok=%v err=%v", ok, err)
|
||||
}
|
||||
if err := ks.Add("ingress:8443", sc.Fingerprint); err != nil {
|
||||
t.Fatalf("Add: %v", err)
|
||||
}
|
||||
if fp, ok, err := ks.Lookup("ingress:8443"); !ok || err != nil || fp != sc.Fingerprint {
|
||||
t.Errorf("Lookup = %q, %v, %v", fp, ok, err)
|
||||
}
|
||||
if err := ks.Add("ingress:8443", sc.Fingerprint); err != nil {
|
||||
t.Errorf("gleichen Eintrag erneut hinzufügen: %v", err)
|
||||
}
|
||||
if err := ks.Add("ingress:8443", other.Fingerprint); !errors.Is(err, ErrFingerprintMismatch) {
|
||||
t.Errorf("Pin überschreiben: erwartet ErrFingerprintMismatch, bekommen %v", err)
|
||||
}
|
||||
if err := ks.Add("anderer:8443", other.Fingerprint); err != nil {
|
||||
t.Errorf("zweiter Server: %v", err)
|
||||
}
|
||||
|
||||
data, _ := os.ReadFile(ks.Path)
|
||||
if n := strings.Count(string(data), "ingress:8443"); n != 1 {
|
||||
t.Errorf("ingress:8443 steht %d-mal in der Datei", n)
|
||||
}
|
||||
info, _ := os.Stat(ks.Path)
|
||||
if info.Mode().Perm() != 0o600 {
|
||||
t.Errorf("Dateirechte %o, erwartet 600", info.Mode().Perm())
|
||||
}
|
||||
}
|
||||
|
||||
func TestKnownServers_FileFormat(t *testing.T) {
|
||||
ks := newKnownServers(t)
|
||||
sc, _ := LoadOrCreateServerCert(t.TempDir())
|
||||
content := "# Kommentar\n\n ingress:8443 " + string(sc.Fingerprint) + " \n"
|
||||
os.WriteFile(ks.Path, []byte(content), 0o600)
|
||||
if fp, ok, err := ks.Lookup("ingress:8443"); !ok || err != nil || fp != sc.Fingerprint {
|
||||
t.Errorf("Lookup = %q, %v, %v", fp, ok, err)
|
||||
}
|
||||
|
||||
for name, bad := range map[string]string{
|
||||
"ein Feld": "ingress:8443\n",
|
||||
"drei Felder": "ingress:8443 " + string(sc.Fingerprint) + " x\n",
|
||||
"kaputter Fingerprint": "ingress:8443 abc\n",
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
os.WriteFile(ks.Path, []byte(bad), 0o600)
|
||||
if _, _, err := ks.Lookup("ingress:8443"); !errors.Is(err, ErrCorruptKnownServers) {
|
||||
t.Errorf("erwartet ErrCorruptKnownServers, bekommen %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
if err := ks.Add("mit leerzeichen", sc.Fingerprint); err == nil {
|
||||
t.Error("Servername mit Leerzeichen akzeptiert")
|
||||
}
|
||||
}
|
||||
|
||||
// tlsServer startet einen Test-Server mit dem Zertifikat aus einem eigenen
|
||||
// Datenverzeichnis und zählt, ob ein Request ihn erreicht.
|
||||
func tlsServer(t *testing.T) (*httptest.Server, ServerCert, *atomic.Int32) {
|
||||
@@ -98,96 +30,63 @@ func tlsServer(t *testing.T) (*httptest.Server, ServerCert, *atomic.Int32) {
|
||||
return srv, sc, &hits
|
||||
}
|
||||
|
||||
func get(t *testing.T, srv *httptest.Server, cfg *tls.Config) error {
|
||||
func get(t *testing.T, url string, cfg *tls.Config) error {
|
||||
t.Helper()
|
||||
c := &http.Client{Transport: &http.Transport{TLSClientConfig: cfg}}
|
||||
resp, err := c.Get(srv.URL)
|
||||
resp, err := c.Get(url)
|
||||
if err == nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func TestClientTLS_FirstContactWithExpectedFingerprint(t *testing.T) {
|
||||
func TestPinnedTLS_Matches(t *testing.T) {
|
||||
srv, sc, hits := tlsServer(t)
|
||||
ks := newKnownServers(t)
|
||||
addr := srv.Listener.Addr().String()
|
||||
|
||||
if err := get(t, srv, ClientTLSConfig(addr, ks, sc.Fingerprint)); err != nil {
|
||||
t.Fatalf("erster Kontakt mit korrektem Fingerprint: %v", err)
|
||||
if err := get(t, srv.URL, PinnedTLSConfig(sc.Fingerprint)); err != nil {
|
||||
t.Fatalf("passender Fingerprint: %v", err)
|
||||
}
|
||||
if hits.Load() != 1 {
|
||||
t.Error("Request kam nicht an")
|
||||
}
|
||||
if fp, ok, _ := ks.Lookup(addr); !ok || fp != sc.Fingerprint {
|
||||
t.Errorf("Pin nicht gespeichert: %q %v", fp, ok)
|
||||
}
|
||||
// Folgekontakt ohne Token-Fingerprint: der Pin genügt.
|
||||
if err := get(t, srv, ClientTLSConfig(addr, ks, "")); err != nil {
|
||||
t.Errorf("Folgekontakt mit Pin: %v", err)
|
||||
}
|
||||
|
||||
// Der Pin hängt am Server, nicht an der Adresse: derselbe Server unter einer
|
||||
// anderen Adresse (hier localhost statt 127.0.0.1) ist weiterhin vertrauenswürdig.
|
||||
func TestPinnedTLS_AddressIndependent(t *testing.T) {
|
||||
srv, sc, _ := tlsServer(t)
|
||||
port := srv.Listener.Addr().(interface{ String() string }).String()
|
||||
port = port[len("127.0.0.1"):]
|
||||
if err := get(t, "https://localhost"+port, PinnedTLSConfig(sc.Fingerprint)); err != nil {
|
||||
t.Errorf("andere Adresse, gleicher Server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Kein blindes TOFU: Ohne Pin und ohne erwarteten Fingerprint wird nicht verbunden.
|
||||
func TestClientTLS_NoBlindFirstContact(t *testing.T) {
|
||||
srv, _, hits := tlsServer(t)
|
||||
ks := newKnownServers(t)
|
||||
addr := srv.Listener.Addr().String()
|
||||
|
||||
if err := get(t, srv, ClientTLSConfig(addr, ks, "")); !errors.Is(err, ErrUnknownServer) {
|
||||
t.Errorf("erwartet ErrUnknownServer, bekommen %v", err)
|
||||
}
|
||||
if hits.Load() != 0 {
|
||||
t.Error("Request hat den Server erreicht")
|
||||
}
|
||||
if _, ok, _ := ks.Lookup(addr); ok {
|
||||
t.Error("unbekannter Server wurde gepinnt")
|
||||
}
|
||||
}
|
||||
|
||||
// Angreifer beim Erstkontakt: Er präsentiert sein eigenes Zertifikat, der
|
||||
// Token enthält aber den Fingerprint des echten Servers.
|
||||
func TestClientTLS_MITMOnFirstContact(t *testing.T) {
|
||||
// Angreifer mit eigenem Zertifikat: Abbruch im Handshake, kein Request
|
||||
// (also auch kein Token, keine Signatur) erreicht ihn.
|
||||
func TestPinnedTLS_Mismatch(t *testing.T) {
|
||||
mitm, _, hits := tlsServer(t)
|
||||
real, _ := LoadOrCreateServerCert(t.TempDir())
|
||||
ks := newKnownServers(t)
|
||||
addr := mitm.Listener.Addr().String()
|
||||
|
||||
if err := get(t, mitm, ClientTLSConfig(addr, ks, real.Fingerprint)); !errors.Is(err, ErrFingerprintMismatch) {
|
||||
if err := get(t, mitm.URL, PinnedTLSConfig(real.Fingerprint)); !errors.Is(err, ErrFingerprintMismatch) {
|
||||
t.Errorf("erwartet ErrFingerprintMismatch, bekommen %v", err)
|
||||
}
|
||||
if hits.Load() != 0 {
|
||||
t.Error("Request (inkl. Token) hat den Angreifer erreicht")
|
||||
}
|
||||
if _, ok, _ := ks.Lookup(addr); ok {
|
||||
t.Error("Angreifer wurde gepinnt")
|
||||
}
|
||||
}
|
||||
|
||||
// Angreifer nach dem Erstkontakt: Der Pin gewinnt, auch wenn ein (neuer)
|
||||
// Token-Fingerprint auf den Angreifer passen würde.
|
||||
func TestClientTLS_PinWinsLater(t *testing.T) {
|
||||
mitm, mitmCert, hits := tlsServer(t)
|
||||
real, _ := LoadOrCreateServerCert(t.TempDir())
|
||||
ks := newKnownServers(t)
|
||||
addr := mitm.Listener.Addr().String()
|
||||
if err := ks.Add(addr, real.Fingerprint); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, expected := range []Fingerprint{"", mitmCert.Fingerprint} {
|
||||
if err := get(t, mitm, ClientTLSConfig(addr, ks, expected)); !errors.Is(err, ErrFingerprintMismatch) {
|
||||
t.Errorf("expected=%q: erwartet ErrFingerprintMismatch, bekommen %v", expected, err)
|
||||
}
|
||||
}
|
||||
if hits.Load() != 0 {
|
||||
t.Error("Request hat den Angreifer erreicht")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientTLSConfig_OnlyTLS13(t *testing.T) {
|
||||
cfg := ClientTLSConfig("x:1", newKnownServers(t), "")
|
||||
if cfg.MinVersion != tls.VersionTLS13 {
|
||||
t.Errorf("MinVersion = %x, erwartet TLS 1.3", cfg.MinVersion)
|
||||
// Kein blindes Vertrauen: ohne erwarteten Fingerprint keine Verbindung.
|
||||
func TestPinnedTLS_NoFingerprint(t *testing.T) {
|
||||
srv, _, hits := tlsServer(t)
|
||||
if err := get(t, srv.URL, PinnedTLSConfig("")); !errors.Is(err, ErrUnknownServer) {
|
||||
t.Errorf("erwartet ErrUnknownServer, bekommen %v", err)
|
||||
}
|
||||
if hits.Load() != 0 {
|
||||
t.Error("Request erreichte den Server")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPinnedTLSConfig_OnlyTLS13(t *testing.T) {
|
||||
if cfg := PinnedTLSConfig("x"); cfg.MinVersion != tls.VersionTLS13 {
|
||||
t.Errorf("MinVersion = %x", cfg.MinVersion)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user