1
0
Fork 0

add vaultwarden example

This commit is contained in:
ibizaman 2023-04-04 00:07:58 -07:00
parent c261fd8750
commit 36c9fe23c3
9 changed files with 764 additions and 0 deletions

View file

@ -0,0 +1,30 @@
# Vaultwarden setup
This folder contain an example configuration for setting up
Vaultwarden on Linode. But before deploying to linode, you can
actually test the deployment locally with VirtualBox.
First, [setup NixOS on a Linode instance](/docs/tutorials/linode.md).
When that's done, explore the files in this folder.
To try it out locally, follow [deploy to staging](/docs/tutorials/deploystaging.md).
```bash
nixops set-args --arg domain '"dev.mydomain.com"' --network dev
nixops info --network dev
```
The TL; DR version is, assuming you're where this file is located:
```bash
export NIXOPS_DEPLOYMENT=vaultwarden-staging
export DISNIXOS_USE_NIXOPS=1
nixops create ./network-virtualbox.nix -d vaultwarden-staging
nixops deploy --network dev
nixops reboot
disnixos-env -s services.nix -n network-virtualbox.nix -d distribution.nix
```

View file

@ -0,0 +1,35 @@
{
domain ? "dev.mydomain.com",
}:
{
network = {
storage.legacy = {};
};
machine1 = { system, pkgs, lib, ... }:
with lib;
let
utils = pkgs.lib.callPackageWith pkgs ./../../../../utils.nix { };
base = ((import ./../network.nix).machine1 {
inherit system pkgs lib;
inherit domain utils;
secret = x: x;
});
vbox = (import ./../network.nix).virtualbox;
mkPortMapping = {name, host, guest, protocol ? "tcp"}:
["--natpf1" "${name},${protocol},,${toString host},,${toString guest}"];
in
recursiveUpdate base {
deployment.targetEnv = "virtualbox";
deployment.virtualbox = {
memorySize = 1024;
vcpu = 2;
headless = true;
vmFlags = concatMap mkPortMapping vbox.portMappings;
};
};
}

View file

@ -0,0 +1,23 @@
{ infrastructure
, pkgs ? import <nixpkgs> {}
}:
with infrastructure;
let
customPkgs = (pkgs.callPackage (./../../..) {}).customPkgs {
inherit pkgs;
};
keycloak = customPkgs.keycloak {};
vaultwarden = customPkgs.vaultwarden {};
in
{
HaproxyService = [ machine1 ];
KeycloakService = [ machine1 ];
KeycloakCliService = [ machine1 ];
KeycloakHaproxyService = [ machine1 ];
}
// keycloak.distribute [ machine1 ]
// vaultwarden.distribute [ machine1 ]

View file

@ -0,0 +1,160 @@
{ hostname
, userName
, userPackages
, systemPackages
, address
, gateway
, sshPublicKey
, allowedTCPPorts
}:
{
imports =
[ # Include the results of the hardware scan.
./machine1-hardware-configuration.nix
];
# Use the GRUB 2 boot loader.
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
# boot.loader.grub.efiSupport = true;
# boot.loader.grub.efiInstallAsRemovable = true;
# boot.loader.efi.efiSysMountPoint = "/boot/efi";
# Define on which hard drive you want to install Grub.
# boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
networking.hostName = hostname; # Define your hostname.
# Pick only one of the below networking options.
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
networking.usePredictableInterfaceNames = false;
networking.enableIPv6 = false;
# Set your time zone.
# time.timeZone = "Europe/Amsterdam";
# Configure network proxy if necessary
# networking.proxy.default = "http://user:password@proxy:port/";
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
# Select internationalisation properties.
# i18n.defaultLocale = "en_US.UTF-8";
# console = {
# font = "Lat2-Terminus16";
# keyMap = "us";
# useXkbConfig = true; # use xkbOptions in tty.
# };
# Enable the X11 windowing system.
# services.xserver.enable = true;
# Configure keymap in X11
# services.xserver.layout = "us";
# services.xserver.xkbOptions = {
# "eurosign:e";
# "caps:escape" # map caps to escape.
# };
# Enable CUPS to print documents.
# services.printing.enable = true;
# Enable sound.
# sound.enable = true;
# hardware.pulseaudio.enable = true;
# Enable touchpad support (enabled default in most desktopManager).
# services.xserver.libinput.enable = true;
# Define a user account. Don't forget to set a password with passwd.
users.users.${userName} = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" ]; # Enable sudo for the user.
packages = userPackages;
openssh.authorizedKeys.keys = [ sshPublicKey ];
};
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = systemPackages;
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
# List services that you want to enable:
# Enable the OpenSSH daemon.
services.openssh = {
enable = true;
permitRootLogin = "yes";
passwordAuthentication = false;
};
nix.trustedUsers = [
"deployer"
];
users.groups.deployer = {};
users.users.deployer = {
isSystemUser = true;
group = "deployer";
extraGroups = [ "wheel" ]; # Enable sudo for the user.
openssh.authorizedKeys.keys = [ sshPublicKey ];
};
users.users."root" = {
openssh.authorizedKeys.keys = [ sshPublicKey ];
};
security.sudo.wheelNeedsPassword = false;
services.longview = {
enable = true;
apiKeyFile = "/var/lib/longview/apiKeyFile";
apacheStatusUrl = "";
nginxStatusUrl = "";
mysqlUser = "";
mysqlPassword = "";
};
# Open ports in the firewall.
networking.firewall.allowedTCPPorts = allowedTCPPorts;
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
# networking.firewall.enable = false;
networking.domain = "members.linode.com";
networking.search = [ "members.linode.com" ];
networking.resolvconf.extraOptions = [ "rotate" ];
networking.nameservers = [
"173.230.145.5"
"173.230.147.5"
"173.230.155.5"
"173.255.212.5"
"173.255.219.5"
"173.255.241.5"
"173.255.243.5"
"173.255.244.5"
"74.207.241.5"
"74.207.242.5"
];
# Copy the NixOS configuration file and link it from the resulting system
# (/run/current-system/configuration.nix). This is useful in case you
# accidentally delete configuration.nix.
# system.copySystemConfiguration = true;
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "22.05"; # Did you read the comment?
}

