Add managed device configuration and default device-flow login#145
Open
c1-squire-dev[bot] wants to merge 4 commits into
Open
Add managed device configuration and default device-flow login#145c1-squire-dev[bot] wants to merge 4 commits into
c1-squire-dev[bot] wants to merge 4 commits into
Conversation
Add a `pkg/managedconfig` package that reads the ConductorOne managed
device configuration — administrator-defined policy delivered to a device
through an MDM — from the company-level `ai.c1` managed store. This lets a
client auto-discover the tenant it belongs to without per-machine manual
setup.
Per-OS backends (read-only):
- Linux: /etc/c1/managed.toml (TOML)
- macOS: the `ai.c1` managed-preferences domain (via `defaults`)
- Windows: HKLM\SOFTWARE\Policies\ConductorOne\C1 (REG_SZ)
The store is read as a key/value map: unknown keys are ignored, and an
absent, unreadable, or malformed store yields a zero Config. The package
never returns an error or panics, so callers can consult it unconditionally.
The single v1 key, `TenantDomain`, is the full DNS host of the tenant's
control plane (e.g. `acme.conductor.one`); `ControlPlaneURL()` derives
`https://{TenantDomain}` from it.
Wire `cone login` to consult the managed configuration first: a bare
`cone login` with no argument now discovers its tenant from managed policy
and goes straight into the existing OAuth 2.0 Device Authorization Grant
flow. When no managed configuration is present, behavior is unchanged and
the tenant must be supplied as an argument.
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Split the macOS managed-config reader by build tag: - managedconfig_darwin_cgo.go (darwin && cgo): read the "ai.c1" managed-preferences store natively via CFPreferencesCopyAppValue, the managed-prefs-aware CoreFoundation API, instead of shelling out. Memory handling follows the crypto/x509/internal/macos idioms: every Create/Copy ref is released, the returned value is type-checked as a CFString before extraction, and the string is read without assuming a fixed buffer (CFStringGetCStringPtr fast path, CFStringGetCString fallback sized by CFStringGetMaximumSizeForEncoding). Absent key, non-string value, or any failure yields an empty result; never panics or errors. - managedconfig_darwin_defaults.go (darwin && !cgo): the existing `defaults read` path, kept as the CGO-off fallback so pure-Go cross-compilation (e.g. darwin/arm64 from Linux, as the release build does) still works. Retains the stubbable defaultsRead var and reuses parseDefaultsValue. Exactly one file compiles per (darwin, cgo?) combination; both expose the same readManagedConfig entrypoint and preserve the existing contract (single-key read, unknown keys ignored, absent/malformed -> empty, TenantDomain validated as a full DNS host downstream). Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
golangci-lint (errcheck) flags the unchecked k.Close() return on the GOOS=windows build of the managed-config reader. Defer an explicit discard so the intent is clear and the Windows build lints clean. Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Each nil guard used t.Fatal without a terminal statement, so staticcheck (SA5011) sees control fall through to the pointer dereference on the nil path. Add an explicit return after t.Fatal so the deref is unreachable when the pointer is nil. Semantics unchanged; unblocks CI go-lint. Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
pquerna
approved these changes
Jul 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a way for
coneto auto-discover the tenant it belongs to from managed device configuration — administrator-defined policy delivered to a device through an MDM — so users don't have to configure the tenant by hand. When such policy is present, a barecone login(no argument) discovers the tenant automatically and goes straight into the existing OAuth 2.0 Device Authorization Grant flow.What's included
1. New package:
pkg/managedconfigReads the company-level
ai.c1managed store, OS-selected at build time (read-only):/etc/c1/managed.toml(TOML)ai.c1managed-preferences domain (managed layer, not the on-disk plist)HKLM\SOFTWARE\Policies\ConductorOne\C1, valueTenantDomain(REG_SZ)On macOS the reader is split by build tag so it works both natively and under pure-Go cross-compilation:
darwin && cgo→ native CoreFoundation:CFPreferencesCopyAppValue("TenantDomain", "ai.c1"), the managed-preferences-aware API, with no subprocess. Memory handling follows thecrypto/x509/internal/macosidioms — everyCreate/Copyref is released, the returned value is type-checked as aCFStringbefore extraction, and the string is read without assuming a fixed buffer (CFStringGetCStringPtrfast path,CFStringGetCStringfallback sized viaCFStringGetMaximumSizeForEncoding). An absent key, a non-string value, or any failure yields an empty result — it never errors or panics.darwin && !cgo→ the existingdefaults readpath, kept as the CGO-off fallback so pure-Go cross-compilation (e.g.darwin/arm64from Linux, which is how the release binaries are built) still works.Exactly one of the two compiles per build; both expose the same reader and preserve identical semantics.
Semantics:
Config. The package never returns an error or panics, so callers can consult it unconditionally.TenantDomainis the full DNS host of the tenant's control plane (e.g.acme.conductor.one,acme.eu.c1.ai); a bare slug is rejected.Config.ControlPlaneURL()deriveshttps://{TenantDomain}.2.
cone loginreads managed config firstresolveLoginTenantconsults managed configuration before the positional argument:cone loginneeds no argument and proceeds directly into the device code flow.The device authorization flow itself is
cone's existingconductoroneapi.LoginFlowwith the existing public client ID — no new client ID and no client secret is introduced.Testing
go build ./...,go vet, andgolangci-lint runare clean.go test ./pkg/managedconfig/...passes. The Linux TOML path is unit-tested end to end (valid parse, unknown-key-ignored, absent file, malformed file, bare-slug rejection); OS-independent parsing/validation helpers are covered directly.windows/amd64and, withCGO_ENABLED=0, fordarwin/amd64anddarwin/arm64(the CGO-offdefaultsfallback — this is the configuration the release binaries ship).darwin && cgonative reader is not built by CI (CI runs on Linux and the release build cross-compiles darwin withCGO_ENABLED=0), so it needs a macOS build withCGO_ENABLED=1to validate the CoreFoundation linkage. It was written to thecrypto/x509/internal/macosCF idioms andgofmted, but not compiled here — a reviewer on a Mac (or a macOS+cgo CI job) should confirm it builds.CI lint
Also fixes a pre-existing staticcheck SA5011 (nil-deref-after-check) in
cmd/cone/secret_test.go— eachif x == nil { t.Fatal(...) }guard now has an explicitreturn, so CIgo-lintis green. Additionally handles the previously-uncheckedk.Close()error in the Windows reader (errcheckon theGOOS=windowsbuild).