fix(containers): remap identity bind sources to host paths in dev

The dev maestro container's data root is /data/.dcntrlized
(XDG_DATA_HOME=/data), but bind-mount sources are resolved on the daemon's
host namespace — a nonexistent /data/.dcntrlized/... source made Docker
create empty directories, so the auth hub saw /dcntrlized/pki/tls.crt as a
directory and TLS setup failed. DCNTRLIZED_HOST_DATA_ROOT (compose-injected,
/home/franciscofadigas/.dcntrlized) is now substituted for the container-internal prefix when
building identity bind sources. hostBindSource leaves paths unchanged on host
installs.
This commit is contained in:
2026-08-16 16:34:46 +01:00
parent e13831ca20
commit cda3091ad8
3 changed files with 47 additions and 3 deletions
+4
View File
@@ -16,6 +16,10 @@ services:
environment: environment:
- DCNTRLIZED_DEV=true - DCNTRLIZED_DEV=true
- XDG_DATA_HOME=/data - XDG_DATA_HOME=/data
# Host path backing the data root, so Maestro's containerized start path
# can produce bind-mount sources the daemon resolves on the host
# namespace (identity injection for infra/peers).
- DCNTRLIZED_HOST_DATA_ROOT=${XDG_DATA_HOME:-${HOME}}/.dcntrlized
- DCNTRLIZED_GITEA_URL=${DCNTRLIZED_GITEA_URL:-http://192.168.122.183:3000} - DCNTRLIZED_GITEA_URL=${DCNTRLIZED_GITEA_URL:-http://192.168.122.183:3000}
- GITEA_REGISTRY_RO_TOKEN=${GITEA_REGISTRY_RO_TOKEN} - GITEA_REGISTRY_RO_TOKEN=${GITEA_REGISTRY_RO_TOKEN}
- GITEA_RW_PACKAGE_TOKEN=${GITEA_RW_PACKAGE_TOKEN} - GITEA_RW_PACKAGE_TOKEN=${GITEA_RW_PACKAGE_TOKEN}
+20 -3
View File
@@ -18,6 +18,7 @@ package containers
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -130,9 +131,9 @@ func injectServiceIdentity(ctx context.Context, project *composetypes.Project, s
} }
mounts := []string{ mounts := []string{
fmt.Sprintf("%s:%s/ca.crt:ro", pki.CertPath(dataRoot), identityMountDir), fmt.Sprintf("%s:%s/ca.crt:ro", hostBindSource(dataRoot, pki.CertPath(dataRoot)), identityMountDir),
fmt.Sprintf("%s:%s/tls.crt:ro", identity.CertPath, identityMountDir), fmt.Sprintf("%s:%s/tls.crt:ro", hostBindSource(dataRoot, identity.CertPath), identityMountDir),
fmt.Sprintf("%s:%s/tls.key:ro", identity.KeyPath, identityMountDir), fmt.Sprintf("%s:%s/tls.key:ro", hostBindSource(dataRoot, identity.KeyPath), identityMountDir),
} }
env := map[string]string{ env := map[string]string{
"SERVICE_NAME": serviceName, "SERVICE_NAME": serviceName,
@@ -161,6 +162,22 @@ func injectServiceIdentity(ctx context.Context, project *composetypes.Project, s
return nil return nil
} }
// hostBindSource maps a dataRoot-relative path to a bind-mount source the
// Docker daemon can resolve. Bind sources are resolved on the daemon's host
// namespace, but inside the dev container the data root lives at a
// container-internal path (/data/.dcntrlized via XDG_DATA_HOME=/data) that
// does not exist on the host — the daemon would silently create it as an
// empty directory. DCNTRLIZED_HOST_DATA_ROOT (compose-injected, e.g.
// $HOME/.dcntrlized) carries the host path backing the data root; when set,
// the container-internal prefix is swapped for it. On a host install the path
// is already host-resolvable and returned unchanged.
func hostBindSource(dataRoot, p string) string {
if host := os.Getenv("DCNTRLIZED_HOST_DATA_ROOT"); host != "" && strings.HasPrefix(p, dataRoot) {
return filepath.Join(host, strings.TrimPrefix(p, dataRoot))
}
return p
}
// pinAuthFingerprint returns the service's pinned auth fingerprint, fetching // pinAuthFingerprint returns the service's pinned auth fingerprint, fetching
// and persisting it when the install record does not have one yet (M2 §5). // and persisting it when the install record does not have one yet (M2 §5).
// Best-effort: when the auth service is unreachable, it returns "" and the // Best-effort: when the auth service is unreachable, it returns "" and the
+23
View File
@@ -14,3 +14,26 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
package containers package containers
import (
"testing"
)
func TestHostBindSource(t *testing.T) {
t.Setenv("DCNTRLIZED_HOST_DATA_ROOT", "")
got := hostBindSource("/data/.dcntrlized", "/data/.dcntrlized/pki/services/auth/tls.crt")
if got != "/data/.dcntrlized/pki/services/auth/tls.crt" {
t.Fatalf("without host root the path must pass through, got %q", got)
}
t.Setenv("DCNTRLIZED_HOST_DATA_ROOT", "/home/user/.dcntrlized")
got = hostBindSource("/data/.dcntrlized", "/data/.dcntrlized/pki/services/auth/tls.crt")
if want := "/home/user/.dcntrlized/pki/services/auth/tls.crt"; got != want {
t.Fatalf("host root must replace the container-internal prefix, got %q want %q", got, want)
}
got = hostBindSource("/data/.dcntrlized", "/data/.dcntrlized/pki/ca.crt")
if want := "/home/user/.dcntrlized/pki/ca.crt"; got != want {
t.Fatalf("ca path must be remapped too, got %q want %q", got, want)
}
}