mirror of
https://github.com/SignalWalker/nix.nginx.vhost-defaults.git
synced 2025-10-06 00:02:40 +02:00
initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
result
|
||||||
|
result-*
|
60
README.md
Normal file
60
README.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Nginx Virtual Host Defaults
|
||||||
|
|
||||||
|
A small NixOS module adding the option `services.nginx.virtualHostDefaults`, which is a submodule merged into every virtual host configuration, allowing you to set options common to every Nginx virtual host.
|
||||||
|
|
||||||
|
It also adds `services.nginx.virtualHosts.<name>.blockAgents`, which allows you to easily block a a set of user agent strings from accessing a vhost.
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
`flake.nix`:
|
||||||
|
```nix
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||||
|
nginx-vhost-defaults = {
|
||||||
|
url = "github:signalwalker/nix.nginx.vhost-defaults";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
outputs = inputs @ {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
nixosConfigurations."example" = nixpkgs.lib.nixosSystem {
|
||||||
|
# ...
|
||||||
|
modules = [
|
||||||
|
inputs.nginx-vhost-defaults.nixosModules.default
|
||||||
|
./nixos-module.nix
|
||||||
|
# ...
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`nixos-module.nix`:
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
config = {
|
||||||
|
services.nginx = {
|
||||||
|
virtualHostDefaults = {
|
||||||
|
# force SSL on every virtual host
|
||||||
|
forceSSL = true;
|
||||||
|
# Block a set of user agents from accessing any virtual host.
|
||||||
|
blockAgents = {
|
||||||
|
enable = true;
|
||||||
|
# Use `lib.mkOptionDefault` if you want to preserve the default agent list, which includes all agents found in https://github.com/ai-robots-txt/ai-robots-txt
|
||||||
|
agents = lib.mkOptionDefault ["SemrushBot"];
|
||||||
|
# This is the default, which causes Nginx to drop the connection without any response.
|
||||||
|
method = "return 444";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
122
flake.lock
generated
Normal file
122
flake.lock
generated
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"ai-robots-txt": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1725844581,
|
||||||
|
"narHash": "sha256-3A1cuHtqrFeJKvdizAVQxYBOZRDs/MF02HNR0DICMsM=",
|
||||||
|
"owner": "ai-robots-txt",
|
||||||
|
"repo": "ai.robots.txt",
|
||||||
|
"rev": "6b8d7f5890d6bed722a95297996c054c210bd3b8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ai-robots-txt",
|
||||||
|
"repo": "ai.robots.txt",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"alejandra": {
|
||||||
|
"inputs": {
|
||||||
|
"fenix": "fenix",
|
||||||
|
"flakeCompat": "flakeCompat",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1719514321,
|
||||||
|
"narHash": "sha256-ys1nJdZ8zB8JlpUbQmnj0hZalg03bEPgQdZN30DhETE=",
|
||||||
|
"owner": "kamadorueda",
|
||||||
|
"repo": "alejandra",
|
||||||
|
"rev": "d7552fef2ccf1bbf0d36b27f6fddb19073f205b7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "kamadorueda",
|
||||||
|
"repo": "alejandra",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fenix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"alejandra",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1668234453,
|
||||||
|
"narHash": "sha256-FmuZThToBvRsqCauYJ3l8HJoGLAY5cMULeYEKIaGrRw=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "8f219f6b36e8d0d56afa7f67e6e3df63ef013cdb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flakeCompat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1650374568,
|
||||||
|
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1727089097,
|
||||||
|
"narHash": "sha256-ZMHMThPsthhUREwDebXw7GX45bJnBCVbfnH1g5iuSPc=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "568bfef547c14ca438c56a0bece08b8bb2b71a9c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"ai-robots-txt": "ai-robots-txt",
|
||||||
|
"alejandra": "alejandra",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-analyzer-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1668182250,
|
||||||
|
"narHash": "sha256-PYGaOCiFvnJdVz+ZCaKF8geGdffXjJUNcMwaBHv0FT4=",
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"rev": "45ec315e01dc8dd1146dfeb65f0ef6e5c2efed78",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"ref": "nightly",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
25
flake.nix
Normal file
25
flake.nix
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
description = "A NixOS module for easily blocking user agents from Nginx.";
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
alejandra = {
|
||||||
|
url = "github:kamadorueda/alejandra";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
ai-robots-txt = {
|
||||||
|
url = "github:ai-robots-txt/ai.robots.txt";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
outputs = inputs @ {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with builtins; let
|
||||||
|
std = nixpkgs.lib;
|
||||||
|
in {
|
||||||
|
formatter = std.mapAttrs (system: pkgs: pkgs.default) inputs.alejandra.packages;
|
||||||
|
nixosModules.default = import ./nixos-module.nix {inherit self;};
|
||||||
|
};
|
||||||
|
}
|
83
nixos-module.nix
Normal file
83
nixos-module.nix
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
{self}: {
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with builtins; let
|
||||||
|
std = pkgs.lib;
|
||||||
|
nginx = config.services.nginx;
|
||||||
|
mkRobotsTxt = robots: let
|
||||||
|
agents = std.concatStringsSep "\n" (map (agent: "User-agent: ${agent}") robots);
|
||||||
|
in
|
||||||
|
pkgs.writeText "robots.txt" ''
|
||||||
|
${agents}
|
||||||
|
Disallow: /
|
||||||
|
'';
|
||||||
|
regexEscapes = ["\"" "[" "]" "(" ")" "{" "}" "^" "$" "+" "*" "." "|" "?" "\\"];
|
||||||
|
defaultBlockList = attrNames (fromJSON (readFile "${self.inputs.ai-robots-txt}/robots.json"));
|
||||||
|
in {
|
||||||
|
options = with lib; {
|
||||||
|
services.nginx = {
|
||||||
|
virtualHostDefaults = mkOption {
|
||||||
|
type = types.deferredModuleWith {
|
||||||
|
staticModules = [];
|
||||||
|
};
|
||||||
|
description = "Default configuration merged into every virtual host.";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
virtualHosts = mkOption {
|
||||||
|
# type-merge the `virtualHosts` submodule so we can import `nginx.virtualHostDefaults` into every virtualHost
|
||||||
|
type = types.attrsOf (types.submoduleWith {
|
||||||
|
modules = [
|
||||||
|
nginx.virtualHostDefaults
|
||||||
|
({
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
options = {
|
||||||
|
blockAgents = {
|
||||||
|
enable = mkEnableOption "blocking a set of user agents from accessing this virtual host.";
|
||||||
|
agents = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "User agent strings to block from accessing this virtual host.";
|
||||||
|
default = defaultBlockList;
|
||||||
|
defaultText = "The user agent list from [github:ai-robots-txt/ai-robots-txt](https://github.com/ai-robots-txt/ai-robots-txt).";
|
||||||
|
example = ["Amazonbot" "AI2Bot" "Applebot"];
|
||||||
|
};
|
||||||
|
method = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Method by which to block agents.";
|
||||||
|
default = "return 444";
|
||||||
|
defaultText = "`return 444`, dropping the connection.";
|
||||||
|
example = "return 307 https://ash-speed.hetzner.com/10GB.bin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf (config.blockAgents.enable && (length config.blockAgents.agents) > 0) {
|
||||||
|
locations."=/robots.txt" = {
|
||||||
|
alias = nginx.robotsTxt;
|
||||||
|
};
|
||||||
|
extraConfig = let
|
||||||
|
agentRules = pkgs.lib.concatStringsSep "|" (map (lib.strings.escape regexEscapes) config.blockAgents.agents);
|
||||||
|
in ''
|
||||||
|
if ($http_user_agent ~* "(${agentRules})") {
|
||||||
|
${config.blockAgents.method};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
disabledModules = [];
|
||||||
|
imports = [];
|
||||||
|
config = {};
|
||||||
|
meta = {};
|
||||||
|
}
|
Reference in New Issue
Block a user