feat(auth): ad.max_tls_version zum Deckeln der angebotenen TLS-Version
Alte Schannel-Stacks brechen an einem TLS-1.3-ClientHello kommentarlos ab: sie nehmen die TCP-Verbindung an, lesen den ClientHello und setzen zurueck, ohne ein Zertifikat zu schicken. Herunterhandeln hilft dann nicht — TLS 1.3 darf gar nicht erst angeboten werden. max_tls_version (Default 1.3) deckelt die Hoechstversion; die Validierung lehnt eine Hoechstversion unterhalb der Mindestversion ab. check erkennt zusaetzlich zurueckgesetzte Verbindungen und nennt die beiden plausiblen Ursachen: fehlendes LDAPS-Zertifikat auf dem Server oder TLS-1.3-Inkompatibilitaet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NBHF4R9EAejDJUMdwr6C68
This commit is contained in:
parent
fd88251e07
commit
11a3ec2e5f
10 changed files with 136 additions and 10 deletions
|
|
@ -67,7 +67,11 @@ type ADConfig struct {
|
|||
// MinTLSVersion ist die niedrigste akzeptierte TLS-Version zum
|
||||
// Verzeichnisdienst: "1.0", "1.1", "1.2" (Default) oder "1.3".
|
||||
// Unter 1.2 nur für Altsysteme, die nichts Besseres können.
|
||||
MinTLSVersion string `yaml:"min_tls_version"`
|
||||
MinTLSVersion string `yaml:"min_tls_version"`
|
||||
// MaxTLSVersion deckelt die angebotene TLS-Version, Default "1.3".
|
||||
// Auf "1.2" setzen, wenn ein alter Schannel-Stack den Verbindungsaufbau
|
||||
// bereits am TLS-1.3-ClientHello abbricht.
|
||||
MaxTLSVersion string `yaml:"max_tls_version"`
|
||||
BindUser string `yaml:"bind_user"`
|
||||
BindPassword string `yaml:"bind_password"`
|
||||
BindPasswordFile string `yaml:"bind_password_file"`
|
||||
|
|
@ -101,6 +105,7 @@ func Defaults() *Config {
|
|||
Port: 636,
|
||||
TLSMode: "ldaps",
|
||||
MinTLSVersion: "1.2",
|
||||
MaxTLSVersion: "1.3",
|
||||
Timeout: Duration(8 * time.Second),
|
||||
},
|
||||
Matching: MatchingConfig{CNPattern: "{username}"},
|
||||
|
|
|
|||
|
|
@ -110,10 +110,22 @@ func (c *Config) Validate() error {
|
|||
if !slices.Contains([]string{"ldaps", "starttls"}, c.AD.TLSMode) {
|
||||
errs = append(errs, fmt.Errorf("ad.tls_mode %q ist ungültig (erlaubt: ldaps, starttls)", c.AD.TLSMode))
|
||||
}
|
||||
if !slices.Contains(MinTLSVersions, c.AD.MinTLSVersion) {
|
||||
minOK := slices.Contains(MinTLSVersions, c.AD.MinTLSVersion)
|
||||
maxOK := slices.Contains(MinTLSVersions, c.AD.MaxTLSVersion)
|
||||
if !minOK {
|
||||
errs = append(errs, fmt.Errorf("ad.min_tls_version %q ist ungültig (erlaubt: %s)",
|
||||
c.AD.MinTLSVersion, strings.Join(MinTLSVersions, ", ")))
|
||||
}
|
||||
if !maxOK {
|
||||
errs = append(errs, fmt.Errorf("ad.max_tls_version %q ist ungültig (erlaubt: %s)",
|
||||
c.AD.MaxTLSVersion, strings.Join(MinTLSVersions, ", ")))
|
||||
}
|
||||
if minOK && maxOK &&
|
||||
slices.Index(MinTLSVersions, c.AD.MaxTLSVersion) < slices.Index(MinTLSVersions, c.AD.MinTLSVersion) {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ad.max_tls_version %q liegt unter ad.min_tls_version %q — so kommt keine Verbindung zustande",
|
||||
c.AD.MaxTLSVersion, c.AD.MinTLSVersion))
|
||||
}
|
||||
if c.AD.Port <= 0 || c.AD.Port > 65535 {
|
||||
errs = append(errs, fmt.Errorf("ad.port %d liegt außerhalb 1–65535", c.AD.Port))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,49 @@ func TestValidateRejectsUnknownMinTLSVersion(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMaxTLSVersionDefaultsTo13(t *testing.T) {
|
||||
cfg := validCfg(t)
|
||||
if cfg.AD.MaxTLSVersion != "1.3" {
|
||||
t.Errorf("MaxTLSVersion = %q, want Default 1.3", cfg.AD.MaxTLSVersion)
|
||||
}
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Fatalf("Default muss gültig sein: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAcceptsCappedMaxTLSVersion(t *testing.T) {
|
||||
// Alte Schannel-Stacks brechen an einem TLS-1.3-ClientHello ab. Dann muss
|
||||
// sich die Hoechstversion auf 1.2 deckeln lassen.
|
||||
cfg := validCfg(t)
|
||||
cfg.AD.MaxTLSVersion = "1.2"
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Fatalf("max_tls_version 1.2 muss zulässig sein: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRejectsMaxBelowMin(t *testing.T) {
|
||||
cfg := validCfg(t)
|
||||
cfg.AD.MinTLSVersion = "1.2"
|
||||
cfg.AD.MaxTLSVersion = "1.1"
|
||||
err := cfg.Validate()
|
||||
if err == nil {
|
||||
t.Fatal("max_tls_version unter min_tls_version muss abgelehnt werden")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "max_tls_version") {
|
||||
t.Errorf("Fehler muss das Feld nennen: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRejectsUnknownMaxTLSVersion(t *testing.T) {
|
||||
for _, v := range []string{"1.4", "tls1.3", "", "13"} {
|
||||
cfg := validCfg(t)
|
||||
cfg.AD.MaxTLSVersion = v
|
||||
if err := cfg.Validate(); err == nil {
|
||||
t.Errorf("max_tls_version %q muss abgelehnt werden", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinTLSVersionIsWeakBelow12(t *testing.T) {
|
||||
cfg := validCfg(t)
|
||||
for v, wantWeak := range map[string]bool{"1.0": true, "1.1": true, "1.2": false, "1.3": false} {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue