A node may move a port a module publishes as a mapping's machine side

A module publishing `2222:22` — the machine's own ssh daemon holds 22, so
the module takes 2222 and says so in `listens` — could not be moved. The
setting was read against the last segment of each mapping alone, so the
number the module uses everywhere else was refused as a port it does not
publish, and the node's every push failed for as long as the setting was
stored. The one key that was accepted, the container's own port, was then
read only when the container's mapping was rewritten: the mapping moved
and the ports map, the filter, the adopted node's openings, its guard and
what a consumer is told all stayed on the number the software had left.

Either end of a mapping now names it, and a given port comes back under
both, so every reader finds the same number under the key it holds.
Ambiguity is refused where it is real — one number naming two different
mappings, or the two ends of one mapping given two different numbers.
This commit is contained in:
2026-09-23 02:08:35 +02:00
parent 91a41b7d20
commit 58644fd282
5 changed files with 429 additions and 16 deletions
+15 -1
View File
@@ -1289,7 +1289,13 @@ func publishedOn(resource map[string]any, module string, with Rendering) {
}
// givenOuter rewrites the machine side of a long-form mapping to the port this node was given for
// its software side, when it was given one.
// it, when it was given one.
//
// **Under either of the mapping's names.** A node gives a port by the number the module names it
// by, and a module publishing `"2222:22"` may say it listens on 22 or on 2222 — GivenPorts accepts
// both and answers to both, so looking the machine side up first and the container's port second
// finds the same number either way. Read only by the container's port, this moved nothing for the
// module that declares the machine side, and the setting was refused before it got here.
func givenOuter(written string, given map[int]int) string {
if len(given) == 0 {
return written
@@ -1299,11 +1305,19 @@ func givenOuter(written string, given map[int]int) string {
mapping, protocol = written[:cut], written[cut:]
}
parts := strings.Split(mapping, ":")
if len(parts) < 2 {
return written
}
inner, err := strconv.Atoi(strings.TrimSpace(parts[len(parts)-1]))
if err != nil {
return written
}
at, ok := given[inner]
if outer, err := strconv.Atoi(strings.TrimSpace(parts[len(parts)-2])); err == nil {
if machine, named := given[outer]; named {
at, ok = machine, true
}
}
if !ok {
return written
}