at 24.11-pre 4.5 kB view raw
1{ config, pkgs, lib, ... }: 2let 3 inherit (lib) mkOption types mkIf; 4 cfg = config.services.atuin; 5in 6{ 7 options = { 8 services.atuin = { 9 enable = lib.mkEnableOption "Atuin server for shell history sync"; 10 11 package = lib.mkPackageOption pkgs "atuin" { }; 12 13 openRegistration = mkOption { 14 type = types.bool; 15 default = false; 16 description = "Allow new user registrations with the atuin server."; 17 }; 18 19 path = mkOption { 20 type = types.str; 21 default = ""; 22 description = "A path to prepend to all the routes of the server."; 23 }; 24 25 host = mkOption { 26 type = types.str; 27 default = "127.0.0.1"; 28 description = "The host address the atuin server should listen on."; 29 }; 30 31 maxHistoryLength = mkOption { 32 type = types.int; 33 default = 8192; 34 description = "The max length of each history item the atuin server should store."; 35 }; 36 37 port = mkOption { 38 type = types.port; 39 default = 8888; 40 description = "The port the atuin server should listen on."; 41 }; 42 43 openFirewall = mkOption { 44 type = types.bool; 45 default = false; 46 description = "Open ports in the firewall for the atuin server."; 47 }; 48 49 database = { 50 createLocally = mkOption { 51 type = types.bool; 52 default = true; 53 description = "Create the database and database user locally."; 54 }; 55 56 uri = mkOption { 57 type = types.nullOr types.str; 58 default = "postgresql:///atuin?host=/run/postgresql"; 59 example = "postgresql://atuin@localhost:5432/atuin"; 60 description = '' 61 URI to the database. 62 Can be set to null in which case ATUIN_DB_URI should be set through an EnvironmentFile 63 ''; 64 }; 65 }; 66 }; 67 }; 68 69 config = mkIf cfg.enable { 70 assertions = [ 71 { 72 assertion = cfg.database.createLocally -> config.services.postgresql.enable; 73 message = "Postgresql must be enabled to create a local database"; 74 } 75 ]; 76 77 services.postgresql = mkIf cfg.database.createLocally { 78 enable = true; 79 ensureUsers = [{ 80 name = "atuin"; 81 ensureDBOwnership = true; 82 }]; 83 ensureDatabases = [ "atuin" ]; 84 }; 85 86 systemd.services.atuin = { 87 description = "atuin server"; 88 requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ]; 89 after = [ "network.target" ] ++ lib.optionals cfg.database.createLocally [ "postgresql.service" ]; 90 wantedBy = [ "multi-user.target" ]; 91 92 serviceConfig = { 93 ExecStart = "${lib.getExe cfg.package} server start"; 94 RuntimeDirectory = "atuin"; 95 RuntimeDirectoryMode = "0700"; 96 DynamicUser = true; 97 98 # Hardening 99 CapabilityBoundingSet = ""; 100 LockPersonality = true; 101 NoNewPrivileges = true; 102 MemoryDenyWriteExecute = true; 103 PrivateDevices = true; 104 PrivateMounts = true; 105 PrivateTmp = true; 106 PrivateUsers = true; 107 ProcSubset = "pid"; 108 ProtectClock = true; 109 ProtectControlGroups = true; 110 ProtectHome = true; 111 ProtectHostname = true; 112 ProtectKernelLogs = true; 113 ProtectKernelModules = true; 114 ProtectKernelTunables = true; 115 ProtectProc = "invisible"; 116 ProtectSystem = "full"; 117 RemoveIPC = true; 118 RestrictAddressFamilies = [ 119 "AF_INET" 120 "AF_INET6" 121 # Required for connecting to database sockets, 122 "AF_UNIX" 123 ]; 124 RestrictNamespaces = true; 125 RestrictRealtime = true; 126 RestrictSUIDSGID = true; 127 SystemCallArchitectures = "native"; 128 SystemCallFilter = [ 129 "@system-service" 130 "~@privileged" 131 ]; 132 UMask = "0077"; 133 }; 134 135 environment = { 136 ATUIN_HOST = cfg.host; 137 ATUIN_PORT = toString cfg.port; 138 ATUIN_MAX_HISTORY_LENGTH = toString cfg.maxHistoryLength; 139 ATUIN_OPEN_REGISTRATION = lib.boolToString cfg.openRegistration; 140 ATUIN_PATH = cfg.path; 141 ATUIN_CONFIG_DIR = "/run/atuin"; # required to start, but not used as configuration is via environment variables 142 } // lib.optionalAttrs (cfg.database.uri != null) { 143 ATUIN_DB_URI = cfg.database.uri; 144 }; 145 }; 146 147 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; 148 }; 149}