diff options
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/doc/manual/release-notes/rl-2311.section.md | 2 | ||||
-rw-r--r-- | nixos/doc/manual/release-notes/rl-2405.section.md | 3 | ||||
-rw-r--r-- | nixos/modules/installer/tools/tools.nix | 9 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 9 | ||||
-rw-r--r-- | nixos/modules/profiles/macos-builder.nix | 13 | ||||
-rw-r--r-- | nixos/modules/programs/mininet.nix | 33 | ||||
-rw-r--r-- | nixos/modules/services/development/athens.md | 52 | ||||
-rw-r--r-- | nixos/modules/services/development/athens.nix | 936 | ||||
-rw-r--r-- | nixos/modules/services/misc/sourcehut/default.nix | 5 | ||||
-rw-r--r-- | nixos/modules/services/security/fail2ban.nix | 4 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/lanraragi.nix | 13 | ||||
-rw-r--r-- | nixos/modules/system/boot/unl0kr.nix | 89 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 1 | ||||
-rw-r--r-- | nixos/tests/lanraragi.nix | 8 | ||||
-rw-r--r-- | nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix | 12 | ||||
-rw-r--r-- | nixos/tests/nixops/default.nix | 6 | ||||
-rw-r--r-- | nixos/tests/systemd-initrd-luks-unl0kr.nix | 75 |
17 files changed, 1230 insertions, 40 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index e8292087dc82..5df8473babae 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -79,6 +79,8 @@ * [NS-USBLoader](https://github.com/developersu/ns-usbloader/), an all-in-one tool for managing Nintendo Switch homebrew. Available as [programs.ns-usbloader](#opt-programs.ns-usbloader.enable). +- [athens](https://github.com/gomods/athens), a Go module datastore and proxy. Available as [services.athens](#opt-services.athens.enable). + - [Mobilizon](https://joinmobilizon.org/), a Fediverse platform for publishing events. - [Anuko Time Tracker](https://github.com/anuko/timetracker), a simple, easy to use, open source time tracking system. Available as [services.anuko-time-tracker](#opt-services.anuko-time-tracker.enable). diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index d96992b018ab..b6b343145d78 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -20,7 +20,8 @@ In addition to numerous new and upgraded packages, this release has the followin <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> -- Create the first release note entry in this section! +- `mkosi` was updated to v19. Parts of the user interface have changed. Consult the + [release notes](https://github.com/systemd/mkosi/releases/tag/v19) for a list of changes. ## Other Notable Changes {#sec-release-24.05-notable-changes} diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix index 15e10128ac9a..9ccc76a82c95 100644 --- a/nixos/modules/installer/tools/tools.nix +++ b/nixos/modules/installer/tools/tools.nix @@ -130,7 +130,7 @@ in ''; }; - config = lib.mkIf (config.nix.enable && !config.system.disableInstallerTools) { + config = lib.mkMerge [ (lib.mkIf (config.nix.enable && !config.system.disableInstallerTools) { system.nixos-generate-config.configuration = mkDefault '' # Edit this configuration file to define what should be installed on @@ -257,10 +257,13 @@ in documentation.man.man-db.skipPackages = [ nixos-version ]; + }) + + # These may be used in auxiliary scripts (ie not part of toplevel), so they are defined unconditionally. + ({ system.build = { inherit nixos-install nixos-generate-config nixos-option nixos-rebuild nixos-enter; }; - - }; + })]; } diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b92786506a29..e40b7ed8015f 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -480,6 +480,7 @@ ./services/desktops/telepathy.nix ./services/desktops/tumbler.nix ./services/desktops/zeitgeist.nix + ./services/development/athens.nix ./services/development/blackfire.nix ./services/development/bloop.nix ./services/development/distccd.nix @@ -1441,6 +1442,7 @@ ./system/boot/stratisroot.nix ./system/boot/modprobe.nix ./system/boot/networkd.nix + ./system/boot/unl0kr.nix ./system/boot/plymouth.nix ./system/boot/resolved.nix ./system/boot/shutdown.nix @@ -1538,9 +1540,10 @@ ./virtualisation/waydroid.nix ./virtualisation/xe-guest-utilities.nix ./virtualisation/xen-dom0.nix - { documentation.nixos.extraModules = [ - ./virtualisation/qemu-vm.nix - ./image/repart.nix + { + documentation.nixos.extraModules = [ + ./virtualisation/qemu-vm.nix + ./image/repart.nix ]; } ] diff --git a/nixos/modules/profiles/macos-builder.nix b/nixos/modules/profiles/macos-builder.nix index d48afed18f7e..6c2602881d6b 100644 --- a/nixos/modules/profiles/macos-builder.nix +++ b/nixos/modules/profiles/macos-builder.nix @@ -103,6 +103,19 @@ in # server that QEMU provides (normally 10.0.2.3) networking.nameservers = [ "8.8.8.8" ]; + # The linux builder is a lightweight VM for remote building; not evaluation. + nix.channel.enable = false; + # remote builder uses `nix-daemon` (ssh-ng:) or `nix-store --serve` (ssh:) + # --force: do not complain when missing + # TODO: install a store-only nix + # https://github.com/NixOS/rfcs/blob/master/rfcs/0134-nix-store-layer.md#detailed-design + environment.extraSetup = '' + rm --force $out/bin/{nix-instantiate,nix-build,nix-shell,nix-prefetch*,nix} + ''; + # Deployment is by image. + # TODO system.switch.enable = false;? + system.disableInstallerTools = true; + nix.settings = { auto-optimise-store = true; diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix index 02272729d233..01ffd811e70e 100644 --- a/nixos/modules/programs/mininet.nix +++ b/nixos/modules/programs/mininet.nix @@ -5,26 +5,39 @@ with lib; let - cfg = config.programs.mininet; + cfg = config.programs.mininet; - generatedPath = with pkgs; makeSearchPath "bin" [ - iperf ethtool iproute2 socat + telnet = pkgs.runCommand "inetutils-telnet" + { } + '' + mkdir -p $out/bin + ln -s ${pkgs.inetutils}/bin/telnet $out/bin + ''; + + generatedPath = with pkgs; makeSearchPath "bin" [ + iperf + ethtool + iproute2 + socat + # mn errors out without a telnet binary + # pkgs.inetutils brings an undesired ifconfig into PATH see #43105 + nettools + telnet ]; - pyEnv = pkgs.python.withPackages(ps: [ ps.mininet-python ]); + pyEnv = pkgs.python3.withPackages (ps: [ ps.mininet-python ]); mnexecWrapped = pkgs.runCommand "mnexec-wrapper" - { nativeBuildInputs = [ pkgs.makeWrapper pkgs.pythonPackages.wrapPython ]; } + { nativeBuildInputs = [ pkgs.makeWrapper pkgs.python3Packages.wrapPython ]; } '' makeWrapper ${pkgs.mininet}/bin/mnexec \ $out/bin/mnexec \ --prefix PATH : "${generatedPath}" - ln -s ${pyEnv}/bin/mn $out/bin/mn - - # mn errors out without a telnet binary - # pkgs.inetutils brings an undesired ifconfig into PATH see #43105 - ln -s ${pkgs.inetutils}/bin/telnet $out/bin/telnet + makeWrapper ${pyEnv}/bin/mn \ + $out/bin/mn \ + --prefix PYTHONPATH : "${pyEnv}/${pyEnv.sitePackages}" \ + --prefix PATH : "${generatedPath}" ''; in { diff --git a/nixos/modules/services/development/athens.md b/nixos/modules/services/development/athens.md new file mode 100644 index 000000000000..77663db509d5 --- /dev/null +++ b/nixos/modules/services/development/athens.md @@ -0,0 +1,52 @@ +# Athens {#module-athens} + +*Source:* {file}`modules/services/development/athens.nix` + +*Upstream documentation:* <https://docs.gomods.io/> + +[Athens](https://github.com/gomods/athens) +is a Go module datastore and proxy + +The main goal of Athens is providing a Go proxy (`$GOPROXY`) in regions without access to `https://proxy.golang.org` or to +improve the speed of Go module downloads for CI/CD systems. + +## Configuring {#module-services-development-athens-configuring} + +A complete list of options for the Athens module may be found +[here](#opt-services.athens.enable). + +## Basic usage for a caching proxy configuration {#opt-services-development-athens-caching-proxy} + +A very basic configuration for Athens that acts as a caching and forwarding HTTP proxy is: +``` +{ + services.athens = { + enable = true; + }; +} +``` + +If you want to prevent Athens from writing to disk, you can instead configure it to cache modules only in memory: + +``` +{ + services.athens = { + enable = true; + storageType = "memory"; + }; +} +``` + +To use the local proxy in Go builds, you can set the proxy as environment variable: + +``` +{ + environment.variables = { + GOPROXY = "http://localhost:3000" + }; +} +``` + +It is currently not possible to use the local proxy for builds done by the Nix daemon. This might be enabled +by experimental features, specifically [`configurable-impure-env`](https://nixos.org/manual/nix/unstable/contributing/experimental-features#xp-feature-configurable-impure-env), +in upcoming Nix versions. diff --git a/nixos/modules/services/development/athens.nix b/nixos/modules/services/development/athens.nix new file mode 100644 index 000000000000..34f8964a3bd5 --- /dev/null +++ b/nixos/modules/services/development/athens.nix @@ -0,0 +1,936 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.athens; + + athensConfig = flip recursiveUpdate cfg.extraConfig ( + { + GoBinary = "${cfg.goBinary}/bin/go"; + GoEnv = cfg.goEnv; + GoBinaryEnvVars = lib.mapAttrsToList (k: v: "${k}=${v}") cfg.goBinaryEnvVars; + GoGetWorkers = cfg.goGetWorkers; + GoGetDir = cfg.goGetDir; + ProtocolWorkers = cfg.protocolWorkers; + LogLevel = cfg.logLevel; + CloudRuntime = cfg.cloudRuntime; + EnablePprof = cfg.enablePprof; + PprofPort = ":${toString cfg.pprofPort}"; + FilterFile = cfg.filterFile; + RobotsFile = cfg.robotsFile; + Timeout = cfg.timeout; + StorageType = cfg.storageType; + TLSCertFile = cfg.tlsCertFile; + TLSKeyFile = cfg.tlsKeyFile; + Port = ":${toString cfg.port}"; + UnixSocket = cfg.unixSocket; + GlobalEndpoint = cfg.globalEndpoint; + BasicAuthUser = cfg.basicAuthUser; + BasicAuthPass = cfg.basicAuthPass; + ForceSSL = cfg.forceSSL; + ValidatorHook = cfg.validatorHook; + PathPrefix = cfg.pathPrefix; + NETRCPath = cfg.netrcPath; + GithubToken = cfg.githubToken; + HGRCPath = cfg.hgrcPath; + TraceExporter = cfg.traceExporter; + StatsExporter = cfg.statsExporter; + SumDBs = cfg.sumDBs; + NoSumPatterns = cfg.noSumPatterns; + DownloadMode = cfg.downloadMode; + NetworkMode = cfg.networkMode; + DownloadURL = cfg.downloadURL; + SingleFlightType = cfg.singleFlightType; + IndexType = cfg.indexType; + ShutdownTimeout = cfg.shutdownTimeout; + SingleFlight = { + Etcd = { + Endpoints = builtins.concatStringsSep "," cfg.singleFlight.etcd.endpoints; + }; + Redis = { + Endpoint = cfg.singleFlight.redis.endpoint; + Password = cfg.singleFlight.redis.password; + LockConfig = { + TTL = cfg.singleFlight.redis.lockConfig.ttl; + Timeout = cfg.singleFlight.redis.lockConfig.timeout; + MaxRetries = cfg.singleFlight.redis.lockConfig.maxRetries; + }; + }; + RedisSentinel = { + Endpoints = cfg.singleFlight.redisSentinel.endpoints; + MasterName = cfg.singleFlight.redisSentinel.masterName; + SentinelPassword = cfg.singleFlight.redisSentinel.sentinelPassword; + LockConfig = { + TTL = cfg.singleFlight.redisSentinel.lockConfig.ttl; + Timeout = cfg.singleFlight.redisSentinel.lockConfig.timeout; + MaxRetries = cfg.singleFlight.redisSentinel.lockConfig.maxRetries; + }; + }; + }; + Storage = { + CDN = { + Endpoint = cfg.storage.cdn.endpoint; + }; + Disk = { + RootPath = cfg.storage.disk.rootPath; + }; + GCP = { + ProjectID = cfg.storage.gcp.projectID; + Bucket = cfg.storage.gcp.bucket; + JSONKey = cfg.storage.gcp.jsonKey; + }; + Minio = { + Endpoint = cfg.storage.minio.endpoint; + Key = cfg.storage.minio.key; + Secret = cfg.storage.minio.secret; + EnableSSL = cfg.storage.minio.enableSSL; + Bucket = cfg.storage.minio.bucket; + region = cfg.storage.minio.region; + }; + Mongo = { + URL = cfg.storage.mongo.url; + DefaultDBName = cfg.storage.mongo.defaultDBName; + CertPath = cfg.storage.mongo.certPath; + Insecure = cfg.storage.mongo.insecure; + }; + S3 = { + Region = cfg.storage.s3.region; + Key = cfg.storage.s3.key; + Secret = cfg.storage.s3.secret; + Token = cfg.storage.s3.token; + Bucket = cfg.storage.s3.bucket; + ForcePathStyle = cfg.storage.s3.forcePathStyle; + UseDefaultConfiguration = cfg.storage.s3.useDefaultConfiguration; + CredentialsEndpoint = cfg.storage.s3.credentialsEndpoint; + AwsContainerCredentialsRelativeURI = cfg.storage.s3.awsContainerCredentialsRelativeURI; + Endpoint = cfg.storage.s3.endpoint; + }; + AzureBlob = { + AccountName = cfg.storage.azureblob.accountName; + AccountKey = cfg.storage.azureblob.accountKey; + ContainerName = cfg.storage.azureblob.containerName; + }; + External = { + URL = cfg.storage.external.url; + }; + }; + Index = { + MySQL = { + Protocol = cfg.index.mysql.protocol; + Host = cfg.index.mysql.host; + Port = cfg.index.mysql.port; + User = cfg.index.mysql.user; + Password = cfg.index.mysql.password; + Database = cfg.index.mysql.database; + Params = { + parseTime = cfg.index.mysql.params.parseTime; + timeout = cfg.index.mysql.params.timeout; + }; + }; + Postgres = { + Host = cfg.index.postgres.host; + Port = cfg.index.postgres.port; + User = cfg.index.postgres.user; + Password = cfg.index.postgres.password; + Database = cfg.index.postgres.database; + Params = { + connect_timeout = cfg.index.postgres.params.connect_timeout; + sslmode = cfg.index.postgres.params.sslmode; + }; + }; + }; + } + ); + + configFile = pkgs.runCommandLocal "config.toml" { } '' + ${pkgs.buildPackages.jq}/bin/jq 'del(..|nulls)' \ + < ${pkgs.writeText "config.json" (builtins.toJSON athensConfig)} | \ + ${pkgs.buildPackages.remarshal}/bin/remarshal -if json -of toml \ + > $out + ''; +in +{ + meta = { + maintainers = pkgs.athens.meta.maintainers; + doc = ./athens.md; + }; + + options.services.athens = { + enable = mkEnableOption (lib.mdDoc "Go module datastore and proxy"); + + package = mkOption { + default = pkgs.athens; + defaultText = literalExpression "pkgs.athens"; + example = "pkgs.athens"; + description = lib.mdDoc "Which athens derivation to use"; + type = types.package; + }; + + goBinary = mkOption { + type = types.package; + default = pkgs.go; + defaultText = literalExpression "pkgs.go"; + example = "pkgs.go_1_21"; + description = lib.mdDoc '' + The Go package used by Athens at runtime. + + Athens primarily runs two Go commands: + 1. `go mod download -json <module>@<version>` + 2. `go list -m -json <module>@latest` + ''; + }; + + goEnv = mkOption { + type = types.enum [ "development" "production" ]; + description = lib.mdDoc "Specifies the type of environment to run. One of 'development' or 'production'."; + default = "development"; + example = "production"; + }; + + goBinaryEnvVars = mkOption { + type = types.attrs; + description = lib.mdDoc "Environment variables to pass to the Go binary."; + example = '' + { "GOPROXY" = "direct", "GODEBUG" = "true" } + ''; + default = { }; + }; + + goGetWorkers = mkOption { + type = types.int; + description = lib.mdDoc "Number of workers concurrently downloading modules."; + default = 10; + example = 32; + }; + + goGetDir = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc '' + Temporary directory that Athens will use to + fetch modules from VCS prior to persisting + them to a storage backend. + + If the value is empty, Athens will use the + default OS temp directory. + ''; + default = null; + example = "/tmp/athens"; + }; + + protocolWorkers = mkOption { + type = types.int; + description = lib.mdDoc "Number of workers concurrently serving protocol paths."; + default = 30; + }; + + logLevel = mkOption { + type = types.nullOr (types.enum [ "panic" "fatal" "error" "warning" "info" "debug" "trace" ]); + description = lib.mdDoc '' + Log level for Athens. + Supports all logrus log levels (https://github.com/Sirupsen/logrus#level-logging)". + ''; + default = "warning"; + example = "debug"; + }; + + cloudRuntime = mkOption { + type = types.enum [ "GCP" "none" ]; + description = lib.mdDoc '' + Specifies the Cloud Provider on which the Proxy/registry is running. + ''; + default = "none"; + example = "GCP"; + }; + + enablePprof = mkOption { + type = types.bool; + description = lib.mdDoc "Enable pprof endpoints."; + default = false; + }; + + pprofPort = mkOption { + type = types.port; + description = lib.mdDoc "Port number for pprof endpoints."; + default = 3301; + example = 443; + }; + + filterFile = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc ''Filename for the include exclude filter.''; + default = null; + example = literalExpression '' + pkgs.writeText "filterFile" ''' + - github.com/azure + + github.com/azure/azure-sdk-for-go + D golang.org/x/tools + ''' + ''; + }; + + robotsFile = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc ''Provides /robots.txt for net crawlers.''; + default = null; + example = literalExpression ''pkgs.writeText "robots.txt" "# my custom robots.txt ..."''; + }; + + timeout = mkOption { + type = types.int; + description = lib.mdDoc "Timeout for external network calls in seconds."; + default = 300; + example = 3; + }; + + storageType = mkOption { + type = types.enum [ "memory" "disk" "mongo" "gcp" "minio" "s3" "azureblob" "external" ]; + description = lib.mdDoc "Specifies the type of storage backend to use."; + default = "disk"; + }; + + tlsCertFile = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc "Path to the TLS certificate file."; + default = null; + example = "/etc/ssl/certs/athens.crt"; + }; + + tlsKeyFile = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc "Path to the TLS key file."; + default = null; + example = "/etc/ssl/certs/athens.key"; + }; + + port = mkOption { + type = types.port; + default = 3000; + description = lib.mdDoc '' + Port number Athens listens on. + ''; + example = 443; + }; + + unixSocket = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc '' + Path to the unix socket file. + If set, Athens will listen on the unix socket instead of TCP socket. + ''; + default = null; + example = "/run/athens.sock"; + }; + + globalEndpoint = mkOption { + type = types.str; + description = lib.mdDoc '' + Endpoint for a package registry in case of a proxy cache miss. + ''; + default = ""; + example = "http://upstream-athens.example.com:3000"; + }; + + basicAuthUser = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc '' + Username for basic auth. + ''; + default = null; + example = "user"; + }; + + basicAuthPass = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc '' + Password for basic auth. Warning: this is stored in plain text in the config file. + ''; + default = null; + example = "swordfish"; + }; + + forceSSL = mkOption { + type = types.bool; + description = lib.mdDoc '' + Force SSL redirects for incoming requests. + ''; + default = false; + }; + + validatorHook = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc '' + Endpoint to validate modules against. + + Not used if empty. + ''; + default = null; + example = "https://validation.example.com"; + }; + + pathPrefix = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc '' + Sets basepath for all routes. + ''; + default = null; + example = "/athens"; + }; + + netrcPath = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc '' + Path to the .netrc file. + ''; + default = null; + example = "/home/user/.netrc"; + }; + + githubToken = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc '' + Creates .netrc file with the given token to be used for GitHub. + Warning: this is stored in plain text in the config file. + ''; + default = null; + example = "ghp_1234567890"; + }; + + hgrcPath = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc '' + Path to the .hgrc file. + ''; + default = null; + example = "/home/user/.hgrc"; + }; + + traceExporter = mkOption { + type = types.nullOr (types.enum [ "jaeger" "datadog" ]); + description = lib.mdDoc '' + Trace exporter to use. + ''; + default = null; + }; + + traceExporterURL = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc '' + URL endpoint that traces will be sent to. + ''; + default = null; + example = "http://localhost:14268"; + }; + + statsExporter = mkOption { + type = types.nullOr (types.enum [ "prometheus" ]); + description = lib.mdDoc "Stats exporter to use."; + default = null; + }; + + sumDBs = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + List of fully qualified URLs that Athens will proxy + that the go command can use a checksum verifier. + ''; + default = [ "https://sum.golang.org" ]; + }; + + noSumPatterns = mkOption { + type = types.listOf types.str; + description = lib.mdDoc '' + List of patterns that Athens sum db proxy will return a 403 for. + ''; + default = [ ]; + example = [ "github.com/mycompany/*" ]; + }; + + downloadMode = mkOption { + type = types.oneOf [ (types.enum [ "sync" "async" "redirect" "async_redirect" "none" ]) (types.strMatching "^file:.*$|^custom:.*$") ]; + description = lib.mdDoc '' + Defines how Athens behaves when a module@version + is not found in storage. There are 7 options: + 1. "sync": download the module synchronously and + return the results to the client. + 2. "async": return 404, but asynchronously store the module + in the storage backend. + 3. "redirect": return a 301 redirect status to the client + with the base URL as the DownloadRedirectURL from below. + 4. "async_redirect": same as option number 3 but it will + asynchronously store the module to the backend. + 5. "none": return 404 if a module is not found and do nothing. + 6. "file:<path>": will point to an HCL file that specifies + any of the 5 options above based on different import paths. + 7. "custom:<base64-encoded-hcl>" is the same as option 6 + but the file is fully encoded in the option. This is + useful for using an environment variable in serverless + deployments. + ''; + default = "async_redirect"; + }; + + networkMode = mkOption { + type = types.enum [ "strict" "offline" "fallback" ]; + description = lib.mdDoc '' + Configures how Athens will return the results + of the /list endpoint as it can be assembled from both its own + storage and the upstream VCS. + + Note, that for better error messaging, this would also affect how other + endpoints behave. + + Modes: + 1. strict: merge VCS versions with storage versions, but fail if either of them fails. + 2. offline: only get storage versions, never reach out to VCS. + 3. fallback: only return storage versions, if VCS fails. Note this means that you may + see inconsistent results since fallback mode does a best effort of giving you what's + available at the time of requesting versions. + ''; + default = "strict"; + }; + + downloadURL = mkOption { + type = types.str; + description = lib.mdDoc "URL used if DownloadMode is set to redirect."; + default = "https://proxy.golang.org"; + }; + + singleFlightType = mkOption { + type = types.enum [ "memory" "etcd" "redis" "redis-sentinel" "gcp" "azureblob" ]; + description = lib.mdDoc '' + Determines what mechanism Athens uses to manage concurrency flowing into the Athens backend. + ''; + default = "memory"; + }; + + indexType = mkOption { + type = types.enum [ "none" "memory" "mysql" "postgres" ]; + description = lib.mdDoc '' + Type of index backend Athens will use. + ''; + default = "none"; + }; + + shutdownTimeout = mkOption { + type = types.int; + description = lib.mdDoc '' + Number of seconds to wait for the server to shutdown gracefully. + ''; + default = 60; + example = 1; + }; + + singleFlight = { + etcd = { + endpoints = mkOption { + type = types.listOf types.str; + description = lib.mdDoc "URLs that determine all distributed etcd servers."; + default = [ ]; + example = [ "localhost:2379" ]; + }; + }; + redis = { + endpoint = mkOption { + type = types.str; + description = lib.mdDoc "URL of the redis server."; + default = ""; + example = "localhost:6379"; + }; + password = mkOption { + type = types.str; + description = lib.mdDoc "Password for the redis server. Warning: this is stored in plain text in the config file."; + default = ""; + example = "swordfish"; + }; + + lockConfig = { + ttl = mkOption { + type = types.int; + description = lib.mdDoc "TTL for the lock in seconds."; + default = 900; + example = 1; + }; + timeout = mkOption { + type = types.int; + description = lib.mdDoc "Timeout for the lock in seconds."; + default = 15; + example = 1; + }; + maxRetries = mkOption { + type = types.int; + description = lib.mdDoc "Maximum number of retries for the lock."; + default = 10; + example = 1; + }; + }; + }; + + redisSentinel = { + endpoints = mkOption { + type = types.listOf types.str; + description = lib.mdDoc "URLs that determine all distributed redis servers."; + default = [ ]; + example = [ "localhost:26379" ]; + }; + masterName = mkOption { + type = types.str; + description = lib.mdDoc "Name of the sentinel master server."; + default = ""; + example = "redis-1"; + }; + sentinelPassword = mkOption { + type = types.str; + description = lib.mdDoc "Password for the sentinel server. Warning: this is stored in plain text in the config file."; + default = ""; + example = "swordfish"; + }; + + lockConfig = { + ttl = mkOption { + type = types.int; + description = lib.mdDoc "TTL for the lock in seconds."; + default = 900; + example = 1; + }; + timeout = mkOption { + type = types.int; + description = lib.mdDoc "Timeout for the lock in seconds."; + default = 15; + example = 1; + }; + maxRetries = mkOption { + type = types.int; + description = lib.mdDoc "Maximum number of retries for the lock."; + default = 10; + example = 1; + }; + }; + }; + }; + + storage = { + cdn = { + endpoint = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "hostname of the CDN server."; + example = "cdn.example.com"; + default = null; + }; + }; + + disk = { + rootPath = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc "Athens disk root folder."; + default = "/var/lib/athens"; + }; + }; + + gcp = { + projectID = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "GCP project ID."; + example = "my-project"; + default = null; + }; + bucket = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "GCP backend storage bucket."; + example = "my-bucket"; + default = null; + }; + jsonKey = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Base64 encoded GCP service account key. Warning: this is stored in plain text in the config file."; + default = null; + }; + }; + + minio = { + endpoint = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Endpoint of the minio storage backend."; + example = "minio.example.com:9001"; + default = null; + }; + key = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Access key id for the minio storage backend."; + example = "minio"; + default = null; + }; + secret = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Secret key for the minio storage backend. Warning: this is stored in plain text in the config file."; + example = "minio123"; + default = null; + }; + enableSSL = mkOption { + type = types.bool; + description = lib.mdDoc "Enable SSL for the minio storage backend."; + default = false; + }; + bucket = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Bucket name for the minio storage backend."; + example = "gomods"; + default = null; + }; + region = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Region for the minio storage backend."; + example = "us-east-1"; + default = null; + }; + }; + + mongo = { + url = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "URL of the mongo database."; + example = "mongodb://localhost:27017"; + default = null; + }; + defaultDBName = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Name of the mongo database."; + example = "athens"; + default = null; + }; + certPath = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc "Path to the certificate file for the mongo database."; + example = "/etc/ssl/mongo.pem"; + default = null; + }; + insecure = mkOption { + type = types.bool; + description = lib.mdDoc "Allow insecure connections to the mongo database."; + default = false; + }; + }; + + s3 = { + region = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Region of the S3 storage backend."; + example = "eu-west-3"; + default = null; + }; + key = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Access key id for the S3 storage backend."; + example = "minio"; + default = null; + }; + secret = mkOption { + type = types.str; + description = lib.mdDoc "Secret key for the S3 storage backend. Warning: this is stored in plain text in the config file."; + default = ""; + }; + token = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Token for the S3 storage backend. Warning: this is stored in plain text in the config file."; + default = null; + }; + bucket = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Bucket name for the S3 storage backend."; + example = "gomods"; + default = null; + }; + forcePathStyle = mkOption { + type = types.bool; + description = lib.mdDoc "Force path style for the S3 storage backend."; + default = false; + }; + useDefaultConfiguration = mkOption { + type = types.bool; + description = lib.mdDoc "Use default configuration for the S3 storage backend."; + default = false; + }; + credentialsEndpoint = mkOption { + type = types.str; + description = lib.mdDoc "Credentials endpoint for the S3 storage backend."; + default = ""; + }; + awsContainerCredentialsRelativeURI = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Container relative url (used by fargate)."; + default = null; + }; + endpoint = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Endpoint for the S3 storage backend."; + default = null; + }; + }; + + azureblob = { + accountName = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Account name for the Azure Blob storage backend."; + default = null; + }; + accountKey = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Account key for the Azure Blob storage backend. Warning: this is stored in plain text in the config file."; + default = null; + }; + containerName = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Container name for the Azure Blob storage backend."; + default = null; + }; + }; + + external = { + url = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "URL of the backend storage layer."; + example = "https://athens.example.com"; + default = null; + }; + }; + }; + + index = { + mysql = { + protocol = mkOption { + type = types.str; + description = lib.mdDoc "Protocol for the MySQL database."; + default = "tcp"; + }; + host = mkOption { + type = types.str; + description = lib.mdDoc "Host for the MySQL database."; + default = "localhost"; + }; + port = mkOption { + type = types.int; + description = lib.mdDoc "Port for the MySQL database."; + default = 3306; + }; + user = mkOption { + type = types.str; + description = lib.mdDoc "User for the MySQL database."; + default = "root"; + }; + password = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Password for the MySQL database. Warning: this is stored in plain text in the config file."; + default = null; + }; + database = mkOption { + type = types.str; + description = lib.mdDoc "Database name for the MySQL database."; + default = "athens"; + }; + params = { + parseTime = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Parse time for the MySQL database."; + default = "true"; + }; + timeout = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Timeout for the MySQL database."; + default = "30s"; + }; + }; + }; + + postgres = { + host = mkOption { + type = types.str; + description = lib.mdDoc "Host for the Postgres database."; + default = "localhost"; + }; + port = mkOption { + type = types.int; + description = lib.mdDoc "Port for the Postgres database."; + default = 5432; + }; + user = mkOption { + type = types.str; + description = lib.mdDoc "User for the Postgres database."; + default = "postgres"; + }; + password = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Password for the Postgres database. Warning: this is stored in plain text in the config file."; + default = null; + }; + database = mkOption { + type = types.str; + description = lib.mdDoc "Database name for the Postgres database."; + default = "athens"; + }; + params = { + connect_timeout = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "Connect timeout for the Postgres database."; + default = "30s"; + }; + sslmode = mkOption { + type = types.nullOr types.str; + description = lib.mdDoc "SSL mode for the Postgres database."; + default = "disable"; + }; + }; + }; + }; + + extraConfig = mkOption { + type = types.attrs; + description = lib.mdDoc '' + Extra configuration options for the athens config file. + ''; + default = { }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.athens = { + description = "Athens Go module proxy"; + documentation = [ "https://docs.gomods.io" ]; + + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + + serviceConfig = { + Restart = "on-abnormal"; + Nice = 5; + ExecStart = ''${cfg.package}/bin/athens -config_file=${configFile}''; + + KillMode = "mixed"; + KillSignal = "SIGINT"; + TimeoutStopSec = cfg.shutdownTimeout; + + LimitNOFILE = 1048576; + LimitNPROC = 512; + + DynamicUser = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = "read-only"; + ProtectSystem = "full"; + + ReadWritePaths = mkIf (cfg.storage.disk.rootPath != null && (! hasPrefix "/var/lib/" cfg.storage.disk.rootPath)) [ cfg.storage.disk.rootPath ]; + StateDirectory = mkIf (hasPrefix "/var/lib/" cfg.storage.disk.rootPath) [ (removePrefix "/var/lib/" cfg.storage.disk.rootPath) ]; + + CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + NoNewPrivileges = true; + }; + }; + + networking.firewall = { + allowedTCPPorts = optionals (cfg.unixSocket == null) [ cfg.port ] + ++ optionals cfg.enablePprof [ cfg.pprofPort ]; + }; + }; + +} diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix index d44219008466..af9768c98ac3 100644 --- a/nixos/modules/services/misc/sourcehut/default.nix +++ b/nixos/modules/services/misc/sourcehut/default.nix @@ -1325,6 +1325,11 @@ in (import ./service.nix "paste" { inherit configIniOfService; port = 5011; + extraServices.pastesrht-api = { + serviceConfig.Restart = "always"; + serviceConfig.RestartSec = "5s"; + serviceConfig.ExecStart = "${pkgs.sourcehut.pastesrht}/bin/pastesrht-api -b ${cfg.listenAddress}:${toString (cfg.paste.port + 100)}"; + }; }) (import ./service.nix "todo" { diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix index 235f29ab8a6a..3400df370e48 100644 --- a/nixos/modules/services/security/fail2ban.nix +++ b/nixos/modules/services/security/fail2ban.nix @@ -128,8 +128,8 @@ in }; banaction-allports = mkOption { - default = if config.networking.nftables.enable then "nftables-allport" else "iptables-allport"; - defaultText = literalExpression ''if config.networking.nftables.enable then "nftables-allport" else "iptables-allport"''; + default = if config.networking.nftables.enable then "nftables-allports" else "iptables-allports"; + defaultText = literalExpression ''if config.networking.nftables.enable then "nftables-allports" else "iptables-allports"''; type = types.str; description = lib.mdDoc '' Default banning action (e.g. iptables, iptables-new, iptables-multiport, diff --git a/nixos/modules/services/web-apps/lanraragi.nix b/nixos/modules/services/web-apps/lanraragi.nix index f1ab8b8b4eb4..6e452e7a3b35 100644 --- a/nixos/modules/services/web-apps/lanraragi.nix +++ b/nixos/modules/services/web-apps/lanraragi.nix @@ -72,11 +72,10 @@ in "HOME" = "/var/lib/lanraragi"; }; preStart = '' - REDIS_PASS=${lib.optionalString (cfg.redis.passwordFile != null) "$(head -n1 ${cfg.redis.passwordFile})"} cat > lrr.conf <<EOF { redis_address => "127.0.0.1:${toString cfg.redis.port}", - redis_password => "$REDIS_PASS", + redis_password => "${lib.optionalString (cfg.redis.passwordFile != null) ''$(head -n1 ${cfg.redis.passwordFile})''}", redis_database => "0", redis_database_minion => "1", redis_database_config => "2", @@ -84,15 +83,9 @@ in } EOF '' + lib.optionalString (cfg.passwordFile != null) '' - PASS_HASH=$( - PASS=$(head -n1 ${cfg.passwordFile}) ${cfg.package.perlEnv}/bin/perl -I${cfg.package}/share/lanraragi/lib -e \ - 'use LANraragi::Controller::Config; print LANraragi::Controller::Config::make_password_hash($ENV{PASS})' \ - 2>/dev/null - ) - - ${lib.getExe pkgs.redis} -h 127.0.0.1 -p ${toString cfg.redis.port} -a "$REDIS_PASS" <<EOF + ${lib.getExe pkgs.redis} -h 127.0.0.1 -p ${toString cfg.redis.port} ${lib.optionalString (cfg.redis.passwordFile != null) ''-a "$(head -n1 ${cfg.redis.passwordFile})"''}<<EOF SELECT 2 - HSET LRR_CONFIG password $PASS_HASH + HSET LRR_CONFIG password $(${cfg.package}/bin/helpers/lrr-make-password-hash $(head -n1 ${cfg.passwordFile})) EOF ''; }; diff --git a/nixos/modules/system/boot/unl0kr.nix b/nixos/modules/system/boot/unl0kr.nix new file mode 100644 index 000000000000..8d9af37382e0 --- /dev/null +++ b/nixos/modules/system/boot/unl0kr.nix @@ -0,0 +1,89 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.boot.initrd.unl0kr; +in +{ + options.boot.initrd.unl0kr = { + enable = lib.mkEnableOption (lib.mdDoc "unl0kr in initrd") // { + description = lib.mdDoc '' + Whether to enable the unl0kr on-screen keyboard in initrd to unlock LUKS. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + meta.maintainers = with lib.maintainers; [ tomfitzhenry ]; + assertions = [ + { + assertion = cfg.enable -> config.boot.initrd.systemd.enable; + message = "boot.initrd.unl0kr is only supported with boot.initrd.systemd."; + } + ]; + + boot.initrd.systemd = { + storePaths = with pkgs; [ + "${pkgs.gnugrep}/bin/grep" + libinput + xkeyboard_config + "${config.boot.initrd.systemd.package}/lib/systemd/systemd-reply-password" + "${pkgs.unl0kr}/bin/unl0kr" + ]; + services = { + unl0kr-ask-password = { + description = "Forward Password Requests to unl0kr"; + conflicts = [ + "emergency.service" + "initrd-switch-root.target" + "shutdown.target" + ]; + unitConfig.DefaultDependencies = false; + after = [ + "systemd-vconsole-setup.service" + "udev.service" + ]; + before = [ + "shutdown.target" + ]; + script = '' + # This script acts as a Password Agent: https://systemd.io/PASSWORD_AGENTS/ + + DIR=/run/systemd/ask-password/ + # If a user has multiple encrypted disks, the requests might come in different times, + # so make sure to answer as many requests as we can. Once boot succeeds, other + # password agents will be responsible for watching for requests. + while [ -d $DIR ] && [ "$(ls -A $DIR/ask.*)" ]; + do + for file in `ls $DIR/ask.*`; do + socket="$(cat "$file" | ${pkgs.gnugrep}/bin/grep "Socket=" | cut -d= -f2)" + ${pkgs.unl0kr}/bin/unl0kr | ${config.boot.initrd.systemd.package}/lib/systemd/systemd-reply-password 1 "$socket" + done + done + ''; + }; + }; + + paths = { + unl0kr-ask-password = { + description = "Forward Password Requests to unl0kr"; + conflicts = [ + "emergency.service" + "initrd-switch-root.target" + "shutdown.target" + ]; + unitConfig.DefaultDependencies = false; + before = [ + "shutdown.target" + "paths.target" + "cryptsetup.target" + ]; + wantedBy = [ "sysinit.target" ]; + pathConfig = { + DirectoryNotEmpty = "/run/systemd/ask-password"; + MakeDirectory = true; + }; + }; + }; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 1e11cc220805..480439c2a25e 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -813,6 +813,7 @@ in { systemd-initrd-luks-empty-passphrase = handleTest ./initrd-luks-empty-passphrase.nix { systemdStage1 = true; }; systemd-initrd-luks-password = handleTest ./systemd-initrd-luks-password.nix {}; systemd-initrd-luks-tpm2 = handleTest ./systemd-initrd-luks-tpm2.nix {}; + systemd-initrd-luks-unl0kr = handleTest ./systemd-initrd-luks-unl0kr.nix {}; systemd-initrd-modprobe = handleTest ./systemd-initrd-modprobe.nix {}; systemd-initrd-shutdown = handleTest ./systemd-shutdown.nix { systemdStage1 = true; }; systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {}; diff --git a/nixos/tests/lanraragi.nix b/nixos/tests/lanraragi.nix index f513ac9d252b..7a4a1a489bdf 100644 --- a/nixos/tests/lanraragi.nix +++ b/nixos/tests/lanraragi.nix @@ -10,19 +10,17 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { services.lanraragi = { enable = true; passwordFile = pkgs.writeText "lrr-test-pass" '' - ultra-secure-password + Ultra-secure-p@ssword-"with-spec1al\chars ''; port = 4000; redis = { port = 4001; passwordFile = pkgs.writeText "redis-lrr-test-pass" '' - still-a-very-secure-password + 123-redis-PASS ''; }; }; }; - - }; testScript = '' @@ -34,7 +32,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { machine2.wait_for_unit("lanraragi.service") machine2.wait_until_succeeds("curl -f localhost:4000") - machine2.succeed("[ $(curl -o /dev/null -X post 'http://localhost:4000/login' --data-raw 'password=ultra-secure-password' -w '%{http_code}') -eq 302 ]") + machine2.succeed("[ $(curl -o /dev/null -X post 'http://localhost:4000/login' --data-raw 'password=Ultra-secure-p@ssword-\"with-spec1al\\chars' -w '%{http_code}') -eq 302 ]") ''; }) diff --git a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix index e638f2e5b861..addc898bd760 100644 --- a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix +++ b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix @@ -41,10 +41,13 @@ in { }; secretFile = "/etc/nextcloud-secrets.json"; - extraOptions.redis = { - dbindex = 0; - timeout = 1.5; - # password handled via secretfile below + extraOptions = { + allow_local_remote_servers = true; + redis = { + dbindex = 0; + timeout = 1.5; + # password handled via secretfile below + }; }; configureRedis = true; }; @@ -62,6 +65,7 @@ in { services.postgresql = { enable = true; + package = pkgs.postgresql_14; }; systemd.services.postgresql.postStart = pkgs.lib.mkAfter '' password=$(cat ${passFile}) diff --git a/nixos/tests/nixops/default.nix b/nixos/tests/nixops/default.nix index b8f747b2a19f..f7a26f2461c4 100644 --- a/nixos/tests/nixops/default.nix +++ b/nixos/tests/nixops/default.nix @@ -1,4 +1,6 @@ -{ pkgs, ... }: +{ pkgs +, testers +, ... }: let inherit (pkgs) lib; @@ -19,7 +21,7 @@ let passthru.override = args': testsForPackage (args // args'); }; - testLegacyNetwork = { nixopsPkg, ... }: pkgs.nixosTest ({ + testLegacyNetwork = { nixopsPkg, ... }: testers.nixosTest ({ name = "nixops-legacy-network"; nodes = { deployer = { config, lib, nodes, pkgs, ... }: { diff --git a/nixos/tests/systemd-initrd-luks-unl0kr.nix b/nixos/tests/systemd-initrd-luks-unl0kr.nix new file mode 100644 index 000000000000..0658a098cfa2 --- /dev/null +++ b/nixos/tests/systemd-initrd-luks-unl0kr.nix @@ -0,0 +1,75 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: let + passphrase = "secret"; +in { + name = "systemd-initrd-luks-unl0kr"; + meta = with pkgs.lib.maintainers; { + maintainers = [ tomfitzhenry ]; + }; + + enableOCR = true; + + nodes.machine = { pkgs, ... }: { + virtualisation = { + emptyDiskImages = [ 512 512 ]; + useBootLoader = true; + mountHostNixStore = true; + useEFIBoot = true; + qemu.options = [ + "-vga virtio" + ]; + }; + boot.loader.systemd-boot.enable = true; + + boot.initrd.availableKernelModules = [ + "evdev" # for entering pw + "bochs" + ]; + + environment.systemPackages = with pkgs; [ cryptsetup ]; + boot.initrd = { + systemd = { + enable = true; + emergencyAccess = true; + }; + unl0kr.enable = true; + }; + + specialisation.boot-luks.configuration = { + boot.initrd.luks.devices = lib.mkVMOverride { + # We have two disks and only type one password - key reuse is in place + cryptroot.device = "/dev/vdb"; + cryptroot2.device = "/dev/vdc"; + }; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; + virtualisation.fileSystems."/".autoFormat = true; + # test mounting device unlocked in initrd after switching root + virtualisation.fileSystems."/cryptroot2".device = "/dev/mapper/cryptroot2"; + }; + }; + + testScript = '' + # Create encrypted volume + machine.wait_for_unit("multi-user.target") + machine.succeed("echo -n ${passphrase} | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -") + machine.succeed("echo -n ${passphrase} | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -") + machine.succeed("echo -n ${passphrase} | cryptsetup luksOpen -q /dev/vdc cryptroot2") + machine.succeed("mkfs.ext4 /dev/mapper/cryptroot2") + + # Boot from the encrypted disk + machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") + machine.succeed("sync") + machine.crash() + + # Boot and decrypt the disk + machine.start() + machine.wait_for_text("Password required for booting") + machine.screenshot("prompt") + machine.send_chars("${passphrase}") + machine.screenshot("pw") + machine.send_chars("\n") + machine.wait_for_unit("multi-user.target") + + assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount"), "/dev/mapper/cryptroot do not appear in mountpoints list" + assert "/dev/mapper/cryptroot2 on /cryptroot2 type ext4" in machine.succeed("mount") + ''; +}) |