[WIP] certificates: Allow LDAP enrollment#1401
Conversation
de6f2bd to
e7ef5e8
Compare
e7ef5e8 to
b216827
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new native (Go) certificate auto-enrollment path that discovers AD CS enrollment data via LDAP and submits CSRs via MS-ICPR (RPC), while keeping the existing CEPCES/Python path for backward compatibility and wiring enrollment-method selection through daemon/service configuration and packaging/docs.
Changes:
- Add LDAP discovery, Kerberos GSSAPI bind support, MS-ICPR CSR submission, and on-disk enrollment state/trust-store management in the certificate policy manager.
- Add configuration plumbing (
certificate_enrollment) across daemon/service/policy manager, plus updated tests and integration fixtures. - Update docs and Debian packaging to reflect the new enrollment method options and remove legacy script-dump flows.
Reviewed changes
Copilot reviewed 64 out of 99 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/policies/manager.go | Plumbs certificate manager injection and enrollment method into policy manager construction. |
| internal/policies/manager_test.go | Updates policy-manager tests to use the new certificate manager wiring and LDAP enrollment mocks. |
| internal/policies/certificate/trust.go | Adds root CA installation/symlink creation and trust-store update helpers. |
| internal/policies/certificate/state.go | Adds JSON state persistence for enrollment (replacing Samba TDB cache). |
| internal/policies/certificate/ldap.go | Implements LDAP discovery and Kerberos (GSSAPI) bind support and ccache lookup. |
| internal/policies/certificate/ldap_test.go | Adds unit tests for LDAP discovery/template lookup helpers. |
| internal/policies/certificate/gssapi.go | Adds a Go LDAP GSSAPI client implementation for Kerberos binds. |
| internal/policies/certificate/gssapi_test.go | Adds unit tests for GSSAPI token unwrap/wrap behavior and RRC handling. |
| internal/policies/certificate/enroll.go | Implements MS-ICPR CSR submission and key/CSR generation helpers. |
| internal/policies/certificate/enroll_test.go | Adds tests for RPC credential creation from Kerberos ccaches. |
| internal/policies/certificate/certificate.go | Introduces dual enrollment methods (ldap/cepces) and LDAP enrollment flow (state + cert/key writing). |
| internal/policies/certificate/certificate_test.go | Reworks certificate policy tests to exercise LDAP enrollment path and state cleanup. |
| internal/policies/certificate/testdata/TestApplyPolicy/golden/computer,_no_entries,_samba_cache_present | Removes golden output tied to legacy Python autoenroll script behavior. |
| internal/policies/certificate/testdata/TestApplyPolicy/golden/computer,_configured_to_unenroll | Removes golden output tied to legacy Python autoenroll script behavior. |
| internal/policies/certificate/testdata/TestApplyPolicy/golden/computer,_configured_to_enroll,_advanced_configuration | Removes golden output tied to legacy Python autoenroll script behavior. |
| internal/policies/certificate/testdata/TestApplyPolicy/golden/computer,_configured_to_enroll | Removes golden output tied to legacy Python autoenroll script behavior. |
| internal/consts/consts.go | Adds constants for enrollment method selection and defaults. |
| internal/adsysservice/policy.go | Changes CertAutoEnrollScript RPC response to informational text (no longer returns embedded script). |
| internal/adsysservice/adsysservice.go | Adds service option to pass certificate_enrollment into the policy manager. |
| internal/ad/ad.go | Adds test-only env var to disable Kerberos in integration environments without a KDC. |
| cmd/adsysd/daemon/daemon.go | Adds CLI/config flag plumbing for certificate_enrollment. |
| cmd/adsysd/client/policy.go | Removes CLI command that dumped the legacy cert-autoenroll script. |
| cmd/adsys-certsubmit/main.go | Adds a new helper binary intended for certmonger-style operations (SUBMIT/IDENTIFY/GET-SUPPORTED-TEMPLATES). |
| cmd/adsysd/integration_tests/adsysctl_policy_test.go | Splits certmonger/cepces missing-binary scenarios and removes script-dump tests; improves NSS wrapper handling. |
| cmd/adsysd/integration_tests/adsys_test.go | Ensures integration tests run with Kerberos disabled and augments passwd/group for container mocks. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/systemd/system/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/users/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/.ready | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/startup | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/empty-subfolder/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/final-machine-script.sh | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/other-script-user-logon | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/otherfolder/script-user-logoff | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/script-machine-shutdown | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/script-machine-startup | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/script-user-logon | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/subfolder/other-script | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/unreferenced-data | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/run/machine/scripts/scripts/unreferenced-script | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/sudoers.d/99-adsys-privilege-enforcement | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/polkit-1/localauthority.conf.d/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/polkit-1/rules.d/00-adsys-privilege-enforcement.rules | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/apparmor.d/adsys/machine/usr.bin.bar | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/apparmor.d/adsys/machine/usr.bin.foo | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/apparmor.d/adsys/machine/nested/usr.bin.baz | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/apparmor.d/adsys/users/adsystestuser@example.com | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/dconf/profile/gdm | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/dconf/db/gdm.d/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/dconf/db/gdm.d/locks/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/dconf/db/machine.d/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/dconf/db/machine.d/locks/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/lib/private | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_certmonger_is_not_available/lib/samba/cert_gpo_state_HOST.tdb | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/systemd/system/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/users/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/.ready | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/startup | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/empty-subfolder/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/final-machine-script.sh | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/other-script-user-logon | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/otherfolder/script-user-logoff | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/script-machine-shutdown | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/script-machine-startup | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/script-user-logon | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/subfolder/other-script | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/unreferenced-data | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/run/machine/scripts/scripts/unreferenced-script | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/sudoers.d/99-adsys-privilege-enforcement | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/polkit-1/localauthority.conf.d/.empty | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/polkit-1/rules.d/00-adsys-privilege-enforcement.rules | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/apparmor.d/adsys/machine/usr.bin.bar | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/apparmor.d/adsys/machine/usr.bin.foo | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/apparmor.d/adsys/machine/nested/usr.bin.baz | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/apparmor.d/adsys/users/adsystestuser@example.com | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/dconf/profile/gdm | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/dconf/db/gdm.d/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/dconf/db/gdm.d/locks/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/dconf/db/machine.d/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/dconf/db/machine.d/locks/adsys | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/lib/private | Updates golden fixtures for integration test scenario. |
| cmd/adsysd/integration_tests/testdata/TestPolicyUpdate/golden/does_not_error_when_cepces_is_not_available/lib/samba/cert_gpo_state_HOST.tdb | Updates golden fixtures for integration test scenario. |
| e2e/cmd/run_tests/01_provision_client/main.go | Drops python3-cepces install in e2e provisioning. |
| docs/reference/external-links.md | Removes CEP/CES external links section and normalizes formatting. |
| docs/index.md | Adjusts formatting; still references CEP/CES link anchor (now removed elsewhere). |
| docs/how-to/certificates/setup.md | Updates package requirements and introduces method selection documentation. |
| docs/how-to/certificates/configure.md | Documents certificate_enrollment config and updates helper-location example. |
| docs/how-to/certificates/index.md | Removes CEP/CES mention from external resources list. |
| docs/how-to/certificates/troubleshoot.md | Updates troubleshooting guidance toward native LDAP/RPC flow and state files. |
| docs/explanation/certificates.md | Adds method overview/configuration details for LDAP vs CEPCES enrollment. |
| debian/rules | Removes vendored Python install step and adds (currently empty) test override target. |
| debian/control | Moves Python/Samba deps to Recommends and adjusts runtime deps. |
| debian/adsys.postinst | Creates default /etc/adsys.yaml for new installs setting LDAP enrollment. |
| debian/adsys.install | Stops installing legacy Python assets directory. |
| debian/adsys.apport | Removes python3-samba from apport related packages list. |
| debian/copyright | Removes legacy vendored Samba Python copyright stanza. |
| debian/changelog | Adds a test changelog entry for certificate changes. |
| conf.example/adsys.yaml | Documents the new certificate_enrollment configuration key. |
| go.mod | Adds new Go dependencies for LDAP/Kerberos/MSRPC. |
| go.sum | Records new module checksums for added dependencies. |
Comments suppressed due to low confidence (1)
debian/rules:88
- An empty override_dh_auto_test target disables the default test execution in the Debian build. If the intent is not to skip unit tests, call dh_auto_test here (integration tests are already skipped via ADSYS_SKIP_INTEGRATION_TESTS).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b216827 to
ab3b09b
Compare
ab3b09b to
9f69032
Compare
|
Pushed follow-up commits addressing the review threads (now resolved). Summary of what changed:
Intentional/by design: the code-level default stays |
2b31c7e to
f1bc708
Compare
The native enrollment path has to talk to Active Directory's LDAP directory as the machine account, but go-ldap can only drive a GSSAPIClient and ships no Kerberos implementation of its own. Bridging the two lets us reuse the machine credentials already present in the host's credential cache instead of provisioning or storing a separate secret. The adapter performs the GSSAPI/SASL bind with the machine credentials, and LDAP discovery upgrades the connection with StartTLS before the bind so directory responses used for enrollment are protected in transit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The LDAP enrollment method must learn which enrollment services and certificate templates are available without shelling out to the Samba-based Python helper. Querying the directory directly makes discovery a first-class, testable part of the daemon rather than a side effect of an external process. The directory client sits behind an interface so tests can inject responses without a live AD, and connections reuse the machine's Kerberos cache so no extra credentials are needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dropping the Samba/CEPCES stack also removes the bookkeeping it provided: nothing records what has already been enrolled, and the CAs that AD advertises are not trusted by the system. Left unaddressed, every policy refresh would request brand new certificates and the issued certificates would fail to validate. Persisting enrollment state to disk lets repeated policy applications reuse the existing key and certificate pairs instead of re-issuing them, and installing the root CAs discovered over LDAP into the system trust store means both the issued certificates and the servers presenting them verify correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Requesting the certificate is the last step that still required cepces. Submitting the CSR straight to AD CS over the MS-ICPR RPC interface lets the entire enrollment run natively, authenticated with the machine's Kerberos credentials and with no Python runtime on the host. Submission is kept behind an interface so the network call stays out of unit tests, and the pending (manual-approval) result is handled explicitly rather than assuming the CA issues immediately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
certmonger drives certificate renewal through an external submit helper, a role previously filled by cepces-submit. Providing a helper that speaks MS-ICPR directly keeps certmonger-tracked certificates renewable under the LDAP method without reintroducing a dependency on cepces. It implements the submit, identify and template-enumeration operations certmonger invokes and authenticates with the credentials already referenced by KRB5CCNAME. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The new LDAP path and the legacy CEPCES path have to coexist so that upgrading does not break deployments that already rely on cepces. Introducing explicit configuration values to select between them, and defaulting to CEPCES, keeps existing installations on their proven path while allowing new ones to opt into the native method. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
With discovery, trust, state persistence and CSR submission in place, the manager needs to decide which mechanism to run for a given machine. Dispatching on the configured method lets the daemon perform the whole LDAP enrollment in-process while still delegating to the embedded Python script for the CEPCES path. Threading the choice through the daemon flag, the service option and the manager options also makes the behaviour injectable, so the full LDAP flow can be exercised in tests without a live directory. The legacy cert-autoenroll debug script dump remains available for CEPCES support. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
772d307 to
d3ffabf
Compare
Upgrades must not silently change enrollment behaviour under existing deployments, so the compiled-in default stays cepces. A brand new install has no such history to preserve and is better served by the native LDAP/RPC path, which needs no external Python/CEPCES tooling. Seed /etc/adsys.yaml with the ldap method only when postinst sees no prior version, leaving upgrades on their existing default. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
With new installs defaulting to native LDAP enrollment, the Python CEPCES stack is now only the optional legacy fallback rather than part of the primary path. Promote python3-cepces from Suggests to Recommends so that fallback still works out of the box yet can be dropped with --no-install-recommends, and stop listing python3-samba in the apport hook since it is no longer relevant to diagnosing the default path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The documentation still presented enrollment as a CEP/CES-only feature, which no longer matches the default behaviour for new installations. Explaining that two methods exist, how to choose between them, and what the native LDAP flow actually does lets administrators configure and troubleshoot the method their system is running. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
a292570 to
cf28df7
Compare
The adsysd integration tests run the daemon against a guest-only smbd with no real KDC, relying on libsmbclient falling back from kerberos to anonymous authentication. Samba >= 4.23 dropped that implicit fallback, so the SMB fetch fails with EINVAL on current systems. Gating a test-only ADSYS_TESTS_WITHOUT_KERBEROS variable in ad.New() lets the daemon skip kerberos under the harness; it must never be set in production. The mock D-Bus/polkit/systemd containers also resolve the connecting user through the bind-mounted /etc/passwd and /etc/group. When the suite runs as a user only known through NSS (e.g. an LDAP/SSSD user absent from the local files), the container cannot resolve the UID and resets the connection, so the harness now generates augmented passwd/group databases that include the current user before mounting them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Pin LDAP StartTLS to verify against system trust store plus adsys-managed CAs, preventing MITM on first enrollment when the AD root CA is not yet in the system trust store. - Validate discovered CA certificates (self-signed root or chains to trusted anchor, not expired) before installing them into the system trust store. - Use atomic symlink creation (temp + rename) to eliminate TOCTOU race in trust store symlink replacement. - Verify issued certificate public key matches the generated private key before writing to disk. - Write private keys and certificates atomically via safeWriteFile. - Clarify GSSAPI SASL auth-only selection rationale in comments. Addresses Copilot review threads on ldap.go:64, trust.go:69 and security findings on unverified CA installation and symlink TOCTOU.
…ment - Add cleanupOrphanedCerts() to remove cert/key files and trust store symlinks from previous enrollment state that are no longer present in AD, preventing orphaned trust entries from accumulating. - Fix ldapPolicyAllowsEnrollment to return (false, nil) instead of (false, error) when no enabled LDAP endpoint is found, making the 'skip' path in applyPolicyLDAP reachable instead of turning a skip condition into a hard policy error. - Fix state.go Nickname comment to reflect it's a sanitized on-disk identifier, not a certmonger tracking ID. - Update tests to match new behavior. Addresses Copilot review threads on certificate.go:303, certificate.go:540 and state.go:34.
- Validate certificate enrollment method in adsysservice options, rejecting typos and unknown values early instead of silently falling back to the legacy path. - Add 64KB size limit on CSR data in adsys-certsubmit to prevent memory exhaustion DoS. - Update CertAutoEnrollScript RPC message to clarify the script is deprecated rather than claiming no external script exists. Addresses Copilot review threads on manager.go:264, adsys-certsubmit and policy.go:171.
… expiry Follow-ups to the native LDAP enrollment security review: - Re-enroll a certificate on policy refresh when it is missing or within 30 days of expiry (certNeedsRenewal). Issued certs are not tracked by certmonger, and an existing cert was previously reused indefinitely as long as its file existed, so it could silently expire. - Skip a CA when its root certificate fails to install or verify, preserving any previously-enrolled state so a transient failure does not orphan still-valid certificates, instead of issuing certs that cannot validate without the root in the trust store. - Rebuild the system trust store after BOTH installing new roots and removing orphaned ones, so removed CAs are purged from the consolidated bundle in the same pass. - Log orphan-removal failures instead of silently dropping them. - Document that LDAP-issued certificates are renewed before expiry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ers expect Domain-joined Ubuntu machines are configured by SSSD, whose krb5.conf usually does not list KDCs because SSSD locates them through DNS SRV records. The LDAP bind papered over this by loading /etc/krb5.conf and unconditionally forcing dns_lookup_kdc on, which silently overrode an administrator's deliberate configuration, and the MS-ICPR submission path did no KDC discovery at all — so certificate issuance failed on an otherwise healthy domain member. Derive the Kerberos configuration once for both paths: keep whatever the system krb5.conf provides, enable DNS-based KDC discovery only when the target realm has no KDC configured, and refuse with an explanatory error when discovery was explicitly turned off but no KDC exists to fall back on. Resolve the service realm from the target host so cross-realm CA deployments are honoured, and request the exact host SPN rather than letting a cached same-host ticket for another service class be reused, which AD rejects. Finally, translate the opaque "invalid checksum" RPC rejection into guidance about the SPN registration, duplication and realm-mapping mistakes that actually cause it, so operators can fix the environment instead of guessing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ding Microsoft is progressively turning on LDAP channel binding enforcement (LdapEnforceChannelBinding) on domain controllers, and hardened or newer DCs already refuse a GSSAPI bind that carries no channel binding token, answering with LDAP result 49 / data 80090346 (SEC_E_BAD_BINDINGS). Our StartTLS-then-GSSAPI bind sent none, so certificate enrollment could not even authenticate against those DCs. Derive the RFC 5929 tls-server-end-point token from the StartTLS server certificate and carry it in the GSS-API authenticator checksum as RFC 4121 requires, so the bind satisfies a channel-binding-enforcing DC. The token is advisory for DCs that do not enforce it — they ignore it — so sending it unconditionally is safe and needs no probing or configuration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lled CA Native LDAP enrollment must reach the domain controller over StartTLS to discover the enterprise CA and templates, yet on a freshly joined machine that CA is not in any trust store — adsys installs it only later in the same run. Verifying the DC certificate chain strictly made the very first enrollment unsolvable: the client could not fetch the CA because it did not yet trust the certificate that CA had signed. Break the deadlock the way Windows autoenrollment does — by trusting the directory rather than a pre-provisioned certificate. On a bootstrap connection, tolerate only an unknown-authority chain error and anchor trust on the mutually authenticated Kerberos GSSAPI bind (with TLS channel binding) performed immediately after StartTLS. Every other failure — expired, not-yet-valid, wrong hostname — stays fatal, and once adsys installs the CA later in the run, all subsequent refreshes verify the full chain strictly. Hostname checking is also split out from chain building so it can fall back to the Subject Common Name: domain controller certificates from legacy templates often carry no Subject Alternative Name, which crypto/x509 has refused to match since Go 1.15 and which would otherwise break StartTLS even against a trusted DC. Finally, expose the strict (non-bootstrap) connector as NewKerberosLDAPConnector so other AD-facing managers can reuse the same authenticated LDAP path without inheriting the first-run trust exception. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
go.mod now sets the language version to 1.25.0, so building the package with the previous golang-go 1.24 build-dependency floor would select a toolchain older than the source declares it needs. Raise the floor to 1.25 to keep the Debian build in step with the module's stated minimum. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The adsys postinst runs pam-auth-update as part of `configure`, which can open an interactive prompt. Under a non-interactive install (unattended upgrades, image builds, CI) that prompt has no controlling terminal and stalls or fails the whole package configuration. Force DEBIAN_FRONTEND=noninteractive for the invocation so PAM is updated with its default answers and the install always completes unattended. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The native LDAP enrollment method records issued certificates in adsys' own JSON state rather than certmonger, which leaves them invisible to getcert and effectively unmanageable: the troubleshooting guide even resorted to telling administrators to delete state files by hand, inviting drift between what is on disk and what adsys believes. Add a manager-level API (list, status, renew, remove, verify, CA discovery and template lookup) that works from that state so the daemon and CLI can offer supported tooling. Every entry point is gated on the ldap method so cepces deployments keep deferring to certmonger/getcert. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Machine private keys live in a root-only directory and the daemon is the only privileged component, so certificate management has to run server side. Surface the new manager API through the existing service so unprivileged clients can drive it over the daemon socket, the same way policy commands are streamed. Reads are allowed to any caller while renewals and removals require the policy-update authorization, and the "not ldap" sentinel is translated into an actionable error so cepces hosts are pointed at getcert. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Administrators need a getcert-equivalent workflow for adsys-managed certificates instead of hand-editing /var/lib/adsys. Add the certificate command group (list, status, renew, remove, verify, cas, templates) with optional JSON output so results can feed automation. status maps certificate health onto a process exit code so it can back monitoring checks; honouring that meant teaching the top-level runner to surface an exit code carried on the returned error, without logging it as a failure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the "remove the state file by hand" workaround, which risked leaving disk and adsys state out of sync, with guidance built around the new adsysctl certificate commands, and add a dedicated how-to so the supported inspect/renew/remove workflow is discoverable rather than buried in troubleshooting. Also refresh the generated daemon CLI reference, which predated the existing --certificate-enrollment and --gpo-list-timeout flags, so the documented options match the shipped binary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cf28df7 to
29479ac
Compare
Allow certificate enrollment through LDAP protocol.