From 1c7138e49939c200f83e9d6a96d5895f317c86ca Mon Sep 17 00:00:00 2001 From: Sarah Date: Sun, 17 Oct 2021 12:01:40 +0200 Subject: [PATCH] Implement modules. --- VERSION | 1 + default.nix | 10 ++++ module.nix | 131 +++++++++++++++++++++++++++++++++++++++++++++ peerix/__main__.py | 6 ++- peerix/remote.py | 4 +- setup.py | 27 +++++----- 6 files changed, 164 insertions(+), 15 deletions(-) create mode 100644 VERSION create mode 100644 default.nix create mode 100644 module.nix diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..8acdd82 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.1 diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..f05c94e --- /dev/null +++ b/default.nix @@ -0,0 +1,10 @@ +{ pkgs, lib, ... }: +let + sources = import ./sources.nix {}; + mach-nix = import sources.mach-nix { + inherit pkgs; + }; +in +mach-nix.buildPythonApplication { + src = lib.cleanSource ./..; +} diff --git a/module.nix b/module.nix new file mode 100644 index 0000000..7b84eda --- /dev/null +++ b/module.nix @@ -0,0 +1,131 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.services.peerix; + + peerix = pkgs.callPackage ./default.nix {}; +in +{ + options = with lib; { + services.peerix = { + enable = lib.mkEnableOption "peerix"; + + openFirewall = lib.mkOption { + type = types.bool; + default = true; + description = '' + Defines whether or not firewall ports should be opened for it. + ''; + }; + + privateKeyFile = lib.mkOption { + type = types.nullOr types.path; + default = null; + description = '' + The private key to sign the derivations with. + ''; + }; + + publicKeyFile = lib.mkOption { + type = types.nullOr types.path; + default = null; + description = '' + The private key to sign the derivations with. + ''; + }; + + user = lib.mkOption { + type = with types; oneOf [ str int ]; + default = "nobody"; + description = '' + The user the service will use. + ''; + }; + + group = lib.mkOption { + type = with types; oneOf [ str int ]; + default = "nobody"; + description = '' + The user the service will use. + ''; + }; + }; + }; + + config = lib.mkIf (cfg.enable) { + systemd.services.peerix = { + description = "Local p2p nix caching daemon"; + wantedBy = ["multi-user.target"]; + serviceConfig = { + Type = "simple"; + + User = cfg.user; + Group = cfg.group; + + PrivateMounts = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateIPC = true; + PrivateUsers = true; + + SystemCallFilters = [ + "@aio" + "@basic-io" + "@file-system" + "@io-event" + "@process" + "@network-io" + "@timer" + "@signal" + "@alarm" + ]; + SystemCallErrorNumber = "EPERM"; + + ProtectSystem = "full"; + ProtectHome = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_INET", "AF_UNIX" ]; + RestrictNamespaces = ""; + + NoNewPrivileges = true; + ReadOnlyPaths = lib,mkMerge [ + ([ + "/nix/var" + "/nix/store" + ]) + + (lib.mkIf (config.privateKeyFile != null) [ + (toString config.privateKeyFile) + ]); + ]; + ExecPaths = [ + "/nix/store" + ]; + Environment = lib.mkIf (config.privateKeyFile != null) [ + "NIX_SECRET_KEY_FILE=${toString config.privateKeyFile}" + ]; + }; + script = '' + exec ${peerix}/bin/peerix + ''; + }; + + nix = { + binaryCaches = [ + "http://127.0.0.1:12304/" + ]; + binaryCachePublicKeys = lib.mkIf (config.publicKeyFile != null) [ + (builtins.readFile config.publicKeyFile) + ]; + }; + + networking.firewall = lib.mkIf (cfg.openFirewall) { + allowedTCPPorts = [ 12304 ]; + allowedUDPPorts = [ 12304 ]; + }; + }; +} diff --git a/peerix/__main__.py b/peerix/__main__.py index 520b1e6..b440cea 100644 --- a/peerix/__main__.py +++ b/peerix/__main__.py @@ -6,8 +6,12 @@ from hypercorn.asyncio import serve from peerix.app import app -if __name__ == "__main__": +def run(): uvloop.install() config = Config() config.bind = ["0.0.0.0:12304"] asyncio.run(serve(app, config)) + + +if __name__ == "__main__": + run() diff --git a/peerix/remote.py b/peerix/remote.py index 704b593..bd3dd49 100644 --- a/peerix/remote.py +++ b/peerix/remote.py @@ -104,7 +104,9 @@ class DiscoveryProtocol(asyncio.DatagramProtocol, Store): self.transport.sendto(b"".join([b"\x00", idx.to_bytes(4, "big"), hsh.encode("utf-8")]), (addr, self.local_port)) try: - port, url, addr = await asyncio.wait_for(fut, 0.5) + # This must have a short timeout so it does not noticably slow down + # querying of other caches. + port, url, addr = await asyncio.wait_for(fut, 0.05) except asyncio.TimeoutError: print(f"No response for {hsh}") return None diff --git a/setup.py b/setup.py index 14f0c09..536ca4b 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,22 @@ #setup.py: - +import os from distutils.core import setup -from Cython.Build import cythonize -from distutils.extension import Extension -ext_modules = [ - Extension( - name="peerix._nix", - sources=["peerix/_nix.pyx"], - language="c++", - extra_compile_args=["-std=c++17"], - ) -] +DIR = os.path.dirname(__file__) -ext_modules = cythonize(ext_modules) +with open(os.path.join(DIR, "requirements.txt")) as f: + requirements = f.readlines() + +with open(os.path.join(DIR, "VERSION")) as f: + version = f.read().strip() setup( name="peerix", - ext_modules=ext_modules, + entry_points={ + "console_scripts": [ + 'peerix = peerix.__main__:run' + ] + }, + version=version, + requires=requirements )