Security Engineering Foundations · Lesson 1 of 10

Free preview

Threat modelling the service you run

Turn "make the server secure" into a short, specific document that names the assets, the boundaries, the attacks and the control that stops each one.

time
1 h 30 min
on completion
+120 XP

Why this matters

"Make the server secure" is not a task. It has no finish line, no order of work, and no way to tell a good decision from a fashionable one. Every engineer who has been handed it has felt the same pull: start with whatever is easiest to change, feel productive, and discover six weeks later that the thing an attacker would actually have used was never touched.

A threat model replaces that with a page you can argue with. It says what is worth taking, where the attacker crosses from outside to inside, what they would try at each crossing, and which control you built to stop it. It is not a compliance document and it is not long. Two pages, written before you change anything, will reorder your work more than any tool you install.

This course builds nine controls on one host. Today you decide *why* each of them, and in what order, and what will still be exposed when you are done. Every later lesson points back at a row in the table you are about to write, and the final project asks a colleague to act on it at two in the morning.

Concepts

Start from the asset, not the technology

An asset is something an attacker wants. On linux01 that is not "the server": it is the customer records the application holds, the private keys that let you impersonate the service, the certificate authority that would let an attacker mint a trusted identity, and the ability to log in as somebody else. Listing technologies produces a shopping list. Listing assets produces an argument about what to protect first.

Note that some of the most valuable assets are ones you are about to create. A CA key is worth more than the host it lives on, because whoever holds it can issue a certificate for any name the CA is trusted for. Build the model before the assets exist and you will place them deliberately.

A trust boundary is where an assumption changes

A trust boundary is any place where data or a request moves between two levels of trust. The internet reaching port 443 is one. An unauthenticated request becoming an authenticated session is another. A service account calling something that runs as root is a third. Your own workstation reaching the host is a fourth, and the one people forget.

The useful question at each boundary is not "is this encrypted?" but "what does each side assume about the other, and what happens when that assumption is false?" The SSH daemon assumes the key presenting itself belongs to somebody who should be here. The browser assumes the certificate was issued by a CA it trusts. The service assumes the file it reads its token from has not been replaced. Each of those assumptions is a control waiting to be built.

Enumerate attacks with prompts, not with imagination

Staring at a boundary and asking "what could go wrong?" produces whatever you read most recently. Structured prompts produce coverage. The six from STRIDE work well: spoofing (pretending to be someone), tampering (changing data), repudiation (denying an action happened), information disclosure (reading what you should not), denial of service (stopping legitimate use) and elevation of privilege (gaining rights you were not given).

Run all six at each boundary, write one line for the ones that are plausible, and say what the attacker *gains* rather than what they do. "Brute-forces SSH" is an activity. "Obtains an interactive shell as a user with sudo" is a gain, and it tells you immediately that rate limiting alone is a speed bump, not an answer.

An attack tree for the outcome you least want

Pick the single worst outcome — on this host, an attacker with a root shell — and write it as the root of a small tree. Each branch is a way to reach it; each leaf is where a control stops that branch. Drawing it takes ten minutes and shows you two things you cannot see from a list: which branches share a single choke point, and which branch has no leaf at all.

text
Root shell on linux01
├── Log in as a user with sudo
│   ├── Guess or steal a password ......... stopped: passwords disabled, certificates only (lesson 4)
│   ├── Steal a private key from a laptop . reduced: certificates expire in hours (lesson 4)
│   └── Replay a key we already revoked ... stopped: key revocation list (lesson 4)
├── Exploit a service reachable from outside
│   ├── Reach a service we never published  stopped: default-deny firewall (lesson 3)
│   └── Exploit nginx itself .............. reduced: patch policy (lesson 2); NOT stopped
└── Escalate from the service account
    ├── Read a secret the service holds ... stopped: sealed credential, 0400 (lesson 8)
    └── Abuse a SUID binary ............... reduced: SUID inventory in the baseline (lesson 2)

The tree above is the one this course builds against. Read the third leaf of the middle branch: a remote-code-execution flaw in nginx itself is not stopped by anything here. Saying so is the point. A model that claims to stop everything is a model nobody will trust the second time.

The control map is what makes it operational

The last table is the one people actually reopen. One row per boundary: the control, where it is built, and what is left over. During an incident it answers "was this supposed to be possible?" in seconds. During a handover it tells the next engineer which lines of configuration are load-bearing.

Guided exercise

You will produce one file, ~/sec/threat-model.md, for the service this course builds: an nginx site on linux01 serving a small application over HTTPS, administered over SSH, with logs kept on the host.

  1. Make the working area the whole course uses, and record where you started from:
bash
mkdir -p ~/sec/{bin,ca,changes,evidence,baseline}
chmod 700 ~/sec
cd ~/sec
{ echo "# starting state $(date -u +%FT%TZ)"; ss -H -tln; systemctl is-active ssh nginx ufw 2>&1; } \
  | tee ~/sec/evidence/00-starting-state.txt
text
# starting state 2026-09-20T05:31:44Z
LISTEN 0      4096       127.0.0.53%lo:53         0.0.0.0:*
LISTEN 0      128              0.0.0.0:22         0.0.0.0:*
LISTEN 0      128                 [::]:22            [::]:*
active
inactive
active

