mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
Fail provider-MSP proof when workspace entitlement leases cannot chain-verify
The install proof passed green this morning while every provisioned workspace ran unlicensed; the entitlement gap survived because nothing asserted lease health. The workspace proof now reads each workspace's provisioned billing state and verifies the lease exactly the way a release-build client runtime will: against the hosted entitlement trust root, through the provider MSP license chain, requiring white_label. A present license with an unverifiable lease fails the proof with the specific reason; the environment-fallback plan (dev, no license) reports entitlement_skipped_reason=no_provider_msp_license instead of asserting. Proof and install-proof workspace output lines gain entitlement_lease_checked/verified, entitlement_white_label, and the skip reason.
This commit is contained in:
parent
a17882458a
commit
87473aa49d
3 changed files with 132 additions and 2 deletions
|
|
@ -605,7 +605,7 @@ func printProviderMSPInstallProofReport(report *providerMSPInstallProofReport) {
|
|||
fmt.Printf("final_status_unhealthy_tenants=%d\n", report.FinalStatusUnhealthyTenants)
|
||||
fmt.Printf("final_status_stuck_provisioning_tenants=%d\n", report.FinalStatusStuckProvisioningTenants)
|
||||
for _, workspace := range report.Workspaces {
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s\n",
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s entitlement_lease_checked=%t entitlement_lease_verified=%t entitlement_white_label=%t entitlement_skipped_reason=%s\n",
|
||||
workspace.TenantID,
|
||||
workspace.DisplayName,
|
||||
workspace.State,
|
||||
|
|
@ -626,6 +626,10 @@ func printProviderMSPInstallProofReport(report *providerMSPInstallProofReport) {
|
|||
workspace.RotatedAgentReportVerified,
|
||||
workspace.HandoffExchangeVerified,
|
||||
workspace.HandoffTargetPath,
|
||||
workspace.EntitlementLeaseChecked,
|
||||
workspace.EntitlementLeaseVerified,
|
||||
workspace.EntitlementWhiteLabel,
|
||||
workspace.EntitlementSkippedReason,
|
||||
)
|
||||
}
|
||||
for _, failure := range report.Failures {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
|
||||
agentshost "github.com/rcourtman/pulse-go-rewrite/pkg/agents/host"
|
||||
internalauth "github.com/rcourtman/pulse-go-rewrite/pkg/auth"
|
||||
pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -76,6 +77,10 @@ type providerMSPProofWorkspace struct {
|
|||
RotatedAgentReportVerified bool
|
||||
HandoffExchangeVerified bool
|
||||
HandoffTargetPath string
|
||||
EntitlementLeaseChecked bool
|
||||
EntitlementLeaseVerified bool
|
||||
EntitlementWhiteLabel bool
|
||||
EntitlementSkippedReason string
|
||||
}
|
||||
|
||||
type providerMSPProofReport struct {
|
||||
|
|
@ -451,6 +456,11 @@ func (rt *providerMSPProofRuntime) proveProviderMSPWorkspace(ctx context.Context
|
|||
return providerMSPProofWorkspace{}, err
|
||||
}
|
||||
|
||||
entitlement, err := rt.verifyProviderMSPProofEntitlementLease(tenant, tenantDataDir)
|
||||
if err != nil {
|
||||
return providerMSPProofWorkspace{}, err
|
||||
}
|
||||
|
||||
return providerMSPProofWorkspace{
|
||||
TenantID: tenant.ID,
|
||||
DisplayName: tenant.DisplayName,
|
||||
|
|
@ -474,9 +484,72 @@ func (rt *providerMSPProofRuntime) proveProviderMSPWorkspace(ctx context.Context
|
|||
RotatedAgentReportVerified: rotation.RotatedAgentReportVerified,
|
||||
HandoffExchangeVerified: exchangedTargetPath == targetPath,
|
||||
HandoffTargetPath: exchangedTargetPath,
|
||||
EntitlementLeaseChecked: entitlement.Checked,
|
||||
EntitlementLeaseVerified: entitlement.Verified,
|
||||
EntitlementWhiteLabel: entitlement.WhiteLabel,
|
||||
EntitlementSkippedReason: entitlement.SkippedReason,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type providerMSPProofEntitlementLease struct {
|
||||
Checked bool
|
||||
Verified bool
|
||||
WhiteLabel bool
|
||||
SkippedReason string
|
||||
}
|
||||
|
||||
// verifyProviderMSPProofEntitlementLease verifies the provisioned workspace's
|
||||
// hosted entitlement lease exactly the way a release-build client runtime
|
||||
// will: against the embedded entitlement trust root, through the provider
|
||||
// MSP license chain. The proof previously passed while every workspace ran
|
||||
// unlicensed; a present license with an unverifiable lease must fail here,
|
||||
// not in the client's first branded report.
|
||||
func (rt *providerMSPProofRuntime) verifyProviderMSPProofEntitlementLease(tenant *registry.Tenant, tenantDataDir string) (providerMSPProofEntitlementLease, error) {
|
||||
result := providerMSPProofEntitlementLease{}
|
||||
if tenant == nil {
|
||||
return result, fmt.Errorf("tenant is required")
|
||||
}
|
||||
if !rt.cfg.IsProviderHostedMSP() || strings.TrimSpace(rt.cfg.ProviderMSPLicenseKey) == "" {
|
||||
result.SkippedReason = "no_provider_msp_license"
|
||||
return result, nil
|
||||
}
|
||||
result.Checked = true
|
||||
|
||||
state, err := runtimeconfig.NewFileBillingStore(tenantDataDir).GetBillingState("default")
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("read provisioned billing state for workspace %s: %w", tenant.ID, err)
|
||||
}
|
||||
if state == nil || strings.TrimSpace(state.EntitlementJWT) == "" {
|
||||
return result, fmt.Errorf("workspace %s was provisioned without a hosted entitlement lease", tenant.ID)
|
||||
}
|
||||
if strings.TrimSpace(state.EntitlementRefreshToken) == "" {
|
||||
return result, fmt.Errorf("workspace %s was provisioned without an entitlement refresh token", tenant.ID)
|
||||
}
|
||||
|
||||
root, err := pkglicensing.HostedEntitlementPublicKey()
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("resolve the entitlement verification root a client runtime would use: %w", err)
|
||||
}
|
||||
claims, err := pkglicensing.VerifyEntitlementLeaseToken(state.EntitlementJWT, root, "", time.Now().UTC())
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("workspace %s entitlement lease does not verify the way a client runtime would: %w", tenant.ID, err)
|
||||
}
|
||||
if strings.TrimSpace(claims.ProviderLicense) == "" {
|
||||
return result, fmt.Errorf("workspace %s entitlement lease does not chain the provider MSP license; release-build client runtimes would reject it", tenant.ID)
|
||||
}
|
||||
result.Verified = true
|
||||
for _, capability := range claims.Capabilities {
|
||||
if capability == pkglicensing.FeatureWhiteLabel {
|
||||
result.WhiteLabel = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !result.WhiteLabel {
|
||||
return result, fmt.Errorf("workspace %s entitlement lease is missing white_label; branded client reports would be locked", tenant.ID)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type providerMSPProofAgentReportIngest struct {
|
||||
Verified bool
|
||||
AgentID string
|
||||
|
|
@ -1030,7 +1103,7 @@ func printProviderMSPProofReport(report *providerMSPProofReport) {
|
|||
fmt.Printf("token_rotation_verified=%t\n", report.TokenRotationVerified)
|
||||
fmt.Printf("cleanup=%t\n", report.Cleanup)
|
||||
for _, workspace := range report.Workspaces {
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s\n",
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s entitlement_lease_checked=%t entitlement_lease_verified=%t entitlement_white_label=%t entitlement_skipped_reason=%s\n",
|
||||
workspace.TenantID,
|
||||
workspace.DisplayName,
|
||||
workspace.State,
|
||||
|
|
@ -1051,6 +1124,10 @@ func printProviderMSPProofReport(report *providerMSPProofReport) {
|
|||
workspace.RotatedAgentReportVerified,
|
||||
workspace.HandoffExchangeVerified,
|
||||
workspace.HandoffTargetPath,
|
||||
workspace.EntitlementLeaseChecked,
|
||||
workspace.EntitlementLeaseVerified,
|
||||
workspace.EntitlementWhiteLabel,
|
||||
workspace.EntitlementSkippedReason,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,15 @@ func TestProviderMSPProofExercisesWorkspaceInstallHandoffAndIsolation(t *testing
|
|||
if !workspace.HandoffExchangeVerified || workspace.HandoffTargetPath != "/settings/infrastructure?add=linux-host" {
|
||||
t.Fatalf("handoff exchange proof mismatch: %#v", workspace)
|
||||
}
|
||||
if !workspace.EntitlementLeaseChecked {
|
||||
t.Fatalf("entitlement lease was not checked for %s despite a provider MSP license being configured", workspace.TenantID)
|
||||
}
|
||||
if !workspace.EntitlementLeaseVerified {
|
||||
t.Fatalf("entitlement lease did not chain-verify for %s", workspace.TenantID)
|
||||
}
|
||||
if !workspace.EntitlementWhiteLabel {
|
||||
t.Fatalf("entitlement lease for %s is missing white_label", workspace.TenantID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,12 +259,47 @@ func TestProviderMSPProofLoadsSignedLicenseFilePlan(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// mintProviderMSPProofLicense installs a fresh license trust root via env
|
||||
// (dev builds only) and returns a root-signed MSP license binding the given
|
||||
// lease signing public key, mirroring what LoadConfig resolves from
|
||||
// CP_PROVIDER_MSP_LICENSE_FILE in a real deployment.
|
||||
func mintProviderMSPProofLicense(t *testing.T, planVersion string, leaseSigningPublicKey ed25519.PublicKey) string {
|
||||
t.Helper()
|
||||
rootPub, rootPriv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey root: %v", err)
|
||||
}
|
||||
t.Setenv("PULSE_LICENSE_PUBLIC_KEY", base64.StdEncoding.EncodeToString(rootPub))
|
||||
t.Setenv("PULSE_TRIAL_ACTIVATION_PUBLIC_KEY", base64.StdEncoding.EncodeToString(rootPub))
|
||||
t.Cleanup(func() { pkglicensing.SetPublicKey(nil) })
|
||||
pkglicensing.InitEmbeddedPublicKey()
|
||||
|
||||
claims := pkglicensing.Claims{
|
||||
LicenseID: "lic_provider_msp_test",
|
||||
Email: "provider@example.com",
|
||||
Tier: pkglicensing.TierMSP,
|
||||
IssuedAt: time.Now().Add(-time.Minute).Unix(),
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
|
||||
PlanVersion: planVersion,
|
||||
EntitlementSigningPublicKey: base64.StdEncoding.EncodeToString(leaseSigningPublicKey),
|
||||
}
|
||||
payloadBytes, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal claims: %v", err)
|
||||
}
|
||||
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"EdDSA","typ":"JWT"}`))
|
||||
payload := base64.RawURLEncoding.EncodeToString(payloadBytes)
|
||||
signature := base64.RawURLEncoding.EncodeToString(ed25519.Sign(rootPriv, []byte(header+"."+payload)))
|
||||
return header + "." + payload + "." + signature
|
||||
}
|
||||
|
||||
func testProviderMSPProofConfig(t *testing.T) *cloudcp.CPConfig {
|
||||
t.Helper()
|
||||
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey: %v", err)
|
||||
}
|
||||
licenseKey := mintProviderMSPProofLicense(t, "msp_growth", publicKey)
|
||||
return &cloudcp.CPConfig{
|
||||
DataDir: t.TempDir(),
|
||||
Environment: "development",
|
||||
|
|
@ -273,6 +317,7 @@ func testProviderMSPProofConfig(t *testing.T) *cloudcp.CPConfig {
|
|||
ProviderMSPPlanSource: cloudcp.ProviderMSPPlanSourceLicenseFile,
|
||||
ProviderMSPLicenseID: "lic_provider_msp_test",
|
||||
ProviderMSPLicenseEmail: "provider@example.com",
|
||||
ProviderMSPLicenseKey: licenseKey,
|
||||
TrialActivationPrivateKey: base64.StdEncoding.EncodeToString(privateKey),
|
||||
TrialActivationPublicKey: base64.StdEncoding.EncodeToString(publicKey),
|
||||
EmailFrom: "noreply@example.com",
|
||||
|
|
@ -309,6 +354,10 @@ func writeProviderMSPProofLicenseForTest(t *testing.T, licenseID, email, planVer
|
|||
t.Fatalf("GenerateKey: %v", err)
|
||||
}
|
||||
t.Setenv("PULSE_LICENSE_PUBLIC_KEY", base64.StdEncoding.EncodeToString(publicKey))
|
||||
// Release binaries embed one key as both the license root and the hosted
|
||||
// entitlement verification root; mirror that so the proof's lease
|
||||
// verification can resolve a root in dev test builds.
|
||||
t.Setenv("PULSE_TRIAL_ACTIVATION_PUBLIC_KEY", base64.StdEncoding.EncodeToString(publicKey))
|
||||
t.Setenv("PULSE_LICENSE_DEV_MODE", "false")
|
||||
t.Cleanup(func() { pkglicensing.SetPublicKey(nil) })
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue