Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running from a read-only device messes up the autoupdate #638

Closed
Tracked by #896
terlar opened this issue Aug 12, 2020 · 8 comments
Closed
Tracked by #896

Running from a read-only device messes up the autoupdate #638

terlar opened this issue Aug 12, 2020 · 8 comments

Comments

@terlar
Copy link

terlar commented Aug 12, 2020

Hello I am trying to run this from a read-only device (e.g. nix store). Given how the autoupdate functionality is configured, setting the updatesDirectory based on the bin path you run into some weird issues. Does it make sense to be able to set the updatesDirectory as part of the configuration?

Or is my best bet to create a wrapper that copies over the executables during runtime into write-able location?

@directionless
Copy link
Contributor

I agree it's a bit weird. It was a tradeoff... When I implemented the current update behavior, I thought about whether to make a config flag. Or stash them in the root directory. But because the updates are invoked before the config and flags are parsed, I needed a more stable path. Thus, based on the binary.

I'm happy to rethink how this works, but there's some startup ordering that seems hard to get right.

@terlar
Copy link
Author

terlar commented Aug 26, 2020

I see, that certainly makes it a bit trickier. One way could be if it is invoked with an environment variable you could set the base path to be used. I guess it is also not super clean and you still run into the issue I guess you try to avoid (running an older version that doesn’t know about the new configuration yet).

I would greatly appreciate that, but I understand it is not a trivial thing. As for security purposes you also don’t want to use a temporary directory or similar.

@directionless
Copy link
Contributor

Answering this comment

We have two approaches forward that I can see:

  1. Disable autoupdates on NixOS.
  2. Support autoupdates on NixOS.

To be honest, I'm inclined towards (1). I understand the security benefits of autoupdating software (especially when it's all signed using TUF—very cool!) but this is in tension with the reproducibility goals of NixOS. I don't expect any upstream support for this, though.

Maybe the best thing to do is support both: in the NixOS configuration, users could choose between autoupdating using a slightly-cleaned-up version of the tricks mentioned earlier in this issue, or pinning their osquery.

Tangent: if you pin osquery, can we still get some of the benefits of TUF? It's possible, in theory: the updater could check that your pinned osquery corresponds to a valid Kolide-provided one on startup. In practice, however, I think this falls over: Nix would be building osquery from source, and probably linking against different libraries.

A couple of comments...

Whether or not auto update is enabled, depends on the underlying configuration flags. I could certainly imagine a world where a linux distribution packages osquery and launcher, and aside from that Kolide distributes the pair with a different setup. Distro maintainers often have different requirements than Kolide does.

For us, full OS support generally means solving the autoupdate issue. And I just don't know enough about Nix to know if there's a good solution. Or even a workable one. Surely there's somewhere for local data to live, how else would a database work?

I don't see much value in TUF if you're building from source. Neither osquery nor launcher builds are repeatable. Launcher includes timestamps, and I forget why osquery isn't.

@terlar
Copy link
Author

terlar commented Oct 14, 2022

Thank you for following up. I have personally for some other reason had to switch from NixOS (for now). But I did get this working in a quite hacky way at that time by wrapping the launcher to copy itself into a write-able path at runtime and then of course the auto-update will continue to work from there.

It is a quite messy package as the deb we got from the company contained some configuration. So the derivation included two sources. Here is the mess, in case it helps someone:

{ lib, stdenv, autoPatchelfHook, fetchzip, makeWrapper, dpkg, requireFile }:

let
  version = "0.11.12";
  maintainers = import ../../maintainers.nix;
  workDir = "/var/lib/kolide-k2";

  system = if stdenv.isLinux then
    "linux"
  else if stdenv.isDarwin then
    "darwin"
  else
    throw "kolide-launcher-k2 is not available for this platform";

  srcs = {
    deb = requireFile rec {
      name = "kolide-launcher.deb";
      url =
        "https://COMPANY-URL-TO-DEB-PACKAGE";
      message = ''
        This Nix expression requires that ${name} already be part of the store. To
        obtain it you need to

        - Download the deb file from ${url}
        - Add the file to the Nix store using:

          nix-store --add-fixed sha256 ${name}
      '';
      sha256 =
        "4a045b2a046dba5d6741a562d0fa4ab6e8a4f70c315d20beace0203499bb8868";
    };
    bin = fetchzip {
      url =
        "https://github.com/kolide/launcher/releases/download/v${version}/launcher_v${version}.zip";
      sha256 = "1a1icllds1cariinkb5sbrfrm4nrykrkmb2rafrrc72sykrqkw6m";
      stripRoot = false;
    };
  };

in stdenv.mkDerivation {
  pname = "kolide-launcher-k2";
  inherit version;

  src = srcs.deb;

  nativeBuildInputs = [ autoPatchelfHook dpkg makeWrapper ];

  dontBuild = true;
  dontConfigure = true;

  unpackPhase = "dpkg-deb -x $src .";

  postPatch = ''
    substituteInPlace lib/systemd/system/launcher.kolide-k2.service \
      --replace "/usr/local/kolide-k2/bin" "$out/bin"
    substituteInPlace lib/systemd/system/launcher.kolide-k2.service \
      --replace "/etc" "$out/etc"

    substituteInPlace etc/kolide-k2/launcher.flags \
      --replace "/usr/local/kolide-k2/bin" "${workDir}/bin"
    substituteInPlace etc/kolide-k2/launcher.flags \
      --replace "/etc" "$out/etc"
    substituteInPlace etc/kolide-k2/launcher.flags \
      --replace "/var/kolide-k2" "${workDir}"
  '';

  installPhase = ''
    mkdir -p $out $out/bin $out/wbin $out/etc $out/lib $out/share

    cp -r ${srcs.bin}/${system}/* $out/wbin/
    cp -r etc/* $out/etc/
    cp -r lib/* $out/lib/
    cp -r usr/share/* $out/share/

    cat > $out/bin/launcher <<EOF
    #! ${stdenv.shell} -e
    mkdir -p "${workDir}/bin"
    for i in $out/wbin/*; do
      [ -f "${workDir}/bin/\$(basename "\$i")" ] || cp "\$i" "${workDir}/bin/"
    done
    exec ${workDir}/bin/launcher "\$@"
    EOF
    chmod +x $out/bin/launcher
  '';

  meta = with lib; {
    homepage = "https://www.kolide.com/launcher";
    description = "Kolide Launcher for Osquery";
    license = licenses.mit;
    platforms = [ "x86_64-linux" ];
    maintainers = [ maintainers."terje.larsen" ];
  };
}

@directionless
Copy link
Contributor

Is there somewhere I can read about the writable path stuff? Assuming it's not no-exec, it wouldn't be hard to have launcher run the updates from it

@terlar
Copy link
Author

terlar commented Oct 14, 2022

I think most of the paths on NixOS should be writable or at least have the opportunity to make them writable, it is just the /nix/store where all packages are installed and is housing all the content of packages that is not writable. The NixOS system is usually symlinking the currently activated system parts into "normal" directories if needed.

A common pattern for write-able stuff needed during runtime is to have them under /var/lib/X.

@directionless
Copy link
Contributor

Oh. That should be tractable then.

@RebeccaMahany
Copy link
Contributor

This has been addressed with #896 and #954 -- the new autoupdate library lives outside of the bin path (i.e. in /var/..., outside of the nix store)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants