Merge remote-tracking branch 'origin/master' into haskell-updates

This commit is contained in:
Peter Simons 2020-08-27 14:26:14 +02:00
commit 24cd70f14a
961 changed files with 15970 additions and 8938 deletions

9
.github/CODEOWNERS vendored
View file

@ -195,10 +195,11 @@
/pkgs/top-level/php-packages.nix @NixOS/php
# Podman, CRI-O modules and related
/nixos/modules/virtualisation/containers.nix @NixOS/podman
/nixos/modules/virtualisation/cri-o.nix @NixOS/podman
/nixos/modules/virtualisation/podman.nix @NixOS/podman
/nixos/tests/podman.nix @NixOS/podman
/nixos/modules/virtualisation/containers.nix @NixOS/podman @zowoq
/nixos/modules/virtualisation/cri-o.nix @NixOS/podman @zowoq
/nixos/modules/virtualisation/podman.nix @NixOS/podman @zowoq
/nixos/tests/cri-o.nix @NixOS/podman @zowoq
/nixos/tests/podman.nix @NixOS/podman @zowoq
# Blockchains
/pkgs/applications/blockchains @mmahut

21
.github/workflows/pending-clear.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: "clear pending status"
on:
check_suite:
types: [ completed ]
jobs:
action:
runs-on: ubuntu-latest
steps:
- name: clear pending status
if: github.repository_owner == 'NixOS' && github.event.check_suite.app.name == 'OfBorg'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
curl \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token $GITHUB_TOKEN" \
-d '{"state": "success", "target_url": " ", "description": " ", "context": "Wait for ofborg"}' \
"https://api.github.com/repos/NixOS/nixpkgs/statuses/${{ github.event.check_suite.head_sha }}"

20
.github/workflows/pending-set.yml vendored Normal file
View file

@ -0,0 +1,20 @@
name: "set pending status"
on:
pull_request_target:
jobs:
action:
runs-on: ubuntu-latest
steps:
- name: set pending status
if: github.repository_owner == 'NixOS'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
curl \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token $GITHUB_TOKEN" \
-d '{"state": "failure", "target_url": " ", "description": "This failed status will be cleared when ofborg finishes eval.", "context": "Wait for ofborg"}' \
"https://api.github.com/repos/NixOS/nixpkgs/statuses/${{ github.event.pull_request.head.sha }}"

View file

@ -17,7 +17,6 @@ pkgs.runCommandNoCC "nixpkgs-lib-tests" {
export TEST_ROOT=$(pwd)/test-tmp
export NIX_BUILD_HOOK=
export NIX_CONF_DIR=$TEST_ROOT/etc
export NIX_DB_DIR=$TEST_ROOT/db
export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
export NIX_STATE_DIR=$TEST_ROOT/var/nix

View file

@ -254,6 +254,12 @@
githubId = 732652;
name = "Andreas Herrmann";
};
ahrzb = {
email = "ahrzb5@gmail.com";
github = "ahrzb";
githubId = 5220438;
name = "AmirHossein Roozbahani";
};
ahuzik = {
email = "ales.guzik@gmail.com";
github = "alesguzik";
@ -466,6 +472,12 @@
githubId = 858965;
name = "Andrew Morsillo";
};
andehen = {
email = "git@andehen.net";
github = "andehen";
githubId = 754494;
name = "Anders Asheim Hennum";
};
andersk = {
email = "andersk@mit.edu";
github = "andersk";
@ -2705,6 +2717,12 @@
githubId = 857308;
name = "Joe Hermaszewski";
};
extends = {
email = "sharosari@gmail.com";
github = "ImExtends";
githubId = 55919390;
name = "Vincent VILLIAUMEY";
};
eyjhb = {
email = "eyjhbb@gmail.com";
github = "eyJhb";
@ -3343,6 +3361,12 @@
githubId = 131599;
name = "Martin Weinelt";
};
hh = {
email = "hh@m-labs.hk";
github = "HarryMakes";
githubId = 66358631;
name = "Harry Ho";
};
hhm = {
email = "heehooman+nixpkgs@gmail.com";
github = "hhm0";
@ -3715,6 +3739,12 @@
}];
name = "Jiri Daněk";
};
jdbaldry = {
email = "jack.baldry@grafana.com";
github = "jdbaldry";
githubId = 4599384;
name = "Jack Baldry";
};
jdehaas = {
email = "qqlq@nullptr.club";
github = "jeroendehaas";
@ -3835,6 +3865,12 @@
githubId = 51518420;
name = "jitwit";
};
jjjollyjim = {
email = "jamie@kwiius.com";
github = "JJJollyjim";
githubId = 691552;
name = "Jamie McClymont";
};
jk = {
email = "hello+nixpkgs@j-k.io";
github = "06kellyjac";
@ -6719,6 +6755,12 @@
githubId = 37715;
name = "Brian McKenna";
};
purcell = {
email = "steve@sanityinc.com";
github = "purcell";
githubId = 5636;
name = "Steve Purcell";
};
puzzlewolf = {
email = "nixos@nora.pink";
github = "puzzlewolf";
@ -6755,6 +6797,12 @@
githubId = 115877;
name = "Kenny Shen";
};
quentini = {
email = "quentini@airmail.cc";
github = "QuentinI";
githubId = 18196237;
name = "Quentin Inkling";
};
qyliss = {
email = "hi@alyssa.is";
github = "alyssais";
@ -8133,6 +8181,12 @@
githubId = 863327;
name = "Tyler Benster";
};
tcbravo = {
email = "tomas.bravo@protonmail.ch";
github = "tcbravo";
githubId = 66133083;
name = "Tomas Bravo";
};
tckmn = {
email = "andy@tck.mn";
github = "tckmn";
@ -8523,6 +8577,12 @@
githubId = 699403;
name = "Tomas Vestelind";
};
tviti = {
email = "tviti@hawaii.edu";
github = "tviti";
githubId = 2251912;
name = "Taylor Viti";
};
tvorog = {
email = "marszaripov@gmail.com";
github = "tvorog";
@ -9132,6 +9192,16 @@
fingerprint = "85F8 E850 F8F2 F823 F934 535B EC50 6589 9AEA AF4C";
}];
};
yusdacra = {
email = "y.bera003.06@protonmail.com";
github = "yusdacra";
githubId = 19897088;
name = "Yusuf Bera Ertan";
keys = [{
longkeyid = "rsa2048/0x61807181F60EFCB2";
fingerprint = "9270 66BD 8125 A45B 4AC4 0326 6180 7181 F60E FCB2";
}];
};
yvesf = {
email = "yvesf+nix@xapek.org";
github = "yvesf";
@ -9404,4 +9474,20 @@
github = "fzakaria";
githubId = 605070;
};
yevhenshymotiuk = {
name = "Yevhen Shymotiuk";
email = "yevhenshymotiuk@gmail.com";
github = "yevhenshymotiuk";
githubId = 44244245;
};
hmenke = {
name = "Henri Menke";
email = "henri@henrimenke.de";
github = "hmenke";
githubId = 1903556;
keys = [{
longkeyid = "rsa4096/0xD65C9AFB4C224DA3";
fingerprint = "F1C5 760E 45B9 9A44 72E9 6BFB D65C 9AFB 4C22 4DA3";
}];
};
}

View file

@ -70,35 +70,12 @@ Platform Vendor Advanced Micro Devices, Inc.</screen>
Core Next</link> (GCN) GPUs are supported through the
<package>rocm-opencl-icd</package> package. Adding this package to
<xref linkend="opt-hardware.opengl.extraPackages"/> enables OpenCL
support. However, OpenCL Image support is provided through the
non-free <package>rocm-runtime-ext</package> package. This package can
be added to the same configuration option, but requires that
<varname>allowUnfree</varname> option is is enabled for nixpkgs. Full
OpenCL support on supported AMD GPUs is thus enabled as follows:
support:
<programlisting><xref linkend="opt-hardware.opengl.extraPackages"/> = [
rocm-opencl-icd
rocm-runtime-ext
];</programlisting>
</para>
<para>
It is also possible to use the OpenCL Image extension without a
system-wide installation of the <package>rocm-runtime-ext</package>
package by setting the <varname>ROCR_EXT_DIR</varname> environment
variable to the directory that contains the extension:
<screen><prompt>$</prompt> export \
ROCR_EXT_DIR=`nix-build '&lt;nixpkgs&gt;' --no-out-link -A rocm-runtime-ext`/lib/rocm-runtime-ext</screen>
</para>
<para>
With either approach, you can verify that OpenCL Image support
is indeed working with the <command>clinfo</command> command:
<screen><prompt>$</prompt> clinfo | grep Image
Image support Yes</screen>
</para>
</section>
<section xml:id="sec-gpu-accel-opencl-intel">

View file

@ -136,7 +136,7 @@
<filename>/mnt</filename>:
</para>
<screen>
# nixos-enter /mnt
# nixos-enter --root /mnt
</screen>
<para>
Run a shell command:

View file

