Skip to content
academia.sh

Lesson 02 / 20

Common Ports and Services

Measuring open services in claims: 20 services opening 49 accept points across 6 hosts, 21 accept points resting on no proof at all, 4 externally reachable services receiving not one legitimate request in 30 days, closing unused services and narrowing access cutting accept points to 27 while breaking 4 periodic flows, and verifying the last 7 accept points excluding 240 devices.

Published Updated

In this lesson

  • Defines an open port as notice that a class of claim is ready to be accepted
  • Measures accept point counts, unused services, and zone access in a service inventory
  • Calculates the cost of narrowing the surface in terms of cut periodic flow and excluded devices
Contents

The previous lesson counted claims at the moment the network spoke: a reading traveling from the field to the center carries eighteen claims, and with no defense in place none of them had proof. Yet an endpoint does something before anyone ever sends it anything. It listens.

Listening is not a passive act. An open port is a notice even if no message ever crosses it: it says the service at that point is ready to accept a certain class of claim. A remote shell service is ready to accept the claim “I am this user”; a measurement intake endpoint is ready to accept the claim “this reading came from this meter.” This lesson counts a service inventory through that lens.

An Open Point Is Notice of Acceptance

PS4: an open port announces, even if no message ever crosses it, that the service at that point is ready to accept a specific class of claim. PS5: the size of the surface is not the number of services but the number of accept points; an accept point is a pair of one service and one zone that can reach that service. PS6: whether a service is used is knowable only within an observation window; periodic use falling outside the window reads as “never used.”

PS5 is the item that separates this lesson’s measure from a service count. The same service open on two hosts is two items, but if one is reachable only from the center and the other from all four of four zones, their contributions to the surface are not equal. Port numbering, the distinction between a listening socket and a connected socket, and number ranges were measured in the Network Models and Protocols course; they are not repeated here. The only question here is which claim each open point accepts and on what proof.

The inventory below is a model: it counts the services of a fictional regional measurement network; there is no real hostname or real public address. Producing such an inventory outside one’s own network — testing remotely which point is open — is both illegal and unprofessional without written authorization and a defined scope.

// networks/inventory.mjs — the service inventory of a fictional regional measurement network
// (model): there is no real network, no real hostname, and no real public address. No randomness.

// PS4: an open port is notice even if no message ever crosses it; it says the service at
// that point is ready to accept a specific class of claim.
// PS5: accept point = (service, zone that can reach it) pair; the size of the surface is
// not the number of services but the number of accept points.
// Columns: host, port, service class, verifier, 30-day legitimate requests, reachable
// zones, zones that sent requests in the last 30 days, zones that use it periodically,
// count of legacy devices sending requests that cannot verify (if these devices are
// excluded, the service's entire 30-day request volume stops).
// Zones: e = external network, f = field, b = branch, c = center.
export const SERVICE = `
  branch-gateway  22    remote-shell          client-proof      268     b,c      c      b  0
  branch-gateway  23    legacy-remote-shell   password          0       b,c      -      -  0
  branch-gateway  161   device-monitoring     community-string  9240    b,c      c      -  8
  branch-gateway  8080  management-interface  password          140     b,c      c      -  0
  collector       22    remote-shell          client-proof      412     f,b,c    b,c    f  0
  collector       1883  measurement-ingest   none              89400   e,f,b,c  f,b    -  220
  collector       9100  measurement-export   none              8640    b,c      c      -  0
  collector       514   log-collection        none              43200   f,b,c    b,c    -  14
  verifier        22    remote-shell          client-proof      190     b,c      c      -  0
  verifier        443   internal-service      token             61300   b,c      b,c    -  0
  verifier        5432  database-listener     password          0       b,c      -      c  0
  billing         443   internal-service      token             22800   b,c      b,c    -  0
  billing         80    unencrypted-web       none              1150    e,b,c    b      -  0
  billing         5432  database-listener     password          47600   b,c      c      -  0
  billing         21    legacy-file-transfer  password          36      e,b,c    c      -  0
  name-server     53    name-resolution       none              128400  e,f,b,c  f,b,c  -  0
  name-server     123   time-sync             none              34600   f,b,c    f,b,c  -  6
  directory       389   directory-query       password          19700   b,c      b,c    -  0
  directory       445   file-sharing          password          3300    b,c      b      c  0
  directory       631   printer-service       none              0       b,c      -      -  0`
  .trim().split("\n").map((r) => {
    const p = r.trim().split(/\s+/);
    const set = (x) => (x === "-" ? [] : x.split(","));
    return { host: p[0], port: +p[1], service: p[2], verifier: p[3], requests: +p[4],
      reach: set(p[5]), used: set(p[6]), periodic: set(p[7]), legacy: +p[8] };
  });

export const HOST = [...new Set(SERVICE.map((s) => s.host))];
export const points = (l) => l.reduce((t, s) => t + s.reach.length, 0);
export const openPoints = (l) =>
  l.filter((s) => s.verifier === "none").reduce((t, s) => t + s.reach.length, 0);
