fix(authority): use isolated service credential mount
CI / test (push) Failing after 36s

This commit is contained in:
m
2026-08-31 13:26:50 +03:00
parent 35666c9923
commit f42a94ee7a
5 changed files with 66 additions and 7 deletions
@@ -8,6 +8,7 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"strings"
"time"
@@ -22,7 +23,7 @@ import (
const (
delegatedControlPlaneActor identity.ActorID = "domovoy-control-plane-actor"
delegatedControlPlanePrincipal identity.PrincipalID = "domovoy-control-plane-principal"
delegatedControlPlaneTokenPath = "/run/secrets/control_plane_service_token"
delegatedControlPlaneTokenFile = "control_plane_service_token"
)
type delegatedControlPlaneAuthoritySource interface {
@@ -157,13 +158,13 @@ func delegatedControlPlaneCredential() (string, error) {
if ref == "" {
return "", errors.New("control-plane service credential ref is required")
}
root := os.Getenv("SECRET_BACKEND_ROOT")
root := os.Getenv("CONTROL_PLANE_SERVICE_SECRET_ROOT")
if root == "" {
root = "/run/secrets"
root = "/run/control-plane-secrets"
}
resolver, err := platformsecrets.NewFileResolver(root)
if err != nil {
return "", err
}
return resolver.ResolveFile(secretcontracts.SecretFileRef{CredentialRef: secretcontracts.CredentialRef(ref), Purpose: secretcontracts.SecretPurposeServiceAuthentication, Consumer: secretcontracts.RuntimeConsumerCommit, Path: delegatedControlPlaneTokenPath})
return resolver.ResolveFile(secretcontracts.SecretFileRef{CredentialRef: secretcontracts.CredentialRef(ref), Purpose: secretcontracts.SecretPurposeServiceAuthentication, Consumer: secretcontracts.RuntimeConsumerCommit, Path: filepath.Join(root, delegatedControlPlaneTokenFile)})
}
@@ -5,6 +5,8 @@ import (
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
@@ -20,6 +22,33 @@ type delegatedAuthorityFake struct {
revoked bool
}
func TestDelegatedAuthorityCredentialRotationRejectsOldToken(t *testing.T) {
root := t.TempDir()
tokenPath := filepath.Join(root, "control_plane_service_token")
if err := os.WriteFile(tokenPath, []byte("old-token\n"), 0o600); err != nil {
t.Fatal(err)
}
t.Setenv("CONTROL_PLANE_SERVICE_CREDENTIAL_REF", "credential://domovoy/control-plane/service-token/v1")
t.Setenv("CONTROL_PLANE_SERVICE_SECRET_ROOT", root)
h := delegatedControlPlaneHandler{credential: delegatedControlPlaneCredential}
oldRequest := httptest.NewRequest(http.MethodPost, "/v1/internal/control-plane/authorize", nil)
oldRequest.Header.Set("Authorization", "Bearer old-token")
if !h.serviceAuthenticated(oldRequest) {
t.Fatal("active credential was rejected before rotation")
}
if err := os.WriteFile(tokenPath, []byte("new-token\n"), 0o600); err != nil {
t.Fatal(err)
}
if h.serviceAuthenticated(oldRequest) {
t.Fatal("old credential remained valid after rotation")
}
newRequest := httptest.NewRequest(http.MethodPost, "/v1/internal/control-plane/authorize", nil)
newRequest.Header.Set("Authorization", "Bearer new-token")
if !h.serviceAuthenticated(newRequest) {
t.Fatal("new credential was rejected after rotation")
}
}
func (f *delegatedAuthorityFake) LoadDelegatedControlPlaneGrant(context.Context, authority.GrantID) (authority.DelegatedControlPlaneGrant, error) {
if f.grant.ID == "" {
return authority.DelegatedControlPlaneGrant{}, errors.New("missing")
@@ -4,6 +4,8 @@ import (
"context"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"git.cin.su/m/Assistant/contracts/authority"
@@ -23,3 +25,15 @@ func TestControlPlaneClientSendsOnlyServiceCredentialToAuthorityRuntime(t *testi
t.Fatalf("authorize: %v", err)
}
}
func TestControlPlaneCredentialUsesDedicatedSecretRoot(t *testing.T) {
root := t.TempDir()
if err := os.WriteFile(filepath.Join(root, controlPlaneServiceTokenFile), []byte("test-machine-token\n"), 0o600); err != nil {
t.Fatal(err)
}
t.Setenv("CONTROL_PLANE_SERVICE_CREDENTIAL_REF", "credential://domovoy/control-plane/service-token/v1")
t.Setenv("CONTROL_PLANE_SERVICE_SECRET_ROOT", root)
if got, err := readControlPlaneServiceCredential(); err != nil || got != "test-machine-token" {
t.Fatalf("control-plane credential resolution failed: %v", err)
}
}
+16 -2
View File
@@ -31,6 +31,7 @@ import (
"log"
"net/http"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
@@ -45,7 +46,7 @@ const (
epistemicsDBPasswordPath = "/run/secrets/epistemics_db_password"
interactionDBPasswordPath = "/run/secrets/interaction_db_password"
selfDBPasswordPath = "/run/secrets/self_db_password"
controlPlaneServiceTokenPath = "/run/secrets/control_plane_service_token"
controlPlaneServiceTokenFile = "control_plane_service_token"
)
const maxCreatorCredentialsBytes = 4 << 10
@@ -141,7 +142,7 @@ func main() {
var delegatedAuthority *controlPlaneAuthority
if os.Getenv("CONTROL_PLANE_SERVICE_CREDENTIAL_REF") != "" {
delegatedAuthority = &controlPlaneAuthority{baseURL: envOr("COMMIT_RUNTIME_URL", "http://commit-runtime:8082"), client: &http.Client{Timeout: 5 * time.Second}, credential: func() (string, error) {
return readCredentialRefSecretPurpose("CONTROL_PLANE_SERVICE_TOKEN", "CONTROL_PLANE_SERVICE_CREDENTIAL_REF", controlPlaneServiceTokenPath, secretcontracts.SecretPurposeServiceAuthentication)
return readControlPlaneServiceCredential()
}}
} else {
log.Printf("delegated control-plane authority is not configured; existing human control remains available")
@@ -2088,6 +2089,19 @@ func readCredentialRefSecret(name, refName, fileName string) (string, error) {
return readCredentialRefSecretPurpose(name, refName, fileName, secretcontracts.SecretPurposeDatabasePassword)
}
func readControlPlaneServiceCredential() (string, error) {
ref := os.Getenv("CONTROL_PLANE_SERVICE_CREDENTIAL_REF")
if ref == "" {
return "", errors.New("CONTROL_PLANE_SERVICE_CREDENTIAL_REF is required")
}
root := envOr("CONTROL_PLANE_SERVICE_SECRET_ROOT", "/run/control-plane-secrets")
resolver, err := platformsecrets.NewFileResolver(root)
if err != nil {
return "", err
}
return resolver.ResolveFile(secretcontracts.SecretFileRef{CredentialRef: secretcontracts.CredentialRef(ref), Purpose: secretcontracts.SecretPurposeServiceAuthentication, Consumer: secretcontracts.RuntimeConsumerCore, Path: filepath.Join(root, controlPlaneServiceTokenFile)})
}
func readCredentialRefSecretPurpose(name, refName, fileName string, purpose secretcontracts.SecretPurpose) (string, error) {
if _, directSet := os.LookupEnv(name); directSet {
return "", fmt.Errorf("%s direct environment secret is forbidden; use the standard secret file", name)
+2 -1
View File
@@ -23,7 +23,6 @@ services:
SECRET_BACKEND_ROOT: /run/secrets
volumes:
- assistant_core_secrets:/run/secrets:ro
- assistant_control_plane_service_secrets:/run/control-plane-secrets:ro
- assistant_research_sources:/var/lib/research-sources:ro
networks:
shared:
@@ -55,6 +54,7 @@ services:
CAPABILITY_POSTGRES_USER: capability_role
CAPABILITY_POSTGRES_PASSWORD: ${CAPABILITY_DB_PASSWORD}
CONTROL_PLANE_SERVICE_CREDENTIAL_REF: credential://domovoy/control-plane/service-token/v1
CONTROL_PLANE_SERVICE_SECRET_ROOT: /run/control-plane-secrets
SELF_POSTGRES_USER: self_role
SELF_POSTGRES_CREDENTIAL_REF: credential://domovoy/self/db-password/v1
EPISTEMICS_POSTGRES_USER: epistemics_role
@@ -71,6 +71,7 @@ services:
RESEARCH_RUNTIME_URL: http://research-runtime-candidate:8087
volumes:
- assistant_core_secrets:/run/secrets:ro
- assistant_control_plane_service_secrets:/run/control-plane-secrets:ro
networks:
shared:
aliases: [core-runtime-candidate]