Compare commits

..

1 Commits

Author SHA1 Message Date
Ryan Lahfa
8966c43feb 23.05 beta release 2023-05-22 21:05:44 +02:00
1271 changed files with 48705 additions and 46066 deletions

View File

@@ -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

View File

@@ -1 +1 @@
23.11
23.05

View File

@@ -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; };
```

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 =

View File

@@ -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";

View File

@@ -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";

View File

@@ -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

View File

@@ -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).

View File

@@ -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.

View File

@@ -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"):

View File

@@ -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

View File

@@ -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 =

View File

@@ -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.

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;
};

View File

@@ -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.";
};

View File

@@ -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

View File

@@ -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 ];
}

View File

@@ -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 ];
}

View File

@@ -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
];
};
}

View File

@@ -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>"}

View File

@@ -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;
};
};
};

View File

@@ -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";
};
};

View File

@@ -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;
}

View File

@@ -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;
};
}];
})

View File

@@ -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 = [

View File

@@ -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 = [ {

View File

@@ -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;

View File

@@ -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";
};
};
};
}

View File

@@ -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";

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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";
};
};
};

View File

@@ -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;
})
];
};
};
}

View File

@@ -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};

View File

@@ -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`
'';
};

View File

@@ -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";

View File

@@ -25,11 +25,9 @@ let
sectionDHCPv4 = checkUnitConfig "DHCPv4" [
(assertOnlyFields [
"ClientIdentifier"
"DUIDType"
"DUIDRawData"
])
(assertValueOneOf "ClientIdentifier" ["mac" "duid" "duid-only"])
];
sectionDHCPv6 = checkUnitConfig "DHCPv6" [

View File

@@ -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
];
};
};
}

View File

@@ -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";

View File

@@ -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";

View File

@@ -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

View File

@@ -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 (

View File

@@ -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,

View File

@@ -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 {};

View File

@@ -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'")
'';
})

View File

@@ -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'")
'';
})

View File

@@ -27,7 +27,6 @@
simpleUefiGrub
simpleUefiGrubSpecialisation
simpleUefiSystemdBoot
stratisRoot
# swraid
zfsroot
;

View File

@@ -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
];
};
};
};
}

View File

@@ -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"
)
'';
})

View File

@@ -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, ... }: {

View File

@@ -27,8 +27,6 @@ import ./make-test-python.nix (
};
};
testScript = ''
import os
start_all()
# Create a fake cache with Nginx service the static files

View File

@@ -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 // {

View File

@@ -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-----

View File

@@ -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-----

View File

@@ -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-----

View File

@@ -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-----

View File

@@ -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)
'';
})

View File

@@ -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
''

View File

@@ -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";
};
}

View File

@@ -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/")
'';
})

View File

@@ -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")
'';
}

View File

@@ -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 = ''

View File

@@ -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 [

View File

@@ -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

View File

@@ -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

View File

@@ -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 ];

View File

@@ -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

View File

@@ -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=";
};

View File

@@ -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 ];

View File

@@ -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" ];

View File

@@ -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=";
};

View File

@@ -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" ];

View File

@@ -1,6 +1,5 @@
{ lib
, python3Packages
, fetchPypi
}:
with python3Packages;

View File

@@ -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 = [

View File

@@ -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=";
};

View File

@@ -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";
};

View File

@@ -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=";

View File

@@ -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";
};

View File

@@ -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";
};

View File

@@ -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 = [];

View File

@@ -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";
};

View File

@@ -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=";
};

View File

@@ -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=";

View File

@@ -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";

View File

@@ -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=";
};

View File

@@ -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";

View File

@@ -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";
};

View File

@@ -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