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:
+54
-16
@@ -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
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.dcntrlized.org/Dcntrlized/Maestro/lib/authmgmt"
|
||||
"git.dcntrlized.org/Dcntrlized/Maestro/lib/logger"
|
||||
"git.dcntrlized.org/Dcntrlized/Maestro/lib/types"
|
||||
|
||||
@@ -92,12 +93,18 @@ func ListContainers(dc *DockerClient) ([]string, error) {
|
||||
// fetchManifestWithRetry polls the container's manifest endpoint until it succeeds or max retries are reached.
|
||||
// Could probably be simplified to (ctx, container) with maxRetries and retryDelay hardcoded, low priority TODO
|
||||
func FetchManifestWithRetry(ctx context.Context, host, port string, maxRetries int, retryDelay time.Duration) (*types.Manifest, error) {
|
||||
url := fmt.Sprintf("http://%s:%s/_manifest", host, port)
|
||||
// The service peer edge is mutual TLS (M2 §2), same as RunAction: Maestro's
|
||||
// identity cert, https scheme, with the plaintext dev escape hatch.
|
||||
url := fmt.Sprintf("%s://%s:%s/_manifest", authmgmt.Scheme(), host, port)
|
||||
var resp *http.Response
|
||||
var err error
|
||||
|
||||
// Create a client that respects context deadlines/cancellation
|
||||
client := &http.Client{Timeout: 2 * time.Second}
|
||||
transport, terr := authmgmt.PeerTransport()
|
||||
if terr != nil {
|
||||
return nil, fmt.Errorf("build peer transport: %w", terr)
|
||||
}
|
||||
client := &http.Client{Timeout: 2 * time.Second, Transport: transport}
|
||||
|
||||
for range maxRetries {
|
||||
req, reqErr := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
|
||||
Reference in New Issue
Block a user