Skip to content

[WIP] certificates: Allow LDAP enrollment#1401

Draft
denisonbarbosa wants to merge 15 commits into
mainfrom
ldap-cert-enrollment
Draft

[WIP] certificates: Allow LDAP enrollment#1401
denisonbarbosa wants to merge 15 commits into
mainfrom
ldap-cert-enrollment

Conversation

@denisonbarbosa

Copy link
Copy Markdown
Member

Allow certificate enrollment through LDAP protocol.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread internal/policies/certificate/ldap.go Outdated
Comment thread internal/policies/certificate/ldap.go
Comment thread internal/policies/certificate/trust.go Outdated
Comment thread internal/policies/certificate/state.go
Comment thread internal/policies/certificate/certificate.go Outdated
Comment thread docs/index.md Outdated
Comment thread internal/policies/certificate/certificate_test.go
Comment thread docs/explanation/certificates.md Outdated
Comment thread internal/policies/certificate/certificate_test.go
Comment thread internal/policies/certificate/ldap_test.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 65 out of 99 changed files in this pull request and generated 14 comments.

Comment thread internal/policies/manager.go
Comment thread internal/policies/manager.go
Comment thread internal/policies/certificate/certificate.go
Comment thread internal/policies/certificate/certificate.go
Comment thread internal/policies/certificate/trust.go Outdated
Comment thread internal/adsysservice/policy.go
Comment thread internal/ad/ad.go
Comment thread cmd/adsys-certsubmit/main.go Outdated
Comment thread cmd/adsys-certsubmit/main.go Outdated
Comment thread internal/policies/certificate/certificate.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 100 changed files in this pull request and generated 4 comments.

Comment thread debian/rules
Comment thread internal/policies/certificate/certificate.go
Comment thread internal/policies/certificate/certificate.go
Comment thread internal/ad/ad.go Outdated
@denisonbarbosa

Copy link
Copy Markdown
Member Author

Pushed follow-up commits addressing the review threads (now resolved). Summary of what changed:

certificate: harden LDAP/TLS and trust store (updated)

  • LDAP MITM / auth-only SASL: StartTLS verifies the DC certificate against the system store plus adsys-managed CAs before the GSSAPI bind, and the configured global trust dir is now honored.
  • CA validation comment made accurate: a discovered self-signed AD CS root can only be sanity-checked (validity window); its trust rests on the authenticated StartTLS+Kerberos channel it was discovered through (TOFU, as Windows autoenrollment trusts the directory). Non-self-signed CAs must chain to an already-trusted anchor.
  • Issued cert is rejected if expired/not-yet-valid; the RPC certificate response is size-capped; key/cert are written via an O_EXCL temp file + rename.

gate Kerberos bypass behind testing.Testing() — the ADSYS_TESTS_WITHOUT_KERBEROS escape hatch can no longer take effect in production builds.

fix orphaned certs, unreachable skip path, state comment — graceful skip path reachable; orphan cleanup; stale comments corrected.

validate enrollment config, CSR size limit, CEPCES packaging — enrollment method validated/normalized across all layers; CSR size checked before the full read; vendored Python reinstalled; adsys-certsubmit accepts the CA via certmonger env vars.

harden LDAP enrollment robustness and renew certs before expiry (new)

  • LDAP-issued certs are re-enrolled on policy refresh when missing or within 30 days of expiry (this path is not certmonger-tracked); docs updated to match.
  • A CA whose root fails to install/verify is skipped (prior state preserved); the system trust store is rebuilt after both installs and orphan removals; a failed renewal retains the still-valid certificate instead of letting orphan cleanup delete it (covered by a new regression test).

Intentional/by design: the code-level default stays cepces (new installs select ldap via postinst); issued-cert checks do not enforce subject/SAN/EKU/chain (AD CS templates legitimately control these and the issuer may be a subordinate CA); renewal is handled in-daemon rather than via certmonger.

@denisonbarbosa denisonbarbosa force-pushed the ldap-cert-enrollment branch 2 times, most recently from 2b31c7e to f1bc708 Compare June 26, 2026 21:43
denisonbarbosa and others added 8 commits June 26, 2026 17:46
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>
Now that a machine can enrol entirely over LDAP, the Python/Samba/cepces
stack is only needed for the legacy method and no longer belongs in the
package's hard dependencies. Demoting those packages to recommendations
slims down new LDAP-first installs while keeping the CEPCES compatibility
assets available for existing deployments.

New installations are provisioned to use the LDAP method by default, but
upgrades can continue using the legacy path when explicitly configured.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
denisonbarbosa and others added 5 commits June 26, 2026 17:53
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>
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.
The ADSYS_TESTS_WITHOUT_KERBEROS environment variable can no longer
disable Kerberos authentication in production builds. The check is now
gated on testing.Testing(), which returns true only when running under
go test, ensuring the escape hatch cannot be triggered via systemd
overrides or inherited environment in production.

Addresses Copilot review threads on ad.go:152 and ad.go:156.
…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.
denisonbarbosa and others added 2 commits June 26, 2026 17:53
…ES packaging

- 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.
- Restore Python assets installation in debian/rules so the legacy
  CEPCES enrollment method doesn't fail with ImportError at runtime.
- 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,
debian/rules:68 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants