Initial commit

This commit is contained in:
Inex Code 2021-11-15 13:02:05 +03:00
commit 85aaf52635
28 changed files with 1360 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
userdata/userdata.json
hardware-configuration.nix
networking.nix

43
api/api-module.nix Normal file
View file

@ -0,0 +1,43 @@
{ config, lib, pkgs, ... }:
with lib;
let
selfprivacy-api = pkgs.callPackage ./api-package.nix { };
cfg = config.services.selfprivacy-api;
directionArg =
if cfg.direction == ""
then ""
else "--direction=${cfg.direction}";
in
{
options.services.selfprivacy-api = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Enable SelfPrivacy API service
'';
};
};
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";
} // 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 ];
after = [ "network-online.target" ];
wantedBy = [ "network-online.target" ];
serviceConfig = {
User = "root";
ExecStart = "${selfprivacy-api}/bin/app.py";
Restart = "always";
RestartSec = "5";
};
};
};
}

24
api/api-package.nix Normal file
View file

@ -0,0 +1,24 @@
{ nixpkgs ? import <nixpkgs> { }, pythonPkgs ? nixpkgs.pkgs.python39Packages }:
let
inherit (nixpkgs) pkgs;
inherit pythonPkgs;
selfprivacy-api = { buildPythonPackage, flask, flask-restful, setuptools }:
buildPythonPackage rec {
pname = "selfprivacy-api";
version = "1.1";
src = builtins.fetchGit {
url = "https://git.selfprivacy.org/ilchub/selfprivacy-rest-api.git";
rev = "dbb4c1095654bba88d4f0c91b7b195d5262976b6";
};
propagatedBuildInputs = [ flask flask-restful setuptools ];
meta = {
description = ''
SelfPrivacy Server Management API
'';
};
};
drv = pythonPkgs.callPackage selfprivacy-api { };
in
if pkgs.lib.inNixShell then drv.env else drv

15
api/api.nix Normal file
View file

@ -0,0 +1,15 @@
{ pkgs, ... }:
{
services.selfprivacy-api = {
enable = true;
};
users.users."selfprivacy-api" = {
isNormalUser = false;
isSystemUser = true;
extraGroups = [ "opendkim" ];
};
users.groups."selfprivacy-api" = {
members = [ "selfprivacy-api" ];
};
}

35
backup/restic.nix Normal file
View file

@ -0,0 +1,35 @@
{ 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;
};
environment.etc."restic/resticPasswd".text = ''
${cfg.resticPassword}
'';
environment.etc."restic/s3Passwd".text = ''
AWS_ACCESS_KEY_ID=${cfg.backblaze.accountId}
AWS_SECRET_ACCESS_KEY=${cfg.backblaze.accountKey}
'';
}

82
configuration.nix Normal file
View file

@ -0,0 +1,82 @@
{ config, pkgs, lib, ... }:
{
imports = [
./hardware-configuration.nix
./variables-module.nix
./variables.nix
./vscode.nix
./files.nix
./users.nix
./mailserver/system/mailserver.nix
./mailserver/system/alps.nix
./vpn/ocserv.nix
./api/api.nix
./api/api-module.nix
./social/pleroma.nix
./letsencrypt/acme.nix
./letsencrypt/resolve.nix
./backup/restic.nix
./passmgr/bitwarden.nix
./webserver/nginx.nix
./webserver/memcached.nix
./nextcloud/nextcloud.nix
./resources/limits.nix
./videomeet/jitsi.nix
./git/gitea.nix
];
boot.cleanTmpDir = true;
networking = {
hostName = config.services.userdata.hostname;
firewall = {
allowedTCPPorts = lib.mkForce [ 22 25 80 143 443 465 587 993 8443 ];
allowedUDPPorts = lib.mkForce [ 8443 ];
};
nameservers = [ "1.1.1.1" "1.0.0.1" ];
};
time.timeZone = "Europe/Uzhgorod";
i18n.defaultLocale = "en_GB.UTF-8";
users.users.root.openssh.authorizedKeys.keys = config.services.userdata.rootSshKeys;
services.openssh = {
enable = true;
passwordAuthentication = true;
permitRootLogin = "yes";
openFirewall = false;
};
programs.ssh = {
pubkeyAcceptedKeyTypes = [ "ssh-ed25519" ];
hostKeyAlgorithms = [ "ssh-ed25519" ];
};
environment.systemPackages = with pkgs; [
git
];
environment.variables = {
DOMAIN = config.services.userdata.domain;
};
system.autoUpgrade.enable = true;
system.autoUpgrade.allowReboot = false;
system.autoUpgrade.channel = https://nixos.org/channels/nixos-21.05-small;
nix = {
optimise.automatic = true;
gc = {
automatic = true;
options = "--delete-older-than 7d";
};
};
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = 1;
};
swapDevices = [
{
device = "/swapfile";
priority = 0;
size = 2048;
}
];
security = {
sudo = {
enable = true;
};
};
}

30
files.nix Normal file
View file

@ -0,0 +1,30 @@
{ config, pkgs, ... }:
let
cfg = config.services.userdata;
in
{
systemd.tmpfiles.rules =
let
nextcloudDBPass = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] cfg.nextcloud.databasePassword;
nextcloudAdminPass = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] cfg.nextcloud.adminPassword;
resticPass = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] cfg.resticPassword;
domain = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] cfg.domain;
cloudflareCredentials = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] ''
CF_API_KEY=${cfg.cloudflare.apiKey}
CLOUDFLARE_DNS_API_TOKEN=${cfg.cloudflare.apiKey}
CLOUDFLARE_ZONE_API_TOKEN=${cfg.cloudflare.apiKey}
'';
in
[
"d /var/restic 0660 restic - - -"
"d /var/bitwarden 0777 bitwarden_rs bitwarden_rs -"
"d /var/bitwarden/backup 0777 bitwarden_rs bitwarden_rs -"
"d /var/lib/pleroma 0600 pleroma pleroma - -"
"f /var/lib/pleroma/secrets.exs 0755 pleroma pleroma - -"
"f /var/domain 0444 selfprivacy-api selfprivacy-api - ${domain}"
"f /var/restic/restic-repo-password 0660 restic - - ${resticPass}"
"f /var/nextcloud-db-pass 0440 nextcloud nextcloud - ${nextcloudDBPass}"
"f /var/nextcloud-admin-pass 0440 nextcloud nextcloud - ${nextcloudAdminPass}"
"f /var/cloudflareCredentials.ini 0440 nginx acmerecievers - ${cloudflareCredentials}"
];
}

57
git/gitea.nix Normal file
View file

@ -0,0 +1,57 @@
{ config, pkgs, ... }:
let
cfg = config.services.userdata;
in
{
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;
};
};
};
};
}

22
letsencrypt/acme.nix Normal file
View file

@ -0,0 +1,22 @@
{ config, pkgs, ... }:
let
cfg = config.services.userdata;
in
{
users.groups.acmerecievers = {
members = [ "nginx" "dovecot2" "postfix" "virtualMail" "ocserv" ];
};
security.acme = {
acceptTerms = true;
email = "${cfg.username}@${cfg.domain}";
certs = {
"${cfg.domain}" = {
domain = "*.${cfg.domain}";
extraDomainNames = [ "${cfg.domain}" ];
group = "acmerecievers";
dnsProvider = "cloudflare";
credentialsFile = "/var/cloudflareCredentials.ini";
};
};
};
}

22
letsencrypt/resolve.nix Normal file
View file

@ -0,0 +1,22 @@
{ config, pkgs, ... }:
let
domain = config.services.userdata.domain;
in
{
systemd = {
services = {
"acme-${domain}" = {
serviceConfig = {
StartLimitBurst = 5;
StartLimitIntervalSec = 5;
Restart = "on-failure";
};
};
"nginx-config-reload" = {
serviceConfig = {
After = [ "acme-${domain}.service" ];
};
};
};
};
}

View file

@ -0,0 +1,30 @@
{ lib, fetchgit, buildGoModule, ... }:
buildGoModule rec {
pname = "alps";
version = "v1.0.0"; # latest available tag at the moment
src = fetchGit {
url = "https://git.selfprivacy.org/ilchub/selfprivacy-alps";
rev = "dc2109ca2fdabfbda5d924faa4947f5694d5d758";
};
vendorSha256 = "0bqg0qjam4mvh07wfil6l5spz32mk5a7kfxxnwfyva805pzmn6dk";
deleteVendor = false;
runVend = true;
buildPhase = ''
go build ./cmd/alps
'';
installPhase = ''
mkdir -p $out/bin
cp -r * $out/bin
'';
meta = with lib; {
description = "Webmail application for the dovecot/postfix mailserver";
homepage = "https://git.selfprivacy.org/ilchub/selfprivacy-alps";
license = licenses.mit;
};
}

View file

@ -0,0 +1,18 @@
{ pkgs, config, lib, fetchgit, buildGoModule, ... }:
let domain = config.services.userdata.domain;
in
{
nixpkgs.overlays =
[ (self: super: { alps = self.callPackage ./alps-package.nix { }; }) ];
systemd.services = {
alps = {
path = [ pkgs.alps pkgs.coreutils ];
serviceConfig = {
ExecStart =
"${pkgs.alps}/bin/alps -theme sourcehut imaps://${domain}:993 smtps://${domain}:465";
WorkingDirectory = "${pkgs.alps}/bin";
};
};
};
}

View file

@ -0,0 +1,85 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.userdata;
in
{
imports = [
(builtins.fetchTarball {
# Pick a commit from the branch you are interested in
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/5675b122/nixos-mailserver-5675b122.tar.gz";
# And set its hash
sha256 = "1fwhb7a5v9c98nzhf3dyqf3a5ianqh7k50zizj8v5nmj3blxw4pi";
})
];
services.dovecot2 = {
enablePAM = lib.mkForce true;
showPAMFailure = lib.mkForce true;
};
users.users = {
virtualMail = {
isNormalUser = false;
};
};
mailserver = {
enable = true;
fqdn = cfg.domain;
domains = [ cfg.domain ];
# A list of all login accounts. To create the password hashes, use
# mkpasswd -m sha-512 "super secret password"
loginAccounts = {
"${cfg.username}@${cfg.domain}" = {
hashedPassword = cfg.hashedMasterPassword;
catchAll = [ cfg.domain ];
sieveScript = ''
require ["fileinto", "mailbox"];
if header :contains "Chat-Version" "1.0"
{
fileinto :create "DeltaChat";
stop;
}
'';
} // builtins.listToAttrs (builtins.map
(user: {
name = "${user.username}@${cfg.domain}";
value = {
hashedPassword = user.hashedPassword;
catchAll = [ cfg.domain ];
sieveScript = ''
require ["fileinto", "mailbox"];
if header :contains "Chat-Version" "1.0"
{
fileinto :create "DeltaChat";
stop;
}
'';
};
})
cfg.users);
};
extraVirtualAliases = {
"admin@${cfg.domain}" = "${cfg.username}@${cfg.domain}";
};
certificateScheme = 1;
certificateFile = "/var/lib/acme/${cfg.domain}/fullchain.pem";
keyFile = "/var/lib/acme/${cfg.domain}/key.pem";
# Enable IMAP and POP3
enableImap = true;
enableImapSsl = true;
enablePop3 = false;
enablePop3Ssl = false;
dkimSelector = "selector";
# Enable the ManageSieve protocol
enableManageSieve = true;
virusScanning = false;
};
}

34
nextcloud/nextcloud.nix Normal file
View file

@ -0,0 +1,34 @@
{ pkgs, config, ... }:
let
cfg = config.services.userdata;
in
{
services.nextcloud = {
enable = cfg.nextcloud.enable;
package = pkgs.nextcloud21;
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 = "http";
# 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/nextcloud-db-pass";
adminpassFile = "/var/nextcloud-admin-pass";
adminuser = "admin";
};
};
}

16
passmgr/bitwarden.nix Normal file
View file

