Compare commits
203 Commits
Author | SHA1 | Date |
---|---|---|
Alexander | 94d016e5f5 | |
Inex Code | 132b6724cd | |
Inex Code | 86182f0799 | |
Inex Code | ce3231774e | |
Inex Code | f8223192e5 | |
Alexander | 4c183d5b40 | |
Inex Code | 5e005dc436 | |
Inex Code | 6619760f47 | |
Inex Code | 3b4466b49d | |
Inex Code | 5d9c385d08 | |
Inex Code | 3a7876aeb4 | |
Inex Code | c7583bf501 | |
Alexander | ad43d31c0c | |
Inex Code | 2159c4cc6e | |
Inex Code | 3d6f47e0e7 | |
Inex Code | f103f708da | |
Alexander Tomokhov | 2d4e7e89a1 | |
Alexander | d389b6785f | |
Inex Code | a8aca42762 | |
Alexander | f9b86f403d | |
Alexander | 643f7716ae | |
Alexander | c96e6fe099 | |
Alexander | 842d5a0dd1 | |
Alexander | 3311c5ff50 | |
Alexander | 6b66513870 | |
Alexander | c08d56ae1c | |
Alexander | e8842e455b | |
Alexander | 1a677f273b | |
Inex Code | 94456c8bd8 | |
Inex Code | 3e0eae6d7d | |
Inex Code | c0af154421 | |
Inex Code | f287e68f2b | |
Inex Code | f1e789c7af | |
Alexander | 3d8de64da0 | |
Alexander | 1e73c88547 | |
Alexander | 51f3f12640 | |
Alexander | 013bd9b8e2 | |
Alexander | 15f5d6096d | |
Alexander | a32613ece4 | |
Alexander | 0c895e4015 | |
Alexander | 08aa0b9ffc | |
Alexander | be45d3ed52 | |
Alexander | efc703bf0c | |
Alexander | cc78c2915f | |
Alexander | fe44ba6fd8 | |
Alexander | 77619456d7 | |
Alexander | f94d0aef03 | |
Alexander | 23332cda46 | |
Alexander | 85f85239a3 | |
Alexander | 33ba5c41ac | |
Alexander | 5bd15a768a | |
Alexander | a185dd1e3e | |
Alexander | e6496b95a4 | |
Alexander | 5aba990f95 | |
Alexander | 05fe40ac21 | |
Alexander | 19f30daf80 | |
Alexander | 5f8cc727e0 | |
Alexander | 64fc2ae57e | |
Alexander | 66c0184a93 | |
Alexander | 4c3072ade8 | |
Alexander | 0e62c9292b | |
Alexander | 5760a753af | |
Alexander | f2a951a71e | |
Alexander | fd6e49a21a | |
Alexander | dcaf96c773 | |
Alexander | 3a66da49e1 | |
Alexander | 5cd12848cc | |
Alexander | 4faf8e7dda | |
Alexander | c18f332f5f | |
Alexander | 46366702bc | |
Alexander | 0c4d57c33d | |
Alexander | 426e6f72c5 | |
Alexander | eb59d33e1f | |
Alexander | b37cadff68 | |
Inex Code | 312077240a | |
Alexander | 69f84cdc2b | |
Alexander | 0ad2ffc30e | |
Alexander | 83a17063ac | |
Alexander | 257b0c08e8 | |
Alexander | da5dac6877 | |
Alexander | 3d7aa5e6de | |
Alexander | 365f027326 | |
Alexander | d881cc8ce5 | |
Alexander | b7045a8198 | |
Alexander | 67c2b12c44 | |
Alexander | 83e8f6e8a1 | |
Alexander | 7f6c48f978 | |
Alexander | a797b856fc | |
Alexander | d1711ea9c3 | |
Alexander | defaca8793 | |
Alexander | 25bd151ef3 | |
Alexander | 519ebbcb69 | |
Alexander | 4c4aef5363 | |
Alexander | 9dde55159b | |
Alexander | 6cd002ae1d | |
Alexander | c052f9172a | |
Alexander | 054d6d9182 | |
Alexander | 3f573e3dc3 | |
Alexander | c63b6b808c | |
Alexander Tomokhov | c0aa73ca1b | |
Alexander Tomokhov | ade4dc08b1 | |
Alexander Tomokhov | 4716b9bf19 | |
Alexander Tomokhov | c7419b3255 | |
Alexander Tomokhov | b458458c30 | |
Alexander Tomokhov | 4cbe63ac64 | |
Alexander Tomokhov | 600d8f427d | |
Alexander Tomokhov | b6cd5846f2 | |
Alexander Tomokhov | 1a5a4be306 | |
Alexander Tomokhov | 364a5c8076 | |
Alexander Tomokhov | a224731dcf | |
Alexander Tomokhov | e814157437 | |
Alexander Tomokhov | 0db1c4a6ce | |
Alexander Tomokhov | a98dafc98c | |
Alexander Tomokhov | 3138260605 | |
Alexander Tomokhov | 1bf7190388 | |
Alexander Tomokhov | cc26a5e150 | |
Alexander Tomokhov | 6ebcc35882 | |
Alexander Tomokhov | 985aff90d3 | |
Alexander Tomokhov | bd6b8a5e75 | |
Alexander Tomokhov | 6d96b4aa8f | |
Alexander Tomokhov | 8067559207 | |
Alexander Tomokhov | 252ed72154 | |
Alexander Tomokhov | e53eb8d65d | |
Alexander Tomokhov | e59d86fcdc | |
Alexander Tomokhov | 7c5bc70fec | |
Alexander Tomokhov | 308a70fe20 | |
Alexander Tomokhov | a3063ec732 | |
Alexander Tomokhov | e45224d67f | |
Alexander Tomokhov | 391e41e8a4 | |
Alexander Tomokhov | 0704d18b1a | |
Alexander Tomokhov | 6a3a83e270 | |
Alexander Tomokhov | b64be4e34f | |
Alexander Tomokhov | 65e58666fd | |
Alexander Tomokhov | 43abd2ca45 | |
Alexander Tomokhov | e6e2f1fd84 | |
Alexander Tomokhov | 72d73b6297 | |
Alexander Tomokhov | 0f54898cb8 | |
Alexander Tomokhov | d5b04699c1 | |
Alexander Tomokhov | d69a8cd662 | |
Alexander Tomokhov | 80447abb2e | |
Alexander Tomokhov | f4fb0a9ce8 | |
Alexander Tomokhov | 41c3a0fc00 | |
Alexander Tomokhov | d281f51775 | |
Alexander Tomokhov | b0bb84138b | |
Alexander Tomokhov | 4419a1323a | |
Alexander Tomokhov | 26b3071929 | |
Alexander Tomokhov | f33fb9e9bf | |
Alexander Tomokhov | e656719ebd | |
Alexander Tomokhov | f440006806 | |
Alexander Tomokhov | 80ba7d0bda | |
Alexander Tomokhov | d9e2311f3c | |
Alexander Tomokhov | 6c849e2628 | |
Alexander Tomokhov | 7378329891 | |
Alexander Tomokhov | b4b37f42f2 | |
Alexander Tomokhov | 7cee132d8e | |
Alexander Tomokhov | 4afa777e7c | |
Alexander Tomokhov | 57412e328d | |
Alexander Tomokhov | a822f97699 | |
Alexander Tomokhov | 82dd9ad2d4 | |
Alexander Tomokhov | d4af9f4459 | |
Alexander Tomokhov | ee2a895c61 | |
Alexander Tomokhov | 659ca147fd | |
Inex Code | 65b5a19777 | |
Inex Code | 60dd766846 | |
Inex Code | 8006f83257 | |
Inex Code | 74d35b16f2 | |
Inex Code | dd020c3a7d | |
Inex Code | ba1695c642 | |
Inex Code | bc5778fdea | |
Inex Code | 8d99d1c78a | |
Inex Code | 5e64b08381 | |
Inex Code | 7e590ae60c | |
Inex Code | eb36e9b265 | |
Inex Code | 3626506e3a | |
Inex Code | c8c69957b5 | |
Inex Code | 9a8af62e0b | |
Inex Code | a5b965f08f | |
Inex Code | d7edf5a95d | |
Inex Code | bdaf88208f | |
Inex Code | 2e175f8c10 | |
Inex Code | 497cf28ecc | |
Inex Code | 9c662d9629 | |
Inex Code | 0500315ae0 | |
Inex Code | d8f0922b8a | |
Inex Code | ab0c3e113c | |
Inex Code | b4827e6e26 | |
Inex Code | bfe0d18090 | |
Inex Code | 426d84f636 | |
sоvд[сова] | 41edc9f26f | |
sоvд[сова] | 5d3395648a | |
Inex Code | 1944739d28 | |
Inex Code | 08d8407a86 | |
Inex Code | 0d3e8c890c | |
Inex Code | 3dd8ff1821 | |
Inex Code | 895a816ef5 | |
Inex Code | 5210e610df | |
Inex Code | eab3d1e761 | |
Inex Code | a59fbef22a | |
Inex Code | 7a6f57def8 | |
Inex Code | e4ba827d5a | |
Inex Code | aeeffe42b1 | |
Inex Code | 399790e202 | |
Inex Code | 5f2ab0495b |
|
@ -1,4 +1,5 @@
|
||||||
userdata/userdata.json
|
userdata/userdata.json
|
||||||
userdata/tokens.json
|
userdata/tokens.json
|
||||||
hardware-configuration.nix
|
hardware-configuration.nix
|
||||||
networking.nix
|
networking.nix
|
||||||
|
/result
|
||||||
|
|
125
README.md
125
README.md
|
@ -1,67 +1,70 @@
|
||||||
# SelfPrivacy NixOS configuration
|
# SelfPrivacy NixOS configuration
|
||||||
|
|
||||||
This is a NixOS config which builds a SelfPrivacy server distribution
|
This configuration is not self-contained, as it needs to be plugged as an input of a top-level NixOS configuration flake (i.e. https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-template/). This flake outputs the following function:
|
||||||
based on data provided in `userdata/userdata.json`.
|
```nix
|
||||||
|
nixosConfigurations-fun =
|
||||||
|
{ hardware-configuration # hardware-configuration.nix file
|
||||||
|
, deployment # deployment.nix file
|
||||||
|
, userdata # nix attrset, obtained by fromJSON from userdata.json
|
||||||
|
, top-level-flake # `self`-reference of the top-level flake
|
||||||
|
, sp-modules # flake inputs of sp-modules flake
|
||||||
|
}:
|
||||||
|
```
|
||||||
|
which returns one or more attributes, containing NixOS configurations (created with `nixpkgs.lib.nixosSystem`). (As of 2024-01-10 there is only a single configuration named `default`.)
|
||||||
|
|
||||||
JSON schema is provided in `userdata/schema.json` for reference.
|
## updating flake inputs
|
||||||
|
|
||||||
**hardware-configuration.nix is not included.**
|
We have 2 flake inputs:
|
||||||
|
- nixpkgs
|
||||||
|
- selfprivacy-api
|
||||||
|
|
||||||
Example JSON config:
|
Both get updated the same ways.
|
||||||
|
|
||||||
```json
|
There are 2 methods:
|
||||||
{
|
1. specify input name only in a command, relying on URL inside `flake.nix`
|
||||||
"backblaze": {
|
2. specify input name and URL in a command, **overriding** whatever URL is inside `flake.nix` for the input to update (override)
|
||||||
"accountId": "BACKBLAZE_KEY_ID",
|
|
||||||
"accountKey": "BACKBLAZE_ACCOUNT_KEY",
|
In any case a Nix flake input is specified using some special _references_ syntax, including URLs, revisions, etc, described in manual: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#examples. Such reference can be used inside `flake.nix` or as an argument to `nix flake` commands. When a new reference is encountered Nix downloads and extracts it to /nix/store.
|
||||||
"bucket": "BACKBLAZE_BUCKET_NAME"
|
|
||||||
},
|
Before and after running `nix flake lock` (or `nix flake update`) commands you would most likely want to list current inputs using `nix flake metadata`, which are read from `flake.lock` file. Although, Nix should also print a diff between changed references once changed.
|
||||||
"api": {
|
|
||||||
"token": "API_TOKEN",
|
`--commit-lock-file` option tells Nix commands to do `git commit flake.lock` automatically, creating a new commit for you.
|
||||||
"enableSwagger": false
|
|
||||||
},
|
### method 1: update specific input
|
||||||
"bitwarden": {
|
|
||||||
"enable": true
|
Example:
|
||||||
},
|
```console
|
||||||
"cloudflare": {
|
$ nix flake lock --update-input nixpkgs
|
||||||
"apiKey": "CF_TOKEN"
|
$ nix flake lock --update-input selfprivacy-api
|
||||||
},
|
```
|
||||||
"databasePassword": "DB_PASSWORD",
|
|
||||||
"domain": "DOMAIN",
|
Depending on how "precise" the URL was speficied in `flake.nix`, with _unmodified_ `flake.nix` the result might be:
|
||||||
"hashedMasterPassword": "HASHED_PASSWORD",
|
* URL with `rev` (sha1) parameter => nothing will update (as we're already at exact commit)
|
||||||
"hostname": "DOMAIN",
|
* URL with `ref` (branch) parameter => input will update to the latest commit of the specified branch
|
||||||
"nextcloud": {
|
* URL without `rev` nor `ref` => input will update to the latest commit of a default branch!
|
||||||
"enable": true,
|
|
||||||
"adminPassword": "PASSWORD",
|
---
|
||||||
"databasePassword": "PASSWORD"
|
|
||||||
},
|
Once Nix 2.19 stabilizes, a different command _must_ be used for updating a single input (recursively), like this:
|
||||||
"gitea": {
|
```console
|
||||||
"enable": true
|
$ nix flake update nixpkgs
|
||||||
},
|
```
|
||||||
"jitsi": {
|
|
||||||
"enable": true
|
|
||||||
},
|
### method 2: override specific input
|
||||||
"ocserv": {
|
|
||||||
"enable": true
|
Overriding is more powerful (for non-nested flakes) as it allows to change a flake input reference to anything just in one command (not only update in the bounds of a branch or a repository).
|
||||||
},
|
|
||||||
"pleroma": {
|
Example:
|
||||||
"enable": true
|
```console
|
||||||
},
|
$ nix flake lock --override-input nixpkgs github:nixos/nixpkgs?ref=nixos-23.11
|
||||||
"timezone": "Europe/Moscow",
|
$ nix flake lock --override-input selfprivacy-api git+https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git?ref=flakes
|
||||||
"resticPassword": "PASSWORD",
|
```
|
||||||
"ssh": {
|
|
||||||
"enable": true,
|
Similarly to update mechanism (described above), depending on the "precision" of an URL, its update scope varies accordingly.
|
||||||
"rootSshKeys": [
|
|
||||||
"ssh-ed25519 KEY user@host"
|
Note, that subsequent calls of `nix flake lock --update-input <INPUT>` or `nix flake update` (or `nix flake update INPUT` by Nix 2.19+) will update the input regardless of the prior override. The information about override is stored only in `flake.lock` (`flake.nix` is not altered by Nix).
|
||||||
],
|
|
||||||
"passwordAuthentication": true
|
---
|
||||||
},
|
|
||||||
"username": "LUSER",
|
Note, that override does not update flake inputs recursively (say, you have a flake nested inside your flake input). For recursive updates only `nix flake lock --update-input` and `nix flake update` mechanisms are suitable. However, as of 2024-01-10 none of the SP NixOS configuration inputs contain other flakes, hence override mechanism is fine (don't confuse with top-level flake which has nested inputs).
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"hashedPassword": "OTHER_USER_HASHED_PASSWORD",
|
|
||||||
"username": "OTHER_USER"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,152 +0,0 @@
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
with lib;
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.selfprivacy-api;
|
|
||||||
directionArg =
|
|
||||||
if cfg.direction == ""
|
|
||||||
then ""
|
|
||||||
else "--direction=${cfg.direction}";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.selfprivacy-api = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = true;
|
|
||||||
type = types.bool;
|
|
||||||
description = ''
|
|
||||||
Enable SelfPrivacy API service
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
enableSwagger = mkOption {
|
|
||||||
default = false;
|
|
||||||
type = types.bool;
|
|
||||||
description = ''
|
|
||||||
Enable Swagger UI
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
b2Bucket = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = ''
|
|
||||||
B2 bucket
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
|
|
||||||
systemd.services.selfprivacy-api = {
|
|
||||||
description = "API Server used to control system from the mobile application";
|
|
||||||
environment = config.nix.envVars // {
|
|
||||||
inherit (config.environment.sessionVariables) NIX_PATH;
|
|
||||||
HOME = "/root";
|
|
||||||
PYTHONUNBUFFERED = "1";
|
|
||||||
ENABLE_SWAGGER = (if cfg.enableSwagger then "1" else "0");
|
|
||||||
B2_BUCKET = cfg.b2Bucket;
|
|
||||||
} // config.networking.proxy.envVars;
|
|
||||||
path = [
|
|
||||||
"/var/"
|
|
||||||
"/var/dkim/"
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.gnutar
|
|
||||||
pkgs.xz.bin
|
|
||||||
pkgs.gzip
|
|
||||||
pkgs.gitMinimal
|
|
||||||
config.nix.package.out
|
|
||||||
pkgs.nixos-rebuild
|
|
||||||
pkgs.restic
|
|
||||||
pkgs.mkpasswd
|
|
||||||
pkgs.util-linux
|
|
||||||
pkgs.e2fsprogs
|
|
||||||
pkgs.iproute2
|
|
||||||
];
|
|
||||||
after = [ "network-online.target" ];
|
|
||||||
wantedBy = [ "network-online.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
User = "root";
|
|
||||||
ExecStart = "${pkgs.selfprivacy-graphql-api}/bin/app.py";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "5";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
systemd.services.selfprivacy-api-worker = {
|
|
||||||
description = "Task worker for SelfPrivacy API";
|
|
||||||
environment = config.nix.envVars // {
|
|
||||||
inherit (config.environment.sessionVariables) NIX_PATH;
|
|
||||||
HOME = "/root";
|
|
||||||
PYTHONUNBUFFERED = "1";
|
|
||||||
ENABLE_SWAGGER = (if cfg.enableSwagger then "1" else "0");
|
|
||||||
B2_BUCKET = cfg.b2Bucket;
|
|
||||||
PYTHONPATH = pkgs.selfprivacy-graphql-api.pythonPath + ":${pkgs.selfprivacy-graphql-api}/lib/python3.9/site-packages/";
|
|
||||||
} // config.networking.proxy.envVars;
|
|
||||||
path = [
|
|
||||||
"/var/"
|
|
||||||
"/var/dkim/"
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.gnutar
|
|
||||||
pkgs.xz.bin
|
|
||||||
pkgs.gzip
|
|
||||||
pkgs.gitMinimal
|
|
||||||
config.nix.package.out
|
|
||||||
pkgs.nixos-rebuild
|
|
||||||
pkgs.restic
|
|
||||||
pkgs.mkpasswd
|
|
||||||
pkgs.util-linux
|
|
||||||
pkgs.e2fsprogs
|
|
||||||
pkgs.iproute2
|
|
||||||
];
|
|
||||||
after = [ "network-online.target" ];
|
|
||||||
wantedBy = [ "network-online.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
User = "root";
|
|
||||||
ExecStart = "${pkgs.python39Packages.huey}/bin/huey_consumer.py selfprivacy_api.task_registry.huey";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "5";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# One shot systemd service to rebuild NixOS using nixos-rebuild
|
|
||||||
systemd.services.sp-nixos-rebuild = {
|
|
||||||
description = "Upgrade NixOS using nixos-rebuild";
|
|
||||||
environment = config.nix.envVars // {
|
|
||||||
inherit (config.environment.sessionVariables) NIX_PATH;
|
|
||||||
HOME = "/root";
|
|
||||||
} // config.networking.proxy.envVars;
|
|
||||||
path = [ pkgs.coreutils pkgs.gnutar pkgs.xz.bin pkgs.gzip pkgs.gitMinimal config.nix.package.out pkgs.nixos-rebuild ];
|
|
||||||
serviceConfig = {
|
|
||||||
User = "root";
|
|
||||||
ExecStart = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch";
|
|
||||||
KillMode = "none";
|
|
||||||
SendSIGKILL = "no";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# One shot systemd service to upgrade NixOS using nixos-rebuild
|
|
||||||
systemd.services.sp-nixos-upgrade = {
|
|
||||||
description = "Upgrade NixOS using nixos-rebuild";
|
|
||||||
environment = config.nix.envVars // {
|
|
||||||
inherit (config.environment.sessionVariables) NIX_PATH;
|
|
||||||
HOME = "/root";
|
|
||||||
} // config.networking.proxy.envVars;
|
|
||||||
path = [ pkgs.coreutils pkgs.gnutar pkgs.xz.bin pkgs.gzip pkgs.gitMinimal config.nix.package.out pkgs.nixos-rebuild ];
|
|
||||||
serviceConfig = {
|
|
||||||
User = "root";
|
|
||||||
ExecStart = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --upgrade";
|
|
||||||
KillMode = "none";
|
|
||||||
SendSIGKILL = "no";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# One shot systemd service to rollback NixOS using nixos-rebuild
|
|
||||||
systemd.services.sp-nixos-rollback = {
|
|
||||||
description = "Rollback NixOS using nixos-rebuild";
|
|
||||||
environment = config.nix.envVars // {
|
|
||||||
inherit (config.environment.sessionVariables) NIX_PATH;
|
|
||||||
HOME = "/root";
|
|
||||||
} // config.networking.proxy.envVars;
|
|
||||||
path = [ pkgs.coreutils pkgs.gnutar pkgs.xz.bin pkgs.gzip pkgs.gitMinimal config.nix.package.out pkgs.nixos-rebuild ];
|
|
||||||
serviceConfig = {
|
|
||||||
User = "root";
|
|
||||||
ExecStart = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --rollback";
|
|
||||||
KillMode = "none";
|
|
||||||
SendSIGKILL = "no";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
18
api/api.nix
18
api/api.nix
|
@ -1,18 +0,0 @@
|
||||||
{ config, pkgs, ... }:
|
|
||||||
{
|
|
||||||
services.selfprivacy-api = {
|
|
||||||
enable = true;
|
|
||||||
enableSwagger = config.services.userdata.api.enableSwagger;
|
|
||||||
b2Bucket = config.services.userdata.backblaze.bucket;
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users."selfprivacy-api" = {
|
|
||||||
isNormalUser = false;
|
|
||||||
isSystemUser = true;
|
|
||||||
extraGroups = [ "opendkim" ];
|
|
||||||
group = "selfprivacy-api";
|
|
||||||
};
|
|
||||||
users.groups."selfprivacy-api" = {
|
|
||||||
members = [ "selfprivacy-api" ];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
{ config, pkgs, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.userdata;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
services.restic.backups = {
|
|
||||||
options = {
|
|
||||||
passwordFile = "/etc/restic/resticPasswd";
|
|
||||||
repository = "s3:s3.anazonaws.com/${cfg.backblaze.bucket}";
|
|
||||||
initialize = true;
|
|
||||||
paths = [
|
|
||||||
"/var/dkim"
|
|
||||||
"/var/vmail"
|
|
||||||
];
|
|
||||||
timerConfig = {
|
|
||||||
OnCalendar = [ "daily" ];
|
|
||||||
};
|
|
||||||
user = "restic";
|
|
||||||
pruneOpts = [
|
|
||||||
"--keep-daily 5"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
users.users.restic = {
|
|
||||||
isNormalUser = false;
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "restic";
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,38 +1,60 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
url-overlay = "https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nix-repo/archive/master.tar.gz";
|
redis-sp-api-srv-name = "sp-api";
|
||||||
nix-overlay = (import (builtins.fetchTarball url-overlay));
|
sp-print-api-token = pkgs.writeShellApplication {
|
||||||
|
name = "sp-print-api-token";
|
||||||
|
runtimeInputs = with pkgs; [ redis ];
|
||||||
|
text = ''
|
||||||
|
hash_token="$(redis-cli -s /run/redis-${redis-sp-api-srv-name}/redis.sock keys "token_repo:tokens:*" | head -n 1)"
|
||||||
|
hash_token="''${hash_token#"token_repo:tokens:"}"
|
||||||
|
|
||||||
|
token="$(redis-cli -s /run/redis-${redis-sp-api-srv-name}/redis.sock HGETALL "token_repo:tokens:$hash_token")"
|
||||||
|
token="$(echo "$token" | sed -n '2p')"
|
||||||
|
|
||||||
|
echo "$token"
|
||||||
|
'';
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./hardware-configuration.nix
|
./selfprivacy-module.nix
|
||||||
./variables-module.nix
|
|
||||||
./variables.nix
|
|
||||||
./files.nix
|
|
||||||
./volumes.nix
|
./volumes.nix
|
||||||
./users.nix
|
./users.nix
|
||||||
./mailserver/system/mailserver.nix
|
|
||||||
./vpn/ocserv.nix
|
|
||||||
./api/api.nix
|
|
||||||
./api/api-module.nix
|
|
||||||
./social/pleroma.nix
|
|
||||||
./letsencrypt/acme.nix
|
./letsencrypt/acme.nix
|
||||||
./letsencrypt/resolve.nix
|
./letsencrypt/resolve.nix
|
||||||
./backup/restic.nix
|
|
||||||
./passmgr/bitwarden.nix
|
|
||||||
./webserver/nginx.nix
|
./webserver/nginx.nix
|
||||||
./webserver/memcached.nix
|
./webserver/memcached.nix
|
||||||
./nextcloud/nextcloud.nix
|
# ./resources/limits.nix
|
||||||
./resources/limits.nix
|
|
||||||
./videomeet/jitsi.nix
|
|
||||||
./git/gitea.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
nixpkgs.overlays = [ (nix-overlay) ];
|
fileSystems."/".options = [ "noatime" ];
|
||||||
|
|
||||||
boot.cleanTmpDir = true;
|
services.selfprivacy-api.enable = true;
|
||||||
|
|
||||||
|
services.redis.servers.${redis-sp-api-srv-name} = {
|
||||||
|
enable = true;
|
||||||
|
save = [
|
||||||
|
[
|
||||||
|
30
|
||||||
|
1
|
||||||
|
]
|
||||||
|
[
|
||||||
|
10
|
||||||
|
10
|
||||||
|
]
|
||||||
|
];
|
||||||
|
port = 0;
|
||||||
|
settings = {
|
||||||
|
notify-keyspace-events = "KEA";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.do-agent.enable = if config.selfprivacy.server.provider == "DIGITALOCEAN" then true else false;
|
||||||
|
|
||||||
|
boot.tmp.cleanOnBoot = true;
|
||||||
networking = {
|
networking = {
|
||||||
hostName = config.services.userdata.hostname;
|
hostName = config.selfprivacy.hostname;
|
||||||
|
domain = config.selfprivacy.domain;
|
||||||
usePredictableInterfaceNames = false;
|
usePredictableInterfaceNames = false;
|
||||||
firewall = {
|
firewall = {
|
||||||
allowedTCPPorts = lib.mkForce [ 22 25 80 143 443 465 587 993 4443 8443 ];
|
allowedTCPPorts = lib.mkForce [ 22 25 80 143 443 465 587 993 4443 8443 ];
|
||||||
|
@ -44,42 +66,90 @@ in
|
||||||
};
|
};
|
||||||
nameservers = [ "1.1.1.1" "1.0.0.1" ];
|
nameservers = [ "1.1.1.1" "1.0.0.1" ];
|
||||||
};
|
};
|
||||||
time.timeZone = config.services.userdata.timezone;
|
time.timeZone = config.selfprivacy.timezone;
|
||||||
i18n.defaultLocale = "en_GB.UTF-8";
|
i18n.defaultLocale = "en_GB.UTF-8";
|
||||||
users.users.root.openssh.authorizedKeys.keys = config.services.userdata.ssh.rootKeys;
|
users.users.root.openssh.authorizedKeys.keys = config.selfprivacy.ssh.rootKeys;
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = config.services.userdata.ssh.enable;
|
enable = config.selfprivacy.ssh.enable;
|
||||||
passwordAuthentication = config.services.userdata.ssh.passwordAuthentication;
|
settings = {
|
||||||
permitRootLogin = "yes";
|
PasswordAuthentication = config.selfprivacy.ssh.passwordAuthentication;
|
||||||
|
PermitRootLogin = "yes";
|
||||||
|
};
|
||||||
openFirewall = false;
|
openFirewall = false;
|
||||||
};
|
};
|
||||||
programs.ssh = {
|
programs.ssh = {
|
||||||
pubkeyAcceptedKeyTypes = [ "ssh-ed25519" "ssh-rsa" ];
|
pubkeyAcceptedKeyTypes = [ "ssh-ed25519" "ssh-rsa" "ecdsa-sha2-nistp256" ];
|
||||||
hostKeyAlgorithms = [ "ssh-ed25519" "ssh-rsa" ];
|
hostKeyAlgorithms = [ "ssh-ed25519" "ssh-rsa" ];
|
||||||
};
|
};
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
git
|
git
|
||||||
jq
|
jq
|
||||||
|
sp-print-api-token
|
||||||
];
|
];
|
||||||
environment.variables = {
|
# consider environment.defaultPackages = lib.mkForce [];
|
||||||
DOMAIN = config.services.userdata.domain;
|
documentation.enable = false; # no {man,info}-pages & docs, etc to save space
|
||||||
};
|
# (or create a systemd service with `ConditionFirstBoot=yes`?)
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"# Completely remove remnants of NIXOS_LUSTRATE."
|
||||||
|
"R! /old-root"
|
||||||
|
];
|
||||||
|
system.stateVersion =
|
||||||
|
lib.mkIf (config.selfprivacy.stateVersion != null)
|
||||||
|
config.selfprivacy.stateVersion;
|
||||||
system.autoUpgrade = {
|
system.autoUpgrade = {
|
||||||
enable = config.services.userdata.autoUpgrade.enable;
|
enable = config.selfprivacy.autoUpgrade.enable;
|
||||||
allowReboot = config.services.userdata.autoUpgrade.allowReboot;
|
allowReboot = config.selfprivacy.autoUpgrade.allowReboot;
|
||||||
channel = "https://channel.selfprivacy.org/nixos-selfpricacy";
|
# TODO get attribute name from selfprivacy options
|
||||||
|
flake = "/etc/nixos#default";
|
||||||
};
|
};
|
||||||
|
systemd.services.nixos-upgrade.serviceConfig.WorkingDirectory = "/etc/nixos";
|
||||||
|
# TODO parameterize URL somehow; run nix flake update as non-root user
|
||||||
|
systemd.services.nixos-upgrade.serviceConfig.ExecCondition =
|
||||||
|
pkgs.writeShellScript "flake-update-script" ''
|
||||||
|
set -o xtrace
|
||||||
|
if ${config.nix.package.out}/bin/nix flake update \
|
||||||
|
--override-input selfprivacy-nixos-config git+https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-config.git?ref=flakes
|
||||||
|
then
|
||||||
|
if ${pkgs.diffutils}/bin/diff -u -r /etc/selfprivacy/nixos-config-source/ /etc/nixos/
|
||||||
|
then
|
||||||
|
set +o xtrace
|
||||||
|
echo "No configuration changes detected. Nothing to upgrade."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# ExecStart must not start after 255 exit code, service must fail.
|
||||||
|
exit 255
|
||||||
|
fi
|
||||||
|
'';
|
||||||
nix = {
|
nix = {
|
||||||
optimise.automatic = true;
|
channel.enable = false;
|
||||||
|
|
||||||
|
# daemonCPUSchedPolicy = "idle";
|
||||||
|
# daemonIOSchedClass = "idle";
|
||||||
|
# daemonIOSchedPriority = 7;
|
||||||
|
# this is superseded by nix.settings.auto-optimise-store.
|
||||||
|
# optimise.automatic = true;
|
||||||
|
|
||||||
gc = {
|
gc = {
|
||||||
automatic = true;
|
automatic = true; # TODO it's debatable, because of IO&CPU load
|
||||||
options = "--delete-older-than 7d";
|
options = "--delete-older-than 7d";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
nix.settings = {
|
||||||
|
sandbox = true;
|
||||||
|
experimental-features = [ "nix-command" "flakes" "repl-flake" ];
|
||||||
|
# auto-optimise-store = true;
|
||||||
|
|
||||||
|
# evaluation restrictions:
|
||||||
|
# restrict-eval = true;
|
||||||
|
# allowed-uris = [];
|
||||||
|
allow-dirty = false;
|
||||||
|
};
|
||||||
services.journald.extraConfig = "SystemMaxUse=500M";
|
services.journald.extraConfig = "SystemMaxUse=500M";
|
||||||
boot.kernel.sysctl = {
|
boot.kernel.sysctl = {
|
||||||
"net.ipv4.ip_forward" = 1;
|
"net.ipv4.ip_forward" = 1; # TODO why is it here by default, for VPN only?
|
||||||
};
|
};
|
||||||
|
# TODO must be configurable and determined at nixos-infect stage
|
||||||
swapDevices = [
|
swapDevices = [
|
||||||
{
|
{
|
||||||
device = "/swapfile";
|
device = "/swapfile";
|
||||||
|
@ -87,9 +157,12 @@ in
|
||||||
size = 2048;
|
size = 2048;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
# TODO why is sudo needed?
|
||||||
security = {
|
security = {
|
||||||
sudo = {
|
sudo = {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
systemd.enableEmergencyMode = false;
|
||||||
|
systemd.coredump.enable = false;
|
||||||
}
|
}
|
||||||
|
|
83
files.nix
83
files.nix
|
@ -1,83 +0,0 @@
|
||||||
{ config, pkgs, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.userdata;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
systemd.tmpfiles.rules =
|
|
||||||
let
|
|
||||||
domain = builtins.replaceStrings [ "\n" "\"" "\\" "%" ] [ "\\n" "\\\"" "\\\\" "%%" ] cfg.domain;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
(if cfg.bitwarden.enable then "d /var/lib/bitwarden 0777 vaultwarden vaultwarden -" else "")
|
|
||||||
(if cfg.bitwarden.enable then "d /var/lib/bitwarden/backup 0777 vaultwarden vaultwarden -" else "")
|
|
||||||
(if cfg.pleroma.enable then "d /var/lib/pleroma 0700 pleroma pleroma - -" else "")
|
|
||||||
"d /var/lib/restic 0600 restic - - -"
|
|
||||||
(if cfg.pleroma.enable then "f /var/lib/pleroma/secrets.exs 0755 pleroma pleroma - -" else "")
|
|
||||||
"f+ /var/domain 0444 selfprivacy-api selfprivacy-api - ${domain}"
|
|
||||||
];
|
|
||||||
system.activationScripts =
|
|
||||||
let
|
|
||||||
jq = "${pkgs.jq}/bin/jq";
|
|
||||||
sed = "${pkgs.gnused}/bin/sed";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
nextcloudSecrets =
|
|
||||||
if cfg.nextcloud.enable then ''
|
|
||||||
mkdir -p /var/lib/nextcloud
|
|
||||||
cat /etc/nixos/userdata/userdata.json | ${jq} -r '.nextcloud.databasePassword' > /var/lib/nextcloud/db-pass
|
|
||||||
chmod 0440 /var/lib/nextcloud/db-pass
|
|
||||||
chown nextcloud:nextcloud /var/lib/nextcloud/db-pass
|
|
||||||
|
|
||||||
cat /etc/nixos/userdata/userdata.json | ${jq} -r '.nextcloud.adminPassword' > /var/lib/nextcloud/admin-pass
|
|
||||||
chmod 0440 /var/lib/nextcloud/admin-pass
|
|
||||||
chown nextcloud:nextcloud /var/lib/nextcloud/admin-pass
|
|
||||||
''
|
|
||||||
else ''
|
|
||||||
rm -f /var/lib/nextcloud/db-pass
|
|
||||||
rm -f /var/lib/nextcloud/admin-pass
|
|
||||||
'';
|
|
||||||
cloudflareCredentials = ''
|
|
||||||
mkdir -p /var/lib/cloudflare
|
|
||||||
chmod 0440 /var/lib/cloudflare
|
|
||||||
chown nginx:acmerecievers /var/lib/cloudflare
|
|
||||||
echo 'CF_API_KEY=REPLACEME' > /var/lib/cloudflare/Credentials.ini
|
|
||||||
echo 'CLOUDFLARE_DNS_API_TOKEN=REPLACEME' >> /var/lib/cloudflare/Credentials.ini
|
|
||||||
echo 'CLOUDFLARE_ZONE_API_TOKEN=REPLACEME' >> /var/lib/cloudflare/Credentials.ini
|
|
||||||
${sed} -i "s/REPLACEME/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.cloudflare.apiKey')/g" /var/lib/cloudflare/Credentials.ini
|
|
||||||
chmod 0440 /var/lib/cloudflare/Credentials.ini
|
|
||||||
chown nginx:acmerecievers /var/lib/cloudflare/Credentials.ini
|
|
||||||
'';
|
|
||||||
resticCredentials = ''
|
|
||||||
mkdir -p /root/.config/rclone
|
|
||||||
chmod 0400 /root/.config/rclone
|
|
||||||
chown root:root /root/.config/rclone
|
|
||||||
echo '[backblaze]' > /root/.config/rclone/rclone.conf
|
|
||||||
echo 'type = b2' >> /root/.config/rclone/rclone.conf
|
|
||||||
echo 'account = REPLACEME1' >> /root/.config/rclone/rclone.conf
|
|
||||||
echo 'key = REPLACEME2' >> /root/.config/rclone/rclone.conf
|
|
||||||
|
|
||||||
${sed} -i "s/REPLACEME1/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.backblaze.accountId')/g" /root/.config/rclone/rclone.conf
|
|
||||||
${sed} -i "s/REPLACEME2/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.backblaze.accountKey')/g" /root/.config/rclone/rclone.conf
|
|
||||||
|
|
||||||
chmod 0400 /root/.config/rclone/rclone.conf
|
|
||||||
chown root:root /root/.config/rclone/rclone.conf
|
|
||||||
|
|
||||||
cat /etc/nixos/userdata/userdata.json | ${jq} -r '.resticPassword' > /var/lib/restic/pass
|
|
||||||
chmod 0400 /var/lib/restic/pass
|
|
||||||
chown restic /var/lib/restic/pass
|
|
||||||
'';
|
|
||||||
pleromaCredentials =
|
|
||||||
if cfg.pleroma.enable then ''
|
|
||||||
echo 'import Config' > /var/lib/pleroma/secrets.exs
|
|
||||||
echo 'config :pleroma, Pleroma.Repo,' >> /var/lib/pleroma/secrets.exs
|
|
||||||
echo ' password: "REPLACEME"' >> /var/lib/pleroma/secrets.exs
|
|
||||||
|
|
||||||
${sed} -i "s/REPLACEME/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.databasePassword')/g" /var/lib/pleroma/secrets.exs
|
|
||||||
|
|
||||||
chmod 0750 /var/lib/pleroma/secrets.exs
|
|
||||||
chown pleroma:pleroma /var/lib/pleroma/secrets.exs
|
|
||||||
'' else ''
|
|
||||||
rm -f /var/lib/pleroma/secrets.exs
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1716361217,
|
||||||
|
"narHash": "sha256-mzZDr00WUiUXVm1ujBVv6A0qRd8okaITyUp4ezYRgc4=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "46397778ef1f73414b03ed553a3368f0e7e33c2f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"selfprivacy-api": "selfprivacy-api"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selfprivacy-api": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1716580438,
|
||||||
|
"narHash": "sha256-wWKhCiRmjrFgnHUgcxBb2mtBy9uTrY93yxpMYh/wpVY=",
|
||||||
|
"ref": "master",
|
||||||
|
"rev": "4f1d44ce74432e745f0be3b192f8cc4ae06fd169",
|
||||||
|
"revCount": 1304,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
{
|
||||||
|
description = "SelfPrivacy NixOS configuration flake";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = github:nixos/nixpkgs;
|
||||||
|
|
||||||
|
selfprivacy-api.url =
|
||||||
|
git+https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git;
|
||||||
|
# make selfprivacy-api use the same shared nixpkgs
|
||||||
|
selfprivacy-api.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, selfprivacy-api }: {
|
||||||
|
nixosConfigurations-fun =
|
||||||
|
{ hardware-configuration
|
||||||
|
, deployment
|
||||||
|
, userdata
|
||||||
|
, top-level-flake
|
||||||
|
, sp-modules
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
default = nixpkgs.lib.nixosSystem {
|
||||||
|
modules = [
|
||||||
|
hardware-configuration
|
||||||
|
deployment
|
||||||
|
./configuration.nix
|
||||||
|
selfprivacy-api.nixosModules.default
|
||||||
|
{
|
||||||
|
# pass userdata (parsed from JSON) options to selfprivacy module
|
||||||
|
selfprivacy = userdata;
|
||||||
|
|
||||||
|
# embed top-level flake source folder into the build
|
||||||
|
environment.etc."selfprivacy/nixos-config-source".source =
|
||||||
|
top-level-flake;
|
||||||
|
|
||||||
|
# for running "nix search nixpkgs", etc
|
||||||
|
nix.registry.nixpkgs.flake = nixpkgs;
|
||||||
|
|
||||||
|
# embed commit sha1 for `nixos-version --configuration-revision`
|
||||||
|
system.configurationRevision = self.rev
|
||||||
|
or "@${self.lastModifiedDate}"; # for development
|
||||||
|
# TODO assertion to forbid dirty builds caused by top-level-flake
|
||||||
|
|
||||||
|
# reset contents of /etc/nixos to match running NixOS generation
|
||||||
|
system.activationScripts.selfprivacy-nixos-config-source = ''
|
||||||
|
rm -rf /etc/nixos/{*,.[!.]*}
|
||||||
|
cp -r --no-preserve=all ${top-level-flake}/ -T /etc/nixos/
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++
|
||||||
|
# add SP modules, but contrain available config attributes for each
|
||||||
|
# (TODO revise evaluation performance of the code below)
|
||||||
|
nixpkgs.lib.attrsets.mapAttrsToList
|
||||||
|
(name: sp-module: args@{ config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
lib = nixpkgs.lib;
|
||||||
|
configPathsNeeded = sp-module.configPathsNeeded or
|
||||||
|
(abort "allowed config paths not set for module \"${name}\"");
|
||||||
|
constrainConfigArgs = args'@{ pkgs, ... }: args' // {
|
||||||
|
config =
|
||||||
|
# TODO use lib.attrsets.mergeAttrsList from nixpkgs 23.05
|
||||||
|
(builtins.foldl' lib.attrsets.recursiveUpdate { }
|
||||||
|
(map
|
||||||
|
(p: lib.attrsets.setAttrByPath p
|
||||||
|
(lib.attrsets.getAttrFromPath p config))
|
||||||
|
configPathsNeeded
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
constrainImportsArgsRecursive = lib.attrsets.mapAttrsRecursive
|
||||||
|
(p: v:
|
||||||
|
# TODO traverse only imports and imports of imports, etc
|
||||||
|
# without traversing all attributes
|
||||||
|
if lib.lists.last p == "imports"
|
||||||
|
then
|
||||||
|
map
|
||||||
|
(m:
|
||||||
|
(args'@{ pkgs, ... }: constrainImportsArgsRecursive
|
||||||
|
(if builtins.isPath m
|
||||||
|
then import m (constrainConfigArgs args')
|
||||||
|
else
|
||||||
|
if builtins.isFunction m
|
||||||
|
then m (constrainConfigArgs args')
|
||||||
|
else m))
|
||||||
|
)
|
||||||
|
v
|
||||||
|
else v);
|
||||||
|
in
|
||||||
|
constrainImportsArgsRecursive
|
||||||
|
(sp-module.nixosModules.default (constrainConfigArgs args))
|
||||||
|
)
|
||||||
|
sp-modules;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixpkgs-fmt;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,63 +0,0 @@
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.userdata;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
fileSystems = lib.mkIf cfg.useBinds {
|
|
||||||
"/var/lib/gitea" = {
|
|
||||||
device = "/volumes/${cfg.gitea.location}/gitea";
|
|
||||||
options = [ "bind" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
services = {
|
|
||||||
gitea = {
|
|
||||||
enable = cfg.gitea.enable;
|
|
||||||
stateDir = "/var/lib/gitea";
|
|
||||||
log = {
|
|
||||||
rootPath = "/var/lib/gitea/log";
|
|
||||||
level = "Warn";
|
|
||||||
};
|
|
||||||
user = "gitea";
|
|
||||||
database = {
|
|
||||||
type = "sqlite3";
|
|
||||||
host = "127.0.0.1";
|
|
||||||
name = "gitea";
|
|
||||||
user = "gitea";
|
|
||||||
path = "/var/lib/gitea/data/gitea.db";
|
|
||||||
createDatabase = true;
|
|
||||||
};
|
|
||||||
ssh = {
|
|
||||||
enable = true;
|
|
||||||
clonePort = 22;
|
|
||||||
};
|
|
||||||
lfs = {
|
|
||||||
enable = true;
|
|
||||||
contentDir = "/var/lib/gitea/lfs";
|
|
||||||
};
|
|
||||||
appName = "SelfPrivacy git Service";
|
|
||||||
repositoryRoot = "/var/lib/gitea/repositories";
|
|
||||||
domain = "git.${cfg.domain}";
|
|
||||||
rootUrl = "https://${cfg.domain}/";
|
|
||||||
httpAddress = "0.0.0.0";
|
|
||||||
httpPort = 3000;
|
|
||||||
cookieSecure = true;
|
|
||||||
settings = {
|
|
||||||
mailer = {
|
|
||||||
ENABLED = false;
|
|
||||||
};
|
|
||||||
ui = {
|
|
||||||
DEFAULT_THEME = "arc-green";
|
|
||||||
};
|
|
||||||
picture = {
|
|
||||||
DISABLE_GRAVATAR = true;
|
|
||||||
};
|
|
||||||
admin = {
|
|
||||||
ENABLE_KANBAN_BOARD = true;
|
|
||||||
};
|
|
||||||
repository = {
|
|
||||||
FORCE_PRIVATE = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,28 +1,63 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.services.userdata;
|
cfg = config.selfprivacy;
|
||||||
|
dnsCredentialsTemplates = {
|
||||||
|
DIGITALOCEAN = "DO_AUTH_TOKEN=$TOKEN";
|
||||||
|
CLOUDFLARE = ''
|
||||||
|
CF_API_KEY=$TOKEN
|
||||||
|
CLOUDFLARE_DNS_API_TOKEN=$TOKEN
|
||||||
|
CLOUDFLARE_ZONE_API_TOKEN=$TOKEN
|
||||||
|
CLOUDFLARE_POLLING_INTERVAL=30
|
||||||
|
'';
|
||||||
|
DESEC = ''
|
||||||
|
DESEC_TOKEN=$TOKEN
|
||||||
|
DESEC_POLLING_INTERVAL=30
|
||||||
|
DESEC_PROPAGATION_TIMEOUT=180
|
||||||
|
DESEC_TTL=3600
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
dnsCredentialsTemplate = dnsCredentialsTemplates.${cfg.dns.provider};
|
||||||
|
acme-env-filepath = "/var/lib/selfprivacy/acme-env";
|
||||||
|
secrets-filepath = "/etc/selfprivacy/secrets.json";
|
||||||
|
dnsPropagationCheckExceptions = [ "DIGITALOCEAN" "DESEC" ];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
users.groups.acmerecievers = {
|
users.groups.acmereceivers.members = [ "nginx" ];
|
||||||
members = [ "nginx" "dovecot2" "postfix" "virtualMail" "ocserv" ];
|
|
||||||
};
|
|
||||||
security.acme = {
|
security.acme = {
|
||||||
acceptTerms = true;
|
acceptTerms = true;
|
||||||
email = "${cfg.username}@${cfg.domain}";
|
defaults = {
|
||||||
certs = lib.mkForce {
|
email = "${cfg.username}@${cfg.domain}";
|
||||||
|
server = if cfg.dns.useStagingACME then "https://acme-staging-v02.api.letsencrypt.org/directory" else "https://acme-v02.api.letsencrypt.org/directory";
|
||||||
|
reloadServices = [ "nginx" ];
|
||||||
|
};
|
||||||
|
certs = {
|
||||||
"${cfg.domain}" = {
|
"${cfg.domain}" = {
|
||||||
domain = "*.${cfg.domain}";
|
domain = "*.${cfg.domain}";
|
||||||
extraDomainNames = [ "${cfg.domain}" ];
|
extraDomainNames = [ "${cfg.domain}" ];
|
||||||
group = "acmerecievers";
|
group = "acmereceivers";
|
||||||
dnsProvider = "cloudflare";
|
dnsProvider = lib.strings.toLower cfg.dns.provider;
|
||||||
credentialsFile = "/var/lib/cloudflare/Credentials.ini";
|
credentialsFile = acme-env-filepath;
|
||||||
};
|
dnsPropagationCheck =
|
||||||
"meet.${cfg.domain}" = {
|
! (lib.elem cfg.dns.provider dnsPropagationCheckExceptions);
|
||||||
domain = "meet.${cfg.domain}";
|
|
||||||
group = "acmerecievers";
|
|
||||||
dnsProvider = "cloudflare";
|
|
||||||
credentialsFile = "/var/lib/cloudflare/Credentials.ini";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
systemd.services.acme-secrets = {
|
||||||
|
before = [ "acme-${cfg.domain}.service" ];
|
||||||
|
requiredBy = [ "acme-${cfg.domain}.service" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
path = with pkgs; [ coreutils jq ];
|
||||||
|
script = ''
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
TOKEN="$(jq -re '.dns.apiKey' ${secrets-filepath})"
|
||||||
|
filecontents=$(cat <<- EOF
|
||||||
|
${dnsCredentialsTemplate}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
install -m 0440 -o root -g acmereceivers -DT \
|
||||||
|
<(printf "%s\n" "$filecontents") ${acme-env-filepath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ config, pkgs, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
domain = config.services.userdata.domain;
|
domain = config.selfprivacy.domain;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
systemd = {
|
systemd = {
|
||||||
|
@ -12,11 +12,6 @@ in
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"nginx-config-reload" = {
|
|
||||||
serviceConfig = {
|
|
||||||
After = [ "acme-${domain}.service" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
{ pkgs, lib, config, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.userdata;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
fileSystems = lib.mkIf cfg.useBinds {
|
|
||||||
"/var/lib/nextcloud" = {
|
|
||||||
device = "/volumes/${cfg.nextcloud.location}/nextcloud";
|
|
||||||
options = [ "bind" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
services.nextcloud = {
|
|
||||||
enable = cfg.nextcloud.enable;
|
|
||||||
package = pkgs.nextcloud23;
|
|
||||||
hostName = "cloud.${cfg.domain}";
|
|
||||||
|
|
||||||
# Use HTTPS for links
|
|
||||||
https = false;
|
|
||||||
|
|
||||||
# Auto-update Nextcloud Apps
|
|
||||||
autoUpdateApps.enable = true;
|
|
||||||
# Set what time makes sense for you
|
|
||||||
autoUpdateApps.startAt = "05:00:00";
|
|
||||||
|
|
||||||
config = {
|
|
||||||
# Further forces Nextcloud to use HTTPS
|
|
||||||
overwriteProtocol = "https";
|
|
||||||
|
|
||||||
# Nextcloud PostegreSQL database configuration, recommended over using SQLite
|
|
||||||
dbtype = "sqlite";
|
|
||||||
dbuser = "nextcloud";
|
|
||||||
dbhost = "/run/postgresql"; # nextcloud will add /.s.PGSQL.5432 by itself
|
|
||||||
dbname = "nextcloud";
|
|
||||||
dbpassFile = "/var/lib/nextcloud/db-pass";
|
|
||||||
|
|
||||||
adminpassFile = "/var/lib/nextcloud/admin-pass";
|
|
||||||
adminuser = "admin";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
{ pkgs, lib, config, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.userdata;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
fileSystems = lib.mkIf cfg.useBinds {
|
|
||||||
"/var/lib/bitwarden" = {
|
|
||||||
device = "/volumes/${cfg.bitwarden.location}/bitwarden";
|
|
||||||
options = [ "bind" ];
|
|
||||||
};
|
|
||||||
"/var/lib/bitwarden_rs" = {
|
|
||||||
device = "/volumes/${cfg.bitwarden.location}/bitwarden_rs";
|
|
||||||
options = [ "bind" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
services.vaultwarden = {
|
|
||||||
enable = cfg.bitwarden.enable;
|
|
||||||
dbBackend = "sqlite";
|
|
||||||
backupDir = "/var/lib/bitwarden/backup";
|
|
||||||
config = {
|
|
||||||
domain = "https://password.${cfg.domain}/";
|
|
||||||
signupsAllowed = true;
|
|
||||||
rocketPort = 8222;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,47 +1,46 @@
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
{
|
||||||
systemd.services = {
|
systemd.services = {
|
||||||
dovecot2 = {
|
dovecot2 = {
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
cpuAccounting = true;
|
CpuAccounting = true;
|
||||||
cpuQuota = "20%";
|
CpuQuota = "20%";
|
||||||
memoryAccounting = true;
|
MemoryAccounting = true;
|
||||||
memoryMax = "256M";
|
MemoryMax = "256M";
|
||||||
startLimitIntervalSec = 500;
|
StartLimitIntervalSec = 500;
|
||||||
startLimitBurst = 5;
|
StartLimitBurst = 5;
|
||||||
blockIOWeigth = 25;
|
BlockIOWeight = 25;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
postfix = {
|
postfix = {
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
cpuAccounting = true;
|
CpuAccounting = true;
|
||||||
cpuQuota = "20%";
|
CpuQuota = "20%";
|
||||||
memoryAccounting = true;
|
MemoryAccounting = true;
|
||||||
memoryMax = "256M";
|
MemoryMax = "256M";
|
||||||
startLimitIntervalSec = 500;
|
StartLimitIntervalSec = 500;
|
||||||
startLimitBurst = 5;
|
StartLimitBurst = 5;
|
||||||
blockIOWeigth = 25;
|
BlockIOWeight = 25;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
ocserv = {
|
ocserv = {
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
cpuAccounting = true;
|
CpuAccounting = true;
|
||||||
cpuQuota = "70%";
|
CpuQuota = "70%";
|
||||||
memoryAccounting = true;
|
MemoryAccounting = true;
|
||||||
memoryMax = "512M";
|
MemoryMax = "512M";
|
||||||
startLimitIntervalSec = 500;
|
StartLimitIntervalSec = 500;
|
||||||
startLimitBurst = 5;
|
StartLimitBurst = 5;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
nginx = {
|
nginx = {
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
cpuAccounting = true;
|
CpuAccounting = true;
|
||||||
cpuQuota = "70%";
|
CpuQuota = "70%";
|
||||||
memoryAccounting = true;
|
MemoryAccounting = true;
|
||||||
memoryMax = "768M";
|
MemoryMax = "768M";
|
||||||
startLimitIntervalSec = 500;
|
StartLimitIntervalSec = 500;
|
||||||
startLimitBurst = 5;
|
StartLimitBurst = 5;
|
||||||
blockIOWeigth = 10;
|
BlockIOWeight = 10;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ lib, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.userdata;
|
|
||||||
directionArg =
|
|
||||||
if cfg.direction == ""
|
|
||||||
then ""
|
|
||||||
else "--direction=${cfg.direction}";
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
options.services.userdata = {
|
options.selfprivacy = {
|
||||||
# General server options
|
# General server options
|
||||||
hostname = mkOption {
|
hostname = mkOption {
|
||||||
description = "The hostname of the server.";
|
description = "The hostname of the server.";
|
||||||
|
@ -20,19 +12,20 @@ in
|
||||||
description = ''
|
description = ''
|
||||||
Domain used by the server
|
Domain used by the server
|
||||||
'';
|
'';
|
||||||
type = types.nullOr types.str;
|
# see: https://regexr.com/7p7ep, https://stackoverflow.com/a/26987741
|
||||||
|
type = lib.types.strMatching ''^(xn--)?[a-z0-9][a-z0-9_-]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$'';
|
||||||
};
|
};
|
||||||
timezone = mkOption {
|
timezone = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Timezone used by the server
|
Timezone used by the server
|
||||||
'';
|
'';
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = "Europe/Uzhgorod";
|
default = "Etc/UTC";
|
||||||
};
|
};
|
||||||
autoUpgrade = {
|
autoUpgrade = {
|
||||||
enable = mkOption {
|
enable = mkOption {
|
||||||
description = "Enable auto-upgrade of the server.";
|
description = "Enable auto-upgrade of the server.";
|
||||||
default = true;
|
default = false;
|
||||||
type = types.nullOr types.bool;
|
type = types.nullOr types.bool;
|
||||||
};
|
};
|
||||||
allowReboot = mkOption {
|
allowReboot = mkOption {
|
||||||
|
@ -41,6 +34,11 @@ in
|
||||||
type = types.nullOr types.bool;
|
type = types.nullOr types.bool;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
stateVersion = mkOption {
|
||||||
|
description = "State version of the server";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
########################
|
########################
|
||||||
# Server admin options #
|
# Server admin options #
|
||||||
########################
|
########################
|
||||||
|
@ -63,93 +61,24 @@ in
|
||||||
type = types.nullOr (types.listOf types.str);
|
type = types.nullOr (types.listOf types.str);
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
###############
|
|
||||||
# API options #
|
|
||||||
###############
|
|
||||||
api = {
|
|
||||||
enableSwagger = mkOption {
|
|
||||||
default = true;
|
|
||||||
description = ''
|
|
||||||
Enable Swagger UI
|
|
||||||
'';
|
|
||||||
type = types.bool;
|
|
||||||
};
|
|
||||||
skippedMigrations = mkOption {
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
List of migrations that should be skipped
|
|
||||||
'';
|
|
||||||
type = types.listOf types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
#############
|
#############
|
||||||
# Secrets #
|
# DNS #
|
||||||
#############
|
#############
|
||||||
backblaze = {
|
dns = {
|
||||||
bucket = mkOption {
|
provider = mkOption {
|
||||||
description = "Bucket name used for userdata backups";
|
description = "DNS provider that was defined at the initial setup process.";
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
};
|
};
|
||||||
};
|
useStagingACME = mkOption {
|
||||||
##############
|
description = "Use staging ACME server. Default is false";
|
||||||
# Services #
|
type = types.nullOr types.bool;
|
||||||
##############
|
|
||||||
bitwarden = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = false;
|
default = false;
|
||||||
type = types.nullOr types.bool;
|
|
||||||
};
|
|
||||||
location = mkOption {
|
|
||||||
default = "sda1";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
email = {
|
server = {
|
||||||
location = mkOption {
|
provider = mkOption {
|
||||||
default = "sda1";
|
description = "Server provider that was defined at the initial setup process.";
|
||||||
type = types.nullOr types.str;
|
type = types.str;
|
||||||
};
|
|
||||||
};
|
|
||||||
gitea = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = false;
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
};
|
|
||||||
location = mkOption {
|
|
||||||
default = "sda1";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
nextcloud = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = true;
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
};
|
|
||||||
location = mkOption {
|
|
||||||
default = "sda1";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
pleroma = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = false;
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
};
|
|
||||||
location = mkOption {
|
|
||||||
default = "sda1";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
jitsi = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = false;
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
ocserv = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = true;
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
#########
|
#########
|
||||||
|
@ -162,7 +91,7 @@ in
|
||||||
};
|
};
|
||||||
rootKeys = mkOption {
|
rootKeys = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Root SSH Keys
|
Root SSH authorized keys
|
||||||
'';
|
'';
|
||||||
type = types.nullOr (types.listOf types.str);
|
type = types.nullOr (types.listOf types.str);
|
||||||
default = [ "" ];
|
default = [ "" ];
|
||||||
|
@ -171,7 +100,7 @@ in
|
||||||
description = ''
|
description = ''
|
||||||
Password authentication for SSH
|
Password authentication for SSH
|
||||||
'';
|
'';
|
||||||
default = true;
|
default = false;
|
||||||
type = types.nullOr types.bool;
|
type = types.nullOr types.bool;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -198,6 +127,7 @@ in
|
||||||
useBinds = mkOption {
|
useBinds = mkOption {
|
||||||
type = types.nullOr types.bool;
|
type = types.nullOr types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
description = "Whether to bind-mount vmail and sieve folders";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -1,60 +0,0 @@
|
||||||
{ pkgs, lib, config, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.userdata;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
fileSystems = lib.mkIf cfg.useBinds {
|
|
||||||
"/var/lib/pleroma" = {
|
|
||||||
device = "/volumes/${cfg.pleroma.location}/pleroma";
|
|
||||||
options = [ "bind" ];
|
|
||||||
};
|
|
||||||
"/var/lib/postgresql" = {
|
|
||||||
device = "/volumes/${cfg.pleroma.location}/postgresql";
|
|
||||||
options = [ "bind" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
services = {
|
|
||||||
pleroma = {
|
|
||||||
enable = cfg.pleroma.enable;
|
|
||||||
user = "pleroma";
|
|
||||||
group = "pleroma";
|
|
||||||
configs = [
|
|
||||||
(builtins.replaceStrings
|
|
||||||
[ "$DOMAIN" "$LUSER" ]
|
|
||||||
[ cfg.domain cfg.username ]
|
|
||||||
(builtins.readFile ./config.exs))
|
|
||||||
];
|
|
||||||
};
|
|
||||||
postgresql = {
|
|
||||||
enable = true;
|
|
||||||
package = pkgs.postgresql_12;
|
|
||||||
initialScript = "/etc/setup.psql";
|
|
||||||
ensureDatabases = [
|
|
||||||
"pleroma"
|
|
||||||
];
|
|
||||||
ensureUsers = [
|
|
||||||
{
|
|
||||||
name = "pleroma";
|
|
||||||
ensurePermissions = {
|
|
||||||
"DATABASE pleroma" = "ALL PRIVILEGES";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
environment.etc."setup.psql".text = ''
|
|
||||||
CREATE USER pleroma;
|
|
||||||
CREATE DATABASE pleroma OWNER pleroma;
|
|
||||||
\c pleroma;
|
|
||||||
--Extensions made by ecto.migrate that need superuser access
|
|
||||||
CREATE EXTENSION IF NOT EXISTS citext;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
||||||
'';
|
|
||||||
users.users.pleroma = {
|
|
||||||
extraGroups = [ "postgres" ];
|
|
||||||
isNormalUser = false;
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "pleroma";
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (import ./common.nix config) bitwarden-env sp;
|
||||||
|
in
|
||||||
|
# FIXME do we really want to delete passwords on module deactivation!?
|
||||||
|
{
|
||||||
|
config = lib.mkIf (!sp.modules.bitwarden.enable) {
|
||||||
|
system.activationScripts.bitwarden =
|
||||||
|
lib.trivial.warn
|
||||||
|
(
|
||||||
|
"bitwarden service is disabled, ${bitwarden-env} will be removed!"
|
||||||
|
)
|
||||||
|
''
|
||||||
|
rm -f -v ${bitwarden-env}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
config:
|
||||||
|
{
|
||||||
|
sp = config.selfprivacy;
|
||||||
|
bitwarden-env = "/var/lib/bitwarden/.env";
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
[ "selfprivacy", "domain" ],
|
||||||
|
[ "selfprivacy", "useBinds" ],
|
||||||
|
[ "selfprivacy", "modules", "bitwarden" ]
|
||||||
|
]
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
description = "PoC SP module for Bitwarden password management solution";
|
||||||
|
|
||||||
|
outputs = { self }: {
|
||||||
|
nixosModules.default = _:
|
||||||
|
{ imports = [ ./module.nix ./cleanup-module.nix ]; };
|
||||||
|
configPathsNeeded =
|
||||||
|
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
secrets-filepath = "/etc/selfprivacy/secrets.json";
|
||||||
|
backup-dir = "/var/lib/bitwarden/backup";
|
||||||
|
cfg = sp.modules.bitwarden;
|
||||||
|
inherit (import ./common.nix config) bitwarden-env sp;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfprivacy.modules.bitwarden = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
type = lib.types.bool;
|
||||||
|
};
|
||||||
|
location = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
subdomain = lib.mkOption {
|
||||||
|
default = "password";
|
||||||
|
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf config.selfprivacy.modules.bitwarden.enable {
|
||||||
|
fileSystems = lib.mkIf sp.useBinds {
|
||||||
|
"/var/lib/bitwarden" = {
|
||||||
|
device = "/volumes/${cfg.location}/bitwarden";
|
||||||
|
options = [
|
||||||
|
"bind"
|
||||||
|
"x-systemd.required-by=bitwarden-secrets.service"
|
||||||
|
"x-systemd.required-by=backup-vaultwarden.service"
|
||||||
|
"x-systemd.required-by=vaultwarden.service"
|
||||||
|
"x-systemd.before=bitwarden-secrets.service"
|
||||||
|
"x-systemd.before=backup-vaultwarden.service"
|
||||||
|
"x-systemd.before=vaultwarden.service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"/var/lib/bitwarden_rs" = {
|
||||||
|
device = "/volumes/${cfg.location}/bitwarden_rs";
|
||||||
|
options = [
|
||||||
|
"bind"
|
||||||
|
"x-systemd.required-by=bitwarden-secrets.service"
|
||||||
|
"x-systemd.required-by=backup-vaultwarden.service"
|
||||||
|
"x-systemd.required-by=vaultwarden.service"
|
||||||
|
"x-systemd.before=bitwarden-secrets.service"
|
||||||
|
"x-systemd.before=backup-vaultwarden.service"
|
||||||
|
"x-systemd.before=vaultwarden.service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.vaultwarden = {
|
||||||
|
enable = true;
|
||||||
|
dbBackend = "sqlite";
|
||||||
|
backupDir = backup-dir;
|
||||||
|
environmentFile = "${bitwarden-env}";
|
||||||
|
config = {
|
||||||
|
domain = "https://${cfg.subdomain}.${sp.domain}/";
|
||||||
|
signupsAllowed = true;
|
||||||
|
rocketPort = 8222;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.services.bitwarden-secrets = {
|
||||||
|
before = [ "vaultwarden.service" ];
|
||||||
|
requiredBy = [ "vaultwarden.service" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
path = with pkgs; [ coreutils jq ];
|
||||||
|
script = ''
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
token="$(jq -r '.bitwarden.adminToken' ${secrets-filepath})"
|
||||||
|
if [ "$token" == "null" ]; then
|
||||||
|
# If it's null, empty the contents of the file
|
||||||
|
bitwarden_env=""
|
||||||
|
else
|
||||||
|
bitwarden_env="ADMIN_TOKEN=$token"
|
||||||
|
fi
|
||||||
|
|
||||||
|
install -C -m 0700 -o vaultwarden -g vaultwarden \
|
||||||
|
-d /var/lib/bitwarden
|
||||||
|
|
||||||
|
install -C -m 0600 -o vaultwarden -g vaultwarden -DT \
|
||||||
|
<(printf "%s" "$bitwarden_env") ${bitwarden-env}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
||||||
|
useACMEHost = sp.domain;
|
||||||
|
forceSSL = true;
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Strict-Transport-Security $hsts_header;
|
||||||
|
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
||||||
|
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
||||||
|
add_header X-Frame-Options SAMEORIGIN;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
||||||
|
expires 10m;
|
||||||
|
'';
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:8222";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# NixOS upstream bug? Otherwise, backup-vaultwarden cannot find sqlite DB.
|
||||||
|
systemd.services.backup-vaultwarden.unitConfig.ConditionPathExists =
|
||||||
|
"/var/lib/bitwarden_rs/db.sqlite3";
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
[ "selfprivacy", "domain" ],
|
||||||
|
[ "selfprivacy", "useBinds" ],
|
||||||
|
[ "selfprivacy", "modules", "gitea" ]
|
||||||
|
]
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
description = "PoC SP module for Gitea forge service";
|
||||||
|
|
||||||
|
outputs = { self }: {
|
||||||
|
nixosModules.default = import ./module.nix;
|
||||||
|
configPathsNeeded =
|
||||||
|
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
sp = config.selfprivacy;
|
||||||
|
stateDir =
|
||||||
|
if sp.useBinds
|
||||||
|
then "/volumes/${cfg.location}/gitea"
|
||||||
|
else "/var/lib/gitea";
|
||||||
|
cfg = sp.modules.gitea;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfprivacy.modules.gitea = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
type = lib.types.bool;
|
||||||
|
};
|
||||||
|
location = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
subdomain = lib.mkOption {
|
||||||
|
default = "git";
|
||||||
|
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
fileSystems = lib.mkIf sp.useBinds {
|
||||||
|
"/var/lib/gitea" = {
|
||||||
|
device = "/volumes/${cfg.location}/gitea";
|
||||||
|
options = [ "bind" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.gitea = {
|
||||||
|
enable = true;
|
||||||
|
inherit stateDir;
|
||||||
|
# log = {
|
||||||
|
# rootPath = "/var/lib/gitea/log";
|
||||||
|
# level = "Warn";
|
||||||
|
# };
|
||||||
|
user = "gitea";
|
||||||
|
database = {
|
||||||
|
type = "sqlite3";
|
||||||
|
host = "127.0.0.1";
|
||||||
|
name = "gitea";
|
||||||
|
user = "gitea";
|
||||||
|
path = "${stateDir}/data/gitea.db";
|
||||||
|
createDatabase = true;
|
||||||
|
};
|
||||||
|
# ssh = {
|
||||||
|
# enable = true;
|
||||||
|
# clonePort = 22;
|
||||||
|
# };
|
||||||
|
lfs = {
|
||||||
|
enable = true;
|
||||||
|
contentDir = "${stateDir}/lfs";
|
||||||
|
};
|
||||||
|
appName = "SelfPrivacy git Service";
|
||||||
|
repositoryRoot = "${stateDir}/repositories";
|
||||||
|
# cookieSecure = true;
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
DOMAIN = "${cfg.subdomain}.${sp.domain}";
|
||||||
|
ROOT_URL = "https://${cfg.subdomain}.${sp.domain}/";
|
||||||
|
HTTP_ADDR = "0.0.0.0";
|
||||||
|
HTTP_PORT = 3000;
|
||||||
|
};
|
||||||
|
mailer = {
|
||||||
|
ENABLED = false;
|
||||||
|
};
|
||||||
|
ui = {
|
||||||
|
DEFAULT_THEME = "arc-green";
|
||||||
|
SHOW_USER_EMAIL = false;
|
||||||
|
};
|
||||||
|
picture = {
|
||||||
|
DISABLE_GRAVATAR = true;
|
||||||
|
};
|
||||||
|
admin = {
|
||||||
|
ENABLE_KANBAN_BOARD = true;
|
||||||
|
};
|
||||||
|
repository = {
|
||||||
|
FORCE_PRIVATE = false;
|
||||||
|
};
|
||||||
|
session = {
|
||||||
|
COOKIE_SECURE = true;
|
||||||
|
};
|
||||||
|
log = {
|
||||||
|
ROOT_PATH = "${stateDir}/log";
|
||||||
|
LEVEL = "Warn";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
||||||
|
useACMEHost = sp.domain;
|
||||||
|
forceSSL = true;
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Strict-Transport-Security $hsts_header;
|
||||||
|
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
||||||
|
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
||||||
|
add_header X-Frame-Options DENY;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
||||||
|
expires 10m;
|
||||||
|
'';
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:3000";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.services.gitea.unitConfig.RequiresMountsFor =
|
||||||
|
lib.mkIf sp.useBinds "/volumes/${cfg.location}/gitea";
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
[
|
||||||
|
[ "selfprivacy", "domain" ],
|
||||||
|
[ "selfprivacy", "modules", "jitsi-meet" ]
|
||||||
|
]
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
description = "PoC SP module for Jitsi Meet video conferences server";
|
||||||
|
|
||||||
|
outputs = { self }: {
|
||||||
|
nixosModules.default = import ./module.nix;
|
||||||
|
configPathsNeeded =
|
||||||
|
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
domain = config.selfprivacy.domain;
|
||||||
|
cfg = config.selfprivacy.modules.jitsi-meet;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfprivacy.modules.jitsi-meet = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
type = lib.types.bool;
|
||||||
|
};
|
||||||
|
subdomain = lib.mkOption {
|
||||||
|
default = "meet";
|
||||||
|
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
services.jitsi-meet = {
|
||||||
|
enable = true;
|
||||||
|
hostName = "${cfg.subdomain}.${domain}";
|
||||||
|
nginx.enable = true;
|
||||||
|
interfaceConfig = {
|
||||||
|
SHOW_JITSI_WATERMARK = false;
|
||||||
|
SHOW_WATERMARK_FOR_GUESTS = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."${cfg.subdomain}.${domain}" = {
|
||||||
|
forceSSL = true;
|
||||||
|
useACMEHost = domain;
|
||||||
|
enableACME = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (import ./common.nix config) sp db-pass-filepath admin-pass-filepath;
|
||||||
|
in
|
||||||
|
# FIXME do we really want to delete passwords on module deactivation!?
|
||||||
|
{
|
||||||
|
config = lib.mkIf (!sp.modules.nextcloud.enable) {
|
||||||
|
system.activationScripts.nextcloudSecrets =
|
||||||
|
lib.trivial.warn
|
||||||
|
(
|
||||||
|
"nextcloud service is disabled, " +
|
||||||
|
"${db-pass-filepath} and ${admin-pass-filepath} will be removed!"
|
||||||
|
)
|
||||||
|
''
|
||||||
|
rm -f -v ${db-pass-filepath}
|
||||||
|
rm -f -v ${admin-pass-filepath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
config: rec {
|
||||||
|
sp = config.selfprivacy;
|
||||||
|
secrets-filepath = "/etc/selfprivacy/secrets.json";
|
||||||
|
db-pass-filepath = "/var/lib/nextcloud/db-pass";
|
||||||
|
admin-pass-filepath = "/var/lib/nextcloud/admin-pass";
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
[ "selfprivacy", "domain" ],
|
||||||
|
[ "selfprivacy", "useBinds" ],
|
||||||
|
[ "selfprivacy", "modules", "nextcloud" ]
|
||||||
|
]
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
description = "PoC SP module for nextcloud";
|
||||||
|
|
||||||
|
outputs = { self }: {
|
||||||
|
nixosModules.default = _:
|
||||||
|
{ imports = [ ./module.nix ./cleanup-module.nix ]; };
|
||||||
|
configPathsNeeded =
|
||||||
|
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
{
|
||||||
|
options.selfprivacy.modules.nextcloud = with lib; {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
location = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
subdomain = lib.mkOption {
|
||||||
|
default = "cloud";
|
||||||
|
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
inherit (import ./common.nix config)
|
||||||
|
sp secrets-filepath db-pass-filepath admin-pass-filepath;
|
||||||
|
cfg = sp.modules.nextcloud;
|
||||||
|
hostName = "${cfg.subdomain}.${sp.domain}";
|
||||||
|
in
|
||||||
|
lib.mkIf sp.modules.nextcloud.enable {
|
||||||
|
fileSystems = lib.mkIf sp.useBinds {
|
||||||
|
"/var/lib/nextcloud" = {
|
||||||
|
device = "/volumes/${cfg.location}/nextcloud";
|
||||||
|
options = [
|
||||||
|
"bind"
|
||||||
|
"x-systemd.required-by=nextcloud-setup.service"
|
||||||
|
"x-systemd.required-by=nextcloud-secrets.service"
|
||||||
|
"x-systemd.before=nextcloud-setup.service"
|
||||||
|
"x-systemd.before=nextcloud-secrets.service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.services.nextcloud-secrets = {
|
||||||
|
before = [ "nextcloud-setup.service" ];
|
||||||
|
requiredBy = [ "nextcloud-setup.service" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
path = with pkgs; [ coreutils jq ];
|
||||||
|
script = ''
|
||||||
|
databasePassword=$(jq -re '.modules.nextcloud.databasePassword' ${secrets-filepath})
|
||||||
|
adminPassword=$(jq -re '.modules.nextcloud.adminPassword' ${secrets-filepath})
|
||||||
|
|
||||||
|
install -C -m 0440 -o nextcloud -g nextcloud -DT \
|
||||||
|
<(printf "%s\n" "$databasePassword") \
|
||||||
|
${db-pass-filepath}
|
||||||
|
|
||||||
|
install -C -m 0440 -o nextcloud -g nextcloud -DT \
|
||||||
|
<(printf "%s\n" "$adminPassword") \
|
||||||
|
${admin-pass-filepath}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
services.nextcloud = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.nextcloud26;
|
||||||
|
inherit hostName;
|
||||||
|
|
||||||
|
# Use HTTPS for links
|
||||||
|
https = true;
|
||||||
|
|
||||||
|
# auto-update Nextcloud Apps
|
||||||
|
autoUpdateApps.enable = true;
|
||||||
|
# set what time makes sense for you
|
||||||
|
autoUpdateApps.startAt = "05:00:00";
|
||||||
|
|
||||||
|
config = {
|
||||||
|
# further forces Nextcloud to use HTTPS
|
||||||
|
overwriteProtocol = "https";
|
||||||
|
|
||||||
|
dbtype = "sqlite";
|
||||||
|
dbuser = "nextcloud";
|
||||||
|
dbname = "nextcloud";
|
||||||
|
dbpassFile = db-pass-filepath;
|
||||||
|
adminpassFile = admin-pass-filepath;
|
||||||
|
adminuser = "admin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts.${hostName} = {
|
||||||
|
useACMEHost = sp.domain;
|
||||||
|
forceSSL = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
[ "security", "acme", "certs" ],
|
||||||
|
[ "selfprivacy", "domain" ],
|
||||||
|
[ "selfprivacy", "modules", "ocserv" ]
|
||||||
|
]
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
description = "PoC SP module for OpenConnect VPN server (ocserv)";
|
||||||
|
|
||||||
|
outputs = { self }: {
|
||||||
|
nixosModules.default = import ./module.nix;
|
||||||
|
configPathsNeeded =
|
||||||
|
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
domain = config.selfprivacy.domain;
|
||||||
|
cert = "${config.security.acme.certs.${domain}.directory}/fullchain.pem";
|
||||||
|
key = "${config.security.acme.certs.${domain}.directory}/key.pem";
|
||||||
|
cfg = config.selfprivacy.modules.ocserv;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfprivacy.modules.ocserv = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
type = lib.types.bool;
|
||||||
|
};
|
||||||
|
subdomain = lib.mkOption {
|
||||||
|
default = "vpn";
|
||||||
|
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
users.groups.ocserv.members = [ "ocserv" ];
|
||||||
|
users.users.ocserv = {
|
||||||
|
isNormalUser = false;
|
||||||
|
isSystemUser = true;
|
||||||
|
extraGroups = [ "acmereceivers" ];
|
||||||
|
group = "ocserv";
|
||||||
|
};
|
||||||
|
services.ocserv = {
|
||||||
|
enable = true;
|
||||||
|
config = ''
|
||||||
|
socket-file = /var/run/ocserv-socket
|
||||||
|
|
||||||
|
auth = "pam"
|
||||||
|
|
||||||
|
tcp-port = 8443
|
||||||
|
udp-port = 8443
|
||||||
|
|
||||||
|
server-cert = ${cert}
|
||||||
|
server-key = ${key}
|
||||||
|
|
||||||
|
compression = true
|
||||||
|
|
||||||
|
max-clients = 0
|
||||||
|
max-same-clients = 6
|
||||||
|
|
||||||
|
try-mtu-discovery = true
|
||||||
|
|
||||||
|
idle-timeout=1200
|
||||||
|
mobile-idle-timeout=2400
|
||||||
|
|
||||||
|
default-domain = ${cfg.subdomain}.${domain}
|
||||||
|
|
||||||
|
device = vpn0
|
||||||
|
|
||||||
|
ipv4-network = 10.10.10.0
|
||||||
|
ipv4-netmask = 255.255.255.0
|
||||||
|
|
||||||
|
tunnel-all-dns = true
|
||||||
|
dns = 1.1.1.1
|
||||||
|
dns = 1.0.0.1
|
||||||
|
|
||||||
|
route = default
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."${cfg.subdomain}.${domain}" = {
|
||||||
|
useACMEHost = domain;
|
||||||
|
forceSSL = true;
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Strict-Transport-Security $hsts_header;
|
||||||
|
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
||||||
|
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
||||||
|
add_header X-Frame-Options DENY;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
||||||
|
expires 10m;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
systemd.services.ocserv.unitConfig.ConditionPathExists = [ cert key ];
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (import ./common.nix config) secrets-exs sp;
|
||||||
|
in
|
||||||
|
# FIXME do we really want to delete passwords on module deactivation!?
|
||||||
|
{
|
||||||
|
config = lib.mkIf (!sp.modules.pleroma.enable) {
|
||||||
|
system.activationScripts.pleroma =
|
||||||
|
lib.trivial.warn
|
||||||
|
(
|
||||||
|
"pleroma service is disabled, ${secrets-exs} will be removed!"
|
||||||
|
)
|
||||||
|
''
|
||||||
|
rm -f -v ${secrets-exs}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
config:
|
||||||
|
{
|
||||||
|
sp = config.selfprivacy;
|
||||||
|
secrets-exs = "/var/lib/pleroma/secrets.exs";
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
[
|
||||||
|
[ "selfprivacy", "domain" ],
|
||||||
|
[ "selfprivacy", "username" ],
|
||||||
|
[ "selfprivacy", "useBinds" ],
|
||||||
|
[ "selfprivacy", "modules", "pleroma" ]
|
||||||
|
]
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
description = "PoC SP module for Pleroma lightweight fediverse server";
|
||||||
|
|
||||||
|
outputs = { self }: {
|
||||||
|
nixosModules.default = import ./module.nix;
|
||||||
|
configPathsNeeded =
|
||||||
|
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
secrets-filepath = "/etc/selfprivacy/secrets.json";
|
||||||
|
cfg = config.selfprivacy.modules.pleroma;
|
||||||
|
inherit (import ./common.nix config) secrets-exs sp;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.selfprivacy.modules.pleroma = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
type = lib.types.bool;
|
||||||
|
};
|
||||||
|
location = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
subdomain = lib.mkOption {
|
||||||
|
default = "social";
|
||||||
|
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
fileSystems = lib.mkIf sp.useBinds {
|
||||||
|
"/var/lib/pleroma" = {
|
||||||
|
device = "/volumes/${cfg.location}/pleroma";
|
||||||
|
options = [
|
||||||
|
"bind"
|
||||||
|
"x-systemd.required-by=pleroma-secrets.service"
|
||||||
|
"x-systemd.required-by=pleroma.service"
|
||||||
|
"x-systemd.before=pleroma.service"
|
||||||
|
"x-systemd.before=pleroma-secrets.service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"/var/lib/postgresql" = {
|
||||||
|
device = "/volumes/${cfg.location}/postgresql";
|
||||||
|
options = [
|
||||||
|
"bind"
|
||||||
|
"x-systemd.required-by=pleroma-secrets.service"
|
||||||
|
"x-systemd.required-by=pleroma.service"
|
||||||
|
"x-systemd.before=pleroma-secrets.service"
|
||||||
|
"x-systemd.before=pleroma.service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services = {
|
||||||
|
pleroma = {
|
||||||
|
enable = true;
|
||||||
|
user = "pleroma";
|
||||||
|
group = "pleroma";
|
||||||
|
configs = [
|
||||||
|
(builtins.replaceStrings
|
||||||
|
[ "$DOMAIN" "$LUSER" ]
|
||||||
|
[ sp.domain sp.username ]
|
||||||
|
(builtins.readFile ./config.exs.in))
|
||||||
|
];
|
||||||
|
};
|
||||||
|
postgresql = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.postgresql_12;
|
||||||
|
initialScript = "/etc/setup.psql";
|
||||||
|
ensureDatabases = [
|
||||||
|
"pleroma"
|
||||||
|
];
|
||||||
|
ensureUsers = [
|
||||||
|
{
|
||||||
|
name = "pleroma";
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.services.pleroma-secrets = {
|
||||||
|
before = [ "pleroma.service" ];
|
||||||
|
requiredBy = [ "pleroma.service" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
path = with pkgs; [ coreutils jq ];
|
||||||
|
script = ''
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
password="$(jq -re '.databasePassword' ${secrets-filepath})"
|
||||||
|
filecontents=$(cat <<- EOF
|
||||||
|
import Config
|
||||||
|
config :pleroma, Pleroma.Repo,
|
||||||
|
password: "$password"
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
install -C -m 0700 -o pleroma -g pleroma -d /var/lib/pleroma
|
||||||
|
|
||||||
|
install -C -m 0600 -o pleroma -g pleroma -DT \
|
||||||
|
<(printf "%s" "$filecontents") ${secrets-exs}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
environment.etc."setup.psql".text = ''
|
||||||
|
CREATE USER pleroma;
|
||||||
|
CREATE DATABASE pleroma OWNER pleroma;
|
||||||
|
\c pleroma;
|
||||||
|
--Extensions made by ecto.migrate that need superuser access
|
||||||
|
CREATE EXTENSION IF NOT EXISTS citext;
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
'';
|
||||||
|
users.users.pleroma = {
|
||||||
|
extraGroups = [ "postgres" ];
|
||||||
|
isNormalUser = false;
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "pleroma";
|
||||||
|
};
|
||||||
|
# seems to be an upstream nixpkgs/nixos bug (missing hexdump)
|
||||||
|
systemd.services.pleroma.path = [ pkgs.util-linux ];
|
||||||
|
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
||||||
|
useACMEHost = sp.domain;
|
||||||
|
root = "/var/www/${cfg.subdomain}.${sp.domain}";
|
||||||
|
forceSSL = true;
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Strict-Transport-Security $hsts_header;
|
||||||
|
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
||||||
|
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
||||||
|
add_header X-Frame-Options DENY;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
||||||
|
expires 10m;
|
||||||
|
'';
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:4000";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
[
|
||||||
|
[ "mailserver" ],
|
||||||
|
[ "security", "acme", "certs" ],
|
||||||
|
[ "selfprivacy", "domain" ],
|
||||||
|
[ "selfprivacy", "hashedMasterPassword" ],
|
||||||
|
[ "selfprivacy", "useBinds" ],
|
||||||
|
[ "selfprivacy", "username" ],
|
||||||
|
[ "selfprivacy", "users" ],
|
||||||
|
[ "services", "dovecot2" ],
|
||||||
|
[ "services", "opendkim" ],
|
||||||
|
[ "services", "postfix", "group" ],
|
||||||
|
[ "services", "postfix", "user" ],
|
||||||
|
[ "services", "redis" ],
|
||||||
|
[ "services", "rspamd" ],
|
||||||
|
[ "selfprivacy", "modules", "simple-nixos-mailserver" ]
|
||||||
|
]
|
|
@ -1,28 +1,30 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.services.userdata;
|
sp = config.selfprivacy;
|
||||||
in
|
in
|
||||||
|
lib.mkIf sp.modules.simple-nixos-mailserver.enable
|
||||||
{
|
{
|
||||||
imports = [
|
fileSystems = lib.mkIf sp.useBinds
|
||||||
(builtins.fetchTarball {
|
{
|
||||||
# Pick a commit from the branch you are interested in
|
"/var/vmail" = {
|
||||||
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/5675b122/nixos-mailserver-5675b122.tar.gz";
|
device =
|
||||||
|
"/volumes/${sp.modules.simple-nixos-mailserver.location}/vmail";
|
||||||
# And set its hash
|
options = [
|
||||||
sha256 = "1fwhb7a5v9c98nzhf3dyqf3a5ianqh7k50zizj8v5nmj3blxw4pi";
|
"bind"
|
||||||
})
|
"x-systemd.required-by=postfix.service"
|
||||||
];
|
"x-systemd.before=postfix.service"
|
||||||
|
];
|
||||||
fileSystems = lib.mkIf cfg.useBinds {
|
};
|
||||||
"/var/vmail" = {
|
"/var/sieve" = {
|
||||||
device = "/volumes/${cfg.email.location}/vmail";
|
device =
|
||||||
options = [ "bind" ];
|
"/volumes/${sp.modules.simple-nixos-mailserver.location}/sieve";
|
||||||
|
options = [
|
||||||
|
"bind"
|
||||||
|
"x-systemd.required-by=dovecot2.service"
|
||||||
|
"x-systemd.before=dovecot2.service"
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
"/var/sieve" = {
|
|
||||||
device = "/volumes/${cfg.email.location}/sieve";
|
|
||||||
options = [ "bind" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users = {
|
users.users = {
|
||||||
virtualMail = {
|
virtualMail = {
|
||||||
|
@ -30,16 +32,19 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
users.groups.acmereceivers.members = [ "dovecot2" "postfix" "virtualMail" ];
|
||||||
|
|
||||||
mailserver = {
|
mailserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
fqdn = cfg.domain;
|
fqdn = sp.domain;
|
||||||
domains = [ cfg.domain ];
|
domains = [ sp.domain ];
|
||||||
|
localDnsResolver = false;
|
||||||
|
|
||||||
# A list of all login accounts. To create the password hashes, use
|
# A list of all login accounts. To create the password hashes, use
|
||||||
# mkpasswd -m sha-512 "super secret password"
|
# mkpasswd -m sha-512 "super secret password"
|
||||||
loginAccounts = {
|
loginAccounts = {
|
||||||
"${cfg.username}@${cfg.domain}" = {
|
"${sp.username}@${sp.domain}" = {
|
||||||
hashedPassword = cfg.hashedMasterPassword;
|
hashedPassword = sp.hashedMasterPassword;
|
||||||
sieveScript = ''
|
sieveScript = ''
|
||||||
require ["fileinto", "mailbox"];
|
require ["fileinto", "mailbox"];
|
||||||
if header :contains "Chat-Version" "1.0"
|
if header :contains "Chat-Version" "1.0"
|
||||||
|
@ -51,7 +56,7 @@ in
|
||||||
};
|
};
|
||||||
} // builtins.listToAttrs (builtins.map
|
} // builtins.listToAttrs (builtins.map
|
||||||
(user: {
|
(user: {
|
||||||
name = "${user.username}@${cfg.domain}";
|
name = "${user.username}@${sp.domain}";
|
||||||
value = {
|
value = {
|
||||||
hashedPassword = user.hashedPassword;
|
hashedPassword = user.hashedPassword;
|
||||||
sieveScript = ''
|
sieveScript = ''
|
||||||
|
@ -64,15 +69,13 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
cfg.users);
|
sp.users);
|
||||||
|
|
||||||
extraVirtualAliases = {
|
extraVirtualAliases = {
|
||||||
"admin@${cfg.domain}" = "${cfg.username}@${cfg.domain}";
|
"admin@${sp.domain}" = "${sp.username}@${sp.domain}";
|
||||||
};
|
};
|
||||||
|
|
||||||
certificateScheme = 1;
|
certificateScheme = "acme";
|
||||||
certificateFile = "/var/lib/acme/${cfg.domain}/fullchain.pem";
|
|
||||||
keyFile = "/var/lib/acme/${cfg.domain}/key.pem";
|
|
||||||
|
|
||||||
# Enable IMAP and POP3
|
# Enable IMAP and POP3
|
||||||
enableImap = true;
|
enableImap = true;
|
|
@ -0,0 +1,126 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"blobs": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1604995301,
|
||||||
|
"narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=",
|
||||||
|
"owner": "simple-nixos-mailserver",
|
||||||
|
"repo": "blobs",
|
||||||
|
"rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "simple-nixos-mailserver",
|
||||||
|
"repo": "blobs",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1668681692,
|
||||||
|
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mailserver": {
|
||||||
|
"inputs": {
|
||||||
|
"blobs": "blobs",
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"nixpkgs-22_11": "nixpkgs-22_11",
|
||||||
|
"nixpkgs-23_05": "nixpkgs-23_05",
|
||||||
|
"utils": "utils"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1700085753,
|
||||||
|
"narHash": "sha256-qtib7f3eRwfaUF+VziJXiBcZFqpHCAXS4HlrFsnzzl4=",
|
||||||
|
"owner": "simple-nixos-mailserver",
|
||||||
|
"repo": "nixos-mailserver",
|
||||||
|
"rev": "008d78cc21959e33d0d31f375b88353a7d7121ae",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "simple-nixos-mailserver",
|
||||||
|
"repo": "nixos-mailserver",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1670751203,
|
||||||
|
"narHash": "sha256-XdoH1v3shKDGlrwjgrNX/EN8s3c+kQV7xY6cLCE8vcI=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "64e0bf055f9d25928c31fb12924e59ff8ce71e60",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-22_11": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1669558522,
|
||||||
|
"narHash": "sha256-yqxn+wOiPqe6cxzOo4leeJOp1bXE/fjPEi/3F/bBHv8=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ce5fe99df1f15a09a91a86be9738d68fadfbad82",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-22.11",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-23_05": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1684782344,
|
||||||
|
"narHash": "sha256-SHN8hPYYSX0thDrMLMWPWYulK3YFgASOrCsIL3AJ78g=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "8966c43feba2c701ed624302b6a935f97bcbdf88",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-23.05",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"mailserver": "mailserver"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1605370193,
|
||||||
|
"narHash": "sha256-YyMTf3URDL/otKdKgtoMChu4vfVL3vCMkRqpGifhUn0=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "5021eac20303a61fafe17224c087f5519baed54d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
description = "PoC SP module for the simple-nixos-mailserver";
|
||||||
|
|
||||||
|
inputs.mailserver.url =
|
||||||
|
gitlab:simple-nixos-mailserver/nixos-mailserver;
|
||||||
|
|
||||||
|
outputs = { self, mailserver }: {
|
||||||
|
nixosModules.default = _: {
|
||||||
|
imports = [
|
||||||
|
mailserver.nixosModules.default
|
||||||
|
./options.nix
|
||||||
|
./config.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
configPathsNeeded =
|
||||||
|
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||||
|
|
||||||
|
# TODO generate json docs from module? something like:
|
||||||
|
# nix eval --impure --expr 'let flake = builtins.getFlake (builtins.toPath ./.); pkgs = flake.inputs.mailserver.inputs.nixpkgs.legacyPackages.x86_64-linux; in (pkgs.nixosOptionsDoc { inherit (pkgs.lib.evalModules { modules = [ flake.nixosModules.default ]; }) options; }).optionsJSON'
|
||||||
|
# (doesn't work because of `assertions`)
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
options.selfprivacy.modules.simple-nixos-mailserver = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
type = lib.types.bool;
|
||||||
|
};
|
||||||
|
location = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,190 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json-schema.org/schema#",
|
|
||||||
"$id": "https://git.selfprivacy.org/inex/selfprivacy-nixos-config/raw/branch/master/userdata/schema.json",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"autoUpgrade": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"allowReboot": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hostname": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"domain": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"hashedMasterPassword": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"sshKeys": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"timezone": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"api": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"token": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"enableSwagger": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"skippedMigrations": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"backblaze": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"bucket": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"accountId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"accountKey": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["bucket", "accountId", "accountKey"]
|
|
||||||
},
|
|
||||||
"cloudflare": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"apiKey": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["apiKey"]
|
|
||||||
},
|
|
||||||
"databasePassword": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"bitwarden": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gitea": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nextcloud": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"databasePassword": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"adminPassword": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["databasePassword", "adminPassword"]
|
|
||||||
},
|
|
||||||
"pleroma": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jitsi": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ocserv": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resticPassword": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"ssh": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enable": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"rootKeys": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"passwordAuthentication": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"username": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"hashedPassword": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"sshKeys": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["username", "hashedPassword"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"hostname",
|
|
||||||
"domain",
|
|
||||||
"username",
|
|
||||||
"hashedMasterPassword",
|
|
||||||
"backblaze",
|
|
||||||
"cloudflare",
|
|
||||||
"databasePassword",
|
|
||||||
"nextcloud",
|
|
||||||
"resticPassword"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json-schema.org/schema#",
|
|
||||||
"$id": "https://git.selfprivacy.org/inex/selfprivacy-nixos-config/raw/branch/master/userdata/tokens_schema.json",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"tokens": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"token": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"date": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"token",
|
|
||||||
"name",
|
|
||||||
"date"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"recovery_token": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"token": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"date": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"expiration": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"uses_left": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"token",
|
|
||||||
"date"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"new_device": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"token": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"date": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"expiration": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"token",
|
|
||||||
"date",
|
|
||||||
"expiration"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"tokens"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,10 +1,11 @@
|
||||||
{ pkgs, config, ... }:
|
{ config, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.services.userdata;
|
cfg = config.selfprivacy;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
users.mutableUsers = false;
|
|
||||||
users = {
|
users = {
|
||||||
|
mutableUsers = false;
|
||||||
|
allowNoPasswordLogin = true;
|
||||||
users = {
|
users = {
|
||||||
"${cfg.username}" = {
|
"${cfg.username}" = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
{ pkgs, lib, ... }:
|
|
||||||
let
|
|
||||||
jsonData = builtins.fromJSON (builtins.readFile ./userdata/userdata.json);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
services.userdata = {
|
|
||||||
hostname = lib.attrsets.attrByPath [ "hostname" ] null jsonData;
|
|
||||||
domain = lib.attrsets.attrByPath [ "domain" ] null jsonData;
|
|
||||||
timezone = lib.attrsets.attrByPath [ "timezone" ] "Europe/Uzhgorod" jsonData;
|
|
||||||
autoUpgrade = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "autoUpgrade" "enable" ] true jsonData;
|
|
||||||
allowReboot = lib.attrsets.attrByPath [ "autoUpgrade" "allowReboot" ] true jsonData;
|
|
||||||
};
|
|
||||||
username = lib.attrsets.attrByPath [ "username" ] null jsonData;
|
|
||||||
hashedMasterPassword = lib.attrsets.attrByPath [ "hashedMasterPassword" ] null jsonData;
|
|
||||||
sshKeys = lib.attrsets.attrByPath [ "sshKeys" ] [ ] jsonData;
|
|
||||||
api = {
|
|
||||||
enableSwagger = lib.attrsets.attrByPath [ "api" "enableSwagger" ] false jsonData;
|
|
||||||
skippedMigrations = lib.attrsets.attrByPath [ "api" "skippedMigrations" ] [ ] jsonData;
|
|
||||||
};
|
|
||||||
backblaze = {
|
|
||||||
bucket = lib.attrsets.attrByPath [ "backblaze" "bucket" ] "" jsonData;
|
|
||||||
};
|
|
||||||
bitwarden = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "bitwarden" "enable" ] false jsonData;
|
|
||||||
location = lib.attrsets.attrByPath [ "bitwarden" "location" ] "sda1" jsonData;
|
|
||||||
};
|
|
||||||
gitea = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "gitea" "enable" ] false jsonData;
|
|
||||||
location = lib.attrsets.attrByPath [ "gitea" "location" ] "sda1" jsonData;
|
|
||||||
};
|
|
||||||
nextcloud = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "nextcloud" "enable" ] false jsonData;
|
|
||||||
location = lib.attrsets.attrByPath [ "nextcloud" "location" ] "sda1" jsonData;
|
|
||||||
};
|
|
||||||
pleroma = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "pleroma" "enable" ] false jsonData;
|
|
||||||
location = lib.attrsets.attrByPath [ "pleroma" "location" ] "sda1" jsonData;
|
|
||||||
};
|
|
||||||
jitsi = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "jitsi" "enable" ] false jsonData;
|
|
||||||
};
|
|
||||||
ocserv = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "ocserv" "enable" ] false jsonData;
|
|
||||||
};
|
|
||||||
ssh = {
|
|
||||||
enable = lib.attrsets.attrByPath [ "ssh" "enable" ] true jsonData;
|
|
||||||
rootKeys = lib.attrsets.attrByPath [ "ssh" "rootKeys" ] [ "" ] jsonData;
|
|
||||||
passwordAuthentication = lib.attrsets.attrByPath [ "ssh" "passwordAuthentication" ] true jsonData;
|
|
||||||
};
|
|
||||||
email = {
|
|
||||||
location = lib.attrsets.attrByPath [ "email" "location" ] "sda1" jsonData;
|
|
||||||
};
|
|
||||||
users = lib.attrsets.attrByPath [ "users" ] [ ] jsonData;
|
|
||||||
volumes = lib.attrsets.attrByPath [ "volumes" ] [ ] jsonData;
|
|
||||||
useBinds = lib.attrsets.attrByPath [ "useBinds" ] false jsonData;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
{ pkgs, config, ... }:
|
|
||||||
let
|
|
||||||
domain = config.services.userdata.domain;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
services.jitsi-meet = {
|
|
||||||
enable = config.services.userdata.jitsi.enable;
|
|
||||||
hostName = "meet.${domain}";
|
|
||||||
nginx.enable = true;
|
|
||||||
interfaceConfig = {
|
|
||||||
SHOW_JITSI_WATERMARK = false;
|
|
||||||
SHOW_WATERMARK_FOR_GUESTS = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
{ pkgs, config, ... }:
|
{ config, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.services.userdata;
|
cfg = config.selfprivacy;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
fileSystems = { } // builtins.listToAttrs (builtins.map
|
fileSystems = builtins.listToAttrs (builtins.map
|
||||||
(volume: {
|
(volume: {
|
||||||
name = "${volume.mountPoint}";
|
name = "${volume.mountPoint}";
|
||||||
value = {
|
value = {
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
{ pkgs, config, ... }:
|
|
||||||
let
|
|
||||||
domain = config.services.userdata.domain;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
users.groups.ocserv = {
|
|
||||||
members = [ "ocserv" ];
|
|
||||||
};
|
|
||||||
users.users.ocserv = {
|
|
||||||
isNormalUser = false;
|
|
||||||
isSystemUser = true;
|
|
||||||
extraGroups = [ "ocserv" "acmerecievers" ];
|
|
||||||
group = "ocserv";
|
|
||||||
};
|
|
||||||
services.ocserv = {
|
|
||||||
enable = config.services.userdata.ocserv.enable;
|
|
||||||
config = ''
|
|
||||||
socket-file = /var/run/ocserv-socket
|
|
||||||
|
|
||||||
auth = "pam"
|
|
||||||
|
|
||||||
tcp-port = 8443
|
|
||||||
udp-port = 8443
|
|
||||||
|
|
||||||
server-cert = /var/lib/acme/${domain}/fullchain.pem
|
|
||||||
server-key = /var/lib/acme/${domain}/key.pem
|
|
||||||
|
|
||||||
compression = true
|
|
||||||
|
|
||||||
max-clients = 0
|
|
||||||
max-same-clients = 6
|
|
||||||
|
|
||||||
try-mtu-discovery = true
|
|
||||||
|
|
||||||
idle-timeout=1200
|
|
||||||
mobile-idle-timeout=2400
|
|
||||||
|
|
||||||
default-domain = vpn.${domain}
|
|
||||||
|
|
||||||
device = vpn0
|
|
||||||
|
|
||||||
ipv4-network = 10.10.10.0
|
|
||||||
ipv4-netmask = 255.255.255.0
|
|
||||||
|
|
||||||
tunnel-all-dns = true
|
|
||||||
dns = 1.1.1.1
|
|
||||||
dns = 1.0.0.1
|
|
||||||
|
|
||||||
route = default
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ pkgs, config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
domain = config.services.userdata.domain;
|
domain = config.selfprivacy.domain;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
services.nginx = {
|
services.nginx = {
|
||||||
|
@ -16,42 +16,12 @@ in
|
||||||
map $scheme $hsts_header {
|
map $scheme $hsts_header {
|
||||||
https "max-age=31536000; includeSubdomains; preload";
|
https "max-age=31536000; includeSubdomains; preload";
|
||||||
}
|
}
|
||||||
|
proxy_headers_hash_bucket_size 128;
|
||||||
|
proxy_headers_hash_max_size 512;
|
||||||
'';
|
'';
|
||||||
|
|
||||||
virtualHosts = {
|
virtualHosts = {
|
||||||
"${domain}" = {
|
"${domain}" = {
|
||||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
useACMEHost = domain;
|
||||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
|
||||||
forceSSL = true;
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Strict-Transport-Security $hsts_header;
|
|
||||||
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
|
||||||
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
|
||||||
expires 10m;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"vpn.${domain}" = {
|
|
||||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
|
||||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
|
||||||
forceSSL = true;
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Strict-Transport-Security $hsts_header;
|
|
||||||
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
|
||||||
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
|
||||||
expires 10m;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"git.${domain}" = {
|
|
||||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
|
||||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
add_header Strict-Transport-Security $hsts_header;
|
add_header Strict-Transport-Security $hsts_header;
|
||||||
|
@ -65,53 +35,12 @@ in
|
||||||
'';
|
'';
|
||||||
locations = {
|
locations = {
|
||||||
"/" = {
|
"/" = {
|
||||||
proxyPass = "http://127.0.0.1:3000";
|
root = "/var/www/root";
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"cloud.${domain}" = {
|
|
||||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
|
||||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
|
||||||
forceSSL = true;
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Strict-Transport-Security $hsts_header;
|
|
||||||
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
|
||||||
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
|
||||||
expires 10m;
|
|
||||||
'';
|
|
||||||
locations = {
|
|
||||||
"/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:80/";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"password.${domain}" = {
|
|
||||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
|
||||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
|
||||||
forceSSL = true;
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Strict-Transport-Security $hsts_header;
|
|
||||||
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
|
||||||
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
|
||||||
expires 10m;
|
|
||||||
'';
|
|
||||||
locations = {
|
|
||||||
"/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8222";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"api.${domain}" = {
|
"api.${domain}" = {
|
||||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
useACMEHost = domain;
|
||||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
add_header Strict-Transport-Security $hsts_header;
|
add_header Strict-Transport-Security $hsts_header;
|
||||||
|
@ -130,27 +59,9 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"social.${domain}" = {
|
|
||||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
|
||||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
|
||||||
root = "/var/www/social.${domain}";
|
|
||||||
forceSSL = true;
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Strict-Transport-Security $hsts_header;
|
|
||||||
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
|
||||||
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
|
||||||
expires 10m;
|
|
||||||
'';
|
|
||||||
locations = {
|
|
||||||
"/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:4000";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /var/www/root 0750 nginx nginx - -"
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue