-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
47cf021
commit 7e9ae9e
Showing
1 changed file
with
86 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,26 @@ | ||
flake: { config, lib, pkgs, ... }: | ||
|
||
let | ||
inherit (lib) types strings mdDoc mkOption mkIf mkMerge ; | ||
inherit (lib) types strings mdDoc mkOption mkIf mkMerge; | ||
|
||
inherit (flake.packages.${pkgs.stdenv.hostPlatform.system}) cryptpad; | ||
|
||
cfg = config.services.cryptpad; | ||
|
||
# The Cryptpad configuration file isn't JSON, but a JavaScript source file that assigns a JSON value | ||
# to a variable. | ||
# The Cryptpad configuration file is not JSON, but a JavaScript source file that assigns a JSON configuration | ||
# object to a variable and exports it. | ||
configFile = builtins.toFile "cryptpad_config.js" '' | ||
module.exports = ${builtins.toJSON cfg.config} | ||
''; | ||
|
||
# Derive domain names for Nginx configuration from Cryptpad configuration | ||
mainDomain = strings.removePrefix "https://" cfg.config.httpUnsafeOrigin; | ||
sandboxDomain = if isNull cfg.config.httpSafeOrigin then mainDomain else strings.removePrefix "https://" cfg.config.httpSafeOrigin; | ||
sandboxDomain = | ||
if cfg.config.httpSafeOrigin == null then mainDomain else strings.removePrefix "https://" cfg.config.httpSafeOrigin; | ||
|
||
in | ||
{ | ||
# Some workaround due to cryptpad being a disabled module name: | ||
# Some workaround due to `cryptpad` being a disabled module name: | ||
# Disabling the rename.nix module is necessary to be able to use the name 'cryptpad' | ||
disabledModules = [ "rename.nix" ]; | ||
# The following import is necessary when disabling the rename.nix module above | ||
|
@@ -45,102 +46,132 @@ in | |
default = false; | ||
description = mdDoc '' | ||
Configure Nginx as a reverse proxy for Cryptpad. | ||
Note that this makes some assumptions on your setup, and sets settings that will | ||
Note that this makes some assumptions on your setup, and configures settings that will | ||
affect other virtualHosts running on your Nginx instance, if any. | ||
Alternatively you can configure a reverse-proxy of your choice. | ||
''; | ||
}; | ||
|
||
confinement = mkOption { | ||
type = types.bool; | ||
default = false; | ||
clientMaxBodySize = mkOption { | ||
default = "150m"; | ||
type = types.str; | ||
description = mdDoc '' | ||
Value for the Nginx client_max_body_size header. Only relevant if `configureNginx` is `true`. | ||
''; | ||
}; | ||
|
||
hstsMaxAge = mkOption { | ||
type = types.ints.positive; | ||
default = 63072000; | ||
description = mdDoc '' | ||
FIXME: Enables 'confinement' (explain what it does?) | ||
Enable the nixos systemd.services.cryptpad.confinement setting, including the necessary | ||
extra bind mounts. | ||
Value for the `max-age` directive of the HTTP `Strict-Transport-Security` header. | ||
Only relevant if `configureNginx` is `true`. | ||
See section 6.1.1 of IETF RFC 6797 for detailed information on this | ||
directive and header. | ||
''; | ||
}; | ||
|
||
config = mkOption { | ||
type = types.submodule { | ||
freeformType = (pkgs.formats.json {}).type; | ||
freeformType = (pkgs.formats.json { }).type; | ||
options = { | ||
httpUnsafeOrigin = mkOption { | ||
type = types.str; | ||
example = "https://cryptpad.example.com"; | ||
default = ""; | ||
description = mdDoc "This is the URL that users will enter to load your instance"; | ||
description = mdDoc "This is the URL that users will enter to load your instance."; | ||
}; | ||
httpSafeOrigin = mkOption { | ||
type = types.nullOr types.str; | ||
example = "https://cryptpad-ui.example.com. Apparently optional but recommended."; | ||
description = mdDoc "Cryptpad sandbox URL"; | ||
example = "https://cryptpad-ui.example.com"; | ||
description = mdDoc '' | ||
This is the URL that is used for the 'sandbox' described in the Cryptpad documentation. | ||
''; | ||
}; | ||
httpAddress = mkOption { | ||
type = types.str; | ||
default = "127.0.0.1"; | ||
description = mdDoc "Address on which the Node.js server should listen"; | ||
description = mdDoc "Address on which the Node.js server should listen."; | ||
}; | ||
httpPort = mkOption { | ||
type = types.int; | ||
default = 3000; | ||
description = mdDoc "Port on which the Node.js server should listen"; | ||
description = mdDoc "Port on which the Node.js server should listen."; | ||
}; | ||
maxWorkers = mkOption { | ||
type = types.nullOr types.int; | ||
default = null; | ||
description = mdDoc "Number of child processes, defaults to number of cores available"; | ||
description = mdDoc "Number of child processes, defaults to number of cores available."; | ||
}; | ||
adminKeys = mkOption { | ||
type = types.listOf types.str; | ||
default = []; | ||
description = mdDoc "List of public signing keys of users that can access the admin panel"; | ||
default = [ ]; | ||
description = mdDoc "List of public signing keys of users that can access the admin panel."; | ||
example = [ "[[email protected]/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=]" ]; | ||
}; | ||
filePath = mkOption { | ||
type = types.str; | ||
default = "./datastore/"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where files are stored. | ||
''; | ||
}; | ||
archivePath = mkOption { | ||
type = types.str; | ||
default = "./data/archive"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where archived data is stored. | ||
''; | ||
}; | ||
pinPath = mkOption { | ||
type = types.str; | ||
default = "./data/pins"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where pinned documents are stored. | ||
''; | ||
}; | ||
taskPath = mkOption { | ||
type = types.str; | ||
default = "./data/tasks"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where scheduled tasks are stored. | ||
''; | ||
}; | ||
blockPath = mkOption { | ||
type = types.str; | ||
default = "./block"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where users' authenticated blocks are stored. | ||
''; | ||
}; | ||
blobPath = mkOption { | ||
type = types.str; | ||
default = "./blob"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where encrypted blobs are stored. | ||
''; | ||
}; | ||
blobStagingPath = mkOption { | ||
type = types.str; | ||
default = "./data/blobstage"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where incomplete blobs are stored. | ||
''; | ||
}; | ||
decreePath = mkOption { | ||
type = types.str; | ||
default = "./data/decrees"; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Specifies the directory where decrees are stored. | ||
''; | ||
}; | ||
logPath = mkOption { | ||
type = types.str; | ||
default = "./data/logs"; | ||
description = mdDoc "FIXME"; | ||
type = types.oneOf [ types.str types.bool]; | ||
default = false; | ||
description = mdDoc '' | ||
Specifies the directory where logs are stored. Set to false (or nothing) if you'd rather | ||
not log. | ||
''; | ||
}; | ||
logToStdout = mkOption { | ||
type = types.bool; | ||
|
@@ -155,19 +186,21 @@ in | |
logFeedback = mkOption { | ||
type = types.bool; | ||
default = false; | ||
description = mdDoc "FIXME"; | ||
description = mdDoc '' | ||
Provide usage feedback to the Cryptpad admin. | ||
''; | ||
}; | ||
verbose = mkOption { | ||
type = types.bool; | ||
default = false; | ||
description = mdDoc "Controls verbose logging"; | ||
description = mdDoc "Controls verbose logging."; | ||
}; | ||
installMethod = mkOption { | ||
type = types.str; | ||
default = "nixos"; | ||
description = mdDoc '' | ||
Install method is listed in telemetry if you agree to it through the consentToContact | ||
setting in the admin panel. | ||
Install method information included in server telemetry to voluntarily indicate how many | ||
instances are using unofficial installation methods such as Nix. | ||
''; | ||
}; | ||
blockDailyCheck = mkOption { | ||
|
@@ -192,11 +225,13 @@ in | |
|
||
config = mkIf cfg.enable (mkMerge [ | ||
{ | ||
users.users.cryptpad = { | ||
isSystemUser = true; | ||
group = "cryptpad"; | ||
users = { | ||
users.cryptpad = { | ||
isSystemUser = true; | ||
group = "cryptpad"; | ||
}; | ||
groups.cryptpad = { }; | ||
}; | ||
users.groups.cryptpad = { }; | ||
|
||
systemd.services.cryptpad = { | ||
description = "Cryptpad service"; | ||
|
@@ -216,71 +251,38 @@ in | |
}; | ||
}; | ||
} | ||
(mkIf cfg.confinement { | ||
systemd.services.cryptpad = { | ||
serviceConfig.BindReadOnlyPaths = [ | ||
configFile | ||
# apparently needs proc for workers management | ||
"/proc" | ||
"/dev/urandom" | ||
] ++ (if ! cfg.config.blockDailyCheck then [ | ||
"/etc/resolv.conf" | ||
"/etc/hosts" | ||
] else []); | ||
confinement = { | ||
enable = true; | ||
binSh = null; | ||
mode = "chroot-only"; | ||
}; | ||
}; | ||
}) | ||
|
||
(mkIf cfg.configureNginx { | ||
assertions = [ | ||
{ assertion = cfg.config.httpUnsafeOrigin != ""; | ||
{ | ||
assertion = cfg.config.httpUnsafeOrigin != ""; | ||
message = "services.cryptpad.config.httpUnsafeOrigin is required"; | ||
} | ||
{ assertion = strings.hasPrefix "https://" cfg.config.httpUnsafeOrigin; | ||
{ | ||
assertion = strings.hasPrefix "https://" cfg.config.httpUnsafeOrigin; | ||
message = "services.cryptpad.config.httpUnsafeOrigin must start with https://"; | ||
} | ||
{ assertion = isNull cfg.config.httpSafeOrigin || strings.hasPrefix "https://" cfg.config.httpSafeOrigin; | ||
{ | ||
assertion = cfg.config.httpSafeOrigin == null || strings.hasPrefix "https://" cfg.config.httpSafeOrigin; | ||
message = "services.cryptpad.config.httpSafeOrigin must start with https:// (or be unset)"; | ||
} | ||
]; | ||
|
||
services.nginx = { | ||
enable = true; | ||
recommendedTlsSettings = true; | ||
|
||
# FIXME: Check / compare this with [Nginx module configuration in nixpkgs](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/modules/services/web-servers/nginx/default.nix). | ||
# Find out why Cryptpad has this in their documetation. Does this decrease the security of a Cryptpad install | ||
# if not used? | ||
# diffie-hellman parameters are used to negotiate keys for your session | ||
# generate strong parameters using the following command | ||
# ssl_dhparam /etc/nginx/dhparam.pem; # openssl dhparam -out /etc/nginx/dhparam.pem 4096 | ||
# | ||
# sslDhparam = null; | ||
|
||
# FIXME: Check / compare this with [Nginx module configuration in nixpkgs](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/modules/services/web-servers/nginx/default.nix). | ||
# Find out why Cryptpad has this in their documentation. Does this decrease the security of a Cryptpad install | ||
# if not used? | ||
# replace with the IP address of your resolver | ||
# resolver 8.8.8.8 8.8.4.4 1.1.1.1 1.0.0.1 9.9.9.9 149.112.112.112 208.67.222.222 208.67.220.220; | ||
# | ||
# resolver = {}; | ||
|
||
virtualHosts = mkMerge [ | ||
{ | ||
"${mainDomain}" = { | ||
serverAliases = if isNull cfg.config.httpSafeOrigin then [ ] else [ sandboxDomain ]; | ||
# NOTE: I see no reason not to enable ACME and forcing SSL if you enable Nginx for | ||
# Cryptpad, IMHO. Given the security context of Cryptpad, it should only ever be used with SSL. | ||
serverAliases = lib.optionals (cfg.config.httpSafeOrigin != null) [ sandboxDomain ]; | ||
enableACME = true; | ||
forceSSL = true; | ||
locations."/" = { | ||
proxyPass = "http://${cfg.config.httpAddress}:${builtins.toString cfg.config.httpPort}"; | ||
proxyWebsockets = true; | ||
extraConfig = '' | ||
client_max_body_size 150m; | ||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; | ||
client_max_body_size ${cfg.clientMaxBodySize}; | ||
add_header Strict-Transport-Security "max-age=${toString cfg.hstsMaxAge}; includeSubDomains" always; | ||
''; | ||
}; | ||
}; | ||
|