@ -0,0 +1,16 @@
{ pkgs, config, ... }:
let
cfg = config.services.userdata;
in
{
services.bitwarden_rs = {
enable = cfg.bitwarden.enable;
dbBackend = "sqlite";
backupDir = "/var/bitwarden/backup";
config = {
domain = "https://password.${cfg.domain}/";
signupsAllowed = true;
rocketPort = 8222;
};
};
}

48
resources/limits.nix Normal file
View file

@ -0,0 +1,48 @@
{ pkgs, ... }:
{
systemd.services = {
dovecot2 = {
serviceConfig = {
cpuAccounting = true;
cpuQuota = "20%";
memoryAccounting = true;
memoryMax = "256M";
startLimitIntervalSec = 500;
startLimitBurst = 5;
blockIOWeigth = 25;
};
};
postfix = {
serviceConfig = {
cpuAccounting = true;
cpuQuota = "20%";
memoryAccounting = true;
memoryMax = "256M";
startLimitIntervalSec = 500;
startLimitBurst = 5;
blockIOWeigth = 25;
};
};
ocserv = {
serviceConfig = {
cpuAccounting = true;
cpuQuota = "70%";
memoryAccounting = true;
memoryMax = "512M";
startLimitIntervalSec = 500;
startLimitBurst = 5;
};
};
nginx = {
serviceConfig = {
cpuAccounting = true;
cpuQuota = "70%";
memoryAccounting = true;
memoryMax = "768M";
startLimitIntervalSec = 500;
startLimitBurst = 5;
blockIOWeigth = 10;
};
};
};
}

44
social/config.exs Normal file
View file

@ -0,0 +1,44 @@
import Config
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "social.$DOMAIN", scheme: "https", port: 443],
http: [ip: {127, 0, 0, 1}, port: 4000]
#secret_key_base: "",
#signing_salt: ""
config :pleroma, :instance,
name: "social.$DOMAIN",
email: "$LUSER@$DOMAIN",
notify_email: "$LUSER@$DOMAIN",
limit: 5000,
upload_limit: 1073741824,
registrations_open: true
config :pleroma, :media_proxy,
enabled: false,
redirect_on_failure: true
#base_url: "https://cache.pleroma.social"
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "pleroma",
password: "$DB_PASSWORD",
database: "pleroma",
hostname: "localhost",
pool_size: 10
#config :web_push_encryption, :vapid_details,
#subject: "",
#public_key: "",
#private_key: ""
config :pleroma, :database, rum_enabled: false
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
config :pleroma, :http_security,
sts: true
#config :joken, default_signer: ""
config :pleroma, configurable_from_database: false

133
social/pleroma-module.nix Normal file
View file

@ -0,0 +1,133 @@
{ config, options, lib, pkgs, stdenv, ... }:
let
cfg = config.services.pleroma;
in
{
options = {
services.pleroma = with lib; {
enable = mkEnableOption "pleroma";
package = mkOption {
type = types.package;
default = pkgs.pleroma-otp;
description = "Pleroma package to use.";
};
user = mkOption {
type = types.str;
default = "pleroma";
description = "User account under which pleroma runs.";
};
group = mkOption {
type = types.str;
default = "pleroma";
description = "Group account under which pleroma runs.";
};
stateDir = mkOption {
type = types.str;
default = "/var/lib/pleroma";
readOnly = true;
description = "Directory where the pleroma service will save the uploads and static files.";
};
configs = mkOption {
type = with types; listOf str;
description = ''
Pleroma public configuration.
This list gets appended from left to
right into /etc/pleroma/config.exs. Elixir evaluates its
configuration imperatively, meaning you can override a
setting by appending a new str to this NixOS option list.
<emphasis>DO NOT STORE ANY PLEROMA SECRET
HERE</emphasis>, use
<link linkend="opt-services.pleroma.secretConfigFile">services.pleroma.secretConfigFile</link>
instead.
This setting is going to be stored in a file part of
the Nix store. The Nix store being world-readable, it's not
the right place to store any secret
Have a look to Pleroma section in the NixOS manual for more
informations.
'';
};
secretConfigFile = mkOption {
type = types.str;
default = "/var/lib/pleroma/secrets.exs";
description = ''
Path to the file containing your secret pleroma configuration.
<emphasis>DO NOT POINT THIS OPTION TO THE NIX
STORE</emphasis>, the store being world-readable, it'll
compromise all your secrets.
'';
};
};
};
config = lib.mkIf cfg.enable {
users = {
users."${cfg.user}" = {
description = "Pleroma user";
home = cfg.stateDir;
extraGroups = [ cfg.group ];
};
groups."${cfg.group}" = { };
};
environment.systemPackages = [ cfg.package ];
environment.etc."/pleroma/config.exs".text = ''
${lib.concatMapStrings (x: "${x}") cfg.configs}
# The lau/tzdata library is trying to download the latest
# timezone database in the OTP priv directory by default.
# This directory being in the store, it's read-only.
# Setting that up to a more appropriate location.
config :tzdata, :data_dir, "/var/lib/pleroma/elixir_tzdata_data"
import_config "${cfg.secretConfigFile}"
'';
systemd.services.pleroma = {
description = "Pleroma social network";
after = [ "network-online.target" "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ config.environment.etc."/pleroma/config.exs".source ];
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Type = "exec";
WorkingDirectory = "~";
StateDirectory = "pleroma pleroma/static pleroma/uploads";
StateDirectoryMode = "700";
# Checking the conf file is there then running the database
# migration before each service start, just in case there are
# some pending ones.
#
# It's sub-optimal as we'll always run this, even if pleroma
# has not been updated. But the no-op process is pretty fast.
# Better be safe than sorry migration-wise.
ExecStartPre =
let preScript = pkgs.writers.writeBashBin "pleromaStartPre"
"${cfg.package}/bin/pleroma_ctl migrate";
in "${preScript}/bin/pleromaStartPre";
ExecStart = "${cfg.package}/bin/pleroma start";
ExecStop = "${cfg.package}/bin/pleroma stop";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
# Systemd sandboxing directives.
# Taken from the upstream contrib systemd service at
# pleroma/installation/pleroma.service
PrivateTmp = true;
ProtectHome = true;
ProtectSystem = "full";
PrivateDevices = false;
NoNewPrivileges = true;
CapabilityBoundingSet = "~CAP_SYS_ADMIN";
};
};
};
meta.maintainers = with lib.maintainers; [ ninjatrappeur ];
}

View file

@ -0,0 +1,69 @@
{ lib
, stdenv
, autoPatchelfHook
, fetchurl
, file
, makeWrapper
, ncurses
, nixosTests
, openssl
, unzip
, zlib
}:
stdenv.mkDerivation {
pname = "pleroma-otp";
version = "2.3.0";
# To find the latest binary release stable link, have a look at
# the CI pipeline for the latest commit of the stable branch
# https://git.pleroma.social/pleroma/pleroma/-/tree/stable
src = {
aarch64-linux = fetchurl {
url = "https://git.pleroma.social/pleroma/pleroma/-/jobs/182392/artifacts/download";
sha256 = "1drpd6xh7m2damxi5impb8jwvjl6m3qv5yxynl12i8g66vi3rbwf";
};
x86_64-linux = fetchurl {
url = "https://git.pleroma.social/pleroma/pleroma/-/jobs/182388/artifacts/download";
sha256 = "1c6l04gga9iigm249ywwcrjg6wzy8iiid652mws3j9dnl71w2sim";
};
}."${stdenv.hostPlatform.system}";
nativeBuildInputs = [ unzip ];
buildInputs = [
autoPatchelfHook
file
makeWrapper
ncurses
openssl
zlib
];
# mkDerivation fails to detect the zip nature of $src due to the
# missing .zip extension.
# Let's unpack the archive explicitely.
unpackCmd = "unzip $curSrc";
installPhase = ''
mkdir $out
cp -r * $out'';
# Pleroma is using the project's root path (here the store path)
# as its TMPDIR.
# Patching it to move the tmp dir to the actual tmpdir
postFixup = ''
wrapProgram $out/bin/pleroma --set-default RELEASE_TMP "/tmp"
wrapProgram $out/bin/pleroma_ctl --set-default RELEASE_TMP "/tmp"'';
passthru.tests = {
pleroma = nixosTests.pleroma;
};
meta = with lib; {
description = "ActivityPub microblogging server";
homepage = https://git.pleroma.social/pleroma/pleroma;
license = licenses.agpl3;
maintainers = with maintainers; [ ninjatrappeur ];
platforms = [ "x86_64-linux" "aarch64-linux" ];
};
}

41
social/pleroma.nix Normal file
View file

@ -0,0 +1,41 @@
{ pkgs, config, ... }:
let
cfg = config.services.userdata;
in
{
nixpkgs.overlays = [
(self: super: {
pleroma-otp = self.callPackage ./pleroma-package.nix { };
})
];
services = {
pleroma = {
enable = cfg.pleroma.enable;
user = "pleroma";
group = "pleroma";
configs = [
builtins.replaceStrings
[ "$DOMAIN" "$LUSER" "$DB_PASSWORD" ]
[ cfg.domain cfg.username cfg.databasePassword ]
(builtins.readFile ./config.exs)
];
};
postgresql = {
enable = true;
package = pkgs.postgresql_12;
initialScript = "/etc/setup.psql";
};
};
environment.etc."setup.psql".text = ''
CREATE USER pleroma WITH ENCRYPTED PASSWORD '${cfg.databasePassword}';
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" ];
};
}

142
userdata/schema.json Normal file
View file

@ -0,0 +1,142 @@
{
"$schema": "http://json-schema.org/schema#",
"$id": "https://git.selfprivacy.org/inex/selfprivacy-nixos-config/raw/branch/master/userdata/schema.json",
"type": "object",
"properties": {
"hostname": {
"type": "string"
},
"domain": {
"type": "string"
},
"username": {
"type": "string"
},
"hashedMasterPassword": {
"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"
},
"rootSshKeys": {
"type": "array",
"items": {
"type": "string"
}
},
"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"
]
}

12
users.nix Normal file
View file

@ -0,0 +1,12 @@
{ pkgs, config, ... }:
{
users.mutableUsers = false;
users = {
users = {
"${config.services.userdata.username}" = {
isNormalUser = true;
hashedPassword = config.services.userdata.hashedMasterPassword;
};
};
};
}

153
variables-module.nix Normal file
View file

@ -0,0 +1,153 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.userdata;
directionArg =
if cfg.direction == ""
then ""
else "--direction=${cfg.direction}";
userDef = {
options = {
username = mkOption {
type = types.nullOr types.string;
};
hashedPassword = mkOption {
type = types.nullOr types.string;
};
sshKeys = mkOption {
type = types.nullOr types.listOf types.string;
};
};
};
in
{
options.services.userdata = {
enable = mkOption {
default = true;
type = types.nullOr types.bool;
};
hostname = mkOption {
description = "The hostname of the server.";
type = types.nullOr types.string;
};
domain = mkOption {
description = ''
Domain used by the server
'';
type = types.nullOr types.string;
};
username = mkOption {
description = ''
Username that was defined at the initial setup process
'';
type = types.nullOr types.string;
};
hashedMasterPassword = mkOption {
description = ''
Hash of the password that was defined at the initial setup process
'';
type = types.nullOr types.string;
};
backblaze = {
bucket = mkOption {
description = "Bucket name used for userdata backups";
type = types.nullOr types.string;
};
accountId = mkOption {
description = "Backblaze B2 Account ID";
type = types.nullOr types.string;
};
accountKey = mkOption {
description = "Backblaze B2 Account Key.";
type = types.nullOr types.string;
};
};
cloudflare = {
apiKey = mkOption {
description = "Cloudflare API Key.";
type = types.nullOr types.string;
};
};
databasePassword = mkOption {
description = ''
Password for the database
'';
type = types.nullOr types.string;
};
bitwarden = {
enable = mkOption {
default = false;
type = types.nullOr types.bool;
};
};
gitea = {
enable = mkOption {
default = false;
type = types.nullOr types.bool;
};
};
nextcloud = {
enable = mkOption {
default = true;
type = types.nullOr types.bool;
};
databasePassword = mkOption {
description = ''
Password for the nextcloud database
'';
type = types.nullOr types.string;
};
adminPassword = mkOption {
description = ''
Password for the nextcloud admin user
'';
type = types.nullOr types.string;
};
};
pleroma = {
enable = mkOption {
default = false;
type = types.nullOr types.bool;
};
};
jitsi = {
enable = mkOption {
default = false;
type = types.nullOr types.bool;
};
};
ocserv = {
enable = mkOption {
default = true;
type = types.nullOr types.bool;
};
};
resticPassword = mkOption {
description = ''
Password for the restic
'';
type = types.nullOr types.string;
};
rootSshKeys = mkOption {
description = ''
Root SSH Keys
'';
type = types.nullOr types.listOf types.string;
};
timezone = mkOption {
description = ''
Timezone used by the server
'';
type = types.nullOr types.string;
default = "Europe/Uzhgorod";
};
users = mkOption {
description = ''
Users that will be created on the server
'';
type = with types; nullOr listOf attrsOf (submodule userDef);
};
};
}

