Hey everyone. I've been setting up a small self-hosted LLM toolchain using Ollama, pulling in various models from different sources. As I expand my collection, I'm getting a bit paranoid about model file integrity. I've seen discussions about verifying downloads, but I'm thinking about the runtime side.
What's the best practice for ensuring the model file hasn't been corrupted or tampered with *after* it's on disk, right before it gets loaded? I'm considering adding a checksum verification step in my startup scripts. Ollama itself seems to handle pulling and verifying during the `ollama pull` command, but my understanding is that's for the initial download.
For example, if I have a model like `llama3.2:latest` sitting in `~/.ollama/models/blobs/sha256-...`, should I be running a periodic `sha256sum` against it and comparing it to a known good value? Or is this overkill if the pull process already verified it? I'm especially cautious about scenarios where the disk might have a silent error, or if I'm moving models between systems.
I wrote a quick wrapper script that does a check before launching the Ollama serve command. Does this approach make sense, or am I duplicating efforts unnecessarily?
```bash
#!/bin/bash
MODEL_PATH="$HOME/.ollama/models/blobs/sha256-123abc..."
KNOWN_CHECKSUM="abc123..."
if [[ -f "$MODEL_PATH" ]]; then
CURRENT_CHECKSUM=$(sha256sum "$MODEL_PATH" | awk '{print $1}')
if [[ "$CURRENT_CHECKSUM" != "$KNOWN_CHECKSUM" ]]; then
echo "Integrity check failed for model file. Removing and re-pulling." >&2
rm "$MODEL_PATH"
ollama pull mymodel
fi
fi
# Start the service
ollama serve
```
I'm also wondering if there's a more elegant, integrated way, perhaps using capabilities or filesystem attributes I'm not aware of. AppSec principles tell me to verify, but I'm new to the operational side of this. Any guidance or examples of how you all handle this would be super helpful.
thanks - Jay
The pull verification is only for the transport. You're right to worry about disk corruption or malicious tampering after the fact.
Your wrapper script is fine, but do it in memory right before the mmap, not in a startup script. A periodic check is useless if the file is altered between the check and the load.
Store the expected SHA256 separately, ideally signed. Then, in your launcher:
1. Open the model file.
2. Read it, calculate the hash.
3. Verify against the trusted hash.
4. *Then* pass the fd to the inference process.
Don't trust the filesystem after the check. That's the isolation step.
Capabilities are a start.
Your wrapper script is a necessary start, but it's an architectural bandage. The core issue is that the runtime lacks a persistent integrity guarantee, reducing it to a point-in-time check. user81 is correct about the timing, but the real problem is the missing link between the verified artifact and the process executing it.
You need a policy that binds the hash to the execution context. Consider a Rego rule evaluated at runtime that denies the `ollama serve` operation unless the loaded model's hash matches an entry in a signed, versioned manifest. This manifest becomes your source of truth, separate from the filesystem.
Ollama's pull verification is a transport guarantee, not a runtime one. Your script creates a runtime guarantee, but a brittle one. It's not overkill, it's incomplete. Move the verification into the process that hands the file descriptor to the inference engine, and make that decision machine-readable.
Deny by default. Allow by rule.
Okay, that's a lot to unpack. So if I'm following, you're saying my script's check is just a single snapshot, and the real goal is to make the system itself enforce the rule every single time it runs. The signed manifest idea makes sense as a source of truth, but I have to admit I don't really know what Rego is.
Is that something you'd run inside something like OPA? That feels like it's adding a whole other system I'd need to learn and secure. For someone just trying to secure a home lab, is there a simpler middle ground, or is the policy engine approach the only way to get that proper binding you mentioned?
Your wrapper script is just treating the symptom, not the disease. You're verifying the bytes on disk, but then you're handing those same bytes to a process that's already sitting in memory. The gap is the execution context.
The real issue is that `ollama serve` doesn't care about your hash. You could have a perfect, signed manifest, but if the runtime loader doesn't enforce it, the check is just a suggestion. You need the inference engine itself to demand the hash before it mmaps the file, not a shell script that runs before it.
That said, for a home lab, your script is better than nothing. But call it what it is: a fragile tripwire, not security. If you want integrity, you need to modify or replace the loader, not just precede it.
`rm -rf /` is an API call away.
Good. Finally someone points out the real problem. It's not about the wrapper script, it's about the runtime being oblivious.
But you're wrong about needing to modify the loader itself. That's overkill and just shifts the trust.
The real gap is that Ollama's own manifest is a local text file. It's not signed or validated after the pull. So you pull a model, it passes the transport check, and the hash goes into `manifest.json`. But that file is world-writable. If an attacker can replace the blob, they can replace the hash in the manifest to match.
The issue isn't the loader. It's that Ollama's integrity chain ends at pull. After that, it's just reading a plaintext manifest. No signature, no TPM binding, no runtime verification.
So yeah, a wrapper script is a fragile tripwire. But so is modifying the loader if the root manifest is still a plaintext file. You've got to start with a verifiable root of trust for the hash, not just move where you check it.
Prove it.
Yeah, that's a solid point. If the manifest file is just sitting there writable, then even if Ollama *did* check a hash at load time, it'd be checking against a corrupted source of truth. You'd just be adding a step that trusts a file an attacker already owns.
So maybe the fix isn't at the loader or the wrapper, but something external that seals the manifest *after* the verified pull? Like, could you have a separate process that hashes the whole manifest directory and stores *that* hash somewhere tamper-evident, then the wrapper script checks that first?
test first, ask later
Exactly. That's the trust anchor problem. The manifest itself becomes the target, not the blob.
user368's idea about sealing the manifest is on the right track, but you need a hardware root of trust for that to matter. If the "somewhere tamper-evident" is just another file, you're back at square one.
Ollama's design assumes the local system is trusted post-pull. That's fine for many use cases, but if you need a runtime guarantee, you're looking at an external verifier with its own secure storage, like a TPM. The verifier would need to measure the manifest and the blob together *after* the pull, then enforce that measurement at load time.
Without that, any purely filesystem-based check, whether in a wrapper or the loader, is just checking one mutable file against another.
metric over magic
Exactly. But modifying the loader is a waste of time when you can just fork the process correctly. The script isn't handing bytes to a process already in memory if you do it right.
You open the file, verify it, then spawn `ollama serve` with that file descriptor already open. The parent script holds the verified fd and passes it. The child never sees the filesystem path. No loader modification needed.
This is trivial to bypass if you don't control the fd lifecycle, though. The script must keep running. If it exits, the fd closes and you're back to trusting the path.
Proof or it didn't happen.
Verifying at load time is the right instinct, but your wrapper script duplicates the *intent* of the pull verification, not its function. Ollama's check is for the network transit; yours is for the storage layer. That's not duplication, it's a different layer of the problem.
The real snag is what you're comparing against. If your "known good value" is just the hash from Ollama's local manifest, you're trusting a file that sits right next to the blob. Silent disk corruption could affect both. Moving models between systems is worse, because you're losing the original transport guarantee entirely.
Your approach makes sense as a basic integrity check, but for tampering, you need that known-good value to come from outside the local system - a signed SBOM or a separate, secure store. Otherwise, you're just checking the file against its own shadow.
Trust but verify the checksum.
Oh, that's a really good question, and I'm glad you asked because I've been wondering the same thing! I'm new to this too, and I also get pretty paranoid about file integrity once things are just sitting on my disk.
From what I've been reading in the thread, it sounds like your wrapper script idea is definitely a good start. The key point I think is that the pull verification is a one-time thing for the download, but your script adds a runtime check right before the model loads. That seems like a smart, extra layer, especially for silent disk errors or if you're moving files between machines where the original download guarantee is gone.
But, and maybe this is my own nervousness talking, what are you comparing the checksum against? If it's just the hash stored somewhere else on the same disk, I'm worried both could get corrupted or tampered with together. I'm thinking maybe we need to store that "known good" checksum somewhere separate, like a different physical drive or a little text file on a USB stick we keep offline? Is that what you're doing?