Merge branch 'master' into feature/plasma-5-18

This commit is contained in:
Thomas Tuegel 2020-08-31 20:30:56 -05:00
commit 5091d358b8
No known key found for this signature in database
GPG Key ID: 22CBF5249D4B4D59
1572 changed files with 33521 additions and 28080 deletions

View File

@ -57,10 +57,13 @@ indent_size = unset
[deps.nix]
insert_final_newline = unset
[eggs.nix]
trim_trailing_whitespace = unset
[gemset.nix]
insert_final_newline = unset
[node-packages.nix]
[node-{composition,packages}.nix]
insert_final_newline = unset
[nixos/modules/services/networking/ircd-hybrid/*.{conf,in}]
@ -102,5 +105,8 @@ insert_final_newline = unset
indent_size = unset
trim_trailing_whitespace = unset
[pkgs/top-level/emscripten-packages.nix]
trim_trailing_whitespace = unset
[pkgs/top-level/perl-packages.nix]
indent_size = unset

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

27
.github/workflows/editorconfig.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: "Checking EditorConfig"
on:
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: technote-space/get-diff-action@v3.1.0
- name: Fetch editorconfig-checker
if: env.GIT_DIFF
env:
ECC_VERSION: "2.1.0"
ECC_URL: "https://github.com/editorconfig-checker/editorconfig-checker/releases/download"
run: |
curl -sSf -O -L -C - "$ECC_URL/$ECC_VERSION/ec-linux-amd64.tar.gz" && \
tar xzf ec-linux-amd64.tar.gz && \
mv ./bin/ec-linux-amd64 ./bin/editorconfig-checker
- name: Checking EditorConfig
if: env.GIT_DIFF
run: |
./bin/editorconfig-checker -disable-indentation \
${{ env.GIT_DIFF }}

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

@ -436,6 +436,12 @@ lib.mapAttrs (n: v: v // { shortName = n; }) {
};
# Proprietary binaries; free to redistribute without modification.
databricks = {
fullName = "Databricks Proprietary License";
url = "https://pypi.org/project/databricks-connect";
free = false;
};
issl = {
fullName = "Intel Simplified Software License";
url = "https://software.intel.com/en-us/license/intel-simplified-software-license";

View File

@ -76,6 +76,7 @@ rec {
# uname -r
release = null;
};
isStatic = final.isWasm || final.isRedox;
kernelArch =
if final.isAarch32 then "arm"

View File

@ -46,7 +46,7 @@ rec {
armv7a-android-prebuilt = {
config = "armv7a-unknown-linux-androideabi";
sdkVer = "24";
sdkVer = "29";
ndkVer = "18b";
platform = platforms.armv7a-android;
useAndroidPrebuilt = true;
@ -54,7 +54,7 @@ rec {
aarch64-android-prebuilt = {
config = "aarch64-unknown-linux-android";
sdkVer = "24";
sdkVer = "29";
ndkVer = "18b";
platform = platforms.aarch64-multiplatform;
useAndroidPrebuilt = true;

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";
@ -2861,6 +2879,12 @@
githubId = 5918766;
name = "Franz Thoma";
};
fooker = {
email = "fooker@lab.sh";
github = "fooker";
githubId = 405105;
name = "Dustin Frisch";
};
forkk = {
email = "forkk@forkk.net";
github = "forkk";
@ -3343,6 +3367,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";
@ -3370,10 +3400,14 @@
name = "Hlodver Sigurdsson";
};
hugoreeves = {
email = "hugolreeves@gmail.com";
email = "hugo@hugoreeves.com";
github = "hugoreeves";
githubId = 20039091;
name = "Hugo Reeves";
keys = [{
longkeyid = "rsa4096/0x49FA39F8A7F735F9";
fingerprint = "78C2 E81C 828A 420B 269A EBC1 49FA 39F8 A7F7 35F9";
}];
};
hodapp = {
email = "hodapp87@gmail.com";
@ -3715,6 +3749,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 +3875,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";
@ -4264,6 +4310,12 @@
githubId = 494012;
name = "Kevin Cox";
};
kfollesdal = {
email = "kfollesdal@gmail.com";
github = "kfollesdal";
githubId = 546087;
name = "Kristoffer K. Føllesdal";
};
khumba = {
email = "bog@khumba.net";
github = "khumba";
@ -6365,6 +6417,12 @@
githubId = 157610;
name = "Piotr Bogdan";
};
pblkt = {
email = "pebblekite@gmail.com";
github = "pblkt";
githubId = 6498458;
name = "pebble kite";
};
pcarrier = {
email = "pc@rrier.ca";
github = "pcarrier";
@ -6713,6 +6771,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";
@ -6749,6 +6813,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";
@ -7297,6 +7367,12 @@
githubId = 1153271;
name = "Sander van der Burg";
};
sarcasticadmin = {
email = "rob@sarcasticadmin.com";
github = "sarcasticadmin";
githubId = 30531572;
name = "Robert James Hernandez";
};
sargon = {
email = "danielehlers@mindeye.net";
github = "sargon";
@ -7931,6 +8007,12 @@
githubId = 65870;
name = "Сухарик";
};
SuperSandro2000 = {
email = "sandro.jaeckel@gmail.com";
github = "SuperSandro2000";
githubId = 7258858;
name = "Sandro Jäckel";
};
SuprDewd = {
email = "suprdewd@gmail.com";
github = "SuprDewd";
@ -8127,6 +8209,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";
@ -8517,6 +8605,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";
@ -8732,6 +8826,16 @@
fingerprint = "B3C0 DA1A C18B 82E8 CA8B B1D1 4F62 CD07 CE64 796A";
}];
};
vincentbernat = {
email = "vincent@bernat.ch";
github = "vincentbernat";
githubId = 631446;
name = "Vincent Bernat";
keys = [{
longkeyid = "rsa4096/0x95A42FE8353525F9";
fingerprint = "AEF2 3487 66F3 71C6 89A7 3600 95A4 2FE8 3535 25F9";
}];
};
vinymeuh = {
email = "vinymeuh@gmail.com";
github = "vinymeuh";
@ -9126,6 +9230,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";
@ -9398,4 +9512,26 @@
github = "fzakaria";
githubId = 605070;
};
nagisa = {
name = "Simonas Kazlauskas";
email = "nixpkgs@kazlauskas.me";
github = "nagisa";
githubId = 679122;
};
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

@ -35,6 +35,10 @@ lua-cmsgpack,,,,,
lua-iconv,,,,,
lua-lsp,,http://luarocks.org/dev,,,
lua-messagepack,,,,,
lua-resty-http,,,,,
lua-resty-jwt,,,,,
lua-resty-openidc,,,,,
lua-resty-session,,,,,
lua-term,,,,,
lua-toml,,,,,
lua-zlib,,,,,koral

1 # nix name luarocks name server version luaversion maintainers
35 lua-iconv
36 lua-lsp http://luarocks.org/dev
37 lua-messagepack
38 lua-resty-http
39 lua-resty-jwt
40 lua-resty-openidc
41 lua-resty-session
42 lua-term
43 lua-toml
44 lua-zlib koral

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,64 @@ 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>
<listitem>
<para>
Following its deprecation in 20.03, the Perl NixOS test driver has been removed.
All remaining tests have been ported to the Python test framework.
Code outside nixpkgs using <filename>make-test.nix</filename> or
<filename>testing.nix</filename> needs to be ported to
<filename>make-test-python.nix</filename> and
<filename>testing-python.nix</filename> respectively.
</para>
</listitem>
</itemizedlist>
</section>
@ -175,6 +233,11 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
<para>
There is a new <xref linkend="opt-security.doas.enable"/> module that provides <command>doas</command>, a lighter alternative to <command>sudo</command> with many of the same features.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://hercules-ci.com">Hercules CI</link> Agent is a specialized build agent for projects built with Nix. See the <link xlink:href="https://nixos.org/nixos/options.html#services.hercules-ci-agent">options</link> and <link xlink:href="https://docs.hercules-ci.com/hercules-ci/getting-started/#deploy-agent">setup</link>.
</para>
</listitem>
</itemizedlist>
@ -199,7 +262,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 +274,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 +286,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 +760,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 +985,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,75 +0,0 @@
package Logger;
use strict;
use Thread::Queue;
use XML::Writer;
use Encode qw(decode encode);
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
sub new {
my ($class) = @_;
my $logFile = defined $ENV{LOGFILE} ? "$ENV{LOGFILE}" : "/dev/null";
my $log = new XML::Writer(OUTPUT => new IO::File(">$logFile"));
my $self = {
log => $log,
logQueue => Thread::Queue->new()
};
$self->{log}->startTag("logfile");
bless $self, $class;
return $self;
}
sub close {
my ($self) = @_;
$self->{log}->endTag("logfile");
$self->{log}->end;
}
sub drainLogQueue {
my ($self) = @_;
while (defined (my $item = $self->{logQueue}->dequeue_nb())) {
$self->{log}->dataElement("line", sanitise($item->{msg}), 'machine' => $item->{machine}, 'type' => 'serial');
}
}
sub maybePrefix {
my ($msg, $attrs) = @_;
$msg = $attrs->{machine} . ": " . $msg if defined $attrs->{machine};
return $msg;
}
sub nest {
my ($self, $msg, $coderef, $attrs) = @_;
print STDERR maybePrefix("$msg\n", $attrs);
$self->{log}->startTag("nest");
$self->{log}->dataElement("head", $msg, %{$attrs});
my $now = clock_gettime(CLOCK_MONOTONIC);
$self->drainLogQueue();
eval { &$coderef };
my $res = $@;
$self->drainLogQueue();
$self->log(sprintf("(%.2f seconds)", clock_gettime(CLOCK_MONOTONIC) - $now));
$self->{log}->endTag("nest");
die $@ if $@;
}
sub sanitise {
my ($s) = @_;
$s =~ s/[[:cntrl:]\xff]//g;
$s = decode('UTF-8', $s, Encode::FB_DEFAULT);
return encode('UTF-8', $s, Encode::FB_CROAK);
}
sub log {
my ($self, $msg, $attrs) = @_;
chomp $msg;
print STDERR maybePrefix("$msg\n", $attrs);
$self->drainLogQueue();
$self->{log}->dataElement("line", $msg, %{$attrs});
}
1;

View File

@ -1,734 +0,0 @@
package Machine;
use strict;
use threads;
use Socket;
use IO::Handle;
use POSIX qw(dup2);
use FileHandle;
use Cwd;
use File::Basename;
use File::Path qw(make_path);
use File::Slurp;
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
my $showGraphics = defined $ENV{'DISPLAY'};
my $sharedDir;
sub new {
my ($class, $args) = @_;
my $startCommand = $args->{startCommand};
my $name = $args->{name};
if (!$name) {
$startCommand =~ /run-(.*)-vm$/ if defined $startCommand;
$name = $1 || "machine";
}
if (!$startCommand) {
# !!! merge with qemu-vm.nix.
my $netBackend = "-netdev user,id=net0";
my $netFrontend = "-device virtio-net-pci,netdev=net0";
$netBackend .= "," . $args->{netBackendArgs}
if defined $args->{netBackendArgs};
$netFrontend .= "," . $args->{netFrontendArgs}
if defined $args->{netFrontendArgs};
$startCommand =
"qemu-kvm -m 384 $netBackend $netFrontend \$QEMU_OPTS ";
if (defined $args->{hda}) {
if ($args->{hdaInterface} eq "scsi") {
$startCommand .= "-drive id=hda,file="
. Cwd::abs_path($args->{hda})
. ",werror=report,if=none "
. "-device scsi-hd,drive=hda ";
} else {
$startCommand .= "-drive file=" . Cwd::abs_path($args->{hda})
. ",if=" . $args->{hdaInterface}
. ",werror=report ";
}
}
$startCommand .= "-cdrom $args->{cdrom} "
if defined $args->{cdrom};
$startCommand .= "-device piix3-usb-uhci -drive id=usbdisk,file=$args->{usb},if=none,readonly -device usb-storage,drive=usbdisk "
if defined $args->{usb};
$startCommand .= "-bios $args->{bios} "
if defined $args->{bios};
$startCommand .= $args->{qemuFlags} || "";
}
my $tmpDir = $ENV{'TMPDIR'} || "/tmp";
unless (defined $sharedDir) {
$sharedDir = $tmpDir . "/xchg-shared";
make_path($sharedDir, { mode => 0700, owner => $< });
}
my $allowReboot = 0;
$allowReboot = $args->{allowReboot} if defined $args->{allowReboot};
my $self = {
startCommand => $startCommand,
name => $name,
allowReboot => $allowReboot,
booted => 0,
pid => 0,
connected => 0,
socket => undef,
stateDir => "$tmpDir/vm-state-$name",
monitor => undef,
log => $args->{log},
redirectSerial => $args->{redirectSerial} // 1,
};
mkdir $self->{stateDir}, 0700;
bless $self, $class;
return $self;
}
sub log {
my ($self, $msg) = @_;
$self->{log}->log($msg, { machine => $self->{name} });
}
sub nest {
my ($self, $msg, $coderef, $attrs) = @_;
$self->{log}->nest($msg, $coderef, { %{$attrs || {}}, machine => $self->{name} });
}
sub name {
my ($self) = @_;
return $self->{name};
}
sub stateDir {
my ($self) = @_;
return $self->{stateDir};
}
sub start {
my ($self) = @_;
return if $self->{booted};
$self->log("starting vm");
# Create a socket pair for the serial line input/output of the VM.
my ($serialP, $serialC);
socketpair($serialP, $serialC, PF_UNIX, SOCK_STREAM, 0) or die;
# Create a Unix domain socket to which QEMU's monitor will connect.
my $monitorPath = $self->{stateDir} . "/monitor";
unlink $monitorPath;
my $monitorS;
socket($monitorS, PF_UNIX, SOCK_STREAM, 0) or die;
bind($monitorS, sockaddr_un($monitorPath)) or die "cannot bind monitor socket: $!";
listen($monitorS, 1) or die;
# Create a Unix domain socket to which the root shell in the guest will connect.
my $shellPath = $self->{stateDir} . "/shell";
unlink $shellPath;
my $shellS;
socket($shellS, PF_UNIX, SOCK_STREAM, 0) or die;
bind($shellS, sockaddr_un($shellPath)) or die "cannot bind shell socket: $!";
listen($shellS, 1) or die;
# Start the VM.
my $pid = fork();
die if $pid == -1;
if ($pid == 0) {
close $serialP;
close $monitorS;
close $shellS;
if ($self->{redirectSerial}) {
open NUL, "</dev/null" or die;
dup2(fileno(NUL), fileno(STDIN));
dup2(fileno($serialC), fileno(STDOUT));
dup2(fileno($serialC), fileno(STDERR));
}
$ENV{TMPDIR} = $self->{stateDir};
$ENV{SHARED_DIR} = $sharedDir;
$ENV{USE_TMPDIR} = 1;
$ENV{QEMU_OPTS} =
($self->{allowReboot} ? "" : "-no-reboot ") .
"-monitor unix:./monitor -chardev socket,id=shell,path=./shell " .
"-device virtio-serial -device virtconsole,chardev=shell " .
"-device virtio-rng-pci " .
($showGraphics ? "-serial stdio" : "-nographic") . " " . ($ENV{QEMU_OPTS} || "");
chdir $self->{stateDir} or die;
exec $self->{startCommand};
die "running VM script: $!";
}
# Process serial line output.
close $serialC;
threads->create(\&processSerialOutput, $self, $serialP)->detach;
sub processSerialOutput {
my ($self, $serialP) = @_;
while (<$serialP>) {
chomp;
s/\r$//;
print STDERR $self->{name}, "# $_\n";
$self->{log}->{logQueue}->enqueue({msg => $_, machine => $self->{name}}); # !!!
}
}
eval {
local $SIG{CHLD} = sub { die "QEMU died prematurely\n"; };
# Wait until QEMU connects to the monitor.
accept($self->{monitor}, $monitorS) or die;
# Wait until QEMU connects to the root shell socket. QEMU
# does so immediately; this doesn't mean that the root shell
# has connected yet inside the guest.
accept($self->{socket}, $shellS) or die;
$self->{socket}->autoflush(1);
};
die "$@" if $@;
$self->waitForMonitorPrompt;
$self->log("QEMU running (pid $pid)");
$self->{pid} = $pid;
$self->{booted} = 1;
}
# Send a command to the monitor and wait for it to finish. TODO: QEMU
# also has a JSON-based monitor interface now, but it doesn't support
# all commands yet. We should use it once it does.
sub sendMonitorCommand {
my ($self, $command) = @_;
$self->log("sending monitor command: $command");
syswrite $self->{monitor}, "$command\n";
return $self->waitForMonitorPrompt;
}
# Wait until the monitor sends "(qemu) ".
sub waitForMonitorPrompt {
my ($self) = @_;
my $res = "";
my $s;
while (sysread($self->{monitor}, $s, 1024)) {
$res .= $s;
last if $res =~ s/\(qemu\) $//;
}
return $res;
}
# Call the given code reference repeatedly, with 1 second intervals,
# until it returns 1 or a timeout is reached.
sub retry {
my ($coderef) = @_;
my $n;
for ($n = 899; $n >=0; $n--) {
return if &$coderef($n);
sleep 1;
}
die "action timed out after $n seconds";
}
sub connect {
my ($self) = @_;
return if $self->{connected};
$self->nest("waiting for the VM to finish booting", sub {
$self->start;
my $now = clock_gettime(CLOCK_MONOTONIC);
local $SIG{ALRM} = sub { die "timed out waiting for the VM to connect\n"; };
alarm 600;
readline $self->{socket} or die "the VM quit before connecting\n";
alarm 0;
$self->log("connected to guest root shell");
# We're interested in tracking how close we are to `alarm`.
$self->log(sprintf("(connecting took %.2f seconds)", clock_gettime(CLOCK_MONOTONIC) - $now));
$self->{connected} = 1;
});
}
sub waitForShutdown {
my ($self) = @_;
return unless $self->{booted};
$self->nest("waiting for the VM to power off", sub {
waitpid $self->{pid}, 0;
$self->{pid} = 0;
$self->{booted} = 0;
$self->{connected} = 0;
});
}
sub isUp {
my ($self) = @_;
return $self->{booted} && $self->{connected};
}
sub execute_ {
my ($self, $command) = @_;
$self->connect;
print { $self->{socket} } ("( $command ); echo '|!=EOF' \$?\n");
my $out = "";
while (1) {
my $line = readline($self->{socket});
die "connection to VM lost unexpectedly" unless defined $line;
#$self->log("got line: $line");
if ($line =~ /^(.*)\|\!\=EOF\s+(\d+)$/) {
$out .= $1;
$self->log("exit status $2");
return ($2, $out);
}
$out .= $line;
}
}
sub execute {
my ($self, $command) = @_;
my @res;
$self->nest("running command: $command", sub {
@res = $self->execute_($command);
});
return @res;
}
sub succeed {
my ($self, @commands) = @_;
my $res;
foreach my $command (@commands) {
$self->nest("must succeed: $command", sub {
my ($status, $out) = $self->execute_($command);
if ($status != 0) {
$self->log("output: $out");
die "command `$command' did not succeed (exit code $status)\n";
}
$res .= $out;
});
}
return $res;
}
sub mustSucceed {
succeed @_;
}
sub waitUntilSucceeds {
my ($self, $command) = @_;
$self->nest("waiting for success: $command", sub {
retry sub {
my ($status, $out) = $self->execute($command);
return 1 if $status == 0;
};
});
}
sub waitUntilFails {
my ($self, $command) = @_;
$self->nest("waiting for failure: $command", sub {
retry sub {
my ($status, $out) = $self->execute($command);
return 1 if $status != 0;
};
});
}
sub fail {
my ($self, $command) = @_;
$self->nest("must fail: $command", sub {
my ($status, $out) = $self->execute_($command);
die "command `$command' unexpectedly succeeded"
if $status == 0;
});
}
sub mustFail {
fail @_;
}
sub getUnitInfo {
my ($self, $unit, $user) = @_;
my ($status, $lines) = $self->systemctl("--no-pager show \"$unit\"", $user);
return undef if $status != 0;
my $info = {};
foreach my $line (split '\n', $lines) {
$line =~ /^([^=]+)=(.*)$/ or next;
$info->{$1} = $2;
}
return $info;
}
sub systemctl {
my ($self, $q, $user) = @_;
if ($user) {
$q =~ s/'/\\'/g;
return $self->execute("su -l $user -c \$'XDG_RUNTIME_DIR=/run/user/`id -u` systemctl --user $q'");
}
return $self->execute("systemctl $q");
}
# Fail if the given systemd unit is not in the "active" state.
sub requireActiveUnit {
my ($self, $unit) = @_;
$self->nest("checking if unit $unit has reached state 'active'", sub {
my $info = $self->getUnitInfo($unit);
my $state = $info->{ActiveState};
if ($state ne "active") {
die "Expected unit $unit to to be in state 'active' but it is in state $state\n";
};
});
}
# Wait for a systemd unit to reach the "active" state.
sub waitForUnit {
my ($self, $unit, $user) = @_;
$self->nest("waiting for unit $unit", sub {
retry sub {
my $info = $self->getUnitInfo($unit, $user);
my $state = $info->{ActiveState};
die "unit $unit reached state $state\n" if $state eq "failed";
if ($state eq "inactive") {
# If there are no pending jobs, then assume this unit
# will never reach active state.
my ($status, $jobs) = $self->systemctl("list-jobs --full 2>&1", $user);
if ($jobs =~ /No jobs/) { # FIXME: fragile
# Handle the case where the unit may have started
# between the previous getUnitInfo() and
# list-jobs.
my $info2 = $self->getUnitInfo($unit);
die "unit $unit is inactive and there are no pending jobs\n"
if $info2->{ActiveState} eq $state;
}
}
return 1 if $state eq "active";
};
});
}
sub waitForJob {
my ($self, $jobName) = @_;
return $self->waitForUnit($jobName);
}
# Wait until the specified file exists.
sub waitForFile {
my ($self, $fileName) = @_;
$self->nest("waiting for file $fileName", sub {
retry sub {
my ($status, $out) = $self->execute("test -e $fileName");
return 1 if $status == 0;
}
});
}
sub startJob {
my ($self, $jobName, $user) = @_;
$self->systemctl("start $jobName", $user);
# FIXME: check result
}
sub stopJob {
my ($self, $jobName, $user) = @_;
$self->systemctl("stop $jobName", $user);
}
# Wait until the machine is listening on the given TCP port.
sub waitForOpenPort {
my ($self, $port) = @_;
$self->nest("waiting for TCP port $port", sub {
retry sub {
my ($status, $out) = $self->execute("nc -z localhost $port");
return 1 if $status == 0;
}
});
}
# Wait until the machine is not listening on the given TCP port.
sub waitForClosedPort {
my ($self, $port) = @_;
retry sub {
my ($status, $out) = $self->execute("nc -z localhost $port");
return 1 if $status != 0;
}
}
sub shutdown {
my ($self) = @_;
return unless $self->{booted};
print { $self->{socket} } ("poweroff\n");
$self->waitForShutdown;
}
sub crash {
my ($self) = @_;
return unless $self->{booted};
$self->log("forced crash");
$self->sendMonitorCommand("quit");
$self->waitForShutdown;
}
# Make the machine unreachable by shutting down eth1 (the multicast
# interface used to talk to the other VMs). We keep eth0 up so that
# the test driver can continue to talk to the machine.
sub block {
my ($self) = @_;
$self->sendMonitorCommand("set_link virtio-net-pci.1 off");
}
# Make the machine reachable.
sub unblock {
my ($self) = @_;
$self->sendMonitorCommand("set_link virtio-net-pci.1 on");
}
# Take a screenshot of the X server on :0.0.
sub screenshot {
my ($self, $filename) = @_;
my $dir = $ENV{'out'} || Cwd::abs_path(".");
$filename = "$dir/${filename}.png" if $filename =~ /^\w+$/;
my $tmp = "${filename}.ppm";
my $name = basename($filename);
$self->nest("making screenshot $name", sub {
$self->sendMonitorCommand("screendump $tmp");
system("pnmtopng $tmp > ${filename}") == 0
or die "cannot convert screenshot";
unlink $tmp;
}, { image => $name } );
}
# Get the text of TTY<n>
sub getTTYText {
my ($self, $tty) = @_;
my ($status, $out) = $self->execute("fold -w\$(stty -F /dev/tty${tty} size | awk '{print \$2}') /dev/vcs${tty}");
return $out;
}
# Wait until TTY<n>'s text matches a particular regular expression
sub waitUntilTTYMatches {
my ($self, $tty, $regexp) = @_;
$self->nest("waiting for $regexp to appear on tty $tty", sub {
retry sub {
my ($retries_remaining) = @_;
if ($retries_remaining == 0) {
$self->log("Last chance to match /$regexp/ on TTY$tty, which currently contains:");
$self->log($self->getTTYText($tty));
}
return 1 if $self->getTTYText($tty) =~ /$regexp/;
}
});
}
# Debugging: Dump the contents of the TTY<n>
sub dumpTTYContents {
my ($self, $tty) = @_;
$self->execute("fold -w 80 /dev/vcs${tty} | systemd-cat");
}
# Take a screenshot and return the result as text using optical character
# recognition.
sub getScreenText {
my ($self) = @_;
system("command -v tesseract &> /dev/null") == 0
or die "getScreenText used but enableOCR is false";
my $text;
$self->nest("performing optical character recognition", sub {
my $tmpbase = Cwd::abs_path(".")."/ocr";
my $tmpin = $tmpbase."in.ppm";
$self->sendMonitorCommand("screendump $tmpin");
my $magickArgs = "-filter Catrom -density 72 -resample 300 "
. "-contrast -normalize -despeckle -type grayscale "
. "-sharpen 1 -posterize 3 -negate -gamma 100 "
. "-blur 1x65535";
my $tessArgs = "-c debug_file=/dev/null --psm 11 --oem 2";
$text = `convert $magickArgs $tmpin tiff:- | tesseract - - $tessArgs`;
my $status = $? >> 8;
unlink $tmpin;
die "OCR failed with exit code $status" if $status != 0;
});
return $text;
}
# Wait until a specific regexp matches the textual contents of the screen.
sub waitForText {
my ($self, $regexp) = @_;
$self->nest("waiting for $regexp to appear on the screen", sub {
retry sub {
my ($retries_remaining) = @_;
if ($retries_remaining == 0) {
$self->log("Last chance to match /$regexp/ on the screen, which currently contains:");
$self->log($self->getScreenText);
}
return 1 if $self->getScreenText =~ /$regexp/;
}
});
}
# Wait until it is possible to connect to the X server. Note that
# testing the existence of /tmp/.X11-unix/X0 is insufficient.
sub waitForX {
my ($self, $regexp) = @_;
$self->nest("waiting for the X11 server", sub {
retry sub {
my ($status, $out) = $self->execute("journalctl -b SYSLOG_IDENTIFIER=systemd | grep 'Reached target Current graphical'");
return 0 if $status != 0;
($status, $out) = $self->execute("[ -e /tmp/.X11-unix/X0 ]");
return 1 if $status == 0;
}
});
}
sub getWindowNames {
my ($self) = @_;
my $res = $self->mustSucceed(
q{xwininfo -root -tree | sed 's/.*0x[0-9a-f]* \"\([^\"]*\)\".*/\1/; t; d'});
return split /\n/, $res;
}
sub waitForWindow {
my ($self, $regexp) = @_;
$self->nest("waiting for a window to appear", sub {
retry sub {
my @names = $self->getWindowNames;
my ($retries_remaining) = @_;
if ($retries_remaining == 0) {
$self->log("Last chance to match /$regexp/ on the the window list, which currently contains:");
$self->log(join(", ", @names));
}
foreach my $n (@names) {
return 1 if $n =~ /$regexp/;
}
}
});
}
sub copyFileFromHost {
my ($self, $from, $to) = @_;
my $s = `cat $from` or die;
$s =~ s/'/'\\''/g;
$self->mustSucceed("echo '$s' > $to");
}
my %charToKey = (
'A' => "shift-a", 'N' => "shift-n", '-' => "0x0C", '_' => "shift-0x0C", '!' => "shift-0x02",
'B' => "shift-b", 'O' => "shift-o", '=' => "0x0D", '+' => "shift-0x0D", '@' => "shift-0x03",
'C' => "shift-c", 'P' => "shift-p", '[' => "0x1A", '{' => "shift-0x1A", '#' => "shift-0x04",
'D' => "shift-d", 'Q' => "shift-q", ']' => "0x1B", '}' => "shift-0x1B", '$' => "shift-0x05",
'E' => "shift-e", 'R' => "shift-r", ';' => "0x27", ':' => "shift-0x27", '%' => "shift-0x06",
'F' => "shift-f", 'S' => "shift-s", '\'' => "0x28", '"' => "shift-0x28", '^' => "shift-0x07",
'G' => "shift-g", 'T' => "shift-t", '`' => "0x29", '~' => "shift-0x29", '&' => "shift-0x08",
'H' => "shift-h", 'U' => "shift-u", '\\' => "0x2B", '|' => "shift-0x2B", '*' => "shift-0x09",
'I' => "shift-i", 'V' => "shift-v", ',' => "0x33", '<' => "shift-0x33", '(' => "shift-0x0A",
'J' => "shift-j", 'W' => "shift-w", '.' => "0x34", '>' => "shift-0x34", ')' => "shift-0x0B",
'K' => "shift-k", 'X' => "shift-x", '/' => "0x35", '?' => "shift-0x35",
'L' => "shift-l", 'Y' => "shift-y", ' ' => "spc",
'M' => "shift-m", 'Z' => "shift-z", "\n" => "ret",
);
sub sendKeys {
my ($self, @keys) = @_;
foreach my $key (@keys) {
$key = $charToKey{$key} if exists $charToKey{$key};
$self->sendMonitorCommand("sendkey $key");
}
}
sub sendChars {
my ($self, $chars) = @_;
$self->nest("sending keys $chars", sub {
$self->sendKeys(split //, $chars);
});
}
# Sleep N seconds (in virtual guest time, not real time).
sub sleep {
my ($self, $time) = @_;
$self->succeed("sleep $time");
}
# Forward a TCP port on the host to a TCP port on the guest. Useful
# during interactive testing.
sub forwardPort {
my ($self, $hostPort, $guestPort) = @_;
$hostPort = 8080 unless defined $hostPort;
$guestPort = 80 unless defined $guestPort;
$self->sendMonitorCommand("hostfwd_add tcp::$hostPort-:$guestPort");
}
1;

View File

@ -1,191 +0,0 @@
#! /somewhere/perl -w
use strict;
use Machine;
use Term::ReadLine;
use IO::File;
use IO::Pty;
use Logger;
use Cwd;
use POSIX qw(_exit dup2);
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
$SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
STDERR->autoflush(1);
my $log = new Logger;
# Start vde_switch for each network required by the test.
my %vlans;
foreach my $vlan (split / /, $ENV{VLANS} || "") {
next if defined $vlans{$vlan};
# Start vde_switch as a child process. We don't run it in daemon
# mode because we want the child process to be cleaned up when we
# die. Since we have to make sure that the control socket is
# ready, we send a dummy command to vde_switch (via stdin) and
# wait for a reply. Note that vde_switch requires stdin to be a
# TTY, so we create one.
$log->log("starting VDE switch for network $vlan");
my $socket = Cwd::abs_path "./vde$vlan.ctl";
my $pty = new IO::Pty;
my ($stdoutR, $stdoutW); pipe $stdoutR, $stdoutW;
my $pid = fork(); die "cannot fork" unless defined $pid;
if ($pid == 0) {
dup2(fileno($pty->slave), 0);
dup2(fileno($stdoutW), 1);
exec "vde_switch -s $socket --dirmode 0700" or _exit(1);
}
close $stdoutW;
print $pty "version\n";
readline $stdoutR or die "cannot start vde_switch";
$ENV{"QEMU_VDE_SOCKET_$vlan"} = $socket;
$vlans{$vlan} = $pty;
die unless -e "$socket/ctl";
}
my %vms;
my $context = "";
sub createMachine {
my ($args) = @_;
my $vm = Machine->new({%{$args}, log => $log, redirectSerial => ($ENV{USE_SERIAL} // "0") ne "1"});
$vms{$vm->name} = $vm;
$context .= "my \$" . $vm->name . " = \$vms{'" . $vm->name . "'}; ";
return $vm;
}
foreach my $vmScript (@ARGV) {
my $vm = createMachine({startCommand => $vmScript});
}
sub startAll {
$log->nest("starting all VMs", sub {
$_->start foreach values %vms;
});
}
# Wait until all VMs have terminated.
sub joinAll {
$log->nest("waiting for all VMs to finish", sub {
$_->waitForShutdown foreach values %vms;
});
}
# In interactive tests, this allows the non-interactive test script to
# be executed conveniently.
sub testScript {
eval "$context $ENV{testScript};\n";
warn $@ if $@;
}
my $nrTests = 0;
my $nrSucceeded = 0;
sub subtest {
my ($name, $coderef) = @_;
$log->nest("subtest: $name", sub {
$nrTests++;
eval { &$coderef };
if ($@) {
$log->log("error: $@", { error => 1 });
} else {
$nrSucceeded++;
}
});
}
sub runTests {
if (defined $ENV{tests}) {
$log->nest("running the VM test script", sub {
eval "$context $ENV{tests}";
if ($@) {
$log->log("error: $@", { error => 1 });
die $@;
}
}, { expanded => 1 });
} else {
my $term = Term::ReadLine->new('nixos-vm-test');
$term->ReadHistory;
while (defined ($_ = $term->readline("> "))) {
eval "$context $_\n";
warn $@ if $@;
}
$term->WriteHistory;
}
# Copy the kernel coverage data for each machine, if the kernel
# has been compiled with coverage instrumentation.
$log->nest("collecting coverage data", sub {
foreach my $vm (values %vms) {
my $gcovDir = "/sys/kernel/debug/gcov";
next unless $vm->isUp();
my ($status, $out) = $vm->execute("test -e $gcovDir");
next if $status != 0;
# Figure out where to put the *.gcda files so that the
# report generator can find the corresponding kernel
# sources.
my $kernelDir = $vm->mustSucceed("echo \$(dirname \$(readlink -f /run/current-system/kernel))/.build/linux-*");
chomp $kernelDir;
my $coverageDir = "/tmp/xchg/coverage-data/$kernelDir";
# Copy all the *.gcda files.
$vm->execute("for d in $gcovDir/nix/store/*/.build/linux-*; do for i in \$(cd \$d && find -name '*.gcda'); do echo \$i; mkdir -p $coverageDir/\$(dirname \$i); cp -v \$d/\$i $coverageDir/\$i; done; done");
}
});
$log->nest("syncing", sub {
foreach my $vm (values %vms) {
next unless $vm->isUp();
$vm->execute("sync");
}
});
if ($nrTests != 0) {
$log->log("$nrSucceeded out of $nrTests tests succeeded",
($nrSucceeded < $nrTests ? { error => 1 } : { }));
}
}
# Create an empty raw virtual disk with the given name and size (in
# MiB).
sub createDisk {
my ($name, $size) = @_;
system("qemu-img create -f raw $name ${size}M") == 0
or die "cannot create image of size $size";
}
END {
$log->nest("cleaning up", sub {
foreach my $vm (values %vms) {
if ($vm->{pid}) {
$log->log("killing " . $vm->{name} . " (pid " . $vm->{pid} . ")");
kill 9, $vm->{pid};
}
}
});
$log->close();
}
my $now1 = clock_gettime(CLOCK_MONOTONIC);
runTests;
my $now2 = clock_gettime(CLOCK_MONOTONIC);
printf STDERR "test script finished in %.2fs\n", $now2 - $now1;
exit ($nrSucceeded < $nrTests ? 1 : 0);

View File

@ -424,15 +424,18 @@ class Machine:
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)
(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.
@ -837,7 +840,8 @@ class Machine:
retry(window_is_visible)
def sleep(self, secs: int) -> None:
time.sleep(secs)
# We want to sleep in *guest* time, not *host* time.
self.succeed(f"sleep {secs}")
def forward_port(self, host_port: int = 8080, guest_port: int = 80) -> None:
"""Forward a TCP port on the host to a TCP port on the guest.

View File

@ -1,258 +0,0 @@
{ system
, pkgs ? import ../.. { inherit system config; }
# Use a minimal kernel?
, minimal ? false
# Ignored
, config ? {}
# Modules to add to each VM
, extraConfigurations ? [] }:
with import ./build-vms.nix { inherit system pkgs minimal extraConfigurations; };
with pkgs;
rec {
inherit pkgs;
testDriver = lib.warn ''
Perl VM tests are deprecated and will be removed for 20.09.
Please update your tests to use the python test driver.
See https://github.com/NixOS/nixpkgs/pull/71684 for details.
'' stdenv.mkDerivation {
name = "nixos-test-driver";
buildInputs = [ makeWrapper perl ];
dontUnpack = true;
preferLocalBuild = true;
installPhase =
''
mkdir -p $out/bin
cp ${./test-driver/test-driver.pl} $out/bin/nixos-test-driver
chmod u+x $out/bin/nixos-test-driver
libDir=$out/${perl.libPrefix}
mkdir -p $libDir
cp ${./test-driver/Machine.pm} $libDir/Machine.pm
cp ${./test-driver/Logger.pm} $libDir/Logger.pm
wrapProgram $out/bin/nixos-test-driver \
--prefix PATH : "${lib.makeBinPath [ qemu_test vde2 netpbm coreutils ]}" \
--prefix PERL5LIB : "${with perlPackages; makePerlPath [ TermReadLineGnu XMLWriter IOTty FileSlurp ]}:$out/${perl.libPrefix}"
'';
};
# Run an automated test suite in the given virtual network.
# `driver' is the script that runs the network.
runTests = driver:
stdenv.mkDerivation {
name = "vm-test-run-${driver.testName}";
requiredSystemFeatures = [ "kvm" "nixos-test" ];
buildCommand =
''
mkdir -p $out
LOGFILE=/dev/null tests='eval $ENV{testScript}; die $@ if $@;' ${driver}/bin/nixos-test-driver
for i in */xchg/coverage-data; do
mkdir -p $out/coverage-data
mv $i $out/coverage-data/$(dirname $(dirname $i))
done
'';
};
makeTest =
{ testScript
, makeCoverageReport ? false
, enableOCR ? false
, name ? "unnamed"
, ...
} @ t:
let
# A standard store path to the vm monitor is built like this:
# /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
# The max filename length of a unix domain socket is 108 bytes.
# This means $name can at most be 50 bytes long.
maxTestNameLen = 50;
testNameLen = builtins.stringLength name;
testDriverName = with builtins;
if testNameLen > maxTestNameLen then
abort ("The name of the test '${name}' must not be longer than ${toString maxTestNameLen} " +
"it's currently ${toString testNameLen} characters long.")
else
"nixos-test-driver-${name}";
nodes = buildVirtualNetwork (
t.nodes or (if t ? machine then { machine = t.machine; } else { }));
testScript' =
# Call the test script with the computed nodes.
if lib.isFunction testScript
then testScript { inherit nodes; }
else testScript;
vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
# Generate onvenience wrappers for running the test driver
# interactively with the specified network, and for starting the
# VMs from the command line.
driver = runCommand testDriverName
{ buildInputs = [ makeWrapper];
testScript = testScript';
preferLocalBuild = true;
testName = name;
}
''
mkdir -p $out/bin
echo "$testScript" > $out/test-script
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
wrapProgram $out/bin/nixos-test-driver \
--add-flags "''${vms[*]}" \
${lib.optionalString enableOCR
"--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
--run "export testScript=\"\$(cat $out/test-script)\"" \
--set VLANS '${toString vlans}'
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
wrapProgram $out/bin/nixos-run-vms \
--add-flags "''${vms[*]}" \
${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
--set tests 'startAll; joinAll;' \
--set VLANS '${toString vlans}' \
${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
''; # "
passMeta = drv: drv // lib.optionalAttrs (t ? meta) {
meta = (drv.meta or {}) // t.meta;
};
test = passMeta (runTests driver);
report = passMeta (releaseTools.gcovReport { coverageRuns = [ test ]; });
nodeNames = builtins.attrNames nodes;
invalidNodeNames = lib.filter
(node: builtins.match "^[A-z_][A-z0-9_]+$" node == null) nodeNames;
in
if lib.length invalidNodeNames > 0 then
throw ''
Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
All machines are referenced as perl variables in the testing framework which will break the
script when special characters are used.
Please stick to alphanumeric chars and underscores as separation.
''
else
(if makeCoverageReport then report else test) // {
inherit nodes driver test;
};
runInMachine =
{ drv
, machine
, preBuild ? ""
, postBuild ? ""
, ... # ???
}:
let
vm = buildVM { }
[ machine
{ key = "run-in-machine";
networking.hostName = "client";
nix.readOnlyStore = false;
virtualisation.writableStore = false;
}
];
buildrunner = writeText "vm-build" ''
source $1
${coreutils}/bin/mkdir -p $TMPDIR
cd $TMPDIR
exec $origBuilder $origArgs
'';
testScript = ''
startAll;
$client->waitForUnit("multi-user.target");
${preBuild}
$client->succeed("env -i ${bash}/bin/bash ${buildrunner} /tmp/xchg/saved-env >&2");
${postBuild}
$client->succeed("sync"); # flush all data before pulling the plug
'';
vmRunCommand = writeText "vm-run" ''
xchg=vm-state-client/xchg
${coreutils}/bin/mkdir $out
${coreutils}/bin/mkdir -p $xchg
for i in $passAsFile; do
i2=''${i}Path
_basename=$(${coreutils}/bin/basename ''${!i2})
${coreutils}/bin/cp ''${!i2} $xchg/$_basename
eval $i2=/tmp/xchg/$_basename
${coreutils}/bin/ls -la $xchg
done
unset i i2 _basename
export | ${gnugrep}/bin/grep -v '^xchg=' > $xchg/saved-env
unset xchg
export tests='${testScript}'
${testDriver}/bin/nixos-test-driver ${vm.config.system.build.vm}/bin/run-*-vm
''; # */
in
lib.overrideDerivation drv (attrs: {
requiredSystemFeatures = [ "kvm" ];
builder = "${bash}/bin/sh";
args = ["-e" vmRunCommand];
origArgs = attrs.args;
origBuilder = attrs.builder;
});
runInMachineWithX = { require ? [], ... } @ args:
let
client =
{ ... }:
{
inherit require;
imports = [
../tests/common/auto.nix
];
virtualisation.memorySize = 1024;
services.xserver.enable = true;
test-support.displayManager.auto.enable = true;
services.xserver.displayManager.defaultSession = "none+icewm";
services.xserver.windowManager.icewm.enable = true;
};
in
runInMachine ({
machine = client;
preBuild =
''
$client->waitForX;
'';
} // args);
simpleTest = as: (makeTest as).test;
}

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

@ -108,6 +108,15 @@ in
'';
};
postBuildCommands = mkOption {
example = literalExample "'' dd if=\${pkgs.myBootLoader}/SPL of=$img bs=1024 seek=1 conv=notrunc ''";
default = "";
description = ''
Shell commands to run after the image is built.
Can be used for boards requiring to dd u-boot SPL before actual partitions.
'';
};
compressImage = mkOption {
type = types.bool;
default = true;
@ -197,6 +206,9 @@ in
# Verify the FAT partition before copying it.
fsck.vfat -vn firmware_part.img
dd conv=notrunc if=firmware_part.img of=$img seek=$START count=$SECTORS
${config.sdImage.postBuildCommands}
if test -n "$compressImage"; then
zstd -T$NIX_BUILD_CORES --rm $img
fi

View File

@ -288,7 +288,10 @@ fi
if [ "$action" = edit ]; then
if [[ -z $flake ]]; then
NIXOS_CONFIG=${NIXOS_CONFIG:-$(nix-instantiate --find-file nixos-config)}
exec "${EDITOR:-nano}" "$NIXOS_CONFIG"
if [[ -d $NIXOS_CONFIG ]]; then
NIXOS_CONFIG=$NIXOS_CONFIG/default.nix
fi
exec ${EDITOR:-nano} "$NIXOS_CONFIG"
else
exec nix edit "${lockFlags[@]}" -- "$flake#$flakeAttr"
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

@ -262,6 +262,7 @@
./services/continuous-integration/buildbot/worker.nix
./services/continuous-integration/buildkite-agents.nix
./services/continuous-integration/hail.nix
./services/continuous-integration/hercules-ci-agent/default.nix
./services/continuous-integration/hydra/default.nix
./services/continuous-integration/gitlab-runner.nix
./services/continuous-integration/gocd-agent/default.nix
@ -296,10 +297,10 @@
./services/desktops/accountsservice.nix
./services/desktops/bamf.nix
./services/desktops/blueman.nix
./services/desktops/deepin/deepin.nix
./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
@ -717,6 +718,7 @@
./services/networking/rdnssd.nix
./services/networking/redsocks.nix
./services/networking/resilio.nix
./services/networking/robustirc-bridge.nix
./services/networking/rpcbind.nix
./services/networking/rxe.nix
./services/networking/sabnzbd.nix
@ -886,7 +888,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

@ -23,11 +23,17 @@ in
default = [];
description = "List of packages to be added to apparmor's include path";
};
parserConfig = mkOption {
type = types.str;
default = "";
description = "AppArmor parser configuration file content";
};
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.apparmor-utils ];
environment.etc."apparmor/parser.conf".text = cfg.parserConfig;
boot.kernelParams = [ "apparmor=1" "security=apparmor" ];

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,213 @@
/*
This file is for options that NixOS and nix-darwin have in common.
Platform-specific code is in the respective default.nix files.
*/
{ config, lib, options, pkgs, ... }:
let
inherit (lib) mkOption mkIf types filterAttrs literalExample mkRenamedOptionModule;
cfg =
config.services.hercules-ci-agent;
format = pkgs.formats.toml {};
settingsModule = { config, ... }: {
freeformType = format.type;
options = {
baseDirectory = mkOption {
type = types.path;
default = "/var/lib/hercules-ci-agent";
description = ''
State directory (secrets, work directory, etc) for agent
'';
};
concurrentTasks = mkOption {
description = ''
Number of tasks to perform simultaneously, such as evaluations, derivations.
You must have a total capacity across agents of at least 2 concurrent tasks on <literal>x86_64-linux</literal>
to allow for import from derivation.
'';
type = types.int;
default = 4;
};
workDirectory = mkOption {
description = ''
The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation.
'';
type = types.path;
default = config.baseDirectory + "/work";
defaultText = literalExample ''baseDirectory + "/work"'';
};
staticSecretsDirectory = mkOption {
description = ''
This is the default directory to look for statically configured secrets like <literal>cluster-join-token.key</literal>.
'';
type = types.path;
default = config.baseDirectory + "/secrets";
defaultText = literalExample ''baseDirectory + "/secrets"'';
};
clusterJoinTokenPath = mkOption {
description = ''
Location of the cluster-join-token.key file.
'';
type = types.path;
default = config.staticSecretsDirectory + "/cluster-join-token.key";
defaultText = literalExample ''staticSecretsDirectory + "/cluster-join-token.key"'';
# internal: It's a bit too detailed to show by default in the docs,
# but useful to define explicitly to allow reuse by other modules.
internal = true;
};
binaryCachesPath = mkOption {
description = ''
Location of the binary-caches.json file.
'';
type = types.path;
default = config.staticSecretsDirectory + "/binary-caches.json";
defaultText = literalExample ''staticSecretsDirectory + "/binary-caches.json"'';
# internal: It's a bit too detailed to show by default in the docs,
# but useful to define explicitly to allow reuse by other modules.
internal = true;
};
};
};
checkNix =
if !cfg.checkNix
then ""
else if lib.versionAtLeast config.nix.package.version "2.4.0"
then ""
else pkgs.stdenv.mkDerivation {
name = "hercules-ci-check-system-nix-src";
inherit (config.nix.package) src patches;
configurePhase = ":";
buildPhase = ''
echo "Checking in-memory pathInfoCache expiry"
if ! grep 'struct PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then
cat 1>&2 <<EOF
You are deploying Hercules CI Agent on a system with an incompatible
nix-daemon. Please
- either upgrade Nix to version 2.4.0 (when released),
- or set option services.hercules-ci-agent.patchNix = true;
- or set option nix.package to a build of Nix 2.3 with this patch applied:
https://github.com/NixOS/nix/pull/3405
The patch is required for Nix-daemon clients that expect a change in binary
cache contents while running, like the agent's evaluator. Without it, import
from derivation will fail if your cluster has more than one machine.
We are conservative with changes to the overall system, which is why we
keep changes to a minimum and why we ask for confirmation in the form of
services.hercules-ci-agent.patchNix = true before applying.
EOF
exit 1
fi
'';
installPhase = "touch $out";
};
patchedNix = lib.mkIf (!lib.versionAtLeast pkgs.nix.version "2.4.0") (
if lib.versionAtLeast pkgs.nix.version "2.4pre"
then lib.warn "Hercules CI Agent module will not patch 2.4 pre-release. Make sure it includes (equivalently) PR #3043, commit d048577909 or is no older than 2020-03-13." pkgs.nix
else pkgs.nix.overrideAttrs (
o: {
patches = (o.patches or []) ++ [ backportNix3398 ];
}
)
);
backportNix3398 = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/hercules-ci/hercules-ci-agent/hercules-ci-agent-0.7.3/for-upstream/issue-3398-path-info-cache-ttls-backport-2.3.patch";
sha256 = "0jfckqjir9il2il7904yc1qyadw366y7xqzg81sp9sl3f1pw70ib";
};
in
{
imports = [
(mkRenamedOptionModule ["services" "hercules-ci-agent" "extraOptions"] ["services" "hercules-ci-agent" "settings"])
(mkRenamedOptionModule ["services" "hercules-ci-agent" "baseDirectory"] ["services" "hercules-ci-agent" "settings" "baseDirectory"])
(mkRenamedOptionModule ["services" "hercules-ci-agent" "concurrentTasks"] ["services" "hercules-ci-agent" "settings" "concurrentTasks"])
];
options.services.hercules-ci-agent = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable to run Hercules CI Agent as a system service.
<link xlink:href="https://hercules-ci.com">Hercules CI</link> is a
continuous integation service that is centered around Nix.
Support is available at <link xlink:href="mailto:help@hercules-ci.com">help@hercules-ci.com</link>.
'';
};
patchNix = mkOption {
type = types.bool;
default = false;
description = ''
Fix Nix 2.3 cache path metadata caching behavior. Has the effect of <literal>nix.package = patch pkgs.nix;</literal>
This option will be removed when Hercules CI Agent moves to Nix 2.4 (upcoming Nix release).
'';
};
checkNix = mkOption {
type = types.bool;
default = true;
description = ''
Whether to make sure that the system's Nix (nix-daemon) is compatible.
If you set this to false, please keep up with the change log.
'';
};
package = mkOption {
description = ''
Package containing the bin/hercules-ci-agent executable.
'';
type = types.package;
default = pkgs.hercules-ci-agent;
defaultText = literalExample "pkgs.hercules-ci-agent";
};
settings = mkOption {
description = ''
These settings are written to the <literal>agent.toml</literal> file.
Not all settings are listed as options, can be set nonetheless.
For the exhaustive list of settings, see <link xlink:href="https://docs.hercules-ci.com/hercules-ci/reference/agent-config/"/>.
'';
type = types.submoduleWith { modules = [ settingsModule ]; };
};
/*
Internal and/or computed values.
These are written as options instead of let binding to allow sharing with
default.nix on both NixOS and nix-darwin.
*/
tomlFile = mkOption {
type = types.path;
internal = true;
defaultText = "generated hercules-ci-agent.toml";
description = ''
The fully assembled config file.
'';
};
};
config = mkIf cfg.enable {
nix.extraOptions = lib.addContextFrom checkNix ''
# A store path that was missing at first may well have finished building,
# even shortly after the previous lookup. This *also* applies to the daemon.
narinfo-cache-negative-ttl = 0
'';
nix.package = mkIf cfg.patchNix patchedNix;
services.hercules-ci-agent.tomlFile =
format.generate "hercules-ci-agent.toml" cfg.settings;
};
}

View File

@ -0,0 +1,86 @@
/*
This file is for NixOS-specific options and configs.
Code that is shared with nix-darwin goes in common.nix.
*/
{ pkgs, config, lib, ... }:
let
inherit (lib) mkIf mkDefault;
cfg = config.services.hercules-ci-agent;
command = "${cfg.package}/bin/hercules-ci-agent --config ${cfg.tomlFile}";
testCommand = "${command} --test-configuration";
in
{
imports = [
./common.nix
(lib.mkRenamedOptionModule ["services" "hercules-ci-agent" "user"] ["systemd" "services" "hercules-ci-agent" "serviceConfig" "User"])
];
config = mkIf cfg.enable {
systemd.services.hercules-ci-agent = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
path = [ config.nix.package ];
serviceConfig = {
User = "hercules-ci-agent";
ExecStart = command;
ExecStartPre = testCommand;
Restart = "on-failure";
RestartSec = 120;
StartLimitBurst = 30 * 1000000; # practically infinite
};
};
# Changes in the secrets do not affect the unit in any way that would cause
# a restart, which is currently necessary to reload the secrets.
systemd.paths.hercules-ci-agent-restart-files = {
wantedBy = [ "hercules-ci-agent.service" ];
pathConfig = {
Unit = "hercules-ci-agent-restarter.service";
PathChanged = [ cfg.settings.clusterJoinTokenPath cfg.settings.binaryCachesPath ];
};
};
systemd.services.hercules-ci-agent-restarter = {
serviceConfig.Type = "oneshot";
script = ''
# Wait a bit, with the effect of bundling up file changes into a single
# run of this script and hopefully a single restart.
sleep 10
if systemctl is-active --quiet hercules-ci-agent.service; then
if ${testCommand}; then
systemctl restart hercules-ci-agent.service
else
echo 1>&2 "WARNING: Not restarting agent because config is not valid at this time."
fi
else
echo 1>&2 "Not restarting hercules-ci-agent despite config file update, because it is not already active."
fi
'';
};
# Trusted user allows simplified configuration and better performance
# when operating in a cluster.
nix.trustedUsers = [ config.systemd.services.hercules-ci-agent.serviceConfig.User ];
services.hercules-ci-agent.settings.nixUserIsTrusted = true;
users.users.hercules-ci-agent = {
home = cfg.settings.baseDirectory;
createHome = true;
group = "hercules-ci-agent";
description = "Hercules CI Agent system user";
isSystemUser = true;
};
users.groups.hercules-ci-agent = {};
};
}

View File

@ -11,23 +11,23 @@ let
then cfg.package
else cfg.package.withPackages (_: cfg.extraPlugins);
toStr = value:
if true == value then "yes"
else if false == value then "no"
else if isString value then "'${lib.replaceStrings ["'"] ["''"] value}'"
else toString value;
# The main PostgreSQL configuration file.
configFile = pkgs.writeText "postgresql.conf"
''
hba_file = '${pkgs.writeText "pg_hba.conf" cfg.authentication}'
ident_file = '${pkgs.writeText "pg_ident.conf" cfg.identMap}'
log_destination = 'stderr'
log_line_prefix = '${cfg.logLinePrefix}'
listen_addresses = '${if cfg.enableTCPIP then "*" else "localhost"}'
port = ${toString cfg.port}
${cfg.extraConfig}
'';
configFile = pkgs.writeText "postgresql.conf" (concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${toStr v}") cfg.settings));
groupAccessAvailable = versionAtLeast postgresql.version "11.0";
in
{
imports = [
(mkRemovedOptionModule [ "services" "postgresql" "extraConfig" ] "Use services.postgresql.settings instead.")
];
###### interface
@ -212,10 +212,28 @@ in
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = "Additional text to be appended to <filename>postgresql.conf</filename>.";
settings = mkOption {
type = with types; attrsOf (oneOf [ bool float int str ]);
default = {};
description = ''
PostgreSQL configuration. Refer to
<link xlink:href="https://www.postgresql.org/docs/11/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE"/>
for an overview of <literal>postgresql.conf</literal>.
<note><para>
String values will automatically be enclosed in single quotes. Single quotes will be
escaped with two single quotes as described by the upstream documentation linked above.
</para></note>
'';
example = literalExample ''
{
log_connections = true;
log_statement = "all";
logging_collector = true
log_disconnections = true
log_destination = lib.mkForce "syslog";
}
'';
};
recoveryConfig = mkOption {
@ -245,6 +263,16 @@ in
config = mkIf cfg.enable {
services.postgresql.settings =
{
hba_file = "${pkgs.writeText "pg_hba.conf" cfg.authentication}";
ident_file = "${pkgs.writeText "pg_ident.conf" cfg.identMap}";
log_destination = "stderr";
log_line_prefix = cfg.logLinePrefix;
listen_addresses = if cfg.enableTCPIP then "*" else "localhost";
port = cfg.port;
};
services.postgresql.package =
# Note: when changing the default, make it conditional on
# system.stateVersion to maintain compatibility with existing

View File

@ -1,123 +0,0 @@
# deepin
{ config, pkgs, lib, ... }:
{
###### interface
options = {
services.deepin.core.enable = lib.mkEnableOption "
Basic dbus and systemd services, groups and users needed by the
Deepin Desktop Environment.
";
services.deepin.deepin-menu.enable = lib.mkEnableOption "
DBus service for unified menus in Deepin Desktop Environment.
";
services.deepin.deepin-turbo.enable = lib.mkEnableOption "
Turbo service for the Deepin Desktop Environment. It is a daemon
that helps to launch applications faster.
";
};
###### implementation
config = lib.mkMerge [
(lib.mkIf config.services.deepin.core.enable {
environment.systemPackages = [
pkgs.deepin.dde-api
pkgs.deepin.dde-calendar
pkgs.deepin.dde-control-center
pkgs.deepin.dde-daemon
pkgs.deepin.dde-dock
pkgs.deepin.dde-launcher
pkgs.deepin.dde-file-manager
pkgs.deepin.dde-session-ui
pkgs.deepin.deepin-anything
pkgs.deepin.deepin-image-viewer
];
services.dbus.packages = [
pkgs.deepin.dde-api
pkgs.deepin.dde-calendar
pkgs.deepin.dde-control-center
pkgs.deepin.dde-daemon
pkgs.deepin.dde-dock
pkgs.deepin.dde-launcher
pkgs.deepin.dde-file-manager
pkgs.deepin.dde-session-ui
pkgs.deepin.deepin-anything
pkgs.deepin.deepin-image-viewer
];
systemd.packages = [
pkgs.deepin.dde-api
pkgs.deepin.dde-daemon
pkgs.deepin.dde-file-manager
pkgs.deepin.deepin-anything
];
boot.extraModulePackages = [ config.boot.kernelPackages.deepin-anything ];
boot.kernelModules = [ "vfs_monitor" ];
users.groups.deepin-sound-player = { };
users.users.deepin-sound-player = {
description = "Deepin sound player";
group = "deepin-sound-player";
isSystemUser = true;
};
users.groups.deepin-daemon = { };
users.users.deepin-daemon = {
description = "Deepin daemon user";
group = "deepin-daemon";
isSystemUser = true;
};
users.groups.deepin_anything_server = { };
users.users.deepin_anything_server = {
description = "Deepin Anything Server";
group = "deepin_anything_server";
isSystemUser = true;
};
security.pam.services.deepin-auth-keyboard.text = ''
# original at ${pkgs.deepin.dde-daemon}/etc/pam.d/deepin-auth-keyboard
auth [success=2 default=ignore] pam_lsass.so
auth [success=1 default=ignore] pam_unix.so nullok_secure try_first_pass
auth requisite pam_deny.so
auth required pam_permit.so
'';
environment.etc = {
"polkit-1/localauthority/10-vendor.d/com.deepin.api.device.pkla".source = "${pkgs.deepin.dde-api}/etc/polkit-1/localauthority/10-vendor.d/com.deepin.api.device.pkla";
"polkit-1/localauthority/10-vendor.d/com.deepin.daemon.Accounts.pkla".source = "${pkgs.deepin.dde-daemon}/etc/polkit-1/localauthority/10-vendor.d/com.deepin.daemon.Accounts.pkla";
"polkit-1/localauthority/10-vendor.d/com.deepin.daemon.Grub2.pkla".source = "${pkgs.deepin.dde-daemon}/etc/polkit-1/localauthority/10-vendor.d/com.deepin.daemon.Grub2.pkla";
};
services.deepin.deepin-menu.enable = true;
services.deepin.deepin-turbo.enable = true;
})
(lib.mkIf config.services.deepin.deepin-menu.enable {
services.dbus.packages = [ pkgs.deepin.deepin-menu ];
})
(lib.mkIf config.services.deepin.deepin-turbo.enable {
environment.systemPackages = [ pkgs.deepin.deepin-turbo ];
systemd.packages = [ pkgs.deepin.deepin-turbo ];
})
];
}

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

@ -322,7 +322,7 @@ https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides
If you want, you can tweak the Emacs package itself from your
<filename>emacs.nix</filename>. For example, if you want to have a
GTK 3-based Emacs instead of the default GTK 2-based binary and remove the
automatically generated <filename>emacs.desktop</filename> (useful is you
automatically generated <filename>emacs.desktop</filename> (useful if you
only use <command>emacsclient</command>), you can change your file
<filename>emacs.nix</filename> in this way:
</para>

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

@ -54,7 +54,7 @@ let
'') gitlabConfig.production.repositories.storages))}
'';
gitlabShellConfig = {
gitlabShellConfig = flip recursiveUpdate cfg.extraShellConfig {
user = cfg.user;
gitlab_url = "http+unix://${pathUrlQuote gitlabSocket}";
http_settings.self_signed_cert = false;
@ -517,6 +517,12 @@ in {
'';
};
extraShellConfig = mkOption {
type = types.attrs;
default = {};
description = "Extra configuration to merge into shell-config.yml";
};
extraConfig = mkOption {
type = types.attrs;
default = {};

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

@ -500,13 +500,6 @@ in
config = {
assertions = [
{
assertion = config.nix.distributedBuilds || config.nix.buildMachines == [];
message = "You must set `nix.distributedBuilds = true` to use nix.buildMachines";
}
];
nix.binaryCachePublicKeys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
nix.binaryCaches = [ "https://cache.nixos.org/" ];

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

@ -1,12 +1,12 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkDefault mkEnableOption mkIf mkOption types;
inherit (lib) mkBefore mkDefault mkEnableOption mkIf mkOption mkRemovedOptionModule types;
inherit (lib) concatStringsSep literalExample mapAttrsToList;
inherit (lib) optional optionalAttrs optionalString singleton versionAtLeast;
inherit (lib) optional optionalAttrs optionalString;
cfg = config.services.redmine;
format = pkgs.formats.yaml {};
bundle = "${cfg.package}/share/redmine/bin/bundle";
databaseYml = pkgs.writeText "database.yml" ''
@ -20,24 +20,8 @@ let
${optionalString (cfg.database.type == "mysql2" && cfg.database.socket != null) "socket: ${cfg.database.socket}"}
'';
configurationYml = pkgs.writeText "configuration.yml" ''
default:
scm_subversion_command: ${pkgs.subversion}/bin/svn
scm_mercurial_command: ${pkgs.mercurial}/bin/hg
scm_git_command: ${pkgs.gitAndTools.git}/bin/git
scm_cvs_command: ${pkgs.cvs}/bin/cvs
scm_bazaar_command: ${pkgs.breezy}/bin/bzr
scm_darcs_command: ${pkgs.darcs}/bin/darcs
${cfg.extraConfig}
'';
additionalEnvironment = pkgs.writeText "additional_environment.rb" ''
config.logger = Logger.new("${cfg.stateDir}/log/production.log", 14, 1048576)
config.logger.level = Logger::INFO
${cfg.extraEnv}
'';
configurationYml = format.generate "configuration.yml" cfg.settings;
additionalEnvironment = pkgs.writeText "additional_environment.rb" cfg.extraEnv;
unpackTheme = unpack "theme";
unpackPlugin = unpack "plugin";
@ -56,8 +40,13 @@ let
pgsqlLocal = cfg.database.createLocally && cfg.database.type == "postgresql";
in
{
imports = [
(mkRemovedOptionModule [ "services" "redmine" "extraConfig" ] "Use services.redmine.settings instead.")
(mkRemovedOptionModule [ "services" "redmine" "database" "password" ] "Use services.redmine.database.passwordFile instead.")
];
# interface
options = {
services.redmine = {
enable = mkEnableOption "Redmine";
@ -93,21 +82,24 @@ in
description = "The state directory, logs and plugins are stored here.";
};
extraConfig = mkOption {
type = types.lines;
default = "";
settings = mkOption {
type = format.type;
default = {};
description = ''
Extra configuration in configuration.yml.
See <link xlink:href="https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration"/>
Redmine configuration (<filename>configuration.yml</filename>). Refer to
<link xlink:href="https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration"/>
for details.
'';
example = literalExample ''
email_delivery:
delivery_method: smtp
smtp_settings:
address: mail.example.com
port: 25
{
email_delivery = {
delivery_method = "smtp";
smtp_settings = {
address = "mail.example.com";
port = 25;
};
};
}
'';
};
@ -186,16 +178,6 @@ in
description = "Database user.";
};
password = mkOption {
type = types.str;
default = "";
description = ''
The password corresponding to <option>database.user</option>.
Warning: this is stored in cleartext in the Nix store!
Use <option>database.passwordFile</option> instead.
'';
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
@ -226,11 +208,12 @@ in
};
};
# implementation
config = mkIf cfg.enable {
assertions = [
{ assertion = cfg.database.passwordFile != null || cfg.database.password != "" || cfg.database.socket != null;
message = "one of services.redmine.database.socket, services.redmine.database.passwordFile, or services.redmine.database.password must be set";
{ assertion = cfg.database.passwordFile != null || cfg.database.socket != null;
message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set";
}
{ assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true";
@ -243,6 +226,22 @@ in
}
];
services.redmine.settings = {
production = {
scm_subversion_command = "${pkgs.subversion}/bin/svn";
scm_mercurial_command = "${pkgs.mercurial}/bin/hg";
scm_git_command = "${pkgs.gitAndTools.git}/bin/git";
scm_cvs_command = "${pkgs.cvs}/bin/cvs";
scm_bazaar_command = "${pkgs.breezy}/bin/bzr";
scm_darcs_command = "${pkgs.darcs}/bin/darcs";
};
};
services.redmine.extraEnv = mkBefore ''
config.logger = Logger.new("${cfg.stateDir}/log/production.log", 14, 1048576)
config.logger.level = Logger::INFO
'';
services.mysql = mkIf mysqlLocal {
enable = true;
package = mkDefault pkgs.mariadb;
@ -338,7 +337,7 @@ in
# handle database.passwordFile & permissions
DBPASS=$(head -n1 ${cfg.database.passwordFile})
DBPASS=${optionalString (cfg.database.passwordFile != null) "$(head -n1 ${cfg.database.passwordFile})"}
cp -f ${databaseYml} "${cfg.stateDir}/config/database.yml"
sed -e "s,#dbpass#,$DBPASS,g" -i "${cfg.stateDir}/config/database.yml"
chmod 440 "${cfg.stateDir}/config/database.yml"
@ -379,17 +378,6 @@ in
redmine.gid = config.ids.gids.redmine;
};
warnings = optional (cfg.database.password != "")
''config.services.redmine.database.password will be stored as plaintext
in the Nix store. Use database.passwordFile instead.'';
# Create database passwordFile default when password is configured.
services.redmine.database.passwordFile =
(mkDefault (toString (pkgs.writeTextFile {
name = "redmine-database-password";
text = cfg.database.password;
})));
};
}

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

@ -458,7 +458,7 @@ in {
systemd.services.NetworkManager-dispatcher = {
wantedBy = [ "network.target" ];
restartTriggers = [ configFile ];
restartTriggers = [ configFile overrideNameserversScript ];
# useful binaries for user-specified hooks
path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ];

View File

@ -0,0 +1,47 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.robustirc-bridge;
in
{
options = {
services.robustirc-bridge = {
enable = mkEnableOption "RobustIRC bridge";
extraFlags = mkOption {
type = types.listOf types.str;
default = [];
description = ''Extra flags passed to the <command>robustirc-bridge</command> command. See <link xlink:href="https://robustirc.net/docs/adminguide.html#_bridge">RobustIRC Documentation</link> or robustirc-bridge(1) for details.'';
example = [
"-network robustirc.net"
];
};
};
};
config = mkIf cfg.enable {
systemd.services.robustirc-bridge = {
description = "RobustIRC bridge";
documentation = [
"man:robustirc-bridge(1)"
"https://robustirc.net/"
];
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
ExecStart = "${pkgs.robustirc-bridge}/bin/robustirc-bridge ${concatStringsSep " " cfg.extraFlags}";
Restart = "on-failure";
# Hardening
PrivateDevices = true;
ProtectSystem = true;
ProtectHome = true;
PrivateTmp = true;
};
};
};
}

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

@ -34,8 +34,8 @@ let
User tor
DataDirectory ${torDirectory}
${optionalString cfg.enableGeoIP ''
GeoIPFile ${pkgs.tor.geoip}/share/tor/geoip
GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6
GeoIPFile ${cfg.package.geoip}/share/tor/geoip
GeoIPv6File ${cfg.package.geoip}/share/tor/geoip6
''}
${optint "ControlPort" cfg.controlPort}
@ -123,6 +123,16 @@ in
'';
};
package = mkOption {
type = types.package;
default = pkgs.tor;
defaultText = "pkgs.tor";
example = literalExample "pkgs.tor";
description = ''
Tor package to use
'';
};
enableGeoIP = mkOption {
type = types.bool;
default = true;
@ -749,8 +759,8 @@ in
serviceConfig =
{ Type = "simple";
# Translated from the upstream contrib/dist/tor.service.in
ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config";
ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile}";
ExecStartPre = "${cfg.package}/bin/tor -f ${torRcFile} --verify-config";
ExecStart = "${cfg.package}/bin/tor -f ${torRcFile}";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
KillSignal = "SIGINT";
TimeoutSec = 30;
@ -773,7 +783,7 @@ in
};
};
environment.systemPackages = [ pkgs.tor ];
environment.systemPackages = [ cfg.package ];
services.privoxy = mkIf (cfg.client.enable && cfg.client.privoxy.enable) {
enable = true;

View File

@ -82,6 +82,7 @@ in {
auth required pam_unix.so nullok
account required pam_unix.so
session required pam_unix.so
session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
session required ${pkgs.systemd}/lib/security/pam_systemd.so
'';

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,20 +113,21 @@ 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
copy_bin_and_libs ${pkgs.mdadm}/sbin/mdmon
# Copy udev.
copy_bin_and_libs ${udev}/lib/systemd/systemd-udevd
copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
copy_bin_and_libs ${udev}/bin/udevadm
copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
for BIN in ${udev}/lib/udev/*_id; do
copy_bin_and_libs $BIN
done
# systemd-udevd is only a symlink to udevadm these days
ln -sf udevadm $out/bin/systemd-udevd
# Copy modprobe.
copy_bin_and_libs ${pkgs.kmod}/bin/kmod

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"
@ -903,11 +903,9 @@ in
)
]);
passwd = (mkMerge [
[ "mymachines" ]
(mkAfter [ "systemd" ])
]);
group = (mkMerge [
[ "mymachines" ]
(mkAfter [ "systemd" ])
]);
};

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

@ -106,11 +106,29 @@ in rec {
(onFullSupported "nixos.tests.networking.scripted.bridge")
(onFullSupported "nixos.tests.networking.scripted.dhcpOneIf")
(onFullSupported "nixos.tests.networking.scripted.dhcpSimple")
(onFullSupported "nixos.tests.networking.scripted.link")
(onFullSupported "nixos.tests.networking.scripted.loopback")
(onFullSupported "nixos.tests.networking.scripted.macvlan")
(onFullSupported "nixos.tests.networking.scripted.privacy")
(onFullSupported "nixos.tests.networking.scripted.routes")
(onFullSupported "nixos.tests.networking.scripted.sit")
(onFullSupported "nixos.tests.networking.scripted.static")
(onFullSupported "nixos.tests.networking.scripted.virtual")
(onFullSupported "nixos.tests.networking.scripted.vlan")
(onFullSupported "nixos.tests.networking.networkd.bond")
(onFullSupported "nixos.tests.networking.networkd.bridge")
(onFullSupported "nixos.tests.networking.networkd.dhcpOneIf")
(onFullSupported "nixos.tests.networking.networkd.dhcpSimple")
(onFullSupported "nixos.tests.networking.networkd.link")
(onFullSupported "nixos.tests.networking.networkd.loopback")
# Fails nondeterministically (https://github.com/NixOS/nixpkgs/issues/96709)
#(onFullSupported "nixos.tests.networking.networkd.macvlan")
(onFullSupported "nixos.tests.networking.networkd.privacy")
(onFullSupported "nixos.tests.networking.networkd.routes")
(onFullSupported "nixos.tests.networking.networkd.sit")
(onFullSupported "nixos.tests.networking.networkd.static")
(onFullSupported "nixos.tests.networking.networkd.virtual")
(onFullSupported "nixos.tests.networking.networkd.vlan")
(onFullSupported "nixos.tests.systemd-networkd-ipv6-prefix-delegation")
(onFullSupported "nixos.tests.nfs3.simple")
(onFullSupported "nixos.tests.nfs4.simple")

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
@ -48,6 +49,7 @@ in
ceph-multi-node = handleTestOn ["x86_64-linux"] ./ceph-multi-node.nix {};
certmgr = handleTest ./certmgr.nix {};
cfssl = handleTestOn ["x86_64-linux"] ./cfssl.nix {};
charliecloud = handleTest ./charliecloud.nix {};
chromium = (handleTestOn ["x86_64-linux"] ./chromium.nix {}).stable or {};
cjdns = handleTest ./cjdns.nix {};
clickhouse = handleTest ./clickhouse.nix {};
@ -65,11 +67,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 +82,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 {};
@ -296,6 +298,7 @@ in
redis = handleTest ./redis.nix {};
redmine = handleTest ./redmine.nix {};
restic = handleTest ./restic.nix {};
robustirc-bridge = handleTest ./robustirc-bridge.nix {};
roundcube = handleTest ./roundcube.nix {};
rspamd = handleTest ./rspamd.nix {};
rss2email = handleTest ./rss2email.nix {};
@ -306,6 +309,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 +324,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

@ -0,0 +1,43 @@
# This test checks charliecloud image construction and run
import ./make-test-python.nix ({ pkgs, ...} : let
dockerfile = pkgs.writeText "Dockerfile" ''
FROM nix
RUN mkdir /home /tmp
RUN touch /etc/passwd /etc/group
CMD ["true"]
'';
in {
name = "charliecloud";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ bzizou ];
};
nodes = {
host = { ... }: {
environment.systemPackages = [ pkgs.charliecloud ];
virtualisation.docker.enable = true;
users.users.alice = {
isNormalUser = true;
extraGroups = [ "docker" ];
};
};
};
testScript = ''
host.start()
host.wait_for_unit("docker.service")
host.succeed(
'su - alice -c "docker load --input=${pkgs.dockerTools.examples.nix}"'
)
host.succeed(
"cp ${dockerfile} /home/alice/Dockerfile"
)
host.succeed('su - alice -c "ch-build -t hello ."')
host.succeed('su - alice -c "ch-builder2tar hello /var/tmp"')
host.succeed('su - alice -c "ch-tar2dir /var/tmp/hello.tar.gz /var/tmp"')
host.succeed('su - alice -c "ch-run /var/tmp/hello -- echo Running_From_Container_OK"')
'';
})

View File

@ -59,7 +59,8 @@ pkgs.runCommand "acme-snakeoil-ca" {
openssl genrsa -out snakeoil.key 4096
openssl req -new -key snakeoil.key -out snakeoil.csr
openssl x509 -req -in snakeoil.csr -sha256 -set_serial 666 \
-CA ca.pem -CAkey ca.key -out snakeoil.pem -days 36500
-CA ca.pem -CAkey ca.key -out snakeoil.pem -days 36500 \
-extfile "$OPENSSL_CONF" -extensions req_ext
addpem snakeoil.key ${lib.escapeShellArg fqdn} key
addpem snakeoil.pem ${lib.escapeShellArg fqdn} cert
'') domains}

View File

@ -2,170 +2,171 @@
{
ca.key = builtins.toFile "ca.key" ''
-----BEGIN PRIVATE KEY-----
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
JUsBXFNz132nP4iIr3UKrPedQZijSi4=
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
s9o9Sr49cibZ3CbMjCSNE3AOeVE1/Q==
-----END PRIVATE KEY-----
'';
ca.cert = builtins.toFile "ca.cert" ''
-----BEGIN CERTIFICATE-----
MIIFDzCCAvegAwIBAgIUTRDYSWJvmlhwIR3pzVrIQfnboLEwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLU25ha2VvaWwgQ0EwIBcNMjAwMzIyMjI1NjE3WhgPMjEy
MDAyMjcyMjU2MTdaMBYxFDASBgNVBAMMC1NuYWtlb2lsIENBMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEAwp1WRhJ+vHs15Q1hOYI7Aj6rrUeJ8cP8LyGw
MQdlZNlnkOhxr7diXa8+anq9iF3e+mLaUHqnfn33rWi7THhU6/l8vcqhKcdgH75h
BHC57olm7FBmc7B8Fu/+JD4ABQcvU8CZ/5PPdpUerkGGtEEt/Ql/OZCzGfy2V1Mg
pSAWviMU9TqL5hqjDq+JxgEkUD+nf558p+NSp2rf3bacmmuc4H2MAOmFulMXMsLw
UqTDlzNP/NgRWRsgC7d50Pn35q7SnLzI7DSwRWrJVfs2DtTzGy20wlvEpiU44c7m
qV7QR67r6HtuNd911pb3D9cpWk8dGpcIkFia9VxDlFixBU4Dk3kcolQ8Q7p6c/u6
BnMM3HThvJwoHesNGmGhAs9c70txWE0M/jIqphJMerOG49VPdCNJMFCW3lo46xju
2PUKuItfRmnw4O3ADJ/XhwojWzaz4+rf4H2lWwq25Jt2VY5Ly00Npm9pb+/rVYou
Odtu4jT7jUupcdhVZa/w14kazlX4IT76DsVH186ICYWS1eGZIvsnGxkGucPGDA2A
r+HNPHSgOwGrVYTUutHNc4EDY7lhdwb6HEskshlKJ1U+odQ2JSfWAKoKjaXnYDUf
ZKadN+EjIQe3Y/ZXgei0TAPnNXxv0/tZQ25APr6KXqbePt9KA3Ak2HxIn5dR8Kjc
P21w5tcCAwEAAaNTMFEwHQYDVR0OBBYEFCIoeYSYjtMiPrmxfHmcrsZkyTpvMB8G
A1UdIwQYMBaAFCIoeYSYjtMiPrmxfHmcrsZkyTpvMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQELBQADggIBAHPdwOgAxyhIhbqFObNftW8K3sptorB/Fj6jwYCm
mHleFueqQnjTHMWsflOjREvQp1M307FWooGj+KQkjwvAyDc/Hmy7WgJxBg9p3vc+
/Xf/e7ZfBl8rv7vH8VXW/BC1vVsILdFncrgTrP8/4psV50/cl1F4+nPBiekvvxwZ
k+R7SgeSvcWT7YlOG8tm1M3al4F4mWzSRkYjkrXmwRCKAiya9xcGSt0Bob+LoM/O
mpDGV/PMC1WAoDc1mMuXN2hSc0n68xMcuFs+dj/nQYn8uL5pzOxpX9560ynKyLDv
yOzQlM2VuZ7H2hSIeYOFgrtHJJwhDtzjmUNDQpQdp9Fx+LONQTS1VLCTXND2i/3F
10X6PkdnLEn09RiPt5qy20pQkICxoEydmlwpFs32musYfJPdBPkZqZWrwINBv2Wb
HfOmEB4xUvXufZ5Ju5icgggBkyNA3PCLo0GZFRrMtvA7i9IXOcXNR+njhKa9246V
QQfeWiz05RmIvgShJYVsnZWtael8ni366d+UXypBYncohimyNlAD1n+Bh3z0PvBB
+FK4JgOSeouM4SuBHdwmlZ/H0mvfUG81Y8Jbrw0yuRHtuCtX5HpN5GKpZPHDE7aQ
fEShVB/GElC3n3DvgK9OJBeVVhYQgUEfJi4rsSxt3cdEI0NrdckUoZbApWVJ3CBc
F8Y7
MIIFDzCCAvegAwIBAgIUX0P6NfX4gRUpFz+TNV/f26GHokgwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLU25ha2VvaWwgQ0EwIBcNMjAwODI0MDc0MjEyWhgPMjEy
MDA3MzEwNzQyMTJaMBYxFDASBgNVBAMMC1NuYWtlb2lsIENBMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEA3dRuzhaVyKB5668XcPoYiVQ2Op0AwyhbfRQS
sJb+8doQr1+258rx2i+x+tXPvNDHBjyEq0J9/jI7kWbqsGsePQ3XTV2xreFqhpce
CwHiN+zoDVFM/X6Da9qww1e+OL8KdePzw9X956qVnzoM87IfK8WnNJmeUnvR8TiK
SOzUTbhyY4+x1meGMXjr4tvPa5qbtCT3KduZ48O4ehTXx7vBhqd+FI+sAMbm3Ucn
1YlfM/RTH24JS7gPYtTokstzUKavFcARvrv9Iguz0LEI8Uq2oELtubHwE2CKxSUO
GQr4dJ8aijHsjzwYunQHDKWtgGQKrar/gsFnaN+gbUuV4gu6wKYGJ/srXikB0m5w
ohtlv26Em34PZUZN8ONXREhDQgA1Woy29xKL76dLQTvwQC7nb1o8wD26V5XlTa+p
4FrRpbGUwuy1w4AanVtipc6WRnM/j1twE5cIqf2RUbewNebuXVZFj1aD7kNMNU8P
oPXcVhUzZN4ftneyvOyMUG3L6sXiA0hFRTK8aNHuAhtrnTHHpWcAEZeojUwBE2ts
yVtF7QHRlTPh2NUv5FOlPSd57mw7bQJADAAYz8Moyxu4X8lqlB3/cj7OPRrH7QFb
yMvFyHrtLIQ9QRtdf1c78KVAokXPm7blotnq2eGXz6EOWKEvYLeEviKc+0zxgZfg
FJPRztkCAwEAAaNTMFEwHQYDVR0OBBYEFNhBZxryvykCjfPO85xB3wof2enAMB8G
A1UdIwQYMBaAFNhBZxryvykCjfPO85xB3wof2enAMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQELBQADggIBAEZwlsQ+3yd1MVxLRy9RjoA8hI7iWBNmvPUyNjlb
l/L9N+dZgdx9G5h/KPRUyzvUc/uk/ZxTWVPIOp13WI65qwsBKrwvYKiXiwzjt+9V
CKDRc1sOghTSXk4FD3L5UcKvTQ2lRcFsqxbkopEwQWhoCuhe4vFyt3Nx8ZGLCBUD
3I5zMHtO8FtpZWKJPw46Yc1kasv0nlfly/vUbnErYfgjWX1hgWUcRgYdKwO4sOZ7
KbNma0WUsX5mWhXo4Kk7D15wATHO+j9s+j8m86duBL3A4HzpTo1DhHvBi0dkg0CO
XuSdByIzVLIPh3yhCHN1loRCP2rbzKM8IQeU/X5Q4UJeC/x9ew8Kk+RKXoHc8Y2C
JQO1DxuidyDJRhbb98wZo2YfIsdWQGjYZBe1XQRwBD28JnB+Rb9shml6lORWQn9y
P/STo9uWm5zvOCfqwbnCoetljDweItINx622G9SafBwPZc3o79oL7QSl8DgCtN6g
p0wGIlIBx+25w/96PqZcrYb8B7/uBHJviiKjBXDoIJWNiNRhW5HaFjeJdSKq7KIL
I/PO9KuHafif36ksG69X02Rio2/cTjgjEW1hGHcDRyyJWWaj7bd2eWuouh6FF22b
PA6FGY4vewDPnbLKLaix2ZIKxtedUDOH/qru3Mv58IFXmQ4iyM8oC8aOxYSQLZDn
1yJD
-----END CERTIFICATE-----
'';
"acme.test".key = builtins.toFile "acme.test.key" ''
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
'';
"acme.test".cert = builtins.toFile "acme.test.cert" ''
-----BEGIN CERTIFICATE-----
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
NbjvWnP8lDU8j2l3eSG58S14iGs=
-----END CERTIFICATE-----
'';
}

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

@ -1,9 +0,0 @@
f: {
system ? builtins.currentSystem,
pkgs ? import ../.. { inherit system; config = {}; },
...
} @ args:
with import ../lib/testing.nix { inherit system pkgs; };
makeTest (if pkgs.lib.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f)

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

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