Project import generated by Copybara.

GitOrigin-RevId: 5b091d4fbe3b7b7493c3b46fe0842e4b30ea24b3
This commit is contained in:
Default email 2021-12-30 14:39:12 +01:00
parent 6cb1af2f35
commit a425ba4985
515 changed files with 9997 additions and 6755 deletions

View file

@ -764,7 +764,7 @@ and in this case the `python38` interpreter is automatically used.
### Interpreters {#interpreters} ### Interpreters {#interpreters}
Versions 2.7, 3.6, 3.7, 3.8 and 3.9 of the CPython interpreter are available as Versions 2.7, 3.7, 3.8 and 3.9 of the CPython interpreter are available as
respectively `python27`, `python37`, `python38` and `python39`. The respectively `python27`, `python37`, `python38` and `python39`. The
aliases `python2` and `python3` correspond to respectively `python27` and aliases `python2` and `python3` correspond to respectively `python27` and
`python39`. The attribute `python` maps to `python2`. The PyPy interpreters `python39`. The attribute `python` maps to `python2`. The PyPy interpreters

View file

@ -1696,6 +1696,12 @@
fingerprint = "BF4FCB85C69989B4ED95BF938AE74787A4B7C07E"; fingerprint = "BF4FCB85C69989B4ED95BF938AE74787A4B7C07E";
}]; }];
}; };
broke = {
email = "broke@in-fucking.space";
github = "broke";
githubId = 1071610;
name = "Gunnar Nitsche";
};
bryanasdev000 = { bryanasdev000 = {
email = "bryanasdev000@gmail.com"; email = "bryanasdev000@gmail.com";
matrix = "@bryanasdev000:matrix.org"; matrix = "@bryanasdev000:matrix.org";
@ -2134,6 +2140,12 @@
githubId = 3956062; githubId = 3956062;
name = "Simon Lackerbauer"; name = "Simon Lackerbauer";
}; };
cirno-999 = {
email = "reverene@protonmail.com";
github = "cirno-999";
githubId = 73712874;
name = "cirno-999";
};
citadelcore = { citadelcore = {
email = "alex@arctarus.co.uk"; email = "alex@arctarus.co.uk";
github = "citadelcore"; github = "citadelcore";
@ -3389,6 +3401,12 @@
githubId = 103082; githubId = 103082;
name = "Ed Brindley"; name = "Ed Brindley";
}; };
elliot = {
email = "hack00mind@gmail.com";
github = "Eliot00";
githubId = 18375468;
name = "Elliot Xu";
};
elliottvillars = { elliottvillars = {
email = "elliottvillars@gmail.com"; email = "elliottvillars@gmail.com";
github = "elliottvillars"; github = "elliottvillars";
@ -7066,6 +7084,12 @@
fingerprint = "BA3A 5886 AE6D 526E 20B4 57D6 6A37 DF94 8318 8492"; fingerprint = "BA3A 5886 AE6D 526E 20B4 57D6 6A37 DF94 8318 8492";
}]; }];
}; };
lux = {
email = "lux@lux.name";
githubId = 1208273;
matrix = "@lux:ontheblueplanet.com";
name = "Lux";
};
luz = { luz = {
email = "luz666@daum.net"; email = "luz666@daum.net";
github = "Luz"; github = "Luz";

View file

@ -14,7 +14,17 @@
</itemizedlist> </itemizedlist>
<section xml:id="sec-release-22.05-highlights"> <section xml:id="sec-release-22.05-highlights">
<title>Highlights</title> <title>Highlights</title>
<itemizedlist spacing="compact"> <itemizedlist>
<listitem>
<para>
<literal>security.acme.defaults</literal> has been added to
simplify configuring settings for many certificates at once.
This also opens up the the option to use DNS-01 validation
when using <literal>enableACME</literal> on web server virtual
hosts (e.g.
<literal>services.nginx.virtualHosts.*.enableACME</literal>).
</para>
</listitem>
<listitem> <listitem>
<para> <para>
PHP 8.1 is now available PHP 8.1 is now available
@ -33,6 +43,14 @@
<link linkend="opt-services.aesmd.enable">services.aesmd</link>. <link linkend="opt-services.aesmd.enable">services.aesmd</link>.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<link xlink:href="https://docs.docker.com/engine/security/rootless/">rootless
Docker</link>, a <literal>systemd --user</literal> Docker
service which runs without root permissions. Available as
<link xlink:href="options.html#opt-virtualisation.docker.rootless.enable">virtualisation.docker.rootless.enable</link>.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
<link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html">filebeat</link>, <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html">filebeat</link>,
@ -85,6 +103,12 @@
new versions will release. new versions will release.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<literal>services.kubernetes.addons.dashboard</literal> was
removed due to it being an outdated version.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
The <literal>wafHook</literal> hook now honors The <literal>wafHook</literal> hook now honors
@ -122,6 +146,29 @@
<literal>virtualisation.docker.daemon.settings</literal>. <literal>virtualisation.docker.daemon.settings</literal>.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
The <literal>autorestic</literal> package has been upgraded
from 1.3.0 to 1.5.0 which introduces breaking changes in
config file, check
<link xlink:href="https://autorestic.vercel.app/migration/1.4_1.5">their
migration guide</link> for more details.
</para>
</listitem>
<listitem>
<para>
For <literal>pkgs.python3.pkgs.ipython</literal>, its direct
dependency
<literal>pkgs.python3.pkgs.matplotlib-inline</literal> (which
is really an adapter to integrate matplotlib in ipython if it
is installed) does not depend on
<literal>pkgs.python3.pkgs.matplotlib</literal> anymore. This
is closer to a non-Nix install of ipython. This has the added
benefit to reduce the closure size of
<literal>ipython</literal> from ~400MB to ~160MB (including
~100MB for python itself).
</para>
</listitem>
</itemizedlist> </itemizedlist>
</section> </section>
<section xml:id="sec-release-22.05-notable-changes"> <section xml:id="sec-release-22.05-notable-changes">
@ -180,6 +227,20 @@
using this default will print a warning when rebuilt. using this default will print a warning when rebuilt.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<literal>security.acme</literal> certificates will now
correctly check for CA revokation before reaching their
minimum age.
</para>
</listitem>
<listitem>
<para>
Removing domains from
<literal>security.acme.certs._name_.extraDomainNames</literal>
will now correctly remove those domains during rebuild/renew.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
The option The option
@ -198,6 +259,13 @@
configuration. configuration.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
The option <literal>services.duplicati.dataDir</literal> has
been added to allow changing the location of duplicatis
files.
</para>
</listitem>
</itemizedlist> </itemizedlist>
</section> </section>
</section> </section>

View file

@ -6,11 +6,17 @@ In addition to numerous new and upgraded packages, this release has the followin
## Highlights {#sec-release-22.05-highlights} ## Highlights {#sec-release-22.05-highlights}
- `security.acme.defaults` has been added to simplify configuring
settings for many certificates at once. This also opens up the
the option to use DNS-01 validation when using `enableACME` on
web server virtual hosts (e.g. `services.nginx.virtualHosts.*.enableACME`).
- PHP 8.1 is now available - PHP 8.1 is now available
## New Services {#sec-release-22.05-new-services} ## New Services {#sec-release-22.05-new-services}
- [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable). - [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable).
- [rootless Docker](https://docs.docker.com/engine/security/rootless/), a `systemd --user` Docker service which runs without root permissions. Available as [virtualisation.docker.rootless.enable](options.html#opt-virtualisation.docker.rootless.enable).
- [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html), a lightweight shipper for forwarding and centralizing log data. Available as [services.filebeat](#opt-services.filebeat.enable). - [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html), a lightweight shipper for forwarding and centralizing log data. Available as [services.filebeat](#opt-services.filebeat.enable).
@ -35,6 +41,8 @@ In addition to numerous new and upgraded packages, this release has the followin
org-contrib, refer to the ones in `pkgs.emacsPackages.elpaPackages` and org-contrib, refer to the ones in `pkgs.emacsPackages.elpaPackages` and
`pkgs.emacsPackages.nongnuPackages` where the new versions will release. `pkgs.emacsPackages.nongnuPackages` where the new versions will release.
- `services.kubernetes.addons.dashboard` was removed due to it being an outdated version.
- The `wafHook` hook now honors `NIX_BUILD_CORES` when `enableParallelBuilding` is not set explicitly. Packages can restore the old behaviour by setting `enableParallelBuilding=false`. - The `wafHook` hook now honors `NIX_BUILD_CORES` when `enableParallelBuilding` is not set explicitly. Packages can restore the old behaviour by setting `enableParallelBuilding=false`.
- `pkgs.claws-mail-gtk2`, representing Claws Mail's older release version three, was removed in order to get rid of Python 2. - `pkgs.claws-mail-gtk2`, representing Claws Mail's older release version three, was removed in order to get rid of Python 2.
@ -45,6 +53,15 @@ In addition to numerous new and upgraded packages, this release has the followin
- If you previously used `/etc/docker/daemon.json`, you need to incorporate the changes into the new option `virtualisation.docker.daemon.settings`. - If you previously used `/etc/docker/daemon.json`, you need to incorporate the changes into the new option `virtualisation.docker.daemon.settings`.
- The `autorestic` package has been upgraded from 1.3.0 to 1.5.0 which introduces breaking changes in config file, check [their migration guide](https://autorestic.vercel.app/migration/1.4_1.5) for more details.
- For `pkgs.python3.pkgs.ipython`, its direct dependency `pkgs.python3.pkgs.matplotlib-inline`
(which is really an adapter to integrate matplotlib in ipython if it is installed) does
not depend on `pkgs.python3.pkgs.matplotlib` anymore.
This is closer to a non-Nix install of ipython.
This has the added benefit to reduce the closure size of `ipython` from ~400MB to ~160MB
(including ~100MB for python itself).
## Other Notable Changes {#sec-release-22.05-notable-changes} ## Other Notable Changes {#sec-release-22.05-notable-changes}
- The option [services.redis.servers](#opt-services.redis.servers) was added - The option [services.redis.servers](#opt-services.redis.servers) was added
@ -73,6 +90,12 @@ In addition to numerous new and upgraded packages, this release has the followin
- The `services.unifi.openPorts` option default value of `true` is now deprecated and will be changed to `false` in 22.11. - The `services.unifi.openPorts` option default value of `true` is now deprecated and will be changed to `false` in 22.11.
Configurations using this default will print a warning when rebuilt. Configurations using this default will print a warning when rebuilt.
- `security.acme` certificates will now correctly check for CA
revokation before reaching their minimum age.
- Removing domains from `security.acme.certs._name_.extraDomainNames`
will now correctly remove those domains during rebuild/renew.
- The option - The option
[services.ssh.enableAskPassword](#opt-services.ssh.enableAskPassword) was [services.ssh.enableAskPassword](#opt-services.ssh.enableAskPassword) was
added, decoupling the setting of `SSH_ASKPASS` from added, decoupling the setting of `SSH_ASKPASS` from
@ -80,3 +103,5 @@ In addition to numerous new and upgraded packages, this release has the followin
e.g. Wayland. e.g. Wayland.
- The `services.stubby` module was converted to a [settings-style](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md) configuration. - The `services.stubby` module was converted to a [settings-style](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md) configuration.
- The option `services.duplicati.dataDir` has been added to allow changing the location of duplicati's files.

View file

@ -20,7 +20,7 @@
<title>Configuration Options</title> <title>Configuration Options</title>
<variablelist xml:id="configuration-variable-list"> <variablelist xml:id="configuration-variable-list">
<xsl:for-each select="attrs"> <xsl:for-each select="attrs">
<xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '&lt;', '_'), '>', '_'))" /> <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '&lt;', '_'), '>', '_'), ':', '_'))" />
<varlistentry> <varlistentry>
<term xlink:href="#{$id}"> <term xlink:href="#{$id}">
<xsl:attribute name="xml:id"><xsl:value-of select="$id"/></xsl:attribute> <xsl:attribute name="xml:id"><xsl:value-of select="$id"/></xsl:attribute>

View file

@ -296,7 +296,6 @@
./services/cluster/hadoop/default.nix ./services/cluster/hadoop/default.nix
./services/cluster/k3s/default.nix ./services/cluster/k3s/default.nix
./services/cluster/kubernetes/addons/dns.nix ./services/cluster/kubernetes/addons/dns.nix
./services/cluster/kubernetes/addons/dashboard.nix
./services/cluster/kubernetes/addon-manager.nix ./services/cluster/kubernetes/addon-manager.nix
./services/cluster/kubernetes/apiserver.nix ./services/cluster/kubernetes/apiserver.nix
./services/cluster/kubernetes/controller-manager.nix ./services/cluster/kubernetes/controller-manager.nix
@ -1187,6 +1186,7 @@
./virtualisation/oci-containers.nix ./virtualisation/oci-containers.nix
./virtualisation/cri-o.nix ./virtualisation/cri-o.nix
./virtualisation/docker.nix ./virtualisation/docker.nix
./virtualisation/docker-rootless.nix
./virtualisation/ecs-agent.nix ./virtualisation/ecs-agent.nix
./virtualisation/libvirtd.nix ./virtualisation/libvirtd.nix
./virtualisation/lxc.nix ./virtualisation/lxc.nix

View file

@ -26,6 +26,6 @@ with lib;
###### implementation ###### implementation
config = mkIf config.programs.qt5ct.enable { config = mkIf config.programs.qt5ct.enable {
environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct"; environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct";
environment.systemPackages = with pkgs; [ qt5ct ]; environment.systemPackages = with pkgs; [ libsForQt5.qt5ct ];
}; };
} }

View file

@ -3,6 +3,7 @@ with lib;
let let
cfg = config.security.acme; cfg = config.security.acme;
opt = options.security.acme; opt = options.security.acme;
user = if cfg.useRoot then "root" else "acme";
# Used to calculate timer accuracy for coalescing # Used to calculate timer accuracy for coalescing
numCerts = length (builtins.attrNames cfg.certs); numCerts = length (builtins.attrNames cfg.certs);
@ -23,7 +24,7 @@ let
# security.acme.certs.<cert>.group on some of the services. # security.acme.certs.<cert>.group on some of the services.
commonServiceConfig = { commonServiceConfig = {
Type = "oneshot"; Type = "oneshot";
User = "acme"; User = user;
Group = mkDefault "acme"; Group = mkDefault "acme";
UMask = 0022; UMask = 0022;
StateDirectoryMode = 750; StateDirectoryMode = 750;
@ -101,12 +102,12 @@ let
# is configurable on a per-cert basis. # is configurable on a per-cert basis.
userMigrationService = let userMigrationService = let
script = with builtins; '' script = with builtins; ''
chown -R acme .lego/accounts chown -R ${user} .lego/accounts
'' + (concatStringsSep "\n" (mapAttrsToList (cert: data: '' '' + (concatStringsSep "\n" (mapAttrsToList (cert: data: ''
for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do
if [ -d "$fixpath" ]; then if [ -d "$fixpath" ]; then
chmod -R u=rwX,g=rX,o= "$fixpath" chmod -R u=rwX,g=rX,o= "$fixpath"
chown -R acme:${data.group} "$fixpath" chown -R ${user}:${data.group} "$fixpath"
fi fi
done done
'') certConfigs)); '') certConfigs));
@ -128,7 +129,7 @@ let
}; };
certToConfig = cert: data: let certToConfig = cert: data: let
acmeServer = if data.server != null then data.server else cfg.server; acmeServer = data.server;
useDns = data.dnsProvider != null; useDns = data.dnsProvider != null;
destPath = "/var/lib/acme/${cert}"; destPath = "/var/lib/acme/${cert}";
selfsignedDeps = optionals (cfg.preliminarySelfsigned) [ "acme-selfsigned-${cert}.service" ]; selfsignedDeps = optionals (cfg.preliminarySelfsigned) [ "acme-selfsigned-${cert}.service" ];
@ -156,6 +157,7 @@ let
${toString data.ocspMustStaple} ${data.keyType} ${toString data.ocspMustStaple} ${data.keyType}
''; '';
certDir = mkHash hashData; certDir = mkHash hashData;
# TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532
domainHash = mkHash "${concatStringsSep " " extraDomains} ${data.domain}"; domainHash = mkHash "${concatStringsSep " " extraDomains} ${data.domain}";
accountHash = (mkAccountHash acmeServer data); accountHash = (mkAccountHash acmeServer data);
accountDir = accountDirRoot + accountHash; accountDir = accountDirRoot + accountHash;
@ -210,7 +212,7 @@ let
description = "Renew ACME Certificate for ${cert}"; description = "Renew ACME Certificate for ${cert}";
wantedBy = [ "timers.target" ]; wantedBy = [ "timers.target" ];
timerConfig = { timerConfig = {
OnCalendar = cfg.renewInterval; OnCalendar = data.renewInterval;
Unit = "acme-${cert}.service"; Unit = "acme-${cert}.service";
Persistent = "yes"; Persistent = "yes";
@ -267,7 +269,7 @@ let
cat key.pem fullchain.pem > full.pem cat key.pem fullchain.pem > full.pem
# Group might change between runs, re-apply it # Group might change between runs, re-apply it
chown 'acme:${data.group}' * chown '${user}:${data.group}' *
# Default permissions make the files unreadable by group + anon # Default permissions make the files unreadable by group + anon
# Need to be readable by group # Need to be readable by group
@ -322,7 +324,7 @@ let
fi fi
''); '');
} // optionalAttrs (data.listenHTTP != null && toInt (elemAt (splitString ":" data.listenHTTP) 1) < 1024) { } // optionalAttrs (data.listenHTTP != null && toInt (elemAt (splitString ":" data.listenHTTP) 1) < 1024) {
AmbientCapabilities = "CAP_NET_BIND_SERVICE"; CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
}; };
# Working directory will be /tmp # Working directory will be /tmp
@ -355,7 +357,7 @@ let
expiration_s=$[expiration_date - now] expiration_s=$[expiration_date - now]
expiration_days=$[expiration_s / (3600 * 24)] # rounds down expiration_days=$[expiration_s / (3600 * 24)] # rounds down
[[ $expiration_days -gt ${toString cfg.validMinDays} ]] [[ $expiration_days -gt ${toString data.validMinDays} ]]
} }
${optionalString (data.webroot != null) '' ${optionalString (data.webroot != null) ''
@ -372,37 +374,40 @@ let
echo '${domainHash}' > domainhash.txt echo '${domainHash}' > domainhash.txt
# Check if we can renew # Check if we can renew.
if [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a -n "$(ls -1 accounts)" ]; then # We can only renew if the list of domains has not changed.
if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a -n "$(ls -1 accounts)" ]; then
# When domains are updated, there's no need to do a full # Even if a cert is not expired, it may be revoked by the CA.
# Lego run, but it's likely renew won't work if days is too low. # Try to renew, and silently fail if the cert is not expired.
if [ -e certificates/domainhash.txt ] && cmp -s domainhash.txt certificates/domainhash.txt; then # Avoids #85794 and resolves #129838
if ! lego ${renewOpts} --days ${toString data.validMinDays}; then
if is_expiration_skippable out/full.pem; then if is_expiration_skippable out/full.pem; then
echo 1>&2 "nixos-acme: skipping renewal because expiration isn't within the coming ${toString cfg.validMinDays} days" echo 1>&2 "nixos-acme: Ignoring failed renewal because expiration isn't within the coming ${toString data.validMinDays} days"
else else
echo 1>&2 "nixos-acme: renewing now, because certificate expires within the configured ${toString cfg.validMinDays} days" # High number to avoid Systemd reserved codes.
lego ${renewOpts} --days ${toString cfg.validMinDays} exit 11
fi fi
else
echo 1>&2 "certificate domain(s) have changed; will renew now"
# Any number > 90 works, but this one is over 9000 ;-)
lego ${renewOpts} --days 9001
fi fi
# Otherwise do a full run # Otherwise do a full run
else elif ! lego ${runOpts}; then
lego ${runOpts} # Produce a nice error for those doing their first nixos-rebuild with these certs
echo Failed to fetch certificates. \
This may mean your DNS records are set up incorrectly. \
${optionalString (cfg.preliminarySelfsigned) "Selfsigned certs are in place and dependant services will still start."}
# Exit 10 so that users can potentially amend SuccessExitStatus to ignore this error.
# High number to avoid Systemd reserved codes.
exit 10
fi fi
mv domainhash.txt certificates/ mv domainhash.txt certificates/
# Group might change between runs, re-apply it # Group might change between runs, re-apply it
chown 'acme:${data.group}' certificates/* chown '${user}:${data.group}' certificates/*
# Copy all certs to the "real" certs directory # Copy all certs to the "real" certs directory
CERT='certificates/${keyName}.crt' if ! cmp -s 'certificates/${keyName}.crt' out/fullchain.pem; then
if [ -e "$CERT" ] && ! cmp -s "$CERT" out/fullchain.pem; then
touch out/renewed touch out/renewed
echo Installing new certificate echo Installing new certificate
cp -vp 'certificates/${keyName}.crt' out/fullchain.pem cp -vp 'certificates/${keyName}.crt' out/fullchain.pem
@ -421,7 +426,194 @@ let
certConfigs = mapAttrs certToConfig cfg.certs; certConfigs = mapAttrs certToConfig cfg.certs;
certOpts = { name, ... }: { # These options can be specified within
# security.acme.defaults or security.acme.certs.<name>
inheritableModule = isDefaults: { config, ... }: let
defaultAndText = name: default: {
# When ! isDefaults then this is the option declaration for the
# security.acme.certs.<name> path, which has the extra inheritDefaults
# option, which if disabled means that we can't inherit it
default = if isDefaults || ! config.inheritDefaults then default else cfg.defaults.${name};
# The docs however don't need to depend on inheritDefaults, they should
# stay constant. Though notably it wouldn't matter much, because to get
# the option information, a submodule with name `<name>` is evaluated
# without any definitions.
defaultText = if isDefaults then default else literalExpression "config.security.acme.defaults.${name}";
};
in {
options = {
validMinDays = mkOption {
type = types.int;
inherit (defaultAndText "validMinDays" 30) default defaultText;
description = "Minimum remaining validity before renewal in days.";
};
renewInterval = mkOption {
type = types.str;
inherit (defaultAndText "renewInterval" "daily") default defaultText;
description = ''
Systemd calendar expression when to check for renewal. See
<citerefentry><refentrytitle>systemd.time</refentrytitle>
<manvolnum>7</manvolnum></citerefentry>.
'';
};
enableDebugLogs = mkEnableOption "debug logging for this certificate" // {
inherit (defaultAndText "enableDebugLogs" true) default defaultText;
};
webroot = mkOption {
type = types.nullOr types.str;
inherit (defaultAndText "webroot" null) default defaultText;
example = "/var/lib/acme/acme-challenge";
description = ''
Where the webroot of the HTTP vhost is located.
<filename>.well-known/acme-challenge/</filename> directory
will be created below the webroot if it doesn't exist.
<literal>http://example.org/.well-known/acme-challenge/</literal> must also
be available (notice unencrypted HTTP).
'';
};
server = mkOption {
type = types.nullOr types.str;
inherit (defaultAndText "server" null) default defaultText;
description = ''
ACME Directory Resource URI. Defaults to Let's Encrypt's
production endpoint,
<link xlink:href="https://acme-v02.api.letsencrypt.org/directory"/>, if unset.
'';
};
email = mkOption {
type = types.str;
inherit (defaultAndText "email" null) default defaultText;
description = ''
Email address for account creation and correspondence from the CA.
It is recommended to use the same email for all certs to avoid account
creation limits.
'';
};
group = mkOption {
type = types.str;
inherit (defaultAndText "group" "acme") default defaultText;
description = "Group running the ACME client.";
};
reloadServices = mkOption {
type = types.listOf types.str;
inherit (defaultAndText "reloadServices" []) default defaultText;
description = ''
The list of systemd services to call <code>systemctl try-reload-or-restart</code>
on.
'';
};
postRun = mkOption {
type = types.lines;
inherit (defaultAndText "postRun" "") default defaultText;
example = "cp full.pem backup.pem";
description = ''
Commands to run after new certificates go live. Note that
these commands run as the root user.
Executed in the same directory with the new certificate.
'';
};
keyType = mkOption {
type = types.str;
inherit (defaultAndText "keyType" "ec256") default defaultText;
description = ''
Key type to use for private keys.
For an up to date list of supported values check the --key-type option
at <link xlink:href="https://go-acme.github.io/lego/usage/cli/#usage"/>.
'';
};
dnsProvider = mkOption {
type = types.nullOr types.str;
inherit (defaultAndText "dnsProvider" null) default defaultText;
example = "route53";
description = ''
DNS Challenge provider. For a list of supported providers, see the "code"
field of the DNS providers listed at <link xlink:href="https://go-acme.github.io/lego/dns/"/>.
'';
};
dnsResolver = mkOption {
type = types.nullOr types.str;
inherit (defaultAndText "dnsResolver" null) default defaultText;
example = "1.1.1.1:53";
description = ''
Set the resolver to use for performing recursive DNS queries. Supported:
host:port. The default is to use the system resolvers, or Google's DNS
resolvers if the system's cannot be determined.
'';
};
credentialsFile = mkOption {
type = types.path;
inherit (defaultAndText "credentialsFile" null) default defaultText;
description = ''
Path to an EnvironmentFile for the cert's service containing any required and
optional environment variables for your selected dnsProvider.
To find out what values you need to set, consult the documentation at
<link xlink:href="https://go-acme.github.io/lego/dns/"/> for the corresponding dnsProvider.
'';
example = "/var/src/secrets/example.org-route53-api-token";
};
dnsPropagationCheck = mkOption {
type = types.bool;
inherit (defaultAndText "dnsPropagationCheck" true) default defaultText;
description = ''
Toggles lego DNS propagation check, which is used alongside DNS-01
challenge to ensure the DNS entries required are available.
'';
};
ocspMustStaple = mkOption {
type = types.bool;
inherit (defaultAndText "ocspMustStaple" false) default defaultText;
description = ''
Turns on the OCSP Must-Staple TLS extension.
Make sure you know what you're doing! See:
<itemizedlist>
<listitem><para><link xlink:href="https://blog.apnic.net/2019/01/15/is-the-web-ready-for-ocsp-must-staple/" /></para></listitem>
<listitem><para><link xlink:href="https://blog.hboeck.de/archives/886-The-Problem-with-OCSP-Stapling-and-Must-Staple-and-why-Certificate-Revocation-is-still-broken.html" /></para></listitem>
</itemizedlist>
'';
};
extraLegoFlags = mkOption {
type = types.listOf types.str;
inherit (defaultAndText "extraLegoFlags" []) default defaultText;
description = ''
Additional global flags to pass to all lego commands.
'';
};
extraLegoRenewFlags = mkOption {
type = types.listOf types.str;
inherit (defaultAndText "extraLegoRenewFlags" []) default defaultText;
description = ''
Additional flags to pass to lego renew.
'';
};
extraLegoRunFlags = mkOption {
type = types.listOf types.str;
inherit (defaultAndText "extraLegoRunFlags" []) default defaultText;
description = ''
Additional flags to pass to lego run.
'';
};
};
};
certOpts = { name, config, ... }: {
options = { options = {
# user option has been removed # user option has been removed
user = mkOption { user = mkOption {
@ -441,40 +633,11 @@ let
default = "_mkMergedOptionModule"; default = "_mkMergedOptionModule";
}; };
enableDebugLogs = mkEnableOption "debug logging for this certificate" // { default = cfg.enableDebugLogs; }; directory = mkOption {
type = types.str;
webroot = mkOption { readOnly = true;
type = types.nullOr types.str; default = "/var/lib/acme/${name}";
default = null; description = "Directory where certificate and other state is stored.";
example = "/var/lib/acme/acme-challenge";
description = ''
Where the webroot of the HTTP vhost is located.
<filename>.well-known/acme-challenge/</filename> directory
will be created below the webroot if it doesn't exist.
<literal>http://example.org/.well-known/acme-challenge/</literal> must also
be available (notice unencrypted HTTP).
'';
};
listenHTTP = mkOption {
type = types.nullOr types.str;
default = null;
example = ":1360";
description = ''
Interface and port to listen on to solve HTTP challenges
in the form [INTERFACE]:PORT.
If you use a port other than 80, you must proxy port 80 to this port.
'';
};
server = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
ACME Directory Resource URI. Defaults to Let's Encrypt's
production endpoint,
<link xlink:href="https://acme-v02.api.letsencrypt.org/directory"/>, if unset.
'';
}; };
domain = mkOption { domain = mkOption {
@ -483,47 +646,6 @@ let
description = "Domain to fetch certificate for (defaults to the entry name)."; description = "Domain to fetch certificate for (defaults to the entry name).";
}; };
email = mkOption {
type = types.nullOr types.str;
default = cfg.email;
defaultText = literalExpression "config.${opt.email}";
description = "Contact email address for the CA to be able to reach you.";
};
group = mkOption {
type = types.str;
default = "acme";
description = "Group running the ACME client.";
};
reloadServices = mkOption {
type = types.listOf types.str;
default = [];
description = ''
The list of systemd services to call <code>systemctl try-reload-or-restart</code>
on.
'';
};
postRun = mkOption {
type = types.lines;
default = "";
example = "cp full.pem backup.pem";
description = ''
Commands to run after new certificates go live. Note that
these commands run as the root user.
Executed in the same directory with the new certificate.
'';
};
directory = mkOption {
type = types.str;
readOnly = true;
default = "/var/lib/acme/${name}";
description = "Directory where certificate and other state is stored.";
};
extraDomainNames = mkOption { extraDomainNames = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [];
@ -538,92 +660,25 @@ let
''; '';
}; };
keyType = mkOption { # This setting must be different for each configured certificate, otherwise
type = types.str; # two or more renewals may fail to bind to the address. Hence, it is not in
default = "ec256"; # the inheritableOpts.
description = '' listenHTTP = mkOption {
Key type to use for private keys.
For an up to date list of supported values check the --key-type option
at <link xlink:href="https://go-acme.github.io/lego/usage/cli/#usage"/>.
'';
};
dnsProvider = mkOption {
type = types.nullOr types.str; type = types.nullOr types.str;
default = null; default = null;
example = "route53"; example = ":1360";
description = '' description = ''
DNS Challenge provider. For a list of supported providers, see the "code" Interface and port to listen on to solve HTTP challenges
field of the DNS providers listed at <link xlink:href="https://go-acme.github.io/lego/dns/"/>. in the form [INTERFACE]:PORT.
If you use a port other than 80, you must proxy port 80 to this port.
''; '';
}; };
dnsResolver = mkOption { inheritDefaults = mkOption {
type = types.nullOr types.str;
default = null;
example = "1.1.1.1:53";
description = ''
Set the resolver to use for performing recursive DNS queries. Supported:
host:port. The default is to use the system resolvers, or Google's DNS
resolvers if the system's cannot be determined.
'';
};
credentialsFile = mkOption {
type = types.path;
description = ''
Path to an EnvironmentFile for the cert's service containing any required and
optional environment variables for your selected dnsProvider.
To find out what values you need to set, consult the documentation at
<link xlink:href="https://go-acme.github.io/lego/dns/"/> for the corresponding dnsProvider.
'';
example = "/var/src/secrets/example.org-route53-api-token";
};
dnsPropagationCheck = mkOption {
type = types.bool;
default = true; default = true;
description = '' example = true;
Toggles lego DNS propagation check, which is used alongside DNS-01 description = "Whether to inherit values set in `security.acme.defaults` or not.";
challenge to ensure the DNS entries required are available. type = lib.types.bool;
'';
};
ocspMustStaple = mkOption {
type = types.bool;
default = false;
description = ''
Turns on the OCSP Must-Staple TLS extension.
Make sure you know what you're doing! See:
<itemizedlist>
<listitem><para><link xlink:href="https://blog.apnic.net/2019/01/15/is-the-web-ready-for-ocsp-must-staple/" /></para></listitem>
<listitem><para><link xlink:href="https://blog.hboeck.de/archives/886-The-Problem-with-OCSP-Stapling-and-Must-Staple-and-why-Certificate-Revocation-is-still-broken.html" /></para></listitem>
</itemizedlist>
'';
};
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 = [];
description = ''
Additional flags to pass to lego renew.
'';
};
extraLegoRunFlags = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Additional flags to pass to lego run.
'';
}; };
}; };
}; };
@ -632,41 +687,6 @@ in {
options = { options = {
security.acme = { security.acme = {
enableDebugLogs = mkEnableOption "debug logging for all certificates by default" // { default = true; };
validMinDays = mkOption {
type = types.int;
default = 30;
description = "Minimum remaining validity before renewal in days.";
};
email = mkOption {
type = types.nullOr types.str;
default = null;
description = "Contact email address for the CA to be able to reach you.";
};
renewInterval = mkOption {
type = types.str;
default = "daily";
description = ''
Systemd calendar expression when to check for renewal. See
<citerefentry><refentrytitle>systemd.time</refentrytitle>
<manvolnum>7</manvolnum></citerefentry>.
'';
};
server = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
ACME Directory Resource URI. Defaults to Let's Encrypt's
production endpoint,
<link xlink:href="https://acme-v02.api.letsencrypt.org/directory"/>, if unset.
'';
};
preliminarySelfsigned = mkOption { preliminarySelfsigned = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
@ -689,9 +709,31 @@ in {
''; '';
}; };
useRoot = mkOption {
type = types.bool;
default = false;
description = ''
Whether to use the root user when generating certs. This is not recommended
for security + compatiblity reasons. If a service requires root owned certificates
consider following the guide on "Using ACME with services demanding root
owned certificates" in the NixOS manual, and only using this as a fallback
or for testing.
'';
};
defaults = mkOption {
type = types.submodule (inheritableModule true);
description = ''
Default values inheritable by all configured certs. You can
use this to define options shared by all your certs. These defaults
can also be ignored on a per-cert basis using the
`security.acme.certs.''${cert}.inheritDefaults' option.
'';
};
certs = mkOption { certs = mkOption {
default = { }; default = { };
type = with types; attrsOf (submodule certOpts); type = with types; attrsOf (submodule [ (inheritableModule false) certOpts ]);
description = '' description = ''
Attribute set of certificates to get signed and renewed. Creates Attribute set of certificates to get signed and renewed. Creates
<literal>acme-''${cert}.{service,timer}</literal> systemd units for <literal>acme-''${cert}.{service,timer}</literal> systemd units for
@ -722,12 +764,16 @@ in {
To use the let's encrypt staging server, use security.acme.server = To use the let's encrypt staging server, use security.acme.server =
"https://acme-staging-v02.api.letsencrypt.org/directory". "https://acme-staging-v02.api.letsencrypt.org/directory".
'' '')
)
(mkRemovedOptionModule [ "security" "acme" "directory" ] "ACME Directory is now hardcoded to /var/lib/acme and its permisisons are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.") (mkRemovedOptionModule [ "security" "acme" "directory" ] "ACME Directory is now hardcoded to /var/lib/acme and its permisisons are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.")
(mkRemovedOptionModule [ "security" "acme" "preDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal") (mkRemovedOptionModule [ "security" "acme" "preDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
(mkRemovedOptionModule [ "security" "acme" "activationDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal") (mkRemovedOptionModule [ "security" "acme" "activationDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
(mkChangedOptionModule [ "security" "acme" "validMin" ] [ "security" "acme" "validMinDays" ] (config: config.security.acme.validMin / (24 * 3600))) (mkChangedOptionModule [ "security" "acme" "validMin" ] [ "security" "acme" "defaults" "validMinDays" ] (config: config.security.acme.validMin / (24 * 3600)))
(mkChangedOptionModule [ "security" "acme" "validMinDays" ] [ "security" "acme" "defaults" "validMinDays" ] (config: config.security.acme.validMinDays))
(mkChangedOptionModule [ "security" "acme" "renewInterval" ] [ "security" "acme" "defaults" "renewInterval" ] (config: config.security.acme.renewInterval))
(mkChangedOptionModule [ "security" "acme" "email" ] [ "security" "acme" "defaults" "email" ] (config: config.security.acme.email))
(mkChangedOptionModule [ "security" "acme" "server" ] [ "security" "acme" "defaults" "server" ] (config: config.security.acme.server))
(mkChangedOptionModule [ "security" "acme" "enableDebugLogs" ] [ "security" "acme" "defaults" "enableDebugLogs" ] (config: config.security.acme.enableDebugLogs))
]; ];
config = mkMerge [ config = mkMerge [
@ -842,8 +888,8 @@ in {
# Create some targets which can be depended on to be "active" after cert renewals # Create some targets which can be depended on to be "active" after cert renewals
finishedTargets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" { finishedTargets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" {
wantedBy = [ "default.target" ]; wantedBy = [ "default.target" ];
requires = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; requires = [ "acme-${cert}.service" ];
after = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; after = [ "acme-${cert}.service" ];
}) certConfigs; }) certConfigs;
# Create targets to limit the number of simultaneous account creations # Create targets to limit the number of simultaneous account creations

View file

@ -7,8 +7,9 @@
<para> <para>
NixOS supports automatic domain validation &amp; certificate retrieval and NixOS supports automatic domain validation &amp; certificate retrieval and
renewal using the ACME protocol. Any provider can be used, but by default renewal using the ACME protocol. Any provider can be used, but by default
NixOS uses Let's Encrypt. The alternative ACME client <literal>lego</literal> NixOS uses Let's Encrypt. The alternative ACME client
is used under the hood. <link xlink:href="https://go-acme.github.io/lego/">lego</link> is used under
the hood.
</para> </para>
<para> <para>
Automatic cert validation and configuration for Apache and Nginx virtual Automatic cert validation and configuration for Apache and Nginx virtual
@ -29,7 +30,7 @@
<para> <para>
You must also set an email address to be used when creating accounts with You must also set an email address to be used when creating accounts with
Let's Encrypt. You can set this for all certs with Let's Encrypt. You can set this for all certs with
<literal><xref linkend="opt-security.acme.email" /></literal> <literal><xref linkend="opt-security.acme.defaults.email" /></literal>
and/or on a per-cert basis with and/or on a per-cert basis with
<literal><xref linkend="opt-security.acme.certs._name_.email" /></literal>. <literal><xref linkend="opt-security.acme.certs._name_.email" /></literal>.
This address is only used for registration and renewal reminders, This address is only used for registration and renewal reminders,
@ -38,7 +39,7 @@
<para> <para>
Alternatively, you can use a different ACME server by changing the Alternatively, you can use a different ACME server by changing the
<literal><xref linkend="opt-security.acme.server" /></literal> option <literal><xref linkend="opt-security.acme.defaults.server" /></literal> option
to a provider of your choosing, or just change the server for one cert with to a provider of your choosing, or just change the server for one cert with
<literal><xref linkend="opt-security.acme.certs._name_.server" /></literal>. <literal><xref linkend="opt-security.acme.certs._name_.server" /></literal>.
</para> </para>
@ -60,12 +61,12 @@
= true;</literal> in a virtualHost config. We first create self-signed = true;</literal> in a virtualHost config. We first create self-signed
placeholder certificates in place of the real ACME certs. The placeholder placeholder certificates in place of the real ACME certs. The placeholder
certs are overwritten when the ACME certs arrive. For certs are overwritten when the ACME certs arrive. For
<literal>foo.example.com</literal> the config would look like. <literal>foo.example.com</literal> the config would look like this:
</para> </para>
<programlisting> <programlisting>
<xref linkend="opt-security.acme.acceptTerms" /> = true; <xref linkend="opt-security.acme.acceptTerms" /> = true;
<xref linkend="opt-security.acme.email" /> = "admin+acme@example.com"; <xref linkend="opt-security.acme.defaults.email" /> = "admin+acme@example.com";
services.nginx = { services.nginx = {
<link linkend="opt-services.nginx.enable">enable</link> = true; <link linkend="opt-services.nginx.enable">enable</link> = true;
<link linkend="opt-services.nginx.virtualHosts">virtualHosts</link> = { <link linkend="opt-services.nginx.virtualHosts">virtualHosts</link> = {
@ -114,7 +115,7 @@ services.nginx = {
<programlisting> <programlisting>
<xref linkend="opt-security.acme.acceptTerms" /> = true; <xref linkend="opt-security.acme.acceptTerms" /> = true;
<xref linkend="opt-security.acme.email" /> = "admin+acme@example.com"; <xref linkend="opt-security.acme.defaults.email" /> = "admin+acme@example.com";
# /var/lib/acme/.challenges must be writable by the ACME user # /var/lib/acme/.challenges must be writable by the ACME user
# and readable by the Nginx user. The easiest way to achieve # and readable by the Nginx user. The easiest way to achieve
@ -218,7 +219,7 @@ services.bind = {
# Now we can configure ACME # Now we can configure ACME
<xref linkend="opt-security.acme.acceptTerms" /> = true; <xref linkend="opt-security.acme.acceptTerms" /> = true;
<xref linkend="opt-security.acme.email" /> = "admin+acme@example.com"; <xref linkend="opt-security.acme.defaults.email" /> = "admin+acme@example.com";
<xref linkend="opt-security.acme.certs" />."example.com" = { <xref linkend="opt-security.acme.certs" />."example.com" = {
<link linkend="opt-security.acme.certs._name_.domain">domain</link> = "*.example.com"; <link linkend="opt-security.acme.certs._name_.domain">domain</link> = "*.example.com";
<link linkend="opt-security.acme.certs._name_.dnsProvider">dnsProvider</link> = "rfc2136"; <link linkend="opt-security.acme.certs._name_.dnsProvider">dnsProvider</link> = "rfc2136";
@ -231,10 +232,22 @@ services.bind = {
<para> <para>
The <filename>dnskeys.conf</filename> and <filename>certs.secret</filename> The <filename>dnskeys.conf</filename> and <filename>certs.secret</filename>
must be kept secure and thus you should not keep their contents in your must be kept secure and thus you should not keep their contents in your
Nix config. Instead, generate them one time with these commands: Nix config. Instead, generate them one time with a systemd service:
</para> </para>
<programlisting> <programlisting>
systemd.services.dns-rfc2136-conf = {
requiredBy = ["acme-example.com.service", "bind.service"];
before = ["acme-example.com.service", "bind.service"];
unitConfig = {
ConditionPathExists = "!/var/lib/secrets/dnskeys.conf";
};
serviceConfig = {
Type = "oneshot";
UMask = 0077;
};
path = [ pkgs.bind ];
script = ''
mkdir -p /var/lib/secrets mkdir -p /var/lib/secrets
tsig-keygen rfc2136key.example.com &gt; /var/lib/secrets/dnskeys.conf tsig-keygen rfc2136key.example.com &gt; /var/lib/secrets/dnskeys.conf
chown named:root /var/lib/secrets/dnskeys.conf chown named:root /var/lib/secrets/dnskeys.conf
@ -250,6 +263,8 @@ RFC2136_TSIG_KEY='rfc2136key.example.com'
RFC2136_TSIG_SECRET='your secret key' RFC2136_TSIG_SECRET='your secret key'
EOF EOF
chmod 400 /var/lib/secrets/certs.secret chmod 400 /var/lib/secrets/certs.secret
'';
};
</programlisting> </programlisting>
<para> <para>
@ -258,6 +273,106 @@ chmod 400 /var/lib/secrets/certs.secret
journalctl -fu acme-example.com.service</literal> and watching its log output. journalctl -fu acme-example.com.service</literal> and watching its log output.
</para> </para>
</section> </section>
<section xml:id="module-security-acme-config-dns-with-vhosts">
<title>Using DNS validation with web server virtual hosts</title>
<para>
It is possible to use DNS-01 validation with all certificates,
including those automatically configured via the Nginx/Apache
<literal><link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link></literal>
option. This configuration pattern is fully
supported and part of the module's test suite for Nginx + Apache.
</para>
<para>
You must follow the guide above on configuring DNS-01 validation
first, however instead of setting the options for one certificate
(e.g. <xref linkend="opt-security.acme.certs._name_.dnsProvider" />)
you will set them as defaults
(e.g. <xref linkend="opt-security.acme.defaults.dnsProvider" />).
</para>
<programlisting>
# Configure ACME appropriately
<xref linkend="opt-security.acme.acceptTerms" /> = true;
<xref linkend="opt-security.acme.defaults.email" /> = "admin+acme@example.com";
<xref linkend="opt-security.acme.defaults" /> = {
<link linkend="opt-security.acme.defaults.dnsProvider">dnsProvider</link> = "rfc2136";
<link linkend="opt-security.acme.defaults.credentialsFile">credentialsFile</link> = "/var/lib/secrets/certs.secret";
# We don't need to wait for propagation since this is a local DNS server
<link linkend="opt-security.acme.defaults.dnsPropagationCheck">dnsPropagationCheck</link> = false;
};
# For each virtual host you would like to use DNS-01 validation with,
# set acmeRoot = null
services.nginx = {
<link linkend="opt-services.nginx.enable">enable</link> = true;
<link linkend="opt-services.nginx.virtualHosts">virtualHosts</link> = {
"foo.example.com" = {
<link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
<link linkend="opt-services.nginx.virtualHosts._name_.acmeRoot">acmeRoot</link> = null;
};
};
}
</programlisting>
<para>
And that's it! Next time your configuration is rebuilt, or when
you add a new virtualHost, it will be DNS-01 validated.
</para>
</section>
<section xml:id="module-security-acme-root-owned">
<title>Using ACME with services demanding root owned certificates</title>
<para>
Some services refuse to start if the configured certificate files
are not owned by root. PostgreSQL and OpenSMTPD are examples of these.
There is no way to change the user the ACME module uses (it will always be
<literal>acme</literal>), however you can use systemd's
<literal>LoadCredential</literal> feature to resolve this elegantly.
Below is an example configuration for OpenSMTPD, but this pattern
can be applied to any service.
</para>
<programlisting>
# Configure ACME however you like (DNS or HTTP validation), adding
# the following configuration for the relevant certificate.
# Note: You cannot use `systemctl reload` here as that would mean
# the LoadCredential configuration below would be skipped and
# the service would continue to use old certificates.
security.acme.certs."mail.example.com".postRun = ''
systemctl restart opensmtpd
'';
# Now you must augment OpenSMTPD's systemd service to load
# the certificate files.
<link linkend="opt-systemd.services._name_.requires">systemd.services.opensmtpd.requires</link> = ["acme-finished-mail.example.com.target"];
<link linkend="opt-systemd.services._name_.serviceConfig">systemd.services.opensmtpd.serviceConfig.LoadCredential</link> = let
certDir = config.security.acme.certs."mail.example.com".directory;
in [
"cert.pem:${certDir}/cert.pem"
"key.pem:${certDir}/key.pem"
];
# Finally, configure OpenSMTPD to use these certs.
services.opensmtpd = let
credsDir = "/run/credentials/opensmtpd.service";
in {
enable = true;
setSendmail = false;
serverConfiguration = ''
pki mail.example.com cert "${credsDir}/cert.pem"
pki mail.example.com key "${credsDir}/key.pem"
listen on localhost tls pki mail.example.com
action act1 relay host smtp://127.0.0.1:10027
match for local action act1
'';
};
</programlisting>
</section>
<section xml:id="module-security-acme-regenerate"> <section xml:id="module-security-acme-regenerate">
<title>Regenerating certificates</title> <title>Regenerating certificates</title>

View file

@ -18,6 +18,20 @@ in
''; '';
}; };
dataDir = mkOption {
type = types.str;
default = "/var/lib/duplicati";
description = ''
The directory where Duplicati stores its data files.
<note><para>
If left as the default value this directory will automatically be created
before the Duplicati server starts, otherwise you are responsible for ensuring
the directory exists with appropriate ownership and permissions.
</para></note>
'';
};
interface = mkOption { interface = mkOption {
default = "127.0.0.1"; default = "127.0.0.1";
type = types.str; type = types.str;
@ -45,20 +59,23 @@ in
description = "Duplicati backup"; description = "Duplicati backup";
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = mkMerge [
{
User = cfg.user; User = cfg.user;
Group = "duplicati"; Group = "duplicati";
StateDirectory = "duplicati"; ExecStart = "${pkgs.duplicati}/bin/duplicati-server --webservice-interface=${cfg.interface} --webservice-port=${toString cfg.port} --server-datafolder=${cfg.dataDir}";
ExecStart = "${pkgs.duplicati}/bin/duplicati-server --webservice-interface=${cfg.interface} --webservice-port=${toString cfg.port} --server-datafolder=/var/lib/duplicati";
Restart = "on-failure"; Restart = "on-failure";
}; }
(mkIf (cfg.dataDir == "/var/lib/duplicati") {
StateDirectory = "duplicati";
})
];
}; };
users.users = lib.optionalAttrs (cfg.user == "duplicati") { users.users = lib.optionalAttrs (cfg.user == "duplicati") {
duplicati = { duplicati = {
uid = config.ids.uids.duplicati; uid = config.ids.uids.duplicati;
home = "/var/lib/duplicati"; home = cfg.dataDir;
createHome = true;
group = "duplicati"; group = "duplicati";
}; };
}; };

View file

@ -58,7 +58,7 @@ in
"spec" = { ... }; "spec" = { ... };
}; };
} }
// import <nixpkgs/nixos/modules/services/cluster/kubernetes/dashboard.nix> { cfg = config.services.kubernetes; }; // import <nixpkgs/nixos/modules/services/cluster/kubernetes/dns.nix> { cfg = config.services.kubernetes; };
''; '';
}; };

View file

@ -1,344 +0,0 @@
{ config, options, pkgs, lib, ... }:
with lib;
let
cfg = config.services.kubernetes.addons.dashboard;
opt = options.services.kubernetes.addons.dashboard;
in {
imports = [
(mkRenamedOptionModule [ "services" "kubernetes" "addons" "dashboard" "enableRBAC" ] [ "services" "kubernetes" "addons" "dashboard" "rbac" "enable" ])
];
options.services.kubernetes.addons.dashboard = {
enable = mkEnableOption "kubernetes dashboard addon";
extraArgs = mkOption {
description = "Extra arguments to append to the dashboard cmdline";
type = types.listOf types.str;
default = [];
example = ["--enable-skip-login"];
};
rbac = mkOption {
description = "Role-based access control (RBAC) options";
default = {};
type = types.submodule {
options = {
enable = mkOption {
description = "Whether to enable role based access control is enabled for kubernetes dashboard";
type = types.bool;
default = elem "RBAC" config.services.kubernetes.apiserver.authorizationMode;
defaultText = literalExpression ''
elem "RBAC" config.${options.services.kubernetes.apiserver.authorizationMode}
'';
};
clusterAdmin = mkOption {
description = "Whether to assign cluster admin rights to the kubernetes dashboard";
type = types.bool;
default = false;
};
};
};
};
version = mkOption {
description = "Which version of the kubernetes dashboard to deploy";
type = types.str;
default = "v1.10.1";
};
image = mkOption {
description = "Docker image to seed for the kubernetes dashboard container.";
type = types.attrs;
default = {
imageName = "k8s.gcr.io/kubernetes-dashboard-amd64";
imageDigest = "sha256:0ae6b69432e78069c5ce2bcde0fe409c5c4d6f0f4d9cd50a17974fea38898747";
finalImageTag = cfg.version;
sha256 = "01xrr4pwgr2hcjrjsi3d14ifpzdfbxzqpzxbk2fkbjb9zkv38zxy";
};
defaultText = literalExpression ''
{
imageName = "k8s.gcr.io/kubernetes-dashboard-amd64";
imageDigest = "sha256:0ae6b69432e78069c5ce2bcde0fe409c5c4d6f0f4d9cd50a17974fea38898747";
finalImageTag = config.${opt.version};
sha256 = "01xrr4pwgr2hcjrjsi3d14ifpzdfbxzqpzxbk2fkbjb9zkv38zxy";
};
'';
};
};
config = mkIf cfg.enable {
services.kubernetes.kubelet.seedDockerImages = [(pkgs.dockerTools.pullImage cfg.image)];
services.kubernetes.addonManager.addons = {
kubernetes-dashboard-deployment = {
kind = "Deployment";
apiVersion = "apps/v1";
metadata = {
labels = {
k8s-addon = "kubernetes-dashboard.addons.k8s.io";
k8s-app = "kubernetes-dashboard";
version = cfg.version;
"kubernetes.io/cluster-service" = "true";
"addonmanager.kubernetes.io/mode" = "Reconcile";
};
name = "kubernetes-dashboard";
namespace = "kube-system";
};
spec = {
replicas = 1;
revisionHistoryLimit = 10;
selector.matchLabels.k8s-app = "kubernetes-dashboard";
template = {
metadata = {
labels = {
k8s-addon = "kubernetes-dashboard.addons.k8s.io";
k8s-app = "kubernetes-dashboard";
version = cfg.version;
"kubernetes.io/cluster-service" = "true";
};
annotations = {
"scheduler.alpha.kubernetes.io/critical-pod" = "";
};
};
spec = {
priorityClassName = "system-cluster-critical";
containers = [{
name = "kubernetes-dashboard";
image = with cfg.image; "${imageName}:${finalImageTag}";
ports = [{
containerPort = 8443;
protocol = "TCP";
}];
resources = {
limits = {
cpu = "100m";
memory = "300Mi";
};
requests = {
cpu = "100m";
memory = "100Mi";
};
};
args = ["--auto-generate-certificates"] ++ cfg.extraArgs;
volumeMounts = [{
name = "tmp-volume";
mountPath = "/tmp";
} {
name = "kubernetes-dashboard-certs";
mountPath = "/certs";
}];
livenessProbe = {
httpGet = {
scheme = "HTTPS";
path = "/";
port = 8443;
};
initialDelaySeconds = 30;
timeoutSeconds = 30;
};
}];
volumes = [{
name = "kubernetes-dashboard-certs";
secret = {
secretName = "kubernetes-dashboard-certs";
};
} {
name = "tmp-volume";
emptyDir = {};
}];
serviceAccountName = "kubernetes-dashboard";
tolerations = [{
key = "node-role.kubernetes.io/master";
effect = "NoSchedule";
} {
key = "CriticalAddonsOnly";
operator = "Exists";
}];
};
};
};
};
kubernetes-dashboard-svc = {
apiVersion = "v1";
kind = "Service";
metadata = {
labels = {
k8s-addon = "kubernetes-dashboard.addons.k8s.io";
k8s-app = "kubernetes-dashboard";
"kubernetes.io/cluster-service" = "true";
"kubernetes.io/name" = "KubeDashboard";
"addonmanager.kubernetes.io/mode" = "Reconcile";
};
name = "kubernetes-dashboard";
namespace = "kube-system";
};
spec = {
ports = [{
port = 443;
targetPort = 8443;
}];
selector.k8s-app = "kubernetes-dashboard";
};
};
kubernetes-dashboard-sa = {
apiVersion = "v1";
kind = "ServiceAccount";
metadata = {
labels = {
k8s-app = "kubernetes-dashboard";
k8s-addon = "kubernetes-dashboard.addons.k8s.io";
"addonmanager.kubernetes.io/mode" = "Reconcile";
};
name = "kubernetes-dashboard";
namespace = "kube-system";
};
};
kubernetes-dashboard-sec-certs = {
apiVersion = "v1";
kind = "Secret";
metadata = {
labels = {
k8s-app = "kubernetes-dashboard";
# Allows editing resource and makes sure it is created first.
"addonmanager.kubernetes.io/mode" = "EnsureExists";
};
name = "kubernetes-dashboard-certs";
namespace = "kube-system";
};
type = "Opaque";
};
kubernetes-dashboard-sec-kholder = {
apiVersion = "v1";
kind = "Secret";
metadata = {
labels = {
k8s-app = "kubernetes-dashboard";
# Allows editing resource and makes sure it is created first.
"addonmanager.kubernetes.io/mode" = "EnsureExists";
};
name = "kubernetes-dashboard-key-holder";
namespace = "kube-system";
};
type = "Opaque";
};
kubernetes-dashboard-cm = {
apiVersion = "v1";
kind = "ConfigMap";
metadata = {
labels = {
k8s-app = "kubernetes-dashboard";
# Allows editing resource and makes sure it is created first.
"addonmanager.kubernetes.io/mode" = "EnsureExists";
};
name = "kubernetes-dashboard-settings";
namespace = "kube-system";
};
};
} // (optionalAttrs cfg.rbac.enable
(let
subjects = [{
kind = "ServiceAccount";
name = "kubernetes-dashboard";
namespace = "kube-system";
}];
labels = {
k8s-app = "kubernetes-dashboard";
k8s-addon = "kubernetes-dashboard.addons.k8s.io";
"addonmanager.kubernetes.io/mode" = "Reconcile";
};
in
(if cfg.rbac.clusterAdmin then {
kubernetes-dashboard-crb = {
apiVersion = "rbac.authorization.k8s.io/v1";
kind = "ClusterRoleBinding";
metadata = {
name = "kubernetes-dashboard";
inherit labels;
};
roleRef = {
apiGroup = "rbac.authorization.k8s.io";
kind = "ClusterRole";
name = "cluster-admin";
};
inherit subjects;
};
}
else
{
# Upstream role- and rolebinding as per:
# https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/alternative/kubernetes-dashboard.yaml
kubernetes-dashboard-role = {
apiVersion = "rbac.authorization.k8s.io/v1";
kind = "Role";
metadata = {
name = "kubernetes-dashboard-minimal";
namespace = "kube-system";
inherit labels;
};
rules = [
# Allow Dashboard to create 'kubernetes-dashboard-key-holder' secret.
{
apiGroups = [""];
resources = ["secrets"];
verbs = ["create"];
}
# Allow Dashboard to create 'kubernetes-dashboard-settings' config map.
{
apiGroups = [""];
resources = ["configmaps"];
verbs = ["create"];
}
# Allow Dashboard to get, update and delete Dashboard exclusive secrets.
{
apiGroups = [""];
resources = ["secrets"];
resourceNames = ["kubernetes-dashboard-key-holder"];
verbs = ["get" "update" "delete"];
}
# Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map.
{
apiGroups = [""];
resources = ["configmaps"];
resourceNames = ["kubernetes-dashboard-settings"];
verbs = ["get" "update"];
}
# Allow Dashboard to get metrics from heapster.
{
apiGroups = [""];
resources = ["services"];
resourceNames = ["heapster"];
verbs = ["proxy"];
}
{
apiGroups = [""];
resources = ["services/proxy"];
resourceNames = ["heapster" "http:heapster:" "https:heapster:"];
verbs = ["get"];
}
];
};
kubernetes-dashboard-rb = {
apiVersion = "rbac.authorization.k8s.io/v1";
kind = "RoleBinding";
metadata = {
name = "kubernetes-dashboard-minimal";
namespace = "kube-system";
inherit labels;
};
roleRef = {
apiGroup = "rbac.authorization.k8s.io";
kind = "Role";
name = "kubernetes-dashboard-minimal";
};
inherit subjects;
};
})
));
};
}

View file

@ -106,6 +106,7 @@ let
in { in {
imports = [ imports = [
(mkRemovedOptionModule [ "services" "kubernetes" "addons" "dashboard" ] "Removed due to it being an outdated version")
(mkRemovedOptionModule [ "services" "kubernetes" "verbose" ] "") (mkRemovedOptionModule [ "services" "kubernetes" "verbose" ] "")
]; ];

View file

@ -1,5 +1,7 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;
let let
format = pkgs.formats.json { }; format = pkgs.formats.json { };
cfg = config.services.influxdb2; cfg = config.services.influxdb2;
@ -9,12 +11,14 @@ in
options = { options = {
services.influxdb2 = { services.influxdb2 = {
enable = mkEnableOption "the influxdb2 server"; enable = mkEnableOption "the influxdb2 server";
package = mkOption { package = mkOption {
default = pkgs.influxdb2-server; default = pkgs.influxdb2-server;
defaultText = literalExpression "pkgs.influxdb2"; defaultText = literalExpression "pkgs.influxdb2";
description = "influxdb2 derivation to use."; description = "influxdb2 derivation to use.";
type = types.package; type = types.package;
}; };
settings = mkOption { settings = mkOption {
default = { }; default = { };
description = ''configuration options for influxdb2, see <link xlink:href="https://docs.influxdata.com/influxdb/v2.0/reference/config-options"/> for details.''; description = ''configuration options for influxdb2, see <link xlink:href="https://docs.influxdata.com/influxdb/v2.0/reference/config-options"/> for details.'';
@ -28,18 +32,20 @@ in
assertion = !(builtins.hasAttr "bolt-path" cfg.settings) && !(builtins.hasAttr "engine-path" cfg.settings); assertion = !(builtins.hasAttr "bolt-path" cfg.settings) && !(builtins.hasAttr "engine-path" cfg.settings);
message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd";
}]; }];
systemd.services.influxdb2 = { systemd.services.influxdb2 = {
description = "InfluxDB is an open-source, distributed, time series database"; description = "InfluxDB is an open-source, distributed, time series database";
documentation = [ "https://docs.influxdata.com/influxdb/" ]; documentation = [ "https://docs.influxdata.com/influxdb/" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ "network.target" ]; after = [ "network.target" ];
environment = { environment = {
INFLUXD_CONFIG_PATH = "${configFile}"; INFLUXD_CONFIG_PATH = configFile;
}; };
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/influxd --bolt-path \${STATE_DIRECTORY}/influxd.bolt --engine-path \${STATE_DIRECTORY}/engine"; ExecStart = "${cfg.package}/bin/influxd --bolt-path \${STATE_DIRECTORY}/influxd.bolt --engine-path \${STATE_DIRECTORY}/engine";
StateDirectory = "influxdb2"; StateDirectory = "influxdb2";
DynamicUser = true; User = "influxdb2";
Group = "influxdb2";
CapabilityBoundingSet = ""; CapabilityBoundingSet = "";
SystemCallFilter = "@system-service"; SystemCallFilter = "@system-service";
LimitNOFILE = 65536; LimitNOFILE = 65536;
@ -47,6 +53,13 @@ in
Restart = "on-failure"; Restart = "on-failure";
}; };
}; };
users.extraUsers.influxdb2 = {
isSystemUser = true;
group = "influxdb2";
};
users.extraGroups.influxdb2 = {};
}; };
meta.maintainers = with lib.maintainers; [ nickcao ]; meta.maintainers = with lib.maintainers; [ nickcao ];

View file

@ -54,7 +54,7 @@ in
systemd.packages = [ cfg.package ]; systemd.packages = [ cfg.package ];
services.udev.packages = [ pkgs.libmtp.bin ]; services.udev.packages = [ pkgs.libmtp ];
# Needed for unwrapped applications # Needed for unwrapped applications
environment.variables.GIO_EXTRA_MODULES = [ "${cfg.package}/lib/gio/modules" ]; environment.variables.GIO_EXTRA_MODULES = [ "${cfg.package}/lib/gio/modules" ];

View file

@ -19,7 +19,7 @@ let
description = "tuple of" + concatMapStrings (t: " (${t.description})") ts; description = "tuple of" + concatMapStrings (t: " (${t.description})") ts;
}; };
level = ints.unsigned; level = ints.unsigned;
special = enum [ "level auto" "level full-speed" "level disengage" ]; special = enum [ "level auto" "level full-speed" "level disengaged" ];
in in
tuple [ (either level special) level level ]; tuple [ (either level special) level level ];
@ -164,7 +164,7 @@ in {
LEVEL is the fan level to use: it can be an integer (0-7 with thinkpad_acpi), LEVEL is the fan level to use: it can be an integer (0-7 with thinkpad_acpi),
"level auto" (to keep the default firmware behavior), "level full-speed" or "level auto" (to keep the default firmware behavior), "level full-speed" or
"level disengage" (to run the fan as fast as possible). "level disengaged" (to run the fan as fast as possible).
LOW is the temperature at which to step down to the previous level. LOW is the temperature at which to step down to the previous level.
HIGH is the temperature at which to step up to the next level. HIGH is the temperature at which to step up to the next level.
All numbers are integers. All numbers are integers.

View file

@ -145,7 +145,7 @@ in {
--config='${settingsFile}' \ --config='${settingsFile}' \
--registration='${registrationFile}' --registration='${registrationFile}'
fi fi
'' + lib.optionalString (pkgs.mautrix-telegram ? alembic) ''
# run automatic database init and migration scripts # run automatic database init and migration scripts
${pkgs.mautrix-telegram.alembic}/bin/alembic -x config='${settingsFile}' upgrade head ${pkgs.mautrix-telegram.alembic}/bin/alembic -x config='${settingsFile}' upgrade head
''; '';

File diff suppressed because it is too large Load diff

View file

@ -1,66 +1,375 @@
{ config, pkgs, lib }: srv:
serviceCfg: serviceDrv: iniKey: attrs: { configIniOfService
let , srvsrht ? "${srv}srht" # Because "buildsrht" does not follow that pattern (missing an "s").
cfg = config.services.sourcehut; , iniKey ? "${srv}.sr.ht"
cfgIni = cfg.settings."${iniKey}"; , webhooks ? false
pgSuperUser = config.services.postgresql.superUser; , extraTimers ? {}
, mainService ? {}
, extraServices ? {}
, extraConfig ? {}
, port
}:
{ config, lib, pkgs, ... }:
setupDB = pkgs.writeScript "${serviceDrv.pname}-gen-db" '' with lib;
#! ${cfg.python}/bin/python let
from ${serviceDrv.pname}.app import db inherit (config.services) postgresql;
db.create() redis = config.services.redis.servers."sourcehut-${srvsrht}";
''; inherit (config.users) users;
cfg = config.services.sourcehut;
configIni = configIniOfService srv;
srvCfg = cfg.${srv};
baseService = serviceName: { allowStripe ? false }: extraService: let
runDir = "/run/sourcehut/${serviceName}";
rootDir = "/run/sourcehut/chroots/${serviceName}";
in
mkMerge [ extraService {
after = [ "network.target" ] ++
optional cfg.postgresql.enable "postgresql.service" ++
optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
requires =
optional cfg.postgresql.enable "postgresql.service" ++
optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
path = [ pkgs.gawk ];
environment.HOME = runDir;
serviceConfig = {
User = mkDefault srvCfg.user;
Group = mkDefault srvCfg.group;
RuntimeDirectory = [
"sourcehut/${serviceName}"
# Used by *srht-keys which reads ../config.ini
"sourcehut/${serviceName}/subdir"
"sourcehut/chroots/${serviceName}"
];
RuntimeDirectoryMode = "2750";
# No need for the chroot path once inside the chroot
InaccessiblePaths = [ "-+${rootDir}" ];
# g+rx is for group members (eg. fcgiwrap or nginx)
# to read Git/Mercurial repositories, buildlogs, etc.
# o+x is for intermediate directories created by BindPaths= and like,
# as they're owned by root:root.
UMask = "0026";
RootDirectory = rootDir;
RootDirectoryStartOnly = true;
PrivateTmp = true;
MountAPIVFS = true;
# config.ini is looked up in there, before /etc/srht/config.ini
# Note that it fails to be set in ExecStartPre=
WorkingDirectory = mkDefault ("-"+runDir);
BindReadOnlyPaths = [
builtins.storeDir
"/etc"
"/run/booted-system"
"/run/current-system"
"/run/systemd"
] ++
optional cfg.postgresql.enable "/run/postgresql" ++
optional cfg.redis.enable "/run/redis-sourcehut-${srvsrht}";
# LoadCredential= are unfortunately not available in ExecStartPre=
# Hence this one is run as root (the +) with RootDirectoryStartOnly=
# to reach credentials wherever they are.
# Note that each systemd service gets its own ${runDir}/config.ini file.
ExecStartPre = mkBefore [("+"+pkgs.writeShellScript "${serviceName}-credentials" ''
set -x
# Replace values begining with a '<' by the content of the file whose name is after.
gawk '{ if (match($0,/^([^=]+=)<(.+)/,m)) { getline f < m[2]; print m[1] f } else print $0 }' ${configIni} |
${optionalString (!allowStripe) "gawk '!/^stripe-secret-key=/' |"}
install -o ${srvCfg.user} -g root -m 400 /dev/stdin ${runDir}/config.ini
'')];
# The following options are only for optimizing:
# systemd-analyze security
AmbientCapabilities = "";
CapabilityBoundingSet = "";
# ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateNetwork = mkDefault false;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
#SocketBindAllow = [ "tcp:${toString srvCfg.port}" "tcp:${toString srvCfg.prometheusPort}" ];
#SocketBindDeny = "any";
SystemCallFilter = [
"@system-service"
"~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@timer"
"@chown" "@setuid"
];
SystemCallArchitectures = "native";
};
} ];
in in
with serviceCfg; with lib; recursiveUpdate
{ {
environment.HOME = statePath; options.services.sourcehut.${srv} = {
path = [ config.services.postgresql.package ] ++ (attrs.path or [ ]); enable = mkEnableOption "${srv} service";
restartTriggers = [ config.environment.etc."sr.ht/config.ini".source ];
user = mkOption {
type = types.str;
default = srvsrht;
description = ''
User for ${srv}.sr.ht.
'';
};
group = mkOption {
type = types.str;
default = srvsrht;
description = ''
Group for ${srv}.sr.ht.
Membership grants access to the Git/Mercurial repositories by default,
but not to the config.ini file (where secrets are).
'';
};
port = mkOption {
type = types.port;
default = port;
description = ''
Port on which the "${srv}" backend should listen.
'';
};
redis = {
host = mkOption {
type = types.str;
default = "unix:/run/redis-sourcehut-${srvsrht}/redis.sock?db=0";
example = "redis://shared.wireguard:6379/0";
description = ''
The redis host URL. This is used for caching and temporary storage, and must
be shared between nodes (e.g. git1.sr.ht and git2.sr.ht), but need not be
shared between services. It may be shared between services, however, with no
ill effect, if this better suits your infrastructure.
'';
};
};
postgresql = {
database = mkOption {
type = types.str;
default = "${srv}.sr.ht";
description = ''
PostgreSQL database name for the ${srv}.sr.ht service,
used if <xref linkend="opt-services.sourcehut.postgresql.enable"/> is <literal>true</literal>.
'';
};
};
gunicorn = {
extraArgs = mkOption {
type = with types; listOf str;
default = ["--timeout 120" "--workers 1" "--log-level=info"];
description = "Extra arguments passed to Gunicorn.";
};
};
} // optionalAttrs webhooks {
webhooks = {
extraArgs = mkOption {
type = with types; listOf str;
default = ["--loglevel DEBUG" "--pool eventlet" "--without-heartbeat"];
description = "Extra arguments passed to the Celery responsible for webhooks.";
};
celeryConfig = mkOption {
type = types.lines;
default = "";
description = "Content of the <literal>celeryconfig.py</literal> used by the Celery responsible for webhooks.";
};
};
};
config = lib.mkIf (cfg.enable && srvCfg.enable) (mkMerge [ extraConfig {
users = {
users = {
"${srvCfg.user}" = {
isSystemUser = true;
group = mkDefault srvCfg.group;
description = mkDefault "sourcehut user for ${srv}.sr.ht";
};
};
groups = {
"${srvCfg.group}" = { };
} // optionalAttrs (cfg.postgresql.enable
&& hasSuffix "0" (postgresql.settings.unix_socket_permissions or "")) {
"postgres".members = [ srvCfg.user ];
} // optionalAttrs (cfg.redis.enable
&& hasSuffix "0" (redis.settings.unixsocketperm or "")) {
"redis-sourcehut-${srvsrht}".members = [ srvCfg.user ];
};
};
services.nginx = mkIf cfg.nginx.enable {
virtualHosts."${srv}.${cfg.settings."sr.ht".global-domain}" = mkMerge [ {
forceSSL = mkDefault true;
locations."/".proxyPass = "http://${cfg.listenAddress}:${toString srvCfg.port}";
locations."/static" = {
root = "${pkgs.sourcehut.${srvsrht}}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
extraConfig = mkDefault ''
expires 30d;
'';
};
} cfg.nginx.virtualHost ];
};
services.postgresql = mkIf cfg.postgresql.enable {
authentication = ''
local ${srvCfg.postgresql.database} ${srvCfg.user} trust
'';
ensureDatabases = [ srvCfg.postgresql.database ];
ensureUsers = map (name: {
inherit name;
ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; };
}) [srvCfg.user];
};
services.sourcehut.services = mkDefault (filter (s: cfg.${s}.enable)
[ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
services.sourcehut.settings = mkMerge [
{
"${srv}.sr.ht".origin = mkDefault "https://${srv}.${cfg.settings."sr.ht".global-domain}";
}
(mkIf cfg.postgresql.enable {
"${srv}.sr.ht".connection-string = mkDefault "postgresql:///${srvCfg.postgresql.database}?user=${srvCfg.user}&host=/run/postgresql";
})
];
services.redis.servers."sourcehut-${srvsrht}" = mkIf cfg.redis.enable {
enable = true;
databases = 3;
syslog = true;
# TODO: set a more informed value
save = mkDefault [ [1800 10] [300 100] ];
settings = {
# TODO: set a more informed value
maxmemory = "128MB";
maxmemory-policy = "volatile-ttl";
};
};
systemd.services = mkMerge [
{
"${srvsrht}" = baseService srvsrht { allowStripe = srv == "meta"; } (mkMerge [
{
description = "sourcehut ${srv}.sr.ht website service";
before = optional cfg.nginx.enable "nginx.service";
wants = optional cfg.nginx.enable "nginx.service";
wantedBy = [ "multi-user.target" ];
path = optional cfg.postgresql.enable postgresql.package;
# Beware: change in credentials' content will not trigger restart.
restartTriggers = [ configIni ];
serviceConfig = { serviceConfig = {
Type = "simple"; Type = "simple";
User = user; Restart = mkDefault "always";
Group = user; #RestartSec = mkDefault "2min";
Restart = "always"; StateDirectory = [ "sourcehut/${srvsrht}" ];
WorkingDirectory = statePath; StateDirectoryMode = "2750";
} // (if (cfg.statePath == "/var/lib/sourcehut/${serviceDrv.pname}") then { ExecStart = "${cfg.python}/bin/gunicorn ${srvsrht}.app:app --name ${srvsrht} --bind ${cfg.listenAddress}:${toString srvCfg.port} " + concatStringsSep " " srvCfg.gunicorn.extraArgs;
StateDirectory = [ "sourcehut/${serviceDrv.pname}" ]; };
} else {}) preStart = let
; version = pkgs.sourcehut.${srvsrht}.version;
stateDir = "/var/lib/sourcehut/${srvsrht}";
in mkBefore ''
set -x
# Use the /run/sourcehut/${srvsrht}/config.ini
# installed by a previous ExecStartPre= in baseService
cd /run/sourcehut/${srvsrht}
preStart = '' if test ! -e ${stateDir}/db; then
if ! test -e ${statePath}/db; then # Setup the initial database.
# Setup the initial database # Note that it stamps the alembic head afterward
${setupDB} ${cfg.python}/bin/${srvsrht}-initdb
echo ${version} >${stateDir}/db
# Set the initial state of the database for future database upgrades
if test -e ${cfg.python}/bin/${serviceDrv.pname}-migrate; then
# Run alembic stamp head once to tell alembic the schema is up-to-date
${cfg.python}/bin/${serviceDrv.pname}-migrate stamp head
fi fi
printf "%s" "${serviceDrv.version}" > ${statePath}/db ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
fi if [ "$(cat ${stateDir}/db)" != "${version}" ]; then
# Update copy of each users' profile to the latest
# See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
if ! test -e ${statePath}/webhook; then
# Update ${iniKey}'s users' profile copy to the latest
${cfg.python}/bin/srht-update-profiles ${iniKey}
touch ${statePath}/webhook
fi
${optionalString (builtins.hasAttr "migrate-on-upgrade" cfgIni && cfgIni.migrate-on-upgrade == "yes") ''
if [ "$(cat ${statePath}/db)" != "${serviceDrv.version}" ]; then
# Manage schema migrations using alembic # Manage schema migrations using alembic
${cfg.python}/bin/${serviceDrv.pname}-migrate -a upgrade head ${cfg.python}/bin/${srvsrht}-migrate -a upgrade head
echo ${version} >${stateDir}/db
# Mark down current package version
printf "%s" "${serviceDrv.version}" > ${statePath}/db
fi fi
''} ''}
${attrs.preStart or ""} # Update copy of each users' profile to the latest
# See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
if test ! -e ${stateDir}/webhook; then
# Update ${iniKey}'s users' profile copy to the latest
${cfg.python}/bin/srht-update-profiles ${iniKey}
touch ${stateDir}/webhook
fi
''; '';
} mainService ]);
}
(mkIf webhooks {
"${srvsrht}-webhooks" = baseService "${srvsrht}-webhooks" {}
{
description = "sourcehut ${srv}.sr.ht webhooks service";
after = [ "${srvsrht}.service" ];
wantedBy = [ "${srvsrht}.service" ];
partOf = [ "${srvsrht}.service" ];
preStart = ''
cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" srvCfg.webhooks.celeryConfig} \
/run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
'';
serviceConfig = {
Type = "simple";
Restart = "always";
ExecStart = "${cfg.python}/bin/celery --app ${srvsrht}.webhooks worker --hostname ${srvsrht}-webhooks@%%h " + concatStringsSep " " srvCfg.webhooks.extraArgs;
# Avoid crashing: os.getloadavg()
ProcSubset = mkForce "all";
};
};
})
(mapAttrs (timerName: timer: (baseService timerName {} (mkMerge [
{
description = "sourcehut ${timerName} service";
after = [ "network.target" "${srvsrht}.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${cfg.python}/bin/${timerName}";
};
}
(timer.service or {})
]))) extraTimers)
(mapAttrs (serviceName: extraService: baseService serviceName {} (mkMerge [
{
description = "sourcehut ${serviceName} service";
# So that extraServices have the PostgreSQL database initialized.
after = [ "${srvsrht}.service" ];
wantedBy = [ "${srvsrht}.service" ];
partOf = [ "${srvsrht}.service" ];
serviceConfig = {
Type = "simple";
Restart = mkDefault "always";
};
}
extraService
])) extraServices)
];
systemd.timers = mapAttrs (timerName: timer:
{
description = "sourcehut timer for ${timerName}";
wantedBy = [ "timers.target" ];
inherit (timer) timerConfig;
}) extraTimers;
} ]);
} }
(builtins.removeAttrs attrs [ "path" "preStart" ])

View file

@ -14,13 +14,12 @@
<title>Basic usage</title> <title>Basic usage</title>
<para> <para>
Sourcehut is a Python and Go based set of applications. Sourcehut is a Python and Go based set of applications.
<literal><link linkend="opt-services.sourcehut.enable">services.sourcehut</link></literal> This NixOS module also provides basic configuration integrating Sourcehut into locally running
by default will use
<literal><link linkend="opt-services.nginx.enable">services.nginx</link></literal>, <literal><link linkend="opt-services.nginx.enable">services.nginx</link></literal>,
<literal><link linkend="opt-services.nginx.enable">services.redis</link></literal>, <literal><link linkend="opt-services.redis.servers">services.redis.servers.sourcehut</link></literal>,
<literal><link linkend="opt-services.nginx.enable">services.cron</link></literal>, <literal><link linkend="opt-services.postfix.enable">services.postfix</link></literal>
and and
<literal><link linkend="opt-services.postgresql.enable">services.postgresql</link></literal>. <literal><link linkend="opt-services.postgresql.enable">services.postgresql</link></literal> services.
</para> </para>
<para> <para>
@ -42,18 +41,23 @@ in {
services.sourcehut = { services.sourcehut = {
<link linkend="opt-services.sourcehut.enable">enable</link> = true; <link linkend="opt-services.sourcehut.enable">enable</link> = true;
<link linkend="opt-services.sourcehut.originBase">originBase</link> = fqdn; <link linkend="opt-services.sourcehut.git.enable">git.enable</link> = true;
<link linkend="opt-services.sourcehut.services">services</link> = [ "meta" "man" "git" ]; <link linkend="opt-services.sourcehut.man.enable">man.enable</link> = true;
<link linkend="opt-services.sourcehut.meta.enable">meta.enable</link> = true;
<link linkend="opt-services.sourcehut.nginx.enable">nginx.enable</link> = true;
<link linkend="opt-services.sourcehut.postfix.enable">postfix.enable</link> = true;
<link linkend="opt-services.sourcehut.postgresql.enable">postgresql.enable</link> = true;
<link linkend="opt-services.sourcehut.redis.enable">redis.enable</link> = true;
<link linkend="opt-services.sourcehut.settings">settings</link> = { <link linkend="opt-services.sourcehut.settings">settings</link> = {
"sr.ht" = { "sr.ht" = {
environment = "production"; environment = "production";
global-domain = fqdn; global-domain = fqdn;
origin = "https://${fqdn}"; origin = "https://${fqdn}";
# Produce keys with srht-keygen from <package>sourcehut.coresrht</package>. # Produce keys with srht-keygen from <package>sourcehut.coresrht</package>.
network-key = "SECRET"; network-key = "/run/keys/path/to/network-key";
service-key = "SECRET"; service-key = "/run/keys/path/to/service-key";
}; };
webhooks.private-key= "SECRET"; webhooks.private-key= "/run/keys/path/to/webhook-key";
}; };
}; };

View file

@ -236,6 +236,7 @@ in
environment = { environment = {
KEA_PIDFILE_DIR = "/run/kea"; KEA_PIDFILE_DIR = "/run/kea";
KEA_LOCKFILE_DIR = "/run/kea";
}; };
restartTriggers = [ restartTriggers = [
@ -271,6 +272,7 @@ in
environment = { environment = {
KEA_PIDFILE_DIR = "/run/kea"; KEA_PIDFILE_DIR = "/run/kea";
KEA_LOCKFILE_DIR = "/run/kea";
}; };
restartTriggers = [ restartTriggers = [
@ -313,6 +315,7 @@ in
environment = { environment = {
KEA_PIDFILE_DIR = "/run/kea"; KEA_PIDFILE_DIR = "/run/kea";
KEA_LOCKFILE_DIR = "/run/kea";
}; };
restartTriggers = [ restartTriggers = [
@ -353,6 +356,7 @@ in
environment = { environment = {
KEA_PIDFILE_DIR = "/run/kea"; KEA_PIDFILE_DIR = "/run/kea";
KEA_LOCKFILE_DIR = "/run/kea";
}; };
restartTriggers = [ restartTriggers = [
@ -361,7 +365,7 @@ in
serviceConfig = { serviceConfig = {
ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}"; ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}";
AmbientCapabilites = [ AmbientCapabilities = [
"CAP_NET_BIND_SERVICE" "CAP_NET_BIND_SERVICE"
]; ];
CapabilityBoundingSet = [ CapabilityBoundingSet = [

View file

@ -72,7 +72,7 @@ services.prosody = {
a TLS certificate for the three endponits: a TLS certificate for the three endponits:
<programlisting> <programlisting>
security.acme = { security.acme = {
<link linkend="opt-security.acme.email">email</link> = "root@example.org"; <link linkend="opt-security.acme.defaults.email">email</link> = "root@example.org";
<link linkend="opt-security.acme.acceptTerms">acceptTerms</link> = true; <link linkend="opt-security.acme.acceptTerms">acceptTerms</link> = true;
<link linkend="opt-security.acme.certs">certs</link> = { <link linkend="opt-security.acme.certs">certs</link> = {
"example.org" = { "example.org" = {

View file

@ -25,7 +25,7 @@ services.discourse = {
}; };
<link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file"; <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
}; };
<link linkend="opt-security.acme.email">security.acme.email</link> = "me@example.com"; <link linkend="opt-security.acme.defaults.email">security.acme.email</link> = "me@example.com";
<link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true; <link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true;
</programlisting> </programlisting>
</para> </para>

View file

@ -20,7 +20,7 @@
}; };
<link linkend="opt-services.jitsi-videobridge.openFirewall">services.jitsi-videobridge.openFirewall</link> = true; <link linkend="opt-services.jitsi-videobridge.openFirewall">services.jitsi-videobridge.openFirewall</link> = true;
<link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ]; <link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ];
<link linkend="opt-security.acme.email">security.acme.email</link> = "me@example.com"; <link linkend="opt-security.acme.defaults.email">security.acme.email</link> = "me@example.com";
<link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true; <link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true;
}</programlisting> }</programlisting>
</para> </para>
@ -46,7 +46,7 @@
}; };
<link linkend="opt-services.jitsi-videobridge.openFirewall">services.jitsi-videobridge.openFirewall</link> = true; <link linkend="opt-services.jitsi-videobridge.openFirewall">services.jitsi-videobridge.openFirewall</link> = true;
<link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ]; <link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ];
<link linkend="opt-security.acme.email">security.acme.email</link> = "me@example.com"; <link linkend="opt-security.acme.defaults.email">security.acme.email</link> = "me@example.com";
<link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true; <link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true;
}</programlisting> }</programlisting>
</para> </para>

View file

@ -154,7 +154,7 @@ let
sslServerKey = if useACME then "${sslCertDir}/key.pem" else hostOpts.sslServerKey; sslServerKey = if useACME then "${sslCertDir}/key.pem" else hostOpts.sslServerKey;
sslServerChain = if useACME then "${sslCertDir}/chain.pem" else hostOpts.sslServerChain; sslServerChain = if useACME then "${sslCertDir}/chain.pem" else hostOpts.sslServerChain;
acmeChallenge = optionalString useACME '' acmeChallenge = optionalString (useACME && hostOpts.acmeRoot != null) ''
Alias /.well-known/acme-challenge/ "${hostOpts.acmeRoot}/.well-known/acme-challenge/" Alias /.well-known/acme-challenge/ "${hostOpts.acmeRoot}/.well-known/acme-challenge/"
<Directory "${hostOpts.acmeRoot}"> <Directory "${hostOpts.acmeRoot}">
AllowOverride None AllowOverride None
@ -677,9 +677,16 @@ in
}; };
security.acme.certs = let security.acme.certs = let
acmePairs = map (hostOpts: nameValuePair hostOpts.hostName { acmePairs = map (hostOpts: let
hasRoot = hostOpts.acmeRoot != null;
in nameValuePair hostOpts.hostName {
group = mkDefault cfg.group; group = mkDefault cfg.group;
webroot = hostOpts.acmeRoot; # if acmeRoot is null inherit config.security.acme
# Since config.security.acme.certs.<cert>.webroot's own default value
# should take precedence set priority higher than mkOptionDefault
webroot = mkOverride (if hasRoot then 1000 else 2000) hostOpts.acmeRoot;
# Also nudge dnsProvider to null in case it is inherited
dnsProvider = mkOverride (if hasRoot then 1000 else 2000) null;
extraDomainNames = hostOpts.serverAliases; extraDomainNames = hostOpts.serverAliases;
# Use the vhost-specific email address if provided, otherwise let # Use the vhost-specific email address if provided, otherwise let
# security.acme.email or security.acme.certs.<cert>.email be used. # security.acme.email or security.acme.certs.<cert>.email be used.

View file

@ -128,9 +128,12 @@ in
}; };
acmeRoot = mkOption { acmeRoot = mkOption {
type = types.str; type = types.nullOr types.str;
default = "/var/lib/acme/acme-challenge"; default = "/var/lib/acme/acme-challenge";
description = "Directory for the acme challenge which is PUBLIC, don't put certs or keys in here"; description = ''
Directory for the acme challenge which is PUBLIC, don't put certs or keys in here.
Set to null to inherit from config.security.acme.
'';
}; };
sslServerCert = mkOption { sslServerCert = mkOption {

View file

@ -278,7 +278,7 @@ let
acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) '' acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) ''
location /.well-known/acme-challenge { location /.well-known/acme-challenge {
${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"} ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"}
root ${vhost.acmeRoot}; ${optionalString (vhost.acmeRoot != null) "root ${vhost.acmeRoot};"}
auth_basic off; auth_basic off;
} }
${optionalString (vhost.acmeFallbackHost != null) '' ${optionalString (vhost.acmeFallbackHost != null) ''
@ -948,9 +948,16 @@ in
}; };
security.acme.certs = let security.acme.certs = let
acmePairs = map (vhostConfig: nameValuePair vhostConfig.serverName { acmePairs = map (vhostConfig: let
hasRoot = vhostConfig.acmeRoot != null;
in nameValuePair vhostConfig.serverName {
group = mkDefault cfg.group; group = mkDefault cfg.group;
webroot = vhostConfig.acmeRoot; # if acmeRoot is null inherit config.security.acme
# Since config.security.acme.certs.<cert>.webroot's own default value
# should take precedence set priority higher than mkOptionDefault
webroot = mkOverride (if hasRoot then 1000 else 2000) vhostConfig.acmeRoot;
# Also nudge dnsProvider to null in case it is inherited
dnsProvider = mkOverride (if hasRoot then 1000 else 2000) null;
extraDomainNames = vhostConfig.serverAliases; extraDomainNames = vhostConfig.serverAliases;
# Filter for enableACME-only vhosts. Don't want to create dud certs # Filter for enableACME-only vhosts. Don't want to create dud certs
}) (filter (vhostConfig: vhostConfig.useACMEHost == null) acmeEnabledVhosts); }) (filter (vhostConfig: vhostConfig.useACMEHost == null) acmeEnabledVhosts);

View file

@ -3,7 +3,7 @@
# has additional options that affect the web server as a whole, like # has additional options that affect the web server as a whole, like
# the user/group to run under.) # the user/group to run under.)
{ lib, ... }: { config, lib, ... }:
with lib; with lib;
{ {
@ -85,9 +85,12 @@ with lib;
}; };
acmeRoot = mkOption { acmeRoot = mkOption {
type = types.str; type = types.nullOr types.str;
default = "/var/lib/acme/acme-challenge"; default = "/var/lib/acme/acme-challenge";
description = "Directory for the acme challenge which is PUBLIC, don't put certs or keys in here"; description = ''
Directory for the acme challenge which is PUBLIC, don't put certs or keys in here.
Set to null to inherit from config.security.acme.
'';
}; };
acmeFallbackHost = mkOption { acmeFallbackHost = mkOption {

View file

@ -263,15 +263,6 @@ if test -n "$debug1devices"; then fail; fi
@postDeviceCommands@ @postDeviceCommands@
# Return true if the machine is on AC power, or if we can't determine
# whether it's on AC power.
onACPower() {
! test -d "/proc/acpi/battery" ||
! ls /proc/acpi/battery/BAT[0-9]* > /dev/null 2>&1 ||
! cat /proc/acpi/battery/BAT*/state | grep "^charging state" | grep -q "discharg"
}
# Check the specified file system, if appropriate. # Check the specified file system, if appropriate.
checkFS() { checkFS() {
local device="$1" local device="$1"
@ -316,13 +307,6 @@ checkFS() {
return 0 return 0
fi fi
# Don't run `fsck' if the machine is on battery power. !!! Is
# this a good idea?
if ! onACPower; then
echo "on battery power, so no \`fsck' will be performed on \`$device'"
return 0
fi
echo "checking $device..." echo "checking $device..."
fsckFlags= fsckFlags=

View file

@ -0,0 +1,98 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.virtualisation.docker.rootless;
proxy_env = config.networking.proxy.envVars;
settingsFormat = pkgs.formats.json {};
daemonSettingsFile = settingsFormat.generate "daemon.json" cfg.daemon.settings;
in
{
###### interface
options.virtualisation.docker.rootless = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
This option enables docker in a rootless mode, a daemon that manages
linux containers. To interact with the daemon, one needs to set
<command>DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock</command>.
'';
};
setSocketVariable = mkOption {
type = types.bool;
default = false;
description = ''
Point <command>DOCKER_HOST</command> to rootless Docker instance for
normal users by default.
'';
};
daemon.settings = mkOption {
type = settingsFormat.type;
default = { };
example = {
ipv6 = true;
"fixed-cidr-v6" = "fd00::/80";
};
description = ''
Configuration for docker daemon. The attributes are serialized to JSON used as daemon.conf.
See https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
'';
};
package = mkOption {
default = pkgs.docker;
defaultText = literalExpression "pkgs.docker";
type = types.package;
example = literalExpression "pkgs.docker-edge";
description = ''
Docker package to be used in the module.
'';
};
};
###### implementation
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
environment.extraInit = optionalString cfg.setSocketVariable ''
if [ -z "$DOCKER_HOST" -a -n "$XDG_RUNTIME_DIR" ]; then
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock"
fi
'';
# Taken from https://github.com/moby/moby/blob/master/contrib/dockerd-rootless-setuptool.sh
systemd.user.services.docker = {
wantedBy = [ "default.target" ];
description = "Docker Application Container Engine (Rootless)";
# needs newuidmap from pkgs.shadow
path = [ "/run/wrappers" ];
environment = proxy_env;
unitConfig.StartLimitInterval = "60s";
serviceConfig = {
Type = "notify";
ExecStart = "${cfg.package}/bin/dockerd-rootless --config-file=${daemonSettingsFile}";
ExecReload = "${pkgs.procps}/bin/kill -s HUP $MAINPID";
TimeoutSec = 0;
RestartSec = 2;
Restart = "always";
StartLimitBurst = 3;
LimitNOFILE = "infinity";
LimitNPROC = "infinity";
LimitCORE = "infinity";
Delegate = true;
NotifyAccess = "all";
KillMode = "mixed";
};
};
};
}

View file

@ -1,9 +1,9 @@
let import ./make-test-python.nix ({ pkgs, lib, ... }: let
commonConfig = ./common/acme/client; commonConfig = ./common/acme/client;
dnsServerIP = nodes: nodes.dnsserver.config.networking.primaryIPAddress; dnsServerIP = nodes: nodes.dnsserver.config.networking.primaryIPAddress;
dnsScript = {pkgs, nodes}: let dnsScript = nodes: let
dnsAddress = dnsServerIP nodes; dnsAddress = dnsServerIP nodes;
in pkgs.writeShellScript "dns-hook.sh" '' in pkgs.writeShellScript "dns-hook.sh" ''
set -euo pipefail set -euo pipefail
@ -15,30 +15,137 @@ let
fi fi
''; '';
documentRoot = pkgs: pkgs.runCommand "docroot" {} '' dnsConfig = nodes: {
dnsProvider = "exec";
dnsPropagationCheck = false;
credentialsFile = pkgs.writeText "wildcard.env" ''
EXEC_PATH=${dnsScript nodes}
EXEC_POLLING_INTERVAL=1
EXEC_PROPAGATION_TIMEOUT=1
EXEC_SEQUENCE_INTERVAL=1
'';
};
documentRoot = pkgs.runCommand "docroot" {} ''
mkdir -p "$out" mkdir -p "$out"
echo hello world > "$out/index.html" echo hello world > "$out/index.html"
''; '';
vhostBase = pkgs: { vhostBase = {
forceSSL = true; forceSSL = true;
locations."/".root = documentRoot pkgs; locations."/".root = documentRoot;
}; };
in import ./make-test-python.nix ({ lib, ... }: { vhostBaseHttpd = {
forceSSL = true;
inherit documentRoot;
};
# Base specialisation config for testing general ACME features
webserverBasicConfig = {
services.nginx.enable = true;
services.nginx.virtualHosts."a.example.test" = vhostBase // {
enableACME = true;
};
};
# Generate specialisations for testing a web server
mkServerConfigs = { server, group, vhostBaseData, extraConfig ? {} }: let
baseConfig = { nodes, config, specialConfig ? {} }: lib.mkMerge [
{
security.acme = {
defaults = (dnsConfig nodes) // {
inherit group;
};
# One manual wildcard cert
certs."example.test" = {
domain = "*.example.test";
};
};
services."${server}" = {
enable = true;
virtualHosts = {
# Run-of-the-mill vhost using HTTP-01 validation
"${server}-http.example.test" = vhostBaseData // {
serverAliases = [ "${server}-http-alias.example.test" ];
enableACME = true;
};
# Another which inherits the DNS-01 config
"${server}-dns.example.test" = vhostBaseData // {
serverAliases = [ "${server}-dns-alias.example.test" ];
enableACME = true;
# Set acmeRoot to null instead of using the default of "/var/lib/acme/acme-challenge"
# webroot + dnsProvider are mutually exclusive.
acmeRoot = null;
};
# One using the wildcard certificate
"${server}-wildcard.example.test" = vhostBaseData // {
serverAliases = [ "${server}-wildcard-alias.example.test" ];
useACMEHost = "example.test";
};
};
};
# Used to determine if service reload was triggered
systemd.targets."test-renew-${server}" = {
wants = [ "acme-${server}-http.example.test.service" ];
after = [ "acme-${server}-http.example.test.service" "${server}-config-reload.service" ];
};
}
specialConfig
extraConfig
];
in {
"${server}".configuration = { nodes, config, ... }: baseConfig {
inherit nodes config;
};
# Test that server reloads when an alias is removed (and subsequently test removal works in acme)
"${server}-remove-alias".configuration = { nodes, config, ... }: baseConfig {
inherit nodes config;
specialConfig = {
# Remove an alias, but create a standalone vhost in its place for testing.
# This configuration results in certificate errors as useACMEHost does not imply
# append extraDomains, and thus we can validate the SAN is removed.
services."${server}" = {
virtualHosts."${server}-http.example.test".serverAliases = lib.mkForce [];
virtualHosts."${server}-http-alias.example.test" = vhostBaseData // {
useACMEHost = "${server}-http.example.test";
};
};
};
};
# Test that the server reloads when only the acme configuration is changed.
"${server}-change-acme-conf".configuration = { nodes, config, ... }: baseConfig {
inherit nodes config;
specialConfig = {
security.acme.certs."${server}-http.example.test" = {
keyType = "ec384";
# Also test that postRun is exec'd as root
postRun = "id | grep root";
};
};
};
};
in {
name = "acme"; name = "acme";
meta.maintainers = lib.teams.acme.members; meta.maintainers = lib.teams.acme.members;
nodes = { nodes = {
# The fake ACME server which will respond to client requests # The fake ACME server which will respond to client requests
acme = { nodes, lib, ... }: { acme = { nodes, ... }: {
imports = [ ./common/acme/server ]; imports = [ ./common/acme/server ];
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ]; networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
}; };
# A fake DNS server which can be configured with records as desired # A fake DNS server which can be configured with records as desired
# Used to test DNS-01 challenge # Used to test DNS-01 challenge
dnsserver = { nodes, pkgs, ... }: { dnsserver = { nodes, ... }: {
networking.firewall.allowedTCPPorts = [ 8055 53 ]; networking.firewall.allowedTCPPorts = [ 8055 53 ];
networking.firewall.allowedUDPPorts = [ 53 ]; networking.firewall.allowedUDPPorts = [ 53 ];
systemd.services.pebble-challtestsrv = { systemd.services.pebble-challtestsrv = {
@ -54,7 +161,7 @@ in import ./make-test-python.nix ({ lib, ... }: {
}; };
# A web server which will be the node requesting certs # A web server which will be the node requesting certs
webserver = { pkgs, nodes, lib, config, ... }: { webserver = { nodes, config, ... }: {
imports = [ commonConfig ]; imports = [ commonConfig ];
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ]; networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall.allowedTCPPorts = [ 80 443 ];
@ -63,113 +170,53 @@ in import ./make-test-python.nix ({ lib, ... }: {
environment.systemPackages = [ pkgs.openssl ]; environment.systemPackages = [ pkgs.openssl ];
# Set log level to info so that we can see when the service is reloaded # Set log level to info so that we can see when the service is reloaded
services.nginx.enable = true;
services.nginx.logError = "stderr info"; services.nginx.logError = "stderr info";
# First tests configure a basic cert and run a bunch of openssl checks specialisation = {
services.nginx.virtualHosts."a.example.test" = (vhostBase pkgs) // { # First derivation used to test general ACME features
enableACME = true; general.configuration = { ... }: let
};
# Used to determine if service reload was triggered
systemd.targets.test-renew-nginx = {
wants = [ "acme-a.example.test.service" ];
after = [ "acme-a.example.test.service" "nginx-config-reload.service" ];
};
# Test that account creation is collated into one service
specialisation.account-creation.configuration = { nodes, pkgs, lib, ... }: let
email = "newhostmaster@example.test";
caDomain = nodes.acme.config.test-support.acme.caDomain; caDomain = nodes.acme.config.test-support.acme.caDomain;
email = config.security.acme.defaults.email;
# Exit 99 to make it easier to track if this is the reason a renew failed # Exit 99 to make it easier to track if this is the reason a renew failed
testScript = '' accountCreateTester = ''
test -e accounts/${caDomain}/${email}/account.json || exit 99 test -e accounts/${caDomain}/${email}/account.json || exit 99
''; '';
in { in lib.mkMerge [
security.acme.email = lib.mkForce email; webserverBasicConfig
systemd.services."b.example.test".preStart = testScript; {
systemd.services."c.example.test".preStart = testScript; # Used to test that account creation is collated into one service.
# These should not run until after acme-finished-a.example.test.target
systemd.services."b.example.test".preStart = accountCreateTester;
systemd.services."c.example.test".preStart = accountCreateTester;
services.nginx.virtualHosts."b.example.test" = (vhostBase pkgs) // { services.nginx.virtualHosts."b.example.test" = vhostBase // {
enableACME = true; enableACME = true;
}; };
services.nginx.virtualHosts."c.example.test" = (vhostBase pkgs) // { services.nginx.virtualHosts."c.example.test" = vhostBase // {
enableACME = true; enableACME = true;
}; };
}; }
];
# Cert config changes will not cause the nginx configuration to change.
# This tests that the reload service is correctly triggered.
# It also tests that postRun is exec'd as root
specialisation.cert-change.configuration = { pkgs, ... }: {
security.acme.certs."a.example.test".keyType = "ec384";
security.acme.certs."a.example.test".postRun = ''
set -euo pipefail
touch /home/test
chown root:root /home/test
echo testing > /home/test
'';
};
# Now adding an alias to ensure that the certs are updated
specialisation.nginx-aliases.configuration = { pkgs, ... }: {
services.nginx.virtualHosts."a.example.test" = {
serverAliases = [ "b.example.test" ];
};
};
# Test OCSP Stapling # Test OCSP Stapling
specialisation.ocsp-stapling.configuration = { pkgs, ... }: { ocsp-stapling.configuration = { ... }: lib.mkMerge [
security.acme.certs."a.example.test" = { webserverBasicConfig
ocspMustStaple = true; {
}; security.acme.certs."a.example.test".ocspMustStaple = true;
services.nginx.virtualHosts."a.example.com" = { services.nginx.virtualHosts."a.example.test" = {
extraConfig = '' extraConfig = ''
ssl_stapling on; ssl_stapling on;
ssl_stapling_verify on; ssl_stapling_verify on;
''; '';
}; };
}; }
];
# Test using Apache HTTPD
specialisation.httpd-aliases.configuration = { pkgs, config, lib, ... }: {
services.nginx.enable = lib.mkForce false;
services.httpd.enable = true;
services.httpd.adminAddr = config.security.acme.email;
services.httpd.virtualHosts."c.example.test" = {
serverAliases = [ "d.example.test" ];
forceSSL = true;
enableACME = true;
documentRoot = documentRoot pkgs;
};
# Used to determine if service reload was triggered
systemd.targets.test-renew-httpd = {
wants = [ "acme-c.example.test.service" ];
after = [ "acme-c.example.test.service" "httpd-config-reload.service" ];
};
};
# Validation via DNS-01 challenge
specialisation.dns-01.configuration = { pkgs, config, nodes, ... }: {
security.acme.certs."example.test" = {
domain = "*.example.test";
group = config.services.nginx.group;
dnsProvider = "exec";
dnsPropagationCheck = false;
credentialsFile = pkgs.writeText "wildcard.env" ''
EXEC_PATH=${dnsScript { inherit pkgs nodes; }}
'';
};
services.nginx.virtualHosts."dns.example.test" = (vhostBase pkgs) // {
useACMEHost = "example.test";
};
};
# Validate service relationships by adding a slow start service to nginx' wants. # Validate service relationships by adding a slow start service to nginx' wants.
# Reproducer for https://github.com/NixOS/nixpkgs/issues/81842 # Reproducer for https://github.com/NixOS/nixpkgs/issues/81842
specialisation.slow-startup.configuration = { pkgs, config, nodes, lib, ... }: { slow-startup.configuration = { ... }: lib.mkMerge [
webserverBasicConfig
{
systemd.services.my-slow-service = { systemd.services.my-slow-service = {
wantedBy = [ "multi-user.target" "nginx.service" ]; wantedBy = [ "multi-user.target" "nginx.service" ];
before = [ "nginx.service" ]; before = [ "nginx.service" ];
@ -177,16 +224,88 @@ in import ./make-test-python.nix ({ lib, ... }: {
script = "${pkgs.python3}/bin/python -m http.server"; script = "${pkgs.python3}/bin/python -m http.server";
}; };
services.nginx.virtualHosts."slow.example.com" = { services.nginx.virtualHosts."slow.example.test" = {
forceSSL = true; forceSSL = true;
enableACME = true; enableACME = true;
locations."/".proxyPass = "http://localhost:8000"; locations."/".proxyPass = "http://localhost:8000";
}; };
}
];
# Test lego internal server (listenHTTP option)
# Also tests useRoot option
lego-server.configuration = { ... }: {
security.acme.useRoot = true;
security.acme.certs."lego.example.test" = {
listenHTTP = ":80";
group = "nginx";
};
services.nginx.enable = true;
services.nginx.virtualHosts."lego.example.test" = {
useACMEHost = "lego.example.test";
onlySSL = true;
}; };
}; };
# Test compatiblity with Caddy
# It only supports useACMEHost, hence not using mkServerConfigs
} // (let
baseCaddyConfig = { nodes, config, ... }: {
security.acme = {
defaults = (dnsConfig nodes) // {
group = config.services.caddy.group;
};
# One manual wildcard cert
certs."example.test" = {
domain = "*.example.test";
};
};
services.caddy = {
enable = true;
virtualHosts."a.exmaple.test" = {
useACMEHost = "example.test";
extraConfig = ''
root * ${documentRoot}
'';
};
};
};
in {
caddy.configuration = baseCaddyConfig;
# Test that the server reloads when only the acme configuration is changed.
"caddy-change-acme-conf".configuration = { nodes, config, ... }: lib.mkMerge [
(baseCaddyConfig {
inherit nodes config;
})
{
security.acme.certs."example.test" = {
keyType = "ec384";
};
}
];
# Test compatibility with Nginx
}) // (mkServerConfigs {
server = "nginx";
group = "nginx";
vhostBaseData = vhostBase;
})
# Test compatibility with Apache HTTPD
// (mkServerConfigs {
server = "httpd";
group = "wwwrun";
vhostBaseData = vhostBaseHttpd;
extraConfig = {
services.httpd.adminAddr = config.security.acme.defaults.email;
};
});
};
# The client will be used to curl the webserver to validate configuration # The client will be used to curl the webserver to validate configuration
client = {nodes, lib, pkgs, ...}: { client = { nodes, ... }: {
imports = [ commonConfig ]; imports = [ commonConfig ];
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ]; networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
@ -204,23 +323,26 @@ in import ./make-test-python.nix ({ lib, ... }: {
# Note, wait_for_unit does not work for oneshot services that do not have RemainAfterExit=true, # Note, wait_for_unit does not work for oneshot services that do not have RemainAfterExit=true,
# this is because a oneshot goes from inactive => activating => inactive, and never # this is because a oneshot goes from inactive => activating => inactive, and never
# reaches the active state. Targets do not have this issue. # reaches the active state. Targets do not have this issue.
'' ''
import time import time
has_switched = False
def switch_to(node, name): def switch_to(node, name):
global has_switched # On first switch, this will create a symlink to the current system so that we can
if has_switched: # quickly switch between derivations
node.succeed( root_specs = "/tmp/specialisation"
"${switchToNewServer}" node.execute(
f"test -e {root_specs}"
f" || ln -s $(readlink /run/current-system)/specialisation {root_specs}"
) )
has_switched = True
switcher_path = f"/run/current-system/specialisation/{name}/bin/switch-to-configuration"
rc, _ = node.execute(f"test -e '{switcher_path}'")
if rc > 0:
switcher_path = f"/tmp/specialisation/{name}/bin/switch-to-configuration"
node.succeed( node.succeed(
f"/run/current-system/specialisation/{name}/bin/switch-to-configuration test" f"{switcher_path} test"
) )
@ -310,8 +432,7 @@ in import ./make-test-python.nix ({ lib, ... }: {
return download_ca_certs(node, retries - 1) return download_ca_certs(node, retries - 1)
client.start() start_all()
dnsserver.start()
dnsserver.wait_for_unit("pebble-challtestsrv.service") dnsserver.wait_for_unit("pebble-challtestsrv.service")
client.wait_for_unit("default.target") client.wait_for_unit("default.target")
@ -320,19 +441,30 @@ in import ./make-test-python.nix ({ lib, ... }: {
'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a' 'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a'
) )
acme.start()
webserver.start()
acme.wait_for_unit("network-online.target") acme.wait_for_unit("network-online.target")
acme.wait_for_unit("pebble.service") acme.wait_for_unit("pebble.service")
download_ca_certs(client) download_ca_certs(client)
with subtest("Can request certificate with HTTPS-01 challenge"): # Perform general tests first
switch_to(webserver, "general")
with subtest("Can request certificate with HTTP-01 challenge"):
webserver.wait_for_unit("acme-finished-a.example.test.target") webserver.wait_for_unit("acme-finished-a.example.test.target")
check_fullchain(webserver, "a.example.test")
check_issuer(webserver, "a.example.test", "pebble")
webserver.wait_for_unit("nginx.service")
check_connection(client, "a.example.test")
with subtest("Runs 1 cert for account creation before others"):
webserver.wait_for_unit("acme-finished-b.example.test.target")
webserver.wait_for_unit("acme-finished-c.example.test.target")
check_connection(client, "b.example.test")
check_connection(client, "c.example.test")
with subtest("Certificates and accounts have safe + valid permissions"): with subtest("Certificates and accounts have safe + valid permissions"):
group = "${nodes.webserver.config.security.acme.certs."a.example.test".group}" # Nginx will set the group appropriately when enableACME is used
group = "nginx"
webserver.succeed( webserver.succeed(
f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test/*.pem | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5" f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test/*.pem | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5"
) )
@ -346,12 +478,6 @@ in import ./make-test-python.nix ({ lib, ... }: {
f"test $(find /var/lib/acme/accounts -type f -exec stat -L -c '%a %U %G' {{}} \\; | tee /dev/stderr | grep -v '600 acme {group}' | wc -l) -eq 0" f"test $(find /var/lib/acme/accounts -type f -exec stat -L -c '%a %U %G' {{}} \\; | tee /dev/stderr | grep -v '600 acme {group}' | wc -l) -eq 0"
) )
with subtest("Certs are accepted by web server"):
webserver.succeed("systemctl start nginx.service")
check_fullchain(webserver, "a.example.test")
check_issuer(webserver, "a.example.test", "pebble")
check_connection(client, "a.example.test")
# Selfsigned certs tests happen late so we aren't fighting the system init triggering cert renewal # Selfsigned certs tests happen late so we aren't fighting the system init triggering cert renewal
with subtest("Can generate valid selfsigned certs"): with subtest("Can generate valid selfsigned certs"):
webserver.succeed("systemctl clean acme-a.example.test.service --what=state") webserver.succeed("systemctl clean acme-a.example.test.service --what=state")
@ -365,77 +491,107 @@ in import ./make-test-python.nix ({ lib, ... }: {
# Will succeed if nginx can load the certs # Will succeed if nginx can load the certs
webserver.succeed("systemctl start nginx-config-reload.service") webserver.succeed("systemctl start nginx-config-reload.service")
with subtest("Can reload nginx when timer triggers renewal"):
webserver.succeed("systemctl start test-renew-nginx.target")
check_issuer(webserver, "a.example.test", "pebble")
check_connection(client, "a.example.test")
with subtest("Runs 1 cert for account creation before others"):
switch_to(webserver, "account-creation")
webserver.wait_for_unit("acme-finished-a.example.test.target")
check_connection(client, "a.example.test")
webserver.wait_for_unit("acme-finished-b.example.test.target")
webserver.wait_for_unit("acme-finished-c.example.test.target")
check_connection(client, "b.example.test")
check_connection(client, "c.example.test")
with subtest("Can reload web server when cert configuration changes"):
switch_to(webserver, "cert-change")
webserver.wait_for_unit("acme-finished-a.example.test.target")
check_connection_key_bits(client, "a.example.test", "384")
webserver.succeed("grep testing /home/test")
# Clean to remove the testing file (and anything else messy we did)
webserver.succeed("systemctl clean acme-a.example.test.service --what=state")
with subtest("Correctly implements OCSP stapling"): with subtest("Correctly implements OCSP stapling"):
switch_to(webserver, "ocsp-stapling") switch_to(webserver, "ocsp-stapling")
webserver.wait_for_unit("acme-finished-a.example.test.target") webserver.wait_for_unit("acme-finished-a.example.test.target")
check_stapling(client, "a.example.test") check_stapling(client, "a.example.test")
with subtest("Can request certificate with HTTPS-01 when nginx startup is delayed"): with subtest("Can request certificate with HTTP-01 using lego's internal web server"):
switch_to(webserver, "slow-startup") switch_to(webserver, "lego-server")
webserver.wait_for_unit("acme-finished-slow.example.com.target") webserver.wait_for_unit("acme-finished-lego.example.test.target")
check_issuer(webserver, "slow.example.com", "pebble") webserver.wait_for_unit("nginx.service")
check_connection(client, "slow.example.com") webserver.succeed("echo HENLO && systemctl cat nginx.service")
webserver.succeed("test \"$(stat -c '%U' /var/lib/acme/* | uniq)\" = \"root\"")
with subtest("Can request certificate for vhost + aliases (nginx)"):
# Check the key hash before and after adding an alias. It should not change.
# The previous test reverts the ed384 change
webserver.wait_for_unit("acme-finished-a.example.test.target")
switch_to(webserver, "nginx-aliases")
webserver.wait_for_unit("acme-finished-a.example.test.target")
check_issuer(webserver, "a.example.test", "pebble")
check_connection(client, "a.example.test") check_connection(client, "a.example.test")
check_connection(client, "b.example.test") check_connection(client, "lego.example.test")
with subtest("Can request certificates for vhost + aliases (apache-httpd)"): with subtest("Can request certificate with HTTP-01 when nginx startup is delayed"):
webserver.execute("systemctl stop nginx")
switch_to(webserver, "slow-startup")
webserver.wait_for_unit("acme-finished-slow.example.test.target")
check_issuer(webserver, "slow.example.test", "pebble")
webserver.wait_for_unit("nginx.service")
check_connection(client, "slow.example.test")
with subtest("Works with caddy"):
switch_to(webserver, "caddy")
webserver.wait_for_unit("acme-finished-example.test.target")
webserver.wait_for_unit("caddy.service")
# FIXME reloading caddy is not sufficient to load new certs.
# Restart it manually until this is fixed.
webserver.succeed("systemctl restart caddy.service")
check_connection(client, "a.example.test")
with subtest("security.acme changes reflect on caddy"):
switch_to(webserver, "caddy-change-acme-conf")
webserver.wait_for_unit("acme-finished-example.test.target")
webserver.wait_for_unit("caddy.service")
# FIXME reloading caddy is not sufficient to load new certs.
# Restart it manually until this is fixed.
webserver.succeed("systemctl restart caddy.service")
check_connection_key_bits(client, "a.example.test", "384")
domains = ["http", "dns", "wildcard"]
for server, logsrc in [
("nginx", "journalctl -n 30 -u nginx.service"),
("httpd", "tail -n 30 /var/log/httpd/*.log"),
]:
wait_for_server = lambda: webserver.wait_for_unit(f"{server}.service")
with subtest(f"Works with {server}"):
try: try:
switch_to(webserver, "httpd-aliases") switch_to(webserver, server)
webserver.wait_for_unit("acme-finished-c.example.test.target") # Skip wildcard domain for this check ([:-1])
for domain in domains[:-1]:
webserver.wait_for_unit(
f"acme-finished-{server}-{domain}.example.test.target"
)
except Exception as err: except Exception as err:
_, output = webserver.execute( _, output = webserver.execute(
"cat /var/log/httpd/*.log && ls -al /var/lib/acme/acme-challenge" f"{logsrc} && ls -al /var/lib/acme/acme-challenge"
) )
print(output) print(output)
raise err raise err
check_issuer(webserver, "c.example.test", "pebble")
check_connection(client, "c.example.test")
check_connection(client, "d.example.test")
with subtest("Can reload httpd when timer triggers renewal"): wait_for_server()
for domain in domains[:-1]:
check_issuer(webserver, f"{server}-{domain}.example.test", "pebble")
for domain in domains:
check_connection(client, f"{server}-{domain}.example.test")
check_connection(client, f"{server}-{domain}-alias.example.test")
test_domain = f"{server}-{domains[0]}.example.test"
with subtest(f"Can reload {server} when timer triggers renewal"):
# Switch to selfsigned first # Switch to selfsigned first
webserver.succeed("systemctl clean acme-c.example.test.service --what=state") webserver.succeed(f"systemctl clean acme-{test_domain}.service --what=state")
webserver.succeed("systemctl start acme-selfsigned-c.example.test.service") webserver.succeed(f"systemctl start acme-selfsigned-{test_domain}.service")
check_issuer(webserver, "c.example.test", "minica") check_issuer(webserver, test_domain, "minica")
webserver.succeed("systemctl start httpd-config-reload.service") webserver.succeed(f"systemctl start {server}-config-reload.service")
webserver.succeed("systemctl start test-renew-httpd.target") webserver.succeed(f"systemctl start test-renew-{server}.target")
check_issuer(webserver, "c.example.test", "pebble") check_issuer(webserver, test_domain, "pebble")
check_connection(client, "c.example.test") check_connection(client, test_domain)
with subtest("Can request wildcard certificates using DNS-01 challenge"): with subtest("Can remove an alias from a domain + cert is updated"):
switch_to(webserver, "dns-01") test_alias = f"{server}-{domains[0]}-alias.example.test"
webserver.wait_for_unit("acme-finished-example.test.target") switch_to(webserver, f"{server}-remove-alias")
check_issuer(webserver, "example.test", "pebble") webserver.wait_for_unit(f"acme-finished-{test_domain}.target")
check_connection(client, "dns.example.test") wait_for_server()
check_connection(client, test_domain)
rc, _ = client.execute(
f"openssl s_client -CAfile /tmp/ca.crt -connect {test_alias}:443"
" </dev/null 2>/dev/null | openssl x509 -noout -text"
f" | grep DNS: | grep {test_alias}"
)
assert rc > 0, "Removed extraDomainName was not removed from the cert"
with subtest("security.acme changes reflect on web server"):
# Switch back to normal server config first, reset everything.
switch_to(webserver, server)
wait_for_server()
switch_to(webserver, f"{server}-change-acme-conf")
webserver.wait_for_unit(f"acme-finished-{test_domain}.target")
wait_for_server()
check_connection_key_bits(client, test_domain, "384")
''; '';
}) })

View file

@ -105,6 +105,7 @@ in
dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {}; dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {};
doas = handleTest ./doas.nix {}; doas = handleTest ./doas.nix {};
docker = handleTestOn ["x86_64-linux"] ./docker.nix {}; docker = handleTestOn ["x86_64-linux"] ./docker.nix {};
docker-rootless = handleTestOn ["x86_64-linux"] ./docker-rootless.nix {};
docker-edge = handleTestOn ["x86_64-linux"] ./docker-edge.nix {}; docker-edge = handleTestOn ["x86_64-linux"] ./docker-edge.nix {};
docker-registry = handleTest ./docker-registry.nix {}; docker-registry = handleTest ./docker-registry.nix {};
docker-tools = handleTestOn ["x86_64-linux"] ./docker-tools.nix {}; docker-tools = handleTestOn ["x86_64-linux"] ./docker-tools.nix {};
@ -397,6 +398,7 @@ in
proxy = handleTest ./proxy.nix {}; proxy = handleTest ./proxy.nix {};
prowlarr = handleTest ./prowlarr.nix {}; prowlarr = handleTest ./prowlarr.nix {};
pt2-clone = handleTest ./pt2-clone.nix {}; pt2-clone = handleTest ./pt2-clone.nix {};
pulseaudio = discoverTests (import ./pulseaudio.nix);
qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {}; qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {};
quorum = handleTest ./quorum.nix {}; quorum = handleTest ./quorum.nix {};
rabbitmq = handleTest ./rabbitmq.nix {}; rabbitmq = handleTest ./rabbitmq.nix {};
@ -435,6 +437,7 @@ in
solanum = handleTest ./solanum.nix {}; solanum = handleTest ./solanum.nix {};
solr = handleTest ./solr.nix {}; solr = handleTest ./solr.nix {};
sonarr = handleTest ./sonarr.nix {}; sonarr = handleTest ./sonarr.nix {};
sourcehut = handleTest ./sourcehut.nix {};
spacecookie = handleTest ./spacecookie.nix {}; spacecookie = handleTest ./spacecookie.nix {};
spark = handleTestOn ["x86_64-linux"] ./spark {}; spark = handleTestOn ["x86_64-linux"] ./spark {};
sslh = handleTest ./sslh.nix {}; sslh = handleTest ./sslh.nix {};

View file

@ -5,9 +5,11 @@ let
in { in {
security.acme = { security.acme = {
acceptTerms = true;
defaults = {
server = "https://${caDomain}/dir"; server = "https://${caDomain}/dir";
email = "hostmaster@example.test"; email = "hostmaster@example.test";
acceptTerms = true; };
}; };
security.pki.certificateFiles = [ caCert ]; security.pki.certificateFiles = [ caCert ];

View file

@ -120,6 +120,11 @@ in {
enable = true; enable = true;
description = "Pebble ACME server"; description = "Pebble ACME server";
wantedBy = [ "network.target" ]; wantedBy = [ "network.target" ];
environment = {
# We're not testing lego, we're just testing our configuration.
# No need to sleep.
PEBBLE_VA_NOSLEEP = "1";
};
serviceConfig = { serviceConfig = {
RuntimeDirectory = "pebble"; RuntimeDirectory = "pebble";

View file

@ -0,0 +1,41 @@
# This test runs docker and checks if simple container starts
import ./make-test-python.nix ({ lib, pkgs, ...} : {
name = "docker-rootless";
meta = with pkgs.lib.maintainers; {
maintainers = [ abbradar ];
};
nodes = {
machine = { pkgs, ... }: {
virtualisation.docker.rootless.enable = true;
users.users.alice = {
uid = 1000;
isNormalUser = true;
};
};
};
testScript = { nodes, ... }:
let
user = nodes.machine.config.users.users.alice;
sudo = lib.concatStringsSep " " [
"XDG_RUNTIME_DIR=/run/user/${toString user.uid}"
"DOCKER_HOST=unix:///run/user/${toString user.uid}/docker.sock"
"sudo" "--preserve-env=XDG_RUNTIME_DIR,DOCKER_HOST" "-u" "alice"
];
in ''
machine.wait_for_unit("multi-user.target")
machine.succeed("loginctl enable-linger alice")
machine.wait_until_succeeds("${sudo} systemctl --user is-active docker.service")
machine.succeed("tar cv --files-from /dev/null | ${sudo} docker import - scratchimg")
machine.succeed(
"${sudo} docker run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10"
)
machine.succeed("${sudo} docker ps | grep sleeping")
machine.succeed("${sudo} docker stop sleeping")
'';
})

View file

@ -17,7 +17,7 @@ let
makeHydraTest = with pkgs.lib; name: package: makeTest { makeHydraTest = with pkgs.lib; name: package: makeTest {
name = "hydra-${name}"; name = "hydra-${name}";
meta = with pkgs.lib.maintainers; { meta = with pkgs.lib.maintainers; {
maintainers = [ pstn lewo ma27 ]; maintainers = [ lewo ma27 ];
}; };
machine = { pkgs, lib, ... }: { machine = { pkgs, lib, ... }: {

View file

@ -51,7 +51,6 @@ let
environment.systemPackages = [ kubectl ]; environment.systemPackages = [ kubectl ];
services.flannel.iface = "eth1"; services.flannel.iface = "eth1";
services.kubernetes = { services.kubernetes = {
addons.dashboard.enable = true;
proxy.hostname = "${masterName}.${domain}"; proxy.hostname = "${masterName}.${domain}";
easyCerts = true; easyCerts = true;

View file

@ -0,0 +1,71 @@
let
mkTest = { systemWide ? false }:
import ./make-test-python.nix ({ pkgs, lib, ... }:
let
testFile = pkgs.fetchurl {
url =
"https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3";
hash = "sha256-+iggJW8s0/LfA/okfXsB550/55Q0Sq3OoIzuBrzOPJQ=";
};
makeTestPlay = key:
{ sox, alsa-utils }:
pkgs.writeScriptBin key ''
set -euxo pipefail
${sox}/bin/play ${testFile}
${sox}/bin/sox ${testFile} -t wav - | ${alsa-utils}/bin/aplay
touch /tmp/${key}_success
'';
testers = builtins.mapAttrs makeTestPlay {
testPlay = { inherit (pkgs) sox alsa-utils; };
testPlay32 = { inherit (pkgs.pkgsi686Linux) sox alsa-utils; };
};
in {
name = "pulseaudio${lib.optionalString systemWide "-systemWide"}";
meta = with pkgs.lib.maintainers; {
maintainers = [ synthetica ] ++ pkgs.pulseaudio.meta.maintainers;
};
machine = { ... }:
{
imports = [ ./common/wayland-cage.nix ];
hardware.pulseaudio = {
enable = true;
support32Bit = true;
inherit systemWide;
};
environment.systemPackages = [ testers.testPlay pkgs.pavucontrol ]
++ lib.optional pkgs.stdenv.isx86_64 testers.testPlay32;
} // lib.optionalAttrs systemWide {
users.users.alice.extraGroups = [ "audio" ];
systemd.services.pulseaudio.wantedBy = [ "multi-user.target" ];
};
enableOCR = true;
testScript = { ... }: ''
machine.wait_until_succeeds("pgrep xterm")
machine.wait_for_text("alice@machine")
machine.send_chars("testPlay \n")
machine.wait_for_file("/tmp/testPlay_success")
${lib.optionalString pkgs.stdenv.isx86_64 ''
machine.send_chars("testPlay32 \n")
machine.wait_for_file("/tmp/testPlay32_success")
''}
machine.screenshot("testPlay")
# Pavucontrol only loads when Pulseaudio is running. If it isn't, the
# text "Playback" (one of the tabs) will never show.
machine.send_chars("pavucontrol\n")
machine.wait_for_text("Playback")
machine.screenshot("Pavucontrol")
'';
});
in builtins.mapAttrs (key: val: mkTest val) {
user = { systemWide = false; };
system = { systemWide = true; };
}

View file

@ -1,29 +1,197 @@
import ./make-test-python.nix ({ pkgs, ... }: import ./make-test-python.nix ({ pkgs, lib, ... }:
let
domain = "sourcehut.localdomain";
# Note that wildcard certificates just under the TLD (eg. *.com)
# would be rejected by clients like curl.
tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \
-subj '/CN=${domain}' -extensions v3_req \
-addext 'subjectAltName = DNS:*.${domain}'
install -D -t $out key.pem cert.pem
'';
images = {
nixos.unstable.x86_64 =
let
systemConfig = { pkgs, ... }: {
# passwordless ssh server
services.openssh = {
enable = true;
permitRootLogin = "yes";
extraConfig = "PermitEmptyPasswords yes";
};
users = {
mutableUsers = false;
# build user
extraUsers."build" = {
isNormalUser = true;
uid = 1000;
extraGroups = [ "wheel" ];
password = "";
};
users.root.password = "";
};
security.sudo.wheelNeedsPassword = false;
nix.trustedUsers = [ "root" "build" ];
documentation.nixos.enable = false;
# builds.sr.ht-image-specific network settings
networking = {
hostName = "build";
dhcpcd.enable = false;
defaultGateway.address = "10.0.2.2";
usePredictableInterfaceNames = false;
interfaces."eth0".ipv4.addresses = [{
address = "10.0.2.15";
prefixLength = 25;
}];
enableIPv6 = false;
nameservers = [
# OpenNIC anycast
"185.121.177.177"
"169.239.202.202"
# Google
"8.8.8.8"
];
firewall.allowedTCPPorts = [ 22 ];
};
environment.systemPackages = [
pkgs.gitMinimal
#pkgs.mercurial
pkgs.curl
pkgs.gnupg
];
};
qemuConfig = { pkgs, ... }: {
imports = [ systemConfig ];
fileSystems."/".device = "/dev/disk/by-label/nixos";
boot.initrd.availableKernelModules = [
"ahci"
"ehci_pci"
"sd_mod"
"usb_storage"
"usbhid"
"virtio_balloon"
"virtio_blk"
"virtio_pci"
"virtio_ring"
"xhci_pci"
];
boot.loader = {
grub = {
version = 2;
device = "/dev/vda";
};
timeout = 0;
};
};
config = (import (pkgs.path + "/nixos/lib/eval-config.nix") {
inherit pkgs; modules = [ qemuConfig ];
system = "x86_64-linux";
}).config;
in
import (pkgs.path + "/nixos/lib/make-disk-image.nix") {
inherit pkgs lib config;
diskSize = 16000;
format = "qcow2-compressed";
contents = [
{ source = pkgs.writeText "gitconfig" ''
[user]
name = builds.sr.ht
email = build@sr.ht
'';
target = "/home/build/.gitconfig";
user = "build";
group = "users";
mode = "644";
}
];
};
};
in
{ {
name = "sourcehut"; name = "sourcehut";
meta.maintainers = [ pkgs.lib.maintainers.tomberek ]; meta.maintainers = [ pkgs.lib.maintainers.tomberek ];
machine = { config, pkgs, ... }: { machine = { config, pkgs, nodes, ... }: {
virtualisation.memorySize = 2048; # buildsrht needs space
networking.firewall.allowedTCPPorts = [ 80 ]; virtualisation.diskSize = 4 * 1024;
virtualisation.memorySize = 2 * 1024;
networking.domain = domain;
networking.extraHosts = ''
${config.networking.primaryIPAddress} meta.${domain}
${config.networking.primaryIPAddress} builds.${domain}
'';
services.sourcehut = { services.sourcehut = {
enable = true; enable = true;
services = [ "meta" ]; services = [ "meta" "builds" ];
originBase = "sourcehut"; nginx.enable = true;
settings."sr.ht".service-key = "8888888888888888888888888888888888888888888888888888888888888888"; nginx.virtualHost = {
settings."sr.ht".network-key = "0000000000000000000000000000000000000000000="; forceSSL = true;
settings.webhooks.private-key = "0000000000000000000000000000000000000000000="; sslCertificate = "${tls-cert}/cert.pem";
sslCertificateKey = "${tls-cert}/key.pem";
};
postgresql.enable = true;
redis.enable = true;
meta.enable = true;
builds = {
enable = true;
# FIXME: see why it does not seem to activate fully.
#enableWorker = true;
inherit images;
};
settings."sr.ht" = {
global-domain = config.networking.domain;
service-key = pkgs.writeText "service-key" "8b327279b77e32a3620e2fc9aabce491cc46e7d821fd6713b2a2e650ce114d01";
network-key = pkgs.writeText "network-key" "cEEmc30BRBGkgQZcHFksiG7hjc6_dK1XR2Oo5Jb9_nQ=";
};
settings."builds.sr.ht" = {
oauth-client-secret = pkgs.writeText "buildsrht-oauth-client-secret" "2260e9c4d9b8dcedcef642860e0504bc";
oauth-client-id = "299db9f9c2013170";
};
settings.webhooks.private-key = pkgs.writeText "webhook-key" "Ra3IjxgFiwG9jxgp4WALQIZw/BMYt30xWiOsqD0J7EA=";
};
networking.firewall.allowedTCPPorts = [ 443 ];
security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedTlsSettings = true;
recommendedProxySettings = true;
};
services.postgresql = {
enable = true;
enableTCPIP = false;
settings.unix_socket_permissions = "0770";
}; };
}; };
testScript = '' testScript = ''
start_all() start_all()
machine.wait_for_unit("multi-user.target") machine.wait_for_unit("multi-user.target")
# Testing metasrht
machine.wait_for_unit("metasrht-api.service")
machine.wait_for_unit("metasrht.service") machine.wait_for_unit("metasrht.service")
machine.wait_for_open_port(5000) machine.wait_for_open_port(5000)
machine.succeed("curl -sL http://localhost:5000 | grep meta.sourcehut") machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
# Testing buildsrht
machine.wait_for_unit("buildsrht.service")
machine.wait_for_open_port(5002)
machine.succeed("curl -sL http://localhost:5002 | grep builds.${domain}")
#machine.wait_for_unit("buildsrht-worker.service")
''; '';
}) })

View file

@ -1,68 +1,76 @@
{ alsa-lib { alsa-lib, at-spi2-core, cmake, curl, dbus, libepoxy, fetchFromGitHub, freeglut
, curl , freetype, gcc-unwrapped, gtk3, lib, libGL, libXcursor, libXdmcp, libXext
, fetchFromGitHub , libXinerama, libXrandr, libXtst, libdatrie, libjack2, libpsl, libselinux
, freeglut , libsepol, libsysprof-capture, libthai, libxkbcommon, lv2, pcre, pkg-config
, freetype , python3, sqlite, stdenv }:
, libGL
, libXcursor
, libXext
, libXinerama
, libXrandr
, libjack2
, pkg-config
, python3
, stdenv
, lib
}:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "CHOWTapeModel"; pname = "CHOWTapeModel";
version = "unstable-2020-12-12"; version = "2.10.0";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "jatinchowdhury18"; owner = "jatinchowdhury18";
repo = "AnalogTapeModel"; repo = "AnalogTapeModel";
rev = "a7cf10c3f790d306ce5743bb731e4bc2c1230d70"; rev = "v${version}";
sha256 = "09nq8x2dwabncbp039dqm1brzcz55zg9kpxd4p5348xlaz5m4661"; sha256 = "sha256-iuT7OBRBtMkjcTHayCcne1mNqkcxzKnEYl62n65V7Z4=";
fetchSubmodules = true; fetchSubmodules = true;
}; };
nativeBuildInputs = [ nativeBuildInputs = [ pkg-config cmake ];
pkg-config
];
buildInputs = [ buildInputs = [
alsa-lib alsa-lib
at-spi2-core
curl curl
dbus
libepoxy
freeglut freeglut
freetype freetype
gtk3
libGL libGL
libXcursor libXcursor
libXdmcp
libXext libXext
libXinerama libXinerama
libXrandr libXrandr
libXtst
libdatrie
libjack2 libjack2
libpsl
libselinux
libsepol
libsysprof-capture
libthai
libxkbcommon
lv2
pcre
python3 python3
sqlite
gcc-unwrapped
]; ];
buildPhase = '' cmakeFlags = [
cd Plugin/ "-DCMAKE_AR=${gcc-unwrapped}/bin/gcc-ar"
./build_linux.sh "-DCMAKE_RANLIB=${gcc-unwrapped}/bin/gcc-ranlib"
''; "-DCMAKE_NM=${gcc-unwrapped}/bin/gcc-nm"
];
postPatch = "cd Plugin";
installPhase = '' installPhase = ''
mkdir -p $out/lib/lv2 $out/lib/vst3 $out/bin $out/share/doc/CHOWTapeModel/ mkdir -p $out/lib/lv2 $out/lib/vst3 $out/bin $out/share/doc/CHOWTapeModel/
cd Builds/LinuxMakefile/build/ cd CHOWTapeModel_artefacts/Release
cp CHOWTapeModel.a $out/lib cp libCHOWTapeModel_SharedCode.a $out/lib
cp -r CHOWTapeModel.lv2 $out/lib/lv2 cp -r LV2/CHOWTapeModel.lv2 $out/lib/lv2
cp -r CHOWTapeModel.vst3 $out/lib/vst3 cp -r VST3/CHOWTapeModel.vst3 $out/lib/vst3
cp CHOWTapeModel $out/bin cp Standalone/CHOWTapeModel $out/bin
cp ../../../../Manual/ChowTapeManual.pdf $out/share/doc/CHOWTapeModel/ cp ../../../../Manual/ChowTapeManual.pdf $out/share/doc/CHOWTapeModel/
''; '';
meta = with lib; { meta = with lib; {
homepage = "https://github.com/jatinchowdhury18/AnalogTapeModel"; homepage = "https://github.com/jatinchowdhury18/AnalogTapeModel";
description = "Physical modelling signal processing for analog tape recording. LV2, VST3 and standalone"; description =
"Physical modelling signal processing for analog tape recording. LV2, VST3 and standalone";
license = with licenses; [ gpl3Only ]; license = with licenses; [ gpl3Only ];
maintainers = with maintainers; [ magnetophon ]; maintainers = with maintainers; [ magnetophon ];
platforms = platforms.linux; platforms = platforms.linux;

View file

@ -28,11 +28,11 @@
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "kid3"; pname = "kid3";
version = "3.8.7"; version = "3.9.0";
src = fetchurl { src = fetchurl {
url = "https://download.kde.org/stable/${pname}/${version}/${pname}-${version}.tar.xz"; url = "https://download.kde.org/stable/${pname}/${version}/${pname}-${version}.tar.xz";
sha256 = "sha256-Dr+NLh5ajG42jRKt1Swq6mccPfuAXRvhhoTNuO8lnI0="; sha256 = "sha256-d0Y+swzzGk1FzQ3EK8sN8i1Nf6CRIPMAYgTUYN71FXU=";
}; };
nativeBuildInputs = [ nativeBuildInputs = [

View file

@ -9,6 +9,8 @@ lib.makeScope newScope (self: with self; {
mopidy-iris = callPackage ./iris.nix { }; mopidy-iris = callPackage ./iris.nix { };
mopidy-jellyfin = callPackage ./jellyfin.nix { };
mopidy-local = callPackage ./local.nix { }; mopidy-local = callPackage ./local.nix { };
mopidy-moped = callPackage ./moped.nix { }; mopidy-moped = callPackage ./moped.nix { };

View file

@ -0,0 +1,25 @@
{ lib, python3Packages, mopidy }:
python3Packages.buildPythonApplication rec {
pname = "mopidy-jellyfin";
version = "1.0.2";
src = python3Packages.fetchPypi {
inherit version;
pname = "Mopidy-Jellyfin";
sha256 = "0j7v5xx3c401r5dw1sqm1n2263chjga1d3ml85rg79hjhhhacy75";
};
propagatedBuildInputs = [ mopidy python3Packages.unidecode python3Packages.websocket-client ];
# no tests implemented
doCheck = false;
pythonImportsCheck = [ "mopidy_jellyfin" ];
meta = with lib; {
homepage = "https://github.com/jellyfin/mopidy-jellyfin";
description = "Mopidy extension for playing audio files from Jellyfin";
license = licenses.asl20;
maintainers = [ maintainers.pstn ];
};
}

View file

@ -11,20 +11,22 @@
, libmicrohttpd , libmicrohttpd
, ncurses , ncurses
, pulseaudio , pulseaudio
, lib, stdenv , lib
, stdenv
, taglib , taglib
, systemdSupport ? stdenv.isLinux, systemd , systemdSupport ? stdenv.isLinux
, systemd
}: }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "musikcube"; pname = "musikcube";
version = "0.96.7"; version = "0.96.10";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "clangen"; owner = "clangen";
repo = pname; repo = pname;
rev = version; rev = version;
sha256 = "1y00vwn1h10cfflxrm5bk271ak9gilhjycgi44hlkkhmf5bdgn35"; sha256 = "sha256-Aa52pRGq99Pt++aEVZdmVNhhQuBajgfZp39L1AfKvho=";
}; };
patches = [ patches = [

View file

@ -2,7 +2,6 @@
, lib , lib
, stdenv , stdenv
, fetchFromGitHub , fetchFromGitHub
, fetchpatch
, nix-update-script , nix-update-script
, qmake , qmake
, pkg-config , pkg-config
@ -14,25 +13,15 @@
mkDerivation rec { mkDerivation rec {
pname = "ptcollab"; pname = "ptcollab";
version = "0.5.0.1"; version = "0.5.0.3";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "yuxshao"; owner = "yuxshao";
repo = "ptcollab"; repo = "ptcollab";
rev = "v${version}"; rev = "v${version}";
sha256 = "10v310smm0df233wlh1kqv8i36lsg1m36v0flrhs2202k50d69ri"; sha256 = "sha256-8bgi621psvUlhiLyZ15tKGmGOs6HTf5/6Ru2Z9l8QIo=";
}; };
patches = [
# Lifts macOS version restriction
# Remove when version > 0.5.0.1
(fetchpatch {
name = "0001-ptcollab-lift-10.14-deployment-target-limitation.patch";
url = "https://github.com/yuxshao/ptcollab/commit/a9664b5953e1046e1f7da3b38744f33a7ff0ea24.patch";
sha256 = "0qgpv5hmb4504kamdgxalrhc4zb9rdxln23s7qwc7ikafg54h1fm";
})
];
nativeBuildInputs = [ qmake pkg-config ]; nativeBuildInputs = [ qmake pkg-config ];
buildInputs = [ qtbase qtmultimedia libvorbis rtmidi ]; buildInputs = [ qtbase qtmultimedia libvorbis rtmidi ];

View file

@ -5,14 +5,14 @@
mkDerivation rec { mkDerivation rec {
pname = "qpwgraph"; pname = "qpwgraph";
version = "0.1.0"; version = "0.1.1";
src = fetchFromGitLab { src = fetchFromGitLab {
domain = "gitlab.freedesktop.org"; domain = "gitlab.freedesktop.org";
owner = "rncbc"; owner = "rncbc";
repo = "qpwgraph"; repo = "qpwgraph";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-VDLfOcIXM3+04tEvPzKDEKMperMhB5hDo1MlttS04yM="; sha256 = "sha256-r3FoAV0wah9fwnqyMyu8927c4Uj0zZoQNvLoXP5AP/E=";
}; };
nativeBuildInputs = [ cmake pkg-config ]; nativeBuildInputs = [ cmake pkg-config ];

View file

@ -17,13 +17,13 @@
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "reaper"; pname = "reaper";
version = "6.38"; version = "6.43";
src = fetchurl { src = fetchurl {
url = "https://www.reaper.fm/files/${lib.versions.major version}.x/reaper${builtins.replaceStrings ["."] [""] version}_linux_${stdenv.hostPlatform.qemuArch}.tar.xz"; url = "https://www.reaper.fm/files/${lib.versions.major version}.x/reaper${builtins.replaceStrings ["."] [""] version}_linux_${stdenv.hostPlatform.qemuArch}.tar.xz";
hash = { hash = {
x86_64-linux = "sha256-K5EnrmzP8pyW9dR1fbMzkPzpS6aHm8JF1+m3afnH4rU="; x86_64-linux = "sha256-VQ91px9YZWbrw31fFQxS+H/6fsjkLDrYU6FtI8eSq6E=";
aarch64-linux = "sha256-6wNWDXjQNyfU2l9Xi9JtmAuoKtHuIY5cvNMjYkwh2Sk="; aarch64-linux = "sha256-x6z5+H7ASWiuNL0maNGK05VmJptHdFGRiFf6DgwlZDw=";
}.${stdenv.hostPlatform.system}; }.${stdenv.hostPlatform.system};
}; };
@ -78,6 +78,6 @@ stdenv.mkDerivation rec {
homepage = "https://www.reaper.fm/"; homepage = "https://www.reaper.fm/";
license = licenses.unfree; license = licenses.unfree;
platforms = [ "x86_64-linux" "aarch64-linux" ]; platforms = [ "x86_64-linux" "aarch64-linux" ];
maintainers = with maintainers; [ jfrankenau ilian orivej ]; maintainers = with maintainers; [ jfrankenau ilian orivej uniquepointer ];
}; };
} }

View file

@ -1,18 +1,16 @@
{ lib, stdenv, fetchFromGitHub { lib, stdenv, fetchFromGitHub, libjack2, libsndfile, xorg, freetype
, libjack2, libsndfile, xorg, freetype, libxkbcommon , libxkbcommon, cairo, glib, gnome, flac, libogg, libvorbis, libopus, cmake
, cairo, glib, gnome, flac, libogg, libvorbis, libopus , pango, pkg-config }:
, cmake, pkg-config
}:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "sfizz"; pname = "sfizz";
version = "0.5.1"; version = "1.1.1";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "sfztools"; owner = "sfztools";
repo = pname; repo = pname;
rev = version; rev = version;
sha256 = "sha256-3RdY5+BPsdk6vctDy24w5aJsVOV9qzSgXs62Pm5UEKs="; sha256 = "1gzpbns89j6ggzfjjvyhgigynsv20synrs7lmc32hwp4g73l0j7n";
fetchSubmodules = true; fetchSubmodules = true;
}; };
@ -37,18 +35,18 @@ stdenv.mkDerivation rec {
glib glib
gnome.zenity gnome.zenity
freetype freetype
pango
]; ];
nativeBuildInputs = [ cmake pkg-config ]; nativeBuildInputs = [ cmake pkg-config ];
postPatch = '' postPatch = ''
substituteInPlace editor/external/vstgui4/vstgui/lib/platform/linux/x11fileselector.cpp \ substituteInPlace plugins/editor/external/vstgui4/vstgui/lib/platform/linux/x11fileselector.cpp \
--replace '"/usr/bin/zenity' '"${gnome.zenity}/bin/zenity' --replace 'zenitypath = "zenity"' 'zenitypath = "${gnome.zenity}/bin/zenity"'
substituteInPlace plugins/editor/src/editor/NativeHelpers.cpp \
--replace '/usr/bin/zenity' '${gnome.zenity}/bin/zenity'
''; '';
cmakeFlags = [ cmakeFlags = [ "-DCMAKE_BUILD_TYPE=Release" "-DSFIZZ_TESTS=ON" ];
"-DCMAKE_BUILD_TYPE=Release"
"-DSFIZZ_TESTS=ON"
];
meta = with lib; { meta = with lib; {
homepage = "https://github.com/sfztools/sfizz"; homepage = "https://github.com/sfztools/sfizz";

View file

@ -33,7 +33,7 @@ stdenv.mkDerivation rec {
meta = with lib; { meta = with lib; {
description = "A powerful editor targeted towards programmers and webdevelopers"; description = "A powerful editor targeted towards programmers and webdevelopers";
homepage = "http://bluefish.openoffice.nl/"; homepage = "https://bluefish.openoffice.nl/";
license = licenses.gpl3Plus; license = licenses.gpl3Plus;
maintainers = with maintainers; [ vbgl ]; maintainers = with maintainers; [ vbgl ];
platforms = platforms.all; platforms = platforms.all;

View file

@ -15,7 +15,7 @@ stdenv.mkDerivation {
''; '';
meta = { meta = {
homepage = "http://bruda.ca/emacs/prolog_mode_for_emacs/"; homepage = "https://bruda.ca/emacs/prolog_mode_for_emacs/";
description = "Prolog mode for Emacs"; description = "Prolog mode for Emacs";
license = lib.licenses.gpl2Plus; license = lib.licenses.gpl2Plus;
}; };

View file

@ -26,6 +26,7 @@
, siteStart ? ./site-start.el , siteStart ? ./site-start.el
, nativeComp ? false , nativeComp ? false
, withPgtk ? false , withPgtk ? false
, withXinput2 ? false
, withImageMagick ? lib.versionOlder version "27" && (withX || withNS) , withImageMagick ? lib.versionOlder version "27" && (withX || withNS)
, toolkit ? ( , toolkit ? (
if withGTK2 then "gtk2" if withGTK2 then "gtk2"
@ -152,6 +153,7 @@ let emacs = stdenv.mkDerivation (lib.optionalAttrs nativeComp {
++ lib.optional nativeComp "--with-native-compilation" ++ lib.optional nativeComp "--with-native-compilation"
++ lib.optional withImageMagick "--with-imagemagick" ++ lib.optional withImageMagick "--with-imagemagick"
++ lib.optional withPgtk "--with-pgtk" ++ lib.optional withPgtk "--with-pgtk"
++ lib.optional withXinput2 "--with-xinput2"
; ;
installTargets = [ "tags" "install" ]; installTargets = [ "tags" "install" ];

View file

@ -14,13 +14,13 @@
mkDerivation rec { mkDerivation rec {
pname = "ghostwriter"; pname = "ghostwriter";
version = "2.1.0"; version = "2.1.1";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "wereturtle"; owner = "wereturtle";
repo = pname; repo = pname;
rev = version; rev = version;
sha256 = "sha256-NPuwT0msFvGyS33X7lefdPZZ3AC4pZb1tvmOzzlQlgc="; hash = "sha256-w4qCJgfBnN1PpPfhdsLdBpCRAWai9RrwU3LZl8DdEcw=";
}; };
nativeBuildInputs = [ qmake pkg-config qttools ]; nativeBuildInputs = [ qmake pkg-config qttools ];

View file

@ -91,6 +91,10 @@ let
patchelf --set-interpreter $interp $out/goland*/plugins/go/lib/dlv/linux/dlv patchelf --set-interpreter $interp $out/goland*/plugins/go/lib/dlv/linux/dlv
chmod +x $out/goland*/plugins/go/lib/dlv/linux/dlv chmod +x $out/goland*/plugins/go/lib/dlv/linux/dlv
# fortify source breaks build since delve compiles with -O0
wrapProgram $out/goland*/plugins/go/lib/dlv/linux/dlv \
--prefix disableHardening " " fortify
''; '';
}); });

View file

@ -1,4 +1,4 @@
{ lib, stdenv, fetchFromGitHub, pkg-config, libtool { lib, stdenv, fetchurl, pkg-config, libtool
, bzip2, zlib, libX11, libXext, libXt, fontconfig, freetype, ghostscript, libjpeg, djvulibre , bzip2, zlib, libX11, libXext, libXt, fontconfig, freetype, ghostscript, libjpeg, djvulibre
, lcms2, openexr, libjxl, libpng, liblqr1, libraw, librsvg, libtiff, libxml2, openjpeg, libwebp, libheif , lcms2, openexr, libjxl, libpng, liblqr1, libraw, librsvg, libtiff, libxml2, openjpeg, libwebp, libheif
, ApplicationServices , ApplicationServices
@ -18,13 +18,11 @@ in
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "imagemagick"; pname = "imagemagick";
version = "7.1.0-17"; version = "7.1.0-19";
src = fetchFromGitHub { src = fetchurl {
owner = "ImageMagick"; url = "https://download.imagemagick.org/ImageMagick/download/releases/ImageMagick-${version}.tar.xz";
repo = "ImageMagick"; hash = "sha256-P9eRdKsPMLwWQ68+ZU8dL/zDqVVCY5gRVWiLT0n3/Xc=";
rev = version;
sha256 = "sha256-P6w7dDDvY8r55qN3hnsuzO8kp85gxp2t6vShmhoPOgs=";
}; };
outputs = [ "out" "dev" "doc" ]; # bin/ isn't really big outputs = [ "out" "dev" "doc" ]; # bin/ isn't really big

View file

@ -53,13 +53,13 @@ let
python = python2.withPackages (pp: [ pp.pygtk ]); python = python2.withPackages (pp: [ pp.pygtk ]);
in stdenv.mkDerivation rec { in stdenv.mkDerivation rec {
pname = "gimp"; pname = "gimp";
version = "2.10.28"; version = "2.10.30";
outputs = [ "out" "dev" ]; outputs = [ "out" "dev" ];
src = fetchurl { src = fetchurl {
url = "http://download.gimp.org/pub/gimp/v${lib.versions.majorMinor version}/${pname}-${version}.tar.bz2"; url = "http://download.gimp.org/pub/gimp/v${lib.versions.majorMinor version}/${pname}-${version}.tar.bz2";
sha256 = "T03CLP8atfAm/qoqtV4Fd1s6EeGYGGtHvat5y/oHiCY="; sha256 = "iIFdqnbtfUJ37rNTNYuvoRbNL80shh2VuVE1wdUrZ9w=";
}; };
patches = [ patches = [

View file

@ -9,6 +9,8 @@
, copyDesktopItems , copyDesktopItems
, fontconfig , fontconfig
, libpng , libpng
, pipewire
, makeWrapper
, autoPatchelfHook , autoPatchelfHook
}: }:
@ -38,6 +40,7 @@ stdenv.mkDerivation rec {
fontconfig fontconfig
libva libva
gst_all_1.gst-plugins-base gst_all_1.gst-plugins-base
pipewire
# autoPatchelfHook complains if these are missing, even on wayland # autoPatchelfHook complains if these are missing, even on wayland
xorg.libXft xorg.libXft
xorg.libXinerama xorg.libXinerama
@ -47,12 +50,22 @@ stdenv.mkDerivation rec {
xorg.libXtst xorg.libXtst
]; ];
nativeBuildInputs = [ copyDesktopItems autoPatchelfHook ]; nativeBuildInputs = [ copyDesktopItems autoPatchelfHook makeWrapper ];
postFixup = let
GST_PLUGIN_PATH = lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" [
gst_all_1.gst-plugins-base
pipewire
];
in ''
wrapProgram $out/bin/weylus --prefix GST_PLUGIN_PATH : ${GST_PLUGIN_PATH}
'';
meta = with lib; { meta = with lib; {
description = "Use your tablet as graphic tablet/touch screen on your computer"; description = "Use your tablet as graphic tablet/touch screen on your computer";
homepage = "https://github.com/H-M-H/Weylus"; homepage = "https://github.com/H-M-H/Weylus";
license = with licenses; [ agpl3Only ]; license = with licenses; [ agpl3Only ];
maintainers = with maintainers; [ lom ]; maintainers = with maintainers; [ lom ];
platforms = [ "x86_64-linux" ];
}; };
} }

View file

@ -3,7 +3,7 @@
mkDerivation { mkDerivation {
pname = "kalzium"; pname = "kalzium";
meta = with lib; { meta = with lib; {
homepage = "https://kde.org/applications/en/utilities/org.kde.kalzium"; homepage = "https://edu.kde.org/kalzium/";
description = "Program that shows you the Periodic Table of Elements"; description = "Program that shows you the Periodic Table of Elements";
maintainers = with maintainers; [ freezeboy ]; maintainers = with maintainers; [ freezeboy ];
license = licenses.gpl2Plus; license = licenses.gpl2Plus;

View file

@ -17,7 +17,7 @@ mkDerivation {
meta = { meta = {
description = "Plugins for KDE-based image applications"; description = "Plugins for KDE-based image applications";
license = lib.licenses.gpl2; license = lib.licenses.gpl2;
homepage = "https://cgit.kde.org/kipi-plugins.git"; homepage = "https://github.com/KDE/kipi-plugins";
maintainers = with lib.maintainers; [ ttuegel ]; maintainers = with lib.maintainers; [ ttuegel ];
}; };
} }

View file

@ -3,7 +3,7 @@
mkDerivation { mkDerivation {
pname = "klettres"; pname = "klettres";
meta = with lib; { meta = with lib; {
homepage = "https://kde.org/applications/en/utilities/org.kde.klettres"; homepage = "https://invent.kde.org/education/klettres";
description = "An application specially designed to help the user to learn an alphabet"; description = "An application specially designed to help the user to learn an alphabet";
maintainers = with maintainers; [ freezeboy ]; maintainers = with maintainers; [ freezeboy ];
license = licenses.gpl2Plus; license = licenses.gpl2Plus;

View file

@ -3,7 +3,7 @@
mkDerivation { mkDerivation {
pname = "kturtle"; pname = "kturtle";
meta = with lib; { meta = with lib; {
homepage = "https://kde.org/applications/en/utilities/org.kde.kturtle"; homepage = "https://invent.kde.org/education/kturtle";
description = "An educational programming environment for learning how to program"; description = "An educational programming environment for learning how to program";
maintainers = with maintainers; [ freezeboy ]; maintainers = with maintainers; [ freezeboy ];
license = licenses.gpl2Plus; license = licenses.gpl2Plus;

View file

@ -1,20 +1,21 @@
{ lib, fetchFromGitHub { lib, fetchFromGitHub
, meson, ninja, pkg-config, wrapGAppsHook , meson, ninja, pkg-config, wrapGAppsHook
, desktop-file-utils, gsettings-desktop-schemas, libnotify, libhandy , desktop-file-utils, gsettings-desktop-schemas, libnotify, libhandy, webkitgtk
, python3Packages, gettext , python3Packages, gettext
, appstream-glib, gdk-pixbuf, glib, gobject-introspection, gspell, gtk3 , appstream-glib, gdk-pixbuf, glib, gobject-introspection, gspell, gtk3, gnome
, steam-run, xdg-utils, pciutils, cabextract, wineWowPackages , steam-run, xdg-utils, pciutils, cabextract, wineWowPackages
, freetype, p7zip, gamemode
}: }:
python3Packages.buildPythonApplication rec { python3Packages.buildPythonApplication rec {
pname = "bottles"; pname = "bottles";
version = "2021.7.28-treviso-2"; version = "2021.12.28-treviso";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "bottlesdevs"; owner = "bottlesdevs";
repo = pname; repo = pname;
rev = version; rev = version;
sha256 = "0kvwcajm9izvkwfg7ir7bks39bpc665idwa8mc8d536ajyjriysn"; sha256 = "lZbSLLBg7XM6PuOmu5rJ15dg+QHHRcjijRYE6u3WT9Y=";
}; };
postPatch = '' postPatch = ''
@ -41,10 +42,13 @@ python3Packages.buildPythonApplication rec {
gtk3 gtk3
libhandy libhandy
libnotify libnotify
webkitgtk
gnome.adwaita-icon-theme
]; ];
propagatedBuildInputs = with python3Packages; [ propagatedBuildInputs = with python3Packages; [
pyyaml pyyaml
pytoml
requests requests
pycairo pycairo
pygobject3 pygobject3
@ -53,12 +57,16 @@ python3Packages.buildPythonApplication rec {
gst-python gst-python
liblarch liblarch
patool patool
markdown
] ++ [ ] ++ [
steam-run steam-run
xdg-utils xdg-utils
pciutils pciutils
cabextract cabextract
wineWowPackages.minimal wineWowPackages.minimal
freetype
p7zip
gamemode # programs.gamemode.enable
]; ];
format = "other"; format = "other";
@ -66,13 +74,9 @@ python3Packages.buildPythonApplication rec {
dontWrapGApps = true; # prevent double wrapping dontWrapGApps = true; # prevent double wrapping
preConfigure = '' preConfigure = ''
substituteInPlace build-aux/meson/postinstall.py \ patchShebangs build-aux/meson/postinstall.py
--replace "'update-desktop-database'" "'${desktop-file-utils}/bin/update-desktop-database'" substituteInPlace src/backend/runner.py \
substituteInPlace src/runner.py \ --replace "{Paths.runners}" "${steam-run}/bin/steam-run {Paths.runners}"
--replace " {runner}" " ${steam-run}/bin/steam-run {runner}" \
--replace " {dxvk_setup}" " ${steam-run}/bin/steam-run {dxvk_setup}"
substituteInPlace src/runner_utilities.py \
--replace " {runner}" " ${steam-run}/bin/steam-run {runner}" \
''; '';
preFixup = '' preFixup = ''

View file

@ -2,16 +2,16 @@
buildGoModule rec { buildGoModule rec {
pname = "charm"; pname = "charm";
version = "0.9.0"; version = "0.9.2";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "charmbracelet"; owner = "charmbracelet";
repo = "charm"; repo = "charm";
rev = "v${version}"; rev = "v${version}";
sha256 = "1q5c2qka4srqj82f50iwmcj2j0yw2msz5dmrx2avqppp3fyi9jz3"; sha256 = "sha256-5WNkD+YfmQHGAWWfD9/ZEHnHPT0Ejm9Nz+/mn8xvU4U=";
}; };
vendorSha256 = "1xycgzx706kyz37z3517p98129iy7py7zdizz34k38fvfpila5q5"; vendorSha256 = "sha256-TncVMDeZ8+Wuv1o0Cjks3Ve1OsO+WXH9mClC6GNaSas=";
doCheck = false; doCheck = false;

View file

@ -20,13 +20,13 @@
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "dbeaver"; pname = "dbeaver";
version = "21.3.1"; # When updating also update fetchedMavenDeps.sha256 version = "21.3.2"; # When updating also update fetchedMavenDeps.sha256
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "dbeaver"; owner = "dbeaver";
repo = "dbeaver"; repo = "dbeaver";
rev = version; rev = version;
sha256 = "ePy3uS+LpyDzweLocSk3O/G2zFPISKbMbci9fdELrpE="; sha256 = "SifnnzuETFKtnEwLjJtB7CV2QZaToex3MjKGuiShlwo=";
}; };
fetchedMavenDeps = stdenv.mkDerivation { fetchedMavenDeps = stdenv.mkDerivation {
@ -52,7 +52,7 @@ stdenv.mkDerivation rec {
dontFixup = true; dontFixup = true;
outputHashAlgo = "sha256"; outputHashAlgo = "sha256";
outputHashMode = "recursive"; outputHashMode = "recursive";
outputHash = "7Sm1hAoi5xc4MLONOD8ySLLkpao0qmlMRRva/8zR210="; outputHash = "grSFtkohTlLtK8qE4A4wVppC6UHcyaXRQlGnrOmQDC4=";
}; };
nativeBuildInputs = [ nativeBuildInputs = [

View file

@ -1,7 +1,7 @@
{ lib, fetchFromGitHub, gtk3, pythonPackages, intltool, gexiv2, { lib, fetchFromGitHub, gtk3, pythonPackages, intltool, gexiv2,
pango, gobject-introspection, wrapGAppsHook, gettext, pango, gobject-introspection, wrapGAppsHook, gettext,
# Optional packages: # Optional packages:
enableOSM ? true, osm-gps-map, enableOSM ? true, osm-gps-map, glib-networking,
enableGraphviz ? true, graphviz, enableGraphviz ? true, graphviz,
enableGhostscript ? true, ghostscript enableGhostscript ? true, ghostscript
}: }:
@ -15,7 +15,7 @@ in buildPythonApplication rec {
nativeBuildInputs = [ wrapGAppsHook intltool gettext ]; nativeBuildInputs = [ wrapGAppsHook intltool gettext ];
buildInputs = [ gtk3 gobject-introspection pango gexiv2 ] buildInputs = [ gtk3 gobject-introspection pango gexiv2 ]
# Map support # Map support
++ lib.optional enableOSM osm-gps-map ++ lib.optionals enableOSM [ osm-gps-map glib-networking ]
# Graphviz support # Graphviz support
++ lib.optional enableGraphviz graphviz ++ lib.optional enableGraphviz graphviz
# Ghostscript support # Ghostscript support

View file

@ -0,0 +1,40 @@
{ lib
, fetchFromGitHub
, rustPlatform
, pkg-config
, openssl
, stdenv
, CoreServices
, Security
}:
rustPlatform.buildRustPackage rec {
pname = "inherd-quake";
version = "0.3.0";
src = fetchFromGitHub {
owner = "phodal";
repo = "quake";
rev = "v${version}";
sha256 = "1f7k68g18g3dpnrsmhgmz753bly1i3f4lmsljiyp9ap0c6w8ahgg";
};
cargoSha256 = "17q9sjypa331gdfvmx1kbcbvnj34rnsf37b9rnji4jrqfysgrs5w";
nativeBuildInputs = [ pkg-config ];
buildInputs = [
openssl
] ++ lib.optionals stdenv.isDarwin [
CoreServices
Security
];
meta = with lib; {
description = "A knowledge management meta-framework for geeks";
homepage = "https://github.com/phodal/quake";
license = licenses.mit;
maintainers = [ maintainers.elliot ];
mainProgram = "quake";
};
}

View file

@ -50,5 +50,6 @@ appimageTools.wrapType2 rec {
license = licenses.mit; license = licenses.mit;
maintainers = with maintainers; [ nh2 ]; maintainers = with maintainers; [ nh2 ];
platforms = [ "x86_64-linux" ]; platforms = [ "x86_64-linux" ];
mainProgram = "marktext";
}; };
} }

View file

@ -9,7 +9,7 @@
, itstool , itstool
, libadwaita , libadwaita
, librsvg , librsvg
, meson , meson_0_60
, ninja , ninja
, pkg-config , pkg-config
, poppler_gi , poppler_gi
@ -18,7 +18,7 @@
python3.pkgs.buildPythonApplication rec { python3.pkgs.buildPythonApplication rec {
pname = "metadata-cleaner"; pname = "metadata-cleaner";
version = "2.0.1"; version = "2.1.3";
format = "other"; format = "other";
@ -26,7 +26,7 @@ python3.pkgs.buildPythonApplication rec {
owner = "rmnvgr"; owner = "rmnvgr";
repo = "metadata-cleaner"; repo = "metadata-cleaner";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-iTKs3DEZSzqRARXJKPPygvCS5JNUMbQBkfjacwd168Y="; hash = "sha256-9sLjgqqQBXcudlBRmqAwWcWMUXoIUyAK272zaNKbJNY=";
}; };
nativeBuildInputs = [ nativeBuildInputs = [
@ -35,7 +35,7 @@ python3.pkgs.buildPythonApplication rec {
glib glib
gtk4 gtk4
itstool itstool
meson meson_0_60
ninja ninja
pkg-config pkg-config
wrapGAppsHook wrapGAppsHook

View file

@ -0,0 +1,25 @@
{ lib, stdenv, fetchurl, ncurses }:
stdenv.mkDerivation rec {
pname = "neo";
version = "0.6";
src = fetchurl {
url = "https://github.com/st3w/neo/releases/download/v${version}/neo-${version}.tar.gz";
sha256 = "sha256-skXLT1td4dGdsu+wbX44Z2u5sDEOKXYVVys4Q6RayIk=";
};
buildInputs = [ ncurses ];
meta = with lib; {
description = ''Simulates the digital rain from "The Matrix"'';
license = licenses.gpl3Plus;
longDescription = ''
neo recreates the digital rain effect from "The Matrix". Streams of random
characters will endlessly scroll down your terminal screen.
'';
homepage = "https://github.com/st3w/neo";
platforms = ncurses.meta.platforms;
maintainers = [ maintainers.abbe ];
};
}

View file

@ -1,10 +1,10 @@
{ stdenv, lib, fetchFromGitHub, cmake, copyDesktopItems, makeDesktopItem, pkg-config, wrapGAppsHook { stdenv, lib, fetchFromGitHub, cmake, copyDesktopItems, makeDesktopItem, pkg-config, wrapGAppsHook
, boost, cereal, cgal_5, curl, dbus, eigen, expat, glew, glib, gmp, gtest, gtk3, hicolor-icon-theme , boost, cereal, cgal_5, curl, dbus, eigen, expat, glew, glib, gmp, gtest, gtk3, hicolor-icon-theme
, ilmbase, libpng, mpfr, nlopt, openvdb, pcre, qhull, systemd, tbb, wxGTK31-gtk3, xorg , ilmbase, libpng, mpfr, nlopt, openvdb, pcre, qhull, systemd, tbb, wxGTK31-gtk3, xorg, fetchpatch
}: }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "prusa-slicer"; pname = "prusa-slicer";
version = "2.3.3"; version = "2.4.0";
nativeBuildInputs = [ nativeBuildInputs = [
cmake cmake
@ -38,9 +38,19 @@ stdenv.mkDerivation rec {
xorg.libX11 xorg.libX11
] ++ checkInputs; ] ++ checkInputs;
patches = [
# Fix detection of TBB, see https://github.com/prusa3d/PrusaSlicer/issues/6355
(fetchpatch {
url = "https://github.com/prusa3d/PrusaSlicer/commit/76f4d6fa98bda633694b30a6e16d58665a634680.patch";
sha256 = "1r806ycp704ckwzgrw1940hh1l6fpz0k1ww3p37jdk6mygv53nv6";
})
];
doCheck = true; doCheck = true;
checkInputs = [ gtest ]; checkInputs = [ gtest ];
separateDebugInfo = true;
# The build system uses custom logic - defined in # The build system uses custom logic - defined in
# cmake/modules/FindNLopt.cmake in the package source - for finding the nlopt # cmake/modules/FindNLopt.cmake in the package source - for finding the nlopt
# library, which doesn't pick up the package in the nix store. We # library, which doesn't pick up the package in the nix store. We
@ -56,11 +66,6 @@ stdenv.mkDerivation rec {
NIX_LDFLAGS = "-ludev"; NIX_LDFLAGS = "-ludev";
prePatch = '' prePatch = ''
# In nix ioctls.h isn't available from the standard kernel-headers package
# like in other distributions. The copy in glibc seems to be identical to the
# one in the kernel though, so we use that one instead.
sed -i 's|"/usr/include/asm-generic/ioctls.h"|<asm-generic/ioctls.h>|g' src/libslic3r/GCodeSender.cpp
# Since version 2.5.0 of nlopt we need to link to libnlopt, as libnlopt_cxx # Since version 2.5.0 of nlopt we need to link to libnlopt, as libnlopt_cxx
# now seems to be integrated into the main lib. # now seems to be integrated into the main lib.
sed -i 's|nlopt_cxx|nlopt|g' cmake/modules/FindNLopt.cmake sed -i 's|nlopt_cxx|nlopt|g' cmake/modules/FindNLopt.cmake
@ -69,7 +74,7 @@ stdenv.mkDerivation rec {
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "prusa3d"; owner = "prusa3d";
repo = "PrusaSlicer"; repo = "PrusaSlicer";
sha256 = "0w0synqi3iz9aigsgv6x1c6sg123fasbx19h4w3ic1l48r8qmpwm"; sha256 = "1mb7v0khrmsgy3inmh4mjn709jlhx422kvbnrhsqziph2wwak9bz";
rev = "version_${version}"; rev = "version_${version}";
}; };

View file

@ -20,6 +20,8 @@ let
fetchSubmodules = true; fetchSubmodules = true;
}; };
patches = null;
# We don't need PS overrides anymore, and gcode-viewer is embedded in the binary. # We don't need PS overrides anymore, and gcode-viewer is embedded in the binary.
postInstall = null; postInstall = null;
separateDebugInfo = true; separateDebugInfo = true;

View file

@ -2,16 +2,16 @@
buildGoModule rec { buildGoModule rec {
pname = "skate"; pname = "skate";
version = "0.1.2"; version = "0.1.3";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "charmbracelet"; owner = "charmbracelet";
repo = "skate"; repo = "skate";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-Z+7unYmwPLOhJAMAhMwjapAmslTNxmP01myjgEOBfu8="; sha256 = "sha256-rUOFx0ebZs3xmsSz9oFvjINaHp9gIe7E/5UoJJ47aZc=";
}; };
vendorSha256 = "sha256-CdYyiUiy2q2boEHjdXkgRzVI+6fEb+fBrlInl6IrFjk="; vendorSha256 = "sha256-3+KXirGwZvPhqCeglPqHJ9wEt6naJiRE3TAX7/jxJpk=";
doCheck = false; doCheck = false;

View file

@ -1,32 +1,44 @@
{ lib, stdenv, fetchFromGitLab, ocaml, findlib, ocf, ptime, { lib, buildDunePackage, fetchFromGitLab, ocaml
uutf, uri, ppx_blob, xtmpl, ocaml_lwt, higlo, omd , fmt, lwt_ppx, menhir, ocf_ppx, ppx_blob, xtmpl_ppx
, dune-build-info, dune-site, higlo, logs, lwt, ocf, ptime, uri, uutf, xtmpl
}: }:
stdenv.mkDerivation rec { if lib.versionAtLeast ocaml.version "4.13"
then throw "stog is not available for OCaml ${ocaml.version}"
else
buildDunePackage rec {
pname = "stog"; pname = "stog";
version = "0.18.0"; version = "0.20.0";
useDune2 = true;
minimalOCamlVersion = "4.12";
src = fetchFromGitLab { src = fetchFromGitLab {
domain = "framagit.org"; domain = "framagit.org";
owner = "zoggy"; owner = "zoggy";
repo = "stog"; repo = "stog";
rev = version; rev = version;
sha256 = "154gl3ljxqlw8wz1vmsyv8180igrl5bjq0wir7ybrnzq2cdflkv0"; sha256 = "sha256:0krj5w4y05bcfx7hk9blmap8avl31gp7yi01lpqzs6ync23mvm0x";
}; };
buildInputs = [ ocaml uutf ]; buildInputs = [ fmt lwt_ppx menhir ocf_ppx ppx_blob xtmpl_ppx ];
propagatedBuildInputs = [ findlib omd ppx_blob ocf ptime uri xtmpl ocaml_lwt higlo ]; propagatedBuildInputs = [
dune-build-info
createFindlibDestdir = true; dune-site
higlo
patches = [ ./install.patch ./uri.patch ]; logs
lwt
ocf
ppx_blob
ptime
uri
uutf
xtmpl
];
meta = with lib; { meta = with lib; {
description = "XML documents and web site compiler"; description = "XML documents and web site compiler";
homepage = "https://www.good-eris.net/stog"; homepage = "https://www.good-eris.net/stog";
license = licenses.lgpl3; license = licenses.lgpl3;
platforms = ocaml.meta.platforms or [];
maintainers = with maintainers; [ regnat ]; maintainers = with maintainers; [ regnat ];
}; };
} }

View file

@ -1,18 +0,0 @@
diff --git a/src/Makefile b/src/Makefile
index 736dd037..79a85b9c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -431,11 +431,12 @@ install-lib:
install-share:
install-bin:
+ mkdir $(out)/bin
$(CP) $(MAIN) $(MAIN_BYTE) $(TMPL) $(TMPL_BYTE) \
$(SERVER) $(SERVER_BYTE) $(OCAML_SESSION) \
$(MK_STOG) $(MK_STOG_BYTE) $(MK_STOG_OCAML) \
$(LATEX2STOG) $(LATEX2STOG_BYTE) \
- `dirname \`which $(OCAMLC)\``/
+ $(out)/bin
uninstall: uninstall-lib uninstall-share uninstall-bin

View file

@ -1,13 +0,0 @@
diff --git a/src/stog_url.ml b/src/stog_url.ml
index 5d30a43f..c67bfc36 100644
--- a/src/stog_url.ml
+++ b/src/stog_url.ml
@@ -40,7 +40,7 @@ let of_string s =
with _ ->
failwith (Printf.sprintf "Malformed URL %S" s)
;;
-let to_string = Uri.to_string ;;
+let to_string u = Uri.to_string u;;
let path url =
let l =

View file

@ -2,16 +2,16 @@
buildGoModule rec { buildGoModule rec {
pname = "timew-sync-server"; pname = "timew-sync-server";
version = "1.0.0"; version = "1.1.0";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "timewarrior-synchronize"; owner = "timewarrior-synchronize";
repo = pname; repo = pname;
rev = "v${version}"; rev = "v${version}";
sha256 = "041j618c2bcryhgi2j2w5zlfcxcklgbir2xj3px4w7jxbbg6p68n"; sha256 = "GaDcnPJBcDJ3AQaHzifDgdl0QT4GSbAOIqp4RrAcO3M=";
}; };
vendorSha256 = "0wbd4cpswgbr839sk8qwly8gjq4lqmq448m624akll192mzm9wj7"; vendorSha256 = "iROqiRWkHG6N6kivUmgmu6sg14JDdG4f98BdR7CL1gs=";
meta = with lib; { meta = with lib; {
homepage = "https://github.com/timewarrior-synchronize/timew-sync-server"; homepage = "https://github.com/timewarrior-synchronize/timew-sync-server";

View file

@ -2,13 +2,13 @@
buildGoModule rec { buildGoModule rec {
pname = "amfora"; pname = "amfora";
version = "1.9.0"; version = "1.9.2";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "makeworld-the-better-one"; owner = "makeworld-the-better-one";
repo = "amfora"; repo = "amfora";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-Vj5aFSpyC7X9e9A9r4FAI6a0U8dx8uj7bpAFrQjLSzo="; sha256 = "sha256-93xNzYPoy8VsbY2JyvDXt4J/gIbI2wzrCD83JUaP150=";
}; };
vendorSha256 = "sha256-XtiGj2Tr6sSBduIjBspeZpYaSTd6x6EVf3VEVMXDAD0="; vendorSha256 = "sha256-XtiGj2Tr6sSBduIjBspeZpYaSTd6x6EVf3VEVMXDAD0=";

View file

@ -297,10 +297,17 @@ stdenv.mkDerivation rec {
cp -u --no-preserve=mode,owner "$TBB_IN_STORE/TorBrowser/Data/Browser/profile.default/bookmarks.html" \ cp -u --no-preserve=mode,owner "$TBB_IN_STORE/TorBrowser/Data/Browser/profile.default/bookmarks.html" \
"\$HOME/TorBrowser/Data/Browser/profile.default/bookmarks.html" "\$HOME/TorBrowser/Data/Browser/profile.default/bookmarks.html"
# Clear some files if the last known store path is different from the new one
: "\''${KNOWN_STORE_PATH:=\$HOME/known-store-path}"
if ! [ "\$KNOWN_STORE_PATH" -ef $out ]; then
echo "Cleanup files with outdated store references"
ln -Tsf $out "\$KNOWN_STORE_PATH"
# Clear out some files that tend to capture store references but are # Clear out some files that tend to capture store references but are
# easily generated by firefox at startup. # easily generated by firefox at startup.
rm -f "\$HOME/TorBrowser/Data/Browser/profile.default"/{addonStartup.json.lz4,compatibility.ini,extensions.ini,extensions.json} rm -f "\$HOME/TorBrowser/Data/Browser/profile.default"/{addonStartup.json.lz4,compatibility.ini,extensions.ini,extensions.json}
rm -f "\$HOME/TorBrowser/Data/Browser/profile.default"/startupCache/* rm -f "\$HOME/TorBrowser/Data/Browser/profile.default"/startupCache/*
fi
# XDG # XDG
: "\''${XDG_RUNTIME_DIR:=/run/user/\$(id -u)}" : "\''${XDG_RUNTIME_DIR:=/run/user/\$(id -u)}"

View file

@ -2,13 +2,13 @@
buildGoModule rec { buildGoModule rec {
pname = "k9s"; pname = "k9s";
version = "0.25.12"; version = "0.25.18";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "derailed"; owner = "derailed";
repo = "k9s"; repo = "k9s";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-S+roKkAtiGJmp/MHFeB+8mLQDL9okzLuJW6DMz3dQDk="; sha256 = "sha256-iUhMPtFX7qFULegiyhlT4aG9q3deZ8aRqyEcbZ9jY/s=";
}; };
ldflags = [ ldflags = [

View file

@ -0,0 +1,33 @@
{ buildGoModule, lib, fetchFromGitHub }:
buildGoModule rec {
pname = "kubergrunt";
version = "0.7.11";
src = fetchFromGitHub {
owner = "gruntwork-io";
repo = "kubergrunt";
rev = "v${version}";
sha256 = "1224ssqdz9ak0vylyfbr9c2w0yfdp4hw9jh99qmfi2j5nhw9kzcc";
};
vendorSha256 = "1hbb3hn8mzz9h9p1rl35izz3l6c2rqsg8aq6dgpbpsf5krp3zs3v";
# Disable tests since it requires network access and relies on the
# presence of certain AWS infrastructure
doCheck = false;
runVend = true;
postInstall = ''
# The binary is named kubergrunt
mv $out/bin/cmd $out/bin/kubergrunt
'';
meta = with lib; {
description = "Collection of commands to fill in the gaps between Terraform, Helm, and Kubectl";
homepage = "https://github.com/gruntwork-io/kubergrunt";
license = licenses.asl20;
maintainers = with maintainers; [ psibi ];
};
}

View file

@ -46,7 +46,7 @@ let
meta = { meta = {
description = "Apache Spark is a fast and general engine for large-scale data processing"; description = "Apache Spark is a fast and general engine for large-scale data processing";
homepage = "http://spark.apache.org"; homepage = "https://spark.apache.org/";
license = lib.licenses.asl20; license = lib.licenses.asl20;
platforms = lib.platforms.all; platforms = lib.platforms.all;
maintainers = with maintainers; [ thoughtpolice offline kamilchm illustris ]; maintainers = with maintainers; [ thoughtpolice offline kamilchm illustris ];

View file

@ -13,9 +13,9 @@
# https://github.com/dmacvicar/terraform-provider-libvirt/tree/main/examples # https://github.com/dmacvicar/terraform-provider-libvirt/tree/main/examples
let let
sha256 = "sha256-8GGPd0+qdw7s4cr0RgLoS0Cu4C+RAuuboZzTyYN/kq8="; sha256 = "sha256-1l+ARrXHxtSdnQfYV/6gw3BYHVH8NN4pi+Ttk1nwF88=";
vendorSha256 = "sha256-fpO2sGM+VUKLmdfJ9CQfTFnCfxVTK2m9Sirj9oerD/I="; vendorSha256 = "sha256-OJa8pQgf5PlECZZkFV9fyCOdh6CrregY1BWycx7JPFE=";
version = "0.6.11"; version = "0.6.12";
in buildGoModule { in buildGoModule {
inherit version; inherit version;
inherit vendorSha256; inherit vendorSha256;
@ -50,7 +50,6 @@ in buildGoModule {
meta = with lib; { meta = with lib; {
homepage = "https://github.com/dmacvicar/terraform-provider-libvirt"; homepage = "https://github.com/dmacvicar/terraform-provider-libvirt";
description = "Terraform provider for libvirt"; description = "Terraform provider for libvirt";
platforms = platforms.linux;
license = licenses.asl20; license = licenses.asl20;
maintainers = with maintainers; [ mic92 ]; maintainers = with maintainers; [ mic92 ];
}; };

View file

@ -1129,10 +1129,10 @@
"owner": "hashicorp", "owner": "hashicorp",
"provider-source-address": "registry.terraform.io/hashicorp/vault", "provider-source-address": "registry.terraform.io/hashicorp/vault",
"repo": "terraform-provider-vault", "repo": "terraform-provider-vault",
"rev": "v3.0.1", "rev": "v3.1.1",
"sha256": "0ppx8kc4zf0yp09vbkmj875sqvklbx0p8a1ganpzdm3462zskra4", "sha256": "15fwc0sfdpcl85194gq6r97y18ggh61wbyh6lq7nrprwn2xvjch9",
"vendorSha256": "03l8bk9jsqf4c7gv0hs1rli7wmlcvpdxmxhra9vndnz6g0jvkvyx", "vendorSha256": "1q2yfmg6g3bl6h0vzanz7874xc4g7ggmysh2dqryijvr4kccnari",
"version": "3.0.1" "version": "3.1.1"
}, },
"vcd": { "vcd": {
"owner": "terraform-providers", "owner": "terraform-providers",

View file

@ -2,13 +2,13 @@
buildGoModule rec { buildGoModule rec {
pname = "terragrunt"; pname = "terragrunt";
version = "0.35.14"; version = "0.35.16";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "gruntwork-io"; owner = "gruntwork-io";
repo = pname; repo = pname;
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-bK1xWzIowNF5gS4feRkCbTB1je/ttbmrqweaHplk8n8="; sha256 = "sha256-m32QhQUG3Dkh0odfqYhNmJ5Rrt0qf5wCvxePPusyRyI=";
}; };
vendorSha256 = "sha256-tNgEepKqwiqXhmoRCIEg7VJw7Y0TGt+R+6dZzd8aECg="; vendorSha256 = "sha256-tNgEepKqwiqXhmoRCIEg7VJw7Y0TGt+R+6dZzd8aECg=";

View file

@ -3,16 +3,16 @@
rustPlatform.buildRustPackage rec { rustPlatform.buildRustPackage rec {
pname = "newsboat"; pname = "newsboat";
version = "2.25"; version = "2.26";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "newsboat"; owner = "newsboat";
repo = "newsboat"; repo = "newsboat";
rev = "r${version}"; rev = "r${version}";
sha256 = "sha256-TAnGDxTKYl4niouS6nYdJDaIngAPsPHr9Bw9L3cR2Xk="; hash = "sha256-VFeKj8X7gEyxsdsOK6UYJ6xB24gsuzb1Wm4GK5AJCHc=";
}; };
cargoSha256 = "sha256-MxoyYBLbrCuLVa0p8JrYKSKu2oFPnXMwab42lhhAu48="; cargoHash = "sha256-pr/Vzm321/uX4fIGt3kuWrtcgsnDRbeK3AvNO19NDwQ=";
# TODO: Check if that's still needed # TODO: Check if that's still needed
postPatch = lib.optionalString stdenv.isDarwin '' postPatch = lib.optionalString stdenv.isDarwin ''
@ -34,9 +34,6 @@ rustPlatform.buildRustPackage rec {
make prefix="$out" make prefix="$out"
''; '';
# TODO: Check if that's still needed
NIX_CFLAGS_COMPILE = lib.optionalString stdenv.isDarwin " -Wno-error=format-security";
# https://github.com/NixOS/nixpkgs/pull/98471#issuecomment-703100014 . We set # https://github.com/NixOS/nixpkgs/pull/98471#issuecomment-703100014 . We set
# these for all platforms, since upstream's gettext crate behavior might # these for all platforms, since upstream's gettext crate behavior might
# change in the future. # change in the future.

View file

@ -3,12 +3,12 @@
with lib; with lib;
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "bitlbee-mastodon"; pname = "bitlbee-mastodon";
version = "1.4.4"; version = "1.4.5";
src = fetchgit { src = fetchgit {
url = "https://alexschroeder.ch/cgit/bitlbee-mastodon"; url = "https://alexschroeder.ch/cgit/bitlbee-mastodon";
rev = "v${version}"; rev = "v${version}";
sha256 = "0a8196pyr6bjnqg82zn7jdhiv7xsg4npbpzalla1i2h99j30q8pk"; sha256 = "sha256-8vmq/YstuBYUxe00P4NrxD/eMYI++R9uvn1sCcMTr7I=";
}; };
nativeBuildInputs = [ autoreconfHook pkg-config ]; nativeBuildInputs = [ autoreconfHook pkg-config ];

View file

@ -4,7 +4,7 @@ with ocamlPackages;
buildDunePackage rec { buildDunePackage rec {
pname = "jackline"; pname = "jackline";
version = "unstable-2021-08-10"; version = "unstable-2021-12-28";
minimumOCamlVersion = "4.08"; minimumOCamlVersion = "4.08";
@ -13,8 +13,8 @@ buildDunePackage rec {
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "hannesm"; owner = "hannesm";
repo = "jackline"; repo = "jackline";
rev = "73d87e9a62d534566bb0fbe64990d32d75487f11"; rev = "ca1012098d123c555e9fa5244466d2e009521700";
sha256 = "0wk574rqfg2vqz27nasxzwf67x51pj5fgl4vkc27r741dg4q6c5a"; sha256 = "1j1azskcdrp4g44rv3a4zylkzbzpcs23zzzrx94llbgssw6cd9ih";
}; };
nativeBuildInpts = [ nativeBuildInpts = [

View file

@ -4,11 +4,11 @@ let
in in
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "rocketchat-desktop"; pname = "rocketchat-desktop";
version = "3.7.1"; version = "3.7.2";
src = fetchurl { src = fetchurl {
url = "https://github.com/RocketChat/Rocket.Chat.Electron/releases/download/${version}/rocketchat_${version}_amd64.deb"; url = "https://github.com/RocketChat/Rocket.Chat.Electron/releases/download/${version}/rocketchat_${version}_amd64.deb";
sha256 = "1l4g0y7kb569w1i3c1bq6m0p9wykrf7k6a59j5cvnkl1b9h8mj4p"; sha256 = "sha256-sSSi8L5WXFAc9yRDZ2usWmh6F06/1RMbJInQ4/OORnI=";
}; };
nativeBuildInputs = [ nativeBuildInputs = [

View file

@ -6,23 +6,24 @@ with lib;
perlPackages.buildPerlPackage rec { perlPackages.buildPerlPackage rec {
pname = "convos"; pname = "convos";
version = "6.26"; version = "6.42";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "convos-chat"; owner = "convos-chat";
repo = pname; repo = pname;
rev = "v${version}"; rev = "v${version}";
sha256 = "1wh3ryhd4b7nanh0yp2nycmhky5afw8lpfx34858p6wfimsv9794"; sha256 = "sha256-W7ZVZUCNllpFIQpNz2m/8VFOXDZfuppB+g3qibY6wt8=";
}; };
nativeBuildInputs = [ makeWrapper ] nativeBuildInputs = [ makeWrapper ]
++ optional stdenv.isDarwin [ shortenPerlShebang ]; ++ optional stdenv.isDarwin [ shortenPerlShebang ];
buildInputs = with perlPackages; [ buildInputs = with perlPackages; [
CryptEksblowfish FileHomeDir FileReadBackwards HTTPAcceptLanguage CryptPassphrase CryptPassphraseArgon2 CryptPassphraseBcrypt
FileHomeDir FileReadBackwards HTTPAcceptLanguage
IOSocketSSL IRCUtils JSONValidator LinkEmbedder ModuleInstall IOSocketSSL IRCUtils JSONValidator LinkEmbedder ModuleInstall
Mojolicious MojoliciousPluginOpenAPI MojoliciousPluginWebpack Mojolicious MojoliciousPluginOpenAPI MojoliciousPluginSyslog MojoliciousPluginWebpack
ParseIRC TextMarkdown TimePiece UnicodeUTF8 ParseIRC TextMarkdownHoedown TimePiece UnicodeUTF8
CpanelJSONXS EV CpanelJSONXS EV
]; ];

View file

@ -3,7 +3,7 @@
, heimdal, krb5, libsoup, libvorbis, speex, openssl, zlib, xorg, pango, gtk2 , heimdal, krb5, libsoup, libvorbis, speex, openssl, zlib, xorg, pango, gtk2
, gnome2, mesa, nss, nspr, gtk_engines, freetype, dconf, libpng12, libxml2 , gnome2, mesa, nss, nspr, gtk_engines, freetype, dconf, libpng12, libxml2
, libjpeg, libredirect, tzdata, cacert, systemd, libcxxabi, libcxx, e2fsprogs, symlinkJoin , libjpeg, libredirect, tzdata, cacert, systemd, libcxxabi, libcxx, e2fsprogs, symlinkJoin
, libpulseaudio, pcsclite, glib-networking , libpulseaudio, pcsclite, glib-networking, llvmPackages_12
, homepage, version, prefix, hash , homepage, version, prefix, hash
@ -99,7 +99,8 @@ stdenv.mkDerivation rec {
xorg.libXtst xorg.libXtst
zlib zlib
] ++ lib.optional (lib.versionOlder version "20.04") e2fsprogs ] ++ lib.optional (lib.versionOlder version "20.04") e2fsprogs
++ lib.optional (lib.versionAtLeast version "20.10") libpulseaudio; ++ lib.optional (lib.versionAtLeast version "20.10") libpulseaudio
++ lib.optional (lib.versionAtLeast version "21.12") llvmPackages_12.libunwind;
runtimeDependencies = [ runtimeDependencies = [
glib glib
@ -120,10 +121,11 @@ stdenv.mkDerivation rec {
installPhase = let installPhase = let
icaFlag = program: icaFlag = program:
if (builtins.match "selfservice(.*)" program) != null then "--icaroot" if (builtins.match "selfservice(.*)" program) != null then "--icaroot"
else if (lib.versionAtLeast version "21.12" && builtins.match "wfica(.*)" program != null) then null
else "-icaroot"; else "-icaroot";
wrap = program: '' wrap = program: ''
wrapProgram $out/opt/citrix-icaclient/${program} \ wrapProgram $out/opt/citrix-icaclient/${program} \
--add-flags "${icaFlag program} $ICAInstDir" \ ${lib.optionalString (icaFlag program != null) ''--add-flags "${icaFlag program} $ICAInstDir"''} \
--set ICAROOT "$ICAInstDir" \ --set ICAROOT "$ICAInstDir" \
--prefix LD_LIBRARY_PATH : "$ICAInstDir:$ICAInstDir/lib" \ --prefix LD_LIBRARY_PATH : "$ICAInstDir:$ICAInstDir/lib" \
--set LD_PRELOAD "${libredirect}/lib/libredirect.so" \ --set LD_PRELOAD "${libredirect}/lib/libredirect.so" \

View file

@ -122,6 +122,17 @@ let
x86suffix = "25"; x86suffix = "25";
homepage = "https://www.citrix.com/downloads/workspace-app/linux/workspace-app-for-linux-latest.html"; homepage = "https://www.citrix.com/downloads/workspace-app/linux/workspace-app-for-linux-latest.html";
}; };
"21.12.0" = {
major = "21";
minor = "12";
patch = "0";
x64hash = "de81deab648e1ebe0ddb12aa9591c8014d7fad4eba0db768f25eb156330bb34d";
x86hash = "3746cdbe26727f7f6fb85fbe5f3e6df0322d79bb66e3a70158b22cb4f6b6b292";
x64suffix = "18";
x86suffix = "18";
homepage = "https://www.citrix.com/downloads/workspace-app/linux/workspace-app-for-linux-latest.html";
};
}; };
# Retain attribute-names for abandoned versions of Citrix workspace to # Retain attribute-names for abandoned versions of Citrix workspace to

View file

@ -19,8 +19,11 @@ python3Packages.buildPythonApplication rec {
propagatedBuildInputs = [ gtk3 gdk-pixbuf gobject-introspection ] propagatedBuildInputs = [ gtk3 gdk-pixbuf gobject-introspection ]
++ (with python3Packages; [ pygobject3 ]); ++ (with python3Packages; [ pygobject3 ]);
postInstall = '' postInstall = ''
mv $out/bin/nicotine $out/bin/nicotine-plus ln -s $out/bin/nicotine $out/bin/nicotine-plus
test -e $out/share/applications/org.nicotine_plus.Nicotine.desktop && exit 1
install -D data/org.nicotine_plus.Nicotine.desktop -t $out/share/applications
''; '';
preFixup = '' preFixup = ''

View file

@ -14,13 +14,13 @@
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "unison"; pname = "unison";
version = "2.51.4"; version = "2.51.5";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "bcpierce00"; owner = "bcpierce00";
repo = "unison"; repo = "unison";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-jcfq4X+r98bQqbQ3gRqJyryLdt1Y/2CLawqqIiUaQOo="; sha256 = "sha256-pi5uYwPpIy0lERmgATWQCO3EA3Pg5pnn7gxv49FaPug=";
}; };
nativeBuildInputs = [ makeWrapper ] nativeBuildInputs = [ makeWrapper ]

View file

@ -1,12 +1,17 @@
{lib, stdenvNoCC, haskellPackages, fetchurl, writers}: {lib, stdenvNoCC, haskellPackages, fetchurl, writers}:
let
hledger-lib = haskellPackages.hledger-lib_1_24_1;
in
stdenvNoCC.mkDerivation rec { stdenvNoCC.mkDerivation rec {
pname = "hledger-check-fancyassertions"; pname = "hledger-check-fancyassertions";
version = "1.23"; inherit (hledger-lib) version;
src = fetchurl { src = fetchurl {
name = "hledger-check-fancyassertion-${version}.hs";
url = "https://raw.githubusercontent.com/simonmichael/hledger/hledger-lib-${version}/bin/hledger-check-fancyassertions.hs"; url = "https://raw.githubusercontent.com/simonmichael/hledger/hledger-lib-${version}/bin/hledger-check-fancyassertions.hs";
sha256 = "08p2din1j7l4c29ipn68k8vvs3ys004iy8a3zf318lzby4h04h0n"; sha256 = "0naggvivc6szsc8haa52a6lm079ikz5qfva0ljnqx0f1zlkxv984";
}; };
dontUnpack = true; dontUnpack = true;
@ -15,11 +20,13 @@ stdenvNoCC.mkDerivation rec {
executable = writers.writeHaskell executable = writers.writeHaskell
"hledger-check-fancyassertions" "hledger-check-fancyassertions"
{ {
libraries = with haskellPackages; [ libraries = [
base base-compat base-compat-batteries filepath hledger-lib_1_24 hledger-lib
] ++ (with haskellPackages; [
base base-compat base-compat-batteries filepath
megaparsec microlens optparse-applicative string-qq text time megaparsec microlens optparse-applicative string-qq text time
transformers transformers
]; ]);
inherit (haskellPackages) ghc; inherit (haskellPackages) ghc;
} }
src; src;

View file

@ -1,15 +1,12 @@
{ stdenv , lib , fetchurl , appimageTools , makeWrapper , electron_11 }: { stdenv , lib , fetchurl , appimageTools , makeWrapper , electron }:
let
electron = electron_11;
in
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "super-productivity"; pname = "super-productivity";
version = "7.6.0"; version = "7.9.1";
src = fetchurl { src = fetchurl {
url = "https://github.com/johannesjo/super-productivity/releases/download/v${version}/superProductivity-${version}.AppImage"; url = "https://github.com/johannesjo/super-productivity/releases/download/v${version}/superProductivity-${version}.AppImage";
sha256 = "f02a451a44f48a8e85a0c1269625d89fb1e0b8a75b7e217d96352064e6464ae5"; sha256 = "sha256:0lxnl5ai23dwfsyrkpi9l1a0gl0qn6vp7hzmca77nyx974d6j8m4";
name = "${pname}-${version}.AppImage"; name = "${pname}-${version}.AppImage";
}; };

View file

@ -2,7 +2,7 @@
, stdenv , stdenv
, fetchurl , fetchurl
, hamlib , hamlib
, fltk14 , fltk13
, libjpeg , libjpeg
, libpng , libpng
, portaudio , portaudio
@ -31,7 +31,7 @@ stdenv.mkDerivation rec {
libXinerama libXinerama
gettext gettext
hamlib hamlib
fltk14 fltk13
libjpeg libjpeg
libpng libpng
portaudio portaudio
@ -39,11 +39,16 @@ stdenv.mkDerivation rec {
libsamplerate libsamplerate
] ++ lib.optionals (stdenv.isLinux) [ libpulseaudio alsa-lib udev ]; ] ++ lib.optionals (stdenv.isLinux) [ libpulseaudio alsa-lib udev ];
enableParallelBuilding = true;
meta = with lib; { meta = with lib; {
description = "Digital modem program"; description = "Digital modem program";
homepage = "https://sourceforge.net/projects/fldigi/"; homepage = "https://sourceforge.net/projects/fldigi/";
license = licenses.gpl3Plus; license = licenses.gpl3Plus;
maintainers = with maintainers; [ relrod ftrvxmtrx ]; maintainers = with maintainers; [ relrod ftrvxmtrx ];
platforms = platforms.unix; platforms = platforms.unix;
# unable to execute command: posix_spawn failed: Argument list too long
# Builds fine on aarch64-darwin
broken = stdenv.system == "x86_64-darwin";
}; };
} }

View file

@ -2,16 +2,16 @@
buildGoModule rec { buildGoModule rec {
pname = "flex-ncat"; pname = "flex-ncat";
version = "0.0-20210420.0"; version = "0.1-20211223.0";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "kc2g-flex-tools"; owner = "kc2g-flex-tools";
repo = "nCAT"; repo = "nCAT";
rev = "v${version}"; rev = "v${version}";
sha256 = "0wrdmlp9rrr4n0g9pj0j20ddskllyr59dr3p5fm9z0avkncn3a0m"; hash = "sha256-l5IH6EtWqxMLqUfIYpaKgZE9Jq8q4+WgZIazQ2scyxg=";
}; };
vendorSha256 = "0npzhvpyaxvfaivycnscvh45lp0ycdg9xrlfm8vhfr835yj2adiv"; vendorSha256 = "sha256-OzYlpC8DZQc3qo7mnl5jHlxaCNxMW+Z3VG535e+G/1o=";
meta = with lib; { meta = with lib; {
homepage = "https://github.com/kc2g-flex-tools/nCAT"; homepage = "https://github.com/kc2g-flex-tools/nCAT";

View file

@ -1,5 +1,6 @@
{ lib, fetchFromGitHub, python3Packages { lib, fetchFromGitHub, python3Packages
, hackrf, rtl-sdr, airspy, limesuite, libiio , hackrf, rtl-sdr, airspy, limesuite, libiio
, libbladeRF
, qt5 , qt5
, USRPSupport ? false, uhd }: , USRPSupport ? false, uhd }:
@ -15,7 +16,7 @@ python3Packages.buildPythonApplication rec {
}; };
nativeBuildInputs = [ qt5.wrapQtAppsHook ]; nativeBuildInputs = [ qt5.wrapQtAppsHook ];
buildInputs = [ hackrf rtl-sdr airspy limesuite libiio ] buildInputs = [ hackrf rtl-sdr airspy limesuite libiio libbladeRF ]
++ lib.optional USRPSupport uhd; ++ lib.optional USRPSupport uhd;
propagatedBuildInputs = with python3Packages; [ propagatedBuildInputs = with python3Packages; [

View file

@ -4,12 +4,12 @@
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "wsjtx"; pname = "wsjtx";
version = "2.5.2"; version = "2.5.3";
# This is a "superbuild" tarball containing both wsjtx and a hamlib fork # This is a "superbuild" tarball containing both wsjtx and a hamlib fork
src = fetchurl { src = fetchurl {
url = "http://physics.princeton.edu/pulsar/k1jt/wsjtx-${version}.tgz"; url = "http://physics.princeton.edu/pulsar/k1jt/wsjtx-${version}.tgz";
sha256 = "sha256-4KSJYhfUya8nH1KTsZ7JRgh0KnKdqrgSfofsjWaX7/M="; sha256 = "sha256-Dd99JBPn1TgPF8Yvqk+AZX8ZUuQjYS0Tx3y5A3VZsHw=";
}; };
# Hamlib builds with autotools, wsjtx builds with cmake # Hamlib builds with autotools, wsjtx builds with cmake

View file

@ -2,13 +2,13 @@
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "minimap2"; pname = "minimap2";
version = "2.22"; version = "2.23";
src = fetchFromGitHub { src = fetchFromGitHub {
repo = pname; repo = pname;
owner = "lh3"; owner = "lh3";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-jYXJr2T1enZfSABVV5Kmd5OBtWZtQ2D/2eAlW2WHtGU="; sha256 = "sha256-oNVpSINcXO2eKzOCr/Fl8tSMguRxzmlDNu7hLZeopoQ=";
}; };
buildInputs = [ zlib ]; buildInputs = [ zlib ];

View file

@ -35,7 +35,6 @@
, swig , swig
, python , python
, wxPython , wxPython
, opencascade
, opencascade-occt , opencascade-occt
, libngspice , libngspice
, valgrind , valgrind
@ -44,22 +43,16 @@
, baseName , baseName
, kicadSrc , kicadSrc
, kicadVersion , kicadVersion
, i18n
, withOCE
, withOCC , withOCC
, withNgspice , withNgspice
, withScripting , withScripting
, withI18n
, withPCM
, debug , debug
, sanitizeAddress , sanitizeAddress
, sanitizeThreads , sanitizeThreads
, withI18n
}: }:
assert lib.asserts.assertMsg (!(withOCE && stdenv.isAarch64)) "OCE fails a test on Aarch64";
assert lib.asserts.assertMsg (!(withOCC && withOCE))
"Only one of OCC and OCE may be enabled";
assert lib.assertMsg (!(stable && (sanitizeAddress || sanitizeThreads)))
"Only kicad-unstable(-small) supports address/thread sanitation";
assert lib.assertMsg (!(sanitizeAddress && sanitizeThreads)) assert lib.assertMsg (!(sanitizeAddress && sanitizeThreads))
"'sanitizeAddress' and 'sanitizeThreads' are mutually exclusive, use one."; "'sanitizeAddress' and 'sanitizeThreads' are mutually exclusive, use one.";
@ -75,6 +68,7 @@ stdenv.mkDerivation rec {
# tagged releases don't have "unknown" # tagged releases don't have "unknown"
# kicad nightlies use git describe --dirty # kicad nightlies use git describe --dirty
# nix removes .git, so its approximated here # nix removes .git, so its approximated here
# "6.99.0" doesn't have "-unknown", yet; so leaving this in case it returns
postPatch = '' postPatch = ''
substituteInPlace CMakeModules/KiCadVersion.cmake \ substituteInPlace CMakeModules/KiCadVersion.cmake \
--replace "unknown" "${builtins.substring 0 10 src.rev}" \ --replace "unknown" "${builtins.substring 0 10 src.rev}" \
@ -82,23 +76,14 @@ stdenv.mkDerivation rec {
makeFlags = optionals (debug) [ "CFLAGS+=-Og" "CFLAGS+=-ggdb" ]; makeFlags = optionals (debug) [ "CFLAGS+=-Og" "CFLAGS+=-ggdb" ];
cmakeFlags = optionals (stable && withScripting) [ cmakeFlags = optionals (withScripting) [
"-DKICAD_SCRIPTING=ON" "-DKICAD_SCRIPTING_WXPYTHON=ON"
"-DKICAD_SCRIPTING_MODULES=ON"
"-DKICAD_SCRIPTING_PYTHON3=ON"
"-DKICAD_SCRIPTING_WXPYTHON_PHOENIX=ON"
] ]
++ optionals (!withScripting) [ ++ optionals (!withScripting) [
"-DKICAD_SCRIPTING=OFF"
"-DKICAD_SCRIPTING_WXPYTHON=OFF" "-DKICAD_SCRIPTING_WXPYTHON=OFF"
] ]
++ optional (withNgspice) "-DKICAD_SPICE=ON" ++ optional (!withNgspice) "-DKICAD_SPICE=OFF"
++ optional (!withOCE) "-DKICAD_USE_OCE=OFF"
++ optional (!withOCC) "-DKICAD_USE_OCC=OFF" ++ optional (!withOCC) "-DKICAD_USE_OCC=OFF"
++ optionals (withOCE) [
"-DKICAD_USE_OCE=ON"
"-DOCE_DIR=${opencascade}"
]
++ optionals (withOCC) [ ++ optionals (withOCC) [
"-DKICAD_USE_OCC=ON" "-DKICAD_USE_OCC=ON"
"-DOCC_INCLUDE_DIR=${opencascade-occt}/include/opencascade" "-DOCC_INCLUDE_DIR=${opencascade-occt}/include/opencascade"
@ -108,11 +93,20 @@ stdenv.mkDerivation rec {
"-DKICAD_STDLIB_DEBUG=ON" "-DKICAD_STDLIB_DEBUG=ON"
"-DKICAD_USE_VALGRIND=ON" "-DKICAD_USE_VALGRIND=ON"
] ]
++ optionals (!doInstallCheck) [
"-DKICAD_BUILD_QA_TESTS=OFF"
]
++ optionals (sanitizeAddress) [ ++ optionals (sanitizeAddress) [
"-DKICAD_SANITIZE_ADDRESS=ON" "-DKICAD_SANITIZE_ADDRESS=ON"
] ]
++ optionals (sanitizeThreads) [ ++ optionals (sanitizeThreads) [
"-DKICAD_SANITIZE_THREADS=ON" "-DKICAD_SANITIZE_THREADS=ON"
]
++ optionals (withI18n) [
"-DKICAD_BUILD_I18N=ON"
]
++ optionals (!withPCM) [
"-DKICAD_PCM=OFF"
]; ];
nativeBuildInputs = [ nativeBuildInputs = [
@ -154,34 +148,27 @@ stdenv.mkDerivation rec {
curl curl
openssl openssl
boost boost
swig
python
] ]
# unstable requires swig and python
# wxPython still optional
++ optionals (withScripting || (!stable)) [ swig python ]
++ optional (withScripting) wxPython ++ optional (withScripting) wxPython
++ optional (withNgspice) libngspice ++ optional (withNgspice) libngspice
++ optional (withOCE) opencascade
++ optional (withOCC) opencascade-occt ++ optional (withOCC) opencascade-occt
++ optional (debug) valgrind ++ optional (debug) valgrind
; ;
# debug builds fail all but the python test # debug builds fail all but the python test
# 5.1.x fails the eeschema test #doInstallCheck = !debug;
doInstallCheck = !debug && !stable; # temporarily disabled until upstream issue 9888 is resolved
doInstallCheck = false;
installCheckTarget = "test"; installCheckTarget = "test";
dontStrip = debug; dontStrip = debug;
postInstall = optionalString (withI18n) ''
mkdir -p $out/share
lndir ${i18n}/share $out/share
'';
meta = { meta = {
description = "Just the built source without the libraries"; description = "Just the built source without the libraries";
longDescription = '' longDescription = ''
Just the build products, optionally with the i18n linked in Just the build products, the libraries are passed via an env var in the wrapper, default.nix
the libraries are passed via an env var in the wrapper, default.nix
''; '';
homepage = "https://www.kicad.org/"; homepage = "https://www.kicad.org/";
license = lib.licenses.agpl3; license = lib.licenses.agpl3;

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