@ -128,7 +128,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
</listitem>
<listitem>
<para>
Two new option <link linkend="opt-documentation.man.generateCaches">documentation.man.generateCaches</link>
The new option <link linkend="opt-documentation.man.generateCaches">documentation.man.generateCaches</link>
has been added to automatically generate the <literal>man-db</literal> caches, which are needed by utilities
like <command>whatis</command> and <command>apropos</command>. The caches are generated during the build of
the NixOS configuration: since this can be expensive when a large number of packages are installed, the
@ -137,7 +137,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
</listitem>
<listitem>
<para>
<varname>services.postfix.sslCACert</varname> was replaced by <varname>services.postfix.tlsTrustedAuthorities</varname> which now defaults to system certifcate authorities.
<varname>services.postfix.sslCACert</varname> was replaced by <varname>services.postfix.tlsTrustedAuthorities</varname> which now defaults to system certificate authorities.
</para>
</listitem>
<listitem>
@ -156,6 +156,54 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
Support for built-in LCDs in various pieces of Logitech hardware (keyboards and USB speakers). <varname>hardware.logitech.lcd.enable</varname> enables support for all hardware supported by the g15daemon project.
</para>
</listitem>
<listitem>
<para>
Zabbix now defaults to 5.0, updated from 4.4. Please carefully read through
<link xlink:href="https://www.zabbix.com/documentation/current/manual/installation/upgrade/sources">the upgrade guide</link>
and apply any changes required. Be sure to take special note of the section on
<link xlink:href="https://www.zabbix.com/documentation/current/manual/installation/upgrade_notes_500#enabling_extended_range_of_numeric_float_values">enabling extended range of numeric (float) values</link>
as you will need to apply this database migration manually.
</para>
<para>
If you are using Zabbix Server with a MySQL or MariaDB database you should note that using a character set of <literal>utf8</literal> and a collate of <literal>utf8_bin</literal> has become mandatory with
this release. See the upstream <link xlink:href="https://support.zabbix.com/browse/ZBX-17357">issue</link> for further discussion. Before upgrading you should check the character set and collation used by
your database and ensure they are correct:
<programlisting>
SELECT
default_character_set_name,
default_collation_name
FROM
information_schema.schemata
WHERE
schema_name = 'zabbix';
</programlisting>
If these values are not correct you should take a backup of your database and convert the character set and collation as required. Here is an
<link xlink:href="https://www.zabbix.com/forum/zabbix-help/396573-reinstall-after-upgrade?p=396891#post396891">example</link> of how to do so, taken from
the Zabbix forums:
<programlisting>
ALTER DATABASE `zabbix` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
-- the following will produce a list of SQL commands you should subsequently execute
SELECT CONCAT("ALTER TABLE ", TABLE_NAME," CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;") AS ExecuteTheString
FROM information_schema.`COLUMNS`
WHERE table_schema = "zabbix" AND COLLATION_NAME = "utf8_general_ci";
</programlisting>
</para>
</listitem>
<listitem>
<para>
The NixOS module system now supports freeform modules as a mix between <literal>types.attrsOf</literal> and <literal>types.submodule</literal>. These allow you to explicitly declare a subset of options while still permitting definitions without an associated option. See <xref linkend='sec-freeform-modules'/> for how to use them.
</para>
</listitem>
<listitem>
<para>
The GRUB module gained support for basic password protection, which
allows to restrict non-default entries in the boot menu to one or more
users. The users and passwords are defined via the option
<option>boot.loader.grub.users</option>.
Note: Password support is only avaiable in GRUB version 2.
</para>
</listitem>
</itemizedlist>
</section>
@ -199,7 +247,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
in the source tree for downloaded modules instead of using go's <link
xlink:href="https://golang.org/cmd/go/#hdr-Module_proxy_protocol">module
proxy protocol</link>. This storage format is simpler and therefore less
likekly to break with future versions of go. As a result
likely to break with future versions of go. As a result
<literal>buildGoModule</literal> switched from
<literal>modSha256</literal> to the <literal>vendorSha256</literal>
attribute to pin fetched version data.
@ -211,7 +259,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
<link xlink:href="https://grafana.com/docs/grafana/latest/guides/whats-new-in-v6-4/">deprecated in Grafana</link>
and the <package>phantomjs</package> project is
<link xlink:href="https://github.com/ariya/phantomjs/issues/15344#issue-302015362">currently unmaintained</link>.
It can still be enabled by providing <literal>phantomJsSupport = true</literal> to the package instanciation:
It can still be enabled by providing <literal>phantomJsSupport = true</literal> to the package instantiation:
<programlisting>{
services.grafana.package = pkgs.grafana.overrideAttrs (oldAttrs: rec {
phantomJsSupport = false;
@ -223,7 +271,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
<para>
The <link linkend="opt-services.supybot.enable">supybot</link> module now uses <literal>/var/lib/supybot</literal>
as its default <link linkend="opt-services.supybot.stateDir">stateDir</link> path if <literal>stateVersion</literal>
is 20.09 or higher. It also enables number of
is 20.09 or higher. It also enables a number of
<link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Sandboxing">systemd sandboxing options</link>
which may possibly interfere with some plugins. If this is the case you can disable the options through attributes in
<option>systemd.services.supybot.serviceConfig</option>.
@ -697,6 +745,13 @@ CREATE ROLE postgres LOGIN SUPERUSER;
The USBGuard module now removes options and instead hardcodes values for <literal>IPCAccessControlFiles</literal>, <literal>ruleFiles</literal>, and <literal>auditFilePath</literal>. Audit logs can be found in the journal.
</para>
</listitem>
<listitem>
<para>
The NixOS module system now evaluates option definitions more strictly, allowing it to detect a larger set of problems.
As a result, what previously evaluated may not do so anymore.
See <link xlink:href="https://github.com/NixOS/nixpkgs/pull/82743#issuecomment-674520472">the PR that changed this</link> for more info.
</para>
</listitem>
</itemizedlist>
</section>
@ -915,9 +970,18 @@ services.transmission.settings.rpc-bind-address = "0.0.0.0";
</listitem>
<listitem>
<para>
Nginx module <literal>nginxModules.fastcgi-cache-purge</literal> renamed to official name <literal>nginxModules.cache-purge</literal>.
Nginx module <literal>nginxModules.ngx_aws_auth</literal> renamed to official name <literal>nginxModules.aws-auth</literal>.
The packages <package>perl</package>, <package>rsync</package> and <package>strace</package> were removed from <option>systemPackages</option>. If you need them, install them again with <code><xref linkend="opt-environment.systemPackages"/> = with pkgs; [ perl rsync strace ];</code> in your <filename>configuration.nix</filename>.
</para>
</listitem>
<listitem>
<para>
The <literal>undervolt</literal> option no longer needs to apply its
settings every 30s. If they still become undone, open an issue and restore
the previous behaviour using <literal>undervolt.useTimer</literal>.
</para>
</listitem>
</itemizedlist>
</section>
</section>

View file

@ -24,11 +24,11 @@
check ? true
, prefix ? []
, lib ? import ../../lib
, extraModules ? let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
in if e == "" then [] else [(import e)]
}:
let extraArgs_ = extraArgs; pkgs_ = pkgs;
extraModules = let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
in if e == "" then [] else [(import e)];
in
let

View file

@ -22,9 +22,9 @@ rec {
else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'";
qemuBinary = qemuPkg: {
x86_64-linux = "${qemuPkg}/bin/qemu-kvm -cpu host";
x86_64-linux = "${qemuPkg}/bin/qemu-kvm -cpu max";
armv7l-linux = "${qemuPkg}/bin/qemu-system-arm -enable-kvm -machine virt -cpu host";
aarch64-linux = "${qemuPkg}/bin/qemu-system-aarch64 -enable-kvm -machine virt,gic-version=host -cpu host";
x86_64-darwin = "${qemuPkg}/bin/qemu-kvm -cpu host";
x86_64-darwin = "${qemuPkg}/bin/qemu-kvm -cpu max";
}.${pkgs.stdenv.hostPlatform.system} or "${qemuPkg}/bin/qemu-kvm";
}

View file

@ -1,19 +1,13 @@
#! /somewhere/python3
from contextlib import contextmanager, _GeneratorContextManager
from queue import Queue, Empty
from typing import Tuple, Any, Callable, Dict, Iterator, Optional, List
from xml.sax.saxutils import XMLGenerator
import queue
import io
import _thread
import argparse
import atexit
import base64
import codecs
import io
import logging
import os
import pathlib
import ptpython.repl
import pty
import queue
import re
import shlex
import shutil
@ -21,9 +15,12 @@ import socket
import subprocess
import sys
import tempfile
import _thread
import time
import traceback
import unicodedata
from contextlib import contextmanager
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple
import ptpython.repl
CHAR_TO_KEY = {
"A": "shift-a",
@ -88,13 +85,17 @@ CHAR_TO_KEY = {
")": "shift-0x0B",
}
# Forward references
log: "Logger"
# Forward reference
machines: "List[Machine]"
logging.basicConfig(format="%(message)s")
logger = logging.getLogger("test-driver")
logger.setLevel(logging.INFO)
def eprint(*args: object, **kwargs: Any) -> None:
print(*args, file=sys.stderr, **kwargs)
class MachineLogAdapter(logging.LoggerAdapter):
def process(self, msg: str, kwargs: Any) -> Tuple[str, Any]:
return f"{self.extra['machine']}: {msg}", kwargs
def make_command(args: list) -> str:
@ -102,8 +103,7 @@ def make_command(args: list) -> str:
def create_vlan(vlan_nr: str) -> Tuple[str, str, "subprocess.Popen[bytes]", Any]:
global log
log.log("starting VDE switch for network {}".format(vlan_nr))
logger.info(f"starting VDE switch for network {vlan_nr}")
vde_socket = tempfile.mkdtemp(
prefix="nixos-test-vde-", suffix="-vde{}.ctl".format(vlan_nr)
)
@ -142,70 +142,6 @@ def retry(fn: Callable) -> None:
raise Exception("action timed out")
class Logger:
def __init__(self) -> None:
self.logfile = os.environ.get("LOGFILE", "/dev/null")
self.logfile_handle = codecs.open(self.logfile, "wb")
self.xml = XMLGenerator(self.logfile_handle, encoding="utf-8")
self.queue: "Queue[Dict[str, str]]" = Queue()
self.xml.startDocument()
self.xml.startElement("logfile", attrs={})
def close(self) -> None:
self.xml.endElement("logfile")
self.xml.endDocument()
self.logfile_handle.close()
def sanitise(self, message: str) -> str:
return "".join(ch for ch in message if unicodedata.category(ch)[0] != "C")
def maybe_prefix(self, message: str, attributes: Dict[str, str]) -> str:
if "machine" in attributes:
return "{}: {}".format(attributes["machine"], message)
return message
def log_line(self, message: str, attributes: Dict[str, str]) -> None:
self.xml.startElement("line", attributes)
self.xml.characters(message)
self.xml.endElement("line")
def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
eprint(self.maybe_prefix(message, attributes))
self.drain_log_queue()
self.log_line(message, attributes)
def enqueue(self, message: Dict[str, str]) -> None:
self.queue.put(message)
def drain_log_queue(self) -> None:
try:
while True:
item = self.queue.get_nowait()
attributes = {"machine": item["machine"], "type": "serial"}
self.log_line(self.sanitise(item["msg"]), attributes)
except Empty:
pass
@contextmanager
def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
eprint(self.maybe_prefix(message, attributes))
self.xml.startElement("nest", attrs={})
self.xml.startElement("head", attributes)
self.xml.characters(message)
self.xml.endElement("head")
tic = time.time()
self.drain_log_queue()
yield
self.drain_log_queue()
toc = time.time()
self.log("({:.2f} seconds)".format(toc - tic))
self.xml.endElement("nest")
class Machine:
def __init__(self, args: Dict[str, Any]) -> None:
if "name" in args:
@ -235,8 +171,8 @@ class Machine:
self.pid: Optional[int] = None
self.socket = None
self.monitor: Optional[socket.socket] = None
self.logger: Logger = args["log"]
self.allow_reboot = args.get("allowReboot", False)
self.logger = MachineLogAdapter(logger, extra=dict(machine=self.name))
@staticmethod
def create_startcommand(args: Dict[str, str]) -> str:
@ -292,14 +228,6 @@ class Machine:
def is_up(self) -> bool:
return self.booted and self.connected
def log(self, msg: str) -> None:
self.logger.log(msg, {"machine": self.name})
def nested(self, msg: str, attrs: Dict[str, str] = {}) -> _GeneratorContextManager:
my_attrs = {"machine": self.name}
my_attrs.update(attrs)
return self.logger.nested(msg, my_attrs)
def wait_for_monitor_prompt(self) -> str:
assert self.monitor is not None
answer = ""
@ -314,7 +242,7 @@ class Machine:
def send_monitor_command(self, command: str) -> str:
message = ("{}\n".format(command)).encode()
self.log("sending monitor command: {}".format(command))
self.logger.info(f"sending monitor command: {command}")
assert self.monitor is not None
self.monitor.send(message)
return self.wait_for_monitor_prompt()
@ -381,16 +309,19 @@ class Machine:
return self.execute("systemctl {}".format(q))
def require_unit_state(self, unit: str, require_state: str = "active") -> None:
with self.nested(
"checking if unit {} has reached state '{}'".format(unit, require_state)
):
info = self.get_unit_info(unit)
state = info["ActiveState"]
if state != require_state:
raise Exception(
"Expected unit {} to to be in state ".format(unit)
+ "'{}' but it is in state {}".format(require_state, state)
)
self.logger.info(
f"checking if unit {unit} has reached state '{require_state}'"
)
info = self.get_unit_info(unit)
state = info["ActiveState"]
if state != require_state:
raise Exception(
"Expected unit {} to to be in state ".format(unit)
+ "'{}' but it is in state {}".format(require_state, state)
)
def log(self, message: str) -> None:
self.logger.info(message)
def execute(self, command: str) -> Tuple[int, str]:
self.connect()
@ -414,25 +345,26 @@ class Machine:
"""Execute each command and check that it succeeds."""
output = ""
for command in commands:
with self.nested("must succeed: {}".format(command)):
(status, out) = self.execute(command)
if status != 0:
self.log("output: {}".format(out))
raise Exception(
"command `{}` failed (exit code {})".format(command, status)
)
output += out
self.logger.info(f"must succeed: {command}")
(status, out) = self.execute(command)
if status != 0:
self.logger.info(f"output: {out}")
raise Exception(
"command `{}` failed (exit code {})".format(command, status)
)
output += out
return output
def fail(self, *commands: str) -> None:
def fail(self, *commands: str) -> str:
"""Execute each command and check that it fails."""
output = ""
for command in commands:
with self.nested("must fail: {}".format(command)):
status, output = self.execute(command)
if status == 0:
raise Exception(
"command `{}` unexpectedly succeeded".format(command)
)
self.logger.info(f"must fail: {command}")
(status, out) = self.execute(command)
if status == 0:
raise Exception("command `{}` unexpectedly succeeded".format(command))
output += out
return output
def wait_until_succeeds(self, command: str) -> str:
"""Wait until a command returns success and return its output.
@ -445,9 +377,9 @@ class Machine:
status, output = self.execute(command)
return status == 0
with self.nested("waiting for success: {}".format(command)):
retry(check_success)
return output
self.logger.info(f"waiting for success: {command}")
retry(check_success)
return output
def wait_until_fails(self, command: str) -> str:
"""Wait until a command returns failure.
@ -460,21 +392,21 @@ class Machine:
status, output = self.execute(command)
return status != 0
with self.nested("waiting for failure: {}".format(command)):
retry(check_failure)
return output
self.logger.info(f"waiting for failure: {command}")
retry(check_failure)
return output
def wait_for_shutdown(self) -> None:
if not self.booted:
return
with self.nested("waiting for the VM to power off"):
sys.stdout.flush()
self.process.wait()
self.logger.info("waiting for the VM to power off")
sys.stdout.flush()
self.process.wait()
self.pid = None
self.booted = False
self.connected = False
self.pid = None
self.booted = False
self.connected = False
def get_tty_text(self, tty: str) -> str:
status, output = self.execute(
@ -492,19 +424,19 @@ class Machine:
def tty_matches(last: bool) -> bool:
text = self.get_tty_text(tty)
if last:
self.log(
self.logger.info(
f"Last chance to match /{regexp}/ on TTY{tty}, "
f"which currently contains: {text}"
)
return len(matcher.findall(text)) > 0
with self.nested("waiting for {} to appear on tty {}".format(regexp, tty)):
retry(tty_matches)
self.logger.info(f"waiting for {regexp} to appear on tty {tty}")
retry(tty_matches)
def send_chars(self, chars: List[str]) -> None:
with self.nested("sending keys {}".format(chars)):
for char in chars:
self.send_key(char)
self.logger.info(f"sending keys {chars}")
for char in chars:
self.send_key(char)
def wait_for_file(self, filename: str) -> None:
"""Waits until the file exists in machine's file system."""
@ -513,16 +445,16 @@ class Machine:
status, _ = self.execute("test -e {}".format(filename))
return status == 0
with self.nested("waiting for file {}".format(filename)):
retry(check_file)
self.logger.info(f"waiting for file {filename}")
retry(check_file)
def wait_for_open_port(self, port: int) -> None:
def port_is_open(_: Any) -> bool:
status, _ = self.execute("nc -z localhost {}".format(port))
return status == 0
with self.nested("waiting for TCP port {}".format(port)):
retry(port_is_open)
self.logger.info(f"waiting for TCP port {port}")
retry(port_is_open)
def wait_for_closed_port(self, port: int) -> None:
def port_is_closed(_: Any) -> bool:
@ -544,17 +476,17 @@ class Machine:
if self.connected:
return
with self.nested("waiting for the VM to finish booting"):
self.start()
self.logger.info("waiting for the VM to finish booting")
self.start()
tic = time.time()
self.shell.recv(1024)
# TODO: Timeout
toc = time.time()
tic = time.time()
self.shell.recv(1024)
# TODO: Timeout
toc = time.time()
self.log("connected to guest root shell")
self.log("(connecting took {:.2f} seconds)".format(toc - tic))
self.connected = True
self.logger.info("connected to guest root shell")
self.logger.info(f"(connecting took {toc - tic:.2f} seconds)")
self.connected = True
def screenshot(self, filename: str) -> None:
out_dir = os.environ.get("out", os.getcwd())
@ -563,15 +495,12 @@ class Machine:
filename = os.path.join(out_dir, "{}.png".format(filename))
tmp = "{}.ppm".format(filename)
with self.nested(
"making screenshot {}".format(filename),
{"image": os.path.basename(filename)},
):
self.send_monitor_command("screendump {}".format(tmp))
ret = subprocess.run("pnmtopng {} > {}".format(tmp, filename), shell=True)
os.unlink(tmp)
if ret.returncode != 0:
raise Exception("Cannot convert screenshot")
self.logger.info(f"making screenshot {filename}")
self.send_monitor_command("screendump {}".format(tmp))
ret = subprocess.run("pnmtopng {} > {}".format(tmp, filename), shell=True)
os.unlink(tmp)
if ret.returncode != 0:
raise Exception("Cannot convert screenshot")
def copy_from_host_via_shell(self, source: str, target: str) -> None:
"""Copy a file from the host into the guest by piping it over the
@ -647,20 +576,18 @@ class Machine:
tess_args = "-c debug_file=/dev/null --psm 11 --oem 2"
with self.nested("performing optical character recognition"):
with tempfile.NamedTemporaryFile() as tmpin:
self.send_monitor_command("screendump {}".format(tmpin.name))
self.logger.info("performing optical character recognition")
with tempfile.NamedTemporaryFile() as tmpin:
self.send_monitor_command("screendump {}".format(tmpin.name))
cmd = "convert {} {} tiff:- | tesseract - - {}".format(
magick_args, tmpin.name, tess_args
)
ret = subprocess.run(cmd, shell=True, capture_output=True)
if ret.returncode != 0:
raise Exception(
"OCR failed with exit code {}".format(ret.returncode)
)
cmd = "convert {} {} tiff:- | tesseract - - {}".format(
magick_args, tmpin.name, tess_args
)
ret = subprocess.run(cmd, shell=True, capture_output=True)
if ret.returncode != 0:
raise Exception("OCR failed with exit code {}".format(ret.returncode))
return ret.stdout.decode("utf-8")
return ret.stdout.decode("utf-8")
def wait_for_text(self, regex: str) -> None:
def screen_matches(last: bool) -> bool:
@ -668,15 +595,15 @@ class Machine:
matches = re.search(regex, text) is not None
if last and not matches:
self.log("Last OCR attempt failed. Text was: {}".format(text))
self.logger.info(f"Last OCR attempt failed. Text was: {text}")
return matches
with self.nested("waiting for {} to appear on screen".format(regex)):
retry(screen_matches)
self.logger.info(f"waiting for {regex} to appear on screen")
retry(screen_matches)
def wait_for_console_text(self, regex: str) -> None:
self.log("waiting for {} to appear on console".format(regex))
self.logger.info(f"waiting for {regex} to appear on console")
# Buffer the console output, this is needed
# to match multiline regexes.
console = io.StringIO()
@ -699,7 +626,7 @@ class Machine:
if self.booted:
return
self.log("starting vm")
self.logger.info("starting vm")
def create_socket(path: str) -> socket.socket:
if os.path.exists(path):
@ -756,7 +683,7 @@ class Machine:
# Store last serial console lines for use
# of wait_for_console_text
self.last_lines: Queue = Queue()
self.last_lines: queue.Queue = queue.Queue()
def process_serial_output() -> None:
assert self.process.stdout is not None
@ -764,8 +691,7 @@ class Machine:
# Ignore undecodable bytes that may occur in boot menus
line = _line.decode(errors="ignore").replace("\r", "").rstrip()
self.last_lines.put(line)
eprint("{} # {}".format(self.name, line))
self.logger.enqueue({"msg": line, "machine": self.name})
self.logger.info(line)
_thread.start_new_thread(process_serial_output, ())
@ -774,10 +700,10 @@ class Machine:
self.pid = self.process.pid
self.booted = True
self.log("QEMU running (pid {})".format(self.pid))
self.logger.info(f"QEMU running (pid {self.pid})")
def cleanup_statedir(self) -> None:
self.log("delete the VM state directory")
self.logger.info("delete the VM state directory")
if os.path.isfile(self.state_dir):
shutil.rmtree(self.state_dir)
@ -792,7 +718,7 @@ class Machine:
if not self.booted:
return
self.log("forced crash")
self.logger.info("forced crash")
self.send_monitor_command("quit")
self.wait_for_shutdown()
@ -812,8 +738,8 @@ class Machine:
status, _ = self.execute("[ -e /tmp/.X11-unix/X0 ]")
return status == 0
with self.nested("waiting for the X11 server"):
retry(check_x)
self.logger.info("waiting for the X11 server")
retry(check_x)
def get_window_names(self) -> List[str]:
return self.succeed(
@ -826,15 +752,14 @@ class Machine:
def window_is_visible(last_try: bool) -> bool:
names = self.get_window_names()
if last_try:
self.log(
"Last chance to match {} on the window list,".format(regexp)
+ " which currently contains: "
+ ", ".join(names)
self.logger.info(
f"Last chance to match {regexp} on the window list, "
+ f"which currently contains: {', '.join(names)}"
)
return any(pattern.search(name) for name in names)
with self.nested("Waiting for a window to appear"):
retry(window_is_visible)
self.logger.info("Waiting for a window to appear")
retry(window_is_visible)
def sleep(self, secs: int) -> None:
time.sleep(secs)
@ -862,23 +787,22 @@ class Machine:
def create_machine(args: Dict[str, Any]) -> Machine:
global log
args["log"] = log
args["redirectSerial"] = os.environ.get("USE_SERIAL", "0") == "1"
return Machine(args)
def start_all() -> None:
global machines
with log.nested("starting all VMs"):
for machine in machines:
machine.start()
logger.info("starting all VMs")
for machine in machines:
machine.start()
def join_all() -> None:
global machines
with log.nested("waiting for all VMs to finish"):
for machine in machines:
machine.wait_for_shutdown()
logger.info("waiting for all VMs to finish")
for machine in machines:
machine.wait_for_shutdown()
def test_script() -> None:
@ -889,13 +813,12 @@ def run_tests() -> None:
global machines
tests = os.environ.get("tests", None)
if tests is not None:
with log.nested("running the VM test script"):
try:
exec(tests, globals())
except Exception as e:
eprint("error: ")
traceback.print_exc()
sys.exit(1)
logger.info("running the VM test script")
try:
exec(tests, globals())
except Exception:
logging.exception("error:")
sys.exit(1)
else:
ptpython.repl.embed(locals(), globals())
@ -908,18 +831,19 @@ def run_tests() -> None:
@contextmanager
def subtest(name: str) -> Iterator[None]:
with log.nested(name):
try:
yield
return True
except Exception as e:
log.log(f'Test "{name}" failed with error: "{e}"')
raise e
logger.info(name)
try:
yield
return True
except Exception as e:
logger.info(f'Test "{name}" failed with error: "{e}"')
raise e
return False
if __name__ == "__main__":
def main() -> None:
global machines
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument(
"-K",
@ -929,8 +853,6 @@ if __name__ == "__main__":
)
(cli_args, vm_scripts) = arg_parser.parse_known_args()
log = Logger()
vlan_nrs = list(dict.fromkeys(os.environ.get("VLANS", "").split()))
vde_sockets = [create_vlan(v) for v in vlan_nrs]
for nr, vde_socket, _, _ in vde_sockets:
@ -941,23 +863,27 @@ if __name__ == "__main__":
if not cli_args.keep_vm_state:
machine.cleanup_statedir()
machine_eval = [
"{0} = machines[{1}]".format(m.name, idx) for idx, m in enumerate(machines)
"global {0}; {0} = machines[{1}]".format(m.name, idx)
for idx, m in enumerate(machines)
]
exec("\n".join(machine_eval))
@atexit.register
def clean_up() -> None:
with log.nested("cleaning up"):
for machine in machines:
if machine.pid is None:
continue
log.log("killing {} (pid {})".format(machine.name, machine.pid))
machine.process.kill()
for _, _, process, _ in vde_sockets:
process.terminate()
log.close()
logger.info("cleaning up")
for machine in machines:
if machine.pid is None:
continue
logger.info(f"killing {machine.name} (pid {machine.pid})")
machine.process.kill()
for _, _, process, _ in vde_sockets:
process.terminate()
tic = time.time()
run_tests()
toc = time.time()
print("test script finished in {:.2f}s".format(toc - tic))
if __name__ == "__main__":
main()

View file

@ -62,7 +62,7 @@ rec {
''
mkdir -p $out
LOGFILE=/dev/null tests='exec(os.environ["testScript"])' ${driver}/bin/nixos-test-driver
tests='exec(os.environ["testScript"])' ${driver}/bin/nixos-test-driver
for i in */xchg/coverage-data; do
mkdir -p $out/coverage-data

View file

@ -29,7 +29,7 @@ log() {
echo "$@" >&2
}
if [ -z "$1" ]; then
if [ "$#" -ne 1 ]; then
log "Usage: ./upload-amazon-image.sh IMAGE_OUTPUT"
exit 1
fi

View file

@ -321,7 +321,7 @@ in
monetdb = 290;
restic = 291;
openvpn = 292;
meguca = 293;
# meguca = 293; # removed 2020-08-21
yarn = 294;
hdfs = 295;
mapred = 296;
@ -622,7 +622,7 @@ in
monetdb = 290;
restic = 291;
openvpn = 292;
meguca = 293;
# meguca = 293; # removed 2020-08-21
yarn = 294;
hdfs = 295;
mapred = 296;

View file

@ -127,7 +127,7 @@ in {
{ LOCATE_PATH = cfg.output;
};
warnings = optional (isMLocate && cfg.localuser != null) "mlocate does not support searching as user other than root"
warnings = optional (isMLocate && cfg.localuser != null) "mlocate does not support the services.locate.localuser option; updatedb will run as root. (Silence with services.locate.localuser = null.)"
++ optional (isFindutils && cfg.pruneNames != []) "findutils locate does not support pruning by directory component"
++ optional (isFindutils && cfg.pruneBindMounts) "findutils locate does not support skipping bind mounts";

View file

@ -178,8 +178,6 @@ in
type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
default = null;
example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
defaultText = literalExample
''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
description = ''
Specifies the platform for which NixOS should be
built. Specify this only if it is different from

View file

@ -300,6 +300,7 @@
./services/desktops/dleyna-renderer.nix
./services/desktops/dleyna-server.nix
./services/desktops/pantheon/files.nix
./services/desktops/espanso.nix
./services/desktops/flatpak.nix
./services/desktops/geoclue2.nix
./services/desktops/gsignond.nix
@ -886,7 +887,6 @@
./services/web-servers/lighttpd/collectd.nix
./services/web-servers/lighttpd/default.nix
./services/web-servers/lighttpd/gitweb.nix
./services/web-servers/meguca.nix
./services/web-servers/mighttpd2.nix
./services/web-servers/minio.nix
./services/web-servers/molly-brown.nix

View file

@ -1,7 +1,7 @@
# A profile with most (vanilla) hardening options enabled by default,
# potentially at the cost of features and performance.
{ lib, pkgs, ... }:
{ config, lib, pkgs, ... }:
with lib;
@ -27,6 +27,9 @@ with lib;
security.forcePageTableIsolation = mkDefault true;
# This is required by podman to run containers in rootless mode.
security.unprivilegedUsernsClone = mkDefault config.virtualisation.containers.enable;
security.virtualisation.flushL1DataCache = mkDefault "always";
security.apparmor.enable = mkDefault true;

View file

@ -48,6 +48,7 @@ with lib;
instead, or any other display manager in NixOS as they all support auto-login.
'')
(mkRemovedOptionModule [ "services" "dnscrypt-proxy" ] "Use services.dnscrypt-proxy2 instead")
(mkRemovedOptionModule [ "services" "meguca" ] "Use meguca has been removed from nixpkgs")
(mkRemovedOptionModule ["hardware" "brightnessctl" ] ''
The brightnessctl module was removed because newer versions of
brightnessctl don't require the udev rules anymore (they can use the

View file

@ -150,6 +150,14 @@ let
'';
};
extraLegoFlags = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Additional global flags to pass to all lego commands.
'';
};
extraLegoRenewFlags = mkOption {
type = types.listOf types.str;
default = [];
@ -157,6 +165,14 @@ let
Additional flags to pass to lego renew.
'';
};
extraLegoRunFlags = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Additional flags to pass to lego run.
'';
};
};
};
@ -313,9 +329,10 @@ in
++ optionals (data.dnsProvider != null && !data.dnsPropagationCheck) [ "--dns.disable-cp" ]
++ concatLists (mapAttrsToList (name: root: [ "-d" name ]) data.extraDomains)
++ (if data.dnsProvider != null then [ "--dns" data.dnsProvider ] else [ "--http" "--http.webroot" data.webroot ])
++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)];
++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)]
++ data.extraLegoFlags;
certOpts = optionals data.ocspMustStaple [ "--must-staple" ];
runOpts = escapeShellArgs (globalOpts ++ [ "run" ] ++ certOpts);
runOpts = escapeShellArgs (globalOpts ++ [ "run" ] ++ certOpts ++ data.extraLegoRunFlags);
renewOpts = escapeShellArgs (globalOpts ++
[ "renew" "--days" (toString cfg.validMinDays) ] ++
certOpts ++ data.extraLegoRenewFlags);

View file

@ -51,7 +51,7 @@ in
};
secretKeyFile = mkOption {
type = types.path;
type = types.nullOr types.path;
default = null;
description = ''
A file containing your secret key. The security of your Duo application is tied to the security of your secret key.

View file

@ -27,6 +27,16 @@ with lib;
'';
};
security.unprivilegedUsernsClone = mkOption {
type = types.bool;
default = false;
description = ''
When disabled, unprivileged users will not be able to create new namespaces.
By default unprivileged user namespaces are disabled.
This option only works in a hardened profile.
'';
};
security.protectKernelImage = mkOption {
type = types.bool;
default = false;
@ -115,6 +125,10 @@ with lib;
];
})
(mkIf config.security.unprivilegedUsernsClone {
boot.kernel.sysctl."kernel.unprivileged_userns_clone" = mkDefault true;
})
(mkIf config.security.protectKernelImage {
# Disable hibernation (allows replacing the running kernel)
boot.kernelParams = [ "nohibernate" ];

View file

@ -47,7 +47,7 @@ in {
enable = mkEnableOption "Icecast server";
hostname = mkOption {
type = types.str;
type = types.nullOr types.str;
description = "DNS name or IP address that will be used for the stream directory lookups or possibily the playlist generation if a Host header is not provided.";
default = config.networking.domain;
};

View file

@ -0,0 +1,25 @@
{ config, lib, pkgs, ... }:
with lib;
let cfg = config.services.espanso;
in {
meta = { maintainers = with lib.maintainers; [ numkem ]; };
options = {
services.espanso = { enable = options.mkEnableOption "Espanso"; };
};
config = mkIf cfg.enable {
systemd.user.services.espanso = {
description = "Espanso daemon";
path = with pkgs; [ espanso libnotify xclip ];
serviceConfig = {
ExecStart = "${pkgs.espanso}/bin/espanso daemon";
Restart = "on-failure";
};
wantedBy = [ "default.target" ];
};
environment.systemPackages = [ pkgs.espanso ];
};
}

View file

@ -15,7 +15,7 @@ let
jupyterhubConfig = pkgs.writeText "jupyterhub_config.py" ''
c.JupyterHub.bind_url = "http://${cfg.host}:${toString cfg.port}"
c.JupyterHub.authentication_class = "${cfg.authentication}"
c.JupyterHub.authenticator_class = "${cfg.authentication}"
c.JupyterHub.spawner_class = "${cfg.spawner}"
c.SystemdSpawner.default_url = '/lab'

View file

@ -12,7 +12,7 @@ in{
config = mkOption {
default = null;
type = types.lines;
type = types.nullOr types.lines;
description = "Fancontrol configuration file content. See <citerefentry><refentrytitle>pwmconfig</refentrytitle><manvolnum>8</manvolnum></citerefentry> from the lm_sensors package.";
example = ''
# Configuration file generated by pwmconfig

View file

@ -103,6 +103,17 @@ in
The temperature target on battery power in Celsius degrees.
'';
};
useTimer = mkOption {
type = types.bool;
default = false;
description = ''
Whether to set a timer that applies the undervolt settings every 30s.
This will cause spam in the journal but might be required for some
hardware under specific conditions.
Enable this if your undervolt settings don't hold.
'';
};
};
config = mkIf cfg.enable {
@ -114,6 +125,11 @@ in
path = [ pkgs.undervolt ];
description = "Intel Undervolting Service";
# Apply undervolt on boot, nixos generation switch and resume
wantedBy = [ "multi-user.target" "post-resume.target" ];
after = [ "post-resume.target" ]; # Not sure why but it won't work without this
serviceConfig = {
Type = "oneshot";
Restart = "no";
@ -121,7 +137,7 @@ in
};
};
systemd.timers.undervolt = {
systemd.timers.undervolt = mkIf cfg.useTimer {
description = "Undervolt timer to ensure voltage settings are always applied";
partOf = [ "undervolt.service" ];
wantedBy = [ "multi-user.target" ];

View file

@ -5,54 +5,93 @@ with lib;
let
cfg = config.services.logrotate;
pathOptions = {
pathOpts = {
options = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable log rotation for this path. This can be used to explicitly disable
logging that has been configured by NixOS.
'';
};
path = mkOption {
type = types.str;
description = "The path to log files to be rotated";
description = ''
The path to log files to be rotated.
'';
};
user = mkOption {
type = types.str;
description = "The user account to use for rotation";
type = with types; nullOr str;
default = null;
description = ''
The user account to use for rotation.
'';
};
group = mkOption {
type = types.str;
description = "The group to use for rotation";
type = with types; nullOr str;
default = null;
description = ''
The group to use for rotation.
'';
};
frequency = mkOption {
type = types.enum [
"daily" "weekly" "monthly" "yearly"
];
type = types.enum [ "daily" "weekly" "monthly" "yearly" ];
default = "daily";
description = "How often to rotate the logs";
description = ''
How often to rotate the logs.
'';
};
keep = mkOption {
type = types.int;
default = 20;
description = "How many rotations to keep";
description = ''
How many rotations to keep.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = "Extra logrotate config options for this path";
description = ''
Extra logrotate config options for this path. Refer to
<link xlink:href="https://linux.die.net/man/8/logrotate"/> for details.
'';
};
priority = mkOption {
type = types.int;
default = 1000;
description = ''
Order of this logrotate block in relation to the others. The semantics are
the same as with `lib.mkOrder`. Smaller values have a greater priority.
'';
};
};
};
pathConfig = options: ''
"${options.path}" {
su ${options.user} ${options.group}
${options.frequency}
config.extraConfig = ''
missingok
notifempty
rotate ${toString options.keep}
${options.extraConfig}
'';
};
mkConf = pathOpts: ''
# generated by NixOS using the `services.logrotate.paths.${pathOpts.name}` attribute set
"${pathOpts.path}" {
${optionalString (pathOpts.user != null || pathOpts.group != null) "su ${pathOpts.user} ${pathOpts.group}"}
${pathOpts.frequency}
rotate ${toString pathOpts.keep}
${pathOpts.extraConfig}
}
'';
configFile = pkgs.writeText "logrotate.conf" (
(concatStringsSep "\n" ((map pathConfig cfg.paths) ++ [cfg.extraConfig]))
);
paths = sortProperties (mapAttrsToList (name: pathOpts: pathOpts // { name = name; }) (filterAttrs (_: pathOpts: pathOpts.enable) cfg.paths));
configFile = pkgs.writeText "logrotate.conf" (concatStringsSep "\n" ((map mkConf paths) ++ [ cfg.extraConfig ]));
in
{
@ -65,41 +104,66 @@ in
enable = mkEnableOption "the logrotate systemd service";
paths = mkOption {
type = types.listOf (types.submodule pathOptions);
default = [];
description = "List of attribute sets with paths to rotate";
example = {
"/var/log/myapp/*.log" = {
user = "myuser";
group = "mygroup";
rotate = "weekly";
keep = 5;
};
};
type = with types; attrsOf (submodule pathOpts);
default = {};
description = ''
Attribute set of paths to rotate. The order each block appears in the generated configuration file
can be controlled by the <link linkend="opt-services.logrotate.paths._name_.priority">priority</link> option
using the same semantics as `lib.mkOrder`. Smaller values have a greater priority.
'';
example = literalExample ''
{
httpd = {
path = "/var/log/httpd/*.log";
user = config.services.httpd.user;
group = config.services.httpd.group;
keep = 7;
};
myapp = {
path = "/var/log/myapp/*.log";
user = "myuser";
group = "mygroup";
frequency = "weekly";
keep = 5;
priority = 1;
};
}
'';
};
extraConfig = mkOption {
default = "";
type = types.lines;
description = ''
Extra contents to add to the logrotate config file.
See https://linux.die.net/man/8/logrotate
Extra contents to append to the logrotate configuration file. Refer to
<link xlink:href="https://linux.die.net/man/8/logrotate"/> for details.
'';
};
};
};
config = mkIf cfg.enable {
systemd.services.logrotate = {
description = "Logrotate Service";
wantedBy = [ "multi-user.target" ];
startAt = "*-*-* *:05:00";
assertions = mapAttrsToList (name: pathOpts:
{ assertion = (pathOpts.user != null) == (pathOpts.group != null);
message = ''
If either of `services.logrotate.paths.${name}.user` or `services.logrotate.paths.${name}.group` are specified then *both* must be specified.
'';
}
) cfg.paths;
serviceConfig.Restart = "no";
serviceConfig.User = "root";
systemd.services.logrotate = {
description = "Logrotate Service";
wantedBy = [ "multi-user.target" ];
startAt = "*-*-* *:05:00";
script = ''
exec ${pkgs.logrotate}/sbin/logrotate ${configFile}
'';
serviceConfig = {
Restart = "no";
User = "root";
};
};
};
}

View file

@ -4,13 +4,9 @@ with lib;
let
cfg = config.services.logstash;
pluginPath = lib.concatStringsSep ":" cfg.plugins;
havePluginPath = lib.length cfg.plugins > 0;
ops = lib.optionalString;
verbosityFlag = "--log.level " + cfg.logLevel;
pluginsPath = "--path.plugins ${pluginPath}";
logstashConf = pkgs.writeText "logstash.conf" ''
input {
${cfg.inputConfig}
@ -173,7 +169,7 @@ in
ExecStart = concatStringsSep " " (filter (s: stringLength s != 0) [
"${cfg.package}/bin/logstash"
"-w ${toString cfg.filterWorkers}"
(ops havePluginPath pluginsPath)
(concatMapStringsSep " " (x: "--path.plugins ${x}") cfg.plugins)
"${verbosityFlag}"
"-f ${logstashConf}"
"--path.settings ${logstashSettingsDir}"

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs, ... }:
{ options, config, lib, pkgs, ... }:
with lib;
@ -83,11 +83,11 @@ let
)
(
optionalString (cfg.mailboxes != []) ''
optionalString (cfg.mailboxes != {}) ''
protocol imap {
namespace inbox {
inbox=yes
${concatStringsSep "\n" (map mailboxConfig cfg.mailboxes)}
${concatStringsSep "\n" (map mailboxConfig (attrValues cfg.mailboxes))}
}
}
''
@ -131,12 +131,13 @@ let
special_use = \${toString mailbox.specialUse}
'' + "}";
mailboxes = { ... }: {
mailboxes = { name, ... }: {
options = {
name = mkOption {
type = types.nullOr (types.strMatching ''[^"]+'');
type = types.strMatching ''[^"]+'';
example = "Spam";
default = null;
default = name;
readOnly = true;
description = "The name of the mailbox.";
};
auto = mkOption {
@ -335,19 +336,11 @@ in
};
mailboxes = mkOption {
type = with types; let m = submodule mailboxes; in either (listOf m) (attrsOf m);
type = with types; coercedTo
(listOf unspecified)
(list: listToAttrs (map (entry: { name = entry.name; value = removeAttrs entry ["name"]; }) list))
(attrsOf (submodule mailboxes));
default = {};
apply = x:
if isList x then warn "Declaring `services.dovecot2.mailboxes' as a list is deprecated and will break eval in 21.03!" x
else mapAttrsToList (name: value:
if value.name != null
then throw ''
When specifying dovecot2 mailboxes as attributes, declaring
a `name'-attribute is prohibited! The name ${value.name} should
be the attribute key!
''
else value // { inherit name; }
) x;
example = literalExample ''
{
Spam = { specialUse = "Junk"; auto = "create"; };
@ -471,6 +464,10 @@ in
environment.systemPackages = [ dovecotPkg ];
warnings = mkIf (any isList options.services.dovecot2.mailboxes.definitions) [
"Declaring `services.dovecot2.mailboxes' as a list is deprecated and will break eval in 21.03! See the release notes for more info for migration."
];
assertions = [
{
assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != [];

View file

@ -172,7 +172,7 @@ in {
};
database = mkOption {
type = types.str;
type = types.nullOr types.str;
default = null;
description = "Database name to store sms data";
};

View file

@ -50,6 +50,12 @@ in
description = "Parse and interpret emoji tags";
};
h1-title = mkOption {
type = types.bool;
default = false;
description = "Use the first h1 as page title";
};
branch = mkOption {
type = types.str;
default = "master";
@ -102,6 +108,7 @@ in
--ref ${cfg.branch} \
${optionalString cfg.mathjax "--mathjax"} \
${optionalString cfg.emoji "--emoji"} \
${optionalString cfg.h1-title "--h1-title"} \
${optionalString (cfg.allowUploads != null) "--allow-uploads ${cfg.allowUploads}"} \
${cfg.stateDir}
'';

View file

@ -68,8 +68,8 @@ in
plugins = mkOption {
default = plugins: [];
defaultText = "plugins: []";
example = literalExample "plugins: [ m3d-fio ]";
description = "Additional plugins.";
example = literalExample "plugins: with plugins; [ m33-fio stlviewer ]";
description = "Additional plugins to be used. Available plugins are passed through the plugins input.";
};
extraConfig = mkOption {

View file

@ -29,13 +29,15 @@ in {
config = mkIf cfg.enable {
systemd.services.ssm-agent = {
users.extraUsers.ssm-user = {};
inherit (cfg.package.meta) description;
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [ fake-lsb-release ];
path = [ fake-lsb-release pkgs.coreutils ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/agent";
ExecStart = "${cfg.package}/bin/amazon-ssm-agent";
KillMode = "process";
Restart = "on-failure";
RestartSec = "15min";

View file

@ -69,7 +69,7 @@ in {
mode = "0400";
};
system.nssModules = pkgs.sssd;
system.nssModules = [ pkgs.sssd ];
system.nssDatabases = {
group = [ "sss" ];
passwd = [ "sss" ];
@ -92,4 +92,6 @@ in {
services.openssh.authorizedKeysCommand = "/etc/ssh/authorized_keys_command";
services.openssh.authorizedKeysCommandUser = "nobody";
})];
meta.maintainers = with maintainers; [ bbigras ];
}

View file

@ -4,19 +4,29 @@ with lib;
let
cfg = config.services.monit;
extraConfig = pkgs.writeText "monitConfig" cfg.extraConfig;
in
{
imports = [
(mkRenamedOptionModule [ "services" "monit" "config" ] ["services" "monit" "extraConfig" ])
];
options.services.monit = {
enable = mkEnableOption "Monit";
config = mkOption {
type = types.lines;
default = "";
description = "monitrc content";
configFiles = mkOption {
type = types.listOf types.path;
default = [];
description = "List of paths to be included in the monitrc file";
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = "Additional monit config as string";
};
};
config = mkIf cfg.enable {
@ -24,7 +34,7 @@ in
environment.systemPackages = [ pkgs.monit ];
environment.etc.monitrc = {
text = cfg.config;
text = concatMapStringsSep "\n" (path: "include ${path}") (cfg.configFiles ++ [extraConfig]);
mode = "0400";
};

View file

@ -46,7 +46,7 @@ let
cmdlineArgs = cfg.extraFlags ++ [
"--storage.tsdb.path=${workingDir}/data/"
"--config.file=${prometheusYml}"
"--web.listen-address=${cfg.listenAddress}"
"--web.listen-address=${cfg.listenAddress}:${builtins.toString cfg.port}"
"--alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}"
"--alertmanager.timeout=${toString cfg.alertmanagerTimeout}s"
] ++
@ -489,9 +489,17 @@ in {
'';
};
port = mkOption {
type = types.port;
default = 9090;
description = ''
Port to listen on.
'';
};
listenAddress = mkOption {
type = types.str;
default = "0.0.0.0:9090";
default = "0.0.0.0";
description = ''
Address to listen on for the web interface, API, and telemetry.
'';
@ -619,6 +627,21 @@ in {
};
config = mkIf cfg.enable {
assertions = [
( let
legacy = builtins.match "(.*):(.*)" cfg.listenAddress;
in {
assertion = legacy == null;
message = ''
Do not specify the port for Prometheus to listen on in the
listenAddress option; use the port option instead:
services.prometheus.listenAddress = ${builtins.elemAt legacy 0};
services.prometheus.port = ${builtins.elemAt legacy 1};
'';
}
)
];
users.groups.prometheus.gid = config.ids.gids.prometheus;
users.users.prometheus = {
description = "Prometheus daemon user";

View file

@ -20,7 +20,7 @@ let
${pkgs.coreutils}/bin/cat << EOF
From: smartd on ${host} <${nm.sender}>
To: undisclosed-recipients:;
Subject: SMART error on $SMARTD_DEVICESTRING: $SMARTD_FAILTYPE
Subject: $SMARTD_SUBJECT
$SMARTD_FULLMESSAGE
EOF
@ -239,11 +239,7 @@ in
systemd.services.smartd = {
description = "S.M.A.R.T. Daemon";
wantedBy = [ "multi-user.target" ];
path = [ pkgs.nettools ]; # for hostname and dnsdomanname calls in smartd
serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd ${lib.concatStringsSep " " cfg.extraOptions} --no-fork --configfile=${smartdConf}";
};

View file

@ -5,8 +5,8 @@ let
pgsql = config.services.postgresql;
mysql = config.services.mysql;
inherit (lib) mkDefault mkEnableOption mkIf mkMerge mkOption;
inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
inherit (lib) mkAfter mkDefault mkEnableOption mkIf mkMerge mkOption;
inherit (lib) attrValues concatMapStringsSep getName literalExample optional optionalAttrs optionalString types;
inherit (lib.generators) toKeyValue;
user = "zabbix";
@ -232,14 +232,15 @@ in
services.mysql = optionalAttrs mysqlLocal {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
systemd.services.mysql.postStart = mkAfter (optionalString mysqlLocal ''
( echo "CREATE DATABASE IF NOT EXISTS \`${cfg.database.name}\` CHARACTER SET utf8 COLLATE utf8_bin;"
echo "CREATE USER IF NOT EXISTS '${cfg.database.user}'@'localhost' IDENTIFIED WITH ${if (getName config.services.mysql.package == getName pkgs.mariadb) then "unix_socket" else "auth_socket"};"
echo "GRANT ALL PRIVILEGES ON \`${cfg.database.name}\`.* TO '${cfg.database.user}'@'localhost';"
) | ${config.services.mysql.package}/bin/mysql -N
'');
services.postgresql = optionalAttrs pgsqlLocal {
enable = true;
ensureDatabases = [ cfg.database.name ];

View file

@ -5,8 +5,8 @@ let
pgsql = config.services.postgresql;
mysql = config.services.mysql;
inherit (lib) mkDefault mkEnableOption mkIf mkMerge mkOption;
inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
inherit (lib) mkAfter mkDefault mkEnableOption mkIf mkMerge mkOption;
inherit (lib) attrValues concatMapStringsSep getName literalExample optional optionalAttrs optionalString types;
inherit (lib.generators) toKeyValue;
user = "zabbix";
@ -220,14 +220,15 @@ in
services.mysql = optionalAttrs mysqlLocal {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
systemd.services.mysql.postStart = mkAfter (optionalString mysqlLocal ''
( echo "CREATE DATABASE IF NOT EXISTS \`${cfg.database.name}\` CHARACTER SET utf8 COLLATE utf8_bin;"
echo "CREATE USER IF NOT EXISTS '${cfg.database.user}'@'localhost' IDENTIFIED WITH ${if (getName config.services.mysql.package == getName pkgs.mariadb) then "unix_socket" else "auth_socket"};"
echo "GRANT ALL PRIVILEGES ON \`${cfg.database.name}\`.* TO '${cfg.database.user}'@'localhost';"
) | ${config.services.mysql.package}/bin/mysql -N
'');
services.postgresql = optionalAttrs pgsqlLocal {
enable = true;
ensureDatabases = [ cfg.database.name ];

View file

@ -83,14 +83,14 @@ in {
};
dataStorageSpace = mkOption {
type = types.str;
type = types.nullOr types.str;
default = null;
example = "/data/storage";
description = "Directory for data storage.";
};
metadataStorageSpace = mkOption {
type = types.str;
type = types.nullOr types.str;
default = null;
example = "/data/meta";
description = "Directory for meta data storage.";

View file

@ -87,7 +87,7 @@ in
};
rpc.password = mkOption {
type = types.str;
type = types.nullOr types.str;
default = null;
description = ''
Password for RPC connections.

View file

@ -89,7 +89,7 @@ in
};
rpc.password = mkOption {
type = types.str;
type = types.nullOr types.str;
default = null;
description = ''
Password for RPC connections.

View file

@ -11,8 +11,13 @@ let
method = cfg.encryptionMethod;
mode = cfg.mode;
user = "nobody";
fast_open = true;
} // optionalAttrs (cfg.password != null) { password = cfg.password; };
fast_open = cfg.fastOpen;
} // optionalAttrs (cfg.plugin != null) {
plugin = cfg.plugin;
plugin_opts = cfg.pluginOpts;
} // optionalAttrs (cfg.password != null) {
password = cfg.password;
};
configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
@ -74,6 +79,14 @@ in
'';
};
fastOpen = mkOption {
type = types.bool;
default = true;
description = ''
use TCP fast-open
'';
};
encryptionMethod = mkOption {
type = types.str;
default = "chacha20-ietf-poly1305";
@ -82,6 +95,23 @@ in
'';
};
plugin = mkOption {
type = types.nullOr types.str;
default = null;
example = "\${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin";
description = ''
SIP003 plugin for shadowsocks
'';
};
pluginOpts = mkOption {
type = types.str;
default = "";
example = "server;host=example.com";
description = ''
Options to pass to the plugin if one was specified
'';
};
};
};
@ -99,7 +129,7 @@ in
description = "shadowsocks-libev Daemon";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq;
path = [ pkgs.shadowsocks-libev cfg.plugin ] ++ optional (cfg.passwordFile != null) pkgs.jq;
serviceConfig.PrivateTmp = true;
script = ''
${optionalString (cfg.passwordFile != null) ''

View file

@ -233,6 +233,9 @@ in {
path = [ pkgs.wpa_supplicant ];
script = ''
if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]
then echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
fi
iface_args="-s -u -D${cfg.driver} -c ${configFile}"
${if ifaces == [] then ''
for i in $(cd /sys/class/net && echo *); do

View file

@ -52,6 +52,14 @@ in
'';
};
lockMessage = mkOption {
type = types.str;
default = "";
description = ''
Message to show on physlock login terminal.
'';
};
lockOn = {
suspend = mkOption {
@ -111,7 +119,7 @@ in
++ cfg.lockOn.extraTargets;
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}";
ExecStart = "${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}${optionalString (cfg.lockMessage != "") " -p \"${cfg.lockMessage}\""}";
};
};

View file

@ -77,7 +77,6 @@ in {
// Paths
WOSendMail = "/run/wrappers/bin/sendmail";
SOGoMailSpoolPath = "/var/lib/sogo/spool";
SOGoZipPath = "${pkgs.zip}/bin/zip";
// Enable CSRF protection
SOGoXSRFValidationEnabled = YES;
// Remove dates from log (jornald does that)

View file

@ -661,6 +661,25 @@ in
pkg
];
services.logrotate = optionalAttrs (cfg.logFormat != "none") {
enable = mkDefault true;
paths.httpd = {
path = "${cfg.logDir}/*.log";
user = cfg.user;
group = cfg.group;
frequency = "daily";
keep = 28;
extraConfig = ''
sharedscripts
compress
delaycompress
postrotate
systemctl reload httpd.service > /dev/null 2>/dev/null || true
endscript
'';
};
};
services.httpd.phpOptions =
''
; Needed for PHP's mail() function.

View file

@ -1,174 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.meguca;
postgres = config.services.postgresql;
in with lib; {
options.services.meguca = {
enable = mkEnableOption "meguca";
dataDir = mkOption {
type = types.path;
default = "/var/lib/meguca";
example = "/home/okina/meguca";
description = "Location where meguca stores it's database and links.";
};
password = mkOption {
type = types.str;
default = "meguca";
example = "dumbpass";
description = "Password for the meguca database.";
};
passwordFile = mkOption {
type = types.path;
default = "/run/keys/meguca-password-file";
example = "/home/okina/meguca/keys/pass";
description = "Password file for the meguca database.";
};
reverseProxy = mkOption {
type = types.nullOr types.str;
default = null;
example = "192.168.1.5";
description = "Reverse proxy IP.";
};
sslCertificate = mkOption {
type = types.nullOr types.str;
default = null;
example = "/home/okina/meguca/ssl.cert";
description = "Path to the SSL certificate.";
};
listenAddress = mkOption {
type = types.nullOr types.str;
default = null;
example = "127.0.0.1:8000";
description = "Listen on a specific IP address and port.";
};
cacheSize = mkOption {
type = types.nullOr types.int;
default = null;
example = 256;
description = "Cache size in MB.";
};
postgresArgs = mkOption {
type = types.str;
example = "user=meguca password=dumbpass dbname=meguca sslmode=disable";
description = "Postgresql connection arguments.";
};
postgresArgsFile = mkOption {
type = types.path;
default = "/run/keys/meguca-postgres-args";
example = "/home/okina/meguca/keys/postgres";
description = "Postgresql connection arguments file.";
};
compressTraffic = mkOption {
type = types.bool;
default = false;
description = "Compress all traffic with gzip.";
};
assumeReverseProxy = mkOption {
type = types.bool;
default = false;
description = "Assume the server is behind a reverse proxy, when resolving client IPs.";
};
httpsOnly = mkOption {
type = types.bool;
default = false;
description = "Serve and listen only through HTTPS.";
};
videoPaths = mkOption {
type = types.listOf types.path;
default = [];
example = [ "/home/okina/Videos/tehe_pero.webm" ];
description = "Videos that will be symlinked into www/videos.";
};
};
config = mkIf cfg.enable {
security.sudo.enable = cfg.enable;
services.postgresql.enable = cfg.enable;
services.postgresql.package = pkgs.postgresql_11;
services.meguca.passwordFile = mkDefault (pkgs.writeText "meguca-password-file" cfg.password);
services.meguca.postgresArgsFile = mkDefault (pkgs.writeText "meguca-postgres-args" cfg.postgresArgs);
services.meguca.postgresArgs = mkDefault "user=meguca password=${cfg.password} dbname=meguca sslmode=disable";
systemd.services.meguca = {
description = "meguca";
after = [ "network.target" "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
# Ensure folder exists or create it and links and permissions are correct
mkdir -p ${escapeShellArg cfg.dataDir}/www
rm -rf ${escapeShellArg cfg.dataDir}/www/videos
ln -sf ${pkgs.meguca}/share/meguca/www/* ${escapeShellArg cfg.dataDir}/www
unlink ${escapeShellArg cfg.dataDir}/www/videos
mkdir -p ${escapeShellArg cfg.dataDir}/www/videos
for vid in ${escapeShellArg cfg.videoPaths}; do
ln -sf $vid ${escapeShellArg cfg.dataDir}/www/videos
done
chmod 750 ${escapeShellArg cfg.dataDir}
chown -R meguca:meguca ${escapeShellArg cfg.dataDir}
# Ensure the database is correct or create it
${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createuser \
-SDR meguca || true
${pkgs.sudo}/bin/sudo -u ${postgres.superUser} ${postgres.package}/bin/createdb \
-T template0 -E UTF8 -O meguca meguca || true
${pkgs.sudo}/bin/sudo -u meguca ${postgres.package}/bin/psql \
-c "ALTER ROLE meguca WITH PASSWORD '$(cat ${escapeShellArg cfg.passwordFile})';" || true
'';
script = ''
cd ${escapeShellArg cfg.dataDir}
${pkgs.meguca}/bin/meguca -d "$(cat ${escapeShellArg cfg.postgresArgsFile})"''
+ optionalString (cfg.reverseProxy != null) " -R ${cfg.reverseProxy}"
+ optionalString (cfg.sslCertificate != null) " -S ${cfg.sslCertificate}"
+ optionalString (cfg.listenAddress != null) " -a ${cfg.listenAddress}"
+ optionalString (cfg.cacheSize != null) " -c ${toString cfg.cacheSize}"
+ optionalString (cfg.compressTraffic) " -g"
+ optionalString (cfg.assumeReverseProxy) " -r"
+ optionalString (cfg.httpsOnly) " -s" + " start";
serviceConfig = {
PermissionsStartOnly = true;
Type = "forking";
User = "meguca";
Group = "meguca";
ExecStop = "${pkgs.meguca}/bin/meguca stop";
};
};
users = {
groups.meguca.gid = config.ids.gids.meguca;
users.meguca = {
description = "meguca server service user";
home = cfg.dataDir;
createHome = true;
group = "meguca";
uid = config.ids.uids.meguca;
};
};
};
imports = [
(mkRenamedOptionModule [ "services" "meguca" "baseDir" ] [ "services" "meguca" "dataDir" ])
];
meta.maintainers = with maintainers; [ chiiruno ];
}

View file

@ -61,7 +61,8 @@ in
"--kill"
] ++ cfg.extraOptions);
ExecStop = "${pkgs.procps}/bin/pkill imwheel";
Restart = "on-failure";
RestartSec = 3;
Restart = "always";
};
};
};

View file

@ -641,7 +641,7 @@ in
credential = mkOption {
default = null;
example = "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2";
type = types.str;
type = types.nullOr types.str;
description = "The FIDO2 credential ID.";
};

View file

@ -378,12 +378,14 @@ mountFS() {
mkdir -p "/mnt-root$mountPoint"
# For CIFS mounts, retry a few times before giving up.
# For ZFS and CIFS mounts, retry a few times before giving up.
# We do this for ZFS as a workaround for issue NixOS/nixpkgs#25383.
local n=0
while true; do
mount "/mnt-root$mountPoint" && break
if [ "$fsType" != cifs -o "$n" -ge 10 ]; then fail; break; fi
if [ \( "$fsType" != cifs -a "$fsType" != zfs \) -o "$n" -ge 10 ]; then fail; break; fi
echo "retrying..."
sleep 1
n=$((n + 1))
done

View file

@ -36,7 +36,7 @@ let
set -euo pipefail
declare -A seen
declare -a left
left=()
patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
@ -48,7 +48,7 @@ let
done
}
add_needed $1
add_needed "$1"
while [ ''${#left[@]} -ne 0 ]; do
next=''${left[0]}
@ -87,7 +87,9 @@ let
# copy what we need. Instead of using statically linked binaries,
# we just copy what we need from Glibc and use patchelf to make it
# work.
extraUtils = pkgs.runCommandCC "extra-utils"
extraUtils = let
# Use lvm2 without udev support, which is the same lvm2 we already have in the closure anyways
lvm2 = pkgs.lvm2.override { udev = null; }; in pkgs.runCommandCC "extra-utils"
{ nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
}
@ -111,8 +113,8 @@ let
copy_bin_and_libs ${pkgs.utillinux}/sbin/blkid
# Copy dmsetup and lvm.
copy_bin_and_libs ${getBin pkgs.lvm2}/bin/dmsetup
copy_bin_and_libs ${getBin pkgs.lvm2}/bin/lvm
copy_bin_and_libs ${getBin lvm2}/bin/dmsetup
copy_bin_and_libs ${getBin lvm2}/bin/lvm
# Add RAID mdadm tool.
copy_bin_and_libs ${pkgs.mdadm}/sbin/mdadm

View file

@ -25,7 +25,7 @@ let
"nss-lookup.target"
"nss-user-lookup.target"
"time-sync.target"
#"cryptsetup.target"
"cryptsetup.target"
"sigpwr.target"
"timers.target"
"paths.target"

View file

@ -1129,7 +1129,6 @@ in
++ optionals config.networking.wireless.enable [
pkgs.wirelesstools # FIXME: obsolete?
pkgs.iw
pkgs.rfkill
]
++ bridgeStp;

View file

@ -110,6 +110,7 @@ in
'';
environment.etc."cni/net.d/10-crio-bridge.conf".source = copyFile "${pkgs.cri-o-unwrapped.src}/contrib/cni/10-crio-bridge.conf";
environment.etc."cni/net.d/99-loopback.conf".source = copyFile "${pkgs.cri-o-unwrapped.src}/contrib/cni/99-loopback.conf";
# Enable common /etc/containers configuration
virtualisation.containers.enable = true;

View file

@ -1,134 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
with builtins;
let
cfg = config.virtualisation;
sanitizeImageName = image: replaceStrings ["/"] ["-"] image.imageName;
hash = drv: head (split "-" (baseNameOf drv.outPath));
# The label of an ext4 FS is limited to 16 bytes
labelFromImage = image: substring 0 16 (hash image);
# The Docker image is loaded and some files from /var/lib/docker/
# are written into a qcow image.
preload = image: pkgs.vmTools.runInLinuxVM (
pkgs.runCommand "docker-preload-image-${sanitizeImageName image}" {
buildInputs = with pkgs; [ docker e2fsprogs utillinux curl kmod ];
preVM = pkgs.vmTools.createEmptyImage {
size = cfg.dockerPreloader.qcowSize;
fullName = "docker-deamon-image.qcow2";
};
}
''
mkfs.ext4 /dev/vda
e2label /dev/vda ${labelFromImage image}
mkdir -p /var/lib/docker
mount -t ext4 /dev/vda /var/lib/docker
modprobe overlay
# from https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
cd /sys/fs/cgroup
for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
mkdir -p $sys
if ! mountpoint -q $sys; then
if ! mount -n -t cgroup -o $sys cgroup $sys; then
rmdir $sys || true
fi
fi
done
dockerd -H tcp://127.0.0.1:5555 -H unix:///var/run/docker.sock &
until $(curl --output /dev/null --silent --connect-timeout 2 http://127.0.0.1:5555); do
printf '.'
sleep 1
done
docker load -i ${image}
kill %1
find /var/lib/docker/ -maxdepth 1 -mindepth 1 -not -name "image" -not -name "overlay2" | xargs rm -rf
'');
preloadedImages = map preload cfg.dockerPreloader.images;
in
{
options.virtualisation.dockerPreloader = {
images = mkOption {
default = [ ];
type = types.listOf types.package;
description =
''
A list of Docker images to preload (in the /var/lib/docker directory).
'';
};
qcowSize = mkOption {
default = 1024;
type = types.int;
description =
''
The size (MB) of qcow files.
'';
};
};
config = mkIf (cfg.dockerPreloader.images != []) {
assertions = [{
# If docker.storageDriver is null, Docker choose the storage
# driver. So, in this case, we cannot be sure overlay2 is used.
assertion = cfg.docker.storageDriver == "overlay2"
|| cfg.docker.storageDriver == "overlay"
|| cfg.docker.storageDriver == null;
message = "The Docker image Preloader only works with overlay2 storage driver!";
}];
virtualisation.qemu.options =
map (path: "-drive if=virtio,file=${path}/disk-image.qcow2,readonly,media=cdrom,format=qcow2")
preloadedImages;
# All attached QCOW files are mounted and their contents are linked
# to /var/lib/docker/ in order to make image available.
systemd.services.docker-preloader = {
description = "Preloaded Docker images";
wantedBy = ["docker.service"];
after = ["network.target"];
path = with pkgs; [ mount rsync jq ];
script = ''
mkdir -p /var/lib/docker/overlay2/l /var/lib/docker/image/overlay2
echo '{}' > /tmp/repositories.json
for i in ${concatStringsSep " " (map labelFromImage cfg.dockerPreloader.images)}; do
mkdir -p /mnt/docker-images/$i
# The ext4 label is limited to 16 bytes
mount /dev/disk/by-label/$(echo $i | cut -c1-16) -o ro,noload /mnt/docker-images/$i
find /mnt/docker-images/$i/overlay2/ -maxdepth 1 -mindepth 1 -not -name l\
-exec ln -s '{}' /var/lib/docker/overlay2/ \;
cp -P /mnt/docker-images/$i/overlay2/l/* /var/lib/docker/overlay2/l/
rsync -a /mnt/docker-images/$i/image/ /var/lib/docker/image/
# Accumulate image definitions
cp /tmp/repositories.json /tmp/repositories.json.tmp
jq -s '.[0] * .[1]' \
/tmp/repositories.json.tmp \
/mnt/docker-images/$i/image/overlay2/repositories.json \
> /tmp/repositories.json
done
mv /tmp/repositories.json /var/lib/docker/image/overlay2/repositories.json
'';
serviceConfig = {
Type = "oneshot";
};
};
};
}

View file

@ -32,7 +32,7 @@ in
};
package = mkOption {
type = types.package;
type = types.nullOr types.package;
default = config.boot.kernelPackages.prl-tools;
defaultText = "config.boot.kernelPackages.prl-tools";
example = literalExample "config.boot.kernelPackages.prl-tools";

View file

@ -264,7 +264,6 @@ in
{
imports = [
../profiles/qemu-guest.nix
./docker-preloader.nix
];
options = {

View file

@ -34,6 +34,7 @@ in
bind = handleTest ./bind.nix {};
bitcoind = handleTest ./bitcoind.nix {};
bittorrent = handleTest ./bittorrent.nix {};
bitwarden = handleTest ./bitwarden.nix {};
blockbook-frontend = handleTest ./blockbook-frontend.nix {};
buildkite-agents = handleTest ./buildkite-agents.nix {};
boot = handleTestOn ["x86_64-linux"] ./boot.nix {}; # syslinux is unsupported on aarch64
@ -65,11 +66,13 @@ in
containers-macvlans = handleTest ./containers-macvlans.nix {};
containers-physical_interfaces = handleTest ./containers-physical_interfaces.nix {};
containers-portforward = handleTest ./containers-portforward.nix {};
containers-reloadable = handleTest ./containers-reloadable.nix {};
containers-restart_networking = handleTest ./containers-restart_networking.nix {};
containers-tmpfs = handleTest ./containers-tmpfs.nix {};
convos = handleTest ./convos.nix {};
corerad = handleTest ./corerad.nix {};
couchdb = handleTest ./couchdb.nix {};
cri-o = handleTestOn ["x86_64-linux"] ./cri-o.nix {};
deluge = handleTest ./deluge.nix {};
dhparams = handleTest ./dhparams.nix {};
dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
@ -78,15 +81,13 @@ in
docker = handleTestOn ["x86_64-linux"] ./docker.nix {};
oci-containers = handleTestOn ["x86_64-linux"] ./oci-containers.nix {};
docker-edge = handleTestOn ["x86_64-linux"] ./docker-edge.nix {};
docker-preloader = handleTestOn ["x86_64-linux"] ./docker-preloader.nix {};
docker-registry = handleTest ./docker-registry.nix {};
docker-tools = handleTestOn ["x86_64-linux"] ./docker-tools.nix {};
docker-tools-overlay = handleTestOn ["x86_64-linux"] ./docker-tools-overlay.nix {};
documize = handleTest ./documize.nix {};
dokuwiki = handleTest ./dokuwiki.nix {};
dovecot = handleTest ./dovecot.nix {};
# ec2-config doesn't work in a sandbox as the simulated ec2 instance needs network access
#ec2-config = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-config or {};
ec2-config = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-config or {};
ec2-nixops = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-nixops or {};
ecryptfs = handleTest ./ecryptfs.nix {};
ejabberd = handleTest ./xmpp/ejabberd.nix {};
@ -306,6 +307,7 @@ in
sanoid = handleTest ./sanoid.nix {};
sddm = handleTest ./sddm.nix {};
service-runner = handleTest ./service-runner.nix {};
shadowsocks = handleTest ./shadowsocks.nix {};
shattered-pixel-dungeon = handleTest ./shattered-pixel-dungeon.nix {};
shiori = handleTest ./shiori.nix {};
signal-desktop = handleTest ./signal-desktop.nix {};
@ -320,6 +322,7 @@ in
spike = handleTest ./spike.nix {};
sonarr = handleTest ./sonarr.nix {};
sslh = handleTest ./sslh.nix {};
sssd = handleTestOn ["x86_64-linux"] ./sssd.nix {};
strongswan-swanctl = handleTest ./strongswan-swanctl.nix {};
sudo = handleTest ./sudo.nix {};
switchTest = handleTest ./switch-test.nix {};

188
nixos/tests/bitwarden.nix Normal file
View file

@ -0,0 +1,188 @@
{ system ? builtins.currentSystem
, config ? { }
, pkgs ? import ../.. { inherit system config; }
}:
# These tests will:
# * Set up a bitwarden-rs server
# * Have Firefox use the web vault to create an account, log in, and save a password to the valut
# * Have the bw cli log in and read that password from the vault
#
# Note that Firefox must be on the same machine as the server for WebCrypto APIs to be available (or HTTPS must be configured)
#
# The same tests should work without modification on the official bitwarden server, if we ever package that.
with import ../lib/testing-python.nix { inherit system pkgs; };
with pkgs.lib;
let
backends = [ "sqlite" "mysql" "postgresql" ];
dbPassword = "please_dont_hack";
userEmail = "meow@example.com";
userPassword = "also_super_secret_ZJWpBKZi668QGt"; # Must be complex to avoid interstitial warning on the signup page
storedPassword = "seeeecret";
makeBitwardenTest = backend: makeTest {
name = "bitwarden_rs-${backend}";
meta = {
maintainers = with pkgs.stdenv.lib.maintainers; [ jjjollyjim ];
};
nodes = {
server = { pkgs, ... }:
let backendConfig = {
mysql = {
services.mysql = {
enable = true;
initialScript = pkgs.writeText "mysql-init.sql" ''
CREATE DATABASE bitwarden;
CREATE USER 'bitwardenuser'@'localhost' IDENTIFIED BY '${dbPassword}';
GRANT ALL ON `bitwarden`.* TO 'bitwardenuser'@'localhost';
FLUSH PRIVILEGES;
'';
package = pkgs.mysql;
};
services.bitwarden_rs.config.databaseUrl = "mysql://bitwardenuser:${dbPassword}@localhost/bitwarden";
systemd.services.bitwarden_rs.after = [ "mysql.service" ];
};
postgresql = {
services.postgresql = {
enable = true;
initialScript = pkgs.writeText "postgresql-init.sql" ''
CREATE DATABASE bitwarden;
CREATE USER bitwardenuser WITH PASSWORD '${dbPassword}';
GRANT ALL PRIVILEGES ON DATABASE bitwarden TO bitwardenuser;
'';
};
services.bitwarden_rs.config.databaseUrl = "postgresql://bitwardenuser:${dbPassword}@localhost/bitwarden";
systemd.services.bitwarden_rs.after = [ "postgresql.service" ];
};
sqlite = { };
};
in
mkMerge [
backendConfig.${backend}
{
services.bitwarden_rs = {
enable = true;
dbBackend = backend;
config.rocketPort = 80;
};
networking.firewall.allowedTCPPorts = [ 80 ];
environment.systemPackages =
let
testRunner = pkgs.writers.writePython3Bin "test-runner"
{
libraries = [ pkgs.python3Packages.selenium ];
} ''
from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.add_argument('--headless')
driver = Firefox(options=options)
driver.implicitly_wait(20)
driver.get('http://localhost/#/register')
wait = WebDriverWait(driver, 10)
wait.until(EC.title_contains("Create Account"))
driver.find_element_by_css_selector('input#email').send_keys(
'${userEmail}'
)
driver.find_element_by_css_selector('input#name').send_keys(
'A Cat'
)
driver.find_element_by_css_selector('input#masterPassword').send_keys(
'${userPassword}'
)
driver.find_element_by_css_selector('input#masterPasswordRetype').send_keys(
'${userPassword}'
)
driver.find_element_by_xpath("//button[contains(., 'Submit')]").click()
wait.until_not(EC.title_contains("Create Account"))
driver.find_element_by_css_selector('input#masterPassword').send_keys(
'${userPassword}'
)
driver.find_element_by_xpath("//button[contains(., 'Log In')]").click()
wait.until(EC.title_contains("My Vault"))
driver.find_element_by_xpath("//button[contains(., 'Add Item')]").click()
driver.find_element_by_css_selector('input#name').send_keys(
'secrets'
)
driver.find_element_by_css_selector('input#loginPassword').send_keys(
'${storedPassword}'
)
driver.find_element_by_xpath("//button[contains(., 'Save')]").click()
'';
in
[ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ];
virtualisation.memorySize = 768;
}
];
client = { pkgs, ... }:
{
environment.systemPackages = [ pkgs.bitwarden-cli ];
};
};
testScript = ''
start_all()
server.wait_for_unit("bitwarden_rs.service")
server.wait_for_open_port(80)
with subtest("configure the cli"):
client.succeed("bw --nointeraction config server http://server")
with subtest("can't login to nonexistant account"):
client.fail(
"bw --nointeraction --raw login ${userEmail} ${userPassword}"
)
with subtest("use the web interface to sign up, log in, and save a password"):
server.succeed("PYTHONUNBUFFERED=1 test-runner | systemd-cat -t test-runner")
with subtest("log in with the cli"):
key = client.succeed(
"bw --nointeraction --raw login ${userEmail} ${userPassword}"
).strip()
with subtest("sync with the cli"):
client.succeed(f"bw --nointeraction --raw --session {key} sync -f")
with subtest("get the password with the cli"):
password = client.succeed(
f"bw --nointeraction --raw --session {key} list items | ${pkgs.jq}/bin/jq -r .[].login.password"
)
assert password.strip() == "${storedPassword}"
'';
};
in
builtins.listToAttrs (
map
(backend: { name = backend; value = makeBitwardenTest backend; })
backends
)

View file

@ -20,30 +20,44 @@ with pkgs.lib;
in makeTest {
name = "ec2-" + name;
nodes = {};
testScript =
''
my $imageDir = ($ENV{'TMPDIR'} // "/tmp") . "/vm-state-machine";
mkdir $imageDir, 0700;
my $diskImage = "$imageDir/machine.qcow2";
system("qemu-img create -f qcow2 -o backing_file=${image} $diskImage") == 0 or die;
system("qemu-img resize $diskImage 10G") == 0 or die;
testScript = ''
import os
import subprocess
# Note: we use net=169.0.0.0/8 rather than
# net=169.254.0.0/16 to prevent dhcpcd from getting horribly
# confused. (It would get a DHCP lease in the 169.254.*
# range, which it would then configure and prompty delete
# again when it deletes link-local addresses.) Ideally we'd
# turn off the DHCP server, but qemu does not have an option
# to do that.
my $startCommand = "qemu-kvm -m 1024";
$startCommand .= " -device virtio-net-pci,netdev=vlan0";
$startCommand .= " -netdev 'user,id=vlan0,net=169.0.0.0/8,guestfwd=tcp:169.254.169.254:80-cmd:${pkgs.micro-httpd}/bin/micro_httpd ${metaData}'";
$startCommand .= " -drive file=$diskImage,if=virtio,werror=report";
$startCommand .= " \$QEMU_OPTS";
image_dir = os.path.join(
os.environ.get("TMPDIR", tempfile.gettempdir()), "tmp", "vm-state-machine"
)
os.makedirs(image_dir, mode=0o700, exist_ok=True)
disk_image = os.path.join(image_dir, "machine.qcow2")
subprocess.check_call(
[
"qemu-img",
"create",
"-f",
"qcow2",
"-o",
"backing_file=${image}",
disk_image,
]
)
subprocess.check_call(["qemu-img", "resize", disk_image, "10G"])
my $machine = createMachine({ startCommand => $startCommand });
# Note: we use net=169.0.0.0/8 rather than
# net=169.254.0.0/16 to prevent dhcpcd from getting horribly
# confused. (It would get a DHCP lease in the 169.254.*
# range, which it would then configure and prompty delete
# again when it deletes link-local addresses.) Ideally we'd
# turn off the DHCP server, but qemu does not have an option
# to do that.
start_command = (
"qemu-kvm -m 1024"
+ " -device virtio-net-pci,netdev=vlan0"
+ " -netdev 'user,id=vlan0,net=169.0.0.0/8,guestfwd=tcp:169.254.169.254:80-cmd:${pkgs.micro-httpd}/bin/micro_httpd ${metaData}'"
+ f" -drive file={disk_image},if=virtio,werror=report"
+ " $QEMU_OPTS"
)
${script}
'';
machine = create_machine({"startCommand": start_command})
'' + script;
};
}

View file

@ -9,13 +9,13 @@ let
};
};
# prevent make-test.nix to change IP
# prevent make-test-python.nix to change IP
networking.interfaces = {
eth1.ipv4.addresses = lib.mkOverride 0 [ ];
};
};
in {
name = "cotnainers-reloadable";
name = "containers-reloadable";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ danbst ];
};

19
nixos/tests/cri-o.nix Normal file
View file

@ -0,0 +1,19 @@
# This test runs CRI-O and verifies via critest
import ./make-test-python.nix ({ pkgs, ... }: {
name = "cri-o";
maintainers = with pkgs.stdenv.lib.maintainers; teams.podman.members;
nodes = {
crio = {
virtualisation.cri-o.enable = true;
};
};
testScript = ''
start_all()
crio.wait_for_unit("crio.service")
crio.succeed(
"critest --ginkgo.focus='Runtime info' --runtime-endpoint unix:///var/run/crio/crio.sock"
)
'';
})

View file

@ -1,27 +0,0 @@
import ./make-test.nix ({ pkgs, ...} : {
name = "docker-preloader";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ lewo ];
};
nodes = {
docker =
{ pkgs, ... }:
{
virtualisation.docker.enable = true;
virtualisation.dockerPreloader.images = [ pkgs.dockerTools.examples.nix pkgs.dockerTools.examples.bash ];
services.openssh.enable = true;
services.openssh.permitRootLogin = "yes";
services.openssh.extraConfig = "PermitEmptyPasswords yes";
users.extraUsers.root.password = "";
};
};
testScript = ''
startAll;
$docker->waitForUnit("sockets.target");
$docker->succeed("docker run nix nix-store --version");
$docker->succeed("docker run bash bash --version");
'';
})

View file

@ -3,58 +3,58 @@
pkgs ? import ../.. { inherit system config; }
}:
with import ../lib/testing.nix { inherit system pkgs; };
with import ../lib/testing-python.nix { inherit system pkgs; };
with pkgs.lib;
with import common/ec2.nix { inherit makeTest pkgs; };
let
imageCfg =
(import ../lib/eval-config.nix {
inherit system;
modules = [
../maintainers/scripts/ec2/amazon-image.nix
../modules/testing/test-instrumentation.nix
../modules/profiles/qemu-guest.nix
{ ec2.hvm = true;
imageCfg = (import ../lib/eval-config.nix {
inherit system;
modules = [
../maintainers/scripts/ec2/amazon-image.nix
../modules/testing/test-instrumentation.nix
../modules/profiles/qemu-guest.nix
{
ec2.hvm = true;
# Hack to make the partition resizing work in QEMU.
boot.initrd.postDeviceCommands = mkBefore
''
ln -s vda /dev/xvda
ln -s vda1 /dev/xvda1
'';
# Hack to make the partition resizing work in QEMU.
boot.initrd.postDeviceCommands = mkBefore ''
ln -s vda /dev/xvda
ln -s vda1 /dev/xvda1
'';
# Needed by nixos-rebuild due to the lack of network
# access. Determined by trial and error.
system.extraDependencies =
with pkgs; (
[
# Needed for a nixos-rebuild.
busybox
stdenv
stdenvNoCC
mkinitcpio-nfs-utils
unionfs-fuse
cloud-utils
desktop-file-utils
texinfo
libxslt.bin
xorg.lndir
# Needed by nixos-rebuild due to the lack of network
# access. Determined by trial and error.
system.extraDependencies = with pkgs; ( [
# Needed for a nixos-rebuild.
busybox
cloud-utils
desktop-file-utils
libxslt.bin
mkinitcpio-nfs-utils
stdenv
stdenvNoCC
texinfo
unionfs-fuse
xorg.lndir
# These are used in the configure-from-userdata tests
# for EC2. Httpd and valgrind are requested by the
# configuration.
apacheHttpd apacheHttpd.doc apacheHttpd.man valgrind.doc
]
);
}
];
}).config;
# These are used in the configure-from-userdata tests
# for EC2. Httpd and valgrind are requested by the
# configuration.
apacheHttpd
apacheHttpd.doc
apacheHttpd.man
valgrind.doc
]);
}
];
}).config;
image = "${imageCfg.system.build.amazonImage}/${imageCfg.amazonImage.name}.vhd";
sshKeys = import ./ssh-keys.nix pkgs;
snakeOilPrivateKey = sshKeys.snakeOilPrivateKey.text;
snakeOilPrivateKeyFile = pkgs.writeText "private-key" snakeOilPrivateKey;
snakeOilPublicKey = sshKeys.snakeOilPublicKey;
in {
@ -68,43 +68,47 @@ in {
SSH_HOST_ED25519_KEY:${replaceStrings ["\n"] ["|"] snakeOilPrivateKey}
'';
script = ''
$machine->start;
$machine->waitForFile("/etc/ec2-metadata/user-data");
$machine->waitForUnit("sshd.service");
machine.start()
machine.wait_for_file("/etc/ec2-metadata/user-data")
machine.wait_for_unit("sshd.service")
$machine->succeed("grep unknown /etc/ec2-metadata/ami-manifest-path");
machine.succeed("grep unknown /etc/ec2-metadata/ami-manifest-path")
# We have no keys configured on the client side yet, so this should fail
$machine->fail("ssh -o BatchMode=yes localhost exit");
machine.fail("ssh -o BatchMode=yes localhost exit")
# Let's install our client private key
$machine->succeed("mkdir -p ~/.ssh");
machine.succeed("mkdir -p ~/.ssh")
$machine->succeed("echo '${snakeOilPrivateKey}' > ~/.ssh/id_ed25519");
$machine->succeed("chmod 600 ~/.ssh/id_ed25519");
machine.copy_from_host_via_shell(
"${snakeOilPrivateKeyFile}", "~/.ssh/id_ed25519"
)
machine.succeed("chmod 600 ~/.ssh/id_ed25519")
# We haven't configured the host key yet, so this should still fail
$machine->fail("ssh -o BatchMode=yes localhost exit");
machine.fail("ssh -o BatchMode=yes localhost exit")
# Add the host key; ssh should finally succeed
$machine->succeed("echo localhost,127.0.0.1 ${snakeOilPublicKey} > ~/.ssh/known_hosts");
$machine->succeed("ssh -o BatchMode=yes localhost exit");
machine.succeed(
"echo localhost,127.0.0.1 ${snakeOilPublicKey} > ~/.ssh/known_hosts"
)
machine.succeed("ssh -o BatchMode=yes localhost exit")
# Test whether the root disk was resized.
my $blocks = $machine->succeed("stat -c %b -f /");
my $bsize = $machine->succeed("stat -c %S -f /");
my $size = $blocks * $bsize;
die "wrong free space $size" if $size < 9.7 * 1024 * 1024 * 1024 || $size > 10 * 1024 * 1024 * 1024;
blocks, block_size = map(int, machine.succeed("stat -c %b:%S -f /").split(":"))
GB = 1024 ** 3
assert 9.7 * GB <= blocks * block_size <= 10 * GB
# Just to make sure resizing is idempotent.
$machine->shutdown;
$machine->start;
$machine->waitForFile("/etc/ec2-metadata/user-data");
machine.shutdown()
machine.start()
machine.wait_for_file("/etc/ec2-metadata/user-data")
'';
};
boot-ec2-config = makeEc2Test {
name = "config-userdata";
meta.broken = true; # amazon-init wants to download from the internet while building the system
inherit image;
sshPublicKey = snakeOilPublicKey;
@ -133,17 +137,17 @@ in {
}
'';
script = ''
$machine->start;
machine.start()
# amazon-init must succeed. if it fails, make the test fail
# immediately instead of timing out in waitForFile.
$machine->waitForUnit('amazon-init.service');
# immediately instead of timing out in wait_for_file.
machine.wait_for_unit("amazon-init.service")
$machine->waitForFile("/etc/testFile");
$machine->succeed("cat /etc/testFile | grep -q 'whoa'");
machine.wait_for_file("/etc/testFile")
assert "whoa" in machine.succeed("cat /etc/testFile")
$machine->waitForUnit("httpd.service");
$machine->succeed("curl http://localhost | grep Valgrind");
machine.wait_for_unit("httpd.service")
assert "Valgrind" in machine.succeed("curl http://localhost")
'';
};
}

View file

@ -23,6 +23,13 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
services.xserver.desktopManager.gnome3.enable = true;
services.xserver.desktopManager.gnome3.debug = true;
environment.systemPackages = [
(pkgs.makeAutostartItem {
name = "org.gnome.Terminal";
package = pkgs.gnome3.gnome-terminal;
})
];
virtualisation.memorySize = 1024;
};
@ -65,9 +72,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
)
with subtest("Open Gnome Terminal"):
machine.succeed(
"${gnomeTerminalCommand}"
)
# correct output should be (true, '"gnome-terminal-server"')
machine.wait_until_succeeds(
"${wmClass} | grep -q 'gnome-terminal-server'"

View file

@ -1,4 +1,4 @@
import ./make-test.nix ({ pkgs, latestKernel ? false, ... } : {
import ./make-test-python.nix ({ pkgs, latestKernel ? false, ... } : {
name = "hardened";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ joachifm ];
@ -47,84 +47,88 @@ import ./make-test.nix ({ pkgs, latestKernel ? false, ... } : {
};
in
''
$machine->waitForUnit("multi-user.target");
machine.wait_for_unit("multi-user.target")
with subtest("AppArmor profiles are loaded"):
machine.succeed("systemctl status apparmor.service")
subtest "apparmor-loaded", sub {
$machine->succeed("systemctl status apparmor.service");
};
# AppArmor securityfs
subtest "apparmor-securityfs", sub {
$machine->succeed("mountpoint -q /sys/kernel/security");
$machine->succeed("cat /sys/kernel/security/apparmor/profiles");
};
with subtest("AppArmor securityfs is mounted"):
machine.succeed("mountpoint -q /sys/kernel/security")
machine.succeed("cat /sys/kernel/security/apparmor/profiles")
# Test loading out-of-tree modules
subtest "extra-module-packages", sub {
$machine->succeed("grep -Fq wireguard /proc/modules");
};
with subtest("Out-of-tree modules can be loaded"):
machine.succeed("grep -Fq wireguard /proc/modules")
# Test hidepid
subtest "hidepid", sub {
$machine->succeed("grep -Fq hidepid=2 /proc/mounts");
with subtest("hidepid=2 option is applied and works"):
machine.succeed("grep -Fq hidepid=2 /proc/mounts")
# cannot use pgrep -u here, it segfaults when access to process info is denied
$machine->succeed("[ `su - sybil -c 'ps --no-headers --user root | wc -l'` = 0 ]");
$machine->succeed("[ `su - alice -c 'ps --no-headers --user root | wc -l'` != 0 ]");
};
machine.succeed("[ `su - sybil -c 'ps --no-headers --user root | wc -l'` = 0 ]")
machine.succeed("[ `su - alice -c 'ps --no-headers --user root | wc -l'` != 0 ]")
# Test kernel module hardening
subtest "lock-modules", sub {
with subtest("No more kernel modules can be loaded"):
# note: this better a be module we normally wouldn't load ...
$machine->fail("modprobe dccp");
};
machine.fail("modprobe dccp")
# Test userns
subtest "userns", sub {
$machine->succeed("unshare --user true");
$machine->fail("su -l alice -c 'unshare --user true'");
};
with subtest("User namespaces are restricted"):
machine.succeed("unshare --user true")
machine.fail("su -l alice -c 'unshare --user true'")
# Test dmesg restriction
subtest "dmesg", sub {
$machine->fail("su -l alice -c dmesg");
};
with subtest("Regular users cannot access dmesg"):
machine.fail("su -l alice -c dmesg")
# Test access to kcore
subtest "kcore", sub {
$machine->fail("cat /proc/kcore");
};
with subtest("Kcore is inaccessible as root"):
machine.fail("cat /proc/kcore")
# Test deferred mount
subtest "mount", sub {
$machine->fail("mountpoint -q /efi"); # was deferred
$machine->execute("mkdir -p /efi");
$machine->succeed("mount /dev/disk/by-label/EFISYS /efi");
$machine->succeed("mountpoint -q /efi"); # now mounted
};
with subtest("Deferred mounts work"):
machine.fail("mountpoint -q /efi") # was deferred
machine.execute("mkdir -p /efi")
machine.succeed("mount /dev/disk/by-label/EFISYS /efi")
machine.succeed("mountpoint -q /efi") # now mounted
# Test Nix dæmon usage
subtest "nix-daemon", sub {
$machine->fail("su -l nobody -s /bin/sh -c 'nix ping-store'");
$machine->succeed("su -l alice -c 'nix ping-store'") =~ "OK";
};
with subtest("nix-daemon cannot be used by all users"):
machine.fail("su -l nobody -s /bin/sh -c 'nix ping-store'")
machine.succeed("su -l alice -c 'nix ping-store'")
# Test kernel image protection
subtest "kernelimage", sub {
$machine->fail("systemctl hibernate");
$machine->fail("systemctl kexec");
};
with subtest("The kernel image is protected"):
machine.fail("systemctl hibernate")
machine.fail("systemctl kexec")
# Test hardened memory allocator
sub runMallocTestProg {
my ($progName, $errorText) = @_;
my $text = "fatal allocator error: " . $errorText;
$machine->fail("${hardened-malloc-tests}/bin/" . $progName) =~ $text;
};
def runMallocTestProg(prog_name, error_text):
text = "fatal allocator error: " + error_text
if not text in machine.fail(
"${hardened-malloc-tests}/bin/"
+ prog_name
+ " 2>&1"
):
raise Exception("Hardened malloc does not work for {}".format(error_text))
subtest "hardenedmalloc", sub {
runMallocTestProg("double_free_large", "invalid free");
runMallocTestProg("unaligned_free_small", "invalid unaligned free");
runMallocTestProg("write_after_free_small", "detected write after free");
};
with subtest("The hardened memory allocator works"):
runMallocTestProg("double_free_large", "invalid free")
runMallocTestProg("unaligned_free_small", "invalid unaligned free")
runMallocTestProg("write_after_free_small", "detected write after free")
'';
})

View file

@ -1,15 +1,16 @@
import ../make-test.nix ({ pkgs, ...} : {
import ../make-test-python.nix ({ pkgs, ...} : {
name = "test-hocker-fetchdocker";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ ixmatus ];
broken = true; # tries to download from registry-1.docker.io - how did this ever work?
};
machine = import ./machine.nix;
testScript = ''
startAll;
start_all()
$machine->waitForUnit("sockets.target");
$machine->waitUntilSucceeds("docker run registry-1.docker.io/v2/library/hello-world:latest");
machine.wait_for_unit("sockets.target")
machine.wait_until_succeeds("docker run registry-1.docker.io/v2/library/hello-world:latest")
'';
})

View file

@ -74,7 +74,7 @@ let
throw "Non-EFI boot methods are only supported on i686 / x86_64"
else ''
def assemble_qemu_flags():
flags = "-cpu host"
flags = "-cpu max"
${if system == "x86_64-linux"
then ''flags += " -m 768"''
else ''flags += " -m 512 -enable-kvm -machine virt,gic-version=host"''
@ -317,6 +317,7 @@ let
texinfo
unionfs-fuse
xorg.lndir
(lvm2.override { udev = null; }) # for initrd (extra-utils)
# add curl so that rather than seeing the test attempt to download
# curl's tarball, we see what it's trying to download
@ -799,7 +800,7 @@ in {
"btrfs subvol create /mnt/badpath/boot",
"btrfs subvol create /mnt/nixos",
"btrfs subvol set-default "
+ "$(btrfs subvol list /mnt | grep 'nixos' | awk '{print \$2}') /mnt",
+ "$(btrfs subvol list /mnt | grep 'nixos' | awk '{print $2}') /mnt",
"umount /mnt",
"mount -o defaults LABEL=root /mnt",
"mkdir -p /mnt/badpath/boot", # Help ensure the detection mechanism

View file

@ -3,30 +3,30 @@
pkgs ? import ../.. { inherit system config; }
}:
with import ../lib/testing.nix { inherit system pkgs; };
with import ../lib/testing-python.nix { inherit system pkgs; };
with pkgs.lib;
with import common/ec2.nix { inherit makeTest pkgs; };
let
image =
(import ../lib/eval-config.nix {
inherit system;
modules = [
../maintainers/scripts/openstack/openstack-image.nix
../modules/testing/test-instrumentation.nix
../modules/profiles/qemu-guest.nix
{
# Needed by nixos-rebuild due to lack of network access.
system.extraDependencies = with pkgs; [
stdenv
];
}
];
}).config.system.build.openstackImage + "/nixos.qcow2";
image = (import ../lib/eval-config.nix {
inherit system;
modules = [
../maintainers/scripts/openstack/openstack-image.nix
../modules/testing/test-instrumentation.nix
../modules/profiles/qemu-guest.nix
{
# Needed by nixos-rebuild due to lack of network access.
system.extraDependencies = with pkgs; [
stdenv
];
}
];
}).config.system.build.openstackImage + "/nixos.qcow2";
sshKeys = import ./ssh-keys.nix pkgs;
snakeOilPrivateKey = sshKeys.snakeOilPrivateKey.text;
snakeOilPrivateKeyFile = pkgs.writeText "private-key" snakeOilPrivateKey;
snakeOilPublicKey = sshKeys.snakeOilPublicKey;
in {
@ -39,32 +39,36 @@ in {
SSH_HOST_ED25519_KEY:${replaceStrings ["\n"] ["|"] snakeOilPrivateKey}
'';
script = ''
$machine->start;
$machine->waitForFile("/etc/ec2-metadata/user-data");
$machine->waitForUnit("sshd.service");
machine.start()
machine.wait_for_file("/etc/ec2-metadata/user-data")
machine.wait_for_unit("sshd.service")
$machine->succeed("grep unknown /etc/ec2-metadata/ami-manifest-path");
machine.succeed("grep unknown /etc/ec2-metadata/ami-manifest-path")
# We have no keys configured on the client side yet, so this should fail
$machine->fail("ssh -o BatchMode=yes localhost exit");
machine.fail("ssh -o BatchMode=yes localhost exit")
# Let's install our client private key
$machine->succeed("mkdir -p ~/.ssh");
machine.succeed("mkdir -p ~/.ssh")
$machine->succeed("echo '${snakeOilPrivateKey}' > ~/.ssh/id_ed25519");
$machine->succeed("chmod 600 ~/.ssh/id_ed25519");
machine.copy_from_host_via_shell(
"${snakeOilPrivateKeyFile}", "~/.ssh/id_ed25519"
)
machine.succeed("chmod 600 ~/.ssh/id_ed25519")
# We haven't configured the host key yet, so this should still fail
$machine->fail("ssh -o BatchMode=yes localhost exit");
machine.fail("ssh -o BatchMode=yes localhost exit")
# Add the host key; ssh should finally succeed
$machine->succeed("echo localhost,127.0.0.1 ${snakeOilPublicKey} > ~/.ssh/known_hosts");
$machine->succeed("ssh -o BatchMode=yes localhost exit");
machine.succeed(
"echo localhost,127.0.0.1 ${snakeOilPublicKey} > ~/.ssh/known_hosts"
)
machine.succeed("ssh -o BatchMode=yes localhost exit")
# Just to make sure resizing is idempotent.
$machine->shutdown;
$machine->start;
$machine->waitForFile("/etc/ec2-metadata/user-data");
machine.shutdown()
machine.start()
machine.wait_for_file("/etc/ec2-metadata/user-data")
'';
};
@ -86,9 +90,9 @@ in {
}
'';
script = ''
$machine->start;
$machine->waitForFile("/etc/testFile");
$machine->succeed("cat /etc/testFile | grep -q 'whoa'");
machine.start()
machine.wait_for_file("/etc/testFile")
assert "whoa" in machine.succeed("cat /etc/testFile")
'';
};
}

View file

@ -1,4 +1,4 @@
import ./make-test.nix ({pkgs, lib, ...}:
import ./make-test-python.nix ({pkgs, lib, ...}:
let
# A filesystem image with a (presumably) bootable debian
debianImage = pkgs.vmTools.diskImageFuns.debian9i386 {
@ -34,9 +34,6 @@ let
'';
};
# options to add the disk to the test vm
QEMU_OPTS = "-drive index=2,file=${debianImage}/disk-image.qcow2,read-only,if=virtio";
# a part of the configuration of the test vm
simpleConfig = {
boot.loader.grub = {
@ -71,7 +68,7 @@ in {
machine = { config, pkgs, ... }: (simpleConfig // {
imports = [ ../modules/profiles/installation-device.nix
../modules/profiles/base.nix ];
virtualisation.memorySize = 1024;
virtualisation.memorySize = 1300;
# The test cannot access the network, so any packages
# nixos-rebuild needs must be included in the VM.
system.extraDependencies = with pkgs;
@ -99,22 +96,28 @@ in {
testScript = ''
# hack to add the secondary disk
$machine->{startCommand} = "QEMU_OPTS=\"\$QEMU_OPTS \"${lib.escapeShellArg QEMU_OPTS} ".$machine->{startCommand};
os.environ[
"QEMU_OPTS"
] = "-drive index=2,file=${debianImage}/disk-image.qcow2,read-only,if=virtio"
$machine->start;
$machine->succeed("udevadm settle");
$machine->waitForUnit("multi-user.target");
machine.start()
machine.succeed("udevadm settle")
machine.wait_for_unit("multi-user.target")
print(machine.succeed("lsblk"))
# check that os-prober works standalone
$machine->succeed("${pkgs.os-prober}/bin/os-prober | grep /dev/vdb1");
machine.succeed(
"${pkgs.os-prober}/bin/os-prober | grep /dev/vdb1"
)
# rebuild and test that debian is available in the grub menu
$machine->succeed("nixos-generate-config");
$machine->copyFileFromHost(
machine.succeed("nixos-generate-config")
machine.copy_from_host(
"${configFile}",
"/etc/nixos/configuration.nix");
$machine->succeed("nixos-rebuild boot >&2");
"/etc/nixos/configuration.nix",
)
machine.succeed("nixos-rebuild boot >&2")
$machine->succeed("egrep 'menuentry.*debian' /boot/grub/grub.cfg");
machine.succeed("egrep 'menuentry.*debian' /boot/grub/grub.cfg")
'';
})

View file

@ -1,103 +1,111 @@
{ system ? builtins.currentSystem
, config ? { }
, pkgs ? import ../.. { inherit system config; } }:
with import ../lib/testing.nix { inherit system pkgs; };
with pkgs.lib;
let
makePostgresqlWalReceiverTest = subTestName: postgresqlPackage: let
# Makes a test for a PostgreSQL package, given by name and looked up from `pkgs`.
makePostgresqlWalReceiverTest = postgresqlPackage:
{
name = postgresqlPackage;
value =
import ./make-test-python.nix ({ pkgs, lib, ... }: let
postgresqlDataDir = "/var/db/postgresql/test";
replicationUser = "wal_receiver_user";
replicationSlot = "wal_receiver_slot";
replicationConn = "postgresql://${replicationUser}@localhost";
baseBackupDir = "/tmp/pg_basebackup";
walBackupDir = "/tmp/pg_wal";
atLeast12 = versionAtLeast postgresqlPackage.version "12.0";
restoreCommand = ''
restore_command = 'cp ${walBackupDir}/%f %p'
'';
recoveryFile = if atLeast12
then pkgs.writeTextDir "recovery.signal" ""
else pkgs.writeTextDir "recovery.conf" "${restoreCommand}";
in makeTest {
name = "postgresql-wal-receiver-${subTestName}";
meta.maintainers = with maintainers; [ pacien ];
machine = { ... }: {
# Needed because this test uses a non-default 'services.postgresql.dataDir'.
systemd.tmpfiles.rules = [
"d /var/db/postgresql 0700 postgres postgres"
];
services.postgresql = {
package = postgresqlPackage;
enable = true;
dataDir = postgresqlDataDir;
extraConfig = ''
wal_level = archive # alias for replica on pg >= 9.6
max_wal_senders = 10
max_replication_slots = 10
'' + optionalString atLeast12 ''
${restoreCommand}
recovery_end_command = 'touch recovery.done'
pkg = pkgs."${postgresqlPackage}";
postgresqlDataDir = "/var/lib/postgresql/${pkg.psqlSchema}";
replicationUser = "wal_receiver_user";
replicationSlot = "wal_receiver_slot";
replicationConn = "postgresql://${replicationUser}@localhost";
baseBackupDir = "/tmp/pg_basebackup";
walBackupDir = "/tmp/pg_wal";
atLeast12 = lib.versionAtLeast pkg.version "12.0";
restoreCommand = ''
restore_command = 'cp ${walBackupDir}/%f %p'
'';
authentication = ''
host replication ${replicationUser} all trust
'';
initialScript = pkgs.writeText "init.sql" ''
create user ${replicationUser} replication;
select * from pg_create_physical_replication_slot('${replicationSlot}');
'';
};
services.postgresqlWalReceiver.receivers.main = {
inherit postgresqlPackage;
connection = replicationConn;
slot = replicationSlot;
directory = walBackupDir;
};
# This is only to speedup test, it isn't time racing. Service is set to autorestart always,
# default 60sec is fine for real system, but is too much for a test
systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = mkForce 5;
recoveryFile = if atLeast12
then pkgs.writeTextDir "recovery.signal" ""
else pkgs.writeTextDir "recovery.conf" "${restoreCommand}";
in {
name = "postgresql-wal-receiver-${postgresqlPackage}";
meta.maintainers = with lib.maintainers; [ pacien ];
machine = { ... }: {
services.postgresql = {
package = pkg;
enable = true;
extraConfig = ''
wal_level = archive # alias for replica on pg >= 9.6
max_wal_senders = 10
max_replication_slots = 10
'' + lib.optionalString atLeast12 ''
${restoreCommand}
recovery_end_command = 'touch recovery.done'
'';
authentication = ''
host replication ${replicationUser} all trust
'';
initialScript = pkgs.writeText "init.sql" ''
create user ${replicationUser} replication;
select * from pg_create_physical_replication_slot('${replicationSlot}');
'';
};
services.postgresqlWalReceiver.receivers.main = {
postgresqlPackage = pkg;
connection = replicationConn;
slot = replicationSlot;
directory = walBackupDir;
};
# This is only to speedup test, it isn't time racing. Service is set to autorestart always,
# default 60sec is fine for real system, but is too much for a test
systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5;
};
testScript = ''
# make an initial base backup
machine.wait_for_unit("postgresql")
machine.wait_for_unit("postgresql-wal-receiver-main")
# WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
# required only for 9.4
machine.sleep(5)
machine.succeed(
"${pkg}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}"
)
# create a dummy table with 100 records
machine.succeed(
"sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'"
)
# stop postgres and destroy data
machine.systemctl("stop postgresql")
machine.systemctl("stop postgresql-wal-receiver-main")
machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}")
# restore the base backup
machine.succeed(
"cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}"
)
# prepare WAL and recovery
machine.succeed("chmod a+rX -R ${walBackupDir}")
machine.execute(
"for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done"
) # make use of partial segments too
machine.succeed(
"cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*"
)
# replay WAL
machine.systemctl("start postgresql")
machine.wait_for_file("${postgresqlDataDir}/recovery.done")
machine.systemctl("restart postgresql")
machine.wait_for_unit("postgresql")
# check that our records have been restored
machine.succeed(
"test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100"
)
'';
});
};
testScript = ''
# make an initial base backup
$machine->waitForUnit('postgresql');
$machine->waitForUnit('postgresql-wal-receiver-main');
# WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
# required only for 9.4
$machine->sleep(5);
$machine->succeed('${postgresqlPackage}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}');
# create a dummy table with 100 records
$machine->succeed('sudo -u postgres psql --command="create table dummy as select * from generate_series(1, 100) as val;"');
# stop postgres and destroy data
$machine->systemctl('stop postgresql');
$machine->systemctl('stop postgresql-wal-receiver-main');
$machine->succeed('rm -r ${postgresqlDataDir}/{base,global,pg_*}');
# restore the base backup
$machine->succeed('cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}');
# prepare WAL and recovery
$machine->succeed('chmod a+rX -R ${walBackupDir}');
$machine->execute('for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done'); # make use of partial segments too
$machine->succeed('cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*');
# replay WAL
$machine->systemctl('start postgresql');
$machine->waitForFile('${postgresqlDataDir}/recovery.done');
$machine->systemctl('restart postgresql');
$machine->waitForUnit('postgresql');
# check that our records have been restored
$machine->succeed('test $(sudo -u postgres psql --pset="pager=off" --tuples-only --command="select count(distinct val) from dummy;") -eq 100');
'';
};
in mapAttrs makePostgresqlWalReceiverTest (import ../../pkgs/servers/sql/postgresql pkgs)
# Maps the generic function over all attributes of PostgreSQL packages
in builtins.listToAttrs (map makePostgresqlWalReceiverTest (builtins.attrNames (import ../../pkgs/servers/sql/postgresql { })))

View file

@ -158,7 +158,10 @@ in import ./make-test-python.nix {
s3 = { pkgs, ... } : {
# Minio requires at least 1GiB of free disk space to run.
virtualisation.diskSize = 2 * 1024;
virtualisation = {
diskSize = 2 * 1024;
memorySize = 1024;
};
networking.firewall.allowedTCPPorts = [ minioPort ];
services.minio = {
@ -235,7 +238,7 @@ in import ./make-test-python.nix {
# Test if the Thanos bucket command is able to retrieve blocks from the S3 bucket
# and check if the blocks have the correct labels:
store.succeed(
"thanos bucket ls "
"thanos tools bucket ls "
+ "--objstore.config-file=${nodes.store.config.services.thanos.store.objstore.config-file} "
+ "--output=json | "
+ "jq .thanos.labels.some_label | "

View file

@ -0,0 +1,80 @@
import ./make-test-python.nix ({ pkgs, lib, ... }: {
name = "shadowsocks";
meta = {
maintainers = with lib.maintainers; [ hmenke ];
};
nodes = {
server = {
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
networking.useDHCP = false;
networking.interfaces.eth1.ipv4.addresses = [
{ address = "192.168.0.1"; prefixLength = 24; }
];
networking.firewall.rejectPackets = true;
networking.firewall.allowedTCPPorts = [ 8488 ];
networking.firewall.allowedUDPPorts = [ 8488 ];
services.shadowsocks = {
enable = true;
encryptionMethod = "chacha20-ietf-poly1305";
password = "pa$$w0rd";
localAddress = [ "0.0.0.0" ];
port = 8488;
fastOpen = false;
mode = "tcp_and_udp";
plugin = "${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin";
pluginOpts = "server;host=nixos.org";
};
services.nginx = {
enable = true;
virtualHosts.server = {
locations."/".root = pkgs.writeTextDir "index.html" "It works!";
};
};
};
client = {
networking.useDHCP = false;
networking.interfaces.eth1.ipv4.addresses = [
{ address = "192.168.0.2"; prefixLength = 24; }
];
systemd.services.shadowsocks-client = {
description = "connect to shadowsocks";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [
shadowsocks-libev
shadowsocks-v2ray-plugin
];
script = ''
exec ss-local \
-s 192.168.0.1 \
-p 8488 \
-l 1080 \
-k 'pa$$w0rd' \
-m chacha20-ietf-poly1305 \
-a nobody \
--plugin "${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin" \
--plugin-opts "host=nixos.org"
'';
};
};
};
testScript = ''
start_all()
server.wait_for_unit("shadowsocks-libev.service")
client.wait_for_unit("shadowsocks-client.service")
client.fail(
"${pkgs.curl}/bin/curl 192.168.0.1:80"
)
msg = client.succeed(
"${pkgs.curl}/bin/curl --socks5 localhost:1080 192.168.0.1:80"
)
assert msg == "It works!", "Could not connect through shadowsocks"
'';
}
)

17
nixos/tests/sssd.nix Normal file
View file

@ -0,0 +1,17 @@
import ./make-test-python.nix ({ pkgs, ... }:
{
name = "sssd";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ bbigras ];
};
machine = { pkgs, ... }: {
services.sssd.enable = true;
};
testScript = ''
start_all()
machine.wait_for_unit("multi-user.target")
machine.wait_for_unit("sssd.service")
'';
})

View file

@ -31,7 +31,7 @@ import ./make-test-python.nix ({pkgs, ...}: {
firewall.enable = false;
interfaces.eth1.ipv4.addresses = lib.mkForce []; # no need for legacy IP
interfaces.eth1.ipv6.addresses = lib.mkForce [
{ address = "2001:DB8::"; prefixLength = 64; }
{ address = "2001:DB8::1"; prefixLength = 64; }
];
};
@ -260,7 +260,7 @@ import ./make-test-python.nix ({pkgs, ...}: {
client.wait_until_succeeds("ping -6 -c 1 FD42::1")
# the global IP of the ISP router should still not be a reachable
router.fail("ping -6 -c 1 2001:DB8::")
router.fail("ping -6 -c 1 2001:DB8::1")
# Once we have internal connectivity boot up the ISP
isp.start()
@ -273,11 +273,11 @@ import ./make-test-python.nix ({pkgs, ...}: {
# wait until the uplink interface has a good status
router.wait_for_unit("network-online.target")
router.wait_until_succeeds("ping -6 -c1 2001:DB8::")
router.wait_until_succeeds("ping -6 -c1 2001:DB8::1")
# shortly after that the client should have received it's global IPv6
# address and thus be able to ping the ISP
client.wait_until_succeeds("ping -6 -c1 2001:DB8::")
client.wait_until_succeeds("ping -6 -c1 2001:DB8::1")
# verify that we got a globally scoped address in eth1 from the
# documentation prefix

View file

@ -4,7 +4,10 @@ import ./make-test-python.nix ({ pkgs, ... }: {
machine = { lib, ... }: {
imports = [ common/user-account.nix common/x11.nix ];
virtualisation.emptyDiskImages = [ 512 ];
virtualisation.emptyDiskImages = [ 512 512 ];
virtualisation.memorySize = 1024;
environment.systemPackages = [ pkgs.cryptsetup ];
fileSystems = lib.mkVMOverride {
"/test-x-initrd-mount" = {
@ -144,5 +147,25 @@ import ./make-test-python.nix ({ pkgs, ... }: {
assert "RuntimeWatchdogUSec=30s" in output
assert "RebootWatchdogUSec=10m" in output
assert "KExecWatchdogUSec=5m" in output
# Test systemd cryptsetup support
with subtest("systemd successfully reads /etc/crypttab and unlocks volumes"):
# create a luks volume and put a filesystem on it
machine.succeed(
"echo -n supersecret | cryptsetup luksFormat -q /dev/vdc -",
"echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vdc foo",
"mkfs.ext3 /dev/mapper/foo",
)
# create a keyfile and /etc/crypttab
machine.succeed("echo -n supersecret > /var/lib/luks-keyfile")
machine.succeed("chmod 600 /var/lib/luks-keyfile")
machine.succeed("echo 'luks1 /dev/vdc /var/lib/luks-keyfile luks' > /etc/crypttab")
# after a reboot, systemd should unlock the volume and we should be able to mount it
machine.shutdown()
machine.succeed("systemctl status systemd-cryptsetup@luks1.service")
machine.succeed("mkdir -p /tmp/luks1")
machine.succeed("mount /dev/mapper/luks1 /tmp/luks1")
'';
})

View file

@ -9,6 +9,8 @@ import ./make-test-python.nix ({ pkgs, ...} : {
networking.firewall.allowedTCPPorts = [ 9091 ];
security.apparmor.enable = true;
services.transmission.enable = true;
};

View file

@ -15,7 +15,7 @@
assert use64bitGuest -> useKvmNestedVirt;
with import ../lib/testing.nix { inherit system pkgs; };
with import ../lib/testing-python.nix { inherit system pkgs; };
with pkgs.lib;
let
@ -91,13 +91,15 @@ let
(isYes "SERIAL_8250_CONSOLE")
(isYes "SERIAL_8250")
];
networking.usePredictableInterfaceNames = false;
};
mkLog = logfile: tag: let
rotated = map (i: "${logfile}.${toString i}") (range 1 9);
all = concatMapStringsSep " " (f: "\"${f}\"") ([logfile] ++ rotated);
logcmd = "tail -F ${all} 2> /dev/null | logger -t \"${tag}\"";
in optionalString debug "$machine->execute(ru '${logcmd} & disown');";
in if debug then "machine.execute(ru('${logcmd} & disown'))" else "pass";
testVM = vmName: vmScript: let
cfg = (import ../lib/eval-config.nix {
@ -204,96 +206,105 @@ let
};
testSubs = ''
my ${"$" + name}_sharepath = '${sharePath}';
sub checkRunning_${name} {
my $cmd = 'VBoxManage list runningvms | grep -q "^\"${name}\""';
my ($status, $out) = $machine->execute(ru $cmd);
return $status == 0;
}
sub cleanup_${name} {
$machine->execute(ru "VBoxManage controlvm ${name} poweroff")
if checkRunning_${name};
$machine->succeed("rm -rf ${sharePath}");
$machine->succeed("mkdir -p ${sharePath}");
$machine->succeed("chown alice.users ${sharePath}");
}
${name}_sharepath = "${sharePath}"
sub createVM_${name} {
vbm("createvm --name ${name} ${createFlags}");
vbm("modifyvm ${name} ${vmFlags}");
vbm("setextradata ${name} VBoxInternal/PDM/HaltOnReset 1");
vbm("storagectl ${name} ${controllerFlags}");
vbm("storageattach ${name} ${diskFlags}");
vbm("sharedfolder add ${name} ${sharedFlags}");
vbm("sharedfolder add ${name} ${nixstoreFlags}");
cleanup_${name};
${mkLog "$HOME/VirtualBox VMs/${name}/Logs/VBox.log" "HOST-${name}"}
}
def check_running_${name}():
cmd = "VBoxManage list runningvms | grep -q '^\"${name}\"'"
(status, _) = machine.execute(ru(cmd))
return status == 0
sub destroyVM_${name} {
cleanup_${name};
vbm("unregistervm ${name} --delete");
}
sub waitForVMBoot_${name} {
$machine->execute(ru(
'set -e; i=0; '.
'while ! test -e ${sharePath}/boot-done; do '.
'sleep 10; i=$(($i + 10)); [ $i -le 3600 ]; '.
'VBoxManage list runningvms | grep -q "^\"${name}\""; '.
'done'
));
}
def cleanup_${name}():
if check_running_${name}():
machine.execute(ru("VBoxManage controlvm ${name} poweroff"))
machine.succeed("rm -rf ${sharePath}")
machine.succeed("mkdir -p ${sharePath}")
machine.succeed("chown alice.users ${sharePath}")
sub waitForIP_${name} ($) {
my $property = "/VirtualBox/GuestInfo/Net/$_[0]/V4/IP";
my $getip = "VBoxManage guestproperty get ${name} $property | ".
"sed -n -e 's/^Value: //p'";
my $ip = $machine->succeed(ru(
'for i in $(seq 1000); do '.
'if ipaddr="$('.$getip.')" && [ -n "$ipaddr" ]; then '.
'echo "$ipaddr"; exit 0; '.
'fi; '.
'sleep 1; '.
'done; '.
'echo "Could not get IPv4 address for ${name}!" >&2; '.
'exit 1'
));
chomp $ip;
return $ip;
}
sub waitForStartup_${name} {
for (my $i = 0; $i <= 120; $i += 10) {
$machine->sleep(10);
return if checkRunning_${name};
eval { $_[0]->() } if defined $_[0];
}
die "VirtualBox VM didn't start up within 2 minutes";
}
def create_vm_${name}():
# fmt: off
vbm(f"createvm --name ${name} ${createFlags}")
vbm(f"modifyvm ${name} ${vmFlags}")
vbm(f"setextradata ${name} VBoxInternal/PDM/HaltOnReset 1")
vbm(f"storagectl ${name} ${controllerFlags}")
vbm(f"storageattach ${name} ${diskFlags}")
vbm(f"sharedfolder add ${name} ${sharedFlags}")
vbm(f"sharedfolder add ${name} ${nixstoreFlags}")
cleanup_${name}()
sub waitForShutdown_${name} {
for (my $i = 0; $i <= 120; $i += 10) {
$machine->sleep(10);
return unless checkRunning_${name};
}
die "VirtualBox VM didn't shut down within 2 minutes";
}
${mkLog "$HOME/VirtualBox VMs/${name}/Logs/VBox.log" "HOST-${name}"}
# fmt: on
sub shutdownVM_${name} {
$machine->succeed(ru "touch ${sharePath}/shutdown");
$machine->execute(
'set -e; i=0; '.
'while test -e ${sharePath}/shutdown '.
' -o -e ${sharePath}/boot-done; do '.
'sleep 1; i=$(($i + 1)); [ $i -le 3600 ]; '.
'done'
);
waitForShutdown_${name};
}
def destroy_vm_${name}():
cleanup_${name}()
vbm("unregistervm ${name} --delete")
def wait_for_vm_boot_${name}():
machine.execute(
ru(
"set -e; i=0; "
"while ! test -e ${sharePath}/boot-done; do "
"sleep 10; i=$(($i + 10)); [ $i -le 3600 ]; "
"VBoxManage list runningvms | grep -q '^\"${name}\"'; "
"done"
)
)
def wait_for_ip_${name}(interface):
property = f"/VirtualBox/GuestInfo/Net/{interface}/V4/IP"
# fmt: off
getip = f"VBoxManage guestproperty get ${name} {property} | sed -n -e 's/^Value: //p'"
# fmt: on
ip = machine.succeed(
ru(
"for i in $(seq 1000); do "
f'if ipaddr="$({getip})" && [ -n "$ipaddr" ]; then '
'echo "$ipaddr"; exit 0; '
"fi; "
"sleep 1; "
"done; "
"echo 'Could not get IPv4 address for ${name}!' >&2; "
"exit 1"
)
).strip()
return ip
def wait_for_startup_${name}(nudge=lambda: None):
for _ in range(0, 130, 10):
machine.sleep(10)
if check_running_${name}():
return
nudge()
raise Exception("VirtualBox VM didn't start up within 2 minutes")
def wait_for_shutdown_${name}():
for _ in range(0, 130, 10):
machine.sleep(10)
if not check_running_${name}():
return
raise Exception("VirtualBox VM didn't shut down within 2 minutes")
def shutdown_vm_${name}():
machine.succeed(ru("touch ${sharePath}/shutdown"))
machine.execute(
"set -e; i=0; "
"while test -e ${sharePath}/shutdown "
" -o -e ${sharePath}/boot-done; do "
"sleep 1; i=$(($i + 1)); [ $i -le 3600 ]; "
"done"
)
wait_for_shutdown_${name}()
'';
};
@ -364,26 +375,31 @@ let
};
testScript = ''
sub ru ($) {
my $esc = $_[0] =~ s/'/'\\${"'"}'/gr;
return "su - alice -c '$esc'";
}
sub vbm {
$machine->succeed(ru("VBoxManage ".$_[0]));
};
sub removeUUIDs {
return join("\n", grep { $_ !~ /^UUID:/ } split(/\n/, $_[0]))."\n";
}
from shlex import quote
${concatStrings (mapAttrsToList (_: getAttr "testSubs") vms)}
$machine->waitForX;
def ru(cmd: str) -> str:
return f"su - alice -c {quote(cmd)}"
def vbm(cmd: str) -> str:
return machine.succeed(ru(f"VBoxManage {cmd}"))
def remove_uuids(output: str) -> str:
return "\n".join(
[line for line in (output or "").splitlines() if not line.startswith("UUID:")]
)
machine.wait_for_x()
# fmt: off
${mkLog "$HOME/.config/VirtualBox/VBoxSVC.log" "HOST-SVC"}
# fmt: on
${testScript}
# (keep black happy)
'';
meta = with pkgs.stdenv.lib.maintainers; {
@ -393,133 +409,129 @@ let
unfreeTests = mapAttrs (mkVBoxTest true vboxVMsWithExtpack) {
enable-extension-pack = ''
createVM_testExtensionPack;
vbm("startvm testExtensionPack");
waitForStartup_testExtensionPack;
$machine->screenshot("cli_started");
waitForVMBoot_testExtensionPack;
$machine->screenshot("cli_booted");
create_vm_testExtensionPack()
vbm("startvm testExtensionPack")
wait_for_startup_testExtensionPack()
machine.screenshot("cli_started")
wait_for_vm_boot_testExtensionPack()
machine.screenshot("cli_booted")
$machine->nest("Checking for privilege escalation", sub {
$machine->fail("test -e '/root/VirtualBox VMs'");
$machine->fail("test -e '/root/.config/VirtualBox'");
$machine->succeed("test -e '/home/alice/VirtualBox VMs'");
});
with machine.nested("Checking for privilege escalation"):
machine.fail("test -e '/root/VirtualBox VMs'")
machine.fail("test -e '/root/.config/VirtualBox'")
machine.succeed("test -e '/home/alice/VirtualBox VMs'")
shutdownVM_testExtensionPack;
destroyVM_testExtensionPack;
shutdown_vm_testExtensionPack()
destroy_vm_testExtensionPack()
'';
};
in mapAttrs (mkVBoxTest false vboxVMs) {
simple-gui = ''
createVM_simple;
$machine->succeed(ru "VirtualBox &");
$machine->waitUntilSucceeds(
ru "xprop -name 'Oracle VM VirtualBox Manager'"
);
$machine->sleep(5);
$machine->screenshot("gui_manager_started");
# Home to select Tools, down to move to the VM, enter to start it.
$machine->sendKeys("home");
$machine->sendKeys("down");
$machine->sendKeys("ret");
$machine->screenshot("gui_manager_sent_startup");
waitForStartup_simple (sub {
$machine->sendKeys("home");
$machine->sendKeys("down");
$machine->sendKeys("ret");
});
$machine->screenshot("gui_started");
waitForVMBoot_simple;
$machine->screenshot("gui_booted");
shutdownVM_simple;
$machine->sleep(5);
$machine->screenshot("gui_stopped");
$machine->sendKeys("ctrl-q");
$machine->sleep(5);
$machine->screenshot("gui_manager_stopped");
destroyVM_simple;
def send_vm_startup():
machine.send_key("home")
machine.send_key("down")
machine.send_key("ret")
create_vm_simple()
machine.succeed(ru("VirtualBox &"))
machine.wait_until_succeeds(ru("xprop -name 'Oracle VM VirtualBox Manager'"))
machine.sleep(5)
machine.screenshot("gui_manager_started")
send_vm_startup()
machine.screenshot("gui_manager_sent_startup")
wait_for_startup_simple(send_vm_startup)
machine.screenshot("gui_started")
wait_for_vm_boot_simple()
machine.screenshot("gui_booted")
shutdown_vm_simple()
machine.sleep(5)
machine.screenshot("gui_stopped")
machine.send_key("ctrl-q")
machine.sleep(5)
machine.screenshot("gui_manager_stopped")
destroy_vm_simple()
'';
simple-cli = ''
createVM_simple;
vbm("startvm simple");
waitForStartup_simple;
$machine->screenshot("cli_started");
waitForVMBoot_simple;
$machine->screenshot("cli_booted");
create_vm_simple()
vbm("startvm simple")
wait_for_startup_simple()
machine.screenshot("cli_started")
wait_for_vm_boot_simple()
machine.screenshot("cli_booted")
$machine->nest("Checking for privilege escalation", sub {
$machine->fail("test -e '/root/VirtualBox VMs'");
$machine->fail("test -e '/root/.config/VirtualBox'");
$machine->succeed("test -e '/home/alice/VirtualBox VMs'");
});
with machine.nested("Checking for privilege escalation"):
machine.fail("test -e '/root/VirtualBox VMs'")
machine.fail("test -e '/root/.config/VirtualBox'")
machine.succeed("test -e '/home/alice/VirtualBox VMs'")
shutdownVM_simple;
destroyVM_simple;
shutdown_vm_simple()
destroy_vm_simple()
'';
headless = ''
createVM_headless;
$machine->succeed(ru("VBoxHeadless --startvm headless & disown %1"));
waitForStartup_headless;
waitForVMBoot_headless;
shutdownVM_headless;
destroyVM_headless;
create_vm_headless()
machine.succeed(ru("VBoxHeadless --startvm headless & disown %1"))
wait_for_startup_headless()
wait_for_vm_boot_headless()
shutdown_vm_headless()
destroy_vm_headless()
'';
host-usb-permissions = ''
my $userUSB = removeUUIDs vbm("list usbhost");
print STDERR $userUSB;
my $rootUSB = removeUUIDs $machine->succeed("VBoxManage list usbhost");
print STDERR $rootUSB;
user_usb = remove_uuids(vbm("list usbhost"))
print(user_usb, file=sys.stderr)
root_usb = remove_uuids(machine.succeed("VBoxManage list usbhost"))
print(root_usb, file=sys.stderr)
die "USB host devices differ for root and normal user"
if $userUSB ne $rootUSB;
die "No USB host devices found" if $userUSB =~ /<none>/;
if user_usb != root_usb:
raise Exception("USB host devices differ for root and normal user")
if "<none>" in user_usb:
raise Exception("No USB host devices found")
'';
systemd-detect-virt = ''
createVM_detectvirt;
vbm("startvm detectvirt");
waitForStartup_detectvirt;
waitForVMBoot_detectvirt;
shutdownVM_detectvirt;
my $result = $machine->succeed("cat '$detectvirt_sharepath/result'");
chomp $result;
destroyVM_detectvirt;
die "systemd-detect-virt returned \"$result\" instead of \"oracle\""
if $result ne "oracle";
create_vm_detectvirt()
vbm("startvm detectvirt")
wait_for_startup_detectvirt()
wait_for_vm_boot_detectvirt()
shutdown_vm_detectvirt()
result = machine.succeed(f"cat '{detectvirt_sharepath}/result'").strip()
destroy_vm_detectvirt()
if result != "oracle":
raise Exception(f'systemd-detect-virt returned "{result}" instead of "oracle"')
'';
net-hostonlyif = ''
createVM_test1;
createVM_test2;
create_vm_test1()
create_vm_test2()
vbm("startvm test1");
waitForStartup_test1;
waitForVMBoot_test1;
vbm("startvm test1")
wait_for_startup_test1()
wait_for_vm_boot_test1()
vbm("startvm test2");
waitForStartup_test2;
waitForVMBoot_test2;
vbm("startvm test2")
wait_for_startup_test2()
wait_for_vm_boot_test2()
$machine->screenshot("net_booted");
machine.screenshot("net_booted")
my $test1IP = waitForIP_test1 1;
my $test2IP = waitForIP_test2 1;
test1_ip = wait_for_ip_test1(1)
test2_ip = wait_for_ip_test2(1)
$machine->succeed("echo '$test2IP' | nc -N '$test1IP' 1234");
$machine->succeed("echo '$test1IP' | nc -N '$test2IP' 1234");
machine.succeed(f"echo '{test2_ip}' | nc -N '{test1_ip}' 1234")
machine.succeed(f"echo '{test1_ip}' | nc -N '{test2_ip}' 1234")
$machine->waitUntilSucceeds("nc -N '$test1IP' 5678 < /dev/null >&2");
$machine->waitUntilSucceeds("nc -N '$test2IP' 5678 < /dev/null >&2");
machine.wait_until_succeeds(f"nc -N '{test1_ip}' 5678 < /dev/null >&2")
machine.wait_until_succeeds(f"nc -N '{test2_ip}' 5678 < /dev/null >&2")
shutdownVM_test1;
shutdownVM_test2;
shutdown_vm_test1()
shutdown_vm_test2()
destroyVM_test1;
destroyVM_test2;
destroy_vm_test1()
destroy_vm_test2()
'';
} // (if enableUnfree then unfreeTests else {})

View file

@ -0,0 +1,26 @@
{ stdenv, fetchFromGitHub, cmake, pkg-config, mpd_clientlib, meson, ninja }:
stdenv.mkDerivation rec {
pname = "ashuffle";
version = "3.4.0";
src = fetchFromGitHub {
owner = "joshkunz";
repo = "ashuffle";
rev = "v${version}";
sha256 = "09q6lwgc1dc8bg1mb9js9qz3xcsxph3548nxzvyb4v8111gixrp7";
fetchSubmodules = true;
};
dontUseCmakeConfigure = true;
nativeBuildInputs = [ cmake pkg-config meson ninja ];
buildInputs = [ mpd_clientlib ];
meta = with stdenv.lib; {
homepage = "https://github.com/joshkunz/ashuffle";
description = "Automatic library-wide shuffle for mpd";
maintainers = [ maintainers.tcbravo ];
platforms = platforms.unix;
license = licenses.mit;
};
}

View file

@ -17,7 +17,7 @@ stdenv.mkDerivation rec {
# When updating, please check if https://github.com/csound/csound/issues/1078
# has been fixed in the new version so we can use the normal fluidsynth
# version and remove fluidsynth 1.x from nixpkgs again.
version = "6.13.0";
version = "6.14.0";
enableParallelBuilding = true;
@ -27,7 +27,7 @@ stdenv.mkDerivation rec {
owner = "csound";
repo = "csound";
rev = version;
sha256 = "14822ybqyp31z18gky2y9zadr9dkbhabg97y139py73w7v3af1bh";
sha256 = "1sr9knfhbm2m0wpkjq2l5n471vnl51wy4p6j4m95zqybimzb4s2j";
};
cmakeFlags = [ "-DBUILD_CSOUND_AC=0" ] # fails to find Score.hpp

View file

@ -7,13 +7,13 @@
stdenv.mkDerivation rec {
pname = "ft2-clone";
version = "1.26";
version = "1.28";
src = fetchFromGitHub {
owner = "8bitbubsy";
repo = "ft2-clone";
rev = "v${version}";
sha256 = "0fqb4415qy2nwjz7ahi43nk795ifswb2b37sc7p5n9m4yc8h53wv";
sha256 = "1hbcl89cpx9bsafxrjyfx6vrbs4h3lnzmqm12smcvdg8ksfgzj0d";
};
nativeBuildInputs = [ cmake ];

View file

@ -1,5 +1,16 @@
{ stdenv, fetchFromGitHub , xorg, freetype, alsaLib, curl, libjack2
, lv2, pkgconfig, libGLU, libGL }:
{ stdenv
, fetchFromGitHub
, fetchpatch
, xorg
, freetype
, alsaLib
, curl
, libjack2
, lv2
, pkgconfig
, libGLU
, libGL
}:
stdenv.mkDerivation {
version = "0.9.0";
@ -20,7 +31,15 @@
CXXFLAGS = "-DHAVE_LROUND";
patchPhase = ''
patches = [
# gcc9 compatibility https://github.com/mtytel/helm/pull/233
(fetchpatch {
url = "https://github.com/mtytel/helm/commit/cb611a80bd5a36d31bfc31212ebbf79aa86c6f08.patch";
sha256 = "1i2289srcfz17c3zzab6f51aznzdj62kk53l4afr32bkjh9s4ixk";
})
];
prePatch = ''
sed -i 's|usr/||g' Makefile
'';

View file

@ -0,0 +1,28 @@
{ stdenv, fetchzip, pkgconfig, lv2, gtkmm2, boost }:
stdenv.mkDerivation rec {
pname = "lv2-cpp-tools";
version = "1.0.5";
src = fetchzip {
url = "http://deb.debian.org/debian/pool/main/l/lv2-c++-tools/lv2-c++-tools_${version}.orig.tar.bz2";
sha256 = "039bq7d7s2bhfcnlsfq0mqxr9a9iqwg5bwcpxfi24c6yl6krydsi";
};
preConfigure = ''
sed -r 's,/bin/bash,${stdenv.shell},g' -i ./configure
sed -r 's,/sbin/ldconfig,ldconfig,g' -i ./Makefile.template
'';
nativeBuildInputs = [ pkgconfig ];
buildInputs = [ lv2 gtkmm2 boost ];
meta = with stdenv.lib; {
homepage = "http://ll-plugins.nongnu.org/hacking.html";
description = "Tools and libraries that may come in handy when writing LV2 plugins in C++";
license = licenses.gpl3;
maintainers = [ maintainers.michalrus ];
platforms = platforms.linux;
};
}

View file

@ -1,4 +1,4 @@
{ stdenv, fetchFromGitHub, cmake, eigen, libav_all }:
{ stdenv, fetchFromGitHub, cmake, eigen, libav }:
stdenv.mkDerivation {
pname = "musly";
version = "unstable-2017-04-26";
@ -9,7 +9,7 @@ stdenv.mkDerivation {
sha256 = "1q42wvdwy2pac7bhfraqqj2czw7w2m33ms3ifjl8phm7d87i8825";
};
nativeBuildInputs = [ cmake ];
buildInputs = [ eigen (libav_all.override { vaapiSupport = stdenv.isLinux; }).libav_11 ];
buildInputs = [ eigen (libav.override { vaapiSupport = stdenv.isLinux; }) ];
fixupPhase = if stdenv.isDarwin then ''
install_name_tool -change libmusly.dylib $out/lib/libmusly.dylib $out/bin/musly
install_name_tool -change libmusly_resample.dylib $out/lib/libmusly_resample.dylib $out/bin/musly

View file

@ -10,13 +10,13 @@ assert pcreSupport -> pcre != null;
stdenv.mkDerivation rec {
pname = "ncmpc";
version = "0.38";
version = "0.39";
src = fetchFromGitHub {
owner = "MusicPlayerDaemon";
repo = "ncmpc";
rev = "v${version}";
sha256 = "1kidpd1xrfax3v31q93r9g9b7jd841476q47wgd94h1a86b70gs9";
sha256 = "08xrcinfm1a7hjycf8la7gnsxbp3six70ks987dr7j42kd42irfq";
};
buildInputs = [ glib ncurses mpd_clientlib boost ]

View file

@ -12,13 +12,13 @@ let
;
in pythonPackages.buildPythonApplication rec {
pname = "picard";
version = "2.4.1";
version = "2.4.2";
src = fetchFromGitHub {
owner = "metabrainz";
repo = pname;
rev = "release-${version}";
sha256 = "0s4jmcg1n6ayxf7x0amq67rgn6y127h98s2k4fcna6n9477krrwf";
sha256 = "0sbccsisk9w0gnblvhg7wk1c5ydppldjbvaa0zhl3yrid5a363ah";
};
nativeBuildInputs = [ gettext qt5.wrapQtAppsHook qt5.qtbase ]

View file

@ -4,11 +4,11 @@
}:
stdenv.mkDerivation rec {
name = "snd-20.2";
name = "snd-20.3";
src = fetchurl {
url = "mirror://sourceforge/snd/${name}.tar.gz";
sha256 = "0ip4sfyxqlbghlggipmvvqjqs1a7qas0zcmzw8d1nwg6krjkfj0r";
sha256 = "016slh34gb6qqb38m8k9yg48rbhc5p12084szcwvanhh5v7fc7mk";
};
nativeBuildInputs = [ pkgconfig ];

View file

@ -0,0 +1,36 @@
{ stdenv, fetchFromGitHub, lv2, fftwFloat, pkgconfig }:
stdenv.mkDerivation rec {
pname = "talentedhack";
version = "1.86";
src = fetchFromGitHub {
owner = "jeremysalwen";
repo = "talentedhack";
rev = "v${version}";
sha256 = "0kwvayalysmk7y49jq0k16al252md8d45z58hphzsksmyz6148bx";
};
nativeBuildInputs = [ pkgconfig ];
buildInputs = [ lv2 fftwFloat ];
# To avoid name clashes, plugins should be compiled with symbols hidden, except for `lv2_descriptor`:
preConfigure = ''
sed -r 's/^CFLAGS.*$/\0 -fvisibility=hidden/' -i Makefile
'';
installPhase = ''
d=$out/lib/lv2/talentedhack.lv2
mkdir -p $d
cp *.so *.ttl $d
'';
meta = with stdenv.lib; {
homepage = "https://github.com/jeremysalwen/TalentedHack";
description = "LV2 port of Autotalent pitch correction plugin";
license = licenses.gpl3;
maintainers = [ maintainers.michalrus ];
platforms = platforms.linux;
};
}

View file

@ -0,0 +1,89 @@
{ stdenv
, fetchzip
, libX11
, libXi
, libGL
, alsaLib
, SDL2
, autoPatchelfHook
}:
stdenv.mkDerivation rec {
pname = "virtual-ans";
version = "3.0.2c";
src = fetchzip {
url = "https://warmplace.ru/soft/ans/virtual_ans-${version}.zip";
sha256 = "03r1v3l7rd59dakr7ndvgsqchv00ppkvi6sslgf1ng07r3rsvb1n";
};
nativeBuildInputs = [
autoPatchelfHook
];
buildInputs = [
stdenv.cc.cc.lib
libX11
libXi
libGL
alsaLib
SDL2
];
installPhase = ''
mkdir -p $out
cp -R ./* $out/
# Remove all executables except for current architecture
ls -1d $out/START* | grep -v ${startScript} | xargs rm -rf
ls -1d $out/bin/pixilang_linux* | grep -v ${linuxExecutable} | xargs rm -rf
# Start script performs relative search for resources, so it cannot be moved
# to bin directory
ln -s $out/${startScript} $out/bin/virtual-ans
'';
startScript = if stdenv.isx86_32 then "START_LINUX_X86"
else if stdenv.isx86_64 then "START_LINUX_X86_64"
#else if stdenv.isDarwin then "START_MACOS.app" # disabled because I cannot test on Darwin
else abort "Unsupported platform: ${stdenv.platform.kernelArch}.";
linuxExecutable = if stdenv.isx86_32 then "pixilang_linux_x86"
else if stdenv.isx86_64 then "pixilang_linux_x86_64"
else "";
meta = with stdenv.lib; {
description = "Photoelectronic microtonal/spectral musical instrument";
longDescription = ''
Virtual ANS is a software simulator of the unique Russian synthesizer ANS
- photoelectronic musical instrument created by Evgeny Murzin from 1938 to
1958. The ANS made it possible to draw music in the form of a spectrogram
(sonogram), without live instruments and performers. It was used by
Stanislav Kreichi, Alfred Schnittke, Edward Artemiev and other Soviet
composers in their experimental works. You can also hear the sound of the
ANS in Andrei Tarkovsky's movies Solaris, The Mirror, Stalker.
The simulator extends the capabilities of the original instrument. Now
it's a full-featured graphics editor where you can convert sound into an
image, load and play pictures, draw microtonal/spectral music and create
some unusual deep atmospheric sounds. This app is for everyone who loves
experiments and is looking for something new.
Key features:
+ unlimited number of pure tone generators;
+ powerful sonogram editor - you can draw the spectrum and play it at the same time;
+ any sound (from a WAV file or a Microphone/Line-in) can be converted to image (sonogram) and vice versa;
+ support for MIDI devices;
+ polyphonic synth mode with MIDI mapping;
+ supported file formats: WAV, AIFF, PNG, JPEG, GIF;
+ supported sound systems: ASIO, DirectSound, MME, ALSA, OSS, JACK, Audiobus, IAA.
'';
homepage = "https://warmplace.ru/soft/ans/";
license = licenses.free;
# I cannot test the Darwin version, so I'll leave it disabled
platforms = [ "x86_64-linux" "i686-linux" ];
maintainers = with maintainers; [ jacg ];
};
}

View file

@ -0,0 +1,27 @@
{ stdenv, fetchzip, pkgconfig, lvtk, lv2, fftw, lv2-cpp-tools, gtkmm2 }:
stdenv.mkDerivation rec {
pname = "vocproc";
version = "0.2.1";
src = fetchzip {
url = "https://hyperglitch.com/files/vocproc/${pname}-${version}.default.tar.gz";
sha256 = "07a1scyz14mg2jdbw6fpv4qg91zsw61qqii64n9qbnny9d5pn8n2";
};
nativeBuildInputs = [ pkgconfig ];
buildInputs = [ lv2 fftw lv2-cpp-tools gtkmm2 ];
makeFlags = [
"INSTALL_DIR=$(out)/lib/lv2"
];
meta = with stdenv.lib; {
homepage = "https://hyperglitch.com/dev/VocProc";
description = "An LV2 plugin for pitch shifting (with or without formant correction), vocoding, automatic pitch correction and harmonizing of singing voice (harmonizer)";
license = licenses.gpl2;
maintainers = [ maintainers.michalrus ];
platforms = platforms.linux;
};
}

View file

@ -7,13 +7,13 @@ with stdenv.lib;
mkDerivation rec {
name = "bitcoin" + (toString (optional (!withGui) "d")) + "-abc-" + version;
version = "0.21.12";
version = "0.21.13";
src = fetchFromGitHub {
owner = "bitcoin-ABC";
repo = "bitcoin-abc";
rev = "v${version}";
sha256 = "1mad3aqfwrxi06135nf8hv13d67nilmxpx4dw5vjcy1zi3lljj1j";
sha256 = "1x8xcdi1vcskggk9bqkwr3ah4vi9b7sj2h8hf7spac6dvz8lmzav";
};
patches = [ ./fix-bitcoin-qt-build.patch ];

View file

@ -4,11 +4,11 @@
with stdenv.lib;
stdenv.mkDerivation rec {
pname = "clightning";
version = "0.9.0";
version = "0.9.0-1";
src = fetchurl {
url = "https://github.com/ElementsProject/lightning/releases/download/v${version}/clightning-v${version}.zip";
sha256 = "11ig5bqxvhx82gq9nl7c5iqaf3x8xbwfx7cf2318pyqdimz4r1v6";
sha256 = "01cwcrqysqsrf96bbbj0grm8j5m46a3acgwy0kzxdx05jdzld9sc";
};
enableParallelBuilding = true;

View file

@ -2,11 +2,11 @@
stdenv.mkDerivation rec {
pname = "ergo";
version = "3.3.0";
version = "3.3.1";
src = fetchurl {
url = "https://github.com/ergoplatform/ergo/releases/download/v${version}/ergo-${version}.jar";
sha256 = "1lja4ba6bm1jk0lh2ra5v8i5g3f1gy7mk2b3yrx1w7x02ll9gr06";
sha256 = "1qr1vfb6mhm2hxl2ksydkhadm7phadn93lwm3f9zni01plk56bb5";
};
nativeBuildInputs = [ makeWrapper ];

View file

@ -2,13 +2,13 @@
buildGoModule rec {
pname = "go-ethereum";
version = "1.9.19";
version = "1.9.20";
src = fetchFromGitHub {
owner = "ethereum";
repo = pname;
rev = "v${version}";
sha256 = "08wf7qklk31dky2z0l2j9vbyr8721gkvy4dsc60afwrwihwd8lrp";
sha256 = "031cbl8yqw5g5yrm5h1x8s5ckdw2xkym46009l579zvafn2vcnj7";
};
runVend = true;
@ -42,6 +42,6 @@ buildGoModule rec {
homepage = "https://geth.ethereum.org/";
description = "Official golang implementation of the Ethereum protocol";
license = with licenses; [ lgpl3 gpl3 ];
maintainers = with maintainers; [ adisbladis lionello xrelkd ];
maintainers = with maintainers; [ adisbladis lionello xrelkd RaghavSood ];
};
}

View file

@ -10,13 +10,13 @@ assert stdenv.isDarwin -> IOKit != null;
stdenv.mkDerivation rec {
pname = "monero";
version = "0.16.0.1";
version = "0.16.0.3";
src = fetchFromGitHub {
owner = "monero-project";
repo = "monero";
rev = "v${version}";
sha256 = "0n2cviqm8radpynx70fc0819k1xknjc58cvb4whlc49ilyvh8ky6";
sha256 = "1r9x3712vhb24dxxirfiwj5f9x0h4m7x0ngiiavf5983dfdlgz33";
fetchSubmodules = true;
};

View file

@ -19,9 +19,9 @@ let
sha256Hash = "11lkwcbzdl86cyz4lci65cx9z5jjhrc4z40maqx2r5hw1xka9290";
};
latestVersion = { # canary & dev
version = "4.2.0.5"; # "Android Studio 4.2 Canary 5"
build = "201.6682321";
sha256Hash = "076q6d7kmi0wcsqak7n6ggp1qns4xj1134xcpdzb92qk3dmg3wrh";
version = "4.2.0.7"; # "Android Studio 4.2 Canary 7"
build = "201.6720134";
sha256Hash = "1c9s6rd0z596qr7hbil5rl3fqby7c8h7ma52d1qj5rxra73k77nz";
};
in {
# Attributes are named by their corresponding release channels

View file

@ -1,18 +1,18 @@
{ stdenv, pkgconfig, qt5, fetchFromGitHub }:
with qt5;
stdenv.mkDerivation rec {
version = "0.10.0";
{ stdenv, mkDerivation, pkgconfig, qmake, qttools, qtbase, qtsvg, qtx11extras, fetchFromGitHub }:
mkDerivation rec {
pname = "featherpad";
version = "0.10.0";
src = fetchFromGitHub {
owner = "tsujan";
repo = "FeatherPad";
rev = "V${version}";
sha256 = "1wrbs6kni9s3x39cckm9kzpglryxn5vyarilvh9pafbzpc6rc57p";
};
nativeBuildInputs = [ qmake pkgconfig qttools ];
buildInputs = [ qtbase qtsvg qtx11extras ];
meta = with stdenv.lib; {
description = "Lightweight Qt5 Plain-Text Editor for Linux";
homepage = "https://github.com/tsujan/FeatherPad";

View file

@ -0,0 +1,27 @@
{ stdenv, fetchFromGitLab }:
stdenv.mkDerivation {
name = "case.kak";
version = "unstable-2020-04-06";
src = fetchFromGitLab {
owner = "FlyingWombat";
repo = "case.kak";
rev = "6f1511820aa3abfa118e0f856118adc8113e2185";
sha256 = "002njrlwgakqgp74wivbppr9qyn57dn4n5bxkr6k6nglk9qndwdp";
};
installPhase = ''
mkdir -p $out/share/kak/autoload/plugins
cp -r rc/case.kak $out/share/kak/autoload/plugins
'';
meta = with stdenv.lib; {
description = "Ease navigation between opened buffers in Kakoune";
homepage = "https://gitlab.com/FlyingWombat/case.kak";
license = licenses.unlicense;
maintainers = with maintainers; [ eraserhd ];
platform = platforms.all;
};
}

Some files were not shown because too many files have changed in this diff Show more