View file

@ -0,0 +1,57 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "virtio_pci" "virtio_scsi" "ahci" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
swapDevices =
[ { device = "/dev/disk/by-label/swap"; }
];
boot.kernelParams = [ "console=ttyS0,19200n8" ];
boot.loader.grub.extraConfig = ''
serial --speed=19200 --unit=0 --word=8 --parity=no --stop=1;
terminal_input serial;
terminal_output serial
'';
boot.loader.grub.forceInstall = true;
boot.loader.grub.device = "nodev";
boot.loader.timeout = 10;
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault false;
# networking.interfaces.eth0.useDHCP = true;
networking.interfaces.eth0 = {
ipv4 = {
addresses = [
{
address = "45.79.76.142";
prefixLength = 24;
}
];
};
};
networking.defaultGateway = {
address = "45.79.76.1";
interface = "eth0";
};
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -0,0 +1,220 @@
rec {
machine1 = { system
, pkgs
, lib
, utils
, domain
, secret
, ... }:
let
customPkgs = (pkgs.callPackage (./../../..) {}).customPkgs {
inherit system pkgs utils secret;
};
vaultwarden = customPkgs.vaultwarden {};
keycloak = customPkgs.keycloak {};
httpUser = "http";
httpGroup = "http";
httpRoot = "/usr/share/webapps";
phpfpmUser = "phpfpm";
phpfpmGroup = "phpfpm";
phpfpmRoot = "/run/php-fpm";
keycloakUser = "keycloak";
keycloakGroup = "keycloak";
caddyHttpPort = 10001;
caddyHttpsPort = 10002;
keycloaksecretsdir = "/run/keys/keycloakcliconfig";
keycloakusers = [ "me" "friend" ];
in
rec {
users.groups = {
http = {
name = httpGroup;
};
phpfpm = {
name = phpfpmGroup;
};
keycloak = {
name = keycloakGroup;
};
keycloakcli = {
name = "keycloakcli";
};
"${vaultwarden.group}" = {
name = "${vaultwarden.group}";
};
};
users.users = {
http = {
name = httpUser;
group = httpGroup;
home = httpRoot;
isSystemUser = true;
};
phpfpm = {
name = phpfpmUser;
group = phpfpmGroup;
home = phpfpmRoot;
isSystemUser = true;
};
keycloak = {
name = keycloakUser;
group = keycloakGroup;
# home ?
isSystemUser = true;
};
keycloakcli = {
name = "keycloakcli";
group = "keycloakcli";
extraGroups = [ "keys" ];
isSystemUser = true;
};
"${vaultwarden.user}" = {
name = vaultwarden.user;
group = vaultwarden.group;
extraGroups = [ "keys" ];
isSystemUser = true;
};
};
# deployment.keys = {
# linode.text = ''
# LINODE_HTTP_TIMEOUT=10
# LINODE_POLLING_INTERVAL=10
# LINODE_PROPAGATION_TIMEOUT=240
# LINODE_TOKEN=383525f4d58919d43506e6ab43a549a6eda6491eccb8e384d43013f0bcf45d47
# '';
# keycloakdbpassword.text = ''
# KC_DB_PASSWORD="${secret "${domain}/keycloakdbpassword"}"
# '';
# keycloakinitialadmin.text = ''
# KEYCLOAK_ADMIN_PASSWORD="${secret "${domain}/${keycloak.subdomain}/admin"}"
# '';
# # This convention is for keycloak-cli-config
# "keycloak.password" = {
# destDir = keycloaksecretsdir;
# user = "keycloakcli";
# text = secret "${domain}/${keycloak.subdomain}/admin";
# };
# "keycloakusers" =
# let
# e = str: lib.strings.escape [''\''] (lib.strings.escape [''"''] str);
# in
# {
# user = "keycloakcli";
# text = lib.concatMapStringsSep "\n"
# (name: "KEYCLOAK_USERS_${lib.strings.toUpper name}_PASSWORD=${e (secret "${domain}/${keycloak.subdomain}/${name}")}")
# keycloakusers;
# };
# }
# // vaultwarden.deployKeys domain;
security.acme = {
acceptTerms = true;
certs = {
"${domain}" = {
extraDomainNames = ["*.${domain}"];
};
};
defaults = {
group = httpGroup;
email = "ibizapeanut@gmail.com";
dnsProvider = "linode";
dnsResolver = "8.8.8.8";
credentialsFile = "/run/keys/linode";
enableDebugLogs = true;
};
};
services = {
openssh = {
enable = true;
};
disnix = {
enable = true;
# useWebServiceInterface = true;
};
dnsmasq = {
enable = true;
servers = [ "192.168.50.15" "192.168.50.1" ];
extraConfig =
let
subdomains = [
"machine1"
keycloak.subdomain
vaultwarden.subdomain
];
inherit domain;
in (lib.concatMapStrings
(subdomain: "address=/${subdomain}.${domain}/127.0.0.1\naddress=/${subdomain}/127.0.0.1\n")
subdomains)
;
};
# tomcat.enable = false;
postgresql = {
enable = true;
package = pkgs.postgresql_14;
port = 5432;
enableTCPIP = true;
authentication = pkgs.lib.mkOverride 10 ''
local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
'';
};
};
dysnomia = {
enable = true;
enableLegacyModules = false;
extraContainerProperties = {
system = {
inherit domain;
};
postgresql-database = {
service_name = "postgresql.service";
port = builtins.toString services.postgresql.port;
};
keycloaksecrets = {
rootdir = keycloaksecretsdir;
};
};
};
networking.firewall.allowedTCPPorts = [ services.postgresql.port ] ++ virtualbox.guestPorts;
};
virtualbox = rec {
portMappings = [
{ name = "ssh";
host = 22;
guest = 22;
}
{ name = "dns";
host = 53;
guest = 53;
}
{ name = "https";
host = 443;
guest = 443;
}
];
hostPorts = map (x: x.host) portMappings;
guestPorts = map (x: x.guest) portMappings;
};
}