// networks/surface.mjs — how many claim accept points an open service opens, how many are
// never used at all, what narrowing and adding verification cost.
import { SERVICE, HOST, points, openPoints } from "./inventory.mjs";

const write = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));
const ZONE = ["e", "f", "b", "c"];
const empty = SERVICE.filter((s) => s.requests === 0);

console.log(`${SERVICE.length} services, ${HOST.length} hosts, ${ZONE.length} zones; ` +
  `accept points ${points(SERVICE)}, unverified accept points ${openPoints(SERVICE)}, ` +
  `services with no requests in 30 days ${empty.length}`);

const A = [-16, 9, 15, 14, 10, 14];
console.log("\n1. per host");
write(A, "host", "service", "unverified", "unused", "accept", "30d requests");
for (const h of HOST) {
  const l = SERVICE.filter((s) => s.host === h);
  write(A, h, l.length, l.filter((s) => s.verifier === "none").length,
    l.filter((s) => s.requests === 0).length, points(l),
    l.reduce((t, s) => t + s.requests, 0));
}

const B = [-18, 9, 9, 14];
console.log("\n2. the proof the accepted claim rests on");
write(B, "verifier", "service", "accept", "30d requests");
for (const d of [...new Set(SERVICE.map((s) => s.verifier))]) {
  const l = SERVICE.filter((s) => s.verifier === d);
  write(B, d, l.length, points(l), l.reduce((t, s) => t + s.requests, 0));
}

const C = [-48, 8, 14, 18, 14, 10];
console.log("\n3. steps to narrow the surface (cumulative)");
write(C, "step", "accept", "unverified", "stopped requests", "cut flow", "excluded");
let remaining = SERVICE, cut = 0, stopped = 0, excluded = 0;
const row = (name) => write(C, name, points(remaining), openPoints(remaining), stopped, cut, excluded);
row("none");
cut += empty.reduce((t, s) => t + s.periodic.length, 0);
remaining = remaining.filter((s) => s.requests > 0);
row("close never-used services");
cut += remaining.reduce((t, s) => t + s.periodic.filter((z) => !s.used.includes(z)).length, 0);
remaining = remaining.map((s) => ({ ...s, reach: s.used }));
row("narrow access to zones that use it");
const verify = (sel) => {
  const l = remaining.filter((s) => s.verifier === "none" && sel(s));
  excluded += l.reduce((t, s) => t + s.legacy, 0);
  stopped += l.filter((s) => s.legacy > 0).reduce((t, s) => t + s.requests, 0);
  remaining = remaining.map((s) => (l.includes(s) ? { ...s, verifier: "added" } : s));
};
verify((s) => s.legacy === 0);
row("add verification where no legacy device exists");
verify(() => true);
row("add verification to the rest");

const D = [-14, -20, 8, 14, 16];
console.log("\n4. services still unverified after narrowing");
write(D, "host", "service", "accept", "30d requests", "legacy devices");
for (const s of SERVICE.filter((x) => x.verifier === "none" && x.requests > 0 && x.legacy > 0))
  write(D, s.host, s.service, s.used.length, s.requests, s.legacy);

const E = [-8, 11, 12, 14, 14];
console.log("\n5. reachable and used services per zone");
write(E, "zone", "reachable", "used", "unused", "unverified");
for (const z of ZONE) {
  const r = SERVICE.filter((s) => s.reach.includes(z));
  const u = SERVICE.filter((s) => s.used.includes(z));
  write(E, z, r.length, u.length, r.length - u.length,
    r.filter((s) => s.verifier === "none").length);
}
20 services, 6 hosts, 4 zones; accept points 49, unverified accept points 21, services with no requests in 30 days 3

1. per host
host              service     unverified        unused    accept  30d requests
branch-gateway          4              0             1         8          9648
collector               4              3             0        12        141652
verifier                3              0             1         6         61490
billing                 4              1             0        10         71586
name-server             2              2             0         7        163000
directory               3              1             1         6         23000

2. the proof the accepted claim rests on
verifier            service   accept  30d requests
client-proof              3        7           870
password                  7       15         70776
community-string          1        2          9240
none                      7       21        305390
token                     2        4         84100

3. steps to narrow the surface (cumulative)
step                                              accept    unverified  stopped requests      cut flow  excluded
none                                                  49            21                 0             0         0
close never-used services                             43            19                 0             1         0
narrow access to zones that use it                    27            12                 0             4         0
add verification where no legacy device exists        27             7                 0             4         0
add verification to the rest                          27             0            167200             4       240

4. services still unverified after narrowing
host          service               accept  30d requests  legacy devices
collector     measurement-ingest         2         89400             220
collector     log-collection             2         43200              14
name-server   time-sync                  3         34600               6

5. reachable and used services per zone
zone      reachable        used        unused    unverified
e                 4           0             4             3
f                 5           3             2             4
b                20          10            10             7
c                20          14             6             7

The numbers belong to the measurement class; their inputs are PS4–PS6.

Forty-Nine Accept Points

Twenty services open 49 accept points across six hosts. That is two and a half times the service count, and the reason for the gap is access breadth: most services in the inventory are reachable from two zones, and some from all four. Twenty-one of the forty-nine points rest on no proof at all.

The second table separates services by what their proof rests on. Seven services perform no verification at all, and with 21 accept points they are the largest group in the inventory; the same seven services carry 305,390 of the thirty-day request volume — the largest share. Seven services open 15 accept points with a password. Three services require client proof and open only 7 accept points; the request volume they carry is 870. The most tightly verified services are the ones carrying the least work; where verification is entirely absent, the whole of the work flows. This inverse relationship is no coincidence: adding verification requires changing every client that uses that service, and the most-used service is the most expensive change.

The first table shows where the load collects. The collector performs no verification on three of its four services and opens 12 accept points on its own. The name server performs no verification on either of its two services and, with 163,000 requests, is the busiest host in the inventory. The branch gateway, by contrast, requires proof on all four of its four services.

Service count per host is also not a stand-in for the surface. The branch gateway and billing each carry four services, but one opens 8 accept points and the other 10; the directory opens 6 points with three services while the verifier also opens 6 points with three services, yet their unverified point counts differ. Host count, service count, and accept point count are three separate quantities, and only the third feeds a narrowing decision.

What Thirty Days Does Not Show

The fifth table reads per zone and produces this lesson’s most striking line: four services are reachable from the external network, yet none of those four services received a single legitimate request from the external network in thirty days. Three of the four have no verification. These four items are an accept surface that does no work in return, while standing fully open to the external network.

A similar gap exists in the field zone: five services are reachable, three are used. The branch zone uses ten of twenty services, the center fourteen. The gap between reachable and used exists in every zone, and its total is close to half the surface.

Three services received no requests at all in thirty days. This is where PS6 comes into play and measures the window’s lie: one of these three services — the database listener on the verifier — is used periodically by the center and did not fall inside the thirty-day window. Closing it based on usage data cuts a flow that is actually working on this item.

The Four Steps of Narrowing

The third table narrows the surface step by step and records the cost of each step. Closing the three never-used services drops the accept point count from 49 to 43, stops no requests, and cuts one periodic flow. Narrowing access to the zones that actually use it gives the biggest gain: it falls from 43 to 27, again stopping no requests, but the number of cut periodic flows rises to four. Together, the first two steps close 44.9% of the surface while spending zero of the thirty-day request volume.

What the four cut flows have in common is that none of them appeared in the thirty-day window: the remote shell connecting to the branch gateway from the branch, the remote shell connecting to the collector from the field, the access from the center to the file sharing on the directory, and the closed database listener. All four are legitimate, and all four would have shown up had the observation window been longer. The cost of every narrowing based on usage data is a function of window length. A thirty-day window catches monthly jobs; it does not catch quarterly closing jobs. Lengthening the window reduces the cut flow, but it also reduces the number of items that can be closed.

Seven Points That Cannot Be Verified

The last two steps try to close the remaining unverified accept points and do not change the accept point count at all — it stays at 27. What changes is the unverified point count, which falls from 12 to 7 and then to 0. Accept points and unverified accept points are two separate numbers: if a service is not closed, it is on the surface; if it is verified, it is only no longer without proof.

The fourth table shows the cost of the last step. Devices that cannot verify are sending requests to all three of the three services that remain unverified: 220 devices to the measurement intake, 14 to log collection, 6 to time sync. Adding verification excludes these 240 devices and stops the entirety of the three services’ 167,200 thirty-day requests. The measurement intake is the network’s actual work, and it is the single largest item that cannot be verified.

This is where defense falls short. The first three steps are free or nearly free; the last step is equivalent to shutting down the service that carries the most work in the inventory. Seven unverified accept points are the result not of a wrong configuration decision but of endpoint devices being unable to produce proof.

Summary

  • An open port is notice that a class of claim is ready to be accepted; the size of the surface is not the number of services but the number of service–reachable-zone pairs.
  • The 20 services in the inventory open 49 accept points across 6 hosts; 21 of them rest on no proof at all, and the same 21 points carry the largest share of the thirty-day request volume.
  • 4 services are reachable from the external network, and none of the four received a single legitimate request from the external network in thirty days; three have no verification.
  • Closing unused services and narrowing access to the zones that use it drops the accept point count from 49 to 27, stops no requests, but cuts 4 periodic flows that fall outside the window.
  • Adding verification to the remaining 7 unverified accept points excludes 240 devices and stops 167,200 requests; these services are unverifiable not because they are misconfigured but because their endpoints cannot produce proof.

Next Step

This lesson assumed a service is reachable: every row in the inventory was written by accepting that a packet can reach a host from a zone. But even two machines on the same segment reaching each other rests on a claim. An endpoint knows its target’s network address but not which hardware address to send the frame to; it asks, and it writes the first reply into its cache without demanding any proof. The next lesson builds that cache as a data structure and counts it: how many records an unverified reply corrupts, how many a consistency check catches, how many a pinned record rescues, and what the manual-management cost of that is.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close