deploy(apple-mdm): route scep to noc1 ca
Adds the GX10 /scep route to the noc1 Apple MDM SCEP CA without exposing NanoHUB APIs.
This commit is contained in:
@@ -13,6 +13,9 @@ traffic at `https://mdm.iamworkin.lan`.
|
|||||||
- Required secret: `Secret/fc-apple-mdm-runtime`, key `NANOHUB_API_KEY`
|
- Required secret: `Secret/fc-apple-mdm-runtime`, key `NANOHUB_API_KEY`
|
||||||
- Optional later bridge secret: `NANOHUB_WEBHOOK_URL`
|
- Optional later bridge secret: `NANOHUB_WEBHOOK_URL`
|
||||||
- Required CA mount: `ConfigMap/fc-apple-mdm-root-ca`, key `root_ca.crt`
|
- Required CA mount: `ConfigMap/fc-apple-mdm-root-ca`, key `root_ca.crt`
|
||||||
|
- SCEP backend: noc1 systemd service `step-ca-apple-mdm-scep`, forwarded through
|
||||||
|
selectorless `Service/fc-apple-mdm-scep` and `EndpointSlice/fc-apple-mdm-scep-noc1`
|
||||||
|
to `10.0.56.10:9080`
|
||||||
|
|
||||||
NanoHUB API authentication is HTTP Basic with username `nanohub` and password
|
NanoHUB API authentication is HTTP Basic with username `nanohub` and password
|
||||||
from `NANOHUB_API_KEY`.
|
from `NANOHUB_API_KEY`.
|
||||||
@@ -24,16 +27,21 @@ The Traefik route intentionally exposes only:
|
|||||||
- `/version`
|
- `/version`
|
||||||
- `/mdm`
|
- `/mdm`
|
||||||
- `/checkin`
|
- `/checkin`
|
||||||
|
- `/scep`
|
||||||
|
|
||||||
NanoHUB APIs under `/api/v1/*` stay cluster-internal for MDM-N1. The
|
NanoHUB APIs under `/api/v1/*` stay cluster-internal for MDM-N1. The
|
||||||
DeviceManagement bridge can use the ClusterIP service directly once its NanoHUB
|
DeviceManagement bridge can use the ClusterIP service directly once its NanoHUB
|
||||||
client lane lands.
|
client lane lands.
|
||||||
|
|
||||||
SCEP is intentionally not exposed here yet. NanoHUB/NanoMDM expects an external
|
SCEP is backed by the dedicated Apple-MDM-specific RSA step-ca hierarchy on
|
||||||
SCEP service; the next runtime lane should either add a dedicated SCEP route
|
noc1, not by the IAmWorkin ACME CA. The live profile URL is:
|
||||||
such as `https://mdm.iamworkin.lan/scep/...` backed by an Apple-MDM-specific CA,
|
|
||||||
or set `APPLE_MDM_SCEP_URL` in the DeviceManagement runtime secret to another
|
```text
|
||||||
live SCEP endpoint. Do not point the profile at a placeholder URL.
|
https://mdm.iamworkin.lan/scep/apple-mdm-scep
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not point `APPLE_MDM_SCEP_URL` at a placeholder URL or at the ECDSA
|
||||||
|
IAmWorkin ACME CA; Smallstep SCEP requires an RSA intermediate/decrypter path.
|
||||||
|
|
||||||
## Deployment Notes
|
## Deployment Notes
|
||||||
|
|
||||||
@@ -45,7 +53,9 @@ live SCEP endpoint. Do not point the profile at a placeholder URL.
|
|||||||
3. Ensure `mdm.iamworkin.lan` resolves to the GX10 Traefik VIP `10.0.57.202`
|
3. Ensure `mdm.iamworkin.lan` resolves to the GX10 Traefik VIP `10.0.57.202`
|
||||||
before cert-manager requests `Certificate/fc-apple-mdm-tls`.
|
before cert-manager requests `Certificate/fc-apple-mdm-tls`.
|
||||||
4. Prove `https://mdm.iamworkin.lan/version` after ArgoCD converges.
|
4. Prove `https://mdm.iamworkin.lan/version` after ArgoCD converges.
|
||||||
|
5. Prove SCEP CA publication with
|
||||||
|
`curl -sk -o /dev/null -w '%{http_code} %{size_download}\n' 'https://mdm.iamworkin.lan/scep/apple-mdm-scep?operation=GetCACert'`.
|
||||||
|
|
||||||
This lane does not create an APNs MDM push certificate, enrollment profile,
|
This lane does not create an APNs MDM push certificate, enrollment profile,
|
||||||
SCEP/device identity service, managed Wi-Fi payload, managed app install, or
|
managed Wi-Fi payload, managed app install, or supervised iPad enrollment. Those
|
||||||
supervised iPad enrollment. Those remain MDM-N2 through MDM-N8.
|
remain MDM-N2 through MDM-N8.
|
||||||
|
|||||||
@@ -192,6 +192,43 @@ spec:
|
|||||||
targetPort: 9004
|
targetPort: 9004
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
---
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: fc-apple-mdm-scep
|
||||||
|
namespace: fc-apple-mdm
|
||||||
|
labels:
|
||||||
|
app: fc-apple-mdm-scep
|
||||||
|
app.kubernetes.io/name: fc-apple-mdm-scep
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 9080
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
|
apiVersion: discovery.k8s.io/v1
|
||||||
|
kind: EndpointSlice
|
||||||
|
metadata:
|
||||||
|
name: fc-apple-mdm-scep-noc1
|
||||||
|
namespace: fc-apple-mdm
|
||||||
|
labels:
|
||||||
|
kubernetes.io/service-name: fc-apple-mdm-scep
|
||||||
|
app.kubernetes.io/name: fc-apple-mdm-scep
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
addressType: IPv4
|
||||||
|
endpoints:
|
||||||
|
- addresses:
|
||||||
|
- 10.0.56.10
|
||||||
|
conditions:
|
||||||
|
ready: true
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 9080
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
apiVersion: cert-manager.io/v1
|
apiVersion: cert-manager.io/v1
|
||||||
kind: Certificate
|
kind: Certificate
|
||||||
metadata:
|
metadata:
|
||||||
@@ -218,6 +255,11 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
|
- match: Host(`mdm.iamworkin.lan`) && PathPrefix(`/scep`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: fc-apple-mdm-scep
|
||||||
|
port: 80
|
||||||
- match: Host(`mdm.iamworkin.lan`) && (PathPrefix(`/mdm`) || PathPrefix(`/checkin`) || PathPrefix(`/version`))
|
- match: Host(`mdm.iamworkin.lan`) && (PathPrefix(`/mdm`) || PathPrefix(`/checkin`) || PathPrefix(`/version`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ public sealed class Gx10AppleMdmNanohubTests
|
|||||||
Documents.Should().Contain(document => Is(document, "Namespace", "fc-apple-mdm"));
|
Documents.Should().Contain(document => Is(document, "Namespace", "fc-apple-mdm"));
|
||||||
Documents.Should().Contain(document => Is(document, "ConfigMap", "fc-apple-mdm-root-ca"));
|
Documents.Should().Contain(document => Is(document, "ConfigMap", "fc-apple-mdm-root-ca"));
|
||||||
Documents.Should().Contain(document => Is(document, "Service", "fc-apple-mdm"));
|
Documents.Should().Contain(document => Is(document, "Service", "fc-apple-mdm"));
|
||||||
|
Documents.Should().Contain(document => Is(document, "Service", "fc-apple-mdm-scep"));
|
||||||
|
Documents.Should().Contain(document => Is(document, "EndpointSlice", "fc-apple-mdm-scep-noc1"));
|
||||||
Documents.Should().Contain(document => Is(document, "NetworkPolicy", "fc-apple-mdm-netpol"));
|
Documents.Should().Contain(document => Is(document, "NetworkPolicy", "fc-apple-mdm-netpol"));
|
||||||
Documents.Should().NotContain(document => (document.Scalar("kind") ?? string.Empty) == "Secret");
|
Documents.Should().NotContain(document => (document.Scalar("kind") ?? string.Empty) == "Secret");
|
||||||
Documents.Should().NotContain(document => (document.Scalar("kind") ?? string.Empty) == "OnePasswordItem");
|
Documents.Should().NotContain(document => (document.Scalar("kind") ?? string.Empty) == "OnePasswordItem");
|
||||||
@@ -66,16 +68,35 @@ public sealed class Gx10AppleMdmNanohubTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Manifest_ExposesOnlyMdmCheckinAndVersionPaths()
|
public void Manifest_ExposesOnlyMdmCheckinVersionAndScepPaths()
|
||||||
{
|
{
|
||||||
var certificate = Single("Certificate", "fc-apple-mdm-tls");
|
var certificate = Single("Certificate", "fc-apple-mdm-tls");
|
||||||
certificate.Scalar("spec", "issuerRef", "name").Should().Be("step-ca-acme");
|
certificate.Scalar("spec", "issuerRef", "name").Should().Be("step-ca-acme");
|
||||||
certificate.Scalar("spec", "issuerRef", "kind").Should().Be("ClusterIssuer");
|
certificate.Scalar("spec", "issuerRef", "kind").Should().Be("ClusterIssuer");
|
||||||
certificate.ScalarSequence("spec", "dnsNames").Should().ContainSingle("mdm.iamworkin.lan");
|
certificate.ScalarSequence("spec", "dnsNames").Should().ContainSingle("mdm.iamworkin.lan");
|
||||||
|
|
||||||
|
var scepService = Single("Service", "fc-apple-mdm-scep");
|
||||||
|
scepService.Scalar("spec", "type").Should().Be("ClusterIP");
|
||||||
|
var scepServicePort = scepService.MappingSequence("spec", "ports").Should().ContainSingle().Subject;
|
||||||
|
scepServicePort.Scalar("name").Should().Be("http");
|
||||||
|
scepServicePort.Scalar("port").Should().Be("80");
|
||||||
|
scepServicePort.Scalar("targetPort").Should().Be("9080");
|
||||||
|
|
||||||
|
var scepEndpointSlice = Single("EndpointSlice", "fc-apple-mdm-scep-noc1");
|
||||||
|
scepEndpointSlice.Scalar("addressType").Should().Be("IPv4");
|
||||||
|
scepEndpointSlice.Scalar("metadata", "labels", "kubernetes.io/service-name").Should().Be("fc-apple-mdm-scep");
|
||||||
|
var scepEndpoint = scepEndpointSlice.MappingSequence("endpoints").Should().ContainSingle().Subject;
|
||||||
|
scepEndpoint.ScalarSequence("addresses").Should().ContainSingle("10.0.56.10");
|
||||||
|
var scepEndpointPort = scepEndpointSlice.MappingSequence("ports").Should().ContainSingle().Subject;
|
||||||
|
scepEndpointPort.Scalar("name").Should().Be("http");
|
||||||
|
scepEndpointPort.Scalar("port").Should().Be("9080");
|
||||||
|
|
||||||
var ingress = Single("IngressRoute", "fc-apple-mdm");
|
var ingress = Single("IngressRoute", "fc-apple-mdm");
|
||||||
var route = ingress.MappingSequence("spec", "routes").Should().ContainSingle().Subject;
|
var routes = ingress.MappingSequence("spec", "routes");
|
||||||
var match = route.Scalar("match");
|
routes.Should().HaveCount(2);
|
||||||
|
var scepRoute = routes.Single(route => route.Scalar("match")?.Contains("PathPrefix(`/scep`)") == true);
|
||||||
|
var nanohubRoute = routes.Single(route => route.Scalar("match")?.Contains("PathPrefix(`/mdm`)") == true);
|
||||||
|
var match = nanohubRoute.Scalar("match");
|
||||||
|
|
||||||
match.Should().Contain("Host(`mdm.iamworkin.lan`)");
|
match.Should().Contain("Host(`mdm.iamworkin.lan`)");
|
||||||
match.Should().Contain("PathPrefix(`/mdm`)");
|
match.Should().Contain("PathPrefix(`/mdm`)");
|
||||||
@@ -83,6 +104,12 @@ public sealed class Gx10AppleMdmNanohubTests
|
|||||||
match.Should().Contain("PathPrefix(`/version`)");
|
match.Should().Contain("PathPrefix(`/version`)");
|
||||||
match.Should().NotContain("/api/v1");
|
match.Should().NotContain("/api/v1");
|
||||||
match.Should().NotContain("PathPrefix(`/api`)");
|
match.Should().NotContain("PathPrefix(`/api`)");
|
||||||
|
|
||||||
|
scepRoute.Scalar("match").Should().Contain("Host(`mdm.iamworkin.lan`)");
|
||||||
|
scepRoute.Scalar("match").Should().Contain("PathPrefix(`/scep`)");
|
||||||
|
var scepRouteService = scepRoute.MappingSequence("services").Should().ContainSingle().Subject;
|
||||||
|
scepRouteService.Scalar("name").Should().Be("fc-apple-mdm-scep");
|
||||||
|
scepRouteService.Scalar("port").Should().Be("80");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -94,6 +121,8 @@ public sealed class Gx10AppleMdmNanohubTests
|
|||||||
readme.Should().Contain("Secret/fc-apple-mdm-runtime");
|
readme.Should().Contain("Secret/fc-apple-mdm-runtime");
|
||||||
readme.Should().Contain("imagePullPolicy: Never");
|
readme.Should().Contain("imagePullPolicy: Never");
|
||||||
readme.Should().Contain("10.0.57.202");
|
readme.Should().Contain("10.0.57.202");
|
||||||
|
readme.Should().Contain("https://mdm.iamworkin.lan/scep/apple-mdm-scep");
|
||||||
|
readme.Should().Contain("Smallstep SCEP requires an RSA intermediate");
|
||||||
readme.Should().Contain("does not create an APNs MDM push certificate");
|
readme.Should().Contain("does not create an APNs MDM push certificate");
|
||||||
readme.Should().Contain("managed Wi-Fi payload");
|
readme.Should().Contain("managed Wi-Fi payload");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user