secure gx10 device management writes
This commit is contained in:
@@ -13,6 +13,8 @@ values to clear readiness checks.
|
||||
|
||||
| Key | Purpose |
|
||||
| --- | --- |
|
||||
| `DEVICE_MANAGEMENT_OPERATOR_API_KEY` | Required operator API key for authenticated REST/MCP write operations, including Android command queueing. |
|
||||
| `DEVICE_MANAGEMENT_ADMIN_API_KEY` | Required admin API key for privileged DeviceManagement operations. |
|
||||
| `NANOHUB_API_KEY` | NanoHUB API password for HTTP Basic user `nanohub`. |
|
||||
| `APPLE_MDM_APNS_TOPIC` | MDM APNs topic returned after uploading the Apple MDM push certificate to NanoHUB/NanoMDM. |
|
||||
| `APPLE_MDM_SCEP_URL` | Live SCEP URL included in the enrollment profile. |
|
||||
@@ -27,6 +29,13 @@ Non-secret profile constants stay in GitOps: NanoHUB base URL, MDM server URL,
|
||||
check-in URL, organization/display names, the HTTPS trust anchor certificate,
|
||||
managed Wi-Fi encryption type, auto-join, and MAC-randomization disablement.
|
||||
|
||||
DeviceManagement auth is enabled on GX10. The deployment maps
|
||||
`DEVICE_MANAGEMENT_OPERATOR_API_KEY` to both `Auth__ApiKey` and
|
||||
`FlowerCore__Auth__ApiKey`; the unprefixed key keeps the MCP API key post-config
|
||||
path aligned with REST auth. Agent enrollment, heartbeat, inventory, command poll,
|
||||
and command-result callbacks remain on the unauthenticated agent channel by
|
||||
application policy; operator write endpoints must use `X-Api-Key`.
|
||||
|
||||
## Readiness Check
|
||||
|
||||
After changing the runtime secret and letting the pod roll, verify:
|
||||
|
||||
@@ -88,15 +88,55 @@
|
||||
"name": "FlowerCore__Database__ConnectionStrings__Sqlite",
|
||||
"value": "Data Source=/data/devicemgmt.db"
|
||||
},
|
||||
{
|
||||
"name": "FlowerCore__Database__Password",
|
||||
"valueFrom": {
|
||||
"secretKeyRef": {
|
||||
"key": "DB-Password",
|
||||
"name": "fc-devicemgmt-runtime"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "FlowerCore__Database__Password",
|
||||
"valueFrom": {
|
||||
"secretKeyRef": {
|
||||
"key": "DB-Password",
|
||||
"name": "fc-devicemgmt-runtime"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "FlowerCore__Auth__Enabled",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "Auth__ApiKey",
|
||||
"valueFrom": {
|
||||
"secretKeyRef": {
|
||||
"key": "DEVICE_MANAGEMENT_OPERATOR_API_KEY",
|
||||
"name": "fc-devicemgmt-runtime"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "FlowerCore__Auth__ApiKey",
|
||||
"valueFrom": {
|
||||
"secretKeyRef": {
|
||||
"key": "DEVICE_MANAGEMENT_OPERATOR_API_KEY",
|
||||
"name": "fc-devicemgmt-runtime"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Auth__AdminApiKey",
|
||||
"valueFrom": {
|
||||
"secretKeyRef": {
|
||||
"key": "DEVICE_MANAGEMENT_ADMIN_API_KEY",
|
||||
"name": "fc-devicemgmt-runtime"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "FlowerCore__Auth__AdminApiKey",
|
||||
"valueFrom": {
|
||||
"secretKeyRef": {
|
||||
"key": "DEVICE_MANAGEMENT_ADMIN_API_KEY",
|
||||
"name": "fc-devicemgmt-runtime"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "FlowerCore__EventBus__Redis__Configuration",
|
||||
"value": "redis.fc-redis.svc:6379"
|
||||
|
||||
@@ -999,6 +999,26 @@ public sealed class FleetManifestLintTests
|
||||
gatewayManifest.Should().Contain("port: 5400");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Gx10DeviceManagementWriteApis_RequireRuntimeBackedOperatorAuth()
|
||||
{
|
||||
var web = Gx10DeploymentContainer("fc-devicemgmt", "deployment-fc-devicemgmt-web.json");
|
||||
|
||||
JsonEnvValue(web, "FlowerCore__Auth__Enabled").Should().Be("true");
|
||||
JsonEnvSecretName(web, "Auth__ApiKey").Should().Be("fc-devicemgmt-runtime");
|
||||
JsonEnvSecretKey(web, "Auth__ApiKey").Should().Be("DEVICE_MANAGEMENT_OPERATOR_API_KEY");
|
||||
JsonEnvSecretOptional(web, "Auth__ApiKey").Should().BeNull();
|
||||
JsonEnvSecretName(web, "FlowerCore__Auth__ApiKey").Should().Be("fc-devicemgmt-runtime");
|
||||
JsonEnvSecretKey(web, "FlowerCore__Auth__ApiKey").Should().Be("DEVICE_MANAGEMENT_OPERATOR_API_KEY");
|
||||
JsonEnvSecretOptional(web, "FlowerCore__Auth__ApiKey").Should().BeNull();
|
||||
JsonEnvSecretName(web, "Auth__AdminApiKey").Should().Be("fc-devicemgmt-runtime");
|
||||
JsonEnvSecretKey(web, "Auth__AdminApiKey").Should().Be("DEVICE_MANAGEMENT_ADMIN_API_KEY");
|
||||
JsonEnvSecretOptional(web, "Auth__AdminApiKey").Should().BeNull();
|
||||
JsonEnvSecretName(web, "FlowerCore__Auth__AdminApiKey").Should().Be("fc-devicemgmt-runtime");
|
||||
JsonEnvSecretKey(web, "FlowerCore__Auth__AdminApiKey").Should().Be("DEVICE_MANAGEMENT_ADMIN_API_KEY");
|
||||
JsonEnvSecretOptional(web, "FlowerCore__Auth__AdminApiKey").Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Gx10PhpTenantRoutes_HaveEdgeControlSubstrate()
|
||||
{
|
||||
@@ -1448,9 +1468,13 @@ public sealed class FleetManifestLintTests
|
||||
|
||||
private static bool? JsonEnvSecretOptional(JsonElement container, string name)
|
||||
{
|
||||
return JsonEnvMapping(container, name) is { } env
|
||||
? env.GetProperty("valueFrom").GetProperty("secretKeyRef").GetProperty("optional").GetBoolean()
|
||||
: null;
|
||||
if (JsonEnvMapping(container, name) is not { } env)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var secretKeyRef = env.GetProperty("valueFrom").GetProperty("secretKeyRef");
|
||||
return secretKeyRef.TryGetProperty("optional", out var optional) ? optional.GetBoolean() : null;
|
||||
}
|
||||
|
||||
private static string? JsonEnvValue(JsonElement container, string name)
|
||||
|
||||
Reference in New Issue
Block a user