T10.1 D13 - the LDAPS client, verified against the live domain

server/ldap_auth.py: simple bind to ldaps://prime.local:636 as
sAMAccountName@prime.local, nested-group membership via the
LDAP_MATCHING_RULE_IN_CHAIN extensible match, and a selftest() that validates
the DC certificate without binding so it can never contribute to a lockout.

Verified against the live domain, not just reasoned about:

  selftest() to prime.local        -> ok, "certificate validates"
  selftest() to 192.168.3.37       -> refused, untrusted (no IP SAN)
  empty / whitespace password      -> empty_input, with Connection nulled out
                                      so any call to bind() would have raised
  missing CA file                  -> unconfigured, is_config_problem=True
  Tls.validate                     -> ssl.CERT_REQUIRED, explicit ca_certs_file

Three things here are load-bearing and commented as such at the call site:

- The empty-password guard runs BEFORE bind(). An LDAP simple bind with an
  empty password is an anonymous bind and it SUCCEEDS, so without the guard a
  blank password authenticates as whatever username was submitted.
- No `version=` pin on Tls. An earlier draft of this file pinned
  PROTOCOL_TLSv1_2, which would have silently downgraded every connection from
  the TLS 1.3 these DCs actually negotiate.
- Retries cover connect failures only. A rejected credential returns
  immediately, because every failed bind counts against the domain lockout
  policy and this endpoint must not become a way to lock people out of Windows.

The trust anchor is server/certs/prime-ca-chain.pem - PRIME CONTROLS ROOT CA
plus ISSUING CA 1, public certificates with no private key, checked in because
they are public and long-lived (2051 / 2036). The system trust store is
deliberately not used: it currently trusts five other self-signed CAs on this
estate. LDAP_CA_FILE overrides the path for a mounted bundle.

docker-compose.yml: the `outbound` network is no longer optional. Its comment
said to detach it if you were not using the Micron asset picker; doing that
now breaks every sign-in, since `internal` has no default gateway and
therefore no route to prime.local:636.

Not yet verified, and called out rather than assumed: the nested-group case
needs a real group with a nested member, and the in-container
`openssl s_client -CAfile` check needs the stack. Both are T10.1 done-when
boxes still open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 14:06:49 -05:00
parent 3c9343dfc8
commit 0577660c86
7 changed files with 572 additions and 19 deletions

View File

@@ -0,0 +1,66 @@
# CN=PRIME CONTROLS ROOT CA
-----BEGIN CERTIFICATE-----
MIIFHzCCAwegAwIBAgIQOnMIcdMiP6pELiXfh2ZmLzANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQD
ExZQUklNRSBDT05UUk9MUyBST09UIENBMCAXDTIxMDkwOTEzMjM1OFoYDzIwNTEwOTA5MTMzMzU2
WjAhMR8wHQYDVQQDExZQUklNRSBDT05UUk9MUyBST09UIENBMIICIjANBgkqhkiG9w0BAQEFAAOC
Ag8AMIICCgKCAgEAri9Vkf+l3bnjGXWMEX15BYRnQTSKUStctG2NBppJ/lwj2LbHAvHf6HdkJAxx
2lqDiG0R+D9NcGEi427XOQ78Nc9aJe4jOwip5u5Md6Szwmu4QDRjUy11dDBvRoftD550052O1WOV
0OY5hxcZIo7bOfqDLesHG/Y74GJvrYai/4xq440uN6iTaMmsfzbIahIXP39NhuW4i4dgkQkRSfqX
y8i3AGS8WhpViVvIlMgGXRrCcBW8MnVp32OvKE2MqQnjZI2i5f8wMT4L0J0DXlSPbV6FPLdpBXl5
+OX+9qcEH6hI+Mv8sC/xGLAt/wwBP6E2kM2lovGGIUhBsay0UM03PJsh4r6q3HV5gpH6uYZlOgOY
UW59pNT/VyESdZb4kfEGHNHrHy3uNb8Q71UzQxrq/UVKXnBUMN/1PszV4YA08ZaYf2EGItZoNM3v
uTOJIifgtAo1DxaTygmglgk8CHlaN4IU8jotrzdksLYQ9MQaD5xnYADCnIf1eZ5VCF+fhpgQOCCV
4TfJ20rR14+jy6L4yKYGgtvg8H+hvhucOOnktpV/zLv4H9pItIzbQPuxNdrEmco2i7zrsMuaNYD/
tmbGf4mhy20zFOkdOjXGeb4cTF4HIBh3aue2T7o08vi1pkVN+8RXa7lZNbLJnpgtfTr9DKBpW2Fd
LqAb1zN2Azorg3kCAwEAAaNRME8wCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFMoWYtRcmyQQmB3AD4DPiau0r9XRMBAGCSsGAQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBCwUA
A4ICAQB0XhWlrK81ZKW9bBlbQe/i6js7ciDhwObgKB0FGZJH8rqK5HFwM/wZbh9h0t+YPk/ev6oG
BXU0rJUr1YVMERBb37c/rHBU4Bnv9r6cLhsfiTTcVGqZTM7JE9JfdmJQ10V1awSFoxU1cYMSzot9
TyrspkZJ6JFJH2wdhZirIySQHH5WuqEOdG6g9/bDW2X27B0S62s8WNIfP+gugkTDkiHQvoUDi8GY
/DA29lZTKPI8PhmKYZvw+fi1weqIYsFLk9pvHW21nIv6qAT7nfuGvuUR5siB7sFbw80XEX6Em8O0
0lnP6u+vecBqLK7D34X+aupDlkZlZdPoa0EaTwvTO8pkecZCdMLcTkB2quc/fcyCmVdj4CGn3yND
cUUR5wZbHtuCU27Rc4d3rY0gxPNpK3EXkTSOQL7BgR6EkwwNwUqeYmFb/SyXYeSqpdChvsWKgRrP
+8n27SeJ01ezk8GMDC0YcOJAAHxXqqOAJo1IwxVvpRkoljKIAprQwHAGUNTmKy+SJXT73/LpPTX+
RnbNTG27UfYONh5/DdkGc1wwmIp1X1a1tAb3MNRasiBGlYJ3NGDHVUwgEtmcVeVXWsj1ZhaXq2YX
TFsDl9m1IMhFD1n8pLJTLd7AT8Exxga+OzFjMzDu0uzKw3KoAVIX/IgfybdKexVT4Z+nBCY9N+n9
/vpujg==
-----END CERTIFICATE-----
# CN=PRIME CONTROLS ISSUING CA 1, DC=prime, DC=local
-----BEGIN CERTIFICATE-----
MIIH/jCCBeagAwIBAgITdQAAAAI3j8Wt9kAshQAAAAAAAjANBgkqhkiG9w0BAQsFADAhMR8wHQYD
VQQDExZQUklNRSBDT05UUk9MUyBST09UIENBMB4XDTIxMDkwOTE0NDUxM1oXDTM2MDkwOTE0NTUx
M1owVDEVMBMGCgmSJomT8ixkARkWBWxvY2FsMRUwEwYKCZImiZPyLGQBGRYFcHJpbWUxJDAiBgNV
BAMTG1BSSU1FIENPTlRST0xTIElTU1VJTkcgQ0EgMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBANvhsMj7RooZ+FzAExgWLex++F1QsdDqcGkwqjaUI5AziI4yb/FMqnO93Sus60hiqql+
hfxW/a55huBfo54kOyp5oWAjd0akfJQvFY8sXsWIGz07wiy0msvZDtHdBXy561zb0SSNpJS1Vceb
ihyDvM5Tyc6QAzO1u76QJPkegn+v6lpWmodba0kWVvRg/P3SLCnq1LdU8oA1VhIiCkCbdry0Syop
bVX0+evzINGmv81VbHct8ptZTBLctM11C16IwPEmntvOqVKpT166/aUO8FMqyHQQZ32ZHBp6ORNu
WlCN/EDdgT2s7Vy9/7Hr6gy1IPjEGNYq1yYxcpth+6/RgvxFnt9SWr7B9qKtYe1rN8r+6qYVw4ne
c0L7vRvw8HTjOJ9EQnPfID9+34Y8OQkqIHnnF80yjMGCdvh/tR/tXsUlz+Byt9m5MFdhPE22MX1x
jyCJFug1Ufso8toAVZcavsRfX0ygeUkv+zZDZKHmF910rB8Cdb980cpKtFvx/v0BNWnzgAhgGkqO
ttUf0PKUeGDIW6DMUKhs5JM7ED5rkepvNG5vePDVy/YidhbM4x1ph+HFVkHgT6rd1M4NeCm818C0
MVsofXdOOdbjmkQyxPGukl9EG1ukYMfUIQpFozTbbHKNUDfqcdHu8Qm39K7opwxMOiLprXGRSgx2
vMHI82mtAgMBAAGjggL6MIIC9jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU94Bw+7t8JXHx
VZoWN/pFMo005ZwwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUyhZi1FybJBCYHcAPgM+Jq7Sv1dEwggEvBgNVHR8EggEm
MIIBIjCCAR6gggEaoIIBFoaByGxkYXA6Ly8vQ049UFJJTUUlMjBDT05UUk9MUyUyMFJPT1QlMjBD
QSxDTj1QUklNRS1ST09UQ0EsQ049Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNl
cnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9cHJpbWUsREM9bG9jYWw/Y2VydGlmaWNhdGVSZXZv
Y2F0aW9uTGlzdD9iYXNlP29iamVjdENsYXNzPWNSTERpc3RyaWJ1dGlvblBvaW50hklodHRwOi8v
Y3JsLnByaW1lLWNvbnRyb2xzLmNvbS9DZXJ0RW5yb2xsL1BSSU1FJTIwQ09OVFJPTFMlMjBST09U
JTIwQ0EuY3JsMIIBNAYIKwYBBQUHAQEEggEmMIIBIjCBuwYIKwYBBQUHMAKGga5sZGFwOi8vL0NO
PVBSSU1FJTIwQ09OVFJPTFMlMjBST09UJTIwQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNl
cnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9cHJpbWUsREM9bG9jYWw/Y0FD
ZXJ0aWZpY2F0ZT9iYXNlP29iamVjdENsYXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwYgYIKwYB
BQUHMAKGVmh0dHA6Ly9jcmwucHJpbWUtY29udHJvbHMuY29tL0NlcnRFbnJvbGwvUFJJTUUtUk9P
VENBX1BSSU1FJTIwQ09OVFJPTFMlMjBST09UJTIwQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQAw
2SIuBMB8JWC/YGbh3LJDt9T/z1BwEniLwEKu4SyBMfW3qJoLR0Zps8xHlCIlUjisZBSilHDNW4uW
4891yqg104OR0dx94dQV2Y6Aw9V4tvlw+GGnWpHbUNwP3/JizlndR6ZdhdnJlIt6g7xCy4LwyXrw
f22CibC1EWUVnsYJ8ez+LxSprWiUaEME8+bEaVuajXRitYyoG0asvtESeBhR3lwaxuzBqyAuGEfc
P8pO/fgjigripPScXnp6opQRzZ12VSNsN8BUmtCfsqX8DK7iRsn/QLZtOKVf6kapPkorovetKwai
wasE2mpKJweKYhGwWXdHrXZfCp6biTzG0oX4DGke7tHxdA92ArgmPR4SsNnpekUGcbUgkdpOc0sC
mt+LXzRvFKJNd205u/FvpZlBzRZl+NQ++9OGAywyzOb4Lxli49G0yFQ5Fpq0UElqDKiLzUqUR2Ye
qDT9nMJNTZEyP6hEjQQ+tSD9XFIlFWD83AyBHhRNKKZfPiu4mJxNYtW1ltVu5Z1EvBfbcRwc0lQa
VKGQS23sSIx6heq2q5FqhnYZ15zSXqaAH680Up5mOkeGIfm45rMIFl/aMAJ2Fntl+Taebosf2rv5
M/QOJs494ePbXxCJ1kzCLaqgucDXtadJ7ZV2AKhu3B9OiDrS8sv990Yn95ItlpthpVetiXxLHg==
-----END CERTIFICATE-----