M2 §2: run actions and manifest fetch over mTLS with Maestro's identity

Extract maestroTransport() (Maestro's identity cert + instance CA) and
expose PeerTransport()/Scheme()/InsecureInternal() so every service peer
edge uses the same client. RunAction now targets
https://{ip}:{port}/action/{name} presenting Maestro's cert (services
require a CA-issued client cert, so only Maestro can drive actions — T4)
and FetchManifestWithRetry fetches /_manifest the same way.
INSECURE_INTERNAL=1 keeps the dev fleet plaintext (debug compose updated
to set it).
This commit is contained in:
2026-08-16 01:12:01 +01:00
parent 77bd21aea2
commit 92f4787146
4 changed files with 80 additions and 21 deletions
+54 -16
View File
@@ -90,6 +90,29 @@ type authzRequest struct {
// New builds the mTLS management client, provisioning Maestro's own identity
// from the instance CA (the CA holder issues its own orchestrator cert).
func New() (*Client, error) {
transport, err := maestroTransport()
if err != nil {
return nil, err
}
baseURL := os.Getenv("AUTH_SERVICE_URL")
if baseURL == "" {
baseURL = AuthServiceURL
}
return &Client{
baseURL: baseURL,
http: &http.Client{
Timeout: 10 * time.Second,
Transport: transport,
},
}, nil
}
// maestroTransport builds the mutual-TLS transport Maestro uses as a peer: its
// own identity certificate (CN=maestro) signed by the instance CA, trusting the
// CA as root. This is the client side of every Maestro peer edge.
func maestroTransport() (*http.Transport, error) {
dataRoot, err := dataRoot()
if err != nil {
return nil, fmt.Errorf("resolve data root: %w", err)
@@ -112,26 +135,41 @@ func New() (*Client, error) {
return nil, fmt.Errorf("no certificates parsed from instance CA")
}
baseURL := os.Getenv("AUTH_SERVICE_URL")
if baseURL == "" {
baseURL = AuthServiceURL
}
return &Client{
baseURL: baseURL,
http: &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: pool,
MinVersion: tls.VersionTLS12,
},
},
return &http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: pool,
MinVersion: tls.VersionTLS12,
},
}, nil
}
// InsecureInternal reports whether the dev escape hatch is enabled
// (INSECURE_INTERNAL=1): peer edges run plaintext, mirroring the services'
// listeners in the dev compose files.
func InsecureInternal() bool {
return os.Getenv("INSECURE_INTERNAL") == "1"
}
// Scheme returns "http" under the dev escape hatch and "https" otherwise, for
// building service peer URLs (RunAction, manifest fetch).
func Scheme() string {
if InsecureInternal() {
return "http"
}
return "https"
}
// PeerTransport returns the transport Maestro uses for the service peer edges
// (M2 §2). Normally mutual TLS with Maestro's identity; a plaintext transport
// under INSECURE_INTERNAL=1 so the dev fleet keeps working mid-migration.
func PeerTransport() (*http.Transport, error) {
if InsecureInternal() {
return &http.Transport{}, nil
}
return maestroTransport()
}
// TrustState returns the pending authorizations and the approved trust store.
func (c *Client) TrustState(ctx context.Context) (*TrustState, error) {
var state TrustState