At the end of the 2FA series you have three things. A credential provider with a TOTP tile from Part 1. An authentication package and an MSV1_0 sub-authentication filter inside LSA from Part 2. And Ristretto from Part 3, a gate that lets network logons through for a few minutes after you type a one-time code.
What you don't have is an easy way to put all of that on a machine. You need the MSVC build tools, two repositories at matching commits, RunAsPPL switched off, an unsigned DLL copied into System32, an enrollment per account, a reindex, and a handful of registry writes that have to happen in a certain order. Get the order wrong and the machine doesn't come back after the reboot. I wrote that in Part 2 and I still mean it: change one thing per reboot.
That's a lot of steps for something you mostly want to try out on a test VM. So I built a wizard for it. It's called Barista, because it makes the Doppio and the Ristretto.
caution
Barista installs code that runs inside lsass.exe. A fault there is a bluescreen on the logon path, and that can leave a machine that won't boot. Test VMs only, and take a snapshot before every reboot.
What it is
One file, Barista.exe. Download it from the release page, check its hash, right-click, Run as administrator. On the machine you need administrator rights. You don't need git, .NET, a compiler or a network connection, because everything Barista installs is already inside the exe.
certutil -hashfile Barista.exe SHA256
# must match the Barista.exe line in SHA256SUMS
Inside is a WPF wizard in C# and everything it installs, already compiled. The pages go in this order:
Risks -> Integrity -> Components -> Preflight -> Accounts -> Install -> Done
Barista itself writes almost nothing to the machine. Every registry key, every copy into System32 and every LSA registration happens in Doppio's and Ristretto's own install-*.ps1 scripts, the same ones the blog posts walk through. Barista finds them, explains what each one does, runs them in order and shows you the output. Those scripts carry the guards: the RunAsPPL check, "Auth0 already belongs to somebody else", the PrevAuth0 bookkeeping, the DLL on disk before the registry names it. A second copy of that logic in C# would drift from the original sooner or later, and then you'd have two install paths that disagree about what's safe.
Where the binaries come from
The obvious way to build a setup tool for code from GitHub is to have it download and build that code on the target machine. The first versions of Barista did that: a bootstrap.bat that installed git, the .NET SDK and the MSVC build tools through winget, cloned both repositories and built them.
It worked, but it built whatever the default branch was on that day. Nothing checked a commit, a hash or a signature. Anyone who took over the GitHub account or pushed a bad commit would have had code in lsass on every machine that ran the bootstrap afterwards. And the same bootstrap built different software on two different days without telling you. So now CI builds everything, and the exe brings it along.
payload.lock.json in the repository names the exact commits:
{
"doppio": {
"commit": "d61192f9e9a2432ab317b0e579a4894e657707f6"
},
"ristretto": {
"commit": "b0236d703d98d2f8d15aee0eac200f58dc1c19e4",
"doppioSubmodule": {
"commit": "37a827d19f741b671054020f01c5bb8a40efe706"
}
}
}
When I push a tag, GitHub Actions checks out Doppio and Ristretto at those commits, compiles them with MSVC and embeds the result in the exe together with a manifest that lists the SHA-256 of every file. The build script refuses to run if a checkout isn't at its pin or has local changes. The workflow ends with a draft release, so nothing goes public until I've looked at it. To move to newer Doppio or Ristretto commits, I change the pins and tag again.
Seventeen hashes before Continue
The first page after the risk checkboxes is called Integrity. It unpacks the 17 embedded files into %ProgramFiles%\TheAdminCafe\Barista\payload, a folder only administrators and SYSTEM can write to. Then it reads every file back from disk and compares its hash with the manifest.
You get one line per file with the expected hash above the one on disk, and a big result at the top. Continue stays greyed out unless all 17 match. Copy report puts the hashes on the clipboard in sha256sum format, so you can compare them with the payload-manifest.json on the release page yourself.
The check runs again after every reboot and before every stage, because the stages are spread over several reboots, and in between something else running as admin could replace a file. After a reboot Barista also ignores the paths in its own state file and uses the payload folder it just verified.
The manifest proves the files on disk are the ones CI built from the pinned commits. It doesn't prove the exe itself is the one I published. That's what SHA256SUMS on the release page is for, and in the long run a code signature. The Integrity page shows the exe's own hash and signature status next to the payload, and right now it says unsigned.
One change per reboot
I built Barista mainly for this part. The install runs in stages, and every stage that touches something LSA or LogonUI reads at boot ends with a reboot and a question.
After each restart Barista opens again on its own and asks whether the machine is healthy before it runs anything else. If you answer no, it stops, tells you what the last stage changed and offers the uninstall script that matches. Because there's only ever one new thing on the machine, you know which one it was.
Each position in that order has a reason:
- The tile goes first. It runs in LogonUI, not in lsass, and it's the one stage you can still undo from Safe Mode.
- Enrollment comes before any filter. The filter judges accounts by whether they have a secret, so they need one first. And
enroll /reindexhas to run before anything reads the RID index. - The provider filter comes before the network stages. This is the step that removes the password tile. While the network paths are still open, a broken tile still leaves you a way in over SMB or WinRM. The other way round, one bad tile means no way in at all, which is the locked machine from Part 1.
- The authentication package is last, because it closes nothing.
Stages 1 and 3 are two runs of the same install-doppio.ps1, once without and once with -EnableFilter. Before stage 3 Barista asks whether you've signed in through the tile yet. No registry read can tell, and it's the last moment the password tile can still get you in.
Stage 4 is where you pick how the network paths get closed, and there are three answers:
| Choice | What it does | Code in lsass |
|---|---|---|
| Deny rights | deny-noninteractive.ps1 sets the network, batch and service deny rights per account | none, no reboot |
| Doppio's TacSubAuth | refuses every network logon for an enrolled account, RDP with NLA included | yes, own reboot |
| The Ristretto gate | network logons work for a few minutes after a one-time code | yes, own reboot |
Deny rights are the supported way and the one I'd start with. The two filters are the interesting ones from Part 2 and Part 3, and for those the question after the reboot comes with a test. For Ristretto: open a gate with ristretto <user>, check that a share works, then ristretto /close <user> and check that it stops working. If it keeps working with the gate closed, MSV1_0 isn't calling the filter on that build and the gate is decorative. You want to find that out while the filter is the only new thing on the machine.
Between reboots Barista keeps a small record in %ProgramData%\TheAdminCafe\Barista\pending-run.json with the finished stages and the log so far, and a RunOnce value that opens the wizard after the next logon. The record holds no secrets, those stay DPAPI-encrypted in Doppio's own store. Barista writes it after every finished stage, including the ones without a reboot, so closing the window at the wrong moment can't run enroll.exe twice for the same account.
The break-glass admin
The accounts page asks two separate questions per account: give it a second factor, and close its non-interactive paths.
The stage 3 filter refuses to register while any enabled local account has no secret, so your break-glass admin has to be enrolled too. But deny-noninteractive.ps1 says in its own header to keep a second admin account that still works. If enrolling an account also denied its network logons, you'd have no way left onto the machine the day the tile breaks. So enrolled means the tile accepts it, and denying SMB, WinRM and RDP is a separate checkbox. The page warns you when your selection leaves nothing that can reach the machine without the tile.
What stays manual
Some things Barista can't or shouldn't do for you:
- The snapshot. A VM can't snapshot itself. Barista stops at the points where it matters and asks you to confirm you took one.
- Your phone.
enroll.exeprints anotpauth://URI, you paste it into the wizard and it draws the QR code locally, without sending the secret anywhere. Scanning it and typing the code is up to you. - Three risk checkboxes. You're about to turn off lsass protection and load an unsigned DLL into the logon path.
RunAsPPL. It has its own button and its own dialog, and it's only offered when you picked a component that loads into lsass. Turning it off makes the machine a lot easier to loot, so no install step does it as a side effect.
The risk page also shows what to do if the machine doesn't boot after the network stage: load the offline SYSTEM hive from WinRE and delete the Auth0 value. You see it before you need it.
reg load HKLM\OFF C:\Windows\System32\config\SYSTEM
reg delete HKLM\OFF\ControlSet001\Control\Lsa\MSV1_0 /v Auth0 /f
reg unload HKLM\OFF
Where it stands
The current version is 0.1.0, built from Doppio d61192f and Ristretto b0236d7. CI runs the test suite on every push: the stage order, stopping at the first failed step, saving every finished stage, arming RunOnce only when a reboot is pending, and the payload checks.
What it doesn't do yet:
- No code signature. SmartScreen will warn you, and the Integrity page says unsigned. Check
SHA256SUMS. - Not reproducible byte for byte. If you build it yourself, you get different hashes for the exe and the DLLs, because MSVC and .NET write timestamps into them.
- No unattended mode and no config file. It's a wizard, you click through it.
- Enrollment opens a console window.
enroll.exereads its prompts directly from the console, so it can't be driven from the wizard yet.
If you'd rather build everything yourself, the developer build is still there. bootstrap.bat fetches Doppio and Ristretto at the same pinned commits as the release, builds all three and opens the same wizard. A clone with local changes or a folder it doesn't recognise stops the run instead of being overwritten.
The README has everything else, including every flag of the bootstrap, what Barista writes to the machine itself and why, and how recovery works:
MIT licensed, like Doppio and Ristretto. If a stage leaves your VM in a state Barista doesn't recognise, I'd like to hear which stage it was and what the machine did.
Licensed under CC BY-NC-SA 4.0.
