├── .gitignore ├── README.md ├── flake.lock └── flake.nix /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | [JA4+](https://blog.foxio.io/ja4+-network-fingerprinting) is a set of techniques for network fingerprinting. The most common version is used to identify TLS clients and servers. 3 | 4 | [FoxIO-LLC/ja4-nginx-module](https://github.com/FoxIO-LLC/ja4-nginx-module) adds this functionality to nginx. 5 | 6 | This repo lets you use the module from a Flakes-based Nix configuration. 7 | 8 | # How do I use this? 9 | 10 | Add this repo to your flake inputs: 11 | 12 | ```nix 13 | inputs = { 14 | # ... 15 | nginx-ja4.url = "github:mgdm/nginx-ja4-flake"; 16 | }; 17 | ``` 18 | 19 | In your system configuration, do something like this: 20 | 21 | ```nix 22 | let 23 | nginx-ja4 = inputs.nginx-ja4.packages."${pkgs.system}".default; 24 | in 25 | # NixOS configuration goes here 26 | services.nginx = { 27 | enable = true; 28 | package = nginx-ja4; 29 | 30 | # to expose the JA4 fingerprint to a FastCGI application such as PHP, 31 | # do something like this. I put it at the top level (`http`) to avoid issues 32 | # when redefining fastcgi_params in location or server blocks 33 | commonHttpConfig = '' 34 | # ... 35 | fastcgi_param HTTP_JA4_FINGERPRINT $http_ssl_ja4; 36 | # ... 37 | ''; 38 | 39 | # to expose the JA4 details to proxied HTTP applications, do something like 40 | locations."/" = { 41 | proxyPass = "http://localhost:3000"; 42 | extraConfig = '' 43 | proxy_set_header JA4_FINGERPRINT $http_ssl_ja4; 44 | ''; 45 | }; 46 | 47 | # ... 48 | }; 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1755027561, 24 | "narHash": "sha256-IVft239Bc8p8Dtvf7UAACMG5P3ZV+3/aO28gXpGtMXI=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "005433b926e16227259a1843015b5b2b7f7d1fc3", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixos-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs = { url = "github:NixOS/nixpkgs/nixos-unstable"; }; 4 | flake-utils = { url = "github:numtide/flake-utils"; }; 5 | }; 6 | 7 | outputs = { self, nixpkgs, flake-utils }: 8 | flake-utils.lib.eachDefaultSystem (system: 9 | let 10 | inherit (pkgs.lib) optional optionals; 11 | pkgs = import nixpkgs { inherit system; }; 12 | 13 | nginx-ja4-module = { 14 | name = "ja4-nginx-module"; 15 | src = let src' = pkgs.fetchFromGitHub { 16 | owner = "FoxIO-LLC"; 17 | repo = "ja4-nginx-module"; 18 | rev = "ad8d7fba6d5d7043c760639e5d95f174a3d8c88e"; 19 | sha256 = "sha256-LZ+7n2Qy2d+Bg2bApI+235LH8P5oerZNrOcoeSQymTA="; 20 | }; in 21 | pkgs.runCommand "ja4-nginx-module" { } '' 22 | cp -a ${src'} $out 23 | 24 | # The nginx compilation with -Werror fails without this as the variable 25 | # is not known to be initialised in the if/else chain 26 | substituteInPlace $out/src/ngx_http_ssl_ja4_module.c \ 27 | --replace 'double propagation_delay_factor;' 'double propagation_delay_factor = 1.0;' 28 | ''; 29 | }; 30 | 31 | openssl-ja4 = pkgs.openssl.overrideAttrs (drv: { 32 | patches = (drv.patches or [ ]) 33 | ++ [ "${nginx-ja4-module.src}/patches/openssl.patch" ]; 34 | # On Linux, we need to run `make update` after configuring so that the new 35 | # function added by the patch isn't stripped out by the linker 36 | postConfigure = '' 37 | make update 38 | ''; 39 | }); 40 | 41 | nginx-ja4 = (pkgs.nginxStable.overrideAttrs (drv: { 42 | patches = (drv.patches or [ ]) 43 | ++ [ "${nginx-ja4-module.src}/patches/nginx.patch" ]; 44 | configureFlags = drv.configureFlags ++ [ "--add-module=${nginx-ja4-module.src}/src" ]; 45 | })).override { openssl = openssl-ja4; }; 46 | 47 | in { 48 | packages = { 49 | nginx-ja4 = nginx-ja4; 50 | default = nginx-ja4; 51 | }; 52 | }); 53 | } 54 | --------------------------------------------------------------------------------