AWS PrivateLink Architecture in HyperShift
Overview
HyperShift uses AWS PrivateLink to establish secure connectivity between worker nodes in the guest cluster VPC and the hosted control plane in the management cluster VPC. This is used when EndpointAccess is set to Private or PublicAndPrivate.
Architecture Diagram
graph TB
subgraph MC["Management Cluster (ROSA Service Account)"]
subgraph HO["hypershift-operator"]
HO_Reconciler["AWSEndpointServiceReconciler
- Watches: NodePools, HostedClusters, AWSEndpointService
- Collects SubnetIDs from NodePool.Spec.Platform.AWS.Subnet.ID
- Updates AWSEndpointService.Spec.SubnetIDs"]
end
subgraph HCP["HCP Namespace (e.g., clusters-foo)"]
subgraph CPO["control-plane-operator"]
PSO["PrivateServiceObserver (x2)
- Watches: kube-apiserver-private Service
- Watches: private-router Service
- Creates AWSEndpointService CR with NLB name"]
CPO_Reconciler["AWSEndpointServiceReconciler
- Waits for EndpointServiceName from HO
- Modifies VPC Endpoint subnets on Spec.SubnetIDs changes"]
end
API_SVC["kube-apiserver-private (Svc)
type: LoadBalancer
⚠ Non-standard path"]:::optional
ROUTER_SVC["private-router (Svc)
type: LoadBalancer"]
end
end
subgraph MGMT_AWS["Management AWS Account"]
subgraph MGMT_VPC["Management VPC"]
NLB_API["NLB (API Server)
kube-apiserver-...
⚠ Non-standard path"]:::optional
NLB_ROUTER["NLB (Router)
private-router-..."]
VPCE_SVC["VPC Endpoint Service(s)
Created by: hypershift-operator (HO)
- AcceptanceRequired: false
- AllowedPrincipals: CPO Role ARN
- Returns: EndpointServiceName"]
end
end
subgraph HC["Hosted Cluster (Customer Account)"]
subgraph GUEST_VPC["Guest VPC"]
VPCE["VPC Endpoint (Interface)
Created by: control-plane-operator (CPO)
- ServiceName: from AWSEndpointService.Status
- SubnetIDs: from AWSEndpointService.Spec
- SecurityGroup: allows 6443/443"]
R53["Route53 Private Hosted Zone
Created by: control-plane-operator (CPO)
- api.cluster.hypershift.local → VPCE DNS
- *.apps.cluster.hypershift.local → VPCE DNS"]
subgraph SUBNETS["Worker Subnets"]
SUB_A["Subnet A (AZ-1)
ENI (VPCE) + Worker Nodes"]
SUB_B["Subnet B (AZ-2)
ENI (VPCE) + Worker Nodes"]
SUB_C["Subnet C (AZ-3)
ENI (VPCE) + Worker Nodes"]
end
end
end
PSO -.-> API_SVC
PSO --> ROUTER_SVC
API_SVC -.-> NLB_API
ROUTER_SVC --> NLB_ROUTER
NLB_API -.-> VPCE_SVC
NLB_ROUTER --> VPCE_SVC
HO_Reconciler -- "Creates VPC Endpoint Service
Writes EndpointServiceName to Status" --> VPCE_SVC
VPCE_SVC -- "AWS PrivateLink" --> VPCE
CPO_Reconciler -- "Creates VPC Endpoint
Creates Route53 records
Writes EndpointID to Status" --> VPCE
VPCE --> R53
R53 --> SUBNETS
classDef optional stroke-dasharray: 5 5
Note: The
kube-apiserver-privateService and its NLB (shown with dashed lines) are only created when the API server is explicitly exposed via LoadBalancer with PrivateLink configured. This is not the standard ROSA HCP path and its use is discouraged. In the standard ROSA HCP flow, only theprivate-routerpath is used for PrivateLink connectivity.
Component Responsibilities
AWS Resources
| AWS Resource | Created By | Description |
|---|---|---|
| NLB (kube-apiserver-private) | control-plane-operator | Load balancer for API server traffic (non-standard path, not used in ROSA HCP) |
| NLB (private-router) | control-plane-operator | Load balancer for ingress router traffic |
| VPC Endpoint Service | hypershift-operator | Exposes NLBs via PrivateLink |
| VPC Endpoint (Interface) | control-plane-operator | Connects guest VPC to endpoint service |
| Security Group (for VPCE) | control-plane-operator | Allows 6443/443 from machine CIDRs |
| Route53 Private Zone records | control-plane-operator | DNS resolution for API and apps |
Kubernetes Resources
| Resource | Created By | Responsibility |
|---|---|---|
| AWSEndpointService CR | CPO (PrivateServiceObserver) | Tracks NLB name |
| AWSEndpointService.Spec.SubnetIDs | hypershift-operator | Collected from NodePools |
| AWSEndpointService.Status.EndpointServiceName | hypershift-operator | AWS VPC Endpoint Service name |
| AWSEndpointService.Status.EndpointID | control-plane-operator | AWS VPC Endpoint ID |
Data Flow
- CPO creates NLB Services -
private-routerService of type LoadBalancer (and optionallykube-apiserver-privatewhen the API server is exposed via LoadBalancer, which is not the standard ROSA HCP path) - CPO PrivateServiceObserver watches Services, creates
AWSEndpointServiceCR with NLB name - HO collects SubnetIDs from all NodePools and updates
AWSEndpointService.Spec.SubnetIDs - HO creates VPC Endpoint Service attached to NLB, writes
EndpointServiceNameto status - CPO creates VPC Endpoint in guest VPC using the endpoint service name and subnet IDs
- CPO creates Route53 records pointing to VPC Endpoint DNS
EndpointAccess Modes
| Mode | Public NLB | Private NLB | VPC Endpoint | PrivateLink |
|---|---|---|---|---|
| Public | Yes | No | No | No |
| PublicAndPrivate | Yes | Yes | Yes | Yes |
| Private | No | Yes | Yes | Yes |
Code References
- hypershift-operator AWS controller:
hypershift-operator/controllers/platform/aws/controller.go - control-plane-operator PrivateLink controller:
control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go - AWSEndpointService API types:
api/hypershift/v1beta1/endpointservice_types.go