Chapter 3 — Operating systems and identities
Lesson map
Lesson 03 published. Map, example, EX009.
flowchart TD
N0["Learning outcomes"]
N1["3.1 Least privilege"]
N2["3.2 Containers"]
N3["3.3 Service and agent identities"]
N4["3.4 Protecting files"]
N5["3.4b Patching"]
N6["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6
N6 --> EX["Worked example"]
EX --> Q["Exercise EX009"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["3.1 Least privilege"]
N2["3.2 Containers"]
N3["3.3 Service and agent identities"]
N4["3.4 Protecting files"]
N5["3.4b Patching"]
N6["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> Q["EX009"]
[Learning outcomes]
|
v
[3.1 Least privilege]
|
v
[3.2 Containers]
|
v
[3.3 Service and agent identities]
|
v
[3.4 Protecting files]
|
v
[3.4b Patching]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX009]
Learning outcomes
You will (1) run AI services with least-privilege accounts; (2) configure containers safely; (3) manage service and agent identities; (4) protect model and configuration files.
3.1 Least privilege
Each service runs as its own non-root user with access only to what it needs. The retrieval service reads the index; it does not need database credentials. The orchestrator reads its prompt files; it does not need write access to them in production.
Ana's check of HelpBot's services found two violations of this. The orchestrator's production account could write to the prompt files, which meant anyone who compromised it could change HelpBot's instructions without a deployment. The retrieval service had the account database password in its environment, left from an early prototype that personalised answers. Removing both took one afternoon. The test afterwards was simple: try the write and the database connection as each service account on staging, and record that both were refused.
3.2 Containers
Use minimal base images; run as non-root; make file systems read-only where possible; drop unneeded capabilities; limit CPU and memory; restrict network egress. Patch images regularly and rebuild rather than patching running containers.
For LineWatch these settings had concrete effects. Running as non-root and mounting the model directory read-only meant a compromised process could not replace the model file. Limiting memory stopped one oversized telemetry batch from taking down the host. Restricting egress meant the container could reach the telemetry broker and the logging service but not the internet, so a malicious model file could not send data out. Rebuilding rather than patching in place kept the hash manifest accurate, because every image came from the same pipeline.
3.3 Service and agent identities
Services authenticate to each other with their own identities (certificates or tokens). When HelpBot acts for a customer, the tool call should carry the customer's identity, so the tool service can check the router belongs to that customer. A single all-powerful service account lets anyone who steers the model act with that account's power.
This is the confused-deputy problem. In Ana's staging test, HelpBot called the restart tool with one service account for every customer, so the tool could not tell whose router it was restarting, and 9 of 60 requests to restart another test account's router succeeded. Once the tool call carried the customer's identity and the tool service compared it with the router's owner in the account database, the same 60 attempts all failed. The log line for each restart now names the customer, which also makes audits possible.
3.4 Protecting files
Model files, prompt files and configuration should be write-protected in production and verified by hash at start-up. A changed file should stop the service from starting and raise an alert.
"""Lab H: integrity of model artefacts with SHA-256 manifests (stdlib only).
Creates synthetic 'artefact' bytes, records a manifest, then detects a
one-byte tampering and a swapped file. No real models are involved."""
import hashlib, json
files = {"model.bin": bytes(range(256)) * 40, "tokenizer.json": b'{"vocab": ["a", "b"]}',
"config.yaml": b"layers: 4\nheads: 8\n"}
manifest = {n: hashlib.sha256(b).hexdigest() for n, b in files.items()}
print(json.dumps({k: v[:16] for k, v in manifest.items()}, indent=1))
received = dict(files)
tampered = bytearray(received["model.bin"]); tampered[5000] ^= 1
received["model.bin"] = bytes(tampered)
received["config.yaml"] = b"layers: 4\nheads: 8\ntrust_remote_code: true\n"
for n, b in sorted(received.items()):
ok = hashlib.sha256(b).hexdigest() == manifest[n]
print(f"{n:15s} {'OK' if ok else 'MISMATCH'}")
Expected output:
{
"model.bin": "e96760a87768717b",
"tokenizer.json": "f63d2ddabc61f2d9",
"config.yaml": "e5bbb570ca0fcb87"
}
config.yaml MISMATCH
model.bin MISMATCH
tokenizer.json OK
The lab shows what such a check catches. A one-byte change in model.bin produces a completely different hash, and so does adding a single line, trust_remote_code: true, to config.yaml, which would let loading code run whatever the model package contains. The tokenizer file, unchanged, still reports OK. A manifest only helps if it is stored somewhere the deployment user cannot edit; otherwise anyone able to swap the model can also update the expected hash.
3.4b Patching
AI services depend on many libraries. Rebuild images when security updates are released, test them on staging, and keep a record of versions so you can answer quickly whether an advisory affects you.
For HelpBot that means knowing, for each library, which image contains it and which version is installed. When the document-parsing library advisory arrived, the version list let Ana answer in minutes that HelpBot's ingestion image was affected and the orchestrator image was not. Rebuilding from a patched base image, running the HelpBot test suite on staging and only then promoting the image is slower than patching a running container, but it keeps the start-up hash manifest and the record of versions true.
3.5 Worked example (synthetic Kestrel case)
LineWatch runs a self-hosted model. Ana finds the container running as root with the model directory writable by the deployment user. She changes the container to a non-root user, mounts the model read-only, and adds a start-up hash check like the lab's. During the next deployment, a mismatch stops start-up: someone had copied a test model into production. The check prevented a silent change.
The mismatch was not an attack, but the response was the same as for one: stop, investigate, then decide. Ana compared the file's hash with the test environment's model and found an exact match, and the deployment log showed which engineer had copied it. Because start-up was refused, LineWatch kept running the previous container until the correct file was restored, so no alerts were missed. Without the check, LineWatch would have run for weeks on a model tuned for test data.
3.6 Errors and limits
- Containers running as root.
- Writable model directories in production.
- Tools accepting a service account's authority for any customer.
Each of these errors appeared in the Kestrel case before it was fixed. LineWatch's container ran as root, so any code in a loaded model would have had full control of the container. Its model directory was writable by the deployment user, which is how a test model reached production. HelpBot's restart tool accepted the service account's authority for any customer, which let 9 of 60 cross-account restart attempts succeed on staging. In each case the fix was a configuration change, not a new product.
Exercises
- EX009 — Permissions. List what the orchestrator needs and does not need. Rubric: 3 = needs read prompts, call model and tools; not DB creds, not write; 2 = partial; 1 = vague; 0 = none.
- EX010 — Container settings. Write six container hardening settings. Rubric: 3 = six apt; 2 = four or five; 1 = two or three; 0 = fewer.
- EX011 — Agent identity. Explain why tool calls should carry the customer's identity. Rubric: 3 = per-customer checks, confused deputy, audit; 2 = two; 1 = one; 0 = none.
- EX012 — Hash check. What should the start-up script do on a mismatch? Rubric: 3 = refuse start, alert, log file and hash; 2 = two; 1 = one; 0 = continue.
Synthesis
Least privilege, hardened containers, per-customer identities and verified files limit what any AI component can do if it misbehaves.
Continue studying — chapter 03 of 18
← Chapter 02 (draft) · Course chapter index · Chapter 04 (draft) →