View file

@ -0,0 +1,40 @@
let
hostname = "machine1";
domain = "mydomain.com";
in
{
machine1 = { system, pkgs, lib, ... }:
let
utils = pkgs.lib.callPackageWith pkgs ./utils.nix { };
base = ((import ./network.nix).machine1 {
inherit system pkgs lib;
inherit domain utils;
});
vbox = (import ./network.nix).virtualbox;
in
lib.recursiveUpdate base rec {
deployment.targetHost = hostname;
imports = [
(import ./machines/machine1-configuration.nix {
inherit hostname;
userName = "me";
userPackages = with pkgs; [];
systemPackages = with pkgs; [
curl
inetutils
mtr
sysstat
tmux
vim
];
address = "45.79.76.142";
gateway = "45.79.76.1";
sshPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB/UeAaMECJNLMZ23vLb3A3XT7OJDcpj2OWgXzt8+GLU me@laptop";
allowedTCPPorts = vbox.guestPorts;
})
];
};
}

View file

@ -0,0 +1 @@
/nix/store/wpq9c65h5ahq6aspdvprc547rmym7qgc-virtualbox-nixops-22.11.vmdk

View file

@ -0,0 +1,198 @@
{ system, pkgs, distribution, invDistribution }:
let
utils = pkgs.lib.callPackageWith pkgs ./utils.nix { };
customPkgs = (pkgs.callPackage (./../../..) {}).customPkgs {
inherit system pkgs utils;
};
getTarget = name: builtins.elemAt (builtins.getAttr name distribution) 0;
getDomain = name: (getTarget name).containers.system.domain;
realm = "myrealm";
smtp = utils.recursiveMerge [
{
from = "vaultwarden@${realm}.com";
fromName = "vaultwarden";
port = 587;
authMechanism = "Login";
}
vaultwarden.smtp
];
keycloak = customPkgs.keycloak {};
KeycloakService = customPkgs.mkKeycloakService {
name = "KeycloakService";
subdomain = keycloak.subdomain;
# TODO: Get these from infrastructure.nix
user = "keycloak";
group = "keycloak";
postgresServiceName = (getTarget "KeycloakPostgresDB").containers.postgresql-database.service_name;
initialAdminUsername = "admin";
keys = {
dbPassword = "keycloakdbpassword";
initialAdminPassword = "keycloakinitialadmin";
};
# logLevel = "DEBUG,org.hibernate:info,org.keycloak.authentication:debug,org.keycloak:info,org.postgresql:info,freemarker:info";
logLevel = "INFO";
hostname = "${keycloak.subdomain}.${getDomain "KeycloakService"}";
listenPort = 8080;
dbType = "postgres";
dbDatabase = keycloak.database.name;
dbUsername = keycloak.database.username;
dbHost = {KeycloakPostgresDB}: KeycloakPostgresDB.target.properties.hostname;
dbPort = (getTarget "KeycloakPostgresDB").containers.postgresql-database.port;
KeycloakPostgresDB = keycloak.db;
};
KeycloakCliService = customPkgs.mkKeycloakCliService rec {
name = "KeycloakCliService";
keycloakServiceName = "keycloak.service";
keycloakSecretsDir = (getTarget name).containers.keycloaksecrets.rootdir;
keycloakUrl = "https://${keycloak.subdomain}.${(getDomain "KeycloakService")}";
keycloakUser = KeycloakService.initialAdminUsername;
keys = {
userpasswords = "keycloakusers";
};
dependsOn = {
inherit KeycloakService HaproxyService;
};
config = (utils.recursiveMerge [
rec {
inherit realm;
domain = getDomain name;
roles = {
user = [];
admin = ["user"];
};
users = {
me = {
email = "me@${domain}";
firstName = "Me";
lastName = "Me";
roles = ["admin"];
initialPassword = true;
};
friend = {
email = "friend@${domain}";
firstName = "Friend";
lastName = "Friend";
roles = ["user"];
initialPassword = true;
};
};
}
vaultwarden.keycloakCliConfig
]);
};
KeycloakHaproxyService = customPkgs.mkKeycloakHaproxyService {
name = "KeycloakHaproxyService";
domain = "https://${keycloak.subdomain}.${getDomain "KeycloakService"}";
realms = [realm];
inherit KeycloakService;
};
vaultwarden = customPkgs.vaultwarden {
subdomain = "vaultwarden";
ingress = 18005;
sso.realm = realm;
sso.userRole = "user";
sso.adminRole = "admin";
inherit smtp;
inherit distribution HaproxyService KeycloakService KeycloakCliService;
};
HaproxyService = customPkgs.mkHaproxyService {
name = "HaproxyService";
user = "http";
group = "http";
dependsOn = {
inherit KeycloakHaproxyService;
};
config = {...}:
let
domain = getDomain "HaproxyService";
in {
certPath = "/var/lib/acme/${domain}/full.pem";
stats = {
port = 8404;
uri = "/stats";
refresh = "10s";
prometheusUri = "/metrics";
};
defaults = {
default-server = "init-addr last,none";
};
resolvers = {
default = {
nameservers = {
ns1 = "127.0.0.1:53";
};
};
};
sites = {
vaultwarden = vaultwarden.haproxy distribution.VaultwardenService;
keycloak = {
frontend = {
capture = [
"request header origin len 128"
];
acl = {
acl_keycloak = "hdr_beg(host) ${keycloak.subdomain}.";
acl_keycloak_authorized_origin = "capture.req.hdr(0) -m end .${domain}";
};
use_backend = "if acl_keycloak";
http-response = {
add-header = map (x: x + " if acl_keycloak_authorized_origin") [
"Access-Control-Allow-Origin %[capture.req.hdr(0)]"
"Access-Control-Allow-Methods GET,\\ HEAD,\\ OPTIONS,\\ POST,\\ PUT"
"Access-Control-Allow-Credentials true"
"Access-Control-Allow-Headers Origin,\\ Accept,\\ X-Requested-With,\\ Content-Type,\\ Access-Control-Request-Method,\\ Access-Control-Request-Headers,\\ Authorization"
];
};
};
backend = {
servers = [
{
name = "keycloak1";
address = "127.0.0.1:8080"; # TODO: should use the hostname
resolvers = "default";
}
];
cookie = "JSESSIONID prefix";
};
};
};
};
};
in
with pkgs.lib.attrsets;
rec {
inherit KeycloakPostgresDB KeycloakService KeycloakCliService KeycloakHaproxyService;
inherit HaproxyService;
}
// keycloak.services
// vaultwarden.services