├── .envrc ├── .gitignore ├── LICENSE ├── README.md ├── example ├── devshell.nix ├── evil_build.sh ├── example.nix └── proxy_content.nix ├── flake.lock ├── flake.nix ├── nix-buildproxy ├── build-content.nix ├── build_cache.py ├── buildproxy-capture.nix ├── buildproxy-shell.nix ├── buildproxy.nix ├── confdir │ ├── mitmproxy-ca-cert.cer │ ├── mitmproxy-ca-cert.p12 │ ├── mitmproxy-ca-cert.pem │ ├── mitmproxy-ca.p12 │ ├── mitmproxy-ca.pem │ └── mitmproxy-dhparam.pem ├── deliver.py └── devshell.nix └── proxy_content.nix /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv 2 | __pycache__ 3 | result -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jan Dohl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nix Buildproxy 2 | 3 | Providing reproducible HTTP/HTTPS responders to builds that just can not live without. 4 | 5 | ## Motivation 6 | 7 | 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. 8 | 9 | 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. 10 | 11 | ## Usage 12 | 13 | A quick example on how this package works with the included `example/evil_build.sh` example. 14 | 15 | ### Overlay 16 | 17 | 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: 18 | 19 | ```nix 20 | { 21 | inputs = { 22 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 23 | nix-buildproxy.url = "github:polygon/nix-buildproxy"; 24 | }; 25 | 26 | outputs = inputs@{ self, nixpkgs, nix-buildproxy, ... }: 27 | let 28 | system = "x64_64-linux"; 29 | pkgs = import nixpkgs { 30 | inherit system; 31 | overlays = [ nix-buildproxy.overlays.default ]; 32 | }; 33 | in 34 | { 35 | ... 36 | } 37 | } 38 | ``` 39 | 40 | ### Capturing requests 41 | 42 | 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. 43 | 44 | Then, run your build and exit the subshell when done. This will generate a `proxy_content.nix` file with all the requests. 45 | 46 |
47 | Here is how the session might look like: 48 | 49 | ```bash 50 | nixbrett ➜ nix/nix-buildproxy/example (main ✗) buildproxy-capture 51 | Entering proxy capture shell, run your build now, exit shell when done 52 | nixbrett ➜ nix/nix-buildproxy/example (main ✗) ./evil_build.sh 53 | % Total % Received % Xferd Average Speed Time Time Time Current 54 | Dload Upload Total Spent Left Speed 55 | 100 1237 100 1237 0 0 2811 0 --:--:-- --:--:-- --:--:-- 2817 56 | % Total % Received % Xferd Average Speed Time Time Time Current 57 | Dload Upload Total Spent Left Speed 58 | 100 165 100 165 0 0 553 0 --:--:-- --:--:-- --:--:-- 551 59 | nixbrett ➜ nix/nix-buildproxy/example (main ✗) 60 | Saving captured requests to proxy_content.nix 61 | nixbrett ➜ nix/nix-buildproxy/example (main ✗) cat proxy_content.nix 62 | { fetchurl }: [ 63 | { 64 | url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/package.nix"; 65 | file = fetchurl { 66 | url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/package.nix"; 67 | hash = "sha256-dFkeANLBJW1FWfL0d8ciS4siWP7B4z0vGsj9revgWGw="; 68 | }; 69 | } 70 | { 71 | url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/test.nix"; 72 | file = fetchurl { 73 | url = "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/test.nix"; 74 | hash = "sha256-fg+tJQ4+U2G/9lqvOnakIJ2VBgKJoteewT2LHUV6sP4="; 75 | }; 76 | } 77 | ] 78 | ``` 79 |
80 | 81 | ### Replaying responses 82 | In order to reply to responses, you need to create a buildproxy recipe that serves your `proxy_content.nix`. You can use `lib.mkBuildproxy ` 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: 83 | 84 | ```nix 85 | { stdenv, lib, ... }: 86 | let 87 | buildproxy = lib.mkBuildproxy ./proxy_content.nix; 88 | in 89 | stdenv.mkDerivation { 90 | # ... 91 | prePatch = '' 92 | source ${buildproxy} 93 | ''; 94 | # ... 95 | } 96 | ``` 97 | 98 | ## How it works 99 | 100 | `nix-buildproxy` uses [mitmproxy](https://mitmproxy.org/) 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: 101 | 102 | ```mermaid 103 | sequenceDiagram 104 | participant client 105 | participant mitmproxy 106 | participant upstream 107 | participant inventory 108 | client->>mitmproxy: Request 109 | mitmproxy->>upstream: Upstream Request 110 | upstream->>mitmproxy: Response 111 | mitmproxy->>inventory: Store URL / Hash 112 | mitmproxy->>client: Response 113 | ``` 114 | 115 | During replay, operation looks like this: 116 | 117 | ```mermaid 118 | sequenceDiagram 119 | participant client 120 | participant mitmproxy 121 | participant inventory 122 | client->>mitmproxy: Request 123 | mitmproxy->>inventory: Lookup 124 | inventory->>mitmproxy: Nix Store Path 125 | mitmproxy->>client: Response 126 | ``` 127 | 128 | ## Compatibility / Challenges 129 | 130 | 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`. 131 | 132 | `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. 133 | 134 | 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. 135 | 136 | 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. 137 | 138 | ## Open issues / Roadmap 139 | 140 | * [ ] 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 141 | * [ ] Properly handle non-success HTTP status codes in general 142 | * [ ] Framework for patching of downloaded files 143 | -------------------------------------------------------------------------------- /example/devshell.nix: -------------------------------------------------------------------------------- 1 | { mkShell, buildproxy-capture, curl }: 2 | mkShell { 3 | buildInputs = [ 4 | curl 5 | buildproxy-capture 6 | ]; 7 | } -------------------------------------------------------------------------------- /example/evil_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Simulate some stuff insisting on downloads 4 | curl https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/package.nix > package.txt 5 | curl https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/test.nix > test.txt -------------------------------------------------------------------------------- /example/example.nix: -------------------------------------------------------------------------------- 1 | { stdenv, curl, buildproxy }: 2 | stdenv.mkDerivation (final: { 3 | name = "example"; 4 | src = ./.; 5 | 6 | nativeBuildInputs = [ buildproxy curl ]; 7 | 8 | preConfigure = '' 9 | source ${buildproxy} 10 | ''; 11 | 12 | postBuild = '' 13 | mkdir -p $out 14 | cd $out 15 | bash ${final.src}/evil_build.sh 16 | ''; 17 | }) -------------------------------------------------------------------------------- /example/proxy_content.nix: -------------------------------------------------------------------------------- 1 | { fetchurl }: [ 2 | { 3 | url = 4 | "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/package.nix"; 5 | file = fetchurl { 6 | url = 7 | "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/package.nix"; 8 | hash = "sha256-dFkeANLBJW1FWfL0d8ciS4siWP7B4z0vGsj9revgWGw="; 9 | }; 10 | status_code = 200; 11 | headers = { 12 | "content-type" = "text/plain; charset=utf-8"; 13 | "content-length" = "1237"; 14 | }; 15 | } 16 | { 17 | url = 18 | "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/test.nix"; 19 | file = fetchurl { 20 | url = 21 | "https://raw.githubusercontent.com/NixOS/nixpkgs/ba563a6ec1cd6b3b82ecb7787f9ea2cb4b536a1e/pkgs/by-name/he/hello/test.nix"; 22 | hash = "sha256-fg+tJQ4+U2G/9lqvOnakIJ2VBgKJoteewT2LHUV6sP4="; 23 | }; 24 | status_code = 200; 25 | headers = { 26 | "content-type" = "text/plain; charset=utf-8"; 27 | "content-length" = "165"; 28 | }; 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1708118438, 6 | "narHash": "sha256-kk9/0nuVgA220FcqH/D2xaN6uGyHp/zoxPNUmPCMmEE=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "5863c27340ba4de8f83e7e3c023b9599c3cb3c80", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Nix Buildproxy"; 3 | 4 | inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 5 | 6 | outputs = { self, nixpkgs }: 7 | let 8 | system = "x86_64-linux"; 9 | pkgs = import nixpkgs { 10 | inherit system; 11 | overlays = [ 12 | (prev: final: { 13 | mitmproxy = final.mitmproxy.overrideAttrs (oldAttrs: { 14 | propagatedBuildInputs = oldAttrs.propagatedBuildInputs ++ [ 15 | final.python3Packages.httpx 16 | ]; 17 | }); 18 | }) 19 | ]; 20 | }; 21 | mkContent = proxy-content-file: pkgs.callPackage ./nix-buildproxy/build-content.nix { proxy_content = (import proxy-content-file); }; 22 | mkBuildproxy = self.lib.${system}.mkBuildproxy; 23 | mkBuildproxyShell = self.lib.${system}.mkBuildproxyShell; 24 | in 25 | { 26 | lib.${system} = { 27 | mkBuildproxy = proxy-content-file: (pkgs.callPackage ./nix-buildproxy/buildproxy.nix { inherit self; content = mkContent proxy-content-file; }); 28 | mkBuildproxyShell = proxy-content-file: (pkgs.callPackage ./nix-buildproxy/buildproxy-shell.nix { inherit self; content = mkContent proxy-content-file; }); 29 | }; 30 | packages.${system} = { 31 | example = pkgs.callPackage ./example/example.nix { buildproxy = mkBuildproxy ./example/proxy_content.nix; }; 32 | buildproxy-capture = pkgs.callPackage ./nix-buildproxy/buildproxy-capture.nix { inherit self; }; 33 | }; 34 | 35 | devShells.${system} = { 36 | example = pkgs.callPackage ./example/devshell.nix { buildproxy-capture = self.packages.${system}.buildproxy-capture; }; 37 | nix-buildproxy = pkgs.callPackage ./nix-buildproxy/devshell.nix { 38 | inherit (self.packages.${system}) buildproxy-capture; 39 | buildproxy-shell = mkBuildproxyShell ./proxy_content.nix; 40 | }; 41 | default = self.devShells.${system}.nix-buildproxy; 42 | }; 43 | 44 | overlays.default = (final: prev: { 45 | buildproxy-capture = self.packages.${system}.buildproxy-capture; 46 | lib = prev.lib // { 47 | inherit mkBuildproxy; 48 | inherit mkBuildproxyShell; 49 | }; 50 | }); 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /nix-buildproxy/build-content.nix: -------------------------------------------------------------------------------- 1 | 2 | { fetchurl, writeTextFile, proxy_content ? [] }: 3 | let 4 | content = builtins.map ( 5 | file: 6 | { 7 | inherit (file) url status_code headers; 8 | file = if (builtins.isNull) file.file then null else "${file.file}"; 9 | } 10 | ) (proxy_content { inherit fetchurl; }); 11 | in 12 | writeTextFile { 13 | name = "proxy_content.json"; 14 | text = builtins.toJSON content; 15 | } 16 | -------------------------------------------------------------------------------- /nix-buildproxy/build_cache.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from io import StringIO 3 | import json 4 | import httpx 5 | 6 | from mitmproxy import http 7 | 8 | 9 | class BuildCache: 10 | CACHED_RESPONSES = [ 200, 301, 302, 307, 308 ] 11 | REDIRECT_RESPONSES = [ 301, 302, 307, 308 ] 12 | CACHED_HEADERS = [ 13 | "content-type", 14 | "location", 15 | "content-length", 16 | "content-disposition" 17 | ] 18 | 19 | def __init__(self) -> None: 20 | self.cache = [] 21 | pass 22 | 23 | async def response(self, flow: http.HTTPFlow) -> None: 24 | print(f"Request received - URL: {flow.request.url}, Code: {flow.response.status_code}") 25 | if flow.response.status_code in self.REDIRECT_RESPONSES: 26 | # Resolve redirects instead of storing them, currently seems like a good idea 27 | print("Redirect found, resolving") 28 | async with httpx.AsyncClient() as client: 29 | resp = await client.get(flow.request.url, follow_redirects = True) 30 | flow.response.status_code = resp.status_code 31 | flow.response.headers = http.Headers([(h[0].encode('utf-8'), h[1].encode('utf-8')) for h in resp.headers.items() if h[0] in self.CACHED_HEADERS]) 32 | flow.response.content = resp.content 33 | if flow.response.status_code in self.CACHED_RESPONSES: 34 | print(f"Storing request") 35 | if flow.response.content is not None and len(flow.response.content) > 0: 36 | result = subprocess.run(["nix", "--extra-experimental-features", "nix-command", "hash", "file", "/dev/stdin"], capture_output=True, input=flow.response.content) 37 | nix_hash = result.stdout.decode('utf-8').strip() 38 | else: 39 | nix_hash = None 40 | print(f"Hash: {nix_hash}") 41 | stored_headers = [h for h in flow.response.headers.items() if h[0] in self.CACHED_HEADERS] 42 | self.cache.append({"url": flow.request.url, "hash": nix_hash, "headers": stored_headers, "status": flow.response.status_code}) 43 | 44 | def done(self): 45 | f = StringIO() 46 | f.write("{ fetchurl }:") 47 | f.write("[") 48 | for file in self.cache: 49 | f.write("{") 50 | f.write(f'url = "{file["url"]}";') 51 | if file['hash'] is not None: 52 | f.write(f'file = fetchurl {{ url = "{file["url"]}"; hash = "{file["hash"]}"; }};') 53 | else: 54 | f.write(f'file = null;') 55 | f.write(f'status_code = {file["status"]};') 56 | f.write('headers = {') 57 | for header in file['headers']: 58 | f.write(f'"{header[0]}" = "{header[1]}";') 59 | f.write("};}") 60 | f.write("]") 61 | with open('proxy_content.nix', 'w') as out: 62 | subprocess.run(["nixfmt"], input=f.getvalue().encode("utf-8"), stdout=out) 63 | 64 | 65 | addons = [BuildCache()] 66 | -------------------------------------------------------------------------------- /nix-buildproxy/buildproxy-capture.nix: -------------------------------------------------------------------------------- 1 | { self, writeShellScriptBin, mitmproxy, nix, nixfmt }: 2 | writeShellScriptBin "buildproxy-capture" 3 | '' 4 | PATH=${nix}/bin:${nixfmt}/bin:$PATH ${mitmproxy}/bin/mitmdump --set confdir=${self}/nix-buildproxy/confdir -s ${self}/nix-buildproxy/build_cache.py & 5 | MITM_PID=$! 6 | echo "Entering proxy capture shell, run your build now, exit shell when done" 7 | HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 SSL_CERT_FILE=${self}/nix-buildproxy/confdir/mitmproxy-ca-cert.pem $SHELL 8 | echo "Saving captured requests to proxy_content.nix" 9 | kill $MITM_PID 10 | '' -------------------------------------------------------------------------------- /nix-buildproxy/buildproxy-shell.nix: -------------------------------------------------------------------------------- 1 | { self, fetchurl, writeShellScriptBin, mitmproxy, netcat, content }: 2 | writeShellScriptBin "buildproxy-shell" 3 | '' 4 | export NIX_BUILDPROXY_CONTENT=${content} 5 | ${mitmproxy}/bin/mitmdump --set confdir=${self}/nix-buildproxy/confdir --set connection_strategy=lazy -s ${self}/nix-buildproxy/deliver.py & 6 | MITM_PID=$! 7 | echo "Entering proxy replay shell, exit when done" 8 | HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 SSL_CERT_FILE=${self}/nix-buildproxy/confdir/mitmproxy-ca-cert.pem $SHELL 9 | echo "Proxy shell exit, killing mitmproxy" 10 | kill $MITM_PID 11 | '' -------------------------------------------------------------------------------- /nix-buildproxy/buildproxy.nix: -------------------------------------------------------------------------------- 1 | { self, fetchurl, writeShellScript, mitmproxy, netcat, content }: 2 | writeShellScript "buildproxy" 3 | '' 4 | export NIX_BUILDPROXY_CONTENT=${content} 5 | ${mitmproxy}/bin/mitmdump --set confdir=${self}/nix-buildproxy/confdir --set connection_strategy=lazy -s ${self}/nix-buildproxy/deliver.py > /dev/null & 6 | export HTTP_PROXY=http://localhost:8080 7 | export HTTPS_PROXY=http://localhost:8080 8 | export SSL_CERT_FILE=${self}/nix-buildproxy/confdir/mitmproxy-ca-cert.pem 9 | while ! ${netcat}/bin/nc -z localhost 8080; do 10 | sleep 0.1 # Wait a bit 11 | done 12 | '' -------------------------------------------------------------------------------- /nix-buildproxy/confdir/mitmproxy-ca-cert.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDNTCCAh2gAwIBAgIUVNO+PDa0MTjeJ8M4JtanKLeD8lwwDQYJKoZIhvcNAQEL 3 | BQAwKDESMBAGA1UEAwwJbWl0bXByb3h5MRIwEAYDVQQKDAltaXRtcHJveHkwHhcN 4 | MjQwMjE2MjAyMzMzWhcNMzQwMjE1MjAyMzMzWjAoMRIwEAYDVQQDDAltaXRtcHJv 5 | eHkxEjAQBgNVBAoMCW1pdG1wcm94eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 6 | AQoCggEBANThfMXHZeehrShskZnespcw95CwGkqvBzgS6JOrhbN+OlRApB7CVDR/ 7 | B5yahqLQo5JdAhd7tuYDycOMSSn5knu6k84jJkS6MvNGT7Vx7lRqe9gBNfYTgP/g 8 | yV5urVIUNBBKL8fOMJqzHmEcyCXn1N8fhreDZhJ83aiEan0tEeri8BWl22Qal+Tb 9 | ay74GQ9X0OEJzJPj28s0DJYY2QUMuBw9X3GD6m+bW8c0IhLkEDCC/lOR7nVbmRPb 10 | A9XwkM62YPYHkP427CZmcLCMwNzwSnuz0SPbNjxEtpss9f+4ppQG1+ZQFONRbc1A 11 | Rhdspy+R7fxI6tgeUpUqSr5gHoGL+FcCAwEAAaNXMFUwDwYDVR0TAQH/BAUwAwEB 12 | /zATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE 13 | FL9ObedzkE5oshAIbvnClqCSKPLIMA0GCSqGSIb3DQEBCwUAA4IBAQBo4lcoP/gW 14 | Ou5vHbH7Y1fW6nrFeYpTkyx2sRBMiBIT4+cLuWoZs+/Qio5XiesT/rf74QDwlZ2j 15 | nK/C0Mbs8ZalJZ/7g9ik25zmHfKPU4awtEe41JRi+d9kq3hKRIv560RMFwc0/tHE 16 | +NZ2MWZmti5PHJL7YclSFSba5x88WwBZ2+XchjJfG1j1VMHatnliVuTq6BYLI5sZ 17 | pWaCNYRD6m5PYMiYp9pv4OnACvUGajAREAgJk3MOew6scL7W7RfBpyiLFY6HWQBR 18 | f+BgR7Ra0Y+GeBW2eSxRQ50/iNtionWQCq20ttB4xsmdTsUUdzfyWUozEHa3wyWY 19 | Cad0Ko7g9Vn4 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /nix-buildproxy/confdir/mitmproxy-ca-cert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polygon/nix-buildproxy/c26d73992ddae96812501b5ae1cc45037d8b10be/nix-buildproxy/confdir/mitmproxy-ca-cert.p12 -------------------------------------------------------------------------------- /nix-buildproxy/confdir/mitmproxy-ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDNTCCAh2gAwIBAgIUVNO+PDa0MTjeJ8M4JtanKLeD8lwwDQYJKoZIhvcNAQEL 3 | BQAwKDESMBAGA1UEAwwJbWl0bXByb3h5MRIwEAYDVQQKDAltaXRtcHJveHkwHhcN 4 | MjQwMjE2MjAyMzMzWhcNMzQwMjE1MjAyMzMzWjAoMRIwEAYDVQQDDAltaXRtcHJv 5 | eHkxEjAQBgNVBAoMCW1pdG1wcm94eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 6 | AQoCggEBANThfMXHZeehrShskZnespcw95CwGkqvBzgS6JOrhbN+OlRApB7CVDR/ 7 | B5yahqLQo5JdAhd7tuYDycOMSSn5knu6k84jJkS6MvNGT7Vx7lRqe9gBNfYTgP/g 8 | yV5urVIUNBBKL8fOMJqzHmEcyCXn1N8fhreDZhJ83aiEan0tEeri8BWl22Qal+Tb 9 | ay74GQ9X0OEJzJPj28s0DJYY2QUMuBw9X3GD6m+bW8c0IhLkEDCC/lOR7nVbmRPb 10 | A9XwkM62YPYHkP427CZmcLCMwNzwSnuz0SPbNjxEtpss9f+4ppQG1+ZQFONRbc1A 11 | Rhdspy+R7fxI6tgeUpUqSr5gHoGL+FcCAwEAAaNXMFUwDwYDVR0TAQH/BAUwAwEB 12 | /zATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE 13 | FL9ObedzkE5oshAIbvnClqCSKPLIMA0GCSqGSIb3DQEBCwUAA4IBAQBo4lcoP/gW 14 | Ou5vHbH7Y1fW6nrFeYpTkyx2sRBMiBIT4+cLuWoZs+/Qio5XiesT/rf74QDwlZ2j 15 | nK/C0Mbs8ZalJZ/7g9ik25zmHfKPU4awtEe41JRi+d9kq3hKRIv560RMFwc0/tHE 16 | +NZ2MWZmti5PHJL7YclSFSba5x88WwBZ2+XchjJfG1j1VMHatnliVuTq6BYLI5sZ 17 | pWaCNYRD6m5PYMiYp9pv4OnACvUGajAREAgJk3MOew6scL7W7RfBpyiLFY6HWQBR 18 | f+BgR7Ra0Y+GeBW2eSxRQ50/iNtionWQCq20ttB4xsmdTsUUdzfyWUozEHa3wyWY 19 | Cad0Ko7g9Vn4 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /nix-buildproxy/confdir/mitmproxy-ca.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polygon/nix-buildproxy/c26d73992ddae96812501b5ae1cc45037d8b10be/nix-buildproxy/confdir/mitmproxy-ca.p12 -------------------------------------------------------------------------------- /nix-buildproxy/confdir/mitmproxy-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA1OF8xcdl56GtKGyRmd6ylzD3kLAaSq8HOBLok6uFs346VECk 3 | HsJUNH8HnJqGotCjkl0CF3u25gPJw4xJKfmSe7qTziMmRLoy80ZPtXHuVGp72AE1 4 | 9hOA/+DJXm6tUhQ0EEovx84wmrMeYRzIJefU3x+Gt4NmEnzdqIRqfS0R6uLwFaXb 5 | ZBqX5NtrLvgZD1fQ4QnMk+PbyzQMlhjZBQy4HD1fcYPqb5tbxzQiEuQQMIL+U5Hu 6 | dVuZE9sD1fCQzrZg9geQ/jbsJmZwsIzA3PBKe7PRI9s2PES2myz1/7imlAbX5lAU 7 | 41FtzUBGF2ynL5Ht/Ejq2B5SlSpKvmAegYv4VwIDAQABAoIBAAD9wyLOoLhZOzZZ 8 | ebzBhIu0rtUYeimH3XI9kEyuZfMl2X1ZRwpc9Z8Vn6zzK+un1Lh9QLcS8eaUn5oO 9 | q1aL3XVAXf4okWiM2hOM68PDXIyTFFw3S+TcnA3/oiBo8TCqfGrpADD00lVzaRGj 10 | Byw6A0vorda+lg6W+0HqH+7Q82joacVl8/4A1Evn7PB+XPoTSiXFk5f+Xb3XUUyv 11 | 5afvl23UCiNms+WYnnsPxAR0oJLjfXLlwaY8aC7XeBjTVle0oZKnccAG92LGS+XN 12 | gDeZRUOHjArDrJUGlkg9K9w6Uak/Ea3saxEEFCQ9U6M5ZfQ3A1TF9ObBujRuW/NO 13 | sy5kkekCgYEA9nVDRRhsLn7BCdyKCgwaUXCRbKM3rEN6oACEPfBctLwmQnPX6HI1 14 | 8viGL1uPUbMeEBZ3k4hDJsuBd3PeKj5J4o6Aewym14wfoUoMll5Z4FGXn0Uua5Vg 15 | xC+nc5teDQSP6WY0zDyHpTSbdFkLr2aeBq3p/zPDtaymZMtMBrYeljkCgYEA3R9t 16 | laIYsuI2MW76UTJGHn8aQLPjE0DtvI3dlKZOZXPUlvVqmrIppOpvhlByOGUDVjcO 17 | /a0nMeY2Xdt8YO+FAd7GXLaowr+71H2D7JzwAt6hGSQfAkUoei+UhqvJ2GwJzIgi 18 | SG1Xs+qaBFBQmZhTN+DvM7866EZqsPW/p5Xygw8CgYEA6Cr08N0d4/riDFEl9GsM 19 | S9W4GNf0tSFoHdv+t357RoLLo+QO61jTu4wkk+4zp6oNUuhnQqKlZ6Fj18xd5/t/ 20 | jJ3jKId1kC3fCgiArRI6plcWdyIrpYs1efrOSth8k9TNYPg4GoGW4qkZHFRZ7qTD 21 | jtU8Tn1MCxOWN/NPLi9XxvkCgYBDRyCsellRdApRGBcJRWaYOMvgC9t3LDYpPHDC 22 | nacUrx1roNgCoVqSVtH/59IY3oMZZD8nZ9uZDxZTkEhTpgyt+P4Zj2nhdzzK9jWB 23 | dG6CMQKLB7Z3llsucfOa1gHf219P38uuhbY9g4/A6D7dvL71LXcaI4Dk0yf7F4ps 24 | ju7ueQKBgQDXIOC0MqQOUQrkHz5OoNMpre1XZ8GH6VCodXH332jDruFZFaeA4456 25 | 6aqZeDfAjps8XKhf3VBNKa1WrNGpkyLpfJsN0I8Si3WQK0remLZvecK12ku17fGx 26 | u8KPI3dll00KuqLO+miG1xfxXRDYm+iPWTqmg4KwaoKwRaJ537lqOg== 27 | -----END RSA PRIVATE KEY----- 28 | -----BEGIN CERTIFICATE----- 29 | MIIDNTCCAh2gAwIBAgIUVNO+PDa0MTjeJ8M4JtanKLeD8lwwDQYJKoZIhvcNAQEL 30 | BQAwKDESMBAGA1UEAwwJbWl0bXByb3h5MRIwEAYDVQQKDAltaXRtcHJveHkwHhcN 31 | MjQwMjE2MjAyMzMzWhcNMzQwMjE1MjAyMzMzWjAoMRIwEAYDVQQDDAltaXRtcHJv 32 | eHkxEjAQBgNVBAoMCW1pdG1wcm94eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 33 | AQoCggEBANThfMXHZeehrShskZnespcw95CwGkqvBzgS6JOrhbN+OlRApB7CVDR/ 34 | B5yahqLQo5JdAhd7tuYDycOMSSn5knu6k84jJkS6MvNGT7Vx7lRqe9gBNfYTgP/g 35 | yV5urVIUNBBKL8fOMJqzHmEcyCXn1N8fhreDZhJ83aiEan0tEeri8BWl22Qal+Tb 36 | ay74GQ9X0OEJzJPj28s0DJYY2QUMuBw9X3GD6m+bW8c0IhLkEDCC/lOR7nVbmRPb 37 | A9XwkM62YPYHkP427CZmcLCMwNzwSnuz0SPbNjxEtpss9f+4ppQG1+ZQFONRbc1A 38 | Rhdspy+R7fxI6tgeUpUqSr5gHoGL+FcCAwEAAaNXMFUwDwYDVR0TAQH/BAUwAwEB 39 | /zATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE 40 | FL9ObedzkE5oshAIbvnClqCSKPLIMA0GCSqGSIb3DQEBCwUAA4IBAQBo4lcoP/gW 41 | Ou5vHbH7Y1fW6nrFeYpTkyx2sRBMiBIT4+cLuWoZs+/Qio5XiesT/rf74QDwlZ2j 42 | nK/C0Mbs8ZalJZ/7g9ik25zmHfKPU4awtEe41JRi+d9kq3hKRIv560RMFwc0/tHE 43 | +NZ2MWZmti5PHJL7YclSFSba5x88WwBZ2+XchjJfG1j1VMHatnliVuTq6BYLI5sZ 44 | pWaCNYRD6m5PYMiYp9pv4OnACvUGajAREAgJk3MOew6scL7W7RfBpyiLFY6HWQBR 45 | f+BgR7Ra0Y+GeBW2eSxRQ50/iNtionWQCq20ttB4xsmdTsUUdzfyWUozEHa3wyWY 46 | Cad0Ko7g9Vn4 47 | -----END CERTIFICATE----- 48 | -------------------------------------------------------------------------------- /nix-buildproxy/confdir/mitmproxy-dhparam.pem: -------------------------------------------------------------------------------- 1 | 2 | -----BEGIN DH PARAMETERS----- 3 | MIICCAKCAgEAyT6LzpwVFS3gryIo29J5icvgxCnCebcdSe/NHMkD8dKJf8suFCg3 4 | O2+dguLakSVif/t6dhImxInJk230HmfC8q93hdcg/j8rLGJYDKu3ik6H//BAHKIv 5 | j5O9yjU3rXCfmVJQic2Nne39sg3CreAepEts2TvYHhVv3TEAzEqCtOuTjgDv0ntJ 6 | Gwpj+BJBRQGG9NvprX1YGJ7WOFBP/hWU7d6tgvE6Xa7T/u9QIKpYHMIkcN/l3ZFB 7 | chZEqVlyrcngtSXCROTPcDOQ6Q8QzhaBJS+Z6rcsd7X+haiQqvoFcmaJ08Ks6LQC 8 | ZIL2EtYJw8V8z7C0igVEBIADZBI6OTbuuhDwRw//zU1uq52Oc48CIZlGxTYG/Evq 9 | o9EWAXUYVzWkDSTeBH1r4z/qLPE2cnhtMxbFxuvK53jGB0emy2y1Ei6IhKshJ5qX 10 | IB/aE7SSHyQ3MDHHkCmQJCsOd4Mo26YX61NZ+n501XjqpCBQ2+DfZCBh8Va2wDyv 11 | A2Ryg9SUz8j0AXViRNMJgJrr446yro/FuJZwnQcO3WQnXeqSBnURqKjmqkeFP+d8 12 | 6mk2tqJaY507lRNqtGlLnj7f5RNoBFJDCLBNurVgfvq9TCVWKDIFD4vZRjCrnl6I 13 | rD693XKIHUCWOjMh1if6omGXKHH40QuME2gNa50+YPn1iYDl88uDbbMCAQI= 14 | -----END DH PARAMETERS----- 15 | -------------------------------------------------------------------------------- /nix-buildproxy/deliver.py: -------------------------------------------------------------------------------- 1 | """Deliver URIs previously requested from local cache""" 2 | import os 3 | import json 4 | 5 | from mitmproxy import http 6 | 7 | 8 | class ProxyResponder: 9 | def __init__(self) -> None: 10 | proxy_content_file = os.environ['NIX_BUILDPROXY_CONTENT'] 11 | self.proxy_content = json.load(open(proxy_content_file, 'r')) 12 | 13 | def request(self, flow: http.HTTPFlow) -> None: 14 | print(f"URI requested: {flow.request.url}") 15 | for obj in self.proxy_content: 16 | if obj['url'] == flow.request.url: 17 | print(f"Object found, delivering: {obj['status_code']} : {obj['file']}") 18 | headers = [(k.encode('utf-8'), v.encode('utf-8')) for k, v in obj['headers'].items()] 19 | flow.response = http.Response.make( 20 | status_code = obj['status_code'], 21 | headers = headers, 22 | content = open(obj['file'], 'rb').read() if obj['file'] is not None else b'', 23 | ) 24 | break 25 | else: 26 | flow.response = http.Response.make( 27 | 404, 28 | ) 29 | 30 | addons = [ProxyResponder()] 31 | -------------------------------------------------------------------------------- /nix-buildproxy/devshell.nix: -------------------------------------------------------------------------------- 1 | { mkShell, buildproxy-capture, buildproxy-shell, curl, mitmproxy, python3 }: 2 | mkShell { 3 | buildInputs = [ 4 | curl 5 | buildproxy-capture 6 | buildproxy-shell 7 | mitmproxy 8 | (python3.withPackages (ps: with ps; [ 9 | httpx 10 | ipython 11 | mitmproxy 12 | ])) 13 | ]; 14 | } -------------------------------------------------------------------------------- /proxy_content.nix: -------------------------------------------------------------------------------- 1 | { fetchurl }: [ ] 2 | --------------------------------------------------------------------------------