diff options
| author | Quentin Aristote <quentin@aristote.fr> | 2021-08-06 16:20:44 +0200 |
|---|---|---|
| committer | Quentin Aristote <quentin@aristote.fr> | 2021-08-06 16:20:44 +0200 |
| commit | 210d4e7c2e7102355e8ef92681800157faa57d16 (patch) | |
| tree | c1c599a66e34cc86056c094c438fe742ae41c03e | |
initial commit
| -rw-r--r-- | config/boot.nix | 12 | ||||
| -rw-r--r-- | config/default.nix | 22 | ||||
| -rw-r--r-- | config/environment.nix | 16 | ||||
| -rw-r--r-- | config/hardware-configuration.nix | 28 | ||||
| -rw-r--r-- | config/networking.nix | 37 | ||||
| -rw-r--r-- | config/searx/default.nix | 236 | ||||
| -rw-r--r-- | config/searx/filtron.json | 129 | ||||
| -rw-r--r-- | config/users.nix | 11 | ||||
| -rw-r--r-- | configuration.nix | 10 | ||||
| -rw-r--r-- | modules/default.nix | 7 | ||||
| -rw-r--r-- | modules/filtron.nix | 87 | ||||
| -rw-r--r-- | pkgs/academic-webpage/default.nix | 21 | ||||
| -rw-r--r-- | pkgs/default.nix | 6 | ||||
| -rw-r--r-- | pkgs/filtron/default.nix | 16 |
14 files changed, 638 insertions, 0 deletions
diff --git a/config/boot.nix b/config/boot.nix new file mode 100644 index 0000000..df60fea --- /dev/null +++ b/config/boot.nix @@ -0,0 +1,12 @@ +{ ... }: + +{ + boot = { + loader.grub = { + enable = true; + version = 2; + enableCryptodisk = true; + device = "/dev/vda"; + }; + }; +} diff --git a/config/default.nix b/config/default.nix new file mode 100644 index 0000000..39de497 --- /dev/null +++ b/config/default.nix @@ -0,0 +1,22 @@ +{ pkgs, modulesPath, ... }: + +{ + imports = [ + (modulesPath + "/profiles/headless.nix") + (modulesPath + "/profiles/minimal.nix") + ./boot.nix + ./environment.nix + ./hardware-configuration.nix + ./networking.nix + ./searx + ./users.nix + ]; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "20.03"; # Did you read the comment? +} diff --git a/config/environment.nix b/config/environment.nix new file mode 100644 index 0000000..e8faf42 --- /dev/null +++ b/config/environment.nix @@ -0,0 +1,16 @@ +{ pkgs, ... }: + +{ + environment.systemPackages = with pkgs; [ vim ]; + programs.bash.promptInit = '' + PS1='\n\[\033[1;32m\][\[\e]0;\u@\H: \w\a\]\u@\H:\w]\$\[\033[0m\] ' + ''; + + i18n.defaultLocale = "en_US.UTF-8"; + console = { + font = "Lat2-Terminus16"; + keyMap = "fr"; + }; + + time.timeZone = "Europe/Paris"; +} diff --git a/config/hardware-configuration.nix b/config/hardware-configuration.nix new file mode 100644 index 0000000..efcdb77 --- /dev/null +++ b/config/hardware-configuration.nix @@ -0,0 +1,28 @@ +{ lib, modulesPath, ... }: + +{ + imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; + + boot.initrd.availableKernelModules = + [ "ata_piix" "uhci_hcd" "virtio_pci" "sr_mod" "virtio_blk" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/2b302948-5608-41c6-b54c-1c0e39ff6a58"; + fsType = "ext4"; + }; + + boot.initrd.luks.devices."root".device = + "/dev/disk/by-uuid/eaec758b-ba22-42ab-8992-e765cec9be55"; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/74d78eba-c29a-4724-8fb7-624e0a03faa5"; + fsType = "ext4"; + }; + + swapDevices = [{ device = "/swap"; }]; + + nix.maxJobs = lib.mkDefault 1; +} diff --git a/config/networking.nix b/config/networking.nix new file mode 100644 index 0000000..21bcc83 --- /dev/null +++ b/config/networking.nix @@ -0,0 +1,37 @@ +{ ... }: + +{ + networking = { + hostName = "hermes.aristote.fr"; + + useDHCP = false; + interfaces.ens3.ipv4.addresses = [{ + address = "93.95.228.53"; + prefixLength = 16; + }]; + defaultGateway = "93.95.228.1"; + nameservers = [ "93.95.224.28" "93.95.224.29" ]; + + firewall = { + enable = true; + allowedTCPPorts = [ 80 443 ]; + }; + }; + + services.nginx = { + enable = true; + virtualHosts = { + "quentin.aristote.fr" = { root = "${pkgs.personal.academic-webpage}"; }; + }; + }; + + services.openssh = { + enable = true; + permitRootLogin = "no"; + passwordAuthentication = false; + extraConfig = '' + AcceptEnv PS1 + ''; + }; + services.fail2ban.enable = true; +} diff --git a/config/searx/default.nix b/config/searx/default.nix new file mode 100644 index 0000000..1181d22 --- /dev/null +++ b/config/searx/default.nix @@ -0,0 +1,236 @@ +{ pkgs, lib, ... }: + +let + ports = { + searx = 8888; + filtron = { + listen = 4004; + api = 4005; + }; + morty = 3000; + }; + keys = { morty = "1t/rvXuoX/9OKwZ6Zby1zBc5t1DRFYIiE15xhIi72TKX"; }; +in { + # Nginx + services.nginx.virtualHosts."searx.aristote.fr" = { + locations = { + "/" = { + proxyPass = "http://127.0.0.1:${toString ports.filtron.listen}"; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header Connection $http_connection; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Scheme $scheme; + # proxy_set_header X-Script-Name /; + ''; + }; + "/static/" = { alias = "${pkgs.searx}/share/static/"; }; + "/morty" = { + proxyPass = "http://127.0.0.1:${toString ports.morty}"; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header Connection $http_connection; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Scheme $scheme; + ''; + }; + }; + }; + + # Searx + services.searx = { + enable = true; + environmentFile = /etc/searx/secrets; + settings = { + use_default_settings = true; + general = { + debug = false; + contact_url = "mailto:quentin@aristote.fr"; + enable_stats = false; + }; + search = { + autocomplete = "dbpedia"; + default_lang = "fr-FR"; + }; + server = { + secret_key = "@SECRET_KEY@"; + image_proxy = true; + http_protocol_version = "1.0"; + method = "GET"; + }; + ui = { + # default_locale = "fr"; + theme_args = { oscar_style = "pointhi"; }; + }; + # result_proxy = { + # url = "http://searx.aristote.fr/morty"; + # key = ''!!binary | "${keys.morty}"''; + # }; + enabled_plugins = [ + "Open Access DOI rewrite" + "Hash plugin" + "HTTPS rewrite" + "Self Informations" + "Search on category select" + "Tracker URL remover" + "Vim-like hotkeys" + ]; + }; + runInUwsgi = true; + uwsgiConfig = { + cache2 = "name=searxcache,items=2000,blocks=2000,blocksize=4096,bitmap=1"; + http = ":${toString ports.searx}"; + }; + }; + + # Filtron + # users.users.filtron = { + # description = "Filtron daemon user"; + # group = "filtron"; + # isSystemUser = true; + # }; + # users.groups.filtron = { }; + + # systemd.services.filtron = { + # wantedBy = [ "multi-user.target" ]; + # after = [ "network.target" ]; + # description = "Start a filtron instance."; + # serviceConfig = { + # User = "filtron"; + # ExecStart = '' + # ${pkgs.personal.filtron}/bin/filtron -rules ${./filtron.json} \ + # -api "127.0.0.1:${toString ports.filtron.api}" \ + # -listen "127.0.0.1:${toString ports.filtron.listen}" \ + # -target "127.0.0.1:${toString ports.searx}" + # ''; + # }; + # }; + + services.filtron = { + enable = true; + rules = [ + { + name = "roboagent limit"; + filters = [ + "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby|UniversalFeedParser)" + ]; + limit = 0; + stop = true; + actions = [ + { name = "log"; } + { + name = "block"; + params = { message = "Rate limit exceeded"; }; + } + ]; + } + { + name = "botlimit"; + filters = [ + "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)" + ]; + limit = 0; + stop = true; + actions = [ + { name = "log"; } + { + name = "block"; + params = { message = "Rate limit exceeded"; }; + } + ]; + } + { + name = "suspiciously frequent IP"; + filters = [ ]; + interval = 600; + limit = 30; + aggregations = [ "Header:X-Forwarded-For" ]; + actions = [{ name = "log"; }]; + } + { + name = "search request"; + filters = [ "Param:q" "Path=^(/|/search)$" ]; + interval = 61; + limit = 999; + subrules = [ + { + name = "missing Accept-Language"; + filters = [ "!Header:Accept-Language" ]; + limit = 0; + stop = true; + actions = [ + { name = "log"; } + { + name = "block"; + params = { message = "Rate limit exceeded"; }; + } + ]; + } + { + name = "suspiciously Connection=close header"; + filters = [ "Header:Connection=close" ]; + limit = 0; + stop = true; + actions = [ + { name = "log"; } + { + name = "block"; + params = { message = "Rate limit exceeded"; }; + } + ]; + } + { + name = "IP limit"; + interval = 61; + limit = 9; + stop = true; + aggregations = [ "Header:X-Forwarded-For" ]; + actions = [ + { name = "log"; } + { + name = "block"; + params = { message = "Rate limit exceeded"; }; + } + ]; + } + { + name = "rss/json limit"; + filters = [ "Param:format=(csv|json|rss)" ]; + interval = 121; + limit = 2; + stop = true; + actions = [ + { name = "log"; } + { + name = "block"; + params = { message = "Rate limit exceeded"; }; + } + ]; + } + { + name = "useragent limit"; + interval = 61; + limit = 199; + aggregations = [ "Header:User-Agent" ]; + actions = [ + { name = "log"; } + { + name = "block"; + params = { message = "Rate limit exceeded"; }; + } + ]; + } + ]; + } + ]; + }; + + # Morty + # services.morty = { + # enable = true; + # key = keys.morty; + # port = ports.morty; + # }; +} diff --git a/config/searx/filtron.json b/config/searx/filtron.json new file mode 100644 index 0000000..285d933 --- /dev/null +++ b/config/searx/filtron.json @@ -0,0 +1,129 @@ +[ + { + name = "roboagent limit"; + filters = [ + "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby|UniversalFeedParser)" + ]; + limit = 0; + stop = true; + actions = [ + { name = "log"; }; + { name = "block"; + params = { + message = "Rate limit exceeded" + }; + }; + ]; + }; + { + name = "botlimit"; + filters = [ + "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)" + ]; + limit = 0; + stop = true; + actions = [ + { name = "log"; }; + { name = "block"; + params = { + message = "Rate limit exceeded" + }; + }; + ]; + }; + { + name = "suspiciously frequent IP"; + filters = []; + interval = 600; + limit = 30; + aggregations = [ + "Header:X-Forwarded-For" + ]; + actions =[ + {name ="log"; }; + ]; + }; + { + name = "search request"; + filters = [ + "Param:q"; + "Path=^(/|/search)$" + ]; + interval = 61; + limit = 999; + subrules = [ + { + name = "missing Accept-Language"; + filters = ["!Header:Accept-Language"]; + limit = 0; + stop = true; + actions = [ + {name ="log"; }; + {name = "block"; + params = {"message": "Rate limit exceeded"; }}; + ]; + }; + { + name = "suspiciously Connection=close header"; + filters = ["Header:Connection=close"]; + limit = 0; + stop = true; + actions = [ + {name ="log"; }; + {name = "block"; + params = {"message": "Rate limit exceeded"; }}; + ]; + }; + { + name = "IP limit"; + interval = 61; + limit = 9; + stop = true; + aggregations = [ + "Header:X-Forwarded-For" + ]; + actions = [ + { name = "log"; }; + { name = "block"; + params = { + message = "Rate limit exceeded" + }; + }; + ]; + }; + { + name = "rss/json limit"; + filters = [ + "Param:format=(csv|json|rss)" + ]; + interval = 121; + limit = 2; + stop = true; + actions = [ + { name = "log"; }; + { name = "block"; + params = { + message = "Rate limit exceeded" + }; + }; + ]; + }; + { + name = "useragent limit"; + interval = 61; + limit = 199; + aggregations = [ + "Header:User-Agent" + ]; + actions = [ + { name = "log"; }; + { name = "block"; + params = { + message = "Rate limit exceeded" + }; + }; + ]; + }; + ]; + }; +]; diff --git a/config/users.nix b/config/users.nix new file mode 100644 index 0000000..99a497d --- /dev/null +++ b/config/users.nix @@ -0,0 +1,11 @@ +{ ... }: + +{ + users.users.qaristote = { + isNormalUser = true; + extraGroups = [ "wheel" ]; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK4wGbl3++lqCjLUhoRyABBrVEeNhIXYO4371srkRoyq qaristote@latitude-7490" + ]; + }; +} diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..958a63c --- /dev/null +++ b/configuration.nix @@ -0,0 +1,10 @@ +{ ... }: + +{ + imports = [ ./config ./modules ]; + + nixpkgs = { + overlays = + [ (final: prev: { personal = import ./pkgs { pkgs = final; }; }) ]; + }; +} diff --git a/modules/default.nix b/modules/default.nix new file mode 100644 index 0000000..d09a8c0 --- /dev/null +++ b/modules/default.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + imports = [ + ./filtron.nix + ]; +} diff --git a/modules/filtron.nix b/modules/filtron.nix new file mode 100644 index 0000000..55374a7 --- /dev/null +++ b/modules/filtron.nix @@ -0,0 +1,87 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.filtron; + addressType = types.submodule { + options = { + address = mkOption { + type = types.str; + default = "127.0.0.1"; + }; + port = mkOption { type = types.port; }; + }; + }; +in { + options.services.filtron = { + enable = mkEnableOption { name = "filtron"; }; + package = mkOption { + type = types.package; + default = pkgs.personal.filtron; + defaultText = literalExample "pkgs.personal.filtron"; + description = '' + The package containing the filtron executable. + ''; + }; + api = mkOption { + type = addressType; + default = { address = "localhost"; port = 4005; }; + description = '' + API listen address and port. + ''; + }; + listen = mkOption { + type = addressType; + default = { port = 4004; }; + description = '' + Proxy listen address and port. + ''; + }; + target = mkOption { + type = addressType; + default = { port = 8888; }; + description = '' + Target address and port for reverse proxy. + ''; + }; + rules = mkOption { + type = with types; listOf (attrsOf anything); + description = '' + Rule list. + ''; + }; + readBufferSize = mkOption { + type = types.int; + default = 16384; + description = '' + Size of the buffer used for reading. + ''; + }; + }; + + config = mkIf cfg.enable { + users.users.filtron = { + description = "Filtron daemon user"; + group = "filtron"; + isSystemUser = true; + }; + users.groups.filtron = { }; + + systemd.services.filtron = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "Start a filtron instance."; + serviceConfig = { + User = "filtron"; + ExecStart = with builtins; '' + ${cfg.package}/bin/filtron \ + -rules ${toFile "filtron-rules.json" (toJSON cfg.rules)} \ + -api "${cfg.api.address}:${toString cfg.api.port}" \ + -listen "${cfg.listen.address}:${toString cfg.listen.port}" \ + -target "${cfg.target.address}:${toString cfg.target.port}" \ + -read-buffer-size ${toString cfg.readBufferSize} + ''; + }; + }; + }; +} diff --git a/pkgs/academic-webpage/default.nix b/pkgs/academic-webpage/default.nix new file mode 100644 index 0000000..4c909ff --- /dev/null +++ b/pkgs/academic-webpage/default.nix @@ -0,0 +1,21 @@ +{ pkgs, stdenv, ... }: + +stdenv.mkDerivation { + name = "academic-webpage"; + + buildInputs = with pkgs; [ hugo ]; + + src = pkgs.fetchFromGitHub { + owner = "qaristote"; + repo = "academic-webpage"; + rev = "18e00fdd22643831376e012793574d9293243f6f"; + sha256 = "148gknjlwr71q5gkp4q7bnza64222izhn949ki14k7b9y7j486c6"; + fetchSubmodules = true; + }; + + phases = [ "unpackPhase" "buildPhase" ]; + + buildPhase = '' + hugo --destination $out + ''; +} diff --git a/pkgs/default.nix b/pkgs/default.nix new file mode 100644 index 0000000..8182baa --- /dev/null +++ b/pkgs/default.nix @@ -0,0 +1,6 @@ +{ pkgs }: + +{ + academic-webpage = pkgs.callPackage ./academic-webpage { }; + filtron = pkgs.callPackage ./filtron {}; +} diff --git a/pkgs/filtron/default.nix b/pkgs/filtron/default.nix new file mode 100644 index 0000000..863dc4c --- /dev/null +++ b/pkgs/filtron/default.nix @@ -0,0 +1,16 @@ +{ stdenv, buildGoModule, fetchFromGitHub }: + +buildGoModule rec { + pname = "filtron"; + version = "1.0.0"; + + src = fetchFromGitHub { + owner = "asciimo"; + repo = "filtron"; + rev = "v${version}"; + sha256 = "18d3h0i2sfqbc0bjx26jm2n9f37zwp8z9z4wd17sw7nvkfa72a26"; + }; + + doCheck = false; + vendorSha256 = "05q2g591xl08h387mm6njabvki19yih63dfsafgpc9hyk5ydf2n9"; +} |
