List all installed packages+version for audit purposes

For “security & audit purposes” I’m required to submit a list of packages through an MDM solution which uses osquery so I’m going to try to take a shot at implementing nix_packages virtual table.

For the intents of this endeavor I assume that “package” is a derivation which comes from nixpkgs or other “repository” (including flakes) and its output(s) is something akin to a traditional “package” (deb, rpm, etc.) as this is how other virtual package tables work in osquery.

However, it is far from trivial to identify which derivations result in actual packages.

I’ve tried multiple approaches and I achieved what seems like the best results with

  • nix-store --query --requisites <gc-roots...>
  • sqlite3 -readonly /nix/var/nix/db/db.sqlite "select distinct deriver from ValidPaths;"

The nix-store one includes quite a lot of non-packages (confs, *sh_completions, dangling sources, systemd units, scripts, etc.)

The nix db one seems to include drvs which mostly seem to be the recipes for what I consider a “package”. It contains less “non-packages” than the nix-store approach.

however, it contains a lot of drv paths which do not exist in /nix/store which can trigger a lot of false positives.

Because of this, additional test are required.

Currently I still prefer the nix db approach as the drvs contain enough metadata to reasonably assume whether they refer to a “package” or a “non-package” but I’m worried about the nonexistent paths and also whether the list is in fact exhaustive.

“Reasonable assumption” above means the derivation contains env.pname and/or env.version but I’m not sure this can be relied upon 100 %.

I couldn’t find a better approach tho.

Is there a “good-enough” solution? It’s better to have a few false positives than any false negatives.

1 Like

Have a look at GitHub - tiiuae/sbomnix: A suite of utilities to help with software supply chain challenges on nix targets or GitHub - nikstur/bombon: Nix CycloneDX Software Bills of Materials (SBOMs) - there is a bit more discussion at How to do vulnerability scanning with Nix SBOMS?

1 Like

thanks, good stuff. Seems like they figured out a similar approach at guessing what is a “package” but they suffer from similar issues as well. In order to implement the osquery table I’d basically have to reimplement their internal logic in c++ which is well beyond my abilities

however, I took the liberty of bringing this to Nix as Nix itself is probably the best place to provide this information in a prepared and queryable form: Provide SBOM information in an "queryable" way · Issue #13564 · NixOS/nix · GitHub

For an MDM, you need to build a reverse map from .drv to meta attributes and retain only .drv containing an adequate meta attribute set which refers to a package.

Nixpkgs does not always tag those precisely, but this should be enough to shake off all the trivial derivations you do not care about.

1 Like

however, it contains a lot of drv paths which do not exist in /nix/store which can trigger a lot of false positives.

This is because gcroots are not added for derivations by default. Only for the out paths. I think you can tweak this behaviour by setting keep-derivations = true in nix config

You might be interested in

stdenv: allow preserving meta fields in derivation by Princemachiavelli · Pull Request #420575 · NixOS/nixpkgs · GitHub which aims to store more meta info in derivations so the data is less fuzzy. There’s even the idea to store the meta info in the out paths which means it would also work when .drvs get garbage collected.

We have a similar MDM setup at work that builds on top of osquery so i’m sort of interested to get this working as well.

I see, another different approach to solve the underlying issue - provide proper metadata :slight_smile:

It’s good to see people think of different ways to get there rather than just hacking around existing stuff.

Now the question is which of the solutions will make it to upstream. Until then I’m worried it doesn’t make much sense to put the effort in the osquery table :frowning: