Compare commits
4 commits
master
...
96a3d5af6d
Author | SHA1 | Date | |
---|---|---|---|
Tristan Daniël Maat | 96a3d5af6d | ||
Tristan Daniël Maat | e9c9dbfa41 | ||
Tristan Daniël Maat | 9e9680ddf7 | ||
Tristan Daniël Maat | d41f58e378 |
7
.sops.yaml
Normal file
7
.sops.yaml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
keys:
|
||||||
|
- &tlater 535B61015823443941C744DD12264F6BBDFABA89
|
||||||
|
|
||||||
|
creation_rules:
|
||||||
|
- key_groups:
|
||||||
|
- pgp:
|
||||||
|
- *tlater
|
|
@ -1,9 +1,12 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
{
|
let inherit (lib.attrsets) mapAttrs;
|
||||||
|
|
||||||
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
./services/gitea.nix
|
./services/gitea.nix
|
||||||
./services/minecraft.nix
|
./services/minecraft.nix
|
||||||
|
./services/monitoring.nix
|
||||||
./services/nextcloud.nix
|
./services/nextcloud.nix
|
||||||
./services/webserver.nix
|
./services/webserver.nix
|
||||||
./ids.nix
|
./ids.nix
|
||||||
|
@ -34,6 +37,16 @@
|
||||||
|
|
||||||
time.timeZone = "Europe/London";
|
time.timeZone = "Europe/London";
|
||||||
|
|
||||||
|
sops = {
|
||||||
|
gnupg = {
|
||||||
|
home = "/var/lib/sops";
|
||||||
|
sshKeyPaths = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultSopsFile = "/etc/sops/secrets.yaml";
|
||||||
|
validateSopsFiles = false;
|
||||||
|
};
|
||||||
|
|
||||||
users.users.tlater = {
|
users.users.tlater = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
extraGroups = [ "wheel" ];
|
extraGroups = [ "wheel" ];
|
||||||
|
@ -57,6 +70,13 @@
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
clientMaxBodySize = "10G";
|
clientMaxBodySize = "10G";
|
||||||
domain = "tlater.net";
|
domain = "tlater.net";
|
||||||
|
commonHttpConfig = ''
|
||||||
|
log_format custom '$remote_addr - $remote_user [$time_local] '
|
||||||
|
'"$request" $status $body_bytes_sent '
|
||||||
|
'"$http_referrer" "$http_user_agent" '
|
||||||
|
'$upstream_response_time $request_length $request_time';
|
||||||
|
access_log /var/log/nginx/access.log custom;
|
||||||
|
'';
|
||||||
|
|
||||||
virtualHosts = let
|
virtualHosts = let
|
||||||
host = port: extra:
|
host = port: extra:
|
||||||
|
@ -73,9 +93,20 @@
|
||||||
"${domain}" = host 3002 { serverAliases = [ "www.${domain}" ]; };
|
"${domain}" = host 3002 { serverAliases = [ "www.${domain}" ]; };
|
||||||
"gitea.${domain}" = host 3000 { };
|
"gitea.${domain}" = host 3000 { };
|
||||||
"nextcloud.${domain}" = host 3001 { };
|
"nextcloud.${domain}" = host 3001 { };
|
||||||
|
"grafana.${domain}" = host 3003 { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Allow nginxlog group users to read the nginx log
|
||||||
|
users.groups.nginxlog.gid = null;
|
||||||
|
systemd.services.nginx.serviceConfig = {
|
||||||
|
SupplementaryGroups = [ "nginxlog" ];
|
||||||
|
LogsDirectoryMode = lib.mkOverride 99 "0751";
|
||||||
|
ExecStartPost = [
|
||||||
|
"+${pkgs.coreutils}/bin/chown nginx:nginxlog \${LOGS_DIRECTORY}/access.log \${LOGS_DIRECTORY}/error.log"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
security.acme = {
|
security.acme = {
|
||||||
email = "tm@tlater.net";
|
email = "tm@tlater.net";
|
||||||
acceptTerms = true;
|
acceptTerms = true;
|
||||||
|
|
186
configuration/services/monitoring.nix
Normal file
186
configuration/services/monitoring.nix
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (builtins) attrNames concatStringsSep;
|
||||||
|
inherit (lib) stringAfter;
|
||||||
|
inherit (lib.attrsets) filterAttrs mapAttrsToList;
|
||||||
|
inherit (pkgs) openssl writeText;
|
||||||
|
|
||||||
|
domain = "grafana.${config.services.nginx.domain}";
|
||||||
|
keydir = "/run/tempsecrets.d";
|
||||||
|
certdir = "/run/tempcerts.d";
|
||||||
|
nonTlsExporters =
|
||||||
|
filterAttrs (_: exporter: exporter.enable && exporter.extraFlags == [ ])
|
||||||
|
config.services.prometheus.exporters;
|
||||||
|
tlsExporters =
|
||||||
|
filterAttrs (_: exporter: exporter.enable && exporter.extraFlags != [ ])
|
||||||
|
config.services.prometheus.exporters;
|
||||||
|
in {
|
||||||
|
services.grafana = {
|
||||||
|
inherit domain;
|
||||||
|
|
||||||
|
enable = true;
|
||||||
|
port = 3003;
|
||||||
|
|
||||||
|
security = {
|
||||||
|
adminUser = "tlater";
|
||||||
|
adminPasswordFile = "/run/secrets/grafana-admin-pass";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraOptions = {
|
||||||
|
# All services grafana is allowed to source from
|
||||||
|
SECURITY_DATA_SOURCE_PROXY_WHITELIST = "localhost:4000";
|
||||||
|
|
||||||
|
# We want this to always go through the nixos config
|
||||||
|
SECURITY_DISABLE_INITIAL_ADMIN_CREATION = "true";
|
||||||
|
|
||||||
|
# Our nginx host only forwards this through https, so we can use
|
||||||
|
# cookie_secure
|
||||||
|
SECURITY_COOKIE_SECURE = "true";
|
||||||
|
|
||||||
|
# These security settings aren't set by default yet, but
|
||||||
|
# probably will be in the future
|
||||||
|
SECURITY_COOKIE_SAMESITE = "true";
|
||||||
|
SECURITY_X_XSS_PROTECTION = "true";
|
||||||
|
};
|
||||||
|
|
||||||
|
provision = {
|
||||||
|
enable = true;
|
||||||
|
datasources = [{
|
||||||
|
name = "Prometheus";
|
||||||
|
type = "prometheus";
|
||||||
|
url = "https://localhost:4000";
|
||||||
|
jsonData = {
|
||||||
|
tlsAuth = true;
|
||||||
|
tlsAuthWithCACert = true;
|
||||||
|
};
|
||||||
|
# Currently, Grafana doesn't support specifying key/cert from
|
||||||
|
# a file, which makes this very tricky to automate.
|
||||||
|
#
|
||||||
|
# We'd need to set jsonSecureData, which would be
|
||||||
|
# world-readable, and completely break authentication.
|
||||||
|
#
|
||||||
|
# See this discussion:
|
||||||
|
# https://github.com/grafana/grafana/discussions/44296
|
||||||
|
#
|
||||||
|
# For now, hand-add key/cert every time the server restarts,
|
||||||
|
# if this becomes more permanent, maybe write a script that
|
||||||
|
# updates the key via API?
|
||||||
|
editable = true;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.prometheus = let
|
||||||
|
# See https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md#web-configuration
|
||||||
|
makeTlsConfig = client: server:
|
||||||
|
writeText "web.yml" ''
|
||||||
|
tls_server_config:
|
||||||
|
key_file: ${keydir}/${server}.pem
|
||||||
|
cert_file: ${certdir}/${server}.pem
|
||||||
|
|
||||||
|
client_auth_type: RequireAndVerifyClientCert
|
||||||
|
client_ca_file: ${certdir}/${client}.pem
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
enable = true;
|
||||||
|
port = 4000;
|
||||||
|
extraFlags =
|
||||||
|
[ "--web.config.file=${makeTlsConfig "grafana" "prometheus"}" ];
|
||||||
|
|
||||||
|
# From the documentation:
|
||||||
|
#
|
||||||
|
# > When credentials are stored in external files (password_file,
|
||||||
|
# > bearer_token_file, etc), they will not be visible to promtool
|
||||||
|
# > and it will report errors, despite a correct configuration.
|
||||||
|
checkConfig = false;
|
||||||
|
|
||||||
|
exporters = {
|
||||||
|
node = {
|
||||||
|
enable = true;
|
||||||
|
enabledCollectors = [ "systemd" ];
|
||||||
|
port = 4001;
|
||||||
|
extraFlags =
|
||||||
|
[ "--web.config=${makeTlsConfig "prometheus" "node-exporter"}" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nginxlog = {
|
||||||
|
enable = true;
|
||||||
|
group = "nginxlog";
|
||||||
|
port = 4002;
|
||||||
|
# Note: No way to enable TLS/auth here
|
||||||
|
settings.namespaces = [{
|
||||||
|
name = "nginx";
|
||||||
|
format = concatStringsSep " " [
|
||||||
|
"$remote_addr - $remote_user [$time_local]"
|
||||||
|
''"$request" $status $body_bytes_sent''
|
||||||
|
''"$http_referrer" "$http_user_agent"''
|
||||||
|
"$upstream_response_time $request_length $request_time"
|
||||||
|
];
|
||||||
|
source.files = [ "/var/log/nginx/access.log" ];
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
scrapeConfigs = (mapAttrsToList (name: exporter: {
|
||||||
|
job_name = name;
|
||||||
|
scheme = "https";
|
||||||
|
tls_config = {
|
||||||
|
ca_file = "${certdir}/${name}-exporter.pem";
|
||||||
|
cert_file = "${certdir}/prometheus.pem";
|
||||||
|
key_file = "${keydir}/prometheus.pem";
|
||||||
|
server_name = "localhost";
|
||||||
|
};
|
||||||
|
static_configs =
|
||||||
|
[{ targets = [ "127.0.0.1:${toString exporter.port}" ]; }];
|
||||||
|
}) tlsExporters) ++ mapAttrsToList (name: exporter: {
|
||||||
|
job_name = name;
|
||||||
|
scheme = "http";
|
||||||
|
static_configs =
|
||||||
|
[{ targets = [ "127.0.0.1:${toString exporter.port}" ]; }];
|
||||||
|
}) nonTlsExporters;
|
||||||
|
};
|
||||||
|
|
||||||
|
system.activationScripts = {
|
||||||
|
# This will seem a bit strange, and it probably *is*; The
|
||||||
|
# keys/certs here are only used for the various prometheus/grafana
|
||||||
|
# services to authenticate against each other.
|
||||||
|
#
|
||||||
|
# Since they aren't used to actually encrypt anything but
|
||||||
|
# communication that happens once, it's not necessary to keep the
|
||||||
|
# keys around. They're only used internally, and frequently
|
||||||
|
# switching them doesn't cause any issues. In fact, a single-use
|
||||||
|
# key protocol would probably be more secure.
|
||||||
|
#
|
||||||
|
# Sadly, neither of these services support anything more usable
|
||||||
|
# than https, so we need to generate keys. We opt to regenerate
|
||||||
|
# them at each system activation.
|
||||||
|
#
|
||||||
|
# CN=localhost is not really a risk here - this only matters if an
|
||||||
|
# attacker can spoof a service on the correct port somehow, in
|
||||||
|
# which case they either have root or full access to that server's
|
||||||
|
# user anyway. Since we use TLS auth, no secrets would be leaked,
|
||||||
|
# so in the worst case this exploit would enable an attacker to
|
||||||
|
# DoS that specific data source... Which they could do by taking
|
||||||
|
# over the service already anyway.
|
||||||
|
setupMonitoringAuth = let
|
||||||
|
opensslBin = "${openssl}/bin/openssl";
|
||||||
|
services = [ "grafana" "prometheus" ]
|
||||||
|
++ (map (name: "${name}-exporter") (attrNames tlsExporters));
|
||||||
|
in stringAfter ([ "specialfs" "users" "groups" ]) (''
|
||||||
|
[ -e /run/current-system ] || echo setting up monitoring secrets...
|
||||||
|
specialMount ramfs '${keydir}' nodev,nosuid,mode=0751 ramfs
|
||||||
|
specialMount ramfs '${certdir}' nodev,nosuid,mode=0751 ramfs
|
||||||
|
'' + concatStringsSep "\n" (map (service: ''
|
||||||
|
${opensslBin} req -batch -x509 -newkey ed25519 -nodes \
|
||||||
|
-subj '/CN=localhost' \
|
||||||
|
-addext "subjectAltName = DNS:localhost" \
|
||||||
|
-keyout '${keydir}/${service}.pem' \
|
||||||
|
-out '${certdir}/${service}.pem'
|
||||||
|
|
||||||
|
chown ${service}:${service} '${keydir}/${service}.pem'
|
||||||
|
chmod u=r '${keydir}/${service}.pem'
|
||||||
|
chmod =r '${certdir}/${service}.pem'
|
||||||
|
'') services));
|
||||||
|
};
|
||||||
|
}
|
21
flake.lock
21
flake.lock
|
@ -73,6 +73,7 @@
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
|
"sops-nix": "sops-nix",
|
||||||
"tlaternet-templates": "tlaternet-templates",
|
"tlaternet-templates": "tlaternet-templates",
|
||||||
"tlaternet-webserver": "tlaternet-webserver"
|
"tlaternet-webserver": "tlaternet-webserver"
|
||||||
}
|
}
|
||||||
|
@ -102,6 +103,26 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sops-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1641374494,
|
||||||
|
"narHash": "sha256-a56G6Um43+0+n+yNYhRCh/mSvDdRVzQHSKcFaDEB9/8=",
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"rev": "7edb4b080023ef12f39262a3aa7aab31015a7a0e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tlaternet-templates": {
|
"tlaternet-templates": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": [
|
"flake-utils": [
|
||||||
|
|
36
flake.nix
36
flake.nix
|
@ -5,6 +5,10 @@
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-21.11";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-21.11";
|
||||||
nixos-hardware.url = "github:nixos/nixos-hardware/master";
|
nixos-hardware.url = "github:nixos/nixos-hardware/master";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
sops-nix = {
|
||||||
|
url = "github:Mic92/sops-nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
tlaternet-webserver = {
|
tlaternet-webserver = {
|
||||||
url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
|
url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
|
||||||
inputs = {
|
inputs = {
|
||||||
|
@ -21,8 +25,8 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, nixos-hardware, flake-utils, tlaternet-webserver
|
outputs = { self, nixpkgs, nixos-hardware, flake-utils, sops-nix
|
||||||
, tlaternet-templates, ... }@inputs:
|
, tlaternet-webserver, tlaternet-templates, ... }@inputs:
|
||||||
let
|
let
|
||||||
overlays = [
|
overlays = [
|
||||||
(final: prev: {
|
(final: prev: {
|
||||||
|
@ -35,6 +39,7 @@
|
||||||
local-lib = self.lib.${prev.system};
|
local-lib = self.lib.${prev.system};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
sops-nix.overlay
|
||||||
];
|
];
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
@ -44,6 +49,8 @@
|
||||||
inherit system;
|
inherit system;
|
||||||
|
|
||||||
modules = [
|
modules = [
|
||||||
|
sops-nix.nixosModules.sops
|
||||||
|
|
||||||
({ modulesPath, ... }: {
|
({ modulesPath, ... }: {
|
||||||
imports = [ (modulesPath + "/profiles/headless.nix") ];
|
imports = [ (modulesPath + "/profiles/headless.nix") ];
|
||||||
nixpkgs.overlays = overlays;
|
nixpkgs.overlays = overlays;
|
||||||
|
@ -61,6 +68,8 @@
|
||||||
inherit system;
|
inherit system;
|
||||||
|
|
||||||
modules = [
|
modules = [
|
||||||
|
sops-nix.nixosModule
|
||||||
|
|
||||||
({ modulesPath, ... }: {
|
({ modulesPath, ... }: {
|
||||||
imports = [ (modulesPath + "/profiles/headless.nix") ];
|
imports = [ (modulesPath + "/profiles/headless.nix") ];
|
||||||
nixpkgs.overlays = overlays;
|
nixpkgs.overlays = overlays;
|
||||||
|
@ -78,6 +87,13 @@
|
||||||
# can easily test locally with the VM.
|
# can easily test locally with the VM.
|
||||||
services.nginx.domain = lib.mkOverride 99 "localhost";
|
services.nginx.domain = lib.mkOverride 99 "localhost";
|
||||||
|
|
||||||
|
# Use a default password for the grafana instance for
|
||||||
|
# easy testing.
|
||||||
|
services.grafana.security = {
|
||||||
|
adminPassword = "insecure";
|
||||||
|
adminPasswordFile = lib.mkOverride 99 null;
|
||||||
|
};
|
||||||
|
|
||||||
# # Set up VM settings to match real VPS
|
# # Set up VM settings to match real VPS
|
||||||
# virtualisation.memorySize = 3941;
|
# virtualisation.memorySize = 3941;
|
||||||
# virtualisation.cores = 2;
|
# virtualisation.cores = 2;
|
||||||
|
@ -94,19 +110,25 @@
|
||||||
nixfmt
|
nixfmt
|
||||||
git-lfs
|
git-lfs
|
||||||
|
|
||||||
|
sops-init-gpg-key
|
||||||
|
|
||||||
# For the minecraft mod update script
|
# For the minecraft mod update script
|
||||||
(python3.withPackages (pypkgs:
|
(python3.withPackages (pypkgs:
|
||||||
with pypkgs; [
|
with pypkgs; [
|
||||||
dateutil
|
dateutil
|
||||||
requests
|
requests
|
||||||
|
|
||||||
ipython
|
# ipython
|
||||||
python-language-server
|
# python-language-server
|
||||||
pyls-black
|
# pyls-black
|
||||||
pyls-isort
|
# pyls-isort
|
||||||
pyls-mypy
|
# pyls-mypy
|
||||||
]))
|
]))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# nativeBuildInputs = [ sops-import-keys-hook ]; Breaks the shellHook somehow
|
||||||
|
sopsPGPKeyDirs = [ "./keys/hosts/" "./keys/users/" ];
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export QEMU_OPTS="-m 3941 -smp 2 -curses"
|
export QEMU_OPTS="-m 3941 -smp 2 -curses"
|
||||||
export QEMU_NET_OPTS="hostfwd=::3022-:2222,hostfwd=::3080-:80,hostfwd=::3443-:443,hostfwd=::3021-:2221,hostfwd=::25565-:25565"
|
export QEMU_NET_OPTS="hostfwd=::3022-:2222,hostfwd=::3080-:80,hostfwd=::3443-:443,hostfwd=::3021-:2221,hostfwd=::25565-:25565"
|
||||||
|
|
|
@ -9,7 +9,7 @@ let
|
||||||
url = "${mirror}/${version}/forge-${version}-installer.jar";
|
url = "${mirror}/${version}/forge-${version}-installer.jar";
|
||||||
curlOpts = "--globoff";
|
curlOpts = "--globoff";
|
||||||
# Forge doesn't seem to like newer shas
|
# Forge doesn't seem to like newer shas
|
||||||
sha1 = "e97821e5431bdcaa46e12048769922e2cdb5e2e1";
|
sha1 = "sha1-oHNpyrgHluRrAXWZJg9j+OInAwA=";
|
||||||
};
|
};
|
||||||
|
|
||||||
unpackCmd = "mkdir -p src; cp $curSrc src/forge-${version}-installer.jar";
|
unpackCmd = "mkdir -p src; cp $curSrc src/forge-${version}-installer.jar";
|
||||||
|
|
Loading…
Reference in a new issue