Skip to content

[PRODENG-3929] Admit the NodePort range the aws-simple example installs - #703

Merged
james-nesbitt merged 3 commits into
mainfrom
PRODENG-3929-nodeport
Nov 6, 2026
Merged

james-nesbitt merged 3 commits into
mainfrom
PRODENG-3929-nodeport

Conversation

@james-nesbitt

@james-nesbitt james-nesbitt commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Second PR on PRODENG-3929. The ticket is unchanged and no new ticket was raised: this fixes a defect that is a consequence of the first PR on the same ticket, so it belongs to it.

The defect

#689 replaced aws-simple's blanket protocol = "-1" ingress rule with four scoped rules — 22 and 5986 from var.admin_cidrs, 443 and 6443 from admin_cidrs plus the VPC CIDR. That scoping is correct and is kept here untouched.

What went with the blanket rule and was not carried into the four is the Kubernetes NodePort range. examples/terraform/aws-simple/launchpad.tf installs MKE with --nodeport-range=32768-35535, and on main no rule admits 32768-35535 at any CIDR. Re-applying the example therefore produces a cluster that declares a NodePort range nothing outside it can reach, and no tfvars value restores it. It is a consequence of narrowing a rule that was carrying more than it looked like it was carrying, not anyone's oversight.

What actually needed restoring — measured, not assumed

The pre-#689 external rule (git show e09a33f:examples/terraform/aws-simple/launchpad.tf) was from_port 0 / to_port 0 / protocol "-1" / cidr_blocks ["0.0.0.0/0"]. In AWS, -1 ignores the port fields, so the lost set is literally every port of every protocol. Of the ports this repository actually names:

Port Named at Who connects Already covered by the surviving self = true rule
32768-35535 aws-simple/launchpad.tf --nodeport-range clients outside the cluster no
2377/tcp pkg/product/mke/config/host.go:388 — SwarmAddress is built from the host's InternalAddress node↔node yes
7946 tcp+udp, 4789/udp, 10250/tcp host-firewall user_data only node↔node yes
2376/tcp no reference anywhere in the repo nothing in this repo yes if used

#689 left the self = true, all-protocols, all-ports rule in place at launchpad.tf:83-90, and the group covers every nodegroup. So every port above except the NodePort range is already admitted between cluster members, and re-opening any of them externally would widen exposure with no consumer. One rule pair was missing; that is what this adds.

The change

  • local.nodeport_range states 32768-35535 once and feeds both the ingress rule and the --nodeport-range install flag. They could not previously disagree only because one of them did not exist; sharing the local keeps it that way.
  • Two ingress rules over that range, TCP and UDP. Both worked before [PRODENG-3929] Scope example ingress, split node roles, stop inlining creds #689 and a UDP NodePort service is legal, so TCP-only would be a silent narrowing.
  • New var.nodeport_cidrs, default = null, falling back to var.admin_cidrs through a local. NodePort services are reached by application clients, who are not the administrators admin_cidrs names — with a single list, admitting app clients also hands them SSH and the MKE and Kubernetes APIs. The fallback means an existing tfvars is unchanged and a default run restores exactly the pre-[PRODENG-3929] Scope example ingress, split node roles, stop inlining creds #689 exposure of the range. An empty list is rejected, because a cluster installed with --nodeport-range and no way in is the failure the variable exists to prevent.
  • No VPC CIDR on these rules: nothing in local.launchpad_ingresses load-balances the NodePort range, so there is no in-VPC health check to admit.
  • Documented in the variable declaration, terraform.tfvars.template and the README, in the same risk-stating voice admin_cidrs uses.

Decided: the host-firewall gap stays open, knowingly

The repository owner reviewed this PR, un-drafted it himself, and is merging the security-group half with the host-firewall gap still open and documented. That gap is not a precondition of this merge and not a question this PR answers; fixing the host firewall is separate work.

The gap itself is unchanged and still worth knowing. The user_data in terraform.tfvars.template opens 2377, 7946, 4789 and 10250 and does not open 32768-35535. On rhel_9 and sles_15 that user_data is firewall-cmd, so the host firewall blocks the NodePort range independently of the security group. On ubuntu_22.04 the same ports are opened through ufw allow … with no ufw enable, so the omission is identical and the same gate applies whenever ufw is active in that AMI — the ufw-active part is an inference, not a measurement, and is not testable without provisioning. All of it predates #689 and is not what #689 broke, so it was deliberately left out of a regression fix rather than widening this PR into the test platform definitions; the README states it for all three platforms.

Left out deliberately

aws-complete/launchpad.tf declares the same --nodeport-range=32768-35535 with no security group rule for it — before #689 and after. Its permissive group was already self = true-only pre-#689 (git show e09a33f:examples/terraform/aws-complete/sg.tf), and #689 only narrowed source CIDRs there, so no port in aws-complete went from reachable to unreachable. It is a latent gap, not this regression, and it is left alone rather than missed.

One correction, so this reasoning can be checked against #689: that commit message says admin_cidrs is declared "with no default", but main has default = ["0.0.0.0/0"] in both examples (aws-simple/variables.tf, aws-complete/variables.tf) — a later commit inside the same PR added it. That default is what makes a default run here restore the old exposure exactly.

Verification

AWS was pinned off on every invocation (AWS_CONFIG_FILE=/dev/null, AWS_SHARED_CREDENTIALS_FILE=/dev/null, AWS_EC2_METADATA_DISABLED=true). No apply, no describe, no AWS call of any kind.

  • terraform init, validate and fmt -check -recursive pass on aws-simple and on untouched aws-complete.

  • terraform plan against a tfvars reconstructed from the template resolves every variable, plans the three non-AWS resources, and stops at Error: No valid credential sources found — as far as it can go without AWS.

  • terraform console renders the rule list as:

    -1  0-0          self=true   cidrs=
    tcp 22-22        cidrs=0.0.0.0/0
    tcp 5986-5986    cidrs=0.0.0.0/0
    tcp 443-443      cidrs=0.0.0.0/0,172.31.0.0/16
    tcp 6443-6443    cidrs=0.0.0.0/0,172.31.0.0/16
    tcp 32768-35535  cidrs=0.0.0.0/0
    udp 32768-35535  cidrs=0.0.0.0/0
    
  • With admin_cidrs=["198.51.100.17/32"] and nodeport_cidrs=["203.0.113.0/24"], the two NodePort rules carry 203.0.113.0/24 and every other rule carries 198.51.100.17/32 — the separation works.

  • nodeport_cidrs=[] fails at plan with nodeport_cidrs must list at least one CIDR, or be left unset to inherit admin_cidrs.

  • The flag renders "--nodeport-range=32768-35535", byte-identical to the literal it replaces.

Does this complete PRODENG-3929?

No. It closes the NodePort regression #689 introduced in aws-simple, and nothing more.

The two gaps above are deferred as separate work, decided by the repository owner: neither the host-firewall gap (32768-35535 absent from the user_data on rhel_9, sles_15 and both ubuntu_22.04 entries) nor the aws-complete gap (launchpad.tf:242 declares --nodeport-range with no security group rule for it, before and after #689) belongs to PRODENG-3929.

No ticket is filed for either, deliberately, under the owner's standing instruction not to open new tickets — the absence of a ticket here records a decision, not an oversight.

Written by AI: claude-opus-5

Second PR on PRODENG-3929. #689 replaced aws-simple's blanket
`protocol = "-1"` ingress rule with four scoped ones -- 22 and 5986 from
var.admin_cidrs, 443 and 6443 from admin_cidrs plus the VPC CIDR. The
scoping is right and stays. What went with the blanket rule, and was not
replaced, is the Kubernetes NodePort range: launchpad.tf installs MKE with
--nodeport-range=32768-35535 and no rule admitted it at any CIDR, so a
re-apply produced a cluster declaring a NodePort range nothing could reach,
with no tfvars value that restored it.

Measured rather than assumed. The pre-#689 external rule was from_port 0 /
to_port 0 / protocol "-1" from 0.0.0.0/0, which in AWS means every port of
every protocol, so the literal lost set is everything outside the four new
rules. Of the ports this repo actually names, only the NodePort range needs
an external rule:

  - 2377/tcp is node-to-node; SwarmAddress is built from the host's
    InternalAddress (pkg/product/mke/config/host.go:388)
  - 7946 tcp+udp, 4789/udp and 10250/tcp appear only in the host-firewall
    user_data, all node-to-node
  - 2376/tcp is referenced nowhere in the repo at all

All of those are already carried by the self=true rule, which #689 left in
place at launchpad.tf:83-90 and which covers every nodegroup, so re-opening
them externally would be a widening with no consumer.

The range is now stated once, as local.nodeport_range, and feeds both the
security group rule and the --nodeport-range install flag. The two
previously could not disagree only because one of them did not exist;
sharing the local means they still cannot.

Source is a new var.nodeport_cidrs rather than admin_cidrs. NodePort
services are reached by application clients, who are not the administrators
admin_cidrs names: with one list, admitting app clients also hands them SSH
and the MKE and Kubernetes APIs. It defaults to null and falls back to
admin_cidrs through a local, so an existing tfvars is unchanged and a
default run restores exactly the pre-#689 exposure of that range. An empty
list is rejected, because a cluster installed with --nodeport-range and no
way in is the failure the variable exists to prevent. The rule covers TCP
and UDP -- both worked before #689 and a UDP NodePort service is legal --
and takes no VPC CIDR, because nothing in local.launchpad_ingresses
load-balances the range, so there is no in-VPC health check to admit.

Verified without AWS, with AWS_CONFIG_FILE, AWS_SHARED_CREDENTIALS_FILE and
AWS_EC2_METADATA_DISABLED pinned off throughout. terraform init, validate
and fmt -check pass on aws-simple and on untouched aws-complete. A plan
against a tfvars reconstructed from the template resolves every variable,
plans the three non-AWS resources and stops at "No valid credential sources
found", which is as far as it can go. terraform console renders the rule
list as tcp 22, tcp 5986, tcp 443, tcp 6443, then tcp 32768-35535 and udp
32768-35535; with admin_cidrs=["198.51.100.17/32"] and
nodeport_cidrs=["203.0.113.0/24"] the NodePort rules carry 203.0.113.0/24
while the rest carry 198.51.100.17/32; nodeport_cidrs=[] fails at plan with
the validation message; and the flag renders "--nodeport-range=32768-35535",
byte-identical to the literal it replaces. No apply, no AWS call of any
kind.

Two findings left out of this change deliberately, both recorded on the PR.
The tfvars user_data opens 2377, 7946, 4789 and 10250 but not the NodePort
range, so on rhel_9 and sles_15 firewall-cmd still blocks it whatever the
security group says -- that predates #689 and is noted in the README rather
than fixed here. And aws-complete declares the same --nodeport-range with no
rule for it, before and after #689; it lost nothing, so it is not this
regression.

Refs: PRODENG-3929

Written by AI: claude-opus-5

Signed-off-by: James Nesbitt <jnesbitt@mirantis.com>
@james-nesbitt
james-nesbitt requested a balanced review from Copilot November 6, 2026 10:57
@james-nesbitt
james-nesbitt marked this pull request as ready for review November 6, 2026 10:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

A maintainer must decide whether unresolved host-firewall gaps block the security-group-only fix, since end-to-end NodePort reachability remains unvalidated on provisioned clusters.

No open issues 🎉

What changed in this PR

Restores security-group access to the Kubernetes NodePort range in aws-simple after #689 narrowed external ingress, while preserving the existing administrative access rules. Host-firewall restrictions remain outside this fix.

Changes:

  • Adds TCP and UDP ingress for ports 32768–35535, sharing the range definition with the MKE install flag.
  • Introduces nodeport_cidrs, defaulting to admin_cidrs, so application access can be configured independently.
  • Documents default internet exposure, configuration examples, and the remaining host-firewall limitation.
File Description
examples/​terraform/​aws-simple/​variables.tf Defines and validates optional nodeport_cidrs.
examples/​terraform/​aws-simple/​terraform.tfvars.template Adds NodePort access guidance and a CIDR example.
examples/​terraform/​aws-simple/​README.md Documents NodePort exposure and host-firewall limitations.
examples/​terraform/​aws-simple/​launchpad.tf Adds TCP/UDP ingress and shares the range with the install flag.

Review effort: Balanced

The NodePort host-firewall caveat singled out rhel_9 and sles_15. Both
ubuntu_22.04 user_data entries in terraform.tfvars.template carry the
identical omission: `sudo ufw allow 2377,7946,10250/tcp; sudo ufw allow
7946,4789/udp`, with no 32768-35535. Say so, and mark the ufw-active
precondition as the inference it is — it cannot be tested without AWS.

README only; no terraform changed.

Written by AI: claude-opus-5

Signed-off-by: James Nesbitt <jnesbitt@mirantis.com>
@james-nesbitt
james-nesbitt merged commit d878e98 into main Nov 6, 2026
12 checks passed
@james-nesbitt
james-nesbitt deleted the PRODENG-3929-nodeport branch November 6, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants