Giter VIP home page Giter VIP logo

nix-buildproxy's Introduction

Nix Buildproxy

Providing reproducible HTTP/HTTPS responders to builds that just can not live without.

Motivation

When building Nix packages in the sandbox, internet access is usually not available. However, some packages insist on loading content from the internet. The motivation to build this tool came from a CMake build that loaded additional cmake-files that, in turn, would trigger further package downloads.

Unwilling to go through multi-level patching during the build, I wondered if it's possible to capture, nixify, and later serve HTTP/HTTPS requests from the Nix store to create an escape hatch when the proper solution is just too much effort. Turns out, this is possible, with some caveats.

Usage

A quick example on how this package works with the included example/evil_build.sh example.

Overlay

The flake provides an overlay that will make the buildproxy-capture package available as well as extend lib to contain lib.mkBuildproxy. It is recommended to use this overlay. For example, when importing nixpkgs in a flake, the overlay is applied as follows:

{
    inputs = {
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
        nix-buildproxy.url = "github:polygon/nix-buildproxy";
    };

    outputs = inputs@{ self, nixpkgs, nix-buildproxy, ... }:
    let
        system = "x64_64-linux";
        pkgs = import nixpkgs {
            inherit system;
            overlays = [ nix-buildproxy.overlays.default ];
        };
    in
    {
        ...
    }
}

Capturing requests

Before starting to bring in nix-buildproxy, you should be able to build your project (e.g. in a devShell) and downloads during the build are preventing a proper sandboxed nix build. Run buildproxy-capture by either adding the buildproxy-capture program to your environment or directly through nix run github:polygon/nix-buildproxy#buildproxy-capture. This will launch mitmproxy and a subshell that has HTTPS_PROXY and HTTP_PROXY set. This is fine for CMake, since it respects these variables and does not check certificates. Other build systems might require more convincing.

Then, run your build and exit the subshell when done. This will generate a proxy_content.nix file with all the requests.

Here is how the session might look like:
nixbrett ➜ nix/nix-buildproxy/example (main ✗) buildproxy-capture
Entering proxy capture shell, run your build now, exit shell when done
nixbrett ➜ nix/nix-buildproxy/example (main ✗) ./evil_build.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1237  100  1237    0     0   2811      0 --:--:-- --:--:-- --:--:--  2817
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   165  100   165    0     0    553      0 --:--:-- --:--:-- --:--:--   551
nixbrett ➜ nix/nix-buildproxy/example (main ✗) <Ctrl+D>
Saving captured requests to proxy_content.nix
nixbrett ➜ nix/nix-buildproxy/example (main ✗) cat proxy_content.nix
{ fetchurl }: [
  {
    url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/package.nix";
    file = fetchurl {
      url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/package.nix";
      hash = "sha256-dFkeANLBJW1FWfL0d8ciS4siWP7B4z0vGsj9revgWGw=";
    };
  }
  {
    url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/test.nix";
    file = fetchurl {
      url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/test.nix";
      hash = "sha256-fg+tJQ4+U2G/9lqvOnakIJ2VBgKJoteewT2LHUV6sP4=";
    };
  }
]

Replaying responses

In order to reply to responses, you need to create a buildproxy recipe that serves your proxy_content.nix. You can use lib.mkBuildproxy <path-to-proxy_content.nix> for this. To enable the buildproxy in your build, run source ${buildproxy} early in your build (before any downloads are attempted, prePatch is a good candidate). This will start mitmproxy in replay mode and set the HTTP_PROXY and HTTPS_PROXY variables. A basic scaffold:

{ stdenv, lib, ... }:
let
  buildproxy = lib.mkBuildproxy ./proxy_content.nix;
in
stdenv.mkDerivation {
    # ...
    prePatch = ''
      source ${buildproxy}
    '';
    # ...
}

How it works

nix-buildproxy uses mitmproxy under the hood to do the heavy lifting of providing local proxy functionality. Python addons are used to intercept requests and will either create the proxy content library or serve it. Building the proxy content library works as follows:

sequenceDiagram
    participant client
    participant mitmproxy
    participant upstream
    participant inventory
    client->>mitmproxy: Request
    mitmproxy->>upstream: Upstream Request
    upstream->>mitmproxy: Response
    mitmproxy->>inventory: Store URL / Hash
    mitmproxy->>client: Response

During replay, operation looks like this:

sequenceDiagram
    participant client
    participant mitmproxy
    participant inventory
    client->>mitmproxy: Request
    mitmproxy->>inventory: Lookup
    inventory->>mitmproxy: Nix Store Path
     mitmproxy->>client: Response

Compatibility / Challenges

This package was originally built for and works out of the box with CMake. CMake respects the HTTP_PROXY/HTTPS_PROXY environment variables and by default ignores certificate errors. If you are using a different tool, you need to figure out how to configure the proxy server and how to tell the tool to accept the self-signed certificate of mitmproxy.

mitmproxy will load responses full into memory, I have not yet found out if streaming from/to disk is possible. If this is being used to serve large files, expect RAM usage of at least the file size, possibly several times that.

HTTP redirects are currently not properly handled. The resulting proxy_content.nix will contain the original request with a hash for an empty response and the redirected request with the actual hash separately. You can fix this issue by copying the final sha256 to the entry that redirects to it. This will be properly addressed in a future update.

You can modify proxy_content.nix to deliver different files. To make builds more stable, it is recommended to replace requests to, e.g., the moving main branch of a project to a concrete commit hash. Otherwise, future builds might experience checksum failures. This can also be used as an effective patching mechanism but there is currently no support built in.

Open issues / Roadmap

  • Properly handle HTTP redirects: Undecided whether to replay the redirect or whether to deliver the resulting file immediately, the latter breaking in case the client modifies the request
  • Properly handle non-success HTTP status codes in general
  • Framework for patching of downloaded files

nix-buildproxy's People

Contributors

polygon avatar

Stargazers

Sefa Eyeoglu avatar Matthew Croughan avatar Julian Stecklina avatar Mars avatar Christian Kögler avatar Ilia Rodionov avatar Tomasz Maciosowski avatar Marc Jakobi avatar Silvan Mosberger avatar Marius Bergmann avatar Oleg Pykhalov avatar Sebastian Eydam avatar Mohamed Laradji avatar Zacchary Dempsey-Plante avatar éclairevoyant avatar Kiara Grouwstra avatar Fabian avatar Doron Behar avatar Zach avatar  avatar Phani Rithvij avatar  avatar  avatar edef avatar Sumin Son avatar  avatar Artem Nistratov avatar khaled agrama avatar Alex Jackson avatar ajs124 avatar Jake Hamilton avatar Georges avatar ElXreno avatar  avatar Nikodem Rabuliński avatar Federico Damián Schonborn avatar Karim Vergnes avatar Saroj Mahato avatar Guilhem Saurel avatar Brad avatar Robert K. Bell avatar Leandro Emmanuel Reina Kiperman avatar Thilo Billerbeck avatar h7x4 avatar Zhong Jianxin avatar Sandro avatar Philip Taron avatar Andrey Vlasov avatar Nestor Liao avatar Felix avatar arbadacarba avatar Benno Bielmeier avatar  avatar Daniel Karapishchenko avatar Daniel Phan avatar Alexey Debelov avatar Benjamin Staffin avatar Sebastian Wålinder avatar Albin Vass avatar Cody Schafer avatar  avatar j-k avatar Paul Meyer avatar Samuel Gräfenstein avatar Arvin Hsu avatar Kayla Firestack avatar Shayon avatar Ryan Lahfa avatar Robert James Hernandez avatar Daniel Kahlenberg avatar John Murray avatar Masanori Ogino avatar Someone avatar  avatar ocfox avatar Terje Larsen avatar Mr Snake avatar Ryan Yin avatar  avatar Ujp8LfXBJ6wCPR avatar Brian McGee avatar Hawtian Wang avatar  avatar Ryan Cao avatar Astro avatar Vanilla avatar Travis A. Everett avatar  avatar

Watchers

 avatar  avatar  avatar

nix-buildproxy's Issues

Proper handling of HTTP headers and status codes

Currently, several cases that are pretty common are not handled properly by the proxy. The most glaring issue is that HTTP-redirects are not working automatically and require manual editing of the resulting proxy content. Instead of just logging the URI and Hash, we need to log some more response details.

Requests with the following HTTP status codes are stored, including (if present) body content:

  • 200
  • 301, 302, 307, 308

Requests that result in other status codes are not logged and during replay phase will result in the default 404.

The following HTTP headers are stored:

  • content-type
  • location
  • content-length
  • content-disposition

The idea being to find a good balance between amount of stored data and how accurate the session is replayed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.