From 04c1d91502e48aeb83533c5a8f5939e8a7a20f60 Mon Sep 17 00:00:00 2001 From: Mike Cugini Date: Mon, 9 Nov 2020 10:20:09 -0500 Subject: [PATCH] initial commit: nixos base image + matrix server setup --- .gitignore | 2 + base_image/configuration.nix | 10 +++ base_image/gen_base_image.sh | 5 ++ nixos_configs/matrix.nix | 148 +++++++++++++++++++++++++++++++++++ nixos_configs/template.nix | 16 ++++ terraform/matrix.tf | 39 +++++++++ terraform/provider.tf | 14 ++++ 7 files changed, 234 insertions(+) create mode 100644 .gitignore create mode 100644 base_image/configuration.nix create mode 100755 base_image/gen_base_image.sh create mode 100644 nixos_configs/matrix.nix create mode 100644 nixos_configs/template.nix create mode 100644 terraform/matrix.tf create mode 100644 terraform/provider.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f22b60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.envrc +.terraform diff --git a/base_image/configuration.nix b/base_image/configuration.nix new file mode 100644 index 0000000..89b6892 --- /dev/null +++ b/base_image/configuration.nix @@ -0,0 +1,10 @@ +{ config, pkgs, ... }: +{ + environment.systemPackages = [ pkgs.jq ]; + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 ]; + + users.users.root.openssh.authorizedKeys.keys = [ + (builtins.readFile "/home/mike/.ssh/id_mops.pub") + ]; +} diff --git a/base_image/gen_base_image.sh b/base_image/gen_base_image.sh new file mode 100755 index 0000000..7515f43 --- /dev/null +++ b/base_image/gen_base_image.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +nixos-generate -f do -c configuration.nix + +# TODO: automate upload of image diff --git a/nixos_configs/matrix.nix b/nixos_configs/matrix.nix new file mode 100644 index 0000000..adb0217 --- /dev/null +++ b/nixos_configs/matrix.nix @@ -0,0 +1,148 @@ +# adapted from https://nixos.org/manual/nixos/stable/index.html#module-services-matrix +{ modulesPath, config, lib, pkgs, ... }: +let + storage-device = "/dev/disk/by-id/scsi-0DO_Volume_matrix-storage"; + storage-dir = "/opt/matrix-data"; + matrix-reg-key = (builtins.readFile ./matrix_reg_key); + fqdn = + let + join = hostName: domain: hostName + lib.optionalString (domain != null) ".${domain}"; + in join config.networking.hostName config.networking.domain; +in { + imports = [ + "${toString modulesPath}/virtualisation/digital-ocean-image.nix" + ]; + + environment.systemPackages = [ pkgs.jq ]; + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 80 443 ]; + + users.users.root.openssh.authorizedKeys.keys = [ + (builtins.readFile "/home/mike/.ssh/id_mops.pub") + ]; + + ### app specific config + + # mount DigitalOcean volume for use by postgres + fileSystems."${storage-dir}" = { + device = storage-device; + }; + + networking = { + hostName = "matrix"; + domain = "waffle.farm"; + }; + + services.postgresql = { + enable = true; + dataDir = "${storage-dir}/db"; + + initialScript = pkgs.writeText "synapse-init.sql" '' + CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; + CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + ''; + }; + + services.nginx = { + enable = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + + virtualHosts = { + "${config.networking.domain}" = { + enableACME = true; + forceSSL = true; + + locations."= /.well-known/matrix/server".extraConfig = + let + # use 443 instead of the default 8448 port to unite + # the client-server and server-server port for simplicity + server = { "m.server" = "${fqdn}:443"; }; + in '' + add_header Content-Type application/json; + return 200 '${builtins.toJSON server}'; + ''; + locations."= /.well-known/matrix/client".extraConfig = + let + client = { + "m.homeserver" = { "base_url" = "https://${fqdn}"; }; + "m.identity_server" = { "base_url" = "https://vector.im"; }; + }; + # ACAO required to allow element-web on any URL to request this json file + in '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON client}'; + ''; + locations."/".extraConfig = '' + return 301 https://chat.waffle.farm; + ''; + + }; + # Reverse proxy for Matrix client-server and server-server communication + ${fqdn} = { + enableACME = true; + forceSSL = true; + + # Or do a redirect instead of the 404, or whatever is appropriate for you. + # But do not put a Matrix Web client here! See the Element web section below. + locations."/".extraConfig = '' + return 301 https://chat.waffle.farm; + ''; + + # forward all Matrix API calls to the synapse Matrix homeserver + locations."/_matrix" = { + proxyPass = "http://[::1]:8008"; # without a trailing / + }; + }; + + "chat.${config.networking.domain}" = { + enableACME = true; + forceSSL = true; + serverAliases = [ + "chat.${config.networking.domain}" + ]; + root = pkgs.element-web.override { + conf = { + default_server_config."m.homeserver" = { + "base_url" = "https://${fqdn}"; + "server_name" = "${config.networking.domain}"; + }; + }; + }; + }; + }; + }; + services.matrix-synapse = { + enable = true; + server_name = config.networking.domain; + registration_shared_secret = matrix-reg-key; + + listeners = [ + { + port = 8008; + bind_address = "::1"; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = [ "client" "federation" ]; + compress = false; + } + ]; + } + ]; + }; + security.acme.acceptTerms = true; + security.acme.certs = { + "${fqdn}".email = "mike@betamike.com"; + "${config.networking.domain}".email = "mike@betamike.com"; + "chat.${config.networking.domain}".email = "mike@betamike.com"; + }; +} diff --git a/nixos_configs/template.nix b/nixos_configs/template.nix new file mode 100644 index 0000000..f9e2cd5 --- /dev/null +++ b/nixos_configs/template.nix @@ -0,0 +1,16 @@ +{ modulesPath, config, lib, pkgs, ... }: +{ + imports = [ + "${toString modulesPath}/virtualisation/digital-ocean-image.nix" + ]; + + environment.systemPackages = [ pkgs.jq ]; + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 ]; + + users.users.root.openssh.authorizedKeys.keys = [ + (builtins.readFile "/home/mike/.ssh/id_mops.pub") + ]; + + # Put host specific logic here +} diff --git a/terraform/matrix.tf b/terraform/matrix.tf new file mode 100644 index 0000000..36e8905 --- /dev/null +++ b/terraform/matrix.tf @@ -0,0 +1,39 @@ +data "digitalocean_image" "nixos_base" { + name = "nixos-base" +} + +resource "digitalocean_volume" "matrix" { + region = "nyc3" + name = "matrix-storage" + size = 10 + initial_filesystem_type = "ext4" +} + +resource "digitalocean_droplet" "matrix-0" { + name = "matrix-0" + + image = data.digitalocean_image.nixos_base.id + region = "nyc3" + size = "s-1vcpu-1gb" + + ssh_keys = [data.digitalocean_ssh_key.mops.id] +} + +resource "digitalocean_volume_attachment" "matrix" { + droplet_id = digitalocean_droplet.matrix-0.id + volume_id = digitalocean_volume.matrix.id +} + +module "deploy_nixos" { + source = "github.com/tweag/terraform-nixos//deploy_nixos?ref=d055d2180da230e47ba9082fc53a8b7d1fadbc43" + + nixos_config = "../nixos_configs/matrix.nix" + + target_user = "root" + target_host = digitalocean_droplet.matrix-0.ipv4_address + + triggers = { + // Also re-deploy whenever the VM is re-created + instance_id = digitalocean_droplet.matrix-0.id + } +} diff --git a/terraform/provider.tf b/terraform/provider.tf new file mode 100644 index 0000000..abe573c --- /dev/null +++ b/terraform/provider.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + digitalocean = { + source = "digitalocean/digitalocean" + version = "2.1.0" + } + } +} + +provider "digitalocean" {} + +data "digitalocean_ssh_key" "mops" { + name = "mops" +}