Two things in that output are already worth a row in your model: SSH answers on every address, and ufw reports its unit as active while its rules are not enforcing anything. Both get dealt with in lessons 2 and 3.

  1. Start the document from the template in this course's assets (its headings are the ones the rubric grades). Create ~/sec/threat-model.md with these sections, in this order: ## What this system is, ## Assets, ## Trust boundaries, ## What can go wrong, ## Attack tree, ## Control map, ## Assumptions and things out of scope.
  1. Fill in Assets. Aim for five to eight rows, each naming a thing an attacker wants, where it lives, and why it is worth taking. At least two of them should be assets this course is about to create rather than ones that exist today.
  1. Fill in Trust boundaries. Number them B1, B2, B3 … and give each one a "what is assumed on each side" column. Four to six boundaries is right for this system; if you have twelve you are listing components, not boundaries.
  1. Fill in What can go wrong, running all six STRIDE prompts at each boundary and keeping only the plausible ones. Write the attacker's *gain* in its own column.
  1. Draw your attack tree for one outcome in a fenced text block, with a leaf on every branch saying where it is stopped or reduced — and leave at least one branch honestly marked as not stopped.
  1. Fill in the Control map. One row per boundary, each naming a control and the lesson number of this course that builds it. The lessons available to you are: 2 baseline and patching, 3 host firewall, 4 SSH certificates, 5 private CA, 6 TLS, 7 abuse response, 8 secret custody, 9 logging and audit, 10 integrity and re-audit.
  1. Finish with Assumptions: the hypervisor, the platform console, the archive you install packages from, and the fact that you are your own CA. Then save your work as evidence:
bash
cp ~/sec/threat-model.md ~/sec/evidence/01-threat-model.md
wc -l ~/sec/threat-model.md

This is one of four instructor-graded checks in the course. It is graded by a person because the thing being judged is reasoning, and a script that searched your document for the word "spoofing" would reward padding. Submit the file; the rubric is in the course materials your instructor holds.

Troubleshooting

Symptom → your table has twenty boundaries and you cannot finish. Cause: you are listing components (nginx, sshd, systemd, the disk) rather than crossings. Fix: for each row ask "what changes about trust here?" If nothing changes, it is not a boundary; fold it into the one that contains it.

Symptom → every row of the control map says "stopped". Cause: you wrote the map after deciding the answer. Fix: for each control, ask what an attacker who knows about it would do next. If you have no answer, the row is probably "reduced", not "stopped".

Symptom → the model names controls that are not in this course (a WAF, an EDR agent, a bastion host). That is fine and often correct, but mark them clearly as *not built here* so the control map still tells the truth about this machine.

Symptom → mkdir -p ~/sec/{bin,ca,...} created a directory literally called {bin,ca,...}. Cause: you ran it in a shell without brace expansion, or with quotes around the braces. Fix: remove the directory and run the command in bash without quotes.

Symptom → you cannot decide between likelihood and impact for a row. Fix: do not build a scoring system. Write "high/medium/low x high/medium/low" and move on; the value is the ordering, not the arithmetic.

Check your understanding

Why is "an attacker obtains an interactive shell as a user with sudo" a better row than "an attacker brute-forces SSH"?

The first names what the attacker *gains*, which tells you how much the outcome costs you and lets you compare it with other rows. The second names an activity, and quietly suggests the control is "slow the activity down" — which is why rate limiting so often ends up as the only answer to it.

Your control map has a row whose control is "we patch regularly". What is missing?

Where it is built and what is left over. A control with no location cannot be verified by the next engineer, and patching leaves a real residual risk: everything between a vulnerability being exploited in the wild and the update reaching your archive. Name it.

The CA you build in lesson 5 will live on the same host it issues certificates for. Is that an asset, a boundary, or an assumption?

All three, which is why it belongs in the model. It is an asset (whoever holds the key can mint identities), it creates a boundary (the CA's key versus everything else on the host), and putting it here at all is an assumption you are making for the lab that you would not make in production.

Summary and next step

  • A threat model is a short argument about what to protect, in what order, and what will still be

exposed — not a compliance artefact.

  • Assets, boundaries, STRIDE prompts and an attack tree get you coverage; the control map makes the

document useful during an incident and at handover.

  • Honest gaps are a feature: a model with no residual risk is a model nobody will believe twice.

Next, lesson 2 stops arguing and starts measuring: you will audit linux01 against a baseline you can re-run, apply the first hardening set, and prove from the running kernel — not from a file you wrote — that something actually changed.

References

  • Ubuntu Server documentation — https://documentation.ubuntu.com/server/ (accessed 2026-09-20)
  • OWASP Secure Headers Project (used later in the course; the boundary vocabulary here is generic) — https://owasp.org/www-project-secure-headers/ (accessed 2026-09-20)
  • RFC 5737, IPv4 address blocks reserved for documentation — https://www.rfc-editor.org/rfc/rfc5737 (accessed 2026-09-20)
  • CIS Benchmarks (named only as a reference point; no benchmark text is used in this course) — https://www.cisecurity.org/cis-benchmarks (accessed 2026-09-20)

Sign in to record your progress

Signing in saves eligible progress. It does not enroll you or include a lab; review the career path for access terms.

Sign in