Azure Functions Core Tools missing a binary

I’m using the azure-functions-core-tools package for .NET development and found good us of it.

The issue that I’ve bumped into is that the precompiled release of the package contains some more binaries, then what is being packed by NixOS.

I’ve tried to do a manual build of the NixOS package, and that only contains a single “func” binary, while the prebuild package contains:
./func
./in-proc6/func
./in-proc8/func

Can the package be adjusted or can I somehow use the precompiled binary?

I’ve manged to get some of the way with the precompiled binaries and this:

{ stdenv
, fetchzip
, lib
, autoPatchelfHook
, gcc
, gcc-unwrapped
, glibc
, glibc_multi
, pam
, zlib
, lttng-ust
, musl
, icu
, lttng-ust_2_12
, openssl
}:

stdenv.mkDerivation rec {
  pname = "azure-functions-cli-bin";
  version = "4.0.6821";

  src = fetchzip {
    url = "https://github.com/Azure/azure-functions-core-tools/releases/download/4.0.6821/Azure.Functions.Cli.linux-x64.4.0.6821.zip";
    sha256 = "sha256-YwOiXlfcV+3+LRKciIFz+PoLpJLb5FYNDydapM3kocg=";
    stripRoot = false;
  };

  nativeBuildInputs = [ autoPatchelfHook ];
  buildInputs = [
    gcc
    gcc-unwrapped
    glibc
    glibc_multi
    pam
    zlib
    lttng-ust
    musl
    icu
    icu.dev
    stdenv.cc.cc
    lttng-ust_2_12
    openssl
  ];

  installPhase = ''
    mkdir -p $out/usr/lib/azure-functions-cli-bin
    cp -r * $out/usr/lib/azure-functions-cli-bin
    chmod +x $out/usr/lib/azure-functions-cli-bin/func
    chmod +x $out/usr/lib/azure-functions-cli-bin/in-proc6/func
    chmod +x $out/usr/lib/azure-functions-cli-bin/in-proc8/func
    chmod a+x $out/usr/lib/azure-functions-cli-bin/gozip

    mkdir -p $out/usr/bin
    ln -s /usr/lib/azure-functions-cli-bin/func $out/usr/bin/func
  '';

  meta = with lib; {
    description = "Command line tools for Azure Functions";
    homepage = "https://github.com/Azure/azure-functions-core-tools";
    license = licenses.mit;
    maintainers = [ ];
    platforms = platforms.linux;
    longDescription = ''
      This package installs the Azure Functions CLI.
    '';
  };
}

But I’m still missing dependencies, that I haven’t managed to add - could anyone point me in the right direction:

   > error: auto-patchelf could not satisfy dependency libssl.so.1.0.0 wanted by /nix/store/gllzaq8c1xz53k23cr3z0xfysm0c6vh0-azure-functions-cli-bin-4.0.6821/usr/lib/azure-functions-cli-bin/workers/powershell/7/runtimes/linux-x64/native/libmi.so
   > error: auto-patchelf could not satisfy dependency libcrypto.so.1.0.0 wanted by /nix/store/gllzaq8c1xz53k23cr3z0xfysm0c6vh0-azure-functions-cli-bin-4.0.6821/usr/lib/azure-functions-cli-bin/workers/powershell/7/runtimes/linux-x64/native/libmi.so
   > error: auto-patchelf could not satisfy dependency libssl.so.1.0.0 wanted by /nix/store/gllzaq8c1xz53k23cr3z0xfysm0c6vh0-azure-functions-cli-bin-4.0.6821/usr/lib/azure-functions-cli-bin/workers/powershell/7.2/runtimes/linux-x64/native/libmi.so
   > error: auto-patchelf could not satisfy dependency libcrypto.so.1.0.0 wanted by /nix/store/gllzaq8c1xz53k23cr3z0xfysm0c6vh0-azure-functions-cli-bin-4.0.6821/usr/lib/azure-functions-cli-bin/workers/powershell/7.2/runtimes/linux-x64/native/libmi.so

You could use non-auto patchelf to update those dependencies to their 1.1 versions:

patchelf --replace-needed liboriginal.so.1 libreplacement.so.1 my-program

If you do that before the autoPatchelfHook runs you can still rely on it for any other dependencies.

This likely will not work in practice, I don’t think 1.0 and 1.1 are ABI compatible. Here’s a reasonable-looking compatibility chart: API/ABI changes review for OpenSSL

I think this program is just broken, I doubt you will be able to find a libssl1.0 in any reputable distro that hasn’t been EOL since like 2019.

Having looked at their upstream repo for a second, they seem to support running on distros with even openssl 3.0 packaged. Make sure you’re not using some ancient version of it.

I can see that all the dependencies are coming from some powershell related dependencies.

Could there be something that I could be inspired from here:

That binary is simply compiled against a modern libssl: nixpkgs/pkgs/by-name/po/powershell/package.nix at 7ffe0edc685f14b8c635e3d6591b0bbb97365e6c · NixOS/nixpkgs · GitHub

You should just grab a version of yours that is as well.

I’ve got it working (more) with:

  fixupPhase = ''
    echo "Patching ELF dependencies..."
    patchelf --replace-needed libcrypto.so.1.0.0 libcrypto.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7/runtimes/linux-x64/native/libmi.so
    patchelf --replace-needed libssl.so.1.0.0 libssl.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7/runtimes/linux-x64/native/libmi.so
    patchelf --replace-needed libcrypto.so.1.0.0 libcrypto.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7.2/runtimes/linux-x64/native/libmi.so
    patchelf --replace-needed libssl.so.1.0.0 libssl.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7.2/runtimes/linux-x64/native/libmi.so
  '';

Now I just need to figure out how to handle references to /bin/bash:

grep -r "/bin/bash" /nix/store/zvx877chpzhhjrkwba9d0k1j8k0gr92g-azure-functions-cli-bin-4.0.6821
grep: /nix/store/zvx877chpzhhjrkwba9d0k1j8k0gr92g-azure-functions-cli-bin-4.0.6821/usr/lib/azure-functions-cli-bin/func.dll: binary file matches
grep: /nix/store/zvx877chpzhhjrkwba9d0k1j8k0gr92g-azure-functions-cli-bin-4.0.6821/usr/lib/azure-functions-cli-bin/in-proc8/func.dll: binary file matches
grep: /nix/store/zvx877chpzhhjrkwba9d0k1j8k0gr92g-azure-functions-cli-bin-4.0.6821/usr/lib/azure-functions-cli-bin/in-proc6/func.dll: binary file matches

If tried patchShebang and substituteInPlace but no luck so far - but at least there is some progress

And if I symlink bash on the host its running…

buildFhsUserEnv is a nicer way to accomplish this. Chances are any ssl operations will segfault, though, but hey, maybe this just doesn’t use ssl much.

If anyone ends up in the need of the same package this is what I ended up with:

azure-functions-cli-bin.nix

{ stdenv
, fetchzip
, lib
, autoPatchelfHook
, gcc
, gcc-unwrapped
, glibc
, glibc_multi
, pam
, zlib
, lttng-ust
, musl
, icu
, lttng-ust_2_12
, openssl
, makeWrapper
}:

stdenv.mkDerivation rec {
  pname = "azure-functions-cli-bin";
  version = "4.0.6821";

  src = fetchzip {
    url = "https://github.com/Azure/azure-functions-core-tools/releases/download/${version}/Azure.Functions.Cli.linux-x64.${version}.zip";
    sha256 = "sha256-YwOiXlfcV+3+LRKciIFz+PoLpJLb5FYNDydapM3kocg=";
    stripRoot = false;
  };

  nativeBuildInputs = [ autoPatchelfHook ];
  buildInputs = [
    gcc
    gcc-unwrapped
    glibc
    glibc_multi
    pam
    zlib
    lttng-ust
    musl
    icu
    icu.dev
    stdenv.cc.cc
    lttng-ust_2_12
    openssl
    makeWrapper
  ];

  fixupPhase = ''
    echo "Patching ELF dependencies..."
    patchelf --replace-needed libcrypto.so.1.0.0 libcrypto.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7/runtimes/linux-x64/native/libmi.so
    patchelf --replace-needed libssl.so.1.0.0 libssl.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7/runtimes/linux-x64/native/libmi.so
    patchelf --replace-needed libcrypto.so.1.0.0 libcrypto.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7.2/runtimes/linux-x64/native/libmi.so
    patchelf --replace-needed libssl.so.1.0.0 libssl.so.1.1 $out/usr/lib/azure-functions-cli-bin/workers/powershell/7.2/runtimes/linux-x64/native/libmi.so
  '';

  installPhase = ''
    mkdir -p $out/usr/lib/azure-functions-cli-bin
    cp -r * $out/usr/lib/azure-functions-cli-bin
    chmod +x $out/usr/lib/azure-functions-cli-bin/func
    chmod +x $out/usr/lib/azure-functions-cli-bin/in-proc6/func
    chmod +x $out/usr/lib/azure-functions-cli-bin/in-proc8/func
    chmod a+x $out/usr/lib/azure-functions-cli-bin/gozip

    mkdir -p $out/bin
    ln -s $out/usr/lib/azure-functions-cli-bin/func $out/bin/func
  '';

  meta = with lib; {
    description = "Command line tools for Azure Functions";
    homepage = "https://github.com/Azure/azure-functions-core-tools";
    license = licenses.mit;
    maintainers = [ ];
    platforms = platforms.linux;
    longDescription = ''
      This package installs the Azure Functions CLI.
    '';
  };
}

configuration.nix

  environment.sessionVariables = {
    DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "1";
  };

  environment.systemPackages = with pkgs; [
    (callPackage ./azure-functions-cli-bin.nix {}  )
  ];

  services.envfs = {
    enable = true;
    extraFallbackPathCommands = ''
      ln -s ${pkgs.bash}/bin/bash $out/bash
    '';
  };

There may be better practices, but at least I’m able to do my job from nixos

Yeah, so, this’ll work, but you’ve now added a bash to your system path. This means that your system inherently leaks this to everything. If you e.g. write code that assumes a bash-compatible shell, you will only realize after deployment that you’re not POSIX-compatible.

It can also change the behavior of applications because the version of bash deployed now isn’t tied to your nix package.

I don’t think this is per-se wrong, but you’re actively removing one of the selling points of nix (which is fine. I’m not your mom). There is an alternative to that with practically no downsides, however, an fhs env:

  environment.systemPackages = let
    # You can also use `pkgs.buildFHSEnv` if cgroups cause issues
    azure = pkgs.buildFHSEnvBubblewrap {
      pname = "azure-functions-cli-bin";
      version = "4.0.6821";
      runScript = "func";
      targetPkgs = [
        # Bash is included in the env by default
        (callPackage ./azure-functions-cli-bin.nix { })
      ];
    };
  in [
    azure
  ];

Now you can run azure-functions-cli-bin to call your binary in an environment with bash available. Add extra packages as you need more fhs paths.

Under the hood this just writes a script that uses bubblewrap to launch a cgroup that partially decouples the filesystem so that in the cgroup the application sees bash under the hard-coded absolute path where it expects it to be. We refer to this as an “fhs env”, because it’s an fhs-compliant environment specific to this binary.


Having said all of that, you could also play around with the nix package. I’d attempt something like:

pkgs.azure-functions-core-tools.overrideAttrs (old: {
  executables = old.executables ++ [
    "in-proc6"
    "in-proc8"
  ];
})

Probably need to dig a lil’ into their build scripts to figure out how to properly expose that to buildDotnetModule though.