mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-07-22 16:41:08 +00:00
Compare commits
1 Commits
public-inb
...
23.05-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8966c43feb |
4
.github/workflows/periodic-merge-24h.yml
vendored
4
.github/workflows/periodic-merge-24h.yml
vendored
@@ -38,10 +38,6 @@ jobs:
|
||||
into: staging-next-22.11
|
||||
- from: staging-next-22.11
|
||||
into: staging-22.11
|
||||
- from: release-23.05
|
||||
into: staging-next-23.05
|
||||
- from: staging-next-23.05
|
||||
into: staging-23.05
|
||||
name: ${{ matrix.pairs.from }} → ${{ matrix.pairs.into }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -8,7 +8,7 @@ A package set is available for each CUDA version, so for example
|
||||
`cudaPackages_11_6`. Within each set is a matching version of the above listed
|
||||
packages. Additionally, other versions of the packages that are packaged and
|
||||
compatible are available as well. For example, there can be a
|
||||
`cudaPackages.cudnn_8_3` package.
|
||||
`cudaPackages.cudnn_8_3_2` package.
|
||||
|
||||
To use one or more CUDA packages in an expression, give the expression a `cudaPackages` parameter, and in case CUDA is optional
|
||||
```nix
|
||||
@@ -28,7 +28,7 @@ set.
|
||||
```nix
|
||||
mypkg = let
|
||||
cudaPackages = cudaPackages_11_5.overrideScope' (final: prev: {
|
||||
cudnn = prev.cudnn_8_3;
|
||||
cudnn = prev.cudnn_8_3_2;
|
||||
}});
|
||||
in callPackage { inherit cudaPackages; };
|
||||
```
|
||||
|
||||
@@ -19,7 +19,7 @@ In the following is an example expression using `buildGoModule`, the following a
|
||||
To avoid updating this field when dependencies change, run `go mod vendor` in your source repo and set `vendorHash = null;`
|
||||
|
||||
To obtain the actual hash, set `vendorHash = lib.fakeSha256;` and run the build ([more details here](#sec-source-hashes)).
|
||||
- `proxyVendor`: Fetches (go mod download) and proxies the vendor directory. This is useful if your code depends on c code and go mod tidy does not include the needed sources to build or if any dependency has case-insensitive conflicts which will produce platform-dependent `vendorHash` checksums.
|
||||
- `proxyVendor`: Fetches (go mod download) and proxies the vendor directory. This is useful if your code depends on c code and go mod tidy does not include the needed sources to build or if any dependency has case-insensitive conflicts which will produce platform dependant `vendorHash` checksums.
|
||||
- `modPostBuild`: Shell commands to run after the build of the go-modules executes `go mod vendor`, and before calculating fixed output derivation's `vendorHash` (or `vendorSha256`). Note that if you change this attribute, you need to update `vendorHash` (or `vendorSha256`) attribute.
|
||||
|
||||
```nix
|
||||
|
||||
@@ -117,11 +117,10 @@ let
|
||||
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
|
||||
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
|
||||
hiPrioSet getLicenseFromSpdxId getExe;
|
||||
inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile;
|
||||
inherit (self.sources) cleanSourceFilter
|
||||
inherit (self.sources) pathType pathIsDirectory cleanSourceFilter
|
||||
cleanSource sourceByRegex sourceFilesBySuffices
|
||||
commitIdFromGitRepo cleanSourceWith pathHasContext
|
||||
canCleanSource pathIsGitRepo;
|
||||
canCleanSource pathIsRegularFile pathIsGitRepo;
|
||||
inherit (self.modules) evalModules setDefaultModuleLocation
|
||||
unifyModuleSyntax applyModuleArgsIfFunction mergeModules
|
||||
mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions
|
||||
|
||||
@@ -1,93 +1,13 @@
|
||||
# Functions for querying information about the filesystem
|
||||
# without copying any files to the Nix store.
|
||||
# Functions for copying sources to the Nix store.
|
||||
{ lib }:
|
||||
|
||||
# Tested in lib/tests/filesystem.sh
|
||||
let
|
||||
inherit (builtins)
|
||||
readDir
|
||||
pathExists
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
hasPrefix
|
||||
;
|
||||
|
||||
inherit (lib.filesystem)
|
||||
pathType
|
||||
;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
/*
|
||||
The type of a path. The path needs to exist and be accessible.
|
||||
The result is either "directory" for a directory, "regular" for a regular file, "symlink" for a symlink, or "unknown" for anything else.
|
||||
|
||||
Type:
|
||||
pathType :: Path -> String
|
||||
|
||||
Example:
|
||||
pathType /.
|
||||
=> "directory"
|
||||
|
||||
pathType /some/file.nix
|
||||
=> "regular"
|
||||
*/
|
||||
pathType =
|
||||
builtins.readFileType or
|
||||
# Nix <2.14 compatibility shim
|
||||
(path:
|
||||
if ! pathExists path
|
||||
# Fail irrecoverably to mimic the historic behavior of this function and
|
||||
# the new builtins.readFileType
|
||||
then abort "lib.filesystem.pathType: Path ${toString path} does not exist."
|
||||
# The filesystem root is the only path where `dirOf / == /` and
|
||||
# `baseNameOf /` is not valid. We can detect this and directly return
|
||||
# "directory", since we know the filesystem root can't be anything else.
|
||||
else if dirOf path == path
|
||||
then "directory"
|
||||
else (readDir (dirOf path)).${baseNameOf path}
|
||||
);
|
||||
|
||||
/*
|
||||
Whether a path exists and is a directory.
|
||||
|
||||
Type:
|
||||
pathIsDirectory :: Path -> Bool
|
||||
|
||||
Example:
|
||||
pathIsDirectory /.
|
||||
=> true
|
||||
|
||||
pathIsDirectory /this/does/not/exist
|
||||
=> false
|
||||
|
||||
pathIsDirectory /some/file.nix
|
||||
=> false
|
||||
*/
|
||||
pathIsDirectory = path:
|
||||
pathExists path && pathType path == "directory";
|
||||
|
||||
/*
|
||||
Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
|
||||
|
||||
Type:
|
||||
pathIsRegularFile :: Path -> Bool
|
||||
|
||||
Example:
|
||||
pathIsRegularFile /.
|
||||
=> false
|
||||
|
||||
pathIsRegularFile /this/does/not/exist
|
||||
=> false
|
||||
|
||||
pathIsRegularFile /some/file.nix
|
||||
=> true
|
||||
*/
|
||||
pathIsRegularFile = path:
|
||||
pathExists path && pathType path == "regular";
|
||||
|
||||
/*
|
||||
A map of all haskell packages defined in the given path,
|
||||
identified by having a cabal file with the same name as the
|
||||
|
||||
@@ -18,11 +18,21 @@ let
|
||||
pathExists
|
||||
readFile
|
||||
;
|
||||
inherit (lib.filesystem)
|
||||
pathType
|
||||
pathIsDirectory
|
||||
pathIsRegularFile
|
||||
;
|
||||
|
||||
/*
|
||||
Returns the type of a path: regular (for file), symlink, or directory.
|
||||
*/
|
||||
pathType = path: getAttr (baseNameOf path) (readDir (dirOf path));
|
||||
|
||||
/*
|
||||
Returns true if the path exists and is a directory, false otherwise.
|
||||
*/
|
||||
pathIsDirectory = path: if pathExists path then (pathType path) == "directory" else false;
|
||||
|
||||
/*
|
||||
Returns true if the path exists and is a regular file, false otherwise.
|
||||
*/
|
||||
pathIsRegularFile = path: if pathExists path then (pathType path) == "regular" else false;
|
||||
|
||||
/*
|
||||
A basic filter for `cleanSourceWith` that removes
|
||||
@@ -261,20 +271,11 @@ let
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
pathType = lib.warnIf (lib.isInOldestRelease 2305)
|
||||
"lib.sources.pathType has been moved to lib.filesystem.pathType."
|
||||
lib.filesystem.pathType;
|
||||
|
||||
pathIsDirectory = lib.warnIf (lib.isInOldestRelease 2305)
|
||||
"lib.sources.pathIsDirectory has been moved to lib.filesystem.pathIsDirectory."
|
||||
lib.filesystem.pathIsDirectory;
|
||||
|
||||
pathIsRegularFile = lib.warnIf (lib.isInOldestRelease 2305)
|
||||
"lib.sources.pathIsRegularFile has been moved to lib.filesystem.pathIsRegularFile."
|
||||
lib.filesystem.pathIsRegularFile;
|
||||
|
||||
inherit
|
||||
pathType
|
||||
pathIsDirectory
|
||||
pathIsRegularFile
|
||||
|
||||
pathIsGitRepo
|
||||
commitIdFromGitRepo
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Tests lib/filesystem.nix
|
||||
# Run:
|
||||
# [nixpkgs]$ lib/tests/filesystem.sh
|
||||
# or:
|
||||
# [nixpkgs]$ nix-build lib/tests/release.nix
|
||||
|
||||
set -euo pipefail
|
||||
shopt -s inherit_errexit
|
||||
|
||||
# Use
|
||||
# || die
|
||||
die() {
|
||||
echo >&2 "test case failed: " "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if test -n "${TEST_LIB:-}"; then
|
||||
NIX_PATH=nixpkgs="$(dirname "$TEST_LIB")"
|
||||
else
|
||||
NIX_PATH=nixpkgs="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.."; pwd)"
|
||||
fi
|
||||
export NIX_PATH
|
||||
|
||||
work="$(mktemp -d)"
|
||||
clean_up() {
|
||||
rm -rf "$work"
|
||||
}
|
||||
trap clean_up EXIT
|
||||
cd "$work"
|
||||
|
||||
mkdir directory
|
||||
touch regular
|
||||
ln -s target symlink
|
||||
mkfifo fifo
|
||||
|
||||
checkPathType() {
|
||||
local path=$1
|
||||
local expectedPathType=$2
|
||||
local actualPathType=$(nix-instantiate --eval --strict --json 2>&1 \
|
||||
-E '{ path }: let lib = import <nixpkgs/lib>; in lib.filesystem.pathType path' \
|
||||
--argstr path "$path")
|
||||
if [[ "$actualPathType" != "$expectedPathType" ]]; then
|
||||
die "lib.filesystem.pathType \"$path\" == $actualPathType, but $expectedPathType was expected"
|
||||
fi
|
||||
}
|
||||
|
||||
checkPathType "/" '"directory"'
|
||||
checkPathType "$PWD/directory" '"directory"'
|
||||
checkPathType "$PWD/regular" '"regular"'
|
||||
checkPathType "$PWD/symlink" '"symlink"'
|
||||
checkPathType "$PWD/fifo" '"unknown"'
|
||||
checkPathType "$PWD/non-existent" "error: evaluation aborted with the following error message: 'lib.filesystem.pathType: Path $PWD/non-existent does not exist.'"
|
||||
|
||||
checkPathIsDirectory() {
|
||||
local path=$1
|
||||
local expectedIsDirectory=$2
|
||||
local actualIsDirectory=$(nix-instantiate --eval --strict --json 2>&1 \
|
||||
-E '{ path }: let lib = import <nixpkgs/lib>; in lib.filesystem.pathIsDirectory path' \
|
||||
--argstr path "$path")
|
||||
if [[ "$actualIsDirectory" != "$expectedIsDirectory" ]]; then
|
||||
die "lib.filesystem.pathIsDirectory \"$path\" == $actualIsDirectory, but $expectedIsDirectory was expected"
|
||||
fi
|
||||
}
|
||||
|
||||
checkPathIsDirectory "/" "true"
|
||||
checkPathIsDirectory "$PWD/directory" "true"
|
||||
checkPathIsDirectory "$PWD/regular" "false"
|
||||
checkPathIsDirectory "$PWD/symlink" "false"
|
||||
checkPathIsDirectory "$PWD/fifo" "false"
|
||||
checkPathIsDirectory "$PWD/non-existent" "false"
|
||||
|
||||
checkPathIsRegularFile() {
|
||||
local path=$1
|
||||
local expectedIsRegularFile=$2
|
||||
local actualIsRegularFile=$(nix-instantiate --eval --strict --json 2>&1 \
|
||||
-E '{ path }: let lib = import <nixpkgs/lib>; in lib.filesystem.pathIsRegularFile path' \
|
||||
--argstr path "$path")
|
||||
if [[ "$actualIsRegularFile" != "$expectedIsRegularFile" ]]; then
|
||||
die "lib.filesystem.pathIsRegularFile \"$path\" == $actualIsRegularFile, but $expectedIsRegularFile was expected"
|
||||
fi
|
||||
}
|
||||
|
||||
checkPathIsRegularFile "/" "false"
|
||||
checkPathIsRegularFile "$PWD/directory" "false"
|
||||
checkPathIsRegularFile "$PWD/regular" "true"
|
||||
checkPathIsRegularFile "$PWD/symlink" "false"
|
||||
checkPathIsRegularFile "$PWD/fifo" "false"
|
||||
checkPathIsRegularFile "$PWD/non-existent" "false"
|
||||
|
||||
echo >&2 tests ok
|
||||
@@ -44,9 +44,6 @@ pkgs.runCommand "nixpkgs-lib-tests" {
|
||||
echo "Running lib/tests/modules.sh"
|
||||
bash lib/tests/modules.sh
|
||||
|
||||
echo "Running lib/tests/filesystem.sh"
|
||||
TEST_LIB=$PWD/lib bash lib/tests/filesystem.sh
|
||||
|
||||
echo "Running lib/tests/sources.sh"
|
||||
TEST_LIB=$PWD/lib bash lib/tests/sources.sh
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ rec {
|
||||
On each release the first letter is bumped and a new animal is chosen
|
||||
starting with that new letter.
|
||||
*/
|
||||
codeName = "Tapir";
|
||||
codeName = "Stoat";
|
||||
|
||||
/* Returns the current nixpkgs version suffix as string. */
|
||||
versionSuffix =
|
||||
|
||||
@@ -1857,12 +1857,6 @@
|
||||
githubId = 11135;
|
||||
name = "Berk D. Demir";
|
||||
};
|
||||
bddvlpr = {
|
||||
email = "luna@bddvlpr.com";
|
||||
github = "bddvlpr";
|
||||
githubId = 17461028;
|
||||
name = "Luna Simons";
|
||||
};
|
||||
bdesham = {
|
||||
email = "benjamin@esham.io";
|
||||
github = "bdesham";
|
||||
@@ -8942,12 +8936,6 @@
|
||||
githubId = 1572058;
|
||||
name = "Leonardo Cecchi";
|
||||
};
|
||||
leonid = {
|
||||
email = "belyaev.l@northeastern.edu";
|
||||
github = "leonidbelyaev";
|
||||
githubId = 77865363;
|
||||
name = "Leonid Belyaev";
|
||||
};
|
||||
leshainc = {
|
||||
email = "leshainc@fomalhaut.me";
|
||||
github = "LeshaInc";
|
||||
@@ -9018,12 +9006,6 @@
|
||||
githubId = 1769386;
|
||||
name = "Liam Diprose";
|
||||
};
|
||||
liberatys = {
|
||||
email = "liberatys@hey.com";
|
||||
name = "Nick Anthony Flueckiger";
|
||||
github = "liberatys";
|
||||
githubId = 35100156;
|
||||
};
|
||||
libjared = {
|
||||
email = "jared@perrycode.com";
|
||||
github = "libjared";
|
||||
@@ -9670,12 +9652,6 @@
|
||||
githubId = 346094;
|
||||
name = "Michael Alyn Miller";
|
||||
};
|
||||
mangoiv = {
|
||||
email = "contact@mangoiv.com";
|
||||
github = "mangoiv";
|
||||
githubId = 40720523;
|
||||
name = "MangoIV";
|
||||
};
|
||||
manojkarthick = {
|
||||
email = "smanojkarthick@gmail.com";
|
||||
github = "manojkarthick";
|
||||
@@ -11050,11 +11026,6 @@
|
||||
githubId = 1009523;
|
||||
name = "Ashijit Pramanik";
|
||||
};
|
||||
name-snrl = {
|
||||
github = "name-snrl";
|
||||
githubId = 72071763;
|
||||
name = "Yusup Urazaev";
|
||||
};
|
||||
namore = {
|
||||
email = "namor@hemio.de";
|
||||
github = "namore";
|
||||
@@ -12526,12 +12497,6 @@
|
||||
githubId = 3737;
|
||||
name = "Peter Jones";
|
||||
};
|
||||
pjrm = {
|
||||
email = "pedrojrmagalhaes@gmail.com";
|
||||
github = "pjrm";
|
||||
githubId = 4622652;
|
||||
name = "Pedro Magalhães";
|
||||
};
|
||||
pkharvey = {
|
||||
email = "kayharvey@protonmail.com";
|
||||
github = "pkharvey";
|
||||
@@ -15322,13 +15287,6 @@
|
||||
githubId = 20063502;
|
||||
name = "Sybrand Aarnoutse";
|
||||
};
|
||||
syboxez = {
|
||||
email = "syboxez@gmail.com";
|
||||
matrix = "@Syboxez:matrix.org";
|
||||
github = "syboxez";
|
||||
githubId = 12841859;
|
||||
name = "Syboxez Blank";
|
||||
};
|
||||
symphorien = {
|
||||
email = "symphorien_nixpkgs@xlumurb.eu";
|
||||
matrix = "@symphorien:xlumurb.eu";
|
||||
@@ -15832,12 +15790,6 @@
|
||||
github = "thielema";
|
||||
githubId = 898989;
|
||||
};
|
||||
thilobillerbeck = {
|
||||
name = "Thilo Billerbeck";
|
||||
email = "thilo.billerbeck@officerent.de";
|
||||
github = "thilobillerbeck";
|
||||
githubId = 7442383;
|
||||
};
|
||||
thled = {
|
||||
name = "Thomas Le Duc";
|
||||
email = "dev@tleduc.de";
|
||||
@@ -17370,10 +17322,10 @@
|
||||
};
|
||||
yayayayaka = {
|
||||
email = "nixpkgs@uwu.is";
|
||||
matrix = "@yaya:uwu.is";
|
||||
matrix = "@lara:uwu.is";
|
||||
github = "yayayayaka";
|
||||
githubId = 73759599;
|
||||
name = "Yaya";
|
||||
name = "Lara A.";
|
||||
};
|
||||
ydlr = {
|
||||
name = "ydlr";
|
||||
|
||||
@@ -383,6 +383,7 @@ with lib.maintainers; {
|
||||
members = [
|
||||
cleeyv
|
||||
ryantm
|
||||
yuka
|
||||
];
|
||||
scope = "Maintain Jitsi.";
|
||||
shortName = "Jitsi";
|
||||
@@ -593,6 +594,7 @@ with lib.maintainers; {
|
||||
lilyinstarlight
|
||||
marsam
|
||||
winter
|
||||
yuka
|
||||
];
|
||||
scope = "Maintain Node.js runtimes and build tooling.";
|
||||
shortName = "Node.js";
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
This section lists the release notes for each stable version of NixOS and current unstable revision.
|
||||
|
||||
```{=include=} sections
|
||||
rl-2311.section.md
|
||||
rl-2305.section.md
|
||||
rl-2211.section.md
|
||||
rl-2205.section.md
|
||||
|
||||
@@ -96,8 +96,6 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
- [atuin](https://github.com/ellie/atuin), a sync server for shell history. Available as [services.atuin](#opt-services.atuin.enable).
|
||||
|
||||
- [SFTPGo](https://github.com/drakkan/sftpgo), a fully featured and highly configurable SFTP server with optional HTTP/S, FTP/S and WebDAV support. Available as [services.sftpgo](options.html#opt-services.sftpgo.enable).
|
||||
|
||||
- [esphome](https://esphome.io), a dashboard to configure ESP8266/ESP32 devices for use with Home Automation systems. Available as [services.esphome](#opt-services.esphome.enable).
|
||||
|
||||
- [networkd-dispatcher](https://gitlab.com/craftyguy/networkd-dispatcher), a dispatcher service for systemd-networkd connection status changes. Available as [services.networkd-dispatcher](#opt-services.networkd-dispatcher.enable).
|
||||
@@ -421,8 +419,6 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
- The minimal ISO image now uses the `nixos/modules/profiles/minimal.nix` profile.
|
||||
|
||||
- NixOS installer ISOs can now be built for `powerpc64le-linux`; see `nixos/modules/installer/sd-card/sd-image-powerpc64le.nix` and [PR 192672](https://github.com/NixOS/nixpkgs/pull/192672). Hydra does not support this platform, so you must build the binaries yourself.
|
||||
|
||||
- The `ghcWithPackages` and `ghcWithHoogle` wrappers will now also symlink GHC's
|
||||
and all included libraries' documentation to `$out/share/doc` for convenience.
|
||||
If undesired, the old behavior can be restored by overriding the builders with
|
||||
@@ -526,8 +522,6 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
- The `services.fwupd` module now allows arbitrary daemon settings to be configured in a structured manner ([`services.fwupd.daemonSettings`](#opt-services.fwupd.daemonSettings)).
|
||||
|
||||
- Nixpkgs now uses [IEEE-standard floating point arithmetic](https://github.com/NixOS/nixpkgs/pull/170215) on `powerpc64le-linux`.
|
||||
|
||||
- `services.xserver.desktopManager.plasma5.phononBackend` now defaults to vlc according to [upstrean recommendation](https://community.kde.org/Distributions/Packaging_Recommendations#Non-Plasma_packages)
|
||||
|
||||
- The `zramSwap` is now implemented with `zram-generator`, and the option `zramSwap.numDevices` for using ZRAM devices as general purpose ephemeral block devices has been removed.
|
||||
@@ -569,8 +563,6 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
- `boot.initrd.luks.device.<name>` has a new `tryEmptyPassphrase` option, this is useful for OEM's who need to install an encrypted disk with a future settable passphrase
|
||||
|
||||
- there is a new `boot/stratisroot.nix` module that enables booting from a volume managed by the Stratis storage management daemon. Use `fileSystems.<name>.stratis.poolUuid` to configure the pool containing the fs.
|
||||
|
||||
- Lisp gained a [manual section](https://nixos.org/manual/nixpkgs/stable/#lisp), documenting a new and backwards incompatible interface. The previous interface will be removed in a future release.
|
||||
|
||||
- The `bind` module now allows the per-zone `allow-query` setting to be configured (previously it was hard-coded to `any`; it still defaults to `any` to retain compatibility).
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Release 23.11 (“Tapir”, 2023.11/??) {#sec-release-23.11}
|
||||
|
||||
## Highlights {#sec-release-23.11-highlights}
|
||||
|
||||
- FoundationDB now defaults to major version 7.
|
||||
|
||||
## New Services {#sec-release-23.11-new-services}
|
||||
|
||||
- Create the first release note entry in this section!
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
- [river](https://github.com/riverwm/river), A dynamic tiling wayland compositor. Available as [programs.river](#opt-programs.river.enable).
|
||||
|
||||
## Backward Incompatibilities {#sec-release-23.11-incompatibilities}
|
||||
|
||||
- The latest version of `clonehero` now stores custom content in `~/.clonehero`. See the [migration instructions](https://clonehero.net/2022/11/29/v23-to-v1-migration-instructions.html). Typically, these content files would exist along side the binary, but the previous build used a wrapper script that would store them in `~/.config/unity3d/srylain Inc_/Clone Hero`.
|
||||
|
||||
- `etcd` has been updated to 3.5, you will want to read the [3.3 to 3.4](https://etcd.io/docs/v3.5/upgrades/upgrade_3_4/) and [3.4 to 3.5](https://etcd.io/docs/v3.5/upgrades/upgrade_3_5/) upgrade guides
|
||||
|
||||
## Other Notable Changes {#sec-release-23.11-notable-changes}
|
||||
|
||||
- The Cinnamon module now enables XDG desktop integration by default. If you are experiencing collisions related to xdg-desktop-portal-gtk you can safely remove `xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];` from your NixOS configuration.
|
||||
|
||||
- A new option was added to the virtualisation module that enables specifying explicitly named network interfaces in QEMU VMs. The existing `virtualisation.vlans` is still supported for cases where the name of the network interface is irrelevant.
|
||||
|
||||
- `services.nginx` gained a `defaultListen` option at server-level with support for PROXY protocol listeners, also `proxyProtocol` is now exposed in `services.nginx.virtualHosts.<name>.listen` option. It is now possible to run PROXY listeners and non-PROXY listeners at a server-level, see [#213510](https://github.com/NixOS/nixpkgs/pull/213510/) for more details.
|
||||
@@ -163,6 +163,11 @@ class Driver:
|
||||
machine.wait_for_shutdown()
|
||||
|
||||
def create_machine(self, args: Dict[str, Any]) -> Machine:
|
||||
rootlog.warning(
|
||||
"Using legacy create_machine(), please instantiate the"
|
||||
"Machine class directly, instead"
|
||||
)
|
||||
|
||||
tmp_dir = get_tmp_dir()
|
||||
|
||||
if args.get("startCommand"):
|
||||
|
||||
@@ -369,8 +369,8 @@ class Machine:
|
||||
@staticmethod
|
||||
def create_startcommand(args: Dict[str, str]) -> StartCommand:
|
||||
rootlog.warning(
|
||||
"Using legacy create_startcommand(), "
|
||||
"please use proper nix test vm instrumentation, instead "
|
||||
"Using legacy create_startcommand(),"
|
||||
"please use proper nix test vm instrumentation, instead"
|
||||
"to generate the appropriate nixos test vm qemu startup script"
|
||||
)
|
||||
hda = None
|
||||
@@ -855,37 +855,21 @@ class Machine:
|
||||
with self.nested(f"waiting for {regex} to appear on screen"):
|
||||
retry(screen_matches)
|
||||
|
||||
def wait_for_console_text(self, regex: str, timeout: int | None = None) -> None:
|
||||
"""
|
||||
Wait for the provided regex to appear on console.
|
||||
For each reads,
|
||||
|
||||
If timeout is None, timeout is infinite.
|
||||
|
||||
`timeout` is in seconds.
|
||||
"""
|
||||
# Buffer the console output, this is needed
|
||||
# to match multiline regexes.
|
||||
console = io.StringIO()
|
||||
|
||||
def console_matches() -> bool:
|
||||
nonlocal console
|
||||
try:
|
||||
# This will return as soon as possible and
|
||||
# sleep 1 second.
|
||||
console.write(self.last_lines.get(block=False))
|
||||
except queue.Empty:
|
||||
pass
|
||||
console.seek(0)
|
||||
matches = re.search(regex, console.read())
|
||||
return matches is not None
|
||||
|
||||
def wait_for_console_text(self, regex: str) -> None:
|
||||
with self.nested(f"waiting for {regex} to appear on console"):
|
||||
if timeout is not None:
|
||||
retry(console_matches, timeout)
|
||||
else:
|
||||
while not console_matches():
|
||||
pass
|
||||
# Buffer the console output, this is needed
|
||||
# to match multiline regexes.
|
||||
console = io.StringIO()
|
||||
while True:
|
||||
try:
|
||||
console.write(self.last_lines.get())
|
||||
except queue.Empty:
|
||||
self.sleep(1)
|
||||
continue
|
||||
console.seek(0)
|
||||
matches = re.search(regex, console.read())
|
||||
if matches is not None:
|
||||
return
|
||||
|
||||
def send_key(
|
||||
self, key: str, delay: Optional[float] = 0.01, log: Optional[bool] = True
|
||||
|
||||
@@ -12,9 +12,7 @@ let
|
||||
};
|
||||
|
||||
|
||||
vlans = map (m: (
|
||||
m.virtualisation.vlans ++
|
||||
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
|
||||
vlans = map (m: m.virtualisation.vlans) (lib.attrValues config.nodes);
|
||||
vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
|
||||
|
||||
nodeHostNames =
|
||||
|
||||
@@ -4,7 +4,7 @@ let
|
||||
inherit (lib)
|
||||
attrNames concatMap concatMapStrings flip forEach head
|
||||
listToAttrs mkDefault mkOption nameValuePair optionalString
|
||||
range toLower types zipListsWith zipLists
|
||||
range types zipListsWith zipLists
|
||||
mdDoc
|
||||
;
|
||||
|
||||
@@ -18,41 +18,24 @@ let
|
||||
|
||||
networkModule = { config, nodes, pkgs, ... }:
|
||||
let
|
||||
qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
|
||||
|
||||
# Convert legacy VLANs to named interfaces and merge with explicit interfaces.
|
||||
vlansNumbered = forEach (zipLists config.virtualisation.vlans (range 1 255)) (v: {
|
||||
name = "eth${toString v.snd}";
|
||||
vlan = v.fst;
|
||||
assignIP = true;
|
||||
});
|
||||
explicitInterfaces = lib.mapAttrsToList (n: v: v // { name = n; }) config.virtualisation.interfaces;
|
||||
interfaces = vlansNumbered ++ explicitInterfaces;
|
||||
interfacesNumbered = zipLists interfaces (range 1 255);
|
||||
|
||||
# Automatically assign IP addresses to requested interfaces.
|
||||
assignIPs = lib.filter (i: i.assignIP) interfaces;
|
||||
ipInterfaces = forEach assignIPs (i:
|
||||
nameValuePair i.name { ipv4.addresses =
|
||||
[ { address = "192.168.${toString i.vlan}.${toString config.virtualisation.test.nodeNumber}";
|
||||
interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
|
||||
interfaces = forEach interfacesNumbered ({ fst, snd }:
|
||||
nameValuePair "eth${toString snd}" {
|
||||
ipv4.addresses =
|
||||
[{
|
||||
address = "192.168.${toString fst}.${toString config.virtualisation.test.nodeNumber}";
|
||||
prefixLength = 24;
|
||||
}];
|
||||
});
|
||||
|
||||
qemuOptions = lib.flatten (forEach interfacesNumbered ({ fst, snd }:
|
||||
qemu-common.qemuNICFlags snd fst.vlan config.virtualisation.test.nodeNumber));
|
||||
udevRules = forEach interfacesNumbered ({ fst, snd }:
|
||||
# MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive.
|
||||
''SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="${toLower(qemu-common.qemuNicMac fst.vlan config.virtualisation.test.nodeNumber)}",NAME="${fst.name}"'');
|
||||
|
||||
networkConfig =
|
||||
{
|
||||
networking.hostName = mkDefault config.virtualisation.test.nodeName;
|
||||
|
||||
networking.interfaces = listToAttrs ipInterfaces;
|
||||
networking.interfaces = listToAttrs interfaces;
|
||||
|
||||
networking.primaryIPAddress =
|
||||
optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv4.addresses).address;
|
||||
optionalString (interfaces != [ ]) (head (head interfaces).value.ipv4.addresses).address;
|
||||
|
||||
# Put the IP addresses of all VMs in this machine's
|
||||
# /etc/hosts file. If a machine has multiple
|
||||
@@ -68,13 +51,16 @@ let
|
||||
"${config.networking.hostName}.${config.networking.domain} " +
|
||||
"${config.networking.hostName}\n"));
|
||||
|
||||
virtualisation.qemu.options = qemuOptions;
|
||||
boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules;
|
||||
virtualisation.qemu.options =
|
||||
let qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
|
||||
in
|
||||
flip concatMap interfacesNumbered
|
||||
({ fst, snd }: qemu-common.qemuNICFlags snd fst config.virtualisation.test.nodeNumber);
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
key = "network-interfaces";
|
||||
key = "ip-address";
|
||||
config = networkConfig // {
|
||||
# Expose the networkConfig items for tests like nixops
|
||||
# that need to recreate the network config.
|
||||
|
||||
@@ -21,6 +21,9 @@ with lib;
|
||||
# ISO naming.
|
||||
isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}.iso";
|
||||
|
||||
# BIOS booting
|
||||
isoImage.makeBiosBootable = true;
|
||||
|
||||
# EFI booting
|
||||
isoImage.makeEfiBootable = true;
|
||||
|
||||
|
||||
@@ -442,6 +442,9 @@ let
|
||||
fsck.vfat -vn "$out"
|
||||
''; # */
|
||||
|
||||
# Syslinux (and isolinux) only supports x86-based architectures.
|
||||
canx86BiosBoot = pkgs.stdenv.hostPlatform.isx86;
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
@@ -540,17 +543,7 @@ in
|
||||
};
|
||||
|
||||
isoImage.makeBiosBootable = mkOption {
|
||||
# Before this option was introduced, images were BIOS-bootable if the
|
||||
# hostPlatform was x86-based. This option is enabled by default for
|
||||
# backwards compatibility.
|
||||
#
|
||||
# Also note that syslinux package currently cannot be cross-compiled from
|
||||
# non-x86 platforms, so the default is false on non-x86 build platforms.
|
||||
default = pkgs.stdenv.buildPlatform.isx86 && pkgs.stdenv.hostPlatform.isx86;
|
||||
defaultText = lib.literalMD ''
|
||||
`true` if both build and host platforms are x86-based architectures,
|
||||
e.g. i686 and x86_64.
|
||||
'';
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = lib.mdDoc ''
|
||||
Whether the ISO image should be a BIOS-bootable disk.
|
||||
@@ -711,11 +704,6 @@ in
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
# Syslinux (and isolinux) only supports x86-based architectures.
|
||||
assertion = config.isoImage.makeBiosBootable -> pkgs.stdenv.hostPlatform.isx86;
|
||||
message = "BIOS boot is only supported on x86-based architectures.";
|
||||
}
|
||||
{
|
||||
assertion = !(stringLength config.isoImage.volumeID > 32);
|
||||
# https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor
|
||||
@@ -734,7 +722,7 @@ in
|
||||
boot.loader.grub.enable = false;
|
||||
|
||||
environment.systemPackages = [ grubPkgs.grub2 grubPkgs.grub2_efi ]
|
||||
++ optional (config.isoImage.makeBiosBootable) pkgs.syslinux
|
||||
++ optional (config.isoImage.makeBiosBootable && canx86BiosBoot) pkgs.syslinux
|
||||
;
|
||||
|
||||
# In stage 1 of the boot, mount the CD as the root FS by label so
|
||||
@@ -785,7 +773,7 @@ in
|
||||
{ source = pkgs.writeText "version" config.system.nixos.label;
|
||||
target = "/version.txt";
|
||||
}
|
||||
] ++ optionals (config.isoImage.makeBiosBootable) [
|
||||
] ++ optionals (config.isoImage.makeBiosBootable && canx86BiosBoot) [
|
||||
{ source = config.isoImage.splashImage;
|
||||
target = "/isolinux/background.png";
|
||||
}
|
||||
@@ -812,7 +800,7 @@ in
|
||||
{ source = config.isoImage.efiSplashImage;
|
||||
target = "/EFI/boot/efi-background.png";
|
||||
}
|
||||
] ++ optionals (config.boot.loader.grub.memtest86.enable && config.isoImage.makeBiosBootable) [
|
||||
] ++ optionals (config.boot.loader.grub.memtest86.enable && config.isoImage.makeBiosBootable && canx86BiosBoot) [
|
||||
{ source = "${pkgs.memtest86plus}/memtest.bin";
|
||||
target = "/boot/memtest.bin";
|
||||
}
|
||||
@@ -827,10 +815,10 @@ in
|
||||
# Create the ISO image.
|
||||
system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ({
|
||||
inherit (config.isoImage) isoName compressImage volumeID contents;
|
||||
bootable = config.isoImage.makeBiosBootable;
|
||||
bootable = config.isoImage.makeBiosBootable && canx86BiosBoot;
|
||||
bootImage = "/isolinux/isolinux.bin";
|
||||
syslinux = if config.isoImage.makeBiosBootable then pkgs.syslinux else null;
|
||||
} // optionalAttrs (config.isoImage.makeUsbBootable && config.isoImage.makeBiosBootable) {
|
||||
syslinux = if config.isoImage.makeBiosBootable && canx86BiosBoot then pkgs.syslinux else null;
|
||||
} // optionalAttrs (config.isoImage.makeUsbBootable && config.isoImage.makeBiosBootable && canx86BiosBoot) {
|
||||
usbBootable = true;
|
||||
isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin";
|
||||
} // optionalAttrs config.isoImage.makeEfiBootable {
|
||||
|
||||
@@ -335,7 +335,7 @@ sub findStableDevPath {
|
||||
|
||||
my $st = stat($dev) or return $dev;
|
||||
|
||||
foreach my $dev2 (glob("/dev/stratis/*/*"), glob("/dev/disk/by-uuid/*"), glob("/dev/mapper/*"), glob("/dev/disk/by-label/*")) {
|
||||
foreach my $dev2 (glob("/dev/disk/by-uuid/*"), glob("/dev/mapper/*"), glob("/dev/disk/by-label/*")) {
|
||||
my $st2 = stat($dev2) or next;
|
||||
return $dev2 if $st->rdev == $st2->rdev;
|
||||
}
|
||||
@@ -467,17 +467,6 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
# is this a stratis fs?
|
||||
my $stableDevPath = findStableDevPath $device;
|
||||
my $stratisPool;
|
||||
if ($stableDevPath =~ qr#/dev/stratis/(.*)/.*#) {
|
||||
my $poolName = $1;
|
||||
my ($header, @lines) = split "\n", qx/stratis pool list/;
|
||||
my $uuidIndex = index $header, 'UUID';
|
||||
my ($line) = grep /^$poolName /, @lines;
|
||||
$stratisPool = substr $line, $uuidIndex - 32, 36;
|
||||
}
|
||||
|
||||
# Don't emit tmpfs entry for /tmp, because it most likely comes from the
|
||||
# boot.tmp.useTmpfs option in configuration.nix (managed declaratively).
|
||||
next if ($mountPoint eq "/tmp" && $fsType eq "tmpfs");
|
||||
@@ -485,7 +474,7 @@ EOF
|
||||
# Emit the filesystem.
|
||||
$fileSystems .= <<EOF;
|
||||
fileSystems.\"$mountPoint\" =
|
||||
{ device = \"$stableDevPath\";
|
||||
{ device = \"${\(findStableDevPath $device)}\";
|
||||
fsType = \"$fsType\";
|
||||
EOF
|
||||
|
||||
@@ -495,12 +484,6 @@ EOF
|
||||
EOF
|
||||
}
|
||||
|
||||
if ($stratisPool) {
|
||||
$fileSystems .= <<EOF;
|
||||
stratis.poolUuid = "$stratisPool";
|
||||
EOF
|
||||
}
|
||||
|
||||
$fileSystems .= <<EOF;
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ let
|
||||
DOCUMENTATION_URL = lib.optionalString (cfg.distroId == "nixos") "https://nixos.org/learn.html";
|
||||
SUPPORT_URL = lib.optionalString (cfg.distroId == "nixos") "https://nixos.org/community.html";
|
||||
BUG_REPORT_URL = lib.optionalString (cfg.distroId == "nixos") "https://github.com/NixOS/nixpkgs/issues";
|
||||
SUPPORT_END = "2023-12-31";
|
||||
} // lib.optionalAttrs (cfg.variant_id != null) {
|
||||
VARIANT_ID = cfg.variant_id;
|
||||
};
|
||||
@@ -143,7 +144,7 @@ in
|
||||
defaultChannel = mkOption {
|
||||
internal = true;
|
||||
type = types.str;
|
||||
default = "https://nixos.org/channels/nixos-unstable";
|
||||
default = "https://nixos.org/channels/nixos-23.05";
|
||||
description = lib.mdDoc "Default NixOS channel to which the root user is subscribed.";
|
||||
};
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@
|
||||
./programs/starship.nix
|
||||
./programs/steam.nix
|
||||
./programs/streamdeck-ui.nix
|
||||
./programs/sway.nix
|
||||
./programs/sysdig.nix
|
||||
./programs/system-config-printer.nix
|
||||
./programs/systemtap.nix
|
||||
@@ -255,9 +256,7 @@
|
||||
./programs/usbtop.nix
|
||||
./programs/vim.nix
|
||||
./programs/wavemon.nix
|
||||
./programs/wayland/river.nix
|
||||
./programs/wayland/sway.nix
|
||||
./programs/wayland/waybar.nix
|
||||
./programs/waybar.nix
|
||||
./programs/weylus.nix
|
||||
./programs/wireshark.nix
|
||||
./programs/xastir.nix
|
||||
@@ -912,7 +911,6 @@
|
||||
./services/networking/knot.nix
|
||||
./services/networking/kresd.nix
|
||||
./services/networking/lambdabot.nix
|
||||
./services/networking/legit.nix
|
||||
./services/networking/libreswan.nix
|
||||
./services/networking/lldpd.nix
|
||||
./services/networking/logmein-hamachi.nix
|
||||
@@ -1232,7 +1230,6 @@
|
||||
./services/web-apps/powerdns-admin.nix
|
||||
./services/web-apps/prosody-filer.nix
|
||||
./services/web-apps/restya-board.nix
|
||||
./services/web-apps/sftpgo.nix
|
||||
./services/web-apps/rss-bridge.nix
|
||||
./services/web-apps/selfoss.nix
|
||||
./services/web-apps/shiori.nix
|
||||
@@ -1311,6 +1308,7 @@
|
||||
./services/x11/window-managers/default.nix
|
||||
./services/x11/window-managers/fluxbox.nix
|
||||
./services/x11/window-managers/icewm.nix
|
||||
./services/x11/window-managers/bspwm.nix
|
||||
./services/x11/window-managers/katriawm.nix
|
||||
./services/x11/window-managers/metacity.nix
|
||||
./services/x11/window-managers/nimdow.nix
|
||||
@@ -1347,7 +1345,6 @@
|
||||
./system/boot/loader/raspberrypi/raspberrypi.nix
|
||||
./system/boot/loader/systemd-boot/systemd-boot.nix
|
||||
./system/boot/luksroot.nix
|
||||
./system/boot/stratisroot.nix
|
||||
./system/boot/modprobe.nix
|
||||
./system/boot/networkd.nix
|
||||
./system/boot/plymouth.nix
|
||||
|
||||
@@ -123,36 +123,41 @@ in {
|
||||
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable
|
||||
(mkMerge [
|
||||
config = mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
|
||||
message = ''
|
||||
The extraSessionCommands for Sway will not be run if
|
||||
wrapperFeatures.base is disabled.
|
||||
'';
|
||||
}
|
||||
];
|
||||
environment = {
|
||||
systemPackages = optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
|
||||
# Needed for the default wallpaper:
|
||||
pathsToLink = optionals (cfg.package != null) [ "/share/backgrounds/sway" ];
|
||||
etc = {
|
||||
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
|
||||
# Import the most important environment variables into the D-Bus and systemd
|
||||
# user environments (e.g. required for screen sharing and Pinentry prompts):
|
||||
exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
|
||||
'';
|
||||
} // optionalAttrs (cfg.package != null) {
|
||||
"sway/config".source = mkOptionDefault "${cfg.package}/etc/sway/config";
|
||||
};
|
||||
};
|
||||
# To make a Sway session available if a display manager like SDDM is enabled:
|
||||
services.xserver.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ]; }
|
||||
(import ./wayland-session.nix { inherit lib pkgs; })
|
||||
]);
|
||||
assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
|
||||
message = ''
|
||||
The extraSessionCommands for Sway will not be run if
|
||||
wrapperFeatures.base is disabled.
|
||||
'';
|
||||
}
|
||||
];
|
||||
environment = {
|
||||
systemPackages = optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
|
||||
# Needed for the default wallpaper:
|
||||
pathsToLink = optionals (cfg.package != null) [ "/share/backgrounds/sway" ];
|
||||
etc = {
|
||||
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
|
||||
# Import the most important environment variables into the D-Bus and systemd
|
||||
# user environments (e.g. required for screen sharing and Pinentry prompts):
|
||||
exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
|
||||
'';
|
||||
} // optionalAttrs (cfg.package != null) {
|
||||
"sway/config".source = mkOptionDefault "${cfg.package}/etc/sway/config";
|
||||
};
|
||||
};
|
||||
security.polkit.enable = true;
|
||||
security.pam.services.swaylock = {};
|
||||
hardware.opengl.enable = mkDefault true;
|
||||
fonts.enableDefaultFonts = mkDefault true;
|
||||
programs.dconf.enable = mkDefault true;
|
||||
# To make a Sway session available if a display manager like SDDM is enabled:
|
||||
services.xserver.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ];
|
||||
programs.xwayland.enable = mkDefault true;
|
||||
# For screen sharing (this option only has an effect with xdg.portal.enable):
|
||||
xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-wlr ];
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ primeos colemickens ];
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.programs.river;
|
||||
in {
|
||||
options.programs.river = {
|
||||
enable = mkEnableOption (lib.mdDoc "river, a dynamic tiling Wayland compositor");
|
||||
|
||||
package = mkOption {
|
||||
type = with types; nullOr package;
|
||||
default = pkgs.river;
|
||||
defaultText = literalExpression "pkgs.river";
|
||||
description = lib.mdDoc ''
|
||||
River package to use.
|
||||
Set to `null` to not add any River package to your path.
|
||||
This should be done if you want to use the Home Manager River module to install River.
|
||||
'';
|
||||
};
|
||||
|
||||
extraPackages = mkOption {
|
||||
type = with types; listOf package;
|
||||
default = with pkgs; [
|
||||
swaylock
|
||||
foot
|
||||
dmenu
|
||||
];
|
||||
defaultText = literalExpression ''
|
||||
with pkgs; [ swaylock foot dmenu ];
|
||||
'';
|
||||
example = literalExpression ''
|
||||
with pkgs; [
|
||||
termite rofi light
|
||||
]
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
Extra packages to be installed system wide. See
|
||||
[Common X11 apps used on i3 with Wayland alternatives](https://github.com/swaywm/sway/wiki/i3-Migration-Guide#common-x11-apps-used-on-i3-with-wayland-alternatives)
|
||||
for a list of useful software.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
environment.systemPackages = optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
|
||||
|
||||
# To make a river session available if a display manager like SDDM is enabled:
|
||||
programs.xwayland.enable = mkDefault true;
|
||||
}
|
||||
(import ./wayland-session.nix { inherit lib pkgs; })
|
||||
]);
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ GaetanLepage ];
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{ lib, pkgs, ... }: with lib; {
|
||||
security = {
|
||||
polkit.enable = true;
|
||||
pam.services.swaylock = {};
|
||||
};
|
||||
|
||||
hardware.opengl.enable = mkDefault true;
|
||||
fonts.enableDefaultFonts = mkDefault true;
|
||||
|
||||
programs = {
|
||||
dconf.enable = mkDefault true;
|
||||
xwayland.enable = mkDefault true;
|
||||
};
|
||||
|
||||
xdg.portal = {
|
||||
enable = mkDefault true;
|
||||
|
||||
extraPortals = [
|
||||
# For screen sharing
|
||||
pkgs.xdg-desktop-portal-wlr
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -167,11 +167,9 @@ in
|
||||
<!-- create mount point if not present -->
|
||||
<mkmountpoint enable="${if cfg.createMountPoints then "1" else "0"}" remove="${if cfg.removeCreatedMountPoints then "true" else "false"}" />
|
||||
<!-- specify the binaries to be called -->
|
||||
<!-- the comma in front of the options is necessary for empty options -->
|
||||
<fusemount>${pkgs.fuse}/bin/mount.fuse %(VOLUME) %(MNTPT) -o ,${concatStringsSep "," (cfg.fuseMountOptions ++ [ "%(OPTIONS)" ])}'</fusemount>
|
||||
<fusemount>${pkgs.fuse}/bin/mount.fuse %(VOLUME) %(MNTPT) -o ${concatStringsSep "," (cfg.fuseMountOptions ++ [ "%(OPTIONS)" ])}</fusemount>
|
||||
<fuseumount>${pkgs.fuse}/bin/fusermount -u %(MNTPT)</fuseumount>
|
||||
<!-- the comma in front of the options is necessary for empty options -->
|
||||
<cryptmount>${pkgs.pam_mount}/bin/mount.crypt -o ,${concatStringsSep "," (cfg.cryptMountOptions ++ [ "%(OPTIONS)" ])} %(VOLUME) %(MNTPT)</cryptmount>
|
||||
<cryptmount>${pkgs.pam_mount}/bin/mount.crypt -o ${concatStringsSep "," (cfg.cryptMountOptions ++ [ "%(OPTIONS)" ])} %(VOLUME) %(MNTPT)</cryptmount>
|
||||
<cryptumount>${pkgs.pam_mount}/bin/umount.crypt %(MNTPT)</cryptumount>
|
||||
<pmvarrun>${pkgs.pam_mount}/bin/pmvarrun -u %(USER) -o %(OPERATION)</pmvarrun>
|
||||
${optionalString oflRequired "<ofl>${fake_ofl}/bin/fake_ofl %(SIGNAL) %(MNTPT)</ofl>"}
|
||||
|
||||
@@ -10,18 +10,171 @@
|
||||
let
|
||||
inherit (lib)
|
||||
filterAttrs
|
||||
literalMD
|
||||
literalExpression
|
||||
mkIf
|
||||
mkOption
|
||||
mkRemovedOptionModule
|
||||
mkRenamedOptionModule
|
||||
types
|
||||
|
||||
;
|
||||
|
||||
cfg = config.services.hercules-ci-agent;
|
||||
cfg =
|
||||
config.services.hercules-ci-agent;
|
||||
|
||||
inherit (import ./settings.nix { inherit pkgs lib; }) format settingsModule;
|
||||
format = pkgs.formats.toml { };
|
||||
|
||||
settingsModule = { config, ... }: {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
apiBaseUrl = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
API base URL that the agent will connect to.
|
||||
|
||||
When using Hercules CI Enterprise, set this to the URL where your
|
||||
Hercules CI server is reachable.
|
||||
'';
|
||||
type = types.str;
|
||||
default = "https://hercules-ci.com";
|
||||
};
|
||||
baseDirectory = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/hercules-ci-agent";
|
||||
description = lib.mdDoc ''
|
||||
State directory (secrets, work directory, etc) for agent
|
||||
'';
|
||||
};
|
||||
concurrentTasks = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Number of tasks to perform simultaneously.
|
||||
|
||||
A task is a single derivation build, an evaluation or an effect run.
|
||||
At minimum, you need 2 concurrent tasks for `x86_64-linux`
|
||||
in your cluster, to allow for import from derivation.
|
||||
|
||||
`concurrentTasks` can be around the CPU core count or lower if memory is
|
||||
the bottleneck.
|
||||
|
||||
The optimal value depends on the resource consumption characteristics of your workload,
|
||||
including memory usage and in-task parallelism. This is typically determined empirically.
|
||||
|
||||
When scaling, it is generally better to have a double-size machine than two machines,
|
||||
because each split of resources causes inefficiencies; particularly with regards
|
||||
to build latency because of extra downloads.
|
||||
'';
|
||||
type = types.either types.ints.positive (types.enum [ "auto" ]);
|
||||
default = "auto";
|
||||
};
|
||||
labels = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
A key-value map of user data.
|
||||
|
||||
This data will be available to organization members in the dashboard and API.
|
||||
|
||||
The values can be of any TOML type that corresponds to a JSON type, but arrays
|
||||
can not contain tables/objects due to limitations of the TOML library. Values
|
||||
involving arrays of non-primitive types may not be representable currently.
|
||||
'';
|
||||
type = format.type;
|
||||
defaultText = literalExpression ''
|
||||
{
|
||||
agent.source = "..."; # One of "nixpkgs", "flake", "override"
|
||||
lib.version = "...";
|
||||
pkgs.version = "...";
|
||||
}
|
||||
'';
|
||||
};
|
||||
workDirectory = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.baseDirectory + "/work";
|
||||
defaultText = literalExpression ''baseDirectory + "/work"'';
|
||||
};
|
||||
staticSecretsDirectory = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
This is the default directory to look for statically configured secrets like `cluster-join-token.key`.
|
||||
|
||||
See also `clusterJoinTokenPath` and `binaryCachesPath` for fine-grained configuration.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.baseDirectory + "/secrets";
|
||||
defaultText = literalExpression ''baseDirectory + "/secrets"'';
|
||||
};
|
||||
clusterJoinTokenPath = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Location of the cluster-join-token.key file.
|
||||
|
||||
You can retrieve the contents of the file when creating a new agent via
|
||||
<https://hercules-ci.com/dashboard>.
|
||||
|
||||
As this value is confidential, it should not be in the store, but
|
||||
installed using other means, such as agenix, NixOps
|
||||
`deployment.keys`, or manual installation.
|
||||
|
||||
The contents of the file are used for authentication between the agent and the API.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.staticSecretsDirectory + "/cluster-join-token.key";
|
||||
defaultText = literalExpression ''staticSecretsDirectory + "/cluster-join-token.key"'';
|
||||
};
|
||||
binaryCachesPath = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Path to a JSON file containing binary cache secret keys.
|
||||
|
||||
As these values are confidential, they should not be in the store, but
|
||||
copied over using other means, such as agenix, NixOps
|
||||
`deployment.keys`, or manual installation.
|
||||
|
||||
The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/>.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.staticSecretsDirectory + "/binary-caches.json";
|
||||
defaultText = literalExpression ''staticSecretsDirectory + "/binary-caches.json"'';
|
||||
};
|
||||
secretsJsonPath = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Path to a JSON file containing secrets for effects.
|
||||
|
||||
As these values are confidential, they should not be in the store, but
|
||||
copied over using other means, such as agenix, NixOps
|
||||
`deployment.keys`, or manual installation.
|
||||
|
||||
The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/>.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.staticSecretsDirectory + "/secrets.json";
|
||||
defaultText = literalExpression ''staticSecretsDirectory + "/secrets.json"'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# TODO (roberth, >=2022) remove
|
||||
checkNix =
|
||||
if !cfg.checkNix
|
||||
then ""
|
||||
else if lib.versionAtLeast config.nix.package.version "2.3.10"
|
||||
then ""
|
||||
else
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "hercules-ci-check-system-nix-src";
|
||||
inherit (config.nix.package) src patches;
|
||||
dontConfigure = true;
|
||||
buildPhase = ''
|
||||
echo "Checking in-memory pathInfoCache expiry"
|
||||
if ! grep 'PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then
|
||||
cat 1>&2 <<EOF
|
||||
|
||||
You are deploying Hercules CI Agent on a system with an incompatible
|
||||
nix-daemon. Please make sure nix.package is set to a Nix version of at
|
||||
least 2.3.10 or a master version more recent than Mar 12, 2020.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
installPhase = "touch $out";
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
@@ -45,6 +198,15 @@ in
|
||||
Support is available at [help@hercules-ci.com](mailto:help@hercules-ci.com).
|
||||
'';
|
||||
};
|
||||
checkNix = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Whether to make sure that the system's Nix (nix-daemon) is compatible.
|
||||
|
||||
If you set this to false, please keep up with the change log.
|
||||
'';
|
||||
};
|
||||
package = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Package containing the bin/hercules-ci-agent executable.
|
||||
@@ -73,7 +235,7 @@ in
|
||||
tomlFile = mkOption {
|
||||
type = types.path;
|
||||
internal = true;
|
||||
defaultText = lib.literalMD "generated `hercules-ci-agent.toml`";
|
||||
defaultText = literalMD "generated `hercules-ci-agent.toml`";
|
||||
description = lib.mdDoc ''
|
||||
The fully assembled config file.
|
||||
'';
|
||||
@@ -81,27 +243,7 @@ in
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# Make sure that nix.extraOptions does not override trusted-users
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
(cfg.settings.nixUserIsTrusted or false) ->
|
||||
builtins.match ".*(^|\n)[ \t]*trusted-users[ \t]*=.*" config.nix.extraOptions == null;
|
||||
message = ''
|
||||
hercules-ci-agent: Please do not set `trusted-users` in `nix.extraOptions`.
|
||||
|
||||
The hercules-ci-agent module by default relies on `nix.settings.trusted-users`
|
||||
to be effectful, but a line like `trusted-users = ...` in `nix.extraOptions`
|
||||
will override the value set in `nix.settings.trusted-users`.
|
||||
|
||||
Instead of setting `trusted-users` in the `nix.extraOptions` string, you should
|
||||
set an option with additive semantics, such as
|
||||
- the NixOS option `nix.settings.trusted-users`, or
|
||||
- the Nix option in the `extraOptions` string, `extra-trusted-users`
|
||||
'';
|
||||
}
|
||||
];
|
||||
nix.extraOptions = ''
|
||||
nix.extraOptions = lib.addContextFrom checkNix ''
|
||||
# A store path that was missing at first may well have finished building,
|
||||
# even shortly after the previous lookup. This *also* applies to the daemon.
|
||||
narinfo-cache-negative-ttl = 0
|
||||
@@ -109,9 +251,14 @@ in
|
||||
services.hercules-ci-agent = {
|
||||
tomlFile =
|
||||
format.generate "hercules-ci-agent.toml" cfg.settings;
|
||||
settings.config._module.args = {
|
||||
packageOption = options.services.hercules-ci-agent.package;
|
||||
inherit pkgs;
|
||||
|
||||
settings.labels = {
|
||||
agent.source =
|
||||
if options.services.hercules-ci-agent.package.highestPrio == (lib.modules.mkOptionDefault { }).priority
|
||||
then "nixpkgs"
|
||||
else lib.mkOptionDefault "override";
|
||||
pkgs.version = pkgs.lib.version;
|
||||
lib.version = lib.version;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -36,14 +36,8 @@ in
|
||||
Restart = "on-failure";
|
||||
RestartSec = 120;
|
||||
|
||||
# If a worker goes OOM, don't kill the main process. It needs to
|
||||
# report the failure and it's unlikely to be part of the problem.
|
||||
OOMPolicy = "continue";
|
||||
|
||||
# Work around excessive stack use by libstdc++ regex
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164
|
||||
# A 256 MiB stack allows between 400 KiB and 1.5 MiB file to be matched by ".*".
|
||||
LimitSTACK = 256 * 1024 * 1024;
|
||||
OOMPolicy = "continue";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
# Not a module
|
||||
{ pkgs, lib }:
|
||||
let
|
||||
inherit (lib)
|
||||
types
|
||||
literalExpression
|
||||
mkOption
|
||||
;
|
||||
|
||||
format = pkgs.formats.toml { };
|
||||
|
||||
settingsModule = { config, packageOption, pkgs, ... }: {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
apiBaseUrl = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
API base URL that the agent will connect to.
|
||||
|
||||
When using Hercules CI Enterprise, set this to the URL where your
|
||||
Hercules CI server is reachable.
|
||||
'';
|
||||
type = types.str;
|
||||
default = "https://hercules-ci.com";
|
||||
};
|
||||
baseDirectory = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/hercules-ci-agent";
|
||||
description = lib.mdDoc ''
|
||||
State directory (secrets, work directory, etc) for agent
|
||||
'';
|
||||
};
|
||||
concurrentTasks = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Number of tasks to perform simultaneously.
|
||||
|
||||
A task is a single derivation build, an evaluation or an effect run.
|
||||
At minimum, you need 2 concurrent tasks for `x86_64-linux`
|
||||
in your cluster, to allow for import from derivation.
|
||||
|
||||
`concurrentTasks` can be around the CPU core count or lower if memory is
|
||||
the bottleneck.
|
||||
|
||||
The optimal value depends on the resource consumption characteristics of your workload,
|
||||
including memory usage and in-task parallelism. This is typically determined empirically.
|
||||
|
||||
When scaling, it is generally better to have a double-size machine than two machines,
|
||||
because each split of resources causes inefficiencies; particularly with regards
|
||||
to build latency because of extra downloads.
|
||||
'';
|
||||
type = types.either types.ints.positive (types.enum [ "auto" ]);
|
||||
default = "auto";
|
||||
defaultText = lib.literalMD ''
|
||||
`"auto"`, meaning equal to the number of CPU cores.
|
||||
'';
|
||||
};
|
||||
labels = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
A key-value map of user data.
|
||||
|
||||
This data will be available to organization members in the dashboard and API.
|
||||
|
||||
The values can be of any TOML type that corresponds to a JSON type, but arrays
|
||||
can not contain tables/objects due to limitations of the TOML library. Values
|
||||
involving arrays of non-primitive types may not be representable currently.
|
||||
'';
|
||||
type = format.type;
|
||||
defaultText = literalExpression ''
|
||||
{
|
||||
agent.source = "..."; # One of "nixpkgs", "flake", "override"
|
||||
lib.version = "...";
|
||||
pkgs.version = "...";
|
||||
}
|
||||
'';
|
||||
};
|
||||
workDirectory = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.baseDirectory + "/work";
|
||||
defaultText = literalExpression ''baseDirectory + "/work"'';
|
||||
};
|
||||
staticSecretsDirectory = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
This is the default directory to look for statically configured secrets like `cluster-join-token.key`.
|
||||
|
||||
See also `clusterJoinTokenPath` and `binaryCachesPath` for fine-grained configuration.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.baseDirectory + "/secrets";
|
||||
defaultText = literalExpression ''baseDirectory + "/secrets"'';
|
||||
};
|
||||
clusterJoinTokenPath = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Location of the cluster-join-token.key file.
|
||||
|
||||
You can retrieve the contents of the file when creating a new agent via
|
||||
<https://hercules-ci.com/dashboard>.
|
||||
|
||||
As this value is confidential, it should not be in the store, but
|
||||
installed using other means, such as agenix, NixOps
|
||||
`deployment.keys`, or manual installation.
|
||||
|
||||
The contents of the file are used for authentication between the agent and the API.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.staticSecretsDirectory + "/cluster-join-token.key";
|
||||
defaultText = literalExpression ''staticSecretsDirectory + "/cluster-join-token.key"'';
|
||||
};
|
||||
binaryCachesPath = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Path to a JSON file containing binary cache secret keys.
|
||||
|
||||
As these values are confidential, they should not be in the store, but
|
||||
copied over using other means, such as agenix, NixOps
|
||||
`deployment.keys`, or manual installation.
|
||||
|
||||
The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/>.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.staticSecretsDirectory + "/binary-caches.json";
|
||||
defaultText = literalExpression ''staticSecretsDirectory + "/binary-caches.json"'';
|
||||
};
|
||||
secretsJsonPath = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Path to a JSON file containing secrets for effects.
|
||||
|
||||
As these values are confidential, they should not be in the store, but
|
||||
copied over using other means, such as agenix, NixOps
|
||||
`deployment.keys`, or manual installation.
|
||||
|
||||
The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/>.
|
||||
'';
|
||||
type = types.path;
|
||||
default = config.staticSecretsDirectory + "/secrets.json";
|
||||
defaultText = literalExpression ''staticSecretsDirectory + "/secrets.json"'';
|
||||
};
|
||||
};
|
||||
config = {
|
||||
labels = {
|
||||
agent.source =
|
||||
if packageOption.highestPrio == (lib.modules.mkOptionDefault { }).priority
|
||||
then "nixpkgs"
|
||||
else lib.mkOptionDefault "override";
|
||||
pkgs.version = pkgs.lib.version;
|
||||
lib.version = lib.version;
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit format settingsModule;
|
||||
}
|
||||
@@ -53,9 +53,7 @@ let
|
||||
# if running simultaneous services.
|
||||
NonBlocking = true;
|
||||
#LimitNOFILE = 30000;
|
||||
User =
|
||||
lib.mkIf config.systemd.services."public-inbox-${srv}".confinement.enable
|
||||
config.users.users."public-inbox".name;
|
||||
User = config.users.users."public-inbox".name;
|
||||
Group = config.users.groups."public-inbox".name;
|
||||
RuntimeDirectory = [
|
||||
"public-inbox-${srv}/perl-inline"
|
||||
@@ -63,7 +61,9 @@ let
|
||||
RuntimeDirectoryMode = "700";
|
||||
# This is for BindPaths= and BindReadOnlyPaths=
|
||||
# to allow traversal of directories they create inside RootDirectory=
|
||||
UMask = "0026";
|
||||
UMask = "0066";
|
||||
StateDirectory = ["public-inbox"];
|
||||
StateDirectoryMode = "0750";
|
||||
WorkingDirectory = stateDir;
|
||||
BindReadOnlyPaths = [
|
||||
"/etc"
|
||||
@@ -109,6 +109,7 @@ let
|
||||
SystemCallArchitectures = "native";
|
||||
|
||||
# The following options are redundant when confinement is enabled
|
||||
RootDirectory = "/var/empty";
|
||||
TemporaryFileSystem = "/";
|
||||
PrivateMounts = true;
|
||||
MountAPIVFS = true;
|
||||
@@ -433,10 +434,8 @@ in
|
||||
(mkIf cfg.imap.enable
|
||||
{ public-inbox-imapd = mkMerge [(serviceConfig "imapd") {
|
||||
after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
|
||||
environment.PI_DIR = "/var/lib/public-inbox/.public-inbox";
|
||||
requires = [ "public-inbox-init.service" ];
|
||||
serviceConfig = {
|
||||
DynamicUser = !config.systemd.services."public-inbox-imapd".confinement.enable;
|
||||
ExecStart = escapeShellArgs (
|
||||
[ "${cfg.package}/bin/public-inbox-imapd" ] ++
|
||||
cfg.imap.args ++
|
||||
@@ -449,10 +448,8 @@ in
|
||||
(mkIf cfg.http.enable
|
||||
{ public-inbox-httpd = mkMerge [(serviceConfig "httpd") {
|
||||
after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
|
||||
environment.PI_DIR = "/var/lib/public-inbox/.public-inbox";
|
||||
requires = [ "public-inbox-init.service" ];
|
||||
serviceConfig = {
|
||||
DynamicUser = !config.systemd.services."public-inbox-httpd".confinement.enable;
|
||||
ExecStart = escapeShellArgs (
|
||||
[ "${cfg.package}/bin/public-inbox-httpd" ] ++
|
||||
cfg.http.args ++
|
||||
@@ -490,10 +487,8 @@ in
|
||||
(mkIf cfg.nntp.enable
|
||||
{ public-inbox-nntpd = mkMerge [(serviceConfig "nntpd") {
|
||||
after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
|
||||
environment.PI_DIR = "/var/lib/public-inbox/.public-inbox";
|
||||
requires = [ "public-inbox-init.service" ];
|
||||
serviceConfig = {
|
||||
DynamicUser = !config.systemd.services."public-inbox-nntpd".confinement.enable;
|
||||
ExecStart = escapeShellArgs (
|
||||
[ "${cfg.package}/bin/public-inbox-nntpd" ] ++
|
||||
cfg.nntp.args ++
|
||||
@@ -514,10 +509,6 @@ in
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/public-inbox-watch";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
StateDirectory = ["public-inbox"];
|
||||
StateDirectoryMode = "0750";
|
||||
User = config.users.users."public-inbox".name;
|
||||
Group = config.users.groups."public-inbox".name;
|
||||
};
|
||||
}];
|
||||
})
|
||||
@@ -571,22 +562,15 @@ in
|
||||
ls -1 "$inbox" | grep -q '^xap' ||
|
||||
${cfg.package}/bin/public-inbox-index "$inbox"
|
||||
done
|
||||
|
||||
# Older versions of the module did not make inboxes group-readable.
|
||||
# chmod -R g+r ${stateDir}/inboxes
|
||||
'';
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
StateDirectory = [
|
||||
"public-inbox"
|
||||
"public-inbox/.public-inbox"
|
||||
"public-inbox/.public-inbox/emergency"
|
||||
"public-inbox/inboxes"
|
||||
];
|
||||
StateDirectoryMode = "0750";
|
||||
User = config.users.users."public-inbox".name;
|
||||
Group = config.users.groups."public-inbox".name;
|
||||
};
|
||||
}];
|
||||
})
|
||||
|
||||
@@ -29,7 +29,6 @@ in {
|
||||
};
|
||||
|
||||
appservice = rec {
|
||||
id = "facebook";
|
||||
address = "http://${hostname}:${toString port}";
|
||||
hostname = "localhost";
|
||||
port = 29319;
|
||||
@@ -172,7 +171,7 @@ in {
|
||||
|
||||
services.mautrix-facebook = {
|
||||
registrationData = {
|
||||
id = cfg.settings.appservice.id;
|
||||
id = "mautrix-facebook";
|
||||
|
||||
namespaces = {
|
||||
users = [
|
||||
|
||||
@@ -636,7 +636,6 @@ in {
|
||||
|
||||
trusted_key_servers = mkOption {
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
server_name = mkOption {
|
||||
type = types.str;
|
||||
@@ -645,6 +644,22 @@ in {
|
||||
Hostname of the trusted server.
|
||||
'';
|
||||
};
|
||||
|
||||
verify_keys = mkOption {
|
||||
type = types.nullOr (types.attrsOf types.str);
|
||||
default = null;
|
||||
example = literalExpression ''
|
||||
{
|
||||
"ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
|
||||
}
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
Attribute set from key id to base64 encoded public key.
|
||||
|
||||
If specified synapse will check that the response is signed
|
||||
by at least one of the given keys.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [ {
|
||||
|
||||
@@ -15,8 +15,6 @@ in {
|
||||
type = types.bool;
|
||||
};
|
||||
|
||||
package = mkPackageOptionMD pkgs "etcd" { };
|
||||
|
||||
name = mkOption {
|
||||
description = lib.mdDoc "Etcd unique node name.";
|
||||
default = config.networking.hostName;
|
||||
@@ -189,13 +187,13 @@ in {
|
||||
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart = "${cfg.package}/bin/etcd";
|
||||
ExecStart = "${pkgs.etcd}/bin/etcd";
|
||||
User = "etcd";
|
||||
LimitNOFILE = 40000;
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
environment.systemPackages = [ pkgs.etcd ];
|
||||
|
||||
users.users.etcd = {
|
||||
isSystemUser = true;
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
literalExpression
|
||||
mkEnableOption
|
||||
mdDoc
|
||||
mkIf
|
||||
mkOption
|
||||
mkPackageOptionMD
|
||||
optionalAttrs
|
||||
optional
|
||||
types;
|
||||
|
||||
cfg = config.services.legit;
|
||||
|
||||
yaml = pkgs.formats.yaml { };
|
||||
configFile = yaml.generate "legit.yaml" cfg.settings;
|
||||
|
||||
defaultStateDir = "/var/lib/legit";
|
||||
defaultStaticDir = "${cfg.settings.repo.scanPath}/static";
|
||||
defaultTemplatesDir = "${cfg.settings.repo.scanPath}/templates";
|
||||
in
|
||||
{
|
||||
options.services.legit = {
|
||||
enable = mkEnableOption (mdDoc "legit git web frontend");
|
||||
|
||||
package = mkPackageOptionMD pkgs "legit-web" { };
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "legit";
|
||||
description = mdDoc "User account under which legit runs.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "legit";
|
||||
description = mdDoc "Group account under which legit runs.";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
default = { };
|
||||
description = mdDoc ''
|
||||
The primary legit configuration. See the
|
||||
[sample configuration](https://github.com/icyphox/legit/blob/master/config.yaml)
|
||||
for possible values.
|
||||
'';
|
||||
type = types.submodule {
|
||||
options.repo = {
|
||||
scanPath = mkOption {
|
||||
type = types.path;
|
||||
default = defaultStateDir;
|
||||
description = mdDoc "Directory where legit will scan for repositories.";
|
||||
};
|
||||
readme = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = mdDoc "Readme files to look for.";
|
||||
};
|
||||
mainBranch = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "main" "master" ];
|
||||
description = mdDoc "Main branch to look for.";
|
||||
};
|
||||
ignore = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = mdDoc "Repositories to ignore.";
|
||||
};
|
||||
};
|
||||
options.dirs = {
|
||||
templates = mkOption {
|
||||
type = types.path;
|
||||
default = "${pkgs.legit-web}/lib/legit/templates";
|
||||
defaultText = literalExpression ''"''${pkgs.legit-web}/lib/legit/templates"'';
|
||||
description = mdDoc "Directories where template files are located.";
|
||||
};
|
||||
static = mkOption {
|
||||
type = types.path;
|
||||
default = "${pkgs.legit-web}/lib/legit/static";
|
||||
defaultText = literalExpression ''"''${pkgs.legit-web}/lib/legit/static"'';
|
||||
description = mdDoc "Directories where static files are located.";
|
||||
};
|
||||
};
|
||||
options.meta = {
|
||||
title = mkOption {
|
||||
type = types.str;
|
||||
default = "legit";
|
||||
description = mdDoc "Website title.";
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "git frontend";
|
||||
description = mdDoc "Website description.";
|
||||
};
|
||||
};
|
||||
options.server = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = mdDoc "Server name.";
|
||||
};
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = mdDoc "Host address.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 5555;
|
||||
description = mdDoc "Legit port.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.groups = optionalAttrs (cfg.group == "legit") {
|
||||
"${cfg.group}" = { };
|
||||
};
|
||||
|
||||
users.users = optionalAttrs (cfg.user == "legit") {
|
||||
"${cfg.user}" = {
|
||||
group = cfg.group;
|
||||
isSystemUser = true;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.legit = {
|
||||
description = "legit git frontend";
|
||||
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
restartTriggers = [ configFile ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = "${cfg.package}/bin/legit -config ${configFile}";
|
||||
Restart = "always";
|
||||
|
||||
WorkingDirectory = cfg.settings.repo.scanPath;
|
||||
StateDirectory = [ ] ++
|
||||
optional (cfg.settings.repo.scanPath == defaultStateDir) "legit" ++
|
||||
optional (cfg.settings.dirs.static == defaultStaticDir) "legit/static" ++
|
||||
optional (cfg.settings.dirs.templates == defaultTemplatesDir) "legit/templates";
|
||||
|
||||
# Hardening
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
DeviceAllow = [ "" ];
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = "strict";
|
||||
ReadWritePaths = cfg.settings.repo.scanPath;
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" "~@privileged" ];
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -569,10 +569,7 @@ in
|
||||
'';
|
||||
|
||||
assertions = [{ assertion = if cfg.settings.X11Forwarding then cfgc.setXAuthLocation else true;
|
||||
message = "cannot enable X11 forwarding without setting xauth location";}
|
||||
{ assertion = lib.lists.unique (map (x: lib.strings.toLower x) (attrNames cfg.settings))
|
||||
== (map (x: lib.strings.toLower x) (attrNames cfg.settings));
|
||||
message = "Duplicate sshd config key; does your capitalization match the option's?"; } ]
|
||||
message = "cannot enable X11 forwarding without setting xauth location";}]
|
||||
++ forEach cfg.listenAddresses ({ addr, ... }: {
|
||||
assertion = addr != null;
|
||||
message = "addr must be specified in each listenAddresses entry";
|
||||
|
||||
@@ -25,8 +25,6 @@ in
|
||||
options.services.thelounge = {
|
||||
enable = mkEnableOption (lib.mdDoc "The Lounge web IRC client");
|
||||
|
||||
package = mkPackageOptionMD pkgs "thelounge" { };
|
||||
|
||||
public = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
@@ -95,11 +93,11 @@ in
|
||||
serviceConfig = {
|
||||
User = "thelounge";
|
||||
StateDirectory = baseNameOf dataDir;
|
||||
ExecStart = "${getExe cfg.package} start";
|
||||
ExecStart = "${pkgs.thelounge}/bin/thelounge start";
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
environment.systemPackages = [ pkgs.thelounge ];
|
||||
};
|
||||
|
||||
meta = {
|
||||
|
||||
@@ -80,11 +80,11 @@ in
|
||||
options.services.epgstation = {
|
||||
enable = lib.mkEnableOption (lib.mdDoc description);
|
||||
|
||||
package = lib.mkPackageOptionMD pkgs "epgstation" { };
|
||||
|
||||
ffmpeg = lib.mkPackageOptionMD pkgs "ffmpeg" {
|
||||
default = [ "ffmpeg-headless" ];
|
||||
example = "pkgs.ffmpeg-full";
|
||||
package = lib.mkOption {
|
||||
default = pkgs.epgstation;
|
||||
type = lib.types.package;
|
||||
defaultText = lib.literalExpression "pkgs.epgstation";
|
||||
description = lib.mdDoc "epgstation package to use";
|
||||
};
|
||||
|
||||
usePreconfiguredStreaming = lib.mkOption {
|
||||
@@ -278,8 +278,6 @@ in
|
||||
package = lib.mkDefault pkgs.mariadb;
|
||||
ensureDatabases = [ cfg.database.name ];
|
||||
# FIXME: enable once mysqljs supports auth_socket
|
||||
# https://github.com/mysqljs/mysql/issues/1507
|
||||
#
|
||||
# ensureUsers = [ {
|
||||
# name = username;
|
||||
# ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
|
||||
@@ -297,8 +295,8 @@ in
|
||||
database = cfg.database.name;
|
||||
};
|
||||
|
||||
ffmpeg = lib.mkDefault "${cfg.ffmpeg}/bin/ffmpeg";
|
||||
ffprobe = lib.mkDefault "${cfg.ffmpeg}/bin/ffprobe";
|
||||
ffmpeg = lib.mkDefault "${pkgs.ffmpeg-full}/bin/ffmpeg";
|
||||
ffprobe = lib.mkDefault "${pkgs.ffmpeg-full}/bin/ffprobe";
|
||||
|
||||
# for disambiguation with TypeScript files
|
||||
recordedFileExtension = lib.mkDefault ".m2ts";
|
||||
@@ -310,15 +308,9 @@ in
|
||||
];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '/var/lib/epgstation/key' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/streamfiles' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/drop' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/recorded' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/thumbnail' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/db/subscribers' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/db/migrations/mysql' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/db/migrations/postgres' - ${username} ${groupname} - -"
|
||||
"d '/var/lib/epgstation/db/migrations/sqlite' - ${username} ${groupname} - -"
|
||||
];
|
||||
|
||||
systemd.services.epgstation = {
|
||||
|
||||
@@ -154,9 +154,6 @@ in
|
||||
description = "Mirakurun user";
|
||||
group = "video";
|
||||
isSystemUser = true;
|
||||
|
||||
# NPM insists on creating ~/.npm
|
||||
home = "/var/cache/mirakurun";
|
||||
};
|
||||
|
||||
services.mirakurun.serverSettings = {
|
||||
@@ -174,10 +171,9 @@ in
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${mirakurun}/bin/mirakurun start";
|
||||
ExecStart = "${mirakurun}/bin/mirakurun-start";
|
||||
User = username;
|
||||
Group = groupname;
|
||||
CacheDirectory = "mirakurun";
|
||||
RuntimeDirectory="mirakurun";
|
||||
StateDirectory="mirakurun";
|
||||
Nice = -10;
|
||||
|
||||
@@ -117,9 +117,7 @@ in {
|
||||
|
||||
# PHP 8.0 is the only version which is supported/tested by upstream:
|
||||
# https://github.com/grocy/grocy/blob/v3.3.0/README.md#how-to-install
|
||||
# Compatibility with PHP 8.1 is available on their development branch:
|
||||
# https://github.com/grocy/grocy/commit/38a4ad8ec480c29a1bff057b3482fd103b036848
|
||||
phpPackage = pkgs.php81;
|
||||
phpPackage = pkgs.php80;
|
||||
|
||||
inherit (cfg.phpfpm) settings;
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ in
|
||||
|
||||
services.phpfpm.pools.limesurvey = {
|
||||
inherit user group;
|
||||
phpPackage = pkgs.php81;
|
||||
phpPackage = pkgs.php80;
|
||||
phpEnv.DBENGINE = "${cfg.database.dbEngine}";
|
||||
phpEnv.LIMESURVEY_CONFIG = "${limesurveyConfig}";
|
||||
settings = {
|
||||
@@ -288,8 +288,8 @@ in
|
||||
environment.LIMESURVEY_CONFIG = limesurveyConfig;
|
||||
script = ''
|
||||
# update or install the database as required
|
||||
${pkgs.php81}/bin/php ${pkg}/share/limesurvey/application/commands/console.php updatedb || \
|
||||
${pkgs.php81}/bin/php ${pkg}/share/limesurvey/application/commands/console.php install admin password admin admin@example.com verbose
|
||||
${pkgs.php80}/bin/php ${pkg}/share/limesurvey/application/commands/console.php updatedb || \
|
||||
${pkgs.php80}/bin/php ${pkg}/share/limesurvey/application/commands/console.php install admin password admin admin@example.com verbose
|
||||
'';
|
||||
serviceConfig = {
|
||||
User = user;
|
||||
|
||||
@@ -586,7 +586,7 @@ in
|
||||
|
||||
# Create an outline-sequalize wrapper (a wrapper around the wrapper) that
|
||||
# has the config file's path baked in. This is necessary because there is
|
||||
# at least two occurrences of outline calling this from its own code.
|
||||
# at least one occurrence of outline calling this from its own code.
|
||||
sequelize = pkgs.writeShellScriptBin "outline-sequelize" ''
|
||||
exec ${cfg.package}/bin/outline-sequelize \
|
||||
--config $RUNTIME_DIRECTORY/database.json \
|
||||
@@ -687,18 +687,21 @@ in
|
||||
openssl rand -hex 32 > ${lib.escapeShellArg cfg.utilsSecretFile}
|
||||
fi
|
||||
|
||||
# The config file is required for the sequelize CLI.
|
||||
# The config file is required for the CLI, the DATABASE_URL environment
|
||||
# variable is read by the app.
|
||||
${if (cfg.databaseUrl == "local") then ''
|
||||
cat <<EOF > $RUNTIME_DIRECTORY/database.json
|
||||
{
|
||||
"production-ssl-disabled": {
|
||||
"production": {
|
||||
"dialect": "postgres",
|
||||
"host": "/run/postgresql",
|
||||
"username": null,
|
||||
"password": null,
|
||||
"dialect": "postgres"
|
||||
"password": null
|
||||
}
|
||||
}
|
||||
EOF
|
||||
export DATABASE_URL=${lib.escapeShellArg localPostgresqlUrl}
|
||||
export PGSSLMODE=disable
|
||||
'' else ''
|
||||
cat <<EOF > $RUNTIME_DIRECTORY/database.json
|
||||
{
|
||||
@@ -717,7 +720,11 @@ in
|
||||
}
|
||||
}
|
||||
EOF
|
||||
export DATABASE_URL=${lib.escapeShellArg cfg.databaseUrl}
|
||||
''}
|
||||
|
||||
cd $RUNTIME_DIRECTORY
|
||||
${sequelize}/bin/outline-sequelize db:migrate
|
||||
'';
|
||||
|
||||
script = ''
|
||||
@@ -774,7 +781,7 @@ in
|
||||
RuntimeDirectoryMode = "0750";
|
||||
# This working directory is required to find stuff like the set of
|
||||
# onboarding files:
|
||||
WorkingDirectory = "${cfg.package}/share/outline";
|
||||
WorkingDirectory = "${cfg.package}/share/outline/build";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,375 +0,0 @@
|
||||
{ options, config, lib, pkgs, utils, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.sftpgo;
|
||||
defaultUser = "sftpgo";
|
||||
settingsFormat = pkgs.formats.json {};
|
||||
configFile = settingsFormat.generate "sftpgo.json" cfg.settings;
|
||||
hasPrivilegedPorts = any (port: port > 0 && port < 1024) (
|
||||
catAttrs "port" (cfg.settings.httpd.bindings
|
||||
++ cfg.settings.ftpd.bindings
|
||||
++ cfg.settings.sftpd.bindings
|
||||
++ cfg.settings.webdavd.bindings
|
||||
)
|
||||
);
|
||||
in
|
||||
{
|
||||
options.services.sftpgo = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = mdDoc "sftpgo";
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.sftpgo;
|
||||
defaultText = literalExpression "pkgs.sftpgo";
|
||||
description = mdDoc ''
|
||||
Which SFTPGo package to use.
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [];
|
||||
description = mdDoc ''
|
||||
Additional command line arguments to pass to the sftpgo daemon.
|
||||
'';
|
||||
example = [ "--log-level" "info" ];
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/sftpgo";
|
||||
description = mdDoc ''
|
||||
The directory where SFTPGo stores its data files.
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = defaultUser;
|
||||
description = mdDoc ''
|
||||
User account name under which SFTPGo runs.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = defaultUser;
|
||||
description = mdDoc ''
|
||||
Group name under which SFTPGo runs.
|
||||
'';
|
||||
};
|
||||
|
||||
loadDataFile = mkOption {
|
||||
default = null;
|
||||
type = with types; nullOr path;
|
||||
description = mdDoc ''
|
||||
Path to a json file containing users and folders to load (or update) on startup.
|
||||
Check the [documentation](https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md)
|
||||
for the `--loaddata-from` command line argument for more info.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
default = {};
|
||||
description = mdDoc ''
|
||||
The primary sftpgo configuration. See the
|
||||
[configuration reference](https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md)
|
||||
for possible values.
|
||||
'';
|
||||
type = with types; submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
httpd.bindings = mkOption {
|
||||
default = [];
|
||||
description = mdDoc ''
|
||||
Configure listen addresses and ports for httpd.
|
||||
'';
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = mdDoc ''
|
||||
Network listen address. Leave blank to listen on all available network interfaces.
|
||||
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8080;
|
||||
description = mdDoc ''
|
||||
The port for serving HTTP(S) requests.
|
||||
|
||||
Setting the port to `0` disables listening on this interface binding.
|
||||
'';
|
||||
};
|
||||
|
||||
enable_web_admin = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc ''
|
||||
Enable the built-in web admin for this interface binding.
|
||||
'';
|
||||
};
|
||||
|
||||
enable_web_client = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc ''
|
||||
Enable the built-in web client for this interface binding.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
ftpd.bindings = mkOption {
|
||||
default = [];
|
||||
description = mdDoc ''
|
||||
Configure listen addresses and ports for ftpd.
|
||||
'';
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = mdDoc ''
|
||||
Network listen address. Leave blank to listen on all available network interfaces.
|
||||
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 0;
|
||||
description = mdDoc ''
|
||||
The port for serving FTP requests.
|
||||
|
||||
Setting the port to `0` disables listening on this interface binding.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
sftpd.bindings = mkOption {
|
||||
default = [];
|
||||
description = mdDoc ''
|
||||
Configure listen addresses and ports for sftpd.
|
||||
'';
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = mdDoc ''
|
||||
Network listen address. Leave blank to listen on all available network interfaces.
|
||||
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 0;
|
||||
description = mdDoc ''
|
||||
The port for serving SFTP requests.
|
||||
|
||||
Setting the port to `0` disables listening on this interface binding.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
webdavd.bindings = mkOption {
|
||||
default = [];
|
||||
description = mdDoc ''
|
||||
Configure listen addresses and ports for webdavd.
|
||||
'';
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = mdDoc ''
|
||||
Network listen address. Leave blank to listen on all available network interfaces.
|
||||
On *NIX you can specify an absolute path to listen on a Unix-domain socket.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 0;
|
||||
description = mdDoc ''
|
||||
The port for serving WebDAV requests.
|
||||
|
||||
Setting the port to `0` disables listening on this interface binding.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
smtp = mkOption {
|
||||
default = {};
|
||||
description = mdDoc ''
|
||||
SMTP configuration section.
|
||||
'';
|
||||
type = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
options = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = mdDoc ''
|
||||
Location of SMTP email server. Leave empty to disable email sending capabilities.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 465;
|
||||
description = mdDoc "Port of the SMTP Server.";
|
||||
};
|
||||
|
||||
encryption = mkOption {
|
||||
type = types.enum [ 0 1 2 ];
|
||||
default = 1;
|
||||
description = mdDoc ''
|
||||
Encryption scheme:
|
||||
- `0`: No encryption
|
||||
- `1`: TLS
|
||||
- `2`: STARTTLS
|
||||
'';
|
||||
};
|
||||
|
||||
auth_type = mkOption {
|
||||
type = types.enum [ 0 1 2 ];
|
||||
default = 0;
|
||||
description = mdDoc ''
|
||||
- `0`: Plain
|
||||
- `1`: Login
|
||||
- `2`: CRAM-MD5
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "sftpgo";
|
||||
description = mdDoc "SMTP username.";
|
||||
};
|
||||
|
||||
from = mkOption {
|
||||
type = types.str;
|
||||
default = "SFTPGo <sftpgo@example.com>";
|
||||
description = mdDoc ''
|
||||
From address.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.sftpgo.settings = (mapAttrs (name: mkDefault) {
|
||||
ftpd.bindings = [{ port = 0; }];
|
||||
httpd.bindings = [{ port = 0; }];
|
||||
sftpd.bindings = [{ port = 0; }];
|
||||
webdavd.bindings = [{ port = 0; }];
|
||||
httpd.openapi_path = "${cfg.package}/share/sftpgo/openapi";
|
||||
httpd.templates_path = "${cfg.package}/share/sftpgo/templates";
|
||||
httpd.static_files_path = "${cfg.package}/share/sftpgo/static";
|
||||
smtp.templates_path = "${cfg.package}/share/sftpgo/templates";
|
||||
});
|
||||
|
||||
users = optionalAttrs (cfg.user == defaultUser) {
|
||||
users = {
|
||||
${defaultUser} = {
|
||||
description = "SFTPGo system user";
|
||||
isSystemUser = true;
|
||||
group = defaultUser;
|
||||
home = cfg.dataDir;
|
||||
};
|
||||
};
|
||||
|
||||
groups = {
|
||||
${defaultUser} = {
|
||||
members = [ defaultUser ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.sftpgo = {
|
||||
description = "SFTPGo daemon";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
environment = {
|
||||
SFTPGO_CONFIG_FILE = mkDefault configFile;
|
||||
SFTPGO_LOG_FILE_PATH = mkDefault ""; # log to journal
|
||||
SFTPGO_LOADDATA_FROM = mkIf (cfg.loadDataFile != null) cfg.loadDataFile;
|
||||
};
|
||||
|
||||
serviceConfig = mkMerge [
|
||||
({
|
||||
Type = "simple";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
ReadWritePaths = [ cfg.dataDir ];
|
||||
LimitNOFILE = 8192; # taken from upstream
|
||||
KillMode = "mixed";
|
||||
ExecStart = "${cfg.package}/bin/sftpgo serve ${utils.escapeSystemdExecArgs cfg.extraArgs}";
|
||||
ExecReload = "${pkgs.util-linux}/bin/kill -s HUP $MAINPID";
|
||||
|
||||
# Service hardening
|
||||
CapabilityBoundingSet = [ (optionalString hasPrivilegedPorts "CAP_NET_BIND_SERVICE") ];
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" "~@privileged" ];
|
||||
UMask = "0077";
|
||||
})
|
||||
(mkIf hasPrivilegedPorts {
|
||||
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
||||
})
|
||||
(mkIf (cfg.dataDir == options.services.sftpgo.dataDir.default) {
|
||||
StateDirectory = baseNameOf cfg.dataDir;
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -309,54 +309,36 @@ let
|
||||
onlySSL = vhost.onlySSL || vhost.enableSSL;
|
||||
hasSSL = onlySSL || vhost.addSSL || vhost.forceSSL;
|
||||
|
||||
# First evaluation of defaultListen based on a set of listen lines.
|
||||
mkDefaultListenVhost = listenLines:
|
||||
# If this vhost has SSL or is a SSL rejection host.
|
||||
# We enable a TLS variant for lines without explicit ssl or ssl = true.
|
||||
optionals (hasSSL || vhost.rejectSSL)
|
||||
(map (listen: { port = cfg.defaultSSLListenPort; ssl = true; } // listen)
|
||||
(filter (listen: !(listen ? ssl) || listen.ssl) listenLines))
|
||||
# If this vhost is supposed to serve HTTP
|
||||
# We provide listen lines for those without explicit ssl or ssl = false.
|
||||
++ optionals (!onlySSL)
|
||||
(map (listen: { port = cfg.defaultHTTPListenPort; ssl = false; } // listen)
|
||||
(filter (listen: !(listen ? ssl) || !listen.ssl) listenLines));
|
||||
|
||||
defaultListen =
|
||||
if vhost.listen != [] then vhost.listen
|
||||
else
|
||||
if cfg.defaultListen != [] then mkDefaultListenVhost
|
||||
# Cleanup nulls which will mess up with //.
|
||||
# TODO: is there a better way to achieve this? i.e. mergeButIgnoreNullPlease?
|
||||
(map (listenLine: filterAttrs (_: v: (v != null)) listenLine) cfg.defaultListen)
|
||||
else
|
||||
let addrs = if vhost.listenAddresses != [] then vhost.listenAddresses else cfg.defaultListenAddresses;
|
||||
in mkDefaultListenVhost (map (addr: { inherit addr; }) addrs);
|
||||
|
||||
in optionals (hasSSL || vhost.rejectSSL) (map (addr: { inherit addr; port = cfg.defaultSSLListenPort; ssl = true; }) addrs)
|
||||
++ optionals (!onlySSL) (map (addr: { inherit addr; port = cfg.defaultHTTPListenPort; ssl = false; }) addrs);
|
||||
|
||||
hostListen =
|
||||
if vhost.forceSSL
|
||||
then filter (x: x.ssl) defaultListen
|
||||
else defaultListen;
|
||||
|
||||
listenString = { addr, port, ssl, proxyProtocol ? false, extraParameters ? [], ... }:
|
||||
listenString = { addr, port, ssl, extraParameters ? [], ... }:
|
||||
# UDP listener for QUIC transport protocol.
|
||||
(optionalString (ssl && vhost.quic) ("
|
||||
listen ${addr}:${toString port} quic "
|
||||
+ optionalString vhost.default "default_server "
|
||||
+ optionalString vhost.reuseport "reuseport "
|
||||
+ optionalString (extraParameters != []) (concatStringsSep " "
|
||||
(let inCompatibleParameters = [ "ssl" "proxy_protocol" "http2" ];
|
||||
+ optionalString (extraParameters != []) (concatStringsSep " " (
|
||||
let inCompatibleParameters = [ "ssl" "proxy_protocol" "http2" ];
|
||||
isCompatibleParameter = param: !(any (p: p == param) inCompatibleParameters);
|
||||
in filter isCompatibleParameter extraParameters))
|
||||
+ ";"))
|
||||
+ "
|
||||
|
||||
listen ${addr}:${toString port} "
|
||||
+ optionalString (ssl && vhost.http2) "http2 "
|
||||
+ optionalString ssl "ssl "
|
||||
+ optionalString vhost.default "default_server "
|
||||
+ optionalString vhost.reuseport "reuseport "
|
||||
+ optionalString proxyProtocol "proxy_protocol "
|
||||
+ optionalString (extraParameters != []) (concatStringsSep " " extraParameters)
|
||||
+ ";";
|
||||
|
||||
@@ -557,49 +539,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
defaultListen = mkOption {
|
||||
type = with types; listOf (submodule {
|
||||
options = {
|
||||
addr = mkOption {
|
||||
type = str;
|
||||
description = lib.mdDoc "IP address.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = nullOr port;
|
||||
description = lib.mdDoc "Port number.";
|
||||
default = null;
|
||||
};
|
||||
ssl = mkOption {
|
||||
type = nullOr bool;
|
||||
default = null;
|
||||
description = lib.mdDoc "Enable SSL.";
|
||||
};
|
||||
proxyProtocol = mkOption {
|
||||
type = bool;
|
||||
description = lib.mdDoc "Enable PROXY protocol.";
|
||||
default = false;
|
||||
};
|
||||
extraParameters = mkOption {
|
||||
type = listOf str;
|
||||
description = lib.mdDoc "Extra parameters of this listen directive.";
|
||||
default = [ ];
|
||||
example = [ "backlog=1024" "deferred" ];
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [];
|
||||
example = literalExpression ''[
|
||||
{ addr = "10.0.0.12"; proxyProtocol = true; ssl = true; }
|
||||
{ addr = "0.0.0.0"; }
|
||||
{ addr = "[::0]"; }
|
||||
]'';
|
||||
description = lib.mdDoc ''
|
||||
If vhosts do not specify listen, use these addresses by default.
|
||||
This option takes precedence over {option}`defaultListenAddresses` and
|
||||
other listen-related defaults options.
|
||||
'';
|
||||
};
|
||||
|
||||
defaultListenAddresses = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "0.0.0.0" ] ++ optional enableIPv6 "[::0]";
|
||||
@@ -607,7 +546,6 @@ in
|
||||
example = literalExpression ''[ "10.0.0.12" "[2002:a00:1::]" ]'';
|
||||
description = lib.mdDoc ''
|
||||
If vhosts do not specify listenAddresses, use these addresses by default.
|
||||
This is akin to writing `defaultListen = [ { addr = "0.0.0.0" } ]`.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -1140,32 +1078,6 @@ in
|
||||
which can be achieved by setting `services.nginx.package = pkgs.nginxQuic;`.
|
||||
'';
|
||||
}
|
||||
|
||||
{
|
||||
# The idea is to understand whether there is a virtual host with a listen configuration
|
||||
# that requires ACME configuration but has no HTTP listener which will make deterministically fail
|
||||
# this operation.
|
||||
# Options' priorities are the following at the moment:
|
||||
# listen (vhost) > defaultListen (server) > listenAddresses (vhost) > defaultListenAddresses (server)
|
||||
assertion =
|
||||
let
|
||||
hasAtLeastHttpListener = listenOptions: any (listenLine: if listenLine ? proxyProtocol then !listenLine.proxyProtocol else true) listenOptions;
|
||||
hasAtLeastDefaultHttpListener = if cfg.defaultListen != [] then hasAtLeastHttpListener cfg.defaultListen else (cfg.defaultListenAddresses != []);
|
||||
in
|
||||
all (host:
|
||||
let
|
||||
hasAtLeastVhostHttpListener = if host.listen != [] then hasAtLeastHttpListener host.listen else (host.listenAddresses != []);
|
||||
vhostAuthority = host.listen != [] || (cfg.defaultListen == [] && host.listenAddresses != []);
|
||||
in
|
||||
# Either vhost has precedence and we need a vhost specific http listener
|
||||
# Either vhost set nothing and inherit from server settings
|
||||
host.enableACME -> ((vhostAuthority && hasAtLeastVhostHttpListener) || (!vhostAuthority && hasAtLeastDefaultHttpListener))
|
||||
) (attrValues virtualHosts);
|
||||
message = ''
|
||||
services.nginx.virtualHosts.<name>.enableACME requires a HTTP listener
|
||||
to answer to ACME requests.
|
||||
'';
|
||||
}
|
||||
] ++ map (name: mkCertOwnershipAssertion {
|
||||
inherit (cfg) group user;
|
||||
cert = config.security.acme.certs.${name};
|
||||
|
||||
@@ -27,35 +27,12 @@ with lib;
|
||||
};
|
||||
|
||||
listen = mkOption {
|
||||
type = with types; listOf (submodule {
|
||||
options = {
|
||||
addr = mkOption {
|
||||
type = str;
|
||||
description = lib.mdDoc "IP address.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = lib.mdDoc "Port number.";
|
||||
default = 80;
|
||||
};
|
||||
ssl = mkOption {
|
||||
type = bool;
|
||||
description = lib.mdDoc "Enable SSL.";
|
||||
default = false;
|
||||
};
|
||||
proxyProtocol = mkOption {
|
||||
type = bool;
|
||||
description = lib.mdDoc "Enable PROXY protocol.";
|
||||
default = false;
|
||||
};
|
||||
extraParameters = mkOption {
|
||||
type = listOf str;
|
||||
description = lib.mdDoc "Extra parameters of this listen directive.";
|
||||
default = [ ];
|
||||
example = [ "backlog=1024" "deferred" ];
|
||||
};
|
||||
};
|
||||
});
|
||||
type = with types; listOf (submodule { options = {
|
||||
addr = mkOption { type = str; description = lib.mdDoc "IP address."; };
|
||||
port = mkOption { type = port; description = lib.mdDoc "Port number."; default = 80; };
|
||||
ssl = mkOption { type = bool; description = lib.mdDoc "Enable SSL."; default = false; };
|
||||
extraParameters = mkOption { type = listOf str; description = lib.mdDoc "Extra parameters of this listen directive."; default = []; example = [ "backlog=1024" "deferred" ]; };
|
||||
}; });
|
||||
default = [];
|
||||
example = [
|
||||
{ addr = "195.154.1.1"; port = 443; ssl = true; }
|
||||
@@ -68,7 +45,7 @@ with lib;
|
||||
and `onlySSL`.
|
||||
|
||||
If you only want to set the addresses manually and not
|
||||
the ports, take a look at `listenAddresses`.
|
||||
the ports, take a look at `listenAddresses`
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -187,15 +187,6 @@ in
|
||||
xdg.mime.enable = true;
|
||||
xdg.icons.enable = true;
|
||||
|
||||
xdg.portal.enable = true;
|
||||
xdg.portal.extraPortals = [
|
||||
pkgs.xdg-desktop-portal-xapp
|
||||
(pkgs.xdg-desktop-portal-gtk.override {
|
||||
# Do not build portals that we already have.
|
||||
buildPortalsInGnome = false;
|
||||
})
|
||||
];
|
||||
|
||||
# Override GSettings schemas
|
||||
environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-overrides}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
|
||||
|
||||
|
||||
@@ -25,11 +25,9 @@ let
|
||||
|
||||
sectionDHCPv4 = checkUnitConfig "DHCPv4" [
|
||||
(assertOnlyFields [
|
||||
"ClientIdentifier"
|
||||
"DUIDType"
|
||||
"DUIDRawData"
|
||||
])
|
||||
(assertValueOneOf "ClientIdentifier" ["mac" "duid" "duid-only"])
|
||||
];
|
||||
|
||||
sectionDHCPv6 = checkUnitConfig "DHCPv6" [
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{ config, lib, pkgs, utils, ... }:
|
||||
let
|
||||
requiredStratisFilesystems = lib.attrsets.filterAttrs (_: x: utils.fsNeededForBoot x && x.stratis.poolUuid != null) config.fileSystems;
|
||||
in
|
||||
{
|
||||
options = {};
|
||||
config = lib.mkIf (requiredStratisFilesystems != {}) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = config.boot.initrd.systemd.enable;
|
||||
message = "stratis root fs requires systemd stage 1";
|
||||
}
|
||||
];
|
||||
boot.initrd = {
|
||||
systemd = {
|
||||
storePaths = [
|
||||
"${pkgs.stratisd}/lib/udev/stratis-base32-decode"
|
||||
"${pkgs.stratisd}/lib/udev/stratis-str-cmp"
|
||||
"${pkgs.lvm2.bin}/bin/dmsetup"
|
||||
"${pkgs.stratisd}/libexec/stratisd-min"
|
||||
"${pkgs.stratisd.initrd}/bin/stratis-rootfs-setup"
|
||||
];
|
||||
packages = [pkgs.stratisd.initrd];
|
||||
extraBin = {
|
||||
thin_check = "${pkgs."thin-provisioning-tools"}/bin/thin_check";
|
||||
thin_repair = "${pkgs."thin-provisioning-tools"}/bin/thin_repair";
|
||||
thin_metadata_size = "${pkgs."thin-provisioning-tools"}/bin/thin_metadata_size";
|
||||
stratis-min = "${pkgs.stratisd}/bin/stratis-min";
|
||||
};
|
||||
services =
|
||||
lib.attrsets.mapAttrs' (
|
||||
mountPoint: fileSystem: {
|
||||
name = "stratis-setup-${fileSystem.stratis.poolUuid}";
|
||||
value = {
|
||||
description = "setup for Stratis root filesystem";
|
||||
unitConfig.DefaultDependencies = "no";
|
||||
conflicts = [ "shutdown.target" "initrd-switch-root.target" ];
|
||||
onFailure = [ "emergency.target" ];
|
||||
unitConfig.OnFailureJobMode = "isolate";
|
||||
wants = [ "stratisd-min.service" "plymouth-start.service" ];
|
||||
wantedBy = [ "initrd.target" ];
|
||||
after = [ "paths.target" "plymouth-start.service" "stratisd-min.service" ];
|
||||
before = [ "initrd.target" "shutdown.target" "initrd-switch-root.target" ];
|
||||
environment.STRATIS_ROOTFS_UUID = fileSystem.stratis.poolUuid;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${pkgs.stratisd.initrd}/bin/stratis-rootfs-setup";
|
||||
RemainAfterExit = "yes";
|
||||
};
|
||||
};
|
||||
}
|
||||
) requiredStratisFilesystems;
|
||||
};
|
||||
availableKernelModules = [ "dm-thin-pool" "dm-crypt" ] ++ [ "aes" "aes_generic" "blowfish" "twofish"
|
||||
"serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
|
||||
"af_alg" "algif_skcipher"
|
||||
];
|
||||
services.udev.packages = [
|
||||
pkgs.stratisd.initrd
|
||||
pkgs.lvm2
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -36,15 +36,6 @@ let
|
||||
description = lib.mdDoc "Location of the mounted file system.";
|
||||
};
|
||||
|
||||
stratis.poolUuid = lib.mkOption {
|
||||
type = types.uniq (types.nullOr types.str);
|
||||
description = lib.mdDoc ''
|
||||
UUID of the stratis pool that the fs is located in
|
||||
'';
|
||||
example = "04c68063-90a5-4235-b9dd-6180098a20d9";
|
||||
default = null;
|
||||
};
|
||||
|
||||
device = mkOption {
|
||||
default = null;
|
||||
example = "/dev/sda";
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
boolToString
|
||||
mkDefault
|
||||
mkIf
|
||||
optional
|
||||
readFile
|
||||
;
|
||||
in
|
||||
|
||||
with lib;
|
||||
{
|
||||
imports = [
|
||||
../profiles/headless.nix
|
||||
@@ -75,7 +65,7 @@ in
|
||||
systemd.services.google-guest-agent = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
restartTriggers = [ config.environment.etc."default/instance_configs.cfg".source ];
|
||||
path = optional config.users.mutableUsers pkgs.shadow;
|
||||
path = lib.optional config.users.mutableUsers pkgs.shadow;
|
||||
};
|
||||
systemd.services.google-startup-scripts.wantedBy = [ "multi-user.target" ];
|
||||
systemd.services.google-shutdown-scripts.wantedBy = [ "multi-user.target" ];
|
||||
@@ -86,7 +76,7 @@ in
|
||||
|
||||
users.groups.google-sudoers = mkIf config.users.mutableUsers { };
|
||||
|
||||
boot.extraModprobeConfig = readFile "${pkgs.google-guest-configs}/etc/modprobe.d/gce-blacklist.conf";
|
||||
boot.extraModprobeConfig = lib.readFile "${pkgs.google-guest-configs}/etc/modprobe.d/gce-blacklist.conf";
|
||||
|
||||
environment.etc."sysctl.d/60-gce-network-security.conf".source = "${pkgs.google-guest-configs}/etc/sysctl.d/60-gce-network-security.conf";
|
||||
|
||||
|
||||
@@ -187,20 +187,20 @@ with lib;
|
||||
guestAgentSupport = false;
|
||||
}).overrideAttrs ( super: rec {
|
||||
|
||||
version = "7.2.1";
|
||||
version = "7.0.0";
|
||||
src = pkgs.fetchurl {
|
||||
url= "https://download.qemu.org/qemu-${version}.tar.xz";
|
||||
sha256 = "sha256-jIVpms+dekOl/immTN1WNwsMLRrQdLr3CYqCTReq1zs=";
|
||||
sha256 = "sha256-9rN1x5UfcoQCeYsLqrsthkeMpT1Eztvvq74cRr9G+Dk=";
|
||||
};
|
||||
patches = [
|
||||
# Proxmox' VMA tool is published as a particular patch upon QEMU
|
||||
(pkgs.fetchpatch {
|
||||
url =
|
||||
let
|
||||
rev = "abb04bb6272c1202ca9face0827917552b9d06f6";
|
||||
path = "debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch";
|
||||
rev = "1976ca460796f28447b41e3618e5c1e234035dd5";
|
||||
path = "debian/patches/pve/0026-PVE-Backup-add-vma-backup-format-code.patch";
|
||||
in "https://git.proxmox.com/?p=pve-qemu.git;a=blob_plain;hb=${rev};f=${path}";
|
||||
hash = "sha256-3d0HHdvaExCry6zcULnziYnWIAnn24vECkI4sjj2BMg=";
|
||||
hash = "sha256-2Dz+ceTwrcyYYxi76RtyY3v15/2pwGcDhFuoZWlgbjc=";
|
||||
})
|
||||
|
||||
# Proxmox' VMA tool uses O_DIRECT which fails on tmpfs
|
||||
@@ -220,7 +220,6 @@ with lib;
|
||||
];
|
||||
|
||||
buildInputs = super.buildInputs ++ [ pkgs.libuuid ];
|
||||
nativeBuildInputs = super.nativeBuildInputs ++ [ pkgs.perl ];
|
||||
|
||||
});
|
||||
in
|
||||
|
||||
@@ -564,8 +564,7 @@ in
|
||||
virtualisation.vlans =
|
||||
mkOption {
|
||||
type = types.listOf types.ints.unsigned;
|
||||
default = if config.virtualisation.interfaces == {} then [ 1 ] else [ ];
|
||||
defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
|
||||
default = [ 1 ];
|
||||
example = [ 1 2 ];
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
@@ -580,35 +579,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.interfaces = mkOption {
|
||||
default = {};
|
||||
example = {
|
||||
enp1s0.vlan = 1;
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Network interfaces to add to the VM.
|
||||
'';
|
||||
type = with types; attrsOf (submodule {
|
||||
options = {
|
||||
vlan = mkOption {
|
||||
type = types.ints.unsigned;
|
||||
description = lib.mdDoc ''
|
||||
VLAN to which the network interface is connected.
|
||||
'';
|
||||
};
|
||||
|
||||
assignIP = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Automatically assign an IP address to the network interface using the same scheme as
|
||||
virtualisation.vlans.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
virtualisation.writableStore =
|
||||
mkOption {
|
||||
type = types.bool;
|
||||
@@ -893,13 +863,7 @@ in
|
||||
The address must be in the default VLAN (10.0.2.0/24).
|
||||
'';
|
||||
}
|
||||
])) ++ [
|
||||
{ assertion = pkgs.stdenv.hostPlatform.is32bit -> cfg.memorySize < 2047;
|
||||
message = ''
|
||||
virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max.
|
||||
'';
|
||||
}
|
||||
];
|
||||
]));
|
||||
|
||||
warnings =
|
||||
optional (
|
||||
|
||||
@@ -12,7 +12,7 @@ let
|
||||
|
||||
version = fileContents ../.version;
|
||||
versionSuffix =
|
||||
(if stableBranch then "." else "pre") + "${toString nixpkgs.revCount}.${nixpkgs.shortRev}";
|
||||
(if stableBranch then "." else "beta") + "${toString (nixpkgs.revCount - 487364)}.${nixpkgs.shortRev}";
|
||||
|
||||
# Run the tests for each platform. You can run a test by doing
|
||||
# e.g. ‘nix-build release.nix -A tests.login.x86_64-linux’,
|
||||
|
||||
@@ -278,7 +278,6 @@ in {
|
||||
fsck = handleTest ./fsck.nix {};
|
||||
fsck-systemd-stage-1 = handleTest ./fsck.nix { systemdStage1 = true; };
|
||||
ft2-clone = handleTest ./ft2-clone.nix {};
|
||||
legit = handleTest ./legit.nix {};
|
||||
mimir = handleTest ./mimir.nix {};
|
||||
garage = handleTest ./garage {};
|
||||
gemstash = handleTest ./gemstash.nix {};
|
||||
@@ -522,7 +521,6 @@ in {
|
||||
nginx-sandbox = handleTestOn ["x86_64-linux"] ./nginx-sandbox.nix {};
|
||||
nginx-sso = handleTest ./nginx-sso.nix {};
|
||||
nginx-variants = handleTest ./nginx-variants.nix {};
|
||||
nginx-proxyprotocol = handleTest ./nginx-proxyprotocol {};
|
||||
nifi = handleTestOn ["x86_64-linux"] ./web-apps/nifi.nix {};
|
||||
nitter = handleTest ./nitter.nix {};
|
||||
nix-ld = handleTest ./nix-ld.nix {};
|
||||
@@ -557,7 +555,6 @@ in {
|
||||
openstack-image-userdata = (handleTestOn ["x86_64-linux"] ./openstack-image.nix {}).userdata or {};
|
||||
opentabletdriver = handleTest ./opentabletdriver.nix {};
|
||||
owncast = handleTest ./owncast.nix {};
|
||||
outline = handleTest ./outline.nix {};
|
||||
image-contents = handleTest ./image-contents.nix {};
|
||||
openvscode-server = handleTest ./openvscode-server.nix {};
|
||||
orangefs = handleTest ./orangefs.nix {};
|
||||
@@ -667,7 +664,6 @@ in {
|
||||
seafile = handleTest ./seafile.nix {};
|
||||
searx = handleTest ./searx.nix {};
|
||||
service-runner = handleTest ./service-runner.nix {};
|
||||
sftpgo = runTest ./sftpgo.nix;
|
||||
sfxr-qt = handleTest ./sfxr-qt.nix {};
|
||||
sgtpuzzles = handleTest ./sgtpuzzles.nix {};
|
||||
shadow = handleTest ./shadow.nix {};
|
||||
|
||||
@@ -53,7 +53,7 @@ import ./make-test-python.nix ({ pkgs, ... } : let
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
DNS.1 = node1
|
||||
@@ -79,26 +79,23 @@ import ./make-test-python.nix ({ pkgs, ... } : let
|
||||
keyFile = etcd_key;
|
||||
certFile = etcd_cert;
|
||||
trustedCaFile = ca_pem;
|
||||
clientCertAuth = true;
|
||||
peerClientCertAuth = true;
|
||||
listenClientUrls = ["https://127.0.0.1:2379"];
|
||||
listenPeerUrls = ["https://0.0.0.0:2380"];
|
||||
};
|
||||
};
|
||||
|
||||
environment.variables = {
|
||||
ETCD_CERT_FILE = "${etcd_client_cert}";
|
||||
ETCD_KEY_FILE = "${etcd_client_key}";
|
||||
ETCD_CA_FILE = "${ca_pem}";
|
||||
ETCDCTL_ENDPOINTS = "https://127.0.0.1:2379";
|
||||
ETCDCTL_CACERT = "${ca_pem}";
|
||||
ETCDCTL_CERT = "${etcd_cert}";
|
||||
ETCDCTL_KEY = "${etcd_key}";
|
||||
ETCDCTL_CERT_FILE = "${etcd_client_cert}";
|
||||
ETCDCTL_KEY_FILE = "${etcd_client_key}";
|
||||
ETCDCTL_CA_FILE = "${ca_pem}";
|
||||
ETCDCTL_PEERS = "https://127.0.0.1:2379";
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 2380 ];
|
||||
};
|
||||
in {
|
||||
name = "etcd-cluster";
|
||||
name = "etcd";
|
||||
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [ offline ];
|
||||
@@ -137,21 +134,21 @@ in {
|
||||
node2.start()
|
||||
node1.wait_for_unit("etcd.service")
|
||||
node2.wait_for_unit("etcd.service")
|
||||
node2.wait_until_succeeds("etcdctl endpoint status")
|
||||
node1.succeed("etcdctl put /foo/bar 'Hello world'")
|
||||
node2.wait_until_succeeds("etcdctl cluster-health")
|
||||
node1.succeed("etcdctl set /foo/bar 'Hello world'")
|
||||
node2.succeed("etcdctl get /foo/bar | grep 'Hello world'")
|
||||
|
||||
with subtest("should add another member"):
|
||||
node1.wait_until_succeeds("etcdctl member add node3 --peer-urls=https://node3:2380")
|
||||
node1.wait_until_succeeds("etcdctl member add node3 https://node3:2380")
|
||||
node3.start()
|
||||
node3.wait_for_unit("etcd.service")
|
||||
node3.wait_until_succeeds("etcdctl member list | grep 'node3'")
|
||||
node3.succeed("etcdctl endpoint status")
|
||||
node3.succeed("etcdctl cluster-health")
|
||||
|
||||
with subtest("should survive member crash"):
|
||||
node3.crash()
|
||||
node1.succeed("etcdctl endpoint status")
|
||||
node1.succeed("etcdctl put /foo/bar 'Hello degraded world'")
|
||||
node1.succeed("etcdctl cluster-health")
|
||||
node1.succeed("etcdctl set /foo/bar 'Hello degraded world'")
|
||||
node1.succeed("etcdctl get /foo/bar | grep 'Hello degraded world'")
|
||||
'';
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@ import ./make-test-python.nix ({ pkgs, ... } : {
|
||||
node.wait_for_unit("etcd.service")
|
||||
|
||||
with subtest("should write and read some values to etcd"):
|
||||
node.succeed("etcdctl put /foo/bar 'Hello world'")
|
||||
node.succeed("etcdctl set /foo/bar 'Hello world'")
|
||||
node.succeed("etcdctl get /foo/bar | grep 'Hello world'")
|
||||
'';
|
||||
})
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
simpleUefiGrub
|
||||
simpleUefiGrubSpecialisation
|
||||
simpleUefiSystemdBoot
|
||||
stratisRoot
|
||||
# swraid
|
||||
zfsroot
|
||||
;
|
||||
|
||||
@@ -989,39 +989,4 @@ in {
|
||||
)
|
||||
'';
|
||||
};
|
||||
} // optionalAttrs systemdStage1 {
|
||||
stratisRoot = makeInstallerTest "stratisRoot" {
|
||||
createPartitions = ''
|
||||
machine.succeed(
|
||||
"sgdisk --zap-all /dev/vda",
|
||||
"sgdisk --new=1:0:+100M --typecode=0:ef00 /dev/vda", # /boot
|
||||
"sgdisk --new=2:0:+1G --typecode=0:8200 /dev/vda", # swap
|
||||
"sgdisk --new=3:0:+5G --typecode=0:8300 /dev/vda", # /
|
||||
"udevadm settle",
|
||||
|
||||
"mkfs.vfat /dev/vda1",
|
||||
"mkswap /dev/vda2 -L swap",
|
||||
"swapon -L swap",
|
||||
"stratis pool create my-pool /dev/vda3",
|
||||
"stratis filesystem create my-pool nixos",
|
||||
"udevadm settle",
|
||||
|
||||
"mount /dev/stratis/my-pool/nixos /mnt",
|
||||
"mkdir -p /mnt/boot",
|
||||
"mount /dev/vda1 /mnt/boot"
|
||||
)
|
||||
'';
|
||||
bootLoader = "systemd-boot";
|
||||
extraInstallerConfig = { modulesPath, ...}: {
|
||||
config = {
|
||||
services.stratis.enable = true;
|
||||
environment.systemPackages = [
|
||||
pkgs.stratis-cli
|
||||
pkgs.thin-provisioning-tools
|
||||
pkgs.lvm2.bin
|
||||
pkgs.stratisd.initrd
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import ./make-test-python.nix ({ lib, pkgs, ... }:
|
||||
let
|
||||
port = 5000;
|
||||
scanPath = "/var/lib/legit";
|
||||
in
|
||||
{
|
||||
name = "legit-web";
|
||||
meta.maintainers = [ lib.maintainers.ratsclub ];
|
||||
|
||||
nodes = {
|
||||
server = { config, pkgs }: {
|
||||
services.legit = {
|
||||
enable = true;
|
||||
settings = {
|
||||
server.port = 5000;
|
||||
repo = { inherit scanPath; };
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkgs.git ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes, ... }:
|
||||
let
|
||||
strPort = builtins.toString port;
|
||||
in
|
||||
''
|
||||
start_all()
|
||||
|
||||
server.wait_for_unit("network.target")
|
||||
server.wait_for_unit("legit.service")
|
||||
|
||||
server.wait_until_succeeds(
|
||||
"curl -f http://localhost:${strPort}"
|
||||
)
|
||||
|
||||
server.succeed("${pkgs.writeShellScript "setup-legit-test-repo" ''
|
||||
set -e
|
||||
git init --bare -b master ${scanPath}/some-repo
|
||||
git init -b master reference
|
||||
cd reference
|
||||
git remote add origin ${scanPath}/some-repo
|
||||
date > date.txt
|
||||
git add date.txt
|
||||
git -c user.name=test -c user.email=test@localhost commit -m 'add date'
|
||||
git push -u origin master
|
||||
''}")
|
||||
|
||||
server.wait_until_succeeds(
|
||||
"curl -f http://localhost:${strPort}/some-repo"
|
||||
)
|
||||
'';
|
||||
})
|
||||
@@ -3,6 +3,11 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [ OPNA2608 ];
|
||||
# Natively running Mir has problems with capturing the first registered libinput device.
|
||||
# In our VM runners on ARM and on some hardware configs (my RPi4, distro-independent), this misses the keyboard.
|
||||
# It can be worked around by dis- and reconnecting the affected hardware, but we can't do this in these tests.
|
||||
# https://github.com/MirServer/mir/issues/2837
|
||||
broken = pkgs.stdenv.hostPlatform.isAarch;
|
||||
};
|
||||
|
||||
nodes.machine = { config, ... }: {
|
||||
|
||||
@@ -27,8 +27,6 @@ import ./make-test-python.nix (
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
import os
|
||||
|
||||
start_all()
|
||||
|
||||
# Create a fake cache with Nginx service the static files
|
||||
|
||||
@@ -93,19 +93,18 @@ let
|
||||
name = "Static";
|
||||
nodes.router = router;
|
||||
nodes.client = { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.interfaces.enp2s0.vlan = 2;
|
||||
virtualisation.vlans = [ 1 2 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
defaultGateway = "192.168.1.1";
|
||||
defaultGateway6 = "fd00:1234:5678:1::1";
|
||||
interfaces.enp1s0.ipv4.addresses = [
|
||||
interfaces.eth1.ipv4.addresses = mkOverride 0 [
|
||||
{ address = "192.168.1.2"; prefixLength = 24; }
|
||||
{ address = "192.168.1.3"; prefixLength = 32; }
|
||||
{ address = "192.168.1.10"; prefixLength = 32; }
|
||||
];
|
||||
interfaces.enp2s0.ipv4.addresses = [
|
||||
interfaces.eth2.ipv4.addresses = mkOverride 0 [
|
||||
{ address = "192.168.2.2"; prefixLength = 24; }
|
||||
];
|
||||
};
|
||||
@@ -171,12 +170,12 @@ let
|
||||
# Disable test driver default config
|
||||
networking.interfaces = lib.mkForce {};
|
||||
networking.useNetworkd = networkd;
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
};
|
||||
testScript = ''
|
||||
start_all()
|
||||
client.wait_for_unit("multi-user.target")
|
||||
client.wait_until_succeeds("ip addr show dev enp1s0 | grep '192.168.1'")
|
||||
client.wait_until_succeeds("ip addr show dev eth1 | grep '192.168.1'")
|
||||
client.shell_interact()
|
||||
client.succeed("ping -c 1 192.168.1.1")
|
||||
router.succeed("ping -c 1 192.168.1.1")
|
||||
@@ -188,13 +187,20 @@ let
|
||||
name = "SimpleDHCP";
|
||||
nodes.router = router;
|
||||
nodes.client = { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.interfaces.enp2s0.vlan = 2;
|
||||
virtualisation.vlans = [ 1 2 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
interfaces.enp1s0.useDHCP = true;
|
||||
interfaces.enp2s0.useDHCP = true;
|
||||
interfaces.eth1 = {
|
||||
ipv4.addresses = mkOverride 0 [ ];
|
||||
ipv6.addresses = mkOverride 0 [ ];
|
||||
useDHCP = true;
|
||||
};
|
||||
interfaces.eth2 = {
|
||||
ipv4.addresses = mkOverride 0 [ ];
|
||||
ipv6.addresses = mkOverride 0 [ ];
|
||||
useDHCP = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
testScript = { ... }:
|
||||
@@ -205,10 +211,10 @@ let
|
||||
router.wait_for_unit("network-online.target")
|
||||
|
||||
with subtest("Wait until we have an ip address on each interface"):
|
||||
client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
|
||||
client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
|
||||
client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q '192.168.2'")
|
||||
client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q 'fd00:1234:5678:2:'")
|
||||
client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
|
||||
client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
|
||||
client.wait_until_succeeds("ip addr show dev eth2 | grep -q '192.168.2'")
|
||||
client.wait_until_succeeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'")
|
||||
|
||||
with subtest("Test vlan 1"):
|
||||
client.wait_until_succeeds("ping -c 1 192.168.1.1")
|
||||
@@ -237,15 +243,16 @@ let
|
||||
name = "OneInterfaceDHCP";
|
||||
nodes.router = router;
|
||||
nodes.client = { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.interfaces.enp2s0.vlan = 2;
|
||||
virtualisation.vlans = [ 1 2 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
interfaces.enp1s0 = {
|
||||
interfaces.eth1 = {
|
||||
ipv4.addresses = mkOverride 0 [ ];
|
||||
mtu = 1343;
|
||||
useDHCP = true;
|
||||
};
|
||||
interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
|
||||
};
|
||||
};
|
||||
testScript = { ... }:
|
||||
@@ -257,10 +264,10 @@ let
|
||||
router.wait_for_unit("network.target")
|
||||
|
||||
with subtest("Wait until we have an ip address on each interface"):
|
||||
client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
|
||||
client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
|
||||
|
||||
with subtest("ensure MTU is set"):
|
||||
assert "mtu 1343" in client.succeed("ip link show dev enp1s0")
|
||||
assert "mtu 1343" in client.succeed("ip link show dev eth1")
|
||||
|
||||
with subtest("Test vlan 1"):
|
||||
client.wait_until_succeeds("ping -c 1 192.168.1.1")
|
||||
@@ -279,15 +286,16 @@ let
|
||||
};
|
||||
bond = let
|
||||
node = address: { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.interfaces.enp2s0.vlan = 2;
|
||||
virtualisation.vlans = [ 1 2 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
bonds.bond0 = {
|
||||
interfaces = [ "enp1s0" "enp2s0" ];
|
||||
interfaces = [ "eth1" "eth2" ];
|
||||
driverOptions.mode = "802.3ad";
|
||||
};
|
||||
interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
|
||||
interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
|
||||
interfaces.bond0.ipv4.addresses = mkOverride 0
|
||||
[ { inherit address; prefixLength = 30; } ];
|
||||
};
|
||||
@@ -318,11 +326,12 @@ let
|
||||
};
|
||||
bridge = let
|
||||
node = { address, vlan }: { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = vlan;
|
||||
virtualisation.vlans = [ vlan ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
interfaces.enp1s0.ipv4.addresses = [ { inherit address; prefixLength = 24; } ];
|
||||
interfaces.eth1.ipv4.addresses = mkOverride 0
|
||||
[ { inherit address; prefixLength = 24; } ];
|
||||
};
|
||||
};
|
||||
in {
|
||||
@@ -330,12 +339,11 @@ let
|
||||
nodes.client1 = node { address = "192.168.1.2"; vlan = 1; };
|
||||
nodes.client2 = node { address = "192.168.1.3"; vlan = 2; };
|
||||
nodes.router = { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.interfaces.enp2s0.vlan = 2;
|
||||
virtualisation.vlans = [ 1 2 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
bridges.bridge.interfaces = [ "enp1s0" "enp2s0" ];
|
||||
bridges.bridge.interfaces = [ "eth1" "eth2" ];
|
||||
interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
|
||||
interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
|
||||
interfaces.bridge.ipv4.addresses = mkOverride 0
|
||||
@@ -369,7 +377,7 @@ let
|
||||
nodes.router = router;
|
||||
nodes.client = { pkgs, ... }: with pkgs.lib; {
|
||||
environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
@@ -377,9 +385,14 @@ let
|
||||
# reverse path filtering rules for the macvlan interface seem
|
||||
# to be incorrect, causing the test to fail. Disable temporarily.
|
||||
firewall.checkReversePath = false;
|
||||
macvlans.macvlan.interface = "enp1s0";
|
||||
interfaces.enp1s0.useDHCP = true;
|
||||
interfaces.macvlan.useDHCP = true;
|
||||
macvlans.macvlan.interface = "eth1";
|
||||
interfaces.eth1 = {
|
||||
ipv4.addresses = mkOverride 0 [ ];
|
||||
useDHCP = true;
|
||||
};
|
||||
interfaces.macvlan = {
|
||||
useDHCP = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
testScript = { ... }:
|
||||
@@ -391,7 +404,7 @@ let
|
||||
router.wait_for_unit("network.target")
|
||||
|
||||
with subtest("Wait until we have an ip address on each interface"):
|
||||
client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
|
||||
client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
|
||||
client.wait_until_succeeds("ip addr show dev macvlan | grep -q '192.168.1'")
|
||||
|
||||
with subtest("Print lots of diagnostic information"):
|
||||
@@ -418,22 +431,23 @@ let
|
||||
fou = {
|
||||
name = "foo-over-udp";
|
||||
nodes.machine = { ... }: {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
interfaces.enp1s0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
|
||||
interfaces.eth1.ipv4.addresses = mkOverride 0
|
||||
[ { address = "192.168.1.1"; prefixLength = 24; } ];
|
||||
fooOverUDP = {
|
||||
fou1 = { port = 9001; };
|
||||
fou2 = { port = 9002; protocol = 41; };
|
||||
fou3 = mkIf (!networkd)
|
||||
{ port = 9003; local.address = "192.168.1.1"; };
|
||||
fou4 = mkIf (!networkd)
|
||||
{ port = 9004; local = { address = "192.168.1.1"; dev = "enp1s0"; }; };
|
||||
{ port = 9004; local = { address = "192.168.1.1"; dev = "eth1"; }; };
|
||||
};
|
||||
};
|
||||
systemd.services = {
|
||||
fou3-fou-encap.after = optional (!networkd) "network-addresses-enp1s0.service";
|
||||
fou3-fou-encap.after = optional (!networkd) "network-addresses-eth1.service";
|
||||
};
|
||||
};
|
||||
testScript = { ... }:
|
||||
@@ -456,22 +470,22 @@ let
|
||||
"gue": None,
|
||||
"family": "inet",
|
||||
"local": "192.168.1.1",
|
||||
"dev": "enp1s0",
|
||||
"dev": "eth1",
|
||||
} in fous, "fou4 exists"
|
||||
'';
|
||||
};
|
||||
sit = let
|
||||
node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
sits.sit = {
|
||||
inherit remote;
|
||||
local = address4;
|
||||
dev = "enp1s0";
|
||||
dev = "eth1";
|
||||
};
|
||||
interfaces.enp1s0.ipv4.addresses = mkOverride 0
|
||||
interfaces.eth1.ipv4.addresses = mkOverride 0
|
||||
[ { address = address4; prefixLength = 24; } ];
|
||||
interfaces.sit.ipv6.addresses = mkOverride 0
|
||||
[ { address = address6; prefixLength = 64; } ];
|
||||
@@ -671,10 +685,10 @@ let
|
||||
vlan-ping = let
|
||||
baseIP = number: "10.10.10.${number}";
|
||||
vlanIP = number: "10.1.1.${number}";
|
||||
baseInterface = "enp1s0";
|
||||
baseInterface = "eth1";
|
||||
vlanInterface = "vlan42";
|
||||
node = number: {pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking = {
|
||||
#useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
@@ -771,12 +785,12 @@ let
|
||||
privacy = {
|
||||
name = "Privacy";
|
||||
nodes.router = { ... }: {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
interfaces.enp1s0.ipv6.addresses = singleton {
|
||||
interfaces.eth1.ipv6.addresses = singleton {
|
||||
address = "fd00:1234:5678:1::1";
|
||||
prefixLength = 64;
|
||||
};
|
||||
@@ -784,7 +798,7 @@ let
|
||||
services.radvd = {
|
||||
enable = true;
|
||||
config = ''
|
||||
interface enp1s0 {
|
||||
interface eth1 {
|
||||
AdvSendAdvert on;
|
||||
AdvManagedFlag on;
|
||||
AdvOtherConfigFlag on;
|
||||
@@ -798,11 +812,11 @@ let
|
||||
};
|
||||
};
|
||||
nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
interfaces.enp1s0 = {
|
||||
interfaces.eth1 = {
|
||||
tempAddress = "default";
|
||||
ipv4.addresses = mkOverride 0 [ ];
|
||||
ipv6.addresses = mkOverride 0 [ ];
|
||||
@@ -811,11 +825,11 @@ let
|
||||
};
|
||||
};
|
||||
nodes.client = { pkgs, ... }: with pkgs.lib; {
|
||||
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
interfaces.enp1s0 = {
|
||||
interfaces.eth1 = {
|
||||
tempAddress = "enabled";
|
||||
ipv4.addresses = mkOverride 0 [ ];
|
||||
ipv6.addresses = mkOverride 0 [ ];
|
||||
@@ -833,9 +847,9 @@ let
|
||||
|
||||
with subtest("Wait until we have an ip address"):
|
||||
client_with_privacy.wait_until_succeeds(
|
||||
"ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'"
|
||||
"ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"
|
||||
)
|
||||
client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
|
||||
client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
|
||||
|
||||
with subtest("Test vlan 1"):
|
||||
client_with_privacy.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
|
||||
@@ -933,7 +947,7 @@ let
|
||||
), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue)
|
||||
'';
|
||||
};
|
||||
rename = if networkd then {
|
||||
rename = {
|
||||
name = "RenameInterface";
|
||||
nodes.machine = { pkgs, ... }: {
|
||||
virtualisation.vlans = [ 1 ];
|
||||
@@ -941,20 +955,23 @@ let
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
};
|
||||
systemd.network.links."10-custom_name" = {
|
||||
matchConfig.MACAddress = "52:54:00:12:01:01";
|
||||
linkConfig.Name = "custom_name";
|
||||
};
|
||||
};
|
||||
} //
|
||||
(if networkd
|
||||
then { systemd.network.links."10-custom_name" = {
|
||||
matchConfig.MACAddress = "52:54:00:12:01:01";
|
||||
linkConfig.Name = "custom_name";
|
||||
};
|
||||
}
|
||||
else { boot.initrd.services.udev.rules = ''
|
||||
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="52:54:00:12:01:01", KERNEL=="eth*", NAME="custom_name"
|
||||
'';
|
||||
});
|
||||
testScript = ''
|
||||
machine.succeed("udevadm settle")
|
||||
print(machine.succeed("ip link show dev custom_name"))
|
||||
'';
|
||||
} else {
|
||||
name = "RenameInterface";
|
||||
nodes = { };
|
||||
testScript = "";
|
||||
};
|
||||
nodes = { };
|
||||
# even with disabled networkd, systemd.network.links should work
|
||||
# (as it's handled by udev, not networkd)
|
||||
link = {
|
||||
@@ -998,21 +1015,6 @@ let
|
||||
machine.fail("ip address show wlan0 | grep -q ${testMac}")
|
||||
'';
|
||||
};
|
||||
caseSensitiveRenaming = {
|
||||
name = "CaseSensitiveRenaming";
|
||||
nodes.machine = { pkgs, ... }: {
|
||||
virtualisation.interfaces.enCustom.vlan = 11;
|
||||
networking = {
|
||||
useNetworkd = networkd;
|
||||
useDHCP = false;
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
machine.succeed("udevadm settle")
|
||||
print(machine.succeed("ip link show dev enCustom"))
|
||||
machine.wait_until_succeeds("ip link show dev enCustom | grep -q '52:54:00:12:0b:01")
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
in mapAttrs (const (attrs: makeTest (attrs // {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDLjCCAhagAwIBAgIIP2+4GFxOYMgwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||
AxMVbWluaWNhIHJvb3QgY2EgNGU3NTJiMB4XDTIzMDEzMDAzNDExOFoXDTQzMDEz
|
||||
MDAzNDExOFowFTETMBEGA1UEAwwKKi50ZXN0Lm5peDCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAMarJSCzelnzTMT5GMoIKA/MXBNk5j277uI2Gq2MCky/
|
||||
DlBpx+tjSsKsz6QLBduKMF8OH5AgjrVAKQAtsVPDseY0Qcyx/5dgJjkdO4on+DFb
|
||||
V0SJ3ZhYPKACrqQ1SaoG+Xup37puw7sVR13J7oNvP6fAYRcjYqCiFC7VMjJNG4dR
|
||||
251jvWWidSc7v5CYw2AxrngtBgHeQuyG9QCJ1DRH8h6ioV7IeonwReN7noYtTWh8
|
||||
NDjGnw9HH2nYMcL91E+DWCxWVmbC9/orvYOT7u0Orho0t1w9BB0/zzcdojwQpMCv
|
||||
HahEmFQmdGbWTuI4caBeaDBJVsSwKlTcxLSS4MAZ0c8CAwEAAaN3MHUwDgYDVR0P
|
||||
AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMB
|
||||
Af8EAjAAMB8GA1UdIwQYMBaAFGyXySYI3gL88d7GHnGMU6wpiBf2MBUGA1UdEQQO
|
||||
MAyCCioudGVzdC5uaXgwDQYJKoZIhvcNAQELBQADggEBAJ/DpwiLVBgWyozsn++f
|
||||
kR4m0dUjnuCgpHo2EMoMZh+9og+OC0vq6WITXHaJytB3aBMxFOUTim3vwxPyWPXX
|
||||
/vy+q6jJ6QMLx1J3VIWZdmXsT+qLGbVzL/4gNoaRsLPGO06p3yVjhas+OBFx1Fee
|
||||
6kTHb82S/dzBojOJLRRo18CU9yw0FUXOPqN7HF7k2y+Twe6+iwCuCKGSFcvmRjxe
|
||||
bWy11C921bTienW0Rmq6ppFWDaUNYP8kKpMN2ViAvc0tyF6wwk5lyOiqCR+pQHJR
|
||||
H/J4qSeKDchYLKECuzd6SySz8FW/xPKogQ28zba+DBD86hpqiEJOBzxbrcN3cjUn
|
||||
7N4=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAxqslILN6WfNMxPkYyggoD8xcE2TmPbvu4jYarYwKTL8OUGnH
|
||||
62NKwqzPpAsF24owXw4fkCCOtUApAC2xU8Ox5jRBzLH/l2AmOR07iif4MVtXRInd
|
||||
mFg8oAKupDVJqgb5e6nfum7DuxVHXcnug28/p8BhFyNioKIULtUyMk0bh1HbnWO9
|
||||
ZaJ1Jzu/kJjDYDGueC0GAd5C7Ib1AInUNEfyHqKhXsh6ifBF43uehi1NaHw0OMaf
|
||||
D0cfadgxwv3UT4NYLFZWZsL3+iu9g5Pu7Q6uGjS3XD0EHT/PNx2iPBCkwK8dqESY
|
||||
VCZ0ZtZO4jhxoF5oMElWxLAqVNzEtJLgwBnRzwIDAQABAoIBAFuNGOH184cqKJGI
|
||||
3RSVJ6kIGtJRKA0A4vfZyPd61nBBhx4lcRyXOCd4LYPCFKP0DZBwWLk5V6pM89gC
|
||||
NnqMbxnPsRbcXBVtGJAvWXW0L5rHJfMOuVBwMRfnxIUljVnONv/264PlcUtwZd/h
|
||||
o4lsJeBvNg7MnrG5nyVp1+T4RZxYm1P86HLp5zyT+fdj4Cr82b9j6QpxGXEfm1jV
|
||||
QA1xr1ZkrV8fgETyaE0TBIKcdt6xNfv1mpI1RE5gaP/YzcCs/mL+G0kMar4l7pO/
|
||||
6OHXTvHz+W3G6Xlha7Wq1ADoqYz2K7VoL/OgSQhIxRNujyWR6lir7eladVrKkCzu
|
||||
uzFi/HECgYEA0vSNCIK3useSypMPHhYUVNbZ4hbK0WgqSAxfJQtL3nC7KviVMAXj
|
||||
IKVR90xuzJB+ih88KCJpH84JH90paMpW0Gq1yEae90bnWa8Nj7ULLS/Zuj0WrelU
|
||||
+DEGbx47IUPOtiLBxooxFKyIVhX3hWRwZ0pokSQzbgb5zYnlM6tqZ3cCgYEA8Rb2
|
||||
wtt0XmqEQedFacs4fobJoVWMcETjpuxYp0m5Kje/4QkptZIbspXGBgNtPBBRGg51
|
||||
AYSu8wYkGEueI77KiFDgY8AAkpOk2MrMVPszjOhUiO1oEfbT6ynOY5RDOuXcY6jo
|
||||
8RpSk46VkfVxt6LVmappqcVFtVWcAjdGfXeSLmkCgYAWP7SgMSkvidzxgJEXmzyJ
|
||||
th9EuSKq81GCR8vBHG/kBf+3iIAzkGtkBgufCXCmIpc1+hVeJkLwF8rekXTMmIqP
|
||||
cLG7bbdWXSQJUW0cuvtyyJkuC0NZFELh6knDbmzOFVi33PKS/gAvLgMzER4J843n
|
||||
VvGwXSEPeazfAKwrxuhyAQKBgQCOm5TPYlyNVNhy20h18d2zCivOoPn3luhKXtd5
|
||||
7OP4kw2PIYpoesqjcnC2MeS1eLlgfli70y5hVqqXLHOYlUzcIWr51iMAkREbo6oG
|
||||
QqkVmoAWlsfOiICGRC5vPM4f0sPwt4NCyt05p0fWFKd1hn5u7Ryfba90OfWUYfny
|
||||
UX5IsQKBgQCswer4Qc3UepkiYxGwSTxgIh4kYlmamU2I00Kar4uFAr9JsCbk98f0
|
||||
kaCUNZjrrvTwgRmdhwcpMDiMW/F4QkNk0I2unHcoAvzNop6c22VhHJU2XJhrQ57h
|
||||
n1iPiw0NLXiA4RQwMUMjtt3nqlpLOTXGtsF8TmpWPcAN2QcTxOutzw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -1,20 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSzCCAjOgAwIBAgIITnUr3xFw4oEwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||
AxMVbWluaWNhIHJvb3QgY2EgNGU3NTJiMCAXDTIzMDEzMDAzNDExOFoYDzIxMjMw
|
||||
MTMwMDM0MTE4WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA0ZTc1MmIwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1SrJT9k3zXIXApEyL5UDlw7F6
|
||||
MMOqE5d+8ZwMccHbEKLu0ssNRY+j31tnNYQ/r5iCNeNgUZccKBgzdU0ysyw5n4tw
|
||||
0y+MTD9fCfUXYcc8pJRPRolo6zxYO9W7WJr0nfJZ+p7zFRAjRCmzXdnZjKz0EGcg
|
||||
x9mHwn//3SuLt1ItK1n3aZ6im9NlcVtunDe3lCSL0tRgy7wDGNvWDZMO49jk4AFU
|
||||
BlMqScuiNpUzYgCxNaaGMuH3M0f0YyRAxSs6FWewLtqTIaVql7HL+3PcGAhvlKEZ
|
||||
fvfaf80F9aWI88sbEddTA0s5837zEoDwGpZl3K5sPU/O3MVEHIhAY5ICG0IBAgMB
|
||||
AAGjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
|
||||
BgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRsl8kmCN4C/PHe
|
||||
xh5xjFOsKYgX9jAfBgNVHSMEGDAWgBRsl8kmCN4C/PHexh5xjFOsKYgX9jANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAmvgpU+q+TBbz+9Y2rdiIeTfeDXtMNPf+nKI3zxYztRGC
|
||||
MoKP6jCQaFSQra4BVumFLV38DoqR1pOV1ojkiyO5c/9Iym/1Wmm8LeqgsHNqSgyS
|
||||
C7wvBcb/N9PzIBQFq/RiboDoC7bqK/0zQguCmBtGceH+AVpQyfXM+P78B1EkHozu
|
||||
67igP8GfouPp2s4Vd5P2XGkA6vMgYCtFEnCbtmmo7C8B+ymhD/D9axpMKQ1OaBg9
|
||||
jfqLOlk+Rc2nYZuaDjnUmlTkYjC6EwCNe9weYkSJgQ9QzoGJLIRARsdQdsp3C2fZ
|
||||
l2UZKkDJ2GPrrc+TdaGXZTYi0uMmvQsEKZXtqAzorQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAtUqyU/ZN81yFwKRMi+VA5cOxejDDqhOXfvGcDHHB2xCi7tLL
|
||||
DUWPo99bZzWEP6+YgjXjYFGXHCgYM3VNMrMsOZ+LcNMvjEw/Xwn1F2HHPKSUT0aJ
|
||||
aOs8WDvVu1ia9J3yWfqe8xUQI0Qps13Z2Yys9BBnIMfZh8J//90ri7dSLStZ92me
|
||||
opvTZXFbbpw3t5Qki9LUYMu8Axjb1g2TDuPY5OABVAZTKknLojaVM2IAsTWmhjLh
|
||||
9zNH9GMkQMUrOhVnsC7akyGlapexy/tz3BgIb5ShGX732n/NBfWliPPLGxHXUwNL
|
||||
OfN+8xKA8BqWZdyubD1PztzFRByIQGOSAhtCAQIDAQABAoIBAQCLeAWs1kWtvTYg
|
||||
t8UzspC0slItAKrmgt//hvxYDoPmdewC8yPG+AbDOSfmRKOTIxGeyro79UjdHnNP
|
||||
0yQqpvCU/AqYJ7/inR37jXuCG3TdUHfQbSF1F9N6xb1tvYKoQYKaelYiB8g8eUnj
|
||||
dYYM+U5tDNlpvJW6/YTfYFUJzWRo3i8jj5lhbkjcJDvdOhVxMXNXJgJAymu1KysE
|
||||
N1da2l4fzmuoN82wFE9KMyYSn+LOLWBReQQmXHZPP+2LjRIVrWoFoV49k2Ylp9tH
|
||||
yeaFx1Ya/wVx3PRnSW+zebWDcc0bAua9XU3Fi42yRq5iXOyoXHyefDfJoId7+GAO
|
||||
IF2qRw9hAoGBAM1O1l4ceOEDsEBh7HWTvmfwVfkXgT6VHeI6LGEjb88FApXgT+wT
|
||||
1s1IWVVOigLl9OKQbrjqlg9xgzrPDHYRwu5/Oz3X2WaH6wlF+d+okoqls6sCEAeo
|
||||
GfzF3sKOHQyIYjttCXE5G38uhIgVFFFfK97AbUiY8egYBr0zjVXK7xINAoGBAOIN
|
||||
1pDBFBQIoKj64opm/G9lJBLUpWLBFdWXhXS6q2jNsdY1mLMRmu/RBaKSfGz7W1a/
|
||||
a2WBedjcnTWJ/84tBsn4Qj5tLl8xkcXiN/pslWzg724ZnVsbyxM9KvAdXAma3F0g
|
||||
2EsYq8mhvbAEkpE+aoM6jwOJBnMhTRZrNMKN2lbFAoGAHmZWB4lfvLG3H1FgmehO
|
||||
gUVs9X0tff7GdgD3IUsF+zlasKaOLv6hB7R2xdLjTJqQMBwCyQ6zOYYtUD/oMHNg
|
||||
0b+1HesgHbZybuUVorBrQmxWtjOP/BJABtWlrlkso/Zt1S7H/yPdlm9k4GF+qK3W
|
||||
6RzFEcLTzvH/zXQcsV9jFuECgYEAhaX+1KiC0XFkY2OpaoCHAOlAUa3NdjyIRzcF
|
||||
XUU8MINkgCxB8qUXAHCJL1wCGoDluL0FpwbM3m1YuR200tYGLIUNzVDJ2Ng6wk8E
|
||||
H5fxJGU8ydB1Gzescdx5NWt2Tet0G89ecc/NSTHKL3YUnbDUUm/dvA5YdNscc4PA
|
||||
tsIdc60CgYEArvU1MwqGQUTDKUmaM2t3qm70fbwmOViHfyTWpn4aAQR3sK16iJMm
|
||||
V+dka62L/VYs5CIbzXvCioyugUMZGJi/zIwrViRzqJQbNnPADAW4lG88UxXqHHAH
|
||||
q33ivjgd9omGFb37saKOmR44KmjUIDvSIZF4W3EPwAMEyl5mM31Ryns=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -1,144 +0,0 @@
|
||||
let
|
||||
certs = import ./snakeoil-certs.nix;
|
||||
in
|
||||
import ../make-test-python.nix ({ pkgs, ... }: {
|
||||
name = "nginx-proxyprotocol";
|
||||
|
||||
nodes = {
|
||||
webserver = { pkgs, lib, ... }: {
|
||||
environment.systemPackages = [ pkgs.netcat ];
|
||||
security.pki.certificateFiles = [
|
||||
certs.ca.cert
|
||||
];
|
||||
|
||||
networking.extraHosts = ''
|
||||
127.0.0.5 proxy.test.nix
|
||||
127.0.0.5 noproxy.test.nix
|
||||
127.0.0.3 direct-nossl.test.nix
|
||||
127.0.0.4 unsecure-nossl.test.nix
|
||||
127.0.0.2 direct-noproxy.test.nix
|
||||
127.0.0.1 direct-proxy.test.nix
|
||||
'';
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
defaultListen = [
|
||||
{ addr = "127.0.0.1"; proxyProtocol = true; ssl = true; }
|
||||
{ addr = "127.0.0.2"; }
|
||||
{ addr = "127.0.0.3"; ssl = false; }
|
||||
{ addr = "127.0.0.4"; ssl = false; proxyProtocol = true; }
|
||||
];
|
||||
commonHttpConfig = ''
|
||||
log_format pcombined '(proxy_protocol=$proxy_protocol_addr) - (remote_addr=$remote_addr) - (realip=$realip_remote_addr) - (upstream=) - (remote_user=$remote_user) [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent"';
|
||||
access_log /var/log/nginx/access.log pcombined;
|
||||
error_log /var/log/nginx/error.log;
|
||||
'';
|
||||
virtualHosts =
|
||||
let
|
||||
commonConfig = {
|
||||
locations."/".return = "200 '$remote_addr'";
|
||||
extraConfig = ''
|
||||
set_real_ip_from 127.0.0.5/32;
|
||||
real_ip_header proxy_protocol;
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
"*.test.nix" = commonConfig // {
|
||||
sslCertificate = certs."*.test.nix".cert;
|
||||
sslCertificateKey = certs."*.test.nix".key;
|
||||
forceSSL = true;
|
||||
};
|
||||
"direct-nossl.test.nix" = commonConfig;
|
||||
"unsecure-nossl.test.nix" = commonConfig // {
|
||||
extraConfig = ''
|
||||
real_ip_header proxy_protocol;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.sniproxy = {
|
||||
enable = true;
|
||||
config = ''
|
||||
error_log {
|
||||
syslog daemon
|
||||
}
|
||||
access_log {
|
||||
syslog daemon
|
||||
}
|
||||
listener 127.0.0.5:443 {
|
||||
protocol tls
|
||||
source 127.0.0.5
|
||||
}
|
||||
table {
|
||||
^proxy\.test\.nix$ 127.0.0.1 proxy_protocol
|
||||
^noproxy\.test\.nix$ 127.0.0.2
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
def check_origin_ip(src_ip: str, dst_url: str, failure: bool = False, proxy_protocol: bool = False, expected_ip: str | None = None):
|
||||
check = webserver.fail if failure else webserver.succeed
|
||||
if expected_ip is None:
|
||||
expected_ip = src_ip
|
||||
|
||||
return check(f"curl {'--haproxy-protocol' if proxy_protocol else '''} --interface {src_ip} --fail -L {dst_url} | grep '{expected_ip}'")
|
||||
|
||||
webserver.wait_for_unit("nginx")
|
||||
webserver.wait_for_unit("sniproxy")
|
||||
# This should be closed by virtue of ssl = true;
|
||||
webserver.wait_for_closed_port(80, "127.0.0.1")
|
||||
# This should be open by virtue of no explicit ssl
|
||||
webserver.wait_for_open_port(80, "127.0.0.2")
|
||||
# This should be open by virtue of ssl = true;
|
||||
webserver.wait_for_open_port(443, "127.0.0.1")
|
||||
# This should be open by virtue of no explicit ssl
|
||||
webserver.wait_for_open_port(443, "127.0.0.2")
|
||||
# This should be open by sniproxy
|
||||
webserver.wait_for_open_port(443, "127.0.0.5")
|
||||
# This should be closed by sniproxy
|
||||
webserver.wait_for_closed_port(80, "127.0.0.5")
|
||||
|
||||
# Sanity checks for the NGINX module
|
||||
# direct-HTTP connection to NGINX without TLS, this checks that ssl = false; works well.
|
||||
check_origin_ip("127.0.0.10", "http://direct-nossl.test.nix/")
|
||||
# webserver.execute("openssl s_client -showcerts -connect direct-noproxy.test.nix:443")
|
||||
# direct-HTTP connection to NGINX with TLS
|
||||
check_origin_ip("127.0.0.10", "http://direct-noproxy.test.nix/")
|
||||
check_origin_ip("127.0.0.10", "https://direct-noproxy.test.nix/")
|
||||
# Well, sniproxy is not listening on 80 and cannot redirect
|
||||
check_origin_ip("127.0.0.10", "http://proxy.test.nix/", failure=True)
|
||||
check_origin_ip("127.0.0.10", "http://noproxy.test.nix/", failure=True)
|
||||
|
||||
# Actual PROXY protocol related tests
|
||||
# Connecting through sniproxy should passthrough the originating IP address.
|
||||
check_origin_ip("127.0.0.10", "https://proxy.test.nix/")
|
||||
# Connecting through sniproxy to a non-PROXY protocol enabled listener should not pass the originating IP address.
|
||||
check_origin_ip("127.0.0.10", "https://noproxy.test.nix/", expected_ip="127.0.0.5")
|
||||
|
||||
# Attack tests against spoofing
|
||||
# Let's try to spoof our IP address by connecting direct-y to the PROXY protocol listener.
|
||||
# FIXME(RaitoBezarius): rewrite it using Python + (Scapy|something else) as this is too much broken unfortunately.
|
||||
# Or wait for upstream curl patch.
|
||||
# def generate_attacker_request(original_ip: str, target_ip: str, dst_url: str):
|
||||
# return f"""PROXY TCP4 {original_ip} {target_ip} 80 80
|
||||
# GET / HTTP/1.1
|
||||
# Host: {dst_url}
|
||||
|
||||
# """
|
||||
# def spoof(original_ip: str, target_ip: str, dst_url: str, tls: bool = False, expect_failure: bool = True):
|
||||
# method = webserver.fail if expect_failure else webserver.succeed
|
||||
# port = 443 if tls else 80
|
||||
# print(webserver.execute(f"cat <<EOF | nc {target_ip} {port}\n{generate_attacker_request(original_ip, target_ip, dst_url)}\nEOF"))
|
||||
# return method(f"cat <<EOF | nc {target_ip} {port} | grep {original_ip}\n{generate_attacker_request(original_ip, target_ip, dst_url)}\nEOF")
|
||||
|
||||
# check_origin_ip("127.0.0.10", "http://unsecure-nossl.test.nix", proxy_protocol=True)
|
||||
# spoof("1.1.1.1", "127.0.0.4", "direct-nossl.test.nix")
|
||||
# spoof("1.1.1.1", "127.0.0.4", "unsecure-nossl.test.nix", expect_failure=False)
|
||||
'';
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
# Minica can provide a CA key and cert, plus a key
|
||||
# and cert for our fake CA server's Web Front End (WFE).
|
||||
{
|
||||
pkgs ? import <nixpkgs> {},
|
||||
minica ? pkgs.minica,
|
||||
runCommandCC ? pkgs.runCommandCC,
|
||||
}:
|
||||
let
|
||||
conf = import ./snakeoil-certs.nix;
|
||||
domain = conf.domain;
|
||||
domainSanitized = pkgs.lib.replaceStrings ["*"] ["_"] domain;
|
||||
in
|
||||
runCommandCC "generate-tests-certs" {
|
||||
buildInputs = [ (minica.overrideAttrs (old: {
|
||||
postPatch = ''
|
||||
sed -i 's_NotAfter: time.Now().AddDate(2, 0, 30),_NotAfter: time.Now().AddDate(20, 0, 0),_' main.go
|
||||
'';
|
||||
})) ];
|
||||
|
||||
} ''
|
||||
minica \
|
||||
--ca-key ca.key.pem \
|
||||
--ca-cert ca.cert.pem \
|
||||
--domains "${domain}"
|
||||
|
||||
mkdir -p $out
|
||||
mv ca.*.pem $out/
|
||||
mv ${domainSanitized}/key.pem $out/${domainSanitized}.key.pem
|
||||
mv ${domainSanitized}/cert.pem $out/${domainSanitized}.cert.pem
|
||||
''
|
||||
@@ -1,14 +0,0 @@
|
||||
let
|
||||
domain = "*.test.nix";
|
||||
domainSanitized = "_.test.nix";
|
||||
in {
|
||||
inherit domain;
|
||||
ca = {
|
||||
cert = ./ca.cert.pem;
|
||||
key = ./ca.key.pem;
|
||||
};
|
||||
"${domain}" = {
|
||||
cert = ./. + "/${domainSanitized}.cert.pem";
|
||||
key = ./. + "/${domainSanitized}.key.pem";
|
||||
};
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import ./make-test-python.nix ({ pkgs, lib, ... }:
|
||||
let
|
||||
accessKey = "BKIKJAA5BMMU2RHO6IBB";
|
||||
secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12";
|
||||
secretKeyFile = pkgs.writeText "outline-secret-key" ''
|
||||
${secretKey}
|
||||
'';
|
||||
rootCredentialsFile = pkgs.writeText "minio-credentials-full" ''
|
||||
MINIO_ROOT_USER=${accessKey}
|
||||
MINIO_ROOT_PASSWORD=${secretKey}
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "outline";
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ xanderio ];
|
||||
|
||||
nodes = {
|
||||
outline = { pkgs, config, ... }: {
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
environment.systemPackages = [ pkgs.minio-client ];
|
||||
services.outline = {
|
||||
enable = true;
|
||||
forceHttps = false;
|
||||
storage = {
|
||||
inherit accessKey secretKeyFile;
|
||||
uploadBucketUrl = "http://localhost:9000";
|
||||
uploadBucketName = "outline";
|
||||
region = config.services.minio.region;
|
||||
};
|
||||
};
|
||||
services.minio = {
|
||||
enable = true;
|
||||
inherit rootCredentialsFile;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
''
|
||||
machine.wait_for_unit("minio.service")
|
||||
machine.wait_for_open_port(9000)
|
||||
|
||||
# Create a test bucket on the server
|
||||
machine.succeed(
|
||||
"mc config host add minio http://localhost:9000 ${accessKey} ${secretKey} --api s3v4"
|
||||
)
|
||||
machine.succeed("mc mb minio/outline")
|
||||
|
||||
outline.wait_for_unit("outline.service")
|
||||
outline.wait_for_open_port(3000)
|
||||
outline.succeed("curl --fail http://localhost:3000/")
|
||||
'';
|
||||
})
|
||||
@@ -1,384 +0,0 @@
|
||||
# SFTPGo NixOS test
|
||||
#
|
||||
# This NixOS test sets up a basic test scenario for the SFTPGo module
|
||||
# and covers the following scenarios:
|
||||
# - uploading a file via sftp
|
||||
# - downloading the file over sftp
|
||||
# - assert that the ACLs are respected
|
||||
# - share a file between alice and bob (using sftp)
|
||||
# - assert that eve cannot acceess the shared folder between alice and bob.
|
||||
#
|
||||
# Additional test coverage for the remaining protocols (i.e. ftp, http and webdav)
|
||||
# would be a nice to have for the future.
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
|
||||
|
||||
# Returns an attributeset of users who are not system users.
|
||||
normalUsers = config:
|
||||
filterAttrs (name: user: user.isNormalUser) config.users.users;
|
||||
|
||||
# Returns true if a user is a member of the given group
|
||||
isMemberOf =
|
||||
config:
|
||||
# str
|
||||
groupName:
|
||||
# users.users attrset
|
||||
user:
|
||||
any (x: x == user.name) config.users.groups.${groupName}.members;
|
||||
|
||||
# Generates a valid SFTPGo user configuration for a given user
|
||||
# Will be converted to JSON and loaded on application startup.
|
||||
generateUserAttrSet =
|
||||
config:
|
||||
# attrset returned by config.users.users.<username>
|
||||
user: {
|
||||
# 0: user is disabled, login is not allowed
|
||||
# 1: user is enabled
|
||||
status = 1;
|
||||
|
||||
username = user.name;
|
||||
password = ""; # disables password authentication
|
||||
public_keys = user.openssh.authorizedKeys.keys;
|
||||
email = "${user.name}@example.com";
|
||||
|
||||
# User home directory on the local filesystem
|
||||
home_dir = "${config.services.sftpgo.dataDir}/users/${user.name}";
|
||||
|
||||
# Defines a mapping between virtual SFTPGo paths and filesystem paths outside the user home directory.
|
||||
#
|
||||
# Supported for local filesystem only. If one or more of the specified folders are not
|
||||
# inside the dataprovider they will be automatically created.
|
||||
# You have to create the folder on the filesystem yourself
|
||||
virtual_folders =
|
||||
optional (isMemberOf config sharedFolderName user) {
|
||||
name = sharedFolderName;
|
||||
mapped_path = "${config.services.sftpgo.dataDir}/${sharedFolderName}";
|
||||
virtual_path = "/${sharedFolderName}";
|
||||
};
|
||||
|
||||
# Defines the ACL on the virtual filesystem
|
||||
permissions =
|
||||
recursiveUpdate {
|
||||
"/" = [ "list" ]; # read-only top level directory
|
||||
"/private" = [ "*" ]; # private subdirectory, not shared with others
|
||||
} (optionalAttrs (isMemberOf config "shared" user) {
|
||||
"/shared" = [ "*" ];
|
||||
});
|
||||
|
||||
filters = {
|
||||
allowed_ip = [];
|
||||
denied_ip = [];
|
||||
web_client = [
|
||||
"password-change-disabled"
|
||||
"password-reset-disabled"
|
||||
"api-key-auth-change-disabled"
|
||||
];
|
||||
};
|
||||
|
||||
upload_bandwidth = 0; # unlimited
|
||||
download_bandwidth = 0; # unlimited
|
||||
expiration_date = 0; # means no expiration
|
||||
max_sessions = 0;
|
||||
quota_size = 0;
|
||||
quota_files = 0;
|
||||
};
|
||||
|
||||
# Generates a json file containing a static configuration
|
||||
# of users and folders to import to SFTPGo.
|
||||
loadDataJson = config: pkgs.writeText "users-and-folders.json" (builtins.toJSON {
|
||||
users =
|
||||
mapAttrsToList (name: user: generateUserAttrSet config user) (normalUsers config);
|
||||
|
||||
folders = [
|
||||
{
|
||||
name = sharedFolderName;
|
||||
description = "shared folder";
|
||||
|
||||
# 0: local filesystem
|
||||
# 1: AWS S3 compatible
|
||||
# 2: Google Cloud Storage
|
||||
filesystem.provider = 0;
|
||||
|
||||
# Mapped path on the local filesystem
|
||||
mapped_path = "${config.services.sftpgo.dataDir}/${sharedFolderName}";
|
||||
|
||||
# All users in the matching group gain access
|
||||
users = config.users.groups.${sharedFolderName}.members;
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
# Generated Host Key for connecting to SFTPGo's sftp subsystem.
|
||||
snakeOilHostKey = pkgs.writeText "sftpgo_ed25519_host_key" ''
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBOtQu6U135yxtrvUqPoozUymkjoNNPVK6rqjS936RLtQAAAJAXOMoSFzjK
|
||||
EgAAAAtzc2gtZWQyNTUxOQAAACBOtQu6U135yxtrvUqPoozUymkjoNNPVK6rqjS936RLtQ
|
||||
AAAEAoRLEV1VD80mg314ObySpfrCcUqtWoOSS3EtMPPhx08U61C7pTXfnLG2u9So+ijNTK
|
||||
aSOg009UrquqNL3fpEu1AAAADHNmdHBnb0BuaXhvcwE=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
'';
|
||||
|
||||
adminUsername = "admin";
|
||||
adminPassword = "secretadminpassword";
|
||||
aliceUsername = "alice";
|
||||
alicePassword = "secretalicepassword";
|
||||
bobUsername = "bob";
|
||||
bobPassword = "secretbobpassword";
|
||||
eveUsername = "eve";
|
||||
evePassword = "secretevepassword";
|
||||
sharedFolderName = "shared";
|
||||
|
||||
# A file for testing uploading via SFTP
|
||||
testFile = pkgs.writeText "test.txt" "hello world";
|
||||
sharedFile = pkgs.writeText "shared.txt" "shared content";
|
||||
|
||||
# Define the for exposing SFTP
|
||||
sftpPort = 2022;
|
||||
|
||||
# Define the for exposing HTTP
|
||||
httpPort = 8080;
|
||||
in
|
||||
{
|
||||
name = "sftpgo";
|
||||
|
||||
meta.maintainers = with maintainers; [ yayayayaka ];
|
||||
|
||||
nodes = {
|
||||
server = { nodes, ... }: {
|
||||
networking.firewall.allowedTCPPorts = [ sftpPort httpPort ];
|
||||
|
||||
# nodes.server.configure postgresql database
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [ "sftpgo" ];
|
||||
ensureUsers = [{
|
||||
name = "sftpgo";
|
||||
ensurePermissions."DATABASE sftpgo" = "ALL PRIVILEGES";
|
||||
}];
|
||||
};
|
||||
|
||||
services.sftpgo = {
|
||||
enable = true;
|
||||
|
||||
loadDataFile = (loadDataJson nodes.server);
|
||||
|
||||
settings = {
|
||||
data_provider = {
|
||||
driver = "postgresql";
|
||||
name = "sftpgo";
|
||||
username = "sftpgo";
|
||||
host = "/run/postgresql";
|
||||
port = 5432;
|
||||
|
||||
# Enables the possibility to create an initial admin user on first startup.
|
||||
create_default_admin = true;
|
||||
};
|
||||
|
||||
httpd.bindings = [
|
||||
{
|
||||
address = ""; # listen on all interfaces
|
||||
port = httpPort;
|
||||
enable_https = false;
|
||||
|
||||
enable_web_client = true;
|
||||
enable_web_admin = true;
|
||||
}
|
||||
];
|
||||
|
||||
# Enable sftpd
|
||||
sftpd = {
|
||||
bindings = [{
|
||||
address = ""; # listen on all interfaces
|
||||
port = sftpPort;
|
||||
}];
|
||||
host_keys = [ snakeOilHostKey ];
|
||||
password_authentication = false;
|
||||
keyboard_interactive_authentication = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.sftpgo = {
|
||||
after = [ "postgresql.service"];
|
||||
environment = {
|
||||
# Update existing users
|
||||
SFTPGO_LOADDATA_MODE = "0";
|
||||
SFTPGO_DEFAULT_ADMIN_USERNAME = adminUsername;
|
||||
|
||||
# This will end up in cleartext in the systemd service.
|
||||
# Don't use this approach in production!
|
||||
SFTPGO_DEFAULT_ADMIN_PASSWORD = adminPassword;
|
||||
};
|
||||
};
|
||||
|
||||
# Sets up the folder hierarchy on the local filesystem
|
||||
systemd.tmpfiles.rules =
|
||||
let
|
||||
sftpgoUser = nodes.server.services.sftpgo.user;
|
||||
sftpgoGroup = nodes.server.services.sftpgo.group;
|
||||
statePath = nodes.server.services.sftpgo.dataDir;
|
||||
in [
|
||||
# Create state directory
|
||||
"d ${statePath} 0750 ${sftpgoUser} ${sftpgoGroup} -"
|
||||
"d ${statePath}/users 0750 ${sftpgoUser} ${sftpgoGroup} -"
|
||||
|
||||
# Created shared folder directories
|
||||
"d ${statePath}/${sharedFolderName} 2770 ${sftpgoUser} ${sharedFolderName} -"
|
||||
]
|
||||
++ mapAttrsToList (name: user:
|
||||
# Create private user directories
|
||||
''
|
||||
d ${statePath}/users/${user.name} 0700 ${sftpgoUser} ${sftpgoGroup} -
|
||||
d ${statePath}/users/${user.name}/private 0700 ${sftpgoUser} ${sftpgoGroup} -
|
||||
''
|
||||
) (normalUsers nodes.server);
|
||||
|
||||
users.users =
|
||||
let
|
||||
commonAttrs = {
|
||||
isNormalUser = true;
|
||||
openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
|
||||
};
|
||||
in {
|
||||
# SFTPGo admin user
|
||||
admin = commonAttrs // {
|
||||
password = adminPassword;
|
||||
};
|
||||
|
||||
# Alice and bob share folders with each other
|
||||
alice = commonAttrs // {
|
||||
password = alicePassword;
|
||||
extraGroups = [ sharedFolderName ];
|
||||
};
|
||||
|
||||
bob = commonAttrs // {
|
||||
password = bobPassword;
|
||||
extraGroups = [ sharedFolderName ];
|
||||
};
|
||||
|
||||
# Eve has no shared folders
|
||||
eve = commonAttrs // {
|
||||
password = evePassword;
|
||||
};
|
||||
};
|
||||
|
||||
users.groups.${sharedFolderName} = {};
|
||||
|
||||
specialisation = {
|
||||
# A specialisation for asserting that SFTPGo can bind to privileged ports.
|
||||
privilegedPorts.configuration = { ... }: {
|
||||
networking.firewall.allowedTCPPorts = [ 22 80 ];
|
||||
services.sftpgo = {
|
||||
settings = {
|
||||
sftpd.bindings = mkForce [{
|
||||
address = "";
|
||||
port = 22;
|
||||
}];
|
||||
|
||||
httpd.bindings = mkForce [{
|
||||
address = "";
|
||||
port = 80;
|
||||
}];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
client = { nodes, ... }: {
|
||||
# Add the SFTPGo host key to the global known_hosts file
|
||||
programs.ssh.knownHosts =
|
||||
let
|
||||
commonAttrs = {
|
||||
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE61C7pTXfnLG2u9So+ijNTKaSOg009UrquqNL3fpEu1";
|
||||
};
|
||||
in {
|
||||
"server" = commonAttrs;
|
||||
"[server]:2022" = commonAttrs;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes, ... }: let
|
||||
# A function to generate test cases for wheter
|
||||
# a specified username is expected to access the shared folder.
|
||||
accessSharedFoldersSubtest =
|
||||
{ # The username to run as
|
||||
username
|
||||
# Whether the tests are expected to succeed or not
|
||||
, shouldSucceed ? true
|
||||
}: ''
|
||||
with subtest("Test whether ${username} can access shared folders"):
|
||||
client.${if shouldSucceed then "succeed" else "fail"}("sftp -P ${toString sftpPort} -b ${
|
||||
pkgs.writeText "${username}-ls-${sharedFolderName}" ''
|
||||
ls ${sharedFolderName}
|
||||
''
|
||||
} ${username}@server")
|
||||
'';
|
||||
statePath = nodes.server.services.sftpgo.dataDir;
|
||||
in ''
|
||||
start_all()
|
||||
|
||||
client.wait_for_unit("default.target")
|
||||
server.wait_for_unit("sftpgo.service")
|
||||
|
||||
with subtest("web client"):
|
||||
client.wait_until_succeeds("curl -sSf http://server:${toString httpPort}/web/client/login")
|
||||
|
||||
# Ensure sftpgo found the static folder
|
||||
client.wait_until_succeeds("curl -o /dev/null -sSf http://server:${toString httpPort}/static/favicon.ico")
|
||||
|
||||
with subtest("Setup SSH keys"):
|
||||
client.succeed("mkdir -m 700 /root/.ssh")
|
||||
client.succeed("cat ${snakeOilPrivateKey} > /root/.ssh/id_ecdsa")
|
||||
client.succeed("chmod 600 /root/.ssh/id_ecdsa")
|
||||
|
||||
with subtest("Copy a file over sftp"):
|
||||
client.wait_until_succeeds("scp -P ${toString sftpPort} ${toString testFile} alice@server:/private/${testFile.name}")
|
||||
server.succeed("test -s ${statePath}/users/alice/private/${testFile.name}")
|
||||
|
||||
# The configured ACL should prevent uploading files to the root directory
|
||||
client.fail("scp -P ${toString sftpPort} ${toString testFile} alice@server:/")
|
||||
|
||||
with subtest("Attempting an interactive SSH sessions must fail"):
|
||||
client.fail("ssh -p ${toString sftpPort} alice@server")
|
||||
|
||||
${accessSharedFoldersSubtest {
|
||||
username = "alice";
|
||||
shouldSucceed = true;
|
||||
}}
|
||||
|
||||
${accessSharedFoldersSubtest {
|
||||
username = "bob";
|
||||
shouldSucceed = true;
|
||||
}}
|
||||
|
||||
${accessSharedFoldersSubtest {
|
||||
username = "eve";
|
||||
shouldSucceed = false;
|
||||
}}
|
||||
|
||||
with subtest("Test sharing files"):
|
||||
# Alice uploads a file to shared folder
|
||||
client.succeed("scp -P ${toString sftpPort} ${toString sharedFile} alice@server:/${sharedFolderName}/${sharedFile.name}")
|
||||
server.succeed("test -s ${statePath}/${sharedFolderName}/${sharedFile.name}")
|
||||
|
||||
# Bob downloads the file from shared folder
|
||||
client.succeed("scp -P ${toString sftpPort} bob@server:/shared/${sharedFile.name} ${sharedFile.name}")
|
||||
client.succeed("test -s ${sharedFile.name}")
|
||||
|
||||
# Eve should not get the file from shared folder
|
||||
client.fail("scp -P ${toString sftpPort} eve@server:/shared/${sharedFile.name}")
|
||||
|
||||
server.succeed("/run/current-system/specialisation/privilegedPorts/bin/switch-to-configuration test")
|
||||
|
||||
client.wait_until_succeeds("sftp -P 22 -b ${pkgs.writeText "get-hello-world.txt" ''
|
||||
get /private/${testFile.name}
|
||||
''} alice@server")
|
||||
'';
|
||||
}
|
||||
@@ -21,20 +21,20 @@
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "amberol";
|
||||
version = "0.10.3";
|
||||
version = "0.10.2";
|
||||
|
||||
src = fetchFromGitLab {
|
||||
domain = "gitlab.gnome.org";
|
||||
owner = "World";
|
||||
repo = pname;
|
||||
rev = version;
|
||||
hash = "sha256-nAoUO0bGToNGD2W8qJmTegrETOJDdM04hI1jjiYkZXI=";
|
||||
hash = "sha256-edYLdsXlk+3YGyG6bxR8E8q1bzaXWk04v/NxfaxcNhI=";
|
||||
};
|
||||
|
||||
cargoDeps = rustPlatform.fetchCargoTarball {
|
||||
inherit src;
|
||||
name = "${pname}-${version}";
|
||||
hash = "sha256-4ZoliqQ665KPDFl+1eBCE+1fZgr+FA7vesPstoRs0RU=";
|
||||
hash = "sha256-bhB1hFFLYf+TH3pDCyx/hJqPxBdoPjtPBluK9yygpTk=";
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
|
||||
@@ -30,12 +30,10 @@
|
||||
, pcre
|
||||
, mount
|
||||
, gnome
|
||||
, Accelerate
|
||||
, Cocoa
|
||||
, WebKit
|
||||
, CoreServices
|
||||
, CoreAudioKit
|
||||
, IOBluetooth
|
||||
# It is not allowed to distribute binaries with the VST2 SDK plugin without a license
|
||||
# (the author of Bespoke has such a licence but not Nix). VST3 should work out of the box.
|
||||
# Read more in https://github.com/NixOS/nixpkgs/issues/145607
|
||||
@@ -104,12 +102,10 @@ stdenv.mkDerivation rec {
|
||||
pcre
|
||||
mount
|
||||
] ++ lib.optionals stdenv.hostPlatform.isDarwin [
|
||||
Accelerate
|
||||
Cocoa
|
||||
WebKit
|
||||
CoreServices
|
||||
CoreAudioKit
|
||||
IOBluetooth
|
||||
];
|
||||
|
||||
env.NIX_CFLAGS_COMPILE = lib.optionalString stdenv.hostPlatform.isDarwin (toString [
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
, freetype
|
||||
, alsa-lib
|
||||
, libjack2
|
||||
, Accelerate
|
||||
, Cocoa
|
||||
, WebKit
|
||||
, MetalKit
|
||||
@@ -53,7 +52,6 @@ stdenv.mkDerivation rec {
|
||||
alsa-lib
|
||||
libjack2
|
||||
] ++ lib.optionals stdenv.hostPlatform.isDarwin [
|
||||
Accelerate
|
||||
Cocoa
|
||||
WebKit
|
||||
MetalKit
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
{ lib, stdenv
|
||||
, coreutils
|
||||
, fetchFromGitHub
|
||||
, makeWrapper
|
||||
@@ -15,21 +14,19 @@
|
||||
, p11-kit
|
||||
, vim
|
||||
, which
|
||||
, ncurses
|
||||
, fetchpatch
|
||||
}:
|
||||
|
||||
with lib.strings;
|
||||
|
||||
let
|
||||
|
||||
version = "2.59.6";
|
||||
version = "2.54.9";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "grame-cncm";
|
||||
repo = "faust";
|
||||
rev = version;
|
||||
sha256 = "sha256-m6dimBxI9C3KDhUxbJAn2Pf9z+LRahjrzD34W/bf1XA=";
|
||||
sha256 = "sha256-7eSZUsZ0h0vWJIpZWXaS+SHV6N2i9nv6Gr6a9cuu4Fg=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
@@ -41,97 +38,80 @@ let
|
||||
maintainers = with maintainers; [ magnetophon pmahoney ];
|
||||
};
|
||||
|
||||
faust =
|
||||
let ncurses_static = ncurses.override { enableStatic = true; };
|
||||
in stdenv.mkDerivation {
|
||||
faust = stdenv.mkDerivation {
|
||||
|
||||
pname = "faust";
|
||||
inherit version;
|
||||
pname = "faust";
|
||||
inherit version;
|
||||
|
||||
inherit src;
|
||||
inherit src;
|
||||
|
||||
nativeBuildInputs = [ makeWrapper pkg-config cmake vim which ];
|
||||
buildInputs = [
|
||||
llvm
|
||||
emscripten
|
||||
openssl
|
||||
libsndfile
|
||||
libmicrohttpd
|
||||
gnutls
|
||||
libtasn1
|
||||
p11-kit
|
||||
ncurses_static
|
||||
];
|
||||
nativeBuildInputs = [ makeWrapper pkg-config cmake vim which ];
|
||||
buildInputs = [ llvm emscripten openssl libsndfile libmicrohttpd gnutls libtasn1 p11-kit ];
|
||||
|
||||
patches = [
|
||||
# make preset management thread safe
|
||||
# needed for magnetophonDSP.VoiceOfFaust
|
||||
# see: https://github.com/grame-cncm/faust/issues/899
|
||||
(fetchpatch {
|
||||
url = "https://github.com/grame-cncm/faust/commit/a1c3a515abbcafea0a6e4e2ec7ecb0f092de5349.patch";
|
||||
hash = "sha256-1Ndm+CgxvGEbS6TKGggeu9hW7N3pC+d1kluT2vhGzL8=";
|
||||
})
|
||||
];
|
||||
|
||||
passthru = { inherit wrap wrapWithBuildEnv faust2ApplBase; };
|
||||
|
||||
preConfigure = ''
|
||||
cd build
|
||||
sed -i 's@LIBNCURSES_PATH ?= .*@LIBNCURSES_PATH ?= ${ncurses_static}/lib/libncurses.a@' Make.llvm.static
|
||||
substituteInPlace Make.llvm.static \
|
||||
--replace 'mkdir -p $@ && cd $@ && ar -x ../../$<' 'mkdir -p $@ && cd $@ && ar -x ../source/build/lib/libfaust.a && cd ../source/build/'
|
||||
substituteInPlace Make.llvm.static \
|
||||
--replace 'rm -rf $(TMP)' ' '
|
||||
'';
|
||||
|
||||
cmakeFlags = [ "-C../backends/all.cmake" "-C../targets/all.cmake" ];
|
||||
|
||||
postInstall = ''
|
||||
# syntax error when eval'd directly
|
||||
pattern="faust2!(*@(atomsnippets|graph|graphviewer|md|plot|sig|sigviewer|svg))"
|
||||
(shopt -s extglob; rm "$out"/bin/$pattern)
|
||||
'';
|
||||
|
||||
postFixup = ''
|
||||
# The 'faustoptflags' is 'source'd into other faust scripts and
|
||||
# not used as an executable, so patch 'uname' usage directly
|
||||
# rather than use makeWrapper.
|
||||
substituteInPlace "$out"/bin/faustoptflags \
|
||||
--replace uname "${coreutils}/bin/uname"
|
||||
|
||||
# wrapper for scripts that don't need faust.wrap*
|
||||
for script in "$out"/bin/faust2*; do
|
||||
wrapProgram "$script" \
|
||||
--prefix PATH : "$out"/bin
|
||||
done
|
||||
'';
|
||||
|
||||
meta = meta // {
|
||||
description =
|
||||
"A functional programming language for realtime audio signal processing";
|
||||
longDescription = ''
|
||||
FAUST (Functional Audio Stream) is a functional programming
|
||||
language specifically designed for real-time signal processing
|
||||
and synthesis. FAUST targets high-performance signal processing
|
||||
applications and audio plug-ins for a variety of platforms and
|
||||
standards.
|
||||
The Faust compiler translates DSP specifications into very
|
||||
efficient C++ code. Thanks to the notion of architecture,
|
||||
FAUST programs can be easily deployed on a large variety of
|
||||
audio platforms and plugin formats (jack, alsa, ladspa, maxmsp,
|
||||
puredata, csound, supercollider, pure, vst, coreaudio) without
|
||||
any change to the FAUST code.
|
||||
|
||||
This package has just the compiler, libraries, and headers.
|
||||
Install faust2* for specific faust2appl scripts.
|
||||
'';
|
||||
};
|
||||
|
||||
passthru = {
|
||||
inherit wrap wrapWithBuildEnv faust2ApplBase;
|
||||
};
|
||||
|
||||
|
||||
preConfigure = ''
|
||||
cd build
|
||||
'';
|
||||
|
||||
cmakeFlags = [
|
||||
"-C../backends/all.cmake"
|
||||
"-C../targets/all.cmake"
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
# syntax error when eval'd directly
|
||||
pattern="faust2!(*@(atomsnippets|graph|graphviewer|md|plot|sig|sigviewer|svg))"
|
||||
(shopt -s extglob; rm "$out"/bin/$pattern)
|
||||
'';
|
||||
|
||||
postFixup = ''
|
||||
# The 'faustoptflags' is 'source'd into other faust scripts and
|
||||
# not used as an executable, so patch 'uname' usage directly
|
||||
# rather than use makeWrapper.
|
||||
substituteInPlace "$out"/bin/faustoptflags \
|
||||
--replace uname "${coreutils}/bin/uname"
|
||||
|
||||
# wrapper for scripts that don't need faust.wrap*
|
||||
for script in "$out"/bin/faust2*; do
|
||||
wrapProgram "$script" \
|
||||
--prefix PATH : "$out"/bin
|
||||
done
|
||||
'';
|
||||
|
||||
meta = meta // {
|
||||
description = "A functional programming language for realtime audio signal processing";
|
||||
longDescription = ''
|
||||
FAUST (Functional Audio Stream) is a functional programming
|
||||
language specifically designed for real-time signal processing
|
||||
and synthesis. FAUST targets high-performance signal processing
|
||||
applications and audio plug-ins for a variety of platforms and
|
||||
standards.
|
||||
The Faust compiler translates DSP specifications into very
|
||||
efficient C++ code. Thanks to the notion of architecture,
|
||||
FAUST programs can be easily deployed on a large variety of
|
||||
audio platforms and plugin formats (jack, alsa, ladspa, maxmsp,
|
||||
puredata, csound, supercollider, pure, vst, coreaudio) without
|
||||
any change to the FAUST code.
|
||||
|
||||
This package has just the compiler, libraries, and headers.
|
||||
Install faust2* for specific faust2appl scripts.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# Default values for faust2appl.
|
||||
faust2ApplBase =
|
||||
{ baseName, dir ? "tools/faust2appls", scripts ? [ baseName ], ... }@args:
|
||||
{ baseName
|
||||
, dir ? "tools/faust2appls"
|
||||
, scripts ? [ baseName ]
|
||||
, ...
|
||||
}@args:
|
||||
|
||||
args // {
|
||||
name = "${baseName}-${version}";
|
||||
@@ -161,8 +141,7 @@ let
|
||||
'';
|
||||
|
||||
meta = meta // {
|
||||
description =
|
||||
"The ${baseName} script, part of faust functional programming language for realtime audio signal processing";
|
||||
description = "The ${baseName} script, part of faust functional programming language for realtime audio signal processing";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -182,7 +161,11 @@ let
|
||||
#
|
||||
# The build input 'faust' is automatically added to the
|
||||
# propagatedBuildInputs.
|
||||
wrapWithBuildEnv = { baseName, propagatedBuildInputs ? [ ], ... }@args:
|
||||
wrapWithBuildEnv =
|
||||
{ baseName
|
||||
, propagatedBuildInputs ? [ ]
|
||||
, ...
|
||||
}@args:
|
||||
|
||||
stdenv.mkDerivation ((faust2ApplBase args) // {
|
||||
|
||||
@@ -222,25 +205,26 @@ let
|
||||
# simply need to be wrapped with some dependencies on PATH.
|
||||
#
|
||||
# The build input 'faust' is automatically added to the PATH.
|
||||
wrap = { baseName, runtimeInputs ? [ ], ... }@args:
|
||||
wrap =
|
||||
{ baseName
|
||||
, runtimeInputs ? [ ]
|
||||
, ...
|
||||
}@args:
|
||||
|
||||
let
|
||||
|
||||
runtimePath =
|
||||
concatStringsSep ":" (map (p: "${p}/bin") ([ faust ] ++ runtimeInputs));
|
||||
runtimePath = concatStringsSep ":" (map (p: "${p}/bin") ([ faust ] ++ runtimeInputs));
|
||||
|
||||
in
|
||||
stdenv.mkDerivation ((faust2ApplBase args) // {
|
||||
in stdenv.mkDerivation ((faust2ApplBase args) // {
|
||||
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
|
||||
postFixup = ''
|
||||
postFixup = ''
|
||||
for script in "$out"/bin/*; do
|
||||
wrapProgram "$script" --prefix PATH : "${runtimePath}"
|
||||
done
|
||||
'';
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
in
|
||||
faust
|
||||
in faust
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{ stdenv, lib, fetchFromGitHub, faust2jaqt, faust2lv2 }:
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "faustPhysicalModeling";
|
||||
version = "2.59.6";
|
||||
version = "2.54.9";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "grame-cncm";
|
||||
repo = "faust";
|
||||
rev = version;
|
||||
sha256 = "sha256-Z/hAq6JlhlWBzWlodwQW/k9AkozVeMXmbVhkicNZ5os=";
|
||||
sha256 = "sha256-1ZS7SVTWI1vNOGycZIDyKLgwfNooIGDa8Wmr6qfFSkU=";
|
||||
};
|
||||
|
||||
buildInputs = [ faust2jaqt faust2lv2 ];
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
, libXcursor
|
||||
, freetype
|
||||
, alsa-lib
|
||||
, Accelerate
|
||||
, Cocoa
|
||||
, WebKit
|
||||
, CoreServices
|
||||
@@ -77,7 +76,6 @@ stdenv.mkDerivation rec {
|
||||
freetype
|
||||
alsa-lib
|
||||
] ++ lib.optionals stdenv.hostPlatform.isDarwin [
|
||||
Accelerate
|
||||
Cocoa
|
||||
WebKit
|
||||
CoreServices
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ python3Packages, fetchPypi, lib, flac, lame, opusTools, vorbis-tools, ffmpeg }:
|
||||
{ python3Packages, lib, flac, lame, opusTools, vorbis-tools, ffmpeg }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "flac2all";
|
||||
version = "5.1";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "OBjlr7cbSx2WOIfZUNwHy5Hpb2Fmh3vmZdc70JiWsiI=";
|
||||
};
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "flacon";
|
||||
version = "11.0.0";
|
||||
version = "10.0.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "flacon";
|
||||
repo = "flacon";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-xc+pp1phFtcGDCsLzzjWjZBfRJ5ss/F1Nm8/s9sWPfs=";
|
||||
sha256 = "sha256-59p5x+d7Vmxx+bdBDxrlf4+NRIdUBuRk+DqohV98XYY=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ cmake pkg-config wrapQtAppsHook ];
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "go-musicfox";
|
||||
version = "4.1.1";
|
||||
version = "4.0.6";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "go-musicfox";
|
||||
repo = pname;
|
||||
rev = "v${version}";
|
||||
hash = "sha256-A1+JDMT4mHUi10GE4/qV5IMuwNsm4EdBt9VC2ZvJzuU=";
|
||||
hash = "sha256-ZqB3NL/pLIY1lHl3qMIOciqsOW9jNwjVQAq1j/ydDWs=";
|
||||
};
|
||||
|
||||
deleteVendor = true;
|
||||
|
||||
vendorHash = "sha256-xzLUWqzDVT+Htw/BHygOJM16uQvWXopyxxHBZQKcOQ8=";
|
||||
vendorHash = "sha256-rJlyrPQS9UKinxIwGGo3EHlmWrzTKIm1jM1UDqnmVyg=";
|
||||
|
||||
subPackages = [ "cmd/musicfox.go" ];
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{ lib
|
||||
, python3
|
||||
, fetchPypi
|
||||
}:
|
||||
|
||||
python3.pkgs.buildPythonPackage rec {
|
||||
@@ -8,7 +7,7 @@ python3.pkgs.buildPythonPackage rec {
|
||||
version = "2.0.67";
|
||||
format = "setuptools";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3.pkgs.fetchPypi {
|
||||
inherit pname version;
|
||||
hash = "sha256-lFxAMjglQZXCySr83PtvStU6hw2ucQu+rSjIHo1yZBk=";
|
||||
};
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "lsp-plugins";
|
||||
version = "1.2.7";
|
||||
version = "1.2.6";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://github.com/sadko4u/${pname}/releases/download/${version}/${pname}-src-${version}.tar.gz";
|
||||
sha256 = "sha256-UCyPOGfa8tVTZzE5ynv/Ov0L+Q6SjAAIwb3jX8X/x0M=";
|
||||
sha256 = "sha256-lNrIsXW3ZNKMFwsl5qowWqK/ZaCaQUAlrSscnsOxvVg=";
|
||||
};
|
||||
|
||||
outputs = [ "out" "dev" "doc" ];
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{ lib
|
||||
, python3Packages
|
||||
, fetchPypi
|
||||
}:
|
||||
|
||||
with python3Packages;
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
version = "10.13";
|
||||
version = "10.11";
|
||||
pname = "monkeys-audio";
|
||||
|
||||
src = fetchzip {
|
||||
url = "https://monkeysaudio.com/files/MAC_${
|
||||
builtins.concatStringsSep "" (lib.strings.splitString "." version)}_SDK.zip";
|
||||
sha256 = "sha256-r+Xjp5q7ehrF34j1FndiKZ+y+FTG1ORXS+9p+R2KbOQ=";
|
||||
sha256 = "sha256-sA1JXRbyHMHBCsWau9GrlxeRiCzxZfCTuLFebZmIoRE=";
|
||||
stripRoot = false;
|
||||
};
|
||||
nativeBuildInputs = [
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "Mopidy-Bandcamp";
|
||||
version = "1.1.5";
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "sha256-wg9zcOKfZQRhpyA1Cu5wvdwKpmrlcr2m9mrqBHgUXAQ=";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "Mopidy-Iris";
|
||||
version = "3.64.0";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "062x73glhn1x4wgc7zmb9y3cq15d5grgqf5drdpbp6p3cgk4s2vc";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "mopidy-jellyfin";
|
||||
version = "1.0.4";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit version;
|
||||
pname = "Mopidy-Jellyfin";
|
||||
sha256 = "ny0u6HdOlZCsmIzZuQ1rql+bvHU3xkh8IdwhJVHNH9c=";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ lib
|
||||
{ stdenv
|
||||
, lib
|
||||
, mopidy
|
||||
, python3Packages
|
||||
, fetchPypi
|
||||
, fetchpatch
|
||||
}:
|
||||
|
||||
@@ -9,7 +9,7 @@ python3Packages.buildPythonApplication rec {
|
||||
pname = "Mopidy-Local";
|
||||
version = "3.2.1";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "18w39mxpv8p17whd6zfw5653d21q138f8xd6ili6ks2g2dbm25i9";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, pythonPackages, fetchPypi, mopidy, glibcLocales }:
|
||||
{ lib, pythonPackages, mopidy, glibcLocales }:
|
||||
|
||||
pythonPackages.buildPythonApplication rec {
|
||||
pname = "Mopidy-Moped";
|
||||
version = "0.7.1";
|
||||
|
||||
src = fetchPypi {
|
||||
src = pythonPackages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "15461174037d87af93dd59a236d4275c5abf71cea0670ffff24a7d0399a8a2e4";
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{ lib, stdenv, fetchFromGitHub, pythonPackages, wrapGAppsNoGuiHook
|
||||
{ lib, stdenv, fetchFromGitHub, pythonPackages, wrapGAppsHook
|
||||
, gst_all_1, glib-networking, gobject-introspection
|
||||
}:
|
||||
|
||||
@@ -13,7 +13,7 @@ pythonPackages.buildPythonApplication rec {
|
||||
sha256 = "sha256-IUQe5WH2vsrAOgokhTNVVM3lnJXphT2xNGu27hWBLSo=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ wrapGAppsNoGuiHook ];
|
||||
nativeBuildInputs = [ wrapGAppsHook ];
|
||||
|
||||
buildInputs = with gst_all_1; [
|
||||
glib-networking
|
||||
@@ -45,7 +45,10 @@ pythonPackages.buildPythonApplication rec {
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://www.mopidy.com/";
|
||||
description = "An extensible music server that plays music from local disk, Spotify, SoundCloud, and more";
|
||||
description = ''
|
||||
An extensible music server that plays music from local disk, Spotify,
|
||||
SoundCloud, and more
|
||||
'';
|
||||
license = licenses.asl20;
|
||||
maintainers = [ maintainers.fpletz ];
|
||||
hydraPlatforms = [];
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, pythonPackages, fetchPypi, mopidy }:
|
||||
{ lib, pythonPackages, mopidy }:
|
||||
|
||||
pythonPackages.buildPythonApplication rec {
|
||||
pname = "Mopidy-Mopify";
|
||||
version = "1.6.1";
|
||||
|
||||
src = fetchPypi {
|
||||
src = pythonPackages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "93ad2b3d38b1450c8f2698bb908b0b077a96b3f64cdd6486519e518132e23a5c";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "Mopidy-MPD";
|
||||
version = "3.3.0";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "sha256-CeLMRqj9cwBvQrOx7XHVV8MjDjwOosONVlsN2o+vTVM=";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "mopidy-mpris";
|
||||
version = "3.0.3";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit version;
|
||||
pname = "Mopidy-MPRIS";
|
||||
sha256 = "sha256-rHQgNIyludTEL7RDC8dIpyGTMOt1Tazn6i/orKlSP4U=";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, pythonPackages, fetchPypi, mopidy }:
|
||||
{ lib, pythonPackages, mopidy }:
|
||||
|
||||
pythonPackages.buildPythonApplication rec {
|
||||
pname = "mopidy-muse";
|
||||
version = "0.0.27";
|
||||
|
||||
src = fetchPypi {
|
||||
src = pythonPackages.fetchPypi {
|
||||
inherit version;
|
||||
pname = "Mopidy-Muse";
|
||||
sha256 = "0jx9dkgxr07avzz9zskzhqy98zsxkdrf7iid2ax5vygwf8qsx8ks";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, fetchPypi, pythonPackages, mopidy }:
|
||||
{ lib, pythonPackages, mopidy }:
|
||||
|
||||
pythonPackages.buildPythonApplication rec {
|
||||
pname = "Mopidy-Notify";
|
||||
version = "0.2.0";
|
||||
|
||||
src = fetchPypi {
|
||||
src = pythonPackages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "sha256-lzZupjlS0kbNvsn18serOoMfu0sRb0nRwpowvOPvt/g=";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "mopidy-podcast";
|
||||
version = "3.0.0";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit version;
|
||||
pname = "Mopidy-Podcast";
|
||||
sha256 = "1z2b523yvdpcf8p7m7kczrvaw045lmxzhq4qj00dflxa2yw61qxr";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "Mopidy-Scrobbler";
|
||||
version = "2.0.1";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "11vxgax4xgkggnq4fr1rh2rcvzspkkimck5p3h4phdj3qpnj0680";
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ lib, python3Packages, fetchPypi, mopidy }:
|
||||
{ lib, python3Packages, mopidy }:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "mopidy-somafm";
|
||||
version = "2.0.2";
|
||||
|
||||
src = fetchPypi {
|
||||
src = python3Packages.fetchPypi {
|
||||
inherit version;
|
||||
pname = "Mopidy-SomaFM";
|
||||
sha256 = "DC0emxkoWfjGHih2C8nINBFByf521Xf+3Ks4JRxNPLM=";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user