Skip to content

Commit 1d2a859

Browse files
65278Felix Wischke (65278)
andauthored
api/v1alpha1/proxmoxcluster_types: validate controlplane port (#241)
* api/v1alpha1/proxmoxcluster_types: validate controlplane port * pkg/scope/cluster: remove unused clusterScope.ControlPlaneEndpoint (make coverage happy) * api/v1alpha1/proxmoxcluster_types: add port 0 test --------- Co-authored-by: Felix Wischke (65278) <[email protected]>
1 parent f9c532b commit 1d2a859

9 files changed

+37
-34
lines changed

api/v1alpha1/proxmoxcluster_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ const (
3737
type ProxmoxClusterSpec struct {
3838
// ControlPlaneEndpoint represents the endpoint used to communicate with the control plane.
3939
// +optional
40-
ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"`
40+
// +kubebuilder:validation:XValidation:rule="self.port > 0 && self.port < 65536",message="port must be within 1-65535"
41+
ControlPlaneEndpoint *clusterv1.APIEndpoint `json:"controlPlaneEndpoint"`
4142

4243
// AllowedNodes specifies all Proxmox nodes which will be considered
4344
// for operations. This implies that VMs can be cloned on different nodes from

api/v1alpha1/proxmoxcluster_types_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
corev1 "k8s.io/api/core/v1"
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2828
ipamicv1 "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2"
29+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2930
"sigs.k8s.io/controller-runtime/pkg/client"
3031
)
3132

@@ -106,6 +107,24 @@ var _ = Describe("ProxmoxCluster Test", func() {
106107
Expect(client.IgnoreNotFound(err)).To(Succeed())
107108
})
108109

110+
Context("ClusterPort", func() {
111+
It("Should not allow ports higher than 65535", func() {
112+
dc := defaultCluster()
113+
dc.Spec.ControlPlaneEndpoint = &clusterv1.APIEndpoint{
114+
Port: 65536,
115+
}
116+
Expect(k8sClient.Create(context.Background(), dc)).Should(MatchError(ContainSubstring("port must be within 1-65535")))
117+
})
118+
119+
It("Should not allow port 0", func() {
120+
dc := defaultCluster()
121+
dc.Spec.ControlPlaneEndpoint = &clusterv1.APIEndpoint{
122+
Port: 0,
123+
}
124+
Expect(k8sClient.Create(context.Background(), dc)).Should(MatchError(ContainSubstring("port must be within 1-65535")))
125+
})
126+
})
127+
109128
Context("IPv4Config", func() {
110129
It("Should not allow empty addresses", func() {
111130
dc := defaultCluster()

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/infrastructure.cluster.x-k8s.io_proxmoxclusters.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ spec:
561561
- host
562562
- port
563563
type: object
564+
x-kubernetes-validations:
565+
- message: port must be within 1-65535
566+
rule: self.port > 0 && self.port < 65536
564567
credentialsRef:
565568
description: CredentialsRef is a reference to a Secret that contains
566569
the credentials to use for provisioning this cluster. If not supplied

config/crd/bases/infrastructure.cluster.x-k8s.io_proxmoxclustertemplates.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,9 @@ spec:
610610
- host
611611
- port
612612
type: object
613+
x-kubernetes-validations:
614+
- message: port must be within 1-65535
615+
rule: self.port > 0 && self.port < 65536
613616
credentialsRef:
614617
description: CredentialsRef is a reference to a Secret that
615618
contains the credentials to use for provisioning this cluster.

internal/controller/proxmoxcluster_controller_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func buildProxmoxCluster(name string) infrav1.ProxmoxCluster {
228228
},
229229
},
230230
Spec: infrav1.ProxmoxClusterSpec{
231-
ControlPlaneEndpoint: clusterv1.APIEndpoint{
231+
ControlPlaneEndpoint: &clusterv1.APIEndpoint{
232232
Host: "10.10.10.11",
233233
Port: 6443,
234234
},

internal/webhook/proxmoxcluster_webhook.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,6 @@ func validateControlPlaneEndpoint(cluster *infrav1.ProxmoxCluster) error {
127127
field.NewPath("spec", "controlplaneEndpoint"), endpoint, "provided endpoint address is not a valid IP or FQDN"),
128128
})
129129
}
130-
// If the passed control-plane endppoint is an IPv6 address, wrap it in [], so it can properly pass ParseAddrPort validation
131-
if addr.Is6() {
132-
endpoint = fmt.Sprintf("[%s]", endpoint)
133-
}
134-
135-
ipAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", endpoint, ep.Port))
136-
if err != nil {
137-
return apierrors.NewInvalid(
138-
gk,
139-
name,
140-
field.ErrorList{
141-
field.Invalid(
142-
field.NewPath("spec", "controlplaneEndpoint"), fmt.Sprintf("%s:%d", endpoint, ep.Port), "provided endpoint is not in a valid IP and port format"),
143-
})
144-
}
145130

146131
// IPv4
147132
if cluster.Spec.IPv4Config != nil {
@@ -156,7 +141,7 @@ func validateControlPlaneEndpoint(cluster *infrav1.ProxmoxCluster) error {
156141
})
157142
}
158143

159-
if set.Contains(ipAddr.Addr()) {
144+
if set.Contains(addr) {
160145
return apierrors.NewInvalid(
161146
gk,
162147
name,
@@ -180,7 +165,7 @@ func validateControlPlaneEndpoint(cluster *infrav1.ProxmoxCluster) error {
180165
})
181166
}
182167

183-
if set6.Contains(ipAddr.Addr()) {
168+
if set6.Contains(addr) {
184169
return apierrors.NewInvalid(
185170
gk,
186171
name,

internal/webhook/proxmoxcluster_webhook_test.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,6 @@ var _ = Describe("Controller Test", func() {
7979
g.Expect(k8sClient.Create(testEnv.GetContext(), &cluster)).To(Succeed())
8080
})
8181

82-
It("should disallow invalid endpoint IP + port combination", func() {
83-
cluster := invalidProxmoxCluster("test-cluster")
84-
cluster.Spec.ControlPlaneEndpoint.Host = "127.0.0.1"
85-
cluster.Spec.ControlPlaneEndpoint.Port = 69000
86-
g.Expect(k8sClient.Create(testEnv.GetContext(), &cluster)).To(MatchError(ContainSubstring("provided endpoint is not in a valid IP and port format")))
87-
})
88-
8982
It("should disallow invalid IPV4 IPs", func() {
9083
cluster := invalidProxmoxCluster("test-cluster")
9184
cluster.Spec.IPv4Config.Addresses = []string{"invalid"}
@@ -141,7 +134,7 @@ func validProxmoxCluster(name string) infrav1.ProxmoxCluster {
141134
Namespace: metav1.NamespaceDefault,
142135
},
143136
Spec: infrav1.ProxmoxClusterSpec{
144-
ControlPlaneEndpoint: clusterv1.APIEndpoint{
137+
ControlPlaneEndpoint: &clusterv1.APIEndpoint{
145138
Host: "10.10.10.1",
146139
Port: 6443,
147140
},
@@ -159,7 +152,7 @@ func validProxmoxCluster(name string) infrav1.ProxmoxCluster {
159152

160153
func invalidProxmoxCluster(name string) infrav1.ProxmoxCluster {
161154
cl := validProxmoxCluster(name)
162-
cl.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
155+
cl.Spec.ControlPlaneEndpoint = &clusterv1.APIEndpoint{
163156
Host: "10.10.10.2",
164157
Port: 6443,
165158
}

pkg/scope/cluster.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,6 @@ func (s *ClusterScope) KubernetesClusterName() string {
179179
return s.Cluster.Name
180180
}
181181

182-
// ControlPlaneEndpoint returns the ControlPlaneEndpoint for the associated ProxmoxCluster.
183-
func (s *ClusterScope) ControlPlaneEndpoint() clusterv1.APIEndpoint {
184-
return s.ProxmoxCluster.Spec.ControlPlaneEndpoint
185-
}
186-
187182
// PatchObject persists the cluster configuration and status.
188183
func (s *ClusterScope) PatchObject() error {
189184
// always update the readyCondition.

0 commit comments

Comments
 (0)