Old Ruby on modern Nix

The other day I had to deploy an old Ruby 2.7 application. As I've recently started experimenting with NixOS I used this as an opportunity to figure out how to reliably and consistently deploy this application. Along the way I had to figure out a couple of things and the available Nix documentation was either outdated or things didn't work as specified.

A dev shell for old Ruby

To get started all I wanted was a dev shell with the right Ruby version available. So I started with ruby-nix:

nix flake init -t github:inscapist/ruby-nix/main

That got me a basic flake.nix installing Ruby 3.2. I trimmed that down just slightly and swapped in Ruby 2.7 (here's the list of available Ruby version):

{
  description = "An old Ruby application";

  nixConfig = {
    extra-substituters = "https://nixpkgs-ruby.cachix.org";
    extra-trusted-public-keys =
      "nixpkgs-ruby.cachix.org-1:vrcdi50fTolOxWCZZkw0jakOnUI1T19oYJ+PRYdK4SM=";
  };

  inputs = {
    nixpkgs.url = "nixpkgs";
    ruby-nix = {
      url = "github:inscapist/ruby-nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    fu.url = "github:numtide/flake-utils";
    bob-ruby = {
      url = "github:bobvanderlinden/nixpkgs-ruby";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, fu, ruby-nix, bob-ruby }:
    with fu.lib;
    eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          overlays = [ bob-ruby.overlays.default ];
        };
        rubyNix = ruby-nix.lib pkgs;
        ruby = pkgs."ruby-2.7.8";
      in rec {
        inherit (rubyNix {
          inherit ruby;
          name = "old-ruby-app";
        })
          env;

        devShells = rec {
          default = dev;
          dev = pkgs.mkShell {
            buildInputs = [ env ];
          };
        };
      });
}

The important line is this:

ruby = pkgs."ruby-2.7.8";

pkgs contains the bob-ruby overlay and so the previously listed Ruby versions are all directly available.

Now on to installing it into a shell:

nix develop

And boom:

error: Package ‘openssl-1.1.1u’ in /nix/store/b1l1kkp1g07gy67wglfpwlwaxs1rqkpx-source/pkgs/development/libraries/openssl/default.nix:210 is marked as insecure, refusing to evaluate.


Known issues:
 - OpenSSL 1.1 is reaching its end of life on 2023/09/11 and cannot be supported through the NixOS 23.05 release cycle. https://www.openssl.org/blog/blog/2023/03/28/1.1.1-EOL/

You can install it anyway by allowing this package, using the
following methods:

a) To temporarily allow all insecure packages, you can use an environment
   variable for a single invocation of the nix tools:

     $ export NIXPKGS_ALLOW_INSECURE=1

 Note: For `nix shell`, `nix build`, `nix develop` or any other Nix 2.4+
 (Flake) command, `--impure` must be passed in order to read this
 environment variable.

b) for `nixos-rebuild` you can add ‘openssl-1.1.1u’ to
   `nixpkgs.config.permittedInsecurePackages` in the configuration.nix,
   like so:

     {
       nixpkgs.config.permittedInsecurePackages = [
         "openssl-1.1.1u"
       ];
     }

c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
   ‘openssl-1.1.1u’ to `permittedInsecurePackages` in
   ~/.config/nixpkgs/config.nix, like so:

     {
       permittedInsecurePackages = [
         "openssl-1.1.1u"
       ];
     }

That's ... unfortunate, but at least it tells us what to do:

NIXPKGS_ALLOW_INSECURE=1 nix develop --impure

And that works:

$ NIXPKGS_ALLOW_INSECURE=1 nix develop --impure
(nix:nix-shell-env)
; ruby -v
ruby 2.7.8p225 (2023-03-30 revision 1f4d455848) [arm64-darwin22]

Partial success!

A failed attempt to deploy this on NixOS

I set up a server running NixOS recently and wanted to deploy something like the above to it. So I added my custom flake as an input and then references it in my configuration.nix.

Lo and behold, it failed with an error message like the one above. But hey, option b) in there said what to do:

{
 nixpkgs.config.permittedInsecurePackages = [
   "openssl-1.1.1u"
 ];
}

Except no matter where, when and how I added this in my configuration it did not work. I spent a couple of hours trying out every solution I could find on the internet. None of them worked and neither the error messages nor the documentation could clear this up.

Failure.

The hack: Ignoring vulnerabilities

Vulnerabilities in nixpkgs are declared by adding a meta.knownVulnerabilities list. That's what triggers the error above and the erorr message includes the custom description. For OpenSSL 1.1 this Vulnerability is declared in pkgs/development/libraries/openssl/default.nix.

Knowing this I later stumbled upon a StackOverflow question: Nix(OS): Set "permittedInsecurePackages" only for one package build (in an overlay?), which had an answer: Patch the package to just contain an empty knownVulnerabilities list.

So I changed my flake.nix in the Ruby app to override that just for the Ruby version:

ignoringVulns = x: x // { meta = (x.meta // { knownVulnerabilities = []; }); };
ruby = pkgs."ruby-2.7.8".override {
  openssl = pkgs.openssl_1_1.overrideAttrs ignoringVulns;
};

And now it works without any issues:

$ nix develop
(nix:nix-shell-env)
; ruby -v
ruby 2.7.8p225 (2023-03-30 revision 1f4d455848) [arm64-darwin22]

No more changes to my NixOS configuration required. Only this particular flake overrides the openssl version and that's good enough for this deployment.

Success.