6
variables.nix Normal file
View file

@ -0,0 +1,6 @@
{ pkgs, ... }:
{
services = {
userdata = builtins.fromJSON (builtins.readFile "./userdata/userdata.json");
};
}

15
videomeet/jitsi.nix Normal file
View file

@ -0,0 +1,15 @@
{ pkgs, config, ... }:
let
domain = config.services.userdata.domain;
in
{
services.jitsi-meet = {
enable = config.services.userdata.jitsi.enable;
hostName = "meet.${domain}";
nginx.enable = false;
interfaceConfig = {
SHOW_JITSI_WATERMARK = false;
SHOW_WATERMARK_FOR_GUESTS = false;
};
};
}

51
vpn/ocserv.nix Normal file
View file

@ -0,0 +1,51 @@
{ pkgs, config, ... }:
let
domain = config.services.userdata.domain;
in
{
users.groups.ocserv = {
members = [ "ocserv" ];
};
users.users.ocserv = {
isNormalUser = false;
isSystemUser = true;
extraGroups = [ "ocserv" "acmerecievers" ];
};
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
'';
};
}

13
webserver/memcached.nix Normal file
View file

@ -0,0 +1,13 @@
{ pkgs, ... }:
{
services = {
memcached = {
enable = true;
user = "memcached";
listen = "127.0.0.1";
port = 11211;
maxMemory = 64;
maxConnections = 1024;
};
};
}

117
webserver/nginx.nix Normal file
View file

@ -0,0 +1,117 @@
{ pkgs, config, ... }:
let
domain = config.services.userdata.domain;
in
{
services.nginx = {
enable = true;
enableReload = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
clientMaxBodySize = "1024m";
virtualHosts = {
"${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
forceSSL = true;
};
"vpn.${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
forceSSL = true;
};
"git.${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://127.0.0.1:3000";
};
};
};
"cloud.${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://127.0.0.1:80/";
};
};
};
"meet.${domain}" = {
forceSSL = true;
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
root = pkgs.jitsi-meet;
extraConfig = ''
ssi on;
'';
locations = {
"@root_path" = {
extraConfig = ''
rewrite ^/(.*)$ / break;
'';
};
"~ ^/([^/\\?&:'\"]+)$" = {
tryFiles = "$uri @root_path";
};
"=/http-bind" = {
proxyPass = "http://localhost:5280/http-bind";
extraConfig = ''
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
'';
};
"=/external_api.js" = {
alias = "${pkgs.jitsi-meet}/libs/external_api.min.js";
};
"=/config.js" = {
alias = "${pkgs.jitsi-meet}/config.js";
};
"=/interface_config.js" = {
alias = "${pkgs.jitsi-meet}/interface_config.js";
};
};
};
"password.${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://127.0.0.1:8222";
};
};
};
"api.${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://127.0.0.1:5050";
};
};
};
"social.${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
root = "/var/www/social.${domain}";
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://127.0.0.1:4000";
};
};
extraConfig = ''
client_max_body_size 1024m;
'';
};
};
};
}