Step-by-step: UDN private subnet¶
Everything here is real, captured output from an actual run - not invented numbers.
1. Deploy¶
git clone https://github.com/hoolia/examples.openshift.eu.git
cd examples.openshift.eu
oc apply -k examples/udn-private-subnet/
oc wait userdefinednetwork/private-subnet -n udn-demo --for=condition=NetworkCreated --timeout=60s
oc wait vm/vm-a vm/vm-b -n udn-demo --for=condition=Ready --timeout=900s
You'll see nine resources come up: two namespaces, the UDN itself, two VMs, three
NetworkPolicy objects, and a probe pod. No manual route tables, no subnet CIDR wizard, no
separate console flow for a Security Group.
2. Confirm isolation, from outside the namespace¶
Find your VMs' addresses:
oc get vmi vm-a vm-b -n udn-demo -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.status.interfaces[0].ipAddress}{"\n"}{end}'
From the probe pod - outside the udn-demo namespace, on the ordinary default pod network:
What you'll see:
PING 10.200.0.9 (10.200.0.9) 56(84) bytes of data.
--- 10.200.0.9 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1009ms
Notice the NetworkPolicy objects you just applied are all Egress-only - none of them
restrict inbound traffic. The isolation you're about to see comes from the UDN itself, not from
a firewall rule. The subnet is unreachable from outside because the UDN is the namespace's
primary network.
3. Confirm your VMs can still reach each other¶
Open a console session on vm-a:
Log in as fedora / udndemo123, then:
What you'll see:
PING 10.200.0.10 (10.200.0.10) 56(84) bytes of data.
64 bytes from 10.200.0.10: icmp_seq=1 ttl=64 time=1.87 ms
64 bytes from 10.200.0.10: icmp_seq=2 ttl=64 time=0.764 ms
--- 10.200.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
Zero config beyond the manifests you already applied - the allow-intra-namespace-egress
policy is what makes this possible.
4. Confirm egress is gated like a Security Group rule¶
From the same vm-a console session:
What you'll see: http_code=200 - allowed.
curl -sS --max-time 5 -o /dev/null -w 'http_code=%{http_code}\n' http://example.com
ping -c2 -W2 8.8.8.8
What you'll see:
curl: (28) Connection timed out after 5001 milliseconds
http_code=000
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1007ms
Plain HTTP times out and ICMP is blocked entirely. The NetworkPolicy pair is doing the job of
an AWS Security Group egress rule: HTTPS + DNS out, everything else denied.
To exit the console session, press Ctrl+].
5. Tear it down¶
The manifests¶
apiVersion: v1
kind: Namespace
metadata:
name: udn-demo
labels:
k8s.ovn.org/primary-user-defined-network: ""
apiVersion: k8s.ovn.org/v1
kind: UserDefinedNetwork
metadata:
name: private-subnet
namespace: udn-demo
spec:
topology: Layer2
layer2:
role: Primary
subnets:
- 10.200.0.0/24
ipam:
lifecycle: Persistent
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: vm-a
namespace: udn-demo
spec:
dataVolumeTemplates:
- metadata:
name: vm-a-rootdisk
spec:
sourceRef:
kind: DataSource
name: fedora
namespace: openshift-virtualization-os-images
storage:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 30Gi
instancetype:
kind: virtualmachineclusterinstancetype
name: u1.small
preference:
kind: virtualmachineclusterpreference
name: fedora
runStrategy: Always
template:
metadata:
labels:
vm.kubevirt.io/name: vm-a
spec:
domain:
devices:
interfaces:
- name: default
bridge: {}
networks:
- name: default
pod: {}
volumes:
- dataVolume:
name: vm-a-rootdisk
name: rootdisk
- cloudInitNoCloud:
userData: |
#cloud-config
chpasswd:
expire: false
password: udndemo123
user: fedora
ssh_pwauth: true
runcmd:
- systemctl enable --now sshd
name: cloudinitdisk
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: vm-b
namespace: udn-demo
spec:
dataVolumeTemplates:
- metadata:
name: vm-b-rootdisk
spec:
sourceRef:
kind: DataSource
name: fedora
namespace: openshift-virtualization-os-images
storage:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 30Gi
instancetype:
kind: virtualmachineclusterinstancetype
name: u1.small
preference:
kind: virtualmachineclusterpreference
name: fedora
runStrategy: Always
template:
metadata:
labels:
vm.kubevirt.io/name: vm-b
spec:
domain:
devices:
interfaces:
- name: default
bridge: {}
networks:
- name: default
pod: {}
volumes:
- dataVolume:
name: vm-b-rootdisk
name: rootdisk
- cloudInitNoCloud:
userData: |
#cloud-config
chpasswd:
expire: false
password: udndemo123
user: fedora
ssh_pwauth: true
runcmd:
- systemctl enable --now sshd
name: cloudinitdisk
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: udn-demo
spec:
podSelector: {}
policyTypes:
- Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-https-dns-egress
namespace: udn-demo
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 443
- to:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-intra-namespace-egress
namespace: udn-demo
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- podSelector: {}
apiVersion: v1
kind: Pod
metadata:
name: netshoot-probe
namespace: udn-demo-probe
labels:
app: netshoot-probe
spec:
containers:
- name: netshoot
image: nicolaka/netshoot:latest
command: ["sleep", "infinity"]
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace-udn-demo.yaml
- namespace-udn-demo-probe.yaml
- userdefinednetwork-private-subnet.yaml
- networkpolicy-default-deny-egress.yaml
- networkpolicy-allow-https-dns-egress.yaml
- networkpolicy-allow-intra-namespace-egress.yaml
- pod-netshoot-probe.yaml
- virtualmachine-vm-a.yaml
- virtualmachine-vm-b.yaml