Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ render\:all:
observed=$${entry#*::}; \
outfile="$$tmpdir/$$(echo $$entry | tr '/:' '__')"; \
( \
set -o pipefail; \
if [ -n "$$observed" ]; then \
echo "=== Rendering $$example with observed-resources $$observed ==="; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example --observed-resources=$$observed; \
Expand Down Expand Up @@ -75,16 +76,17 @@ validate\:all: generate-configuration
observed=$${entry#*::}; \
outfile="$$tmpdir/$$(echo $$entry | tr '/:' '__')"; \
( \
set -o pipefail; \
if [ -n "$$observed" ]; then \
echo "=== Validating $$example with observed-resources $$observed ==="; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example \
--observed-resources=$$observed --include-full-xr --quiet | \
crossplane beta validate $(XRD_DIR) --error-on-missing-schemas -; \
crossplane resource validate $(XRD_DIR) --error-on-missing-schemas -; \
else \
echo "=== Validating $$example ==="; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example \
--include-full-xr --quiet | \
crossplane beta validate $(XRD_DIR) --error-on-missing-schemas -; \
crossplane resource validate $(XRD_DIR) --error-on-missing-schemas -; \
fi; \
echo "" \
) > "$$outfile" 2>&1 & \
Expand Down Expand Up @@ -121,9 +123,10 @@ validate\:%: generate-configuration
@example="examples/networks/$*.yaml"; \
if [ -f "$$example" ]; then \
echo "=== Validating $$example ==="; \
set -o pipefail; \
up composition render --xrd=$(DEFINITION) $(COMPOSITION) $$example \
--include-full-xr --quiet | \
crossplane beta validate $(XRD_DIR) --error-on-missing-schemas -; \
crossplane resource validate $(XRD_DIR) --error-on-missing-schemas -; \
Comment thread
patrickleet marked this conversation as resolved.
else \
echo "Example $$example not found"; \
exit 1; \
Expand Down
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,24 @@ spec:
enabled: true
poolId: ipam-pool-0123456789abcdef0
netmaskLength: 16
ipv6Ula:
ipv6Gua:
enabled: true
poolId: ipam-pool-0fedcba9876543210
netmaskLength: 56
resourcePlanning:
# The IPAM administrator creates pools sourced from this workload VPC.
providerConfigRef:
name: network
ipv4:
enabled: true
scopeId: ipam-scope-private0123456789
# externalName: ipam-pool-existing-ipv4-planning
ipv6:
enabled: true
scopeId: ipam-scope-public0123456789
# externalName: ipam-pool-existing-ipv6-planning
subnetLayout:
ipv6NetmaskLength: 64
availabilityZones: [a, b, c]
public:
enabled: true
Expand Down Expand Up @@ -93,6 +106,15 @@ This saves ~$32/mo. Add NAT later if you need IPv4 egress to external services.
- **No conflicts** - IPAM prevents overlapping ranges across VPCs
- **Multi-account ready** - Share pools via RAM when you scale

For Amazon-provided IPv6 GUA, use a Regional IPAM pool (for example `/52`) to
allocate a `/56` to each VPC. Subnets then allocate `/64`s from a VPC
resource-planning pool whose source resource is that VPC.

When the planning pools and workload VPC are in different AWS accounts, share
the planning pools with the workload account or its OU through AWS RAM. The
Foundation `ramShares` API can manage this after the Network status exposes the
planning pool IDs.

### IPv6 Benefits
- **EKS Auto Mode** - IPv6 prevents IP exhaustion when scaling
- **Future-proof** - Native dual-stack from day one
Expand Down Expand Up @@ -445,11 +467,26 @@ spec:
| `ipv6Ula.enabled` | boolean | Enable IPv6 ULA CIDR allocation from IPAM |
| `ipv6Ula.poolId` | string | IPAM pool ID for IPv6 |
| `ipv6Ula.netmaskLength` | int | VPC IPv6 netmask (default: 56) |
| `ipv6Gua.enabled` | boolean | Enable IPv6 GUA allocation from a regional IPAM pool |
| `ipv6Gua.poolId` | string | Regional Amazon-provided or BYOIP IPv6 IPAM pool ID |
| `ipv6Gua.netmaskLength` | int | VPC IPv6 netmask (default: 56) |
| `resourcePlanning.providerConfigRef` | object | ProviderConfig for the IPAM administrator account |
| `resourcePlanning.managementPolicies` | []string | Management operations for planning pools |
| `resourcePlanning.ipv4.enabled` | boolean | Create an IPv4 planning pool sourced from the VPC |
| `resourcePlanning.ipv4.externalName` | string | Existing IPv4 planning pool ID to import |
| `resourcePlanning.ipv4.scopeId` | string | Private IPAM scope containing the IPv4 regional pool |
| `resourcePlanning.ipv6.enabled` | boolean | Create an IPv6 planning pool sourced from the VPC |
| `resourcePlanning.ipv6.externalName` | string | Existing IPv6 planning pool ID to import |
| `resourcePlanning.ipv6.publicIpSource` | string | IPv6 public address source (`amazon` or `byoip`) |
| `resourcePlanning.ipv6.scopeId` | string | IPAM scope containing the IPv6 regional pool |
Comment thread
patrickleet marked this conversation as resolved.

### spec.subnetLayout

| Field | Type | Description |
|-------|------|-------------|
| `ipv4PoolId` | string | IPv4 VPC resource-planning IPAM pool ID |
| `ipv6PoolId` | string | IPv6 VPC resource-planning IPAM pool ID |
| `ipv6NetmaskLength` | int | IPv6 subnet netmask (default: 64) |
| `availabilityZones` | []string | AZs for subnet creation (default: [a, b, c]) |
| `public.enabled` | boolean | Create public subnets (default: true) |
| `public.netmaskLength` | int | Public subnet netmask (default: 24) |
Expand Down Expand Up @@ -493,13 +530,19 @@ status:
cidr: "10.100.0.0/16"
ipv6Ula:
cidr: "fd00:dead:beef::/56"
ipv6Gua:
cidr: "2600:1f18:abc::/56"
resourcePlanning:
ipv4PoolId: ipam-pool-vpc-ipv4-abc123
ipv6PoolId: ipam-pool-vpc-ipv6-def456
network:
name: my-network
region: us-east-1
vpcId: vpc-abc123
cidr:
ipv4: "10.100.0.0/16"
ipv6Ula: "fd00:dead:beef::/56"
ipv6Gua: "2600:1f18:abc::/56"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
ipv6AmazonProvided: "2600:1f18:abc::/56" # If using Amazon-provided
availabilityZones:
- us-east-1a
Expand Down
129 changes: 124 additions & 5 deletions apis/networks/definition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ spec:
description: Kind of the provider config reference.
default: ProviderConfig

# IPAM - allocate VPC CIDR from IPAM pool, subnets calculated via cidrmath
# IPAM - allocate VPC CIDRs from regional IPAM pools
ipam:
type: object
description: |
Enable automatic VPC CIDR allocation from an IPAM pool.
Subnet CIDRs are calculated from the VPC CIDR using cidrmath.
Enable automatic VPC CIDR allocation from regional IPAM pools.
Subnet allocation is configured separately through subnetLayout.
properties:
ipv4:
type: object
Expand Down Expand Up @@ -100,14 +100,116 @@ spec:
type: integer
description: Netmask length for VPC IPv6 CIDR. AWS requires /56.
default: 56
ipv6Gua:
type: object
description: IPv6 global unicast address (GUA) VPC allocation from IPAM.
x-kubernetes-validations:
- rule: "!has(self.enabled) || !self.enabled || (has(self.poolId) && self.poolId != '')"
message: poolId is required when ipv6Gua.enabled is true.
properties:
enabled:
type: boolean
description: Enable IPv6 GUA CIDR allocation from a regional IPAM pool.
default: false
poolId:
type: string
description: Regional Amazon-provided or BYOIP IPv6 IPAM pool ID.
netmaskLength:
type: integer
description: Netmask length allocated to the VPC (typically /56).
default: 56
Comment thread
patrickleet marked this conversation as resolved.
resourcePlanning:
type: object
description: |
Create VPC resource-planning pools in the IPAM administrator
account and allocate layout subnets from them. The VPC must
receive its address space from the corresponding regional pool.
properties:
providerConfigRef:
type: object
description: AWS ProviderConfig for the IPAM administrator account.
properties:
name:
type: string
description: Name of the IPAM administrator ProviderConfig.
default: default
kind:
type: string
description: Kind of the provider config reference.
default: ProviderConfig
managementPolicies:
type: array
description: Management operations for resource-planning pools.
items:
type: string
enum:
- "*"
- Create
- Observe
- Update
- Delete
- LateInitialize
ipv4:
type: object
description: IPv4 VPC resource-planning pool configuration.
properties:
enabled:
type: boolean
description: Create an IPv4 planning pool sourced from this VPC.
default: false
scopeId:
type: string
description: Private IPAM scope ID in the IPAM administrator account.
externalName:
type: string
description: Existing IPv4 VPC resource-planning pool ID to import.
ipv6:
type: object
description: IPv6 VPC resource-planning pool configuration.
properties:
enabled:
type: boolean
description: Create an IPv6 planning pool sourced from this VPC.
default: false
scopeId:
type: string
description: IPAM scope ID containing the regional IPv6 source pool.
externalName:
type: string
description: Existing IPv6 VPC resource-planning pool ID to import.
publicIpSource:
type: string
description: Public IPv6 source inherited from the regional GUA pool.
enum:
- amazon
- byoip
default: amazon

# Subnet layout - used with both IPAM and manual VPC CIDR
subnetLayout:
type: object
description: |
Subnet layout configuration. When using IPAM, subnets are
automatically calculated from the VPC CIDR using cidrmath.
Subnet layout configuration. When ipv4PoolId is set, subnets
allocate IPv4 CIDRs from a VPC resource-planning IPAM pool.
Otherwise, IPv4 CIDRs are calculated from the VPC CIDR. When
ipv6PoolId is set, each subnet allocates an IPv6 CIDR from its
VPC resource-planning pool.
properties:
ipv4PoolId:
type: string
description: |
IPv4 VPC resource-planning IPAM pool ID whose source resource
is this VPC. Do not use the regional pool that allocates VPC CIDRs.
ipv6PoolId:
type: string
description: |
IPv6 VPC resource-planning IPAM pool ID whose source resource
is this VPC. Do not use the regional /52 pool that allocates
VPC /56 CIDRs.
ipv6NetmaskLength:
type: integer
description: IPv6 netmask length allocated to each subnet.
default: 64
availabilityZones:
type: array
description: AZ suffixes for subnet creation (e.g., ["a", "b", "c"]).
Expand Down Expand Up @@ -492,6 +594,21 @@ spec:
cidr:
type: string
description: Allocated VPC IPv6 ULA CIDR from IPAM.
ipv6Gua:
type: object
description: IPv6 GUA IPAM allocation status.
properties:
cidr:
type: string
description: Allocated VPC IPv6 GUA CIDR from IPAM.
resourcePlanning:
type: object
description: Observed VPC resource-planning pool IDs.
properties:
ipv4PoolId:
type: string
ipv6PoolId:
type: string
network:
type: object
properties:
Expand All @@ -508,6 +625,8 @@ spec:
type: string
ipv6Ula:
type: string
ipv6Gua:
type: string
ipv6AmazonProvided:
type: string
availabilityZones:
Expand Down
22 changes: 15 additions & 7 deletions examples/networks/with-ipam.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,25 @@ spec:
# Pool ID from aws-ipam status
poolId: ipam-pool-0123456789abcdef0
netmaskLength: 16
ipv6Ula:
ipv6Gua:
enabled: true
# ULA pool ID from aws-ipam status
# Regional Amazon-provided GUA pool; allocate one /56 to this VPC
poolId: ipam-pool-0fedcba9876543210
netmaskLength: 56 # AWS requires /56 for VPC IPv6
netmaskLength: 56
resourcePlanning:
# Planning pools are created in the central IPAM administrator account.
providerConfigRef:
name: network
ipv4:
enabled: true
scopeId: ipam-scope-0123456789abcdef0
ipv6:
enabled: true
scopeId: ipam-scope-0fedcba9876543210

# Subnet layout - CIDRs calculated from VPC CIDR via cidrmath
# This is meant to be temporary until Terraform/Crossplane support for IPAM
# subnet allocations is available
# https://github.com/hops-ops/aws-network/pull/7
# Subnets allocate directly from the automatically created VPC planning pools.
subnetLayout:
ipv6NetmaskLength: 64
availabilityZones:
- a
- b
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ apiVersion: ec2.aws.m.upbound.io/v1beta1
kind: VPC
metadata:
name: with-ipam
namespace: infra
annotations:
crossplane.io/composition-resource-name: vpc
gotemplating.fn.crossplane.io/composition-resource-name: vpc
Expand All @@ -15,7 +16,7 @@ status:
id: vpc-0123456789abcdef0
arn: arn:aws:ec2:us-east-1:123456789012:vpc/vpc-0123456789abcdef0
cidrBlock: "10.100.0.0/16"
ipv6CidrBlock: "fd00:100::/56"
ipv6CidrBlock: "2600:1f26:45:8100::/56"
ipv6AssociationId: vpc-cidr-assoc-0123456789abcdef0
defaultNetworkAclId: acl-0123456789abcdef0
defaultRouteTableId: rtb-0123456789abcdef0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: ec2.aws.m.upbound.io/v1beta1
kind: VPCIpamPoolCidr
metadata:
namespace: infra
name: with-ipam-ipv4-planning-cidr
annotations:
crossplane.io/composition-resource-name: ipam-planning-ipv4-cidr
gotemplating.fn.crossplane.io/composition-resource-name: ipam-planning-ipv4-cidr
status:
conditions:
- type: Ready
status: "True"
- type: Synced
status: "True"
atProvider:
id: ipam-pool-vpc-ipv4-0123456789abcdef0_10.100.0.0/16
ipamPoolId: ipam-pool-vpc-ipv4-0123456789abcdef0
cidr: 10.100.0.0/16
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: ec2.aws.m.upbound.io/v1beta1
kind: VPCIpamPool
metadata:
namespace: infra
name: with-ipam-ipv4-planning
annotations:
crossplane.io/composition-resource-name: ipam-planning-ipv4
gotemplating.fn.crossplane.io/composition-resource-name: ipam-planning-ipv4
status:
conditions:
- type: Ready
status: "True"
- type: Synced
status: "True"
atProvider:
id: ipam-pool-vpc-ipv4-0123456789abcdef0
addressFamily: ipv4
ipamScopeId: ipam-scope-0123456789abcdef0
locale: us-east-1
sourceIpamPoolId: ipam-pool-0123456789abcdef0
sourceResource:
resourceId: vpc-0123456789abcdef0
resourceOwner: "123456789012"
resourceRegion: us-east-1
resourceType: vpc
Loading
Loading