refactor(ops): Split //ops/nixos into different locations
Splits //ops/nixos into: * //ops/nixos.nix - utility functions for building systems * //ops/machines - shared machine definitions (read by readTree) * //ops/modules - shared NixOS modules (skipped by readTree) This simplifies working with the configuration fixpoint in whitby, and is overall a bit more in line with how NixOS systems in user folders currently work. Change-Id: I1322ec5cc76c0207c099c05d44828a3df0b3ffc1 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2931 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org> Reviewed-by: glittershark <grfn@gws.fyi>
This commit is contained in:
parent
7deabb8c8d
commit
90281c4eac
38 changed files with 41 additions and 60 deletions
1
ops/modules/.skip-subtree
Normal file
1
ops/modules/.skip-subtree
Normal file
|
|
@ -0,0 +1 @@
|
|||
NixOS modules are not readTree compatible.
|
||||
7
ops/modules/README.md
Normal file
7
ops/modules/README.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
NixOS modules
|
||||
=============
|
||||
|
||||
This folder contains various NixOS modules shared by our NixOS
|
||||
configurations.
|
||||
|
||||
It is not read by `readTree`.
|
||||
75
ops/modules/clbot.nix
Normal file
75
ops/modules/clbot.nix
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Module that configures CLBot, our Gerrit->IRC info bridge.
|
||||
{ depot, config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) attrValues concatStringsSep mapAttrs readFile;
|
||||
inherit (pkgs) runCommandNoCC;
|
||||
|
||||
inherit (lib)
|
||||
listToAttrs
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
removeSuffix
|
||||
types;
|
||||
|
||||
description = "Bot to forward CL notifications";
|
||||
cfg = config.services.depot.clbot;
|
||||
|
||||
mkFlags = flags:
|
||||
concatStringsSep " "
|
||||
(attrValues (mapAttrs (key: value: "-${key} \"${toString value}\"") flags));
|
||||
|
||||
# Escapes a unit name for use in systemd
|
||||
systemdEscape = name: removeSuffix "\n" (readFile (runCommandNoCC "unit-name" {} ''
|
||||
${pkgs.systemd}/bin/systemd-escape '${name}' >> $out
|
||||
''));
|
||||
|
||||
mkUnit = flags: channel: {
|
||||
name = "clbot-${systemdEscape channel}";
|
||||
value = {
|
||||
description = "${description} to ${channel}";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
script = "${depot.fun.clbot}/bin/clbot ${mkFlags (cfg.flags // {
|
||||
irc_channel = channel;
|
||||
})} -alsologtostderr";
|
||||
|
||||
serviceConfig = {
|
||||
User = "clbot";
|
||||
EnvironmentFile = "/etc/secrets/clbot";
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
options.services.depot.clbot = {
|
||||
enable = mkEnableOption description;
|
||||
|
||||
flags = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "Key value pairs for command line flags";
|
||||
};
|
||||
|
||||
channels = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "Channels in which to post (generates one unit per channel)";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# This does not use DynamicUser because we need to make some files
|
||||
# (notably the SSH private key) readable by this user outside of
|
||||
# the module.
|
||||
users = {
|
||||
groups.clbot = {};
|
||||
|
||||
users.clbot = {
|
||||
group = "clbot";
|
||||
isNormalUser = false;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services = listToAttrs (map (mkUnit cfg.flags) cfg.channels);
|
||||
};
|
||||
}
|
||||
2
ops/modules/default.nix
Normal file
2
ops/modules/default.nix
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Make readTree happy at this level.
|
||||
_: {}
|
||||
49
ops/modules/irccat.nix
Normal file
49
ops/modules/irccat.nix
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{ depot, config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.depot.irccat;
|
||||
description = "irccat - forward messages to IRC";
|
||||
|
||||
# irccat expects to read its configuration from the *current
|
||||
# directory*, and its configuration contains secrets.
|
||||
#
|
||||
# To make this work we construct the JSON configuration file and
|
||||
# then recursively merge it with an on-disk secret using jq on
|
||||
# service launch.
|
||||
configJson = pkgs.writeText "irccat.json" (builtins.toJSON cfg.config);
|
||||
configMerge = pkgs.writeShellScript "merge-irccat-config" ''
|
||||
if [ ! -f "/etc/secrets/irccat.json" ]; then
|
||||
echo "irccat secrets file is missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# jq's * is the recursive merge operator
|
||||
${pkgs.jq}/bin/jq -s '.[0] * .[1]' ${configJson} /etc/secrets/irccat.json \
|
||||
> /var/lib/irccat/irccat.json
|
||||
'';
|
||||
in {
|
||||
options.services.depot.irccat = {
|
||||
enable = lib.mkEnableOption description;
|
||||
|
||||
config = lib.mkOption {
|
||||
type = lib.types.attrs; # varying value types
|
||||
description = "Configuration structure (unchecked!)";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.services.irccat = {
|
||||
inherit description;
|
||||
preStart = "${configMerge}";
|
||||
script = "${depot.third_party.irccat}/bin/irccat";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
StateDirectory = "irccat";
|
||||
WorkingDirectory = "/var/lib/irccat";
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
128
ops/modules/monorepo-gerrit.nix
Normal file
128
ops/modules/monorepo-gerrit.nix
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Gerrit configuration for the TVL monorepo
|
||||
{ depot, pkgs, config, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.gerrit;
|
||||
gerritHooks = pkgs.runCommandNoCC "gerrit-hooks" {} ''
|
||||
mkdir -p $out
|
||||
ln -s ${depot.ops.besadii}/bin/besadii $out/ref-updated
|
||||
'';
|
||||
in {
|
||||
services.gerrit = {
|
||||
enable = true;
|
||||
listenAddress = "[::]:4778"; # 4778 - grrt
|
||||
serverId = "4fdfa107-4df9-4596-8e0a-1d2bbdd96e36";
|
||||
builtinPlugins = [
|
||||
"download-commands"
|
||||
"hooks"
|
||||
];
|
||||
|
||||
plugins = with depot.third_party.gerrit_plugins; [
|
||||
owners
|
||||
oauth
|
||||
depot.ops.gerrit-tvl
|
||||
];
|
||||
|
||||
package = depot.third_party.gerrit;
|
||||
|
||||
jvmHeapLimit = "4g";
|
||||
|
||||
settings = {
|
||||
core.packedGitLimit = "100m";
|
||||
log.jsonLogging = true;
|
||||
log.textLogging = false;
|
||||
sshd.advertisedAddress = "code.tvl.fyi:29418";
|
||||
hooks.path = "${gerritHooks}";
|
||||
cache.web_sessions.maxAge = "3 months";
|
||||
plugins.allowRemoteAdmin = false;
|
||||
change.enableAttentionSet = true;
|
||||
change.enableAssignee = false;
|
||||
|
||||
# Configures gerrit for being reverse-proxied by nginx as per
|
||||
# https://gerrit-review.googlesource.com/Documentation/config-reverseproxy.html
|
||||
gerrit = {
|
||||
canonicalWebUrl = "https://cl.tvl.fyi";
|
||||
docUrl = "/Documentation";
|
||||
};
|
||||
|
||||
httpd.listenUrl = "proxy-https://${cfg.listenAddress}";
|
||||
|
||||
download.command = [
|
||||
"checkout"
|
||||
"cherry_pick"
|
||||
"format_patch"
|
||||
"pull"
|
||||
];
|
||||
|
||||
# Configure for cgit.
|
||||
gitweb = {
|
||||
type = "custom";
|
||||
url = "https://code.tvl.fyi";
|
||||
project = "/";
|
||||
revision = "/commit/?id=\${commit}";
|
||||
branch = "/log/?h=\${branch}";
|
||||
tag = "/tag/?h=\${tag}";
|
||||
roottree = "/tree/?h=\${commit}";
|
||||
file = "/tree/\${file}?h=\${commit}";
|
||||
filehistory = "/log/\${file}?h=\${branch}";
|
||||
linkname = "cgit";
|
||||
};
|
||||
|
||||
# Auto-link panettone bug links
|
||||
commentlink.panettone = {
|
||||
match = "b/(\\\\d+)";
|
||||
html = "<a href=\"https://b.tvl.fyi/issues/$1\">b/$1</a>";
|
||||
};
|
||||
|
||||
# Auto-link other CLs
|
||||
commentlink.gerrit = {
|
||||
match = "cl/(\\\\d+)";
|
||||
html = "<a href=\"https://cl.tvl.fyi/$1\">cl/$1</a>";
|
||||
};
|
||||
|
||||
# Configures integration with CAS, which then integrates with a variety
|
||||
# of backends.
|
||||
auth.type = "OAUTH";
|
||||
plugin.gerrit-oauth-provider-cas-oauth = {
|
||||
root-url = "https://login.tvl.fyi";
|
||||
client-id = "OAUTH-TVL-gerrit-Fv0d8Aizz5";
|
||||
# client-secret is set in /var/lib/gerrit/etc/secure.config.
|
||||
};
|
||||
|
||||
# Use Gerrit's built-in HTTP passwords, rather than trying to use the
|
||||
# password against the backing OAuth provider.
|
||||
auth.gitBasicAuthPolicy = "HTTP";
|
||||
|
||||
# Email sending (emails are relayed via the tazj.in domain's
|
||||
# GSuite currently).
|
||||
#
|
||||
# Note that sendemail.smtpPass is stored in
|
||||
# $site_path/etc/secure.config and is *not* controlled by Nix.
|
||||
#
|
||||
# Receiving email is not currently supported.
|
||||
sendemail = {
|
||||
enable = true;
|
||||
html = false;
|
||||
connectTimeout = "10sec";
|
||||
from = "TVL Code Review <tvlbot@tazj.in>";
|
||||
includeDiff = true;
|
||||
smtpEncryption = "none";
|
||||
smtpServer = "localhost";
|
||||
smtpServerPort = 2525;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.gerrit = {
|
||||
serviceConfig = {
|
||||
# There seems to be no easy way to get `DynamicUser` to play
|
||||
# well with other services (e.g. by using SupplementaryGroups,
|
||||
# which seem to have no effect) so we force the DynamicUser
|
||||
# setting for the Gerrit service to be disabled and reuse the
|
||||
# existing 'git' user.
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = "git";
|
||||
Group = "git";
|
||||
};
|
||||
};
|
||||
}
|
||||
103
ops/modules/panettone.nix
Normal file
103
ops/modules/panettone.nix
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
{ depot, config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.depot.panettone;
|
||||
in {
|
||||
options.services.depot.panettone = with lib; {
|
||||
enable = mkEnableOption "Panettone issue tracker";
|
||||
|
||||
port = mkOption {
|
||||
description = "Port on which Panettone should listen";
|
||||
type = types.int;
|
||||
default = 7268;
|
||||
};
|
||||
|
||||
dbHost = mkOption {
|
||||
description = "Postgresql host to connect to for Panettone";
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
};
|
||||
|
||||
dbName = mkOption {
|
||||
description = "Name of the database for Panettone";
|
||||
type = types.str;
|
||||
default = "panettone";
|
||||
};
|
||||
|
||||
dbUser = mkOption {
|
||||
description = "Name of the database user for Panettone";
|
||||
type = types.str;
|
||||
default = "panettone";
|
||||
};
|
||||
|
||||
secretsFile = mkOption {
|
||||
description = ''
|
||||
Path to a file containing secrets, in the format accepted
|
||||
by systemd's EnvironmentFile
|
||||
'';
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
irccatHost = mkOption {
|
||||
description = "Hostname for the irccat instance";
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
};
|
||||
|
||||
irccatPort = mkOption {
|
||||
description = "Port for the irccat instance";
|
||||
type = types.int;
|
||||
default = 4722;
|
||||
};
|
||||
|
||||
irccatChannel = mkOption {
|
||||
description = "IRC channels to post to via irccat";
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [{
|
||||
assertion =
|
||||
cfg.dbHost != "localhost" || config.services.postgresql.enable;
|
||||
message = "Panettone requires a postgresql database";
|
||||
} {
|
||||
assertion =
|
||||
cfg.dbHost != "localhost" || config.services.postgresql.enableTCPIP;
|
||||
message = "Panettone can only connect to the postgresql database over TCP";
|
||||
} {
|
||||
assertion =
|
||||
cfg.dbHost != "localhost" || (lib.any
|
||||
(user: user.name == cfg.dbUser)
|
||||
config.services.postgresql.ensureUsers);
|
||||
message = "Panettone requires a database user";
|
||||
} {
|
||||
assertion =
|
||||
cfg.dbHost != "localhost" || (lib.any
|
||||
(db: db == cfg.dbName)
|
||||
config.services.postgresql.ensureDatabases);
|
||||
message = "Panettone requires a database";
|
||||
}];
|
||||
|
||||
systemd.services.panettone = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
script = "${depot.web.panettone}/bin/panettone";
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
Restart = "always";
|
||||
EnvironmentFile = cfg.secretsFile;
|
||||
};
|
||||
|
||||
environment = {
|
||||
PANETTONE_PORT = toString cfg.port;
|
||||
PGHOST = "localhost";
|
||||
PGUSER = cfg.dbUser;
|
||||
PGDATABASE = cfg.dbName;
|
||||
IRCCATHOST = cfg.irccatHost;
|
||||
IRCCATPORT = toString cfg.irccatPort;
|
||||
ISSUECHANNEL = cfg.irccatChannel;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
27
ops/modules/paroxysm.nix
Normal file
27
ops/modules/paroxysm.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{ depot, config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.depot.paroxysm;
|
||||
description = "TVL's majestic IRC bot";
|
||||
in {
|
||||
options.services.depot.paroxysm.enable = lib.mkEnableOption description;
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.services.paroxysm = {
|
||||
inherit description;
|
||||
script = "${depot.fun.paroxysm}/bin/paroxysm";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
environment = {
|
||||
PARX_DATABASE_URL = "postgresql://tvldb:tvldb@localhost/tvldb";
|
||||
PARX_IRC_CONFIG_PATH = "/var/lib/paroxysm/irc.toml";
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
StateDirectory = "paroxysm";
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
76
ops/modules/quassel.nix
Normal file
76
ops/modules/quassel.nix
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# A more modern module for running Quassel.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.depot.quassel;
|
||||
quasselDaemon = pkgs.quassel.override {
|
||||
monolithic = false;
|
||||
enableDaemon = true;
|
||||
withKDE = false;
|
||||
};
|
||||
in {
|
||||
options.services.depot.quassel = with lib; {
|
||||
enable = mkEnableOption "Quassel IRC daemon";
|
||||
|
||||
acmeHost = mkOption {
|
||||
description = "ACME host to use for the Quassel TLS certificate";
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
bindAddresses = mkOption {
|
||||
description = "Addresses Quassel will bind to/listen on";
|
||||
default = [ "127.0.0.1" ];
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
description = "Log level for Quassel Core";
|
||||
default = "Info";
|
||||
type = lib.types.enum [
|
||||
"Debug"
|
||||
"Info"
|
||||
"Warning"
|
||||
"Error"
|
||||
];
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
default = 6698;
|
||||
description = ''
|
||||
The port number the Quassel daemon will be listening to.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = with lib; mkIf cfg.enable {
|
||||
systemd.services.quassel = {
|
||||
description = "Quassel IRC daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
script = concatStringsSep " " [
|
||||
"${quasselDaemon}/bin/quasselcore"
|
||||
"--listen=${concatStringsSep "," cfg.bindAddresses}"
|
||||
"--port=${toString cfg.port}"
|
||||
"--configdir=/var/lib/quassel"
|
||||
"--require-ssl"
|
||||
"--ssl-cert=/var/lib/acme/${cfg.acmeHost}/full.pem"
|
||||
"--loglevel=${cfg.logLevel}"
|
||||
];
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "always";
|
||||
User = "quassel";
|
||||
Group = "quassel";
|
||||
StateDirectory = "quassel";
|
||||
};
|
||||
};
|
||||
|
||||
users = {
|
||||
users.quassel = {
|
||||
isNormalUser = false;
|
||||
group = "quassel";
|
||||
};
|
||||
|
||||
groups.quassel = {};
|
||||
};
|
||||
};
|
||||
}
|
||||
53
ops/modules/smtprelay.nix
Normal file
53
ops/modules/smtprelay.nix
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# NixOS module for configuring the simple SMTP relay.
|
||||
{ depot, pkgs, config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) attrValues mapAttrs;
|
||||
inherit (lib)
|
||||
concatStringsSep
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
|
||||
cfg = config.services.depot.smtprelay;
|
||||
description = "Simple SMTP relay";
|
||||
|
||||
# Configuration values that are always overridden. In particular,
|
||||
# `config` is specified to always load $StateDirectory/secure.config
|
||||
# (so that passwords can be loaded from there) and logging is pinned
|
||||
# to stdout for journald compatibility.
|
||||
overrideArgs = {
|
||||
logfile = "";
|
||||
config = "/var/lib/smtprelay/secure.config";
|
||||
};
|
||||
|
||||
# Creates the command line argument string for the service.
|
||||
prepareArgs = args:
|
||||
concatStringsSep " "
|
||||
(attrValues (mapAttrs (key: value: "-${key} '${toString value}'")
|
||||
(args // overrideArgs)));
|
||||
in {
|
||||
options.services.depot.smtprelay = {
|
||||
enable = mkEnableOption description;
|
||||
args = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "Key value pairs for command line arguments";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.smtprelay = {
|
||||
inherit description;
|
||||
script = "${depot.third_party.smtprelay}/bin/smtprelay ${prepareArgs cfg.args}";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "always";
|
||||
StateDirectory = "smtprelay";
|
||||
DynamicUser = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
51
ops/modules/sourcegraph.nix
Normal file
51
ops/modules/sourcegraph.nix
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# Run sourcegraph, including its entire machinery, in a container.
|
||||
# Running it outside of a container is a futile endeavour for now.
|
||||
{ depot, config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.depot.sourcegraph;
|
||||
in {
|
||||
options.services.depot.sourcegraph = with lib; {
|
||||
enable = mkEnableOption "SourceGraph code search engine";
|
||||
|
||||
port = mkOption {
|
||||
description = "Port on which SourceGraph should listen";
|
||||
type = types.int;
|
||||
default = 3463;
|
||||
};
|
||||
|
||||
cheddarPort = mkOption {
|
||||
description = "Port on which cheddar should listen";
|
||||
type = types.int;
|
||||
default = 4238;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Run a cheddar syntax highlighting server
|
||||
systemd.services.cheddar-server = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
script = "${depot.tools.cheddar}/bin/cheddar --listen 0.0.0.0:${toString cfg.cheddarPort} --sourcegraph-server";
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
|
||||
virtualisation.oci-containers.containers.sourcegraph = {
|
||||
image = "sourcegraph/server:3.26.0";
|
||||
|
||||
ports = [
|
||||
"127.0.0.1:${toString cfg.port}:7080"
|
||||
];
|
||||
|
||||
volumes = [
|
||||
"/var/lib/sourcegraph/etc:/etc/sourcegraph"
|
||||
"/var/lib/sourcegraph/data:/var/opt/sourcegraph"
|
||||
];
|
||||
|
||||
environment.SRC_SYNTECT_SERVER = "http://172.17.0.1:${toString cfg.cheddarPort}";
|
||||
};
|
||||
};
|
||||
}
|
||||
48
ops/modules/tvl-buildkite.nix
Normal file
48
ops/modules/tvl-buildkite.nix
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Configuration for the TVL buildkite agents.
|
||||
{ config, depot, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.depot.buildkite;
|
||||
agents = lib.range 1 cfg.agentCount;
|
||||
description = "Buildkite agents for TVL";
|
||||
|
||||
# All Buildkite hooks are actually besadii, but it's being invoked
|
||||
# with different names.
|
||||
buildkiteHooks = pkgs.runCommandNoCC "buildkite-hooks" {} ''
|
||||
mkdir -p $out/bin
|
||||
ln -s ${depot.ops.besadii}/bin/besadii $out/bin/post-command
|
||||
'';
|
||||
in {
|
||||
options.services.depot.buildkite = {
|
||||
enable = lib.mkEnableOption description;
|
||||
agentCount = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
description = "Number of Buildkite agents to launch";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Run the Buildkite agents using the default upstream module.
|
||||
services.buildkite-agents = builtins.listToAttrs (map (n: rec {
|
||||
name = "whitby-${toString n}";
|
||||
value = {
|
||||
inherit name;
|
||||
enable = true;
|
||||
tokenPath = "/etc/secrets/buildkite-agent-token";
|
||||
hooks.post-command = "${buildkiteHooks}/bin/post-command";
|
||||
};
|
||||
}) agents);
|
||||
|
||||
# Set up a group for all Buildkite agent users
|
||||
users = {
|
||||
groups.buildkite-agents = {};
|
||||
users = builtins.listToAttrs (map (n: rec {
|
||||
name = "buildkite-agent-whitby-${toString n}";
|
||||
value = {
|
||||
group = lib.mkForce "buildkite-agents";
|
||||
extraGroups = [ name ];
|
||||
};
|
||||
}) agents);
|
||||
};
|
||||
};
|
||||
}
|
||||
89
ops/modules/tvl-slapd/default.nix
Normal file
89
ops/modules/tvl-slapd/default.nix
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Configures an OpenLDAP instance for TVL
|
||||
#
|
||||
# TODO(tazjin): Configure ldaps://
|
||||
{ depot, lib, pkgs, ... }:
|
||||
|
||||
with depot.nix.yants;
|
||||
|
||||
let
|
||||
user = struct {
|
||||
username = string;
|
||||
email = string;
|
||||
password = string;
|
||||
displayName = option string;
|
||||
};
|
||||
|
||||
toLdif = defun [ user string ] (u: ''
|
||||
dn: cn=${u.username},ou=users,dc=tvl,dc=fyi
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
sn: ${u.username}
|
||||
cn: ${u.username}
|
||||
displayName: ${u.displayName or u.username}
|
||||
mail: ${u.email}
|
||||
userPassword: ${u.password}
|
||||
'');
|
||||
|
||||
inherit (depot.ops) users;
|
||||
|
||||
in {
|
||||
# Use our patched OpenLDAP derivation which enables stronger password hashing.
|
||||
#
|
||||
# Unfortunately the module for OpenLDAP has no package option, so we
|
||||
# need to override it system-wide. Be aware that this triggers a
|
||||
# *large* number of rebuilds of packages such as GPG and Python.
|
||||
nixpkgs.overlays = [
|
||||
(_: _: {
|
||||
inherit (depot.third_party) openldap;
|
||||
})
|
||||
];
|
||||
|
||||
services.openldap = {
|
||||
enable = true;
|
||||
dataDir = "/var/lib/openldap";
|
||||
database = "mdb";
|
||||
suffix = "dc=tvl,dc=fyi";
|
||||
rootdn = "cn=admin,dc=tvl,dc=fyi";
|
||||
rootpw = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$OfcgkOQ96VQ3aJj7NfA9vQ$oS6HQOkYl/bUYg4SejpltQYy7kvqx/RUxvoR4zo1vXU";
|
||||
|
||||
settings.children = {
|
||||
"olcDatabase={1}mdb".attrs = {
|
||||
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
|
||||
olcDatabase = "{1}mdb";
|
||||
olcSuffix = "dc=tvl,dc=fyi";
|
||||
olcAccess = "to * by * read";
|
||||
};
|
||||
|
||||
"cn=module{0}".attrs = {
|
||||
objectClass = "olcModuleList";
|
||||
olcModuleLoad = "pw-argon2";
|
||||
};
|
||||
};
|
||||
|
||||
# Contents are immutable at runtime, and adding user accounts etc.
|
||||
# is done statically in the LDIF-formatted contents in this folder.
|
||||
declarativeContents."dc=tvl,dc=fyi" = ''
|
||||
dn: dc=tvl,dc=fyi
|
||||
dc: tvl
|
||||
o: TVL LDAP server
|
||||
description: Root entry for tvl.fyi
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
|
||||
dn: ou=users,dc=tvl,dc=fyi
|
||||
ou: users
|
||||
description: All users in TVL
|
||||
objectClass: top
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=groups,dc=tvl,dc=fyi
|
||||
ou: groups
|
||||
description: All groups in TVL
|
||||
objectClass: top
|
||||
objectClass: organizationalUnit
|
||||
|
||||
${lib.concatStringsSep "\n" (map toLdif users)}
|
||||
'';
|
||||
};
|
||||
}
|
||||
24
ops/modules/tvl-sso/default.nix
Normal file
24
ops/modules/tvl-sso/default.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Configures an Apereo CAS instance for TVL SSO
|
||||
{ depot, ... }:
|
||||
|
||||
let
|
||||
inherit (depot.third_party) apereo-cas;
|
||||
in {
|
||||
config = {
|
||||
environment.systemPackages = [ apereo-cas ];
|
||||
systemd.services.apereo-cas = {
|
||||
description = "Apereo CAS Single Sign On server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
User = "apereo-cas";
|
||||
Group = "apereo-cas";
|
||||
ExecStart = "${apereo-cas}/bin/cas";
|
||||
EnvironmentFile = "/etc/cas/secrets";
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
users.users.apereo-cas = {};
|
||||
users.groups.apereo-cas = {};
|
||||
};
|
||||
}
|
||||
12
ops/modules/v4l2loopback.nix
Normal file
12
ops/modules/v4l2loopback.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
boot = {
|
||||
extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ];
|
||||
kernelModules = [ "v4l2loopback" ];
|
||||
extraModprobeConfig = ''
|
||||
options v4l2loopback exclusive_caps=1
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
32
ops/modules/www/b.tvl.fyi.nix
Normal file
32
ops/modules/www/b.tvl.fyi.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{ config, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."b-shortlink" = {
|
||||
serverName = "b";
|
||||
extraConfig = "return 302 https://b.tvl.fyi$request_uri;";
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."b.tvl.fyi" = {
|
||||
serverName = "b.tvl.fyi";
|
||||
serverAliases = [ "b.tvl.su" ];
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
# Forward short links to issues to the issue itself (b/32)
|
||||
location ~ ^/(\d+)$ {
|
||||
return 302 https://b.tvl.fyi/issues$request_uri;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:${toString config.services.depot.panettone.port};
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
36
ops/modules/www/base.nix
Normal file
36
ops/modules/www/base.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = {
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
enableReload = true;
|
||||
|
||||
recommendedTlsSettings = true;
|
||||
recommendedGzipSettings = true;
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
|
||||
# NixOS 20.03 broke nginx and I can't be bothered to debug it
|
||||
# anymore, all solution attempts have failed, so here's a
|
||||
# brute-force fix.
|
||||
#
|
||||
# TODO(tazjin): Find a link to the upstream issue and see if
|
||||
# they've sorted it after ~20.09
|
||||
systemd.services.fix-nginx = {
|
||||
script = "${pkgs.coreutils}/bin/chown -f -R nginx: /var/spool/nginx /var/cache/nginx";
|
||||
|
||||
serviceConfig = {
|
||||
User = "root";
|
||||
Type = "oneshot";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.fix-nginx = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = "minutely";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
26
ops/modules/www/cache.tvl.su.nix
Normal file
26
ops/modules/www/cache.tvl.su.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{ config, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."cache.tvl.su" = {
|
||||
serverName = "cache.tvl.su";
|
||||
serverAliases = [ "cache.tvl.fyi" ];
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
location = /cache-key.pub {
|
||||
alias /etc/secrets/nix-cache-key.pub;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:${toString config.services.nix-serve.port};
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
30
ops/modules/www/cl.tvl.fyi.nix
Normal file
30
ops/modules/www/cl.tvl.fyi.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{ config, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."cl-shortlink" = {
|
||||
serverName = "cl";
|
||||
extraConfig = "return 302 https://cl.tvl.fyi$request_uri;";
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts.gerrit = {
|
||||
serverName = "cl.tvl.fyi";
|
||||
serverAliases = [ "cl.tvl.su" ];
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
location / {
|
||||
proxy_pass http://localhost:4778;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
# The :443 suffix is a workaround for https://b.tvl.fyi/issues/88.
|
||||
proxy_set_header Host $host:443;
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
35
ops/modules/www/code.tvl.fyi.nix
Normal file
35
ops/modules/www/code.tvl.fyi.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{ depot, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts.cgit = {
|
||||
serverName = "code.tvl.fyi";
|
||||
serverAliases = [ "code.tvl.su" ];
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
# Serve the rendered Tvix component SVG.
|
||||
#
|
||||
# TODO(tazjin): Implement a way of serving this dynamically
|
||||
location = /about/tvix/docs/component-flow.svg {
|
||||
alias ${depot.tvix.docs.svg}/component-flow.svg;
|
||||
}
|
||||
|
||||
# Static assets must always hit the root.
|
||||
location ~ ^/(favicon\.ico|cgit\.(css|png))$ {
|
||||
proxy_pass http://localhost:2448;
|
||||
}
|
||||
|
||||
# Everything else hits the depot directly.
|
||||
location / {
|
||||
proxy_pass http://localhost:2448/cgit.cgi/depot/;
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
31
ops/modules/www/cs.tvl.fyi.nix
Normal file
31
ops/modules/www/cs.tvl.fyi.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{ config, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."cs.tvl.fyi" = {
|
||||
serverName = "cs.tvl.fyi";
|
||||
serverAliases = [ "cs.tvl.su" ];
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
location = / {
|
||||
return 301 https://cs.tvl.fyi/depot;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_set_header X-Sg-Auth "Anonymous";
|
||||
proxy_pass http://localhost:${toString config.services.depot.sourcegraph.port};
|
||||
}
|
||||
|
||||
location /users/Anonymous/settings {
|
||||
return 301 https://cs.tvl.fyi;
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
24
ops/modules/www/login.tvl.fyi.nix
Normal file
24
ops/modules/www/login.tvl.fyi.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."login.tvl.fyi" = {
|
||||
serverName = "login.tvl.fyi";
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
location / {
|
||||
proxy_pass http://localhost:8443;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
40
ops/modules/www/tazj.in.nix
Normal file
40
ops/modules/www/tazj.in.nix
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# serve tazjin's website & blog
|
||||
{ depot, config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."tazj.in" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
root = depot.users.tazjin.homepage;
|
||||
|
||||
extraConfig = ''
|
||||
${depot.users.tazjin.blog.oldRedirects}
|
||||
location /blog/ {
|
||||
alias ${depot.users.tazjin.blog.rendered}/;
|
||||
|
||||
if ($request_uri ~ ^/(.*)\.html$) {
|
||||
return 302 /$1;
|
||||
}
|
||||
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
}
|
||||
|
||||
# Temporary place for serving static files.
|
||||
location /blobs/ {
|
||||
alias /var/lib/tazjins-blobs/;
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."git.tazj.in" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
extraConfig = "return 301 https://code.tvl.fyi$request_uri;";
|
||||
};
|
||||
};
|
||||
}
|
||||
25
ops/modules/www/todo.tvl.fyi.nix
Normal file
25
ops/modules/www/todo.tvl.fyi.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{ depot, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."todo.tvl.fyi" = {
|
||||
serverName = "todo.tvl.fyi";
|
||||
serverAliases = [ "todo.tvl.su" ];
|
||||
root = depot.web.todolist;
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
|
||||
location ~* \.(webp|woff2)$ {
|
||||
add_header Cache-Control "public, max-age=31536000";
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
30
ops/modules/www/tvl.fyi.nix
Normal file
30
ops/modules/www/tvl.fyi.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{ depot, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."tvl.fyi" = {
|
||||
serverName = "tvl.fyi";
|
||||
root = depot.web.tvl;
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
extraConfig = ''
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
|
||||
rewrite ^/builds/?$ https://buildkite.com/tvl/depot/ last;
|
||||
|
||||
rewrite ^/monorepo-doc/?$ https://docs.google.com/document/d/1nnyByXcH0F6GOmEezNOUa2RFelpeRpDToBLYD_CtjWE/edit?usp=sharing last;
|
||||
|
||||
rewrite ^/irc/?$ ircs://chat.freenode.net:6697/##tvl last;
|
||||
|
||||
location ~* \.(webp|woff2)$ {
|
||||
add_header Cache-Control "public, max-age=31536000";
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
15
ops/modules/www/wigglydonke.rs.nix
Normal file
15
ops/modules/www/wigglydonke.rs.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{ depot, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./base.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.nginx.virtualHosts."wigglydonke.rs" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
root = "${depot.depotPath}/users/glittershark/wigglydonke.rs";
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue