Skip to content
academia.sh

Lesson 09 / 21

Encryption in Transit

What happens when transit encryption is enforced is counted: 9 of 16 connection legs are open, 4 have unknown status. Three-stage enforcement changes 23 configuration items and breaks 79 connections; 34 cannot be upgraded. Only one of six paths becomes proven end to end, and reaching all of them needs encryption in its own layer.

Published Updated

In this lesson

  • Counts connection legs across three states: encrypted, open, unknown
  • Measures the cost of enforcement: configuration items changed, connections broken, permanent breaks
  • Records provider-internal traffic status as an assumption and counts not-knowing itself as a line item
Contents

The question for data at rest is answered. When data moves, though, there is not a single box in front of you — there is a chain: a measurement network record travels from the edge device to the intake endpoint, from there to the gateway, to the collector, and to the database; every hop is a separate leg, and every leg has its own status. This lesson’s question is not the cost of the handshake — that was measured earlier — nor where encryption terminates; that was measured too. The question here is enforcement: which legs are encrypted, which are left open on an “internal network” rationale, how many connections break when policy enforces it, and how many configuration items change.

There is a third state as well: whether transport inside the provider’s own network is encrypted cannot be verified by the tenant. Those legs sit as “unknown,” the provider’s default is written down as an assumption, and not knowing itself counts as a line item. The measurement runs on the regional measurement network, is fictional, and is a model written with node.

CS7. The data path breaks into 16 connection legs; the leg catalog, client counts, configuration items, and request volumes are fictional. CS8. Every leg is on one of three network types: public network, dedicated line, provider-internal network. CS9. A leg’s status is three-valued — encrypted, open, unknown. The third value is used only for provider-internal transport and cannot be verified. CS10. For every open leg, the number of clients that do not support encryption and how many of them can be upgraded is recorded; a client that cannot be upgraded breaks permanently under enforcement. CS11. Enforcement is in three stages: public network first, then dedicated line, then provider-internal network. The transition window is 90 days (fictional), and 61 days have passed in warning mode. CS12. That “unknown” legs are encrypted can only be proven by the tenant encrypting in its own layer; the configuration cost of that is recorded per leg.

Legs, Paths, and Staged Enforcement

// leg.mjs — transit legs, paths, and staged enforcement. MODEL.
// The regional measurement network is FICTIONAL: the leg catalog, client counts, and request volumes are made up.
// status "unknown" = provider-internal transport; the tenant cannot verify it, it stands as an ASSUMPTION.
export const LEG = [
  // id, name, network, status, clients, unsupported, configItems, requestsK, proofItems, upgradable
  ["b1", "edge device -> intake endpoint", "public", "open", 340, 54, 3, 4820, 0, 38],
  ["b2", "intake endpoint -> gateway", "provider-internal", "encrypted", 6, 0, 1, 4820, 0, 0],
  ["b3", "gateway -> collector", "provider-internal", "unknown", 12, 0, 2, 4820, 4, 0],
  ["b4", "collector -> database", "provider-internal", "open", 12, 0, 4, 1960, 0, 0],
  ["b5", "collector -> queue", "provider-internal", "open", 12, 2, 2, 3100, 0, 2],
  ["b6", "processor -> queue", "provider-internal", "open", 9, 0, 2, 3100, 0, 0],
  ["b7", "processor -> object store", "provider-internal", "unknown", 9, 0, 1, 640, 3, 0],
  ["b8", "processor -> cache", "provider-internal", "open", 9, 9, 2, 5400, 0, 0],
  ["b9", "service -> log collector", "provider-internal", "open", 21, 0, 3, 8900, 0, 0],
  ["b10", "log collector -> archive", "provider-internal", "unknown", 2, 0, 1, 190, 2, 0],
  ["b11", "archive -> secondary region", "provider-internal", "unknown", 2, 0, 1, 190, 2, 0],
  ["b12", "invoice export -> reconciliation", "public", "encrypted", 1, 0, 1, 12, 0, 0],
  ["b13", "partner feed -> intake", "public", "open", 7, 3, 2, 260, 0, 1],
  ["b14", "operator -> admin endpoint", "public", "encrypted", 20, 0, 1, 45, 0, 0],
  ["b15", "admin endpoint -> service", "provider-internal", "open", 20, 0, 3, 45, 0, 0],
  ["b16", "measurement network maintenance channel", "dedicated-line", "open", 28, 11, 2, 70, 0, 4],
].map(([id, name, net, status, clients, broken, cfg, req, proof, up]) =>
  ({ id, name, net, status, clients, broken, cfg, req, proof, up }));

export const PATH = {                                     // the leg sequence a request travels
  "measurement intake": ["b1", "b2", "b3", "b4"],
  "summary generation": ["b5", "b6", "b7"],
  "log flow": ["b9", "b10", "b11"],
  "invoice export": ["b4", "b7", "b12"],
  "admin access": ["b14", "b15"],
  "partner intake": ["b13", "b3", "b4"],
};
export const find = (id) => LEG.find((b) => b.id === id);
export const NET = ["public", "dedicated-line", "provider-internal"];  // order of enforcement stages
export const WINDOW = 90, ELAPSED = 61;                    // fictional transition window, days

const total = (l, f) => l.reduce((a, b) => a + f(b), 0);
const B = [28, 7, 11, 10, 13, 13, 15];
export const row = (h) => h.map((v, j) => j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))
  .join("");

if (import.meta.url === `file://${process.argv[1]}`) {
  console.log(`model: ${LEG.length} legs, ${Object.keys(PATH).length} paths, ` +
    `${total(LEG, (b) => b.req)} thousand requests/day (fictional)`);
  console.log("\n" + row(["network type", "legs", "encrypted", "open", "unknown", "clients",
    "requests(k)"]));
  for (const n of NET) {
    const l = LEG.filter((b) => b.net === n);
    const s = (d) => l.filter((b) => b.status === d).length;
    console.log(row([n, l.length, s("encrypted"), s("open"), s("unknown"),
      total(l, (b) => b.clients), total(l, (b) => b.req)]));
  }

  // staged enforcement: at each stage, the open legs of that network type move to encrypted.
  const status = new Map(LEG.map((b) => [b.id, b.status]));
  console.log("\n" + row(["stage", "legs", "cfg items", "broken", "requests(k)", "still open",
    "unknown"]));
  const emit = (name, l) => {
    for (const b of l) status.set(b.id, "encrypted");
    console.log(row([name, l.length, total(l, (b) => b.cfg), total(l, (b) => b.broken),
      total(l, (b) => b.req), [...status.values()].filter((d) => d === "open").length,
      [...status.values()].filter((d) => d === "unknown").length]));
  };
  emit("0. warning mode", []);
  for (const n of NET) emit(`${NET.indexOf(n) + 1}. ${n}`,
    LEG.filter((b) => b.net === n && status.get(b.id) === "open"));

  // per path, end to end: a path counts as proven only if every one of its legs is encrypted.
  const pathRow = (name, d) => {
    const l = PATH[name].map(find);
    const count = (x) => l.filter((b) => d(b) === x).length;
    console.log(row([name, l.length, count("encrypted"), count("open"), count("unknown"),
      count("unknown") === 0 && count("open") === 0 ? "proven" : "no",
      total(l.filter((b) => d(b) !== "encrypted"), (b) => b.req)]));
  };
  console.log("\n" + row(["path (before enforcement)", "legs", "encrypted", "open", "unknown",
    "end to end", "unproven(k)"]));
  for (const name of Object.keys(PATH)) pathRow(name, (b) => b.status);
  console.log("\n" + row(["path (after three stages)", "legs", "encrypted", "open", "unknown",
    "end to end", "unproven(k)"]));
  for (const name of Object.keys(PATH)) pathRow(name, (b) => status.get(b.id));
}
model: 16 legs, 6 paths, 38372 thousand requests/day (fictional)

network type                   legs  encrypted      open      unknown      clients    requests(k)
public                            4          2         2            0          368           5137
dedicated-line                    1          0         1            0           28             70
provider-internal                11          1         6            4          114          33165

stage                          legs  cfg items    broken  requests(k)   still open        unknown
0. warning mode                   0          0         0            0            9              4
1. public                         2          5        57         5080            7              4
2. dedicated-line                 1          2        11           70            6              4
3. provider-internal              6         16        11        22505            0              4

path (before enforcement)      legs  encrypted      open      unknown   end to end    unproven(k)
measurement intake                4          1         2            1           no          11600
summary generation                3          0         2            1           no           6840
log flow                          3          0         1            2           no           9280
invoice export                    3          1         1            1           no           2600
admin access                      2          1         1            0           no             45
partner intake                    3          0         2            1           no           7040

path (after three stages)      legs  encrypted      open      unknown   end to end    unproven(k)
measurement intake                4          3         0            1           no           4820
summary generation                3          2         0            1           no            640
log flow                          3          1         0            2           no            380
invoice export                    3          2         0            1           no            640
admin access                      2          2         0            0       proven              0
partner intake                    3          2         0            1           no           4820

The first table gives away a pattern immediately: 2 of the 4 legs on the public network are encrypted, only 1 of 11 provider-internal legs. Encryption has been applied to the outward-facing side and not applied on the inside — the rationale is written down: “internal network.” The volume that rationale carries is counted: 33,165 thousand requests a day pass through provider-internal legs, eighty-six percent of the total.

The Enforcement Ledger

The stage table splits the bill into three. The public-network stage is cheap — 2 legs, 5 configuration items — but it breaks 57 connections, because 54 edge devices and 3 clients of the partner feed do not support encrypted transport. The dedicated-line stage is 1 leg, 2 items, 11 broken. The provider-internal stage is the most expensive: 6 legs, 16 items, and the path for 22,505 thousand requests a day; yet it breaks only 11 connections. The distribution runs against intuition — the stage that changes the most configuration is the stage that breaks the fewest connections.

The path tables build the real sentence. Before enforcement, none of the six paths are encrypted end to end. After three stages, only one becomes proven: admin access, because both legs it passes through can be verified. The remaining five paths cannot be proven because each carries at least one “unknown” leg. Unproven volume drops from 11,600 to 4,820 thousand requests but does not hit zero; the reason is a single leg — the hop between the gateway and the collector, a shared part of both measurement intake and partner intake.

// ledger.mjs — the enforcement ledger: broken connections, permanent breaks, exceptions, and proof cost.
import { LEG, PATH, WINDOW, ELAPSED } from "./leg.mjs";

const gen = (B) => (h) => h.map((v, j) => j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))
  .join("");
const row = gen([41, 9, 13, 10, 11, 11, 13]);              // broken-connection table
const dr = gen([27, 10, 17, 13, 13, 13, 15]);              // decision table
const LIMIT = 2;                                          // fictional policy: permanent exception cap

const total = (l, f) => l.reduce((a, b) => a + f(b), 0);
const OPEN = LEG.filter((b) => b.status === "open");
const UNKNOWN = LEG.filter((b) => b.status === "unknown");
const permanent = (b) => b.broken - b.up;

console.log(row(["broken leg", "clients", "unsupported", "upgrade", "permanent", "cfg items",
  "requests(k)"]));
for (const b of OPEN.filter((x) => x.broken > 0))
  console.log(row([b.name, b.clients, b.broken, b.up, permanent(b), b.cfg, b.req]));
console.log(row(["total", total(OPEN, (b) => b.clients), total(OPEN, (b) => b.broken),
  total(OPEN, (b) => b.up), total(OPEN, permanent), total(OPEN, (b) => b.cfg), total(OPEN, (b) => b.req)]));

// A decision: which legs get enforced, which get encrypted in their own layer, is a proxy set up.
const evaluate = (enforced, own, proxy) => {
  const d = new Map(LEG.map((b) => [b.id, b.status]));
  let broken = 0, cfg = 0;
  for (const b of LEG) {
    if (enforced.has(b.id)) { d.set(b.id, "encrypted"); cfg += b.cfg; broken += proxy ? 0 : permanent(b); }
    if (own.has(b.id)) { d.set(b.id, "encrypted"); cfg += b.proof; }
  }
  if (proxy) cfg += 3 * LEG.filter((b) => enforced.has(b.id) && permanent(b) > 0).length;
  const proven = Object.values(PATH)
    .filter((y) => y.every((id) => d.get(id) === "encrypted")).length;
  const unproven = total(LEG.filter((b) => d.get(b.id) !== "encrypted"), (b) => b.req);
  return { broken, cfg, proven, unproven,
    exempt: LEG.filter((b) => b.status === "open" && !enforced.has(b.id)).length };
};

const ids = (l) => new Set(l.map((b) => b.id));
const none = new Set();
const permanentBreakers = OPEN.filter((b) => permanent(b) > 0);  // legs that produce a permanent break
const DECISION = {
  "0 warning mode": [none, none, false],
  "1 enforce, no exception": [ids(OPEN), none, false],
  "2 enforce + proxy layer": [ids(OPEN), none, true],
  "3 proxy + own encryption": [ids(OPEN), ids(UNKNOWN), true],
  "4 enforce + exception": [ids(OPEN.filter((b) => permanent(b) === 0)), none, false],
};
console.log("\n" + dr(["decision", "enforced", "permanent break", "exempt open", "proven path",
  "extra cfg", "unproven(k)"]));
for (const [name, [z, k, v]] of Object.entries(DECISION)) {
  const s = evaluate(z, k, v);
  console.log(dr([name, z.size, s.broken, s.exempt, `${s.proven}/${Object.keys(PATH).length}`,
    s.cfg, s.unproven]));
}

const openReq = total(OPEN, (b) => b.req);
console.log(`\ntransition window: ${WINDOW} days, ${ELAPSED} days elapsed, ${WINDOW - ELAPSED} days left; ` +
  `${openReq * ELAPSED} thousand requests passed through open legs during the warning period`);
console.log(`permanent exception cap ${LIMIT} legs (fictional policy); requested ${permanentBreakers.length}, ` +
  `${permanentBreakers.length - LIMIT} legs beyond the cap`);
console.log(`client upgrades: ${total(OPEN, (b) => b.up)} clients, ` +
  `${OPEN.filter((b) => b.up > 0).length} legs; the remaining ${total(OPEN, permanent)} clients ` +
  `cannot be upgraded`);
console.log(`\nunknown item: ${UNKNOWN.length} legs, ` +
  `${total(UNKNOWN, (b) => b.req)} thousand requests/day, proof cost ` +
  `${total(UNKNOWN, (b) => b.proof)} config items`);
for (const b of UNKNOWN)
  console.log(`  ${b.name.padEnd(28)} ${String(b.req).padStart(6)} k/day, ` +
    `proof ${b.proof} items, assumption: provider-internal transport is encrypted`);
broken leg                                 clients  unsupported   upgrade  permanent  cfg items  requests(k)
edge device -> intake endpoint                 340           54        38         16          3         4820
collector -> queue                              12            2         2          0          2         3100
processor -> cache                               9            9         0          9          2         5400
partner feed -> intake                           7            3         1          2          2          260
measurement network maintenance channel         28           11         4          7          2           70
total                                          458           79        45         34         23        27655

decision                     enforced  permanent break  exempt open  proven path    extra cfg    unproven(k)
0 warning mode                      0                0            9          0/6            0          33495
1 enforce, no exception             9               34            0          1/6           23           5840
2 enforce + proxy layer             9                0            0          1/6           35           5840
3 proxy + own encryption            9                0            0          6/6           46              0
4 enforce + exception               5                0            4          1/6           14          16390

transition window: 90 days, 61 days elapsed, 29 days left; 1686955 thousand requests passed through open legs during the warning period
permanent exception cap 2 legs (fictional policy); requested 4, 2 legs beyond the cap
client upgrades: 45 clients, 4 legs; the remaining 34 clients cannot be upgraded

unknown item: 4 legs, 5840 thousand requests/day, proof cost 11 config items
  gateway -> collector           4820 k/day, proof 4 items, assumption: provider-internal transport is encrypted
  processor -> object store       640 k/day, proof 3 items, assumption: provider-internal transport is encrypted
  log collector -> archive        190 k/day, proof 2 items, assumption: provider-internal transport is encrypted
  archive -> secondary region     190 k/day, proof 2 items, assumption: provider-internal transport is encrypted

The broken-connection table sets one number apart: 79 clients do not support encrypted transport, 45 can be upgraded, and 34 cannot. The harshest row is the cache leg: 9 of 9 clients do not support it, none can be upgraded, and that leg carries 5,400 thousand requests a day. Second is the edge devices: 16 of 54 devices are hardware sitting in the field whose software will not change.

The decision table lays five approaches side by side. Warning mode breaks nothing and proves nothing; over 61 days, 1,686,955 thousand requests passed through open legs and that cannot be closed retroactively. Enforcing without exception changes 23 items and permanently severs 34 connections. The proxy layer zeroes out breakage and adds 12 items: 35. None of these three decisions pushes the proven path count above 1/6; the one row that does is the fourth — encrypting “unknown” legs in the tenant’s own layer adds 11 more items, for a total of 46, and 6/6 paths become proven.

The last decision shows the opposite: exempting the 4 legs that produce a permanent break cuts the load to 14 items and breaks no connections, but pushes unproven volume up to 16,390 thousand requests a day. Fictional policy recognizes 2 permanent exceptions; 4 are requested, and 2 legs fall outside the cap. Of the transition window’s 90 days, 61 have passed — 29 days remain.

Next to each of the four “unknown” legs sits the same sentence: the assumption is that provider-internal transport is encrypted. They carry 5,840 thousand requests a day, and their status cannot be read from any configuration the tenant holds. Being unknown is not the same as being open, but it is not the same as being provable either.

Summary

  • 3 of the 16 legs are encrypted, 9 are open, 4 have unknown status; only 1 of 11 provider-internal legs is encrypted, and 33,165 thousand requests a day pass through those legs.
  • Three-stage enforcement changes 23 items; the most expensive stage (provider-internal, 16 items) breaks the fewest connections (11), and the cheapest stage breaks 57.
  • 79 clients do not support encrypted transport; 45 can be upgraded, 34 cannot, and the proxy layer adds 12 items while saving those 34.
  • Before enforcement, no path is encrypted end to end; after three stages it is 1/6, and once encryption is added in the tenant’s own layer it is 6/6, with unproven volume dropping from 33,495 to zero.
  • The exemption path cuts the load to 14 items and pushes unproven volume up to 16,390 thousand requests/day; fictional policy recognizes 2 exceptions, and 2 legs fall outside the cap. 29 days remain in the transition window.

Next Step

Data has been made encrypted both at rest and in transit. Both rest on the same thing: a key or credential is stored somewhere, and services read it. The next lesson returns to that storage layer and measures a single question: when a secret rotates, how large should the window be where the old and new values are valid together — how many clients does a short window miss, and how much does a long window extend the leak duration.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close