feat(dev): DCNTRLIZED_DEV drives dev mode; dev fleet runs real mTLS
The single canonical dev flag is DCNTRLIZED_DEV=true, injected by every docker-compose.debug/dev.yaml. Dev mode now selects endpoints and behaviors without downgrading control-plane security: - lib/env: IsDev()/GiteaURL(); registry BaseURL keys on DCNTRLIZED_DEV and DCNTRLIZED_GITEA_URL (renamed from GITEA_URL). - authmgmt: Scheme()/PeerTransport() (satellite edges) key on DCNTRLIZED_DEV — dev satellites keep a plaintext RN-facing listener, production mTLS. The auth-management edge (New()) stays mTLS in both, preferring DCNTRLIZED_AUTH_URL in dev. - Removed INSECURE_INTERNAL + propagateInsecureInternal from Maestro: the orchestrator no longer runs plaintext, and no longer injects the escape hatch into services it starts. - lib/devboot: at boot (dev only) Maestro provisions the identities its compose-managed peers bind-mount at /dcntrlized/pki (before infra boots) and approves the known fleet in the auth trust store. Unknown peers still record a pending authorization and are refused (mechanisms active). - Composes: prod docker-compose.yaml is clean (no DCNTRLIZED_DEV/GITEA_URL); the debug compose carries the dev flag + DCNTRLIZED_GITEA_URL only.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) 2026 Dcntrlized
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Package devboot provisions the dev fleet the DCNTRLIZED_DEV way: Maestro
|
||||
// issues the identities its docker-compose.dev/debug.yaml peers bind-mount at
|
||||
// /dcntrlized/pki, and approves those satellites in the auth service trust
|
||||
// store. Dev mode runs the real security mechanisms (mTLS peer edges,
|
||||
// authorized-keys enforcement) — this only removes the human-in-the-loop for
|
||||
// the *known* dev fleet; any unknown peer still records a pending
|
||||
// authorization and is refused.
|
||||
package devboot
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.dcntrlized.org/Dcntrlized/Maestro/lib/authmgmt"
|
||||
"git.dcntrlized.org/Dcntrlized/Maestro/lib/containers"
|
||||
"git.dcntrlized.org/Dcntrlized/Maestro/lib/logger"
|
||||
"git.dcntrlized.org/Dcntrlized/Maestro/lib/pki"
|
||||
)
|
||||
|
||||
// Infra are the shared services whose identities the dev composes mount. They
|
||||
// are peers/attesters themselves, not approval candidates (auth is the
|
||||
// trust-store owner, Maestro uses the internal RequireMaestro endpoints).
|
||||
var Infra = []string{"dcntrlized-auth"}
|
||||
|
||||
// Fleet lists the dev-mode satellite identities Maestro issues and approves.
|
||||
// Their CNs must match the service names used in the dev composes' pki mounts.
|
||||
var Fleet = []string{"keepnote", "calendar", "testservice"}
|
||||
|
||||
// EnsureIdentities provisions the instance CA identities the dev composes
|
||||
// bind-mount at /dcntrlized/pki, so compose-managed services come up with a
|
||||
// valid mTLS identity before they ever dial the auth service. Run before the
|
||||
// shared infrastructure is ensured, because dcntrlized-auth's mTLS listener
|
||||
// refuses to start without its identity.
|
||||
func EnsureIdentities() {
|
||||
dataRoot, err := containers.DataRoot()
|
||||
if err != nil {
|
||||
logger.Warn("devboot: resolve data root: %v", err)
|
||||
return
|
||||
}
|
||||
ca, err := pki.EnsureCA(dataRoot)
|
||||
if err != nil {
|
||||
logger.Warn("devboot: ensure instance CA: %v", err)
|
||||
return
|
||||
}
|
||||
for _, name := range append(append([]string{}, Infra...), Fleet...) {
|
||||
if _, err := pki.EnsureServiceIdentity(ca, name); err != nil {
|
||||
logger.Warn("devboot: ensure identity for %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
logger.Info("devboot: ensured dev fleet identities (%d infra + %d satellites)", len(Infra), len(Fleet))
|
||||
}
|
||||
|
||||
// ApproveFleet approves the dev satellites in the auth service trust store —
|
||||
// the owner decision a fresh first-contact would pend, made once for the known
|
||||
// fleet. Best-effort: if the auth service is not up yet, the first-contact
|
||||
// no-op path records the pendings and the next boot approves them.
|
||||
func ApproveFleet(ctx context.Context) {
|
||||
if len(Fleet) == 0 {
|
||||
return
|
||||
}
|
||||
client, err := authmgmt.New()
|
||||
if err != nil {
|
||||
logger.Warn("devboot: build auth client: %v", err)
|
||||
return
|
||||
}
|
||||
dataRoot, err := containers.DataRoot()
|
||||
if err != nil {
|
||||
logger.Warn("devboot: resolve data root: %v", err)
|
||||
return
|
||||
}
|
||||
ca, err := pki.EnsureCA(dataRoot)
|
||||
if err != nil {
|
||||
logger.Warn("devboot: ensure instance CA: %v", err)
|
||||
return
|
||||
}
|
||||
for _, name := range Fleet {
|
||||
identity, err := pki.EnsureServiceIdentity(ca, name)
|
||||
if err != nil {
|
||||
logger.Warn("devboot: load identity for %s: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if err := client.Approve(ctx, name, identity.Meta.Fingerprint); err != nil {
|
||||
logger.Warn("devboot: approve %s: %v", name, err)
|
||||
continue
|
||||
}
|
||||
logger.Info("devboot: approved dev peer %s (%s)", name, identity.Meta.Fingerprint)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user