From 90377544facc29a2357a9d6e6d691c41d13d57ad Mon Sep 17 00:00:00 2001 From: Ingolf Wagner Date: Sun, 14 Feb 2021 20:24:19 +0100 Subject: [PATCH] add space-left setup --- terranix/space-left/.gitignore | 2 + terranix/space-left/README.md | 55 ++++++ terranix/space-left/config.nix | 47 +++++ .../plops/configs/nixserver-host/codimd.nix | 29 +++ .../configs/nixserver-host/configuration.nix | 19 ++ .../plops/configs/nixserver-host/gitlab.nix | 49 ++++++ .../nixserver-host/hardware-configuration.nix | 10 ++ .../plops/configs/nixserver-host/jitsi.nix | 61 +++++++ .../plops/configs/nixserver-host/netdata.nix | 26 +++ .../plops/configs/nixserver-host/ssh.nix | 14 ++ .../configs/nixserver-host/workadventure.nix | 165 ++++++++++++++++++ terranix/space-left/plops/shell.nix | 71 ++++++++ terranix/space-left/shell.nix | 47 +++++ 13 files changed, 595 insertions(+) create mode 100644 terranix/space-left/.gitignore create mode 100644 terranix/space-left/README.md create mode 100644 terranix/space-left/config.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/codimd.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/configuration.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/gitlab.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/hardware-configuration.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/jitsi.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/netdata.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/ssh.nix create mode 100644 terranix/space-left/plops/configs/nixserver-host/workadventure.nix create mode 100644 terranix/space-left/plops/shell.nix create mode 100644 terranix/space-left/shell.nix diff --git a/terranix/space-left/.gitignore b/terranix/space-left/.gitignore new file mode 100644 index 0000000..6073c1a --- /dev/null +++ b/terranix/space-left/.gitignore @@ -0,0 +1,2 @@ +plops/generated/ +sshkey* \ No newline at end of file diff --git a/terranix/space-left/README.md b/terranix/space-left/README.md new file mode 100644 index 0000000..953fcbf --- /dev/null +++ b/terranix/space-left/README.md @@ -0,0 +1,55 @@ +# NixOS Server Example with plops + +This setup shows: + +* how to use a terranix module +* how to use 3rd party provision software after terraform. +* how to run terranix and terraform + +Setup containing opinionated modules to deploy +[NixOS servers](https://nixos.org/) +on +[hcloud](https://www.hetzner.com/cloud) +using +[nixos-infect](https://github.com/elitak/nixos-infect) +with my +[plops](https://github.com/mrVanDalo/plops) +provisioning tool for NixOS, +which is an overlay on +[krops](https://cgit.krebsco.de/krops/about/). + +After server creation, +the initial provisioning uploads the +nixos-infect +script and applys it. +After server creation and initialization +terranix/terraform generates +files used for the "real" provisioning +done by plops. + +Of course instead of plops you can use every provsioning tool you like +here (e.g. NixOps, Ansible, ... ) + +# How to Run + +## What you need + +* a setup [passwordstore](https://www.passwordstore.org/). +* a [hcloud token](https://docs.hetzner.cloud/#overview-getting-started) + stored under `development/hetzner.com/api-token` + +## Steps + +* `terraform-prepare`: to create ssh keys. +* `terraform-build`: to run terranix and terraform do create server. +* `terraform-destroy`: to delete server (don't forget that step, or else it gets costly) +* `terraform-cleanup`: to delete ssh keys and terraform data. + + +## DNS + +define domains with your nameserver and update `jitsi.nix` and `workadventure.nix`. + +* `meet.${domain}` to given ip4 address +* `party.${domain}` to given ip4 address +* `*.party.${domain}` to given ip4 address diff --git a/terranix/space-left/config.nix b/terranix/space-left/config.nix new file mode 100644 index 0000000..32349b0 --- /dev/null +++ b/terranix/space-left/config.nix @@ -0,0 +1,47 @@ +{ config, lib, pkgs, ... }: +let + hcloud-modules = pkgs.fetchgit { + url = "https://github.com/mrVanDalo/terranix-hcloud.git"; + rev = "5fa359a482892cd973dcc6ecfc607f4709f24495"; + sha256 = "0smgmdiklj98y71fmcdjsqjq8l41i66hs8msc7k4m9dpkphqk86p"; + }; +in { + + imports = [ "${hcloud-modules}/default.nix" ]; + + # configure temporary admin ssh keys + users.admins.palo.publicKey = "${lib.fileContents ./sshkey.pub}"; + + # configure provisioning private Key to be used when running provisioning on the machines + provisioner.privateKeyFile = toString ./sshkey; + + hcloud.nixserver = { + host = { + enable = true; + serverType = "cx51"; # 35€/month + configurationFile = pkgs.writeText "configuration.nix" '' + { pkgs, lib, config, ... }: + { + environment.systemPackages = [ pkgs.git ]; + } + ''; + }; + }; + + hcloud.export.nix = toString ./plops/generated/nixos-machines.nix; + + resource.local_file.sshConfig = { + filename = "${toString ./plops/generated/ssh-configuration}"; + content = with lib; + let + configPart = name: '' + Host ''${ hcloud_server.nixserver-${name}.ipv4_address } + IdentityFile ${toString ./sshkey} + ServerAliveInterval 60 + ServerAliveCountMax 3 + ''; + in concatStringsSep "\n" + (map configPart (attrNames config.hcloud.nixserver)); + }; + +} diff --git a/terranix/space-left/plops/configs/nixserver-host/codimd.nix b/terranix/space-left/plops/configs/nixserver-host/codimd.nix new file mode 100644 index 0000000..fbcc958 --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/codimd.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: { + + services.nginx.enable = true; + services.nginx.virtualHosts.codimd = { + enableACME = true; + addSSL = true; + serverName = "codimd.ingolf-wagner.de"; + locations."/".extraConfig = '' + client_max_body_size 4G; + proxy_set_header Host $host; + proxy_pass http://localhost:3091; + ''; + }; + + services.codimd = { + enable = true; + configuration = { + allowFreeURL = true; + db = { + dialect = "sqlite"; + storage = "/var/lib/codimd/db.codimd.sqlite"; + useCDN = false; + }; + port = 3091; + }; + }; + +} + diff --git a/terranix/space-left/plops/configs/nixserver-host/configuration.nix b/terranix/space-left/plops/configs/nixserver-host/configuration.nix new file mode 100644 index 0000000..c710230 --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/configuration.nix @@ -0,0 +1,19 @@ +{ config, pkgs, lib, ... }: { + imports = [ + # + ./codimd.nix + ./hardware-configuration.nix + ./jitsi.nix + ./netdata.nix + ./ssh.nix + ./workadventure.nix + ]; + + environment.systemPackages = [ pkgs.git pkgs.ag pkgs.htop ]; + + networking.hostName = "space-left"; + + security.acme.email = "contact@ingolf-wagner.de"; + security.acme.acceptTerms = true; + +} diff --git a/terranix/space-left/plops/configs/nixserver-host/gitlab.nix b/terranix/space-left/plops/configs/nixserver-host/gitlab.nix new file mode 100644 index 0000000..3786d2e --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/gitlab.nix @@ -0,0 +1,49 @@ +{ config, pkgs, lib, ... }: +let domain = "gitlab.space-left.org"; +in { + + # setup gitlab + services.gitlab = { + enable = true; + host = domain; + databasePasswordFile = "path/todo"; + initialRootPasswordFile = "path/todo"; + + secrets = { + # Make sure the secret is at least 30 characters and all random, + # no regular words or you'll be exposed to dictionary attacks + dbFile = "path/todo"; + + # openssl genrsa 2048 + jwsFile = "path/todo"; + + # Make sure the secret is at least 30 characters and all random, + # no regular words or you'll be exposed to dictionary attacks + otpFile = "path/todo"; + + # Make sure the secret is at least 30 characters and all random, + # no regular words or you'll be exposed to dictionary attacks + secretFile = "path/todo"; + }; + + # smtp? + + # gitlab-runner? + }; + + # setup nginx for gitlab + services.nginx = { + enable = true; + recommendedProxySettings = true; + + virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString config.services.gitlab.port}"; + }; + }; + }; + +} + diff --git a/terranix/space-left/plops/configs/nixserver-host/hardware-configuration.nix b/terranix/space-left/plops/configs/nixserver-host/hardware-configuration.nix new file mode 100644 index 0000000..ae82a10 --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/hardware-configuration.nix @@ -0,0 +1,10 @@ +{ ... }: { + imports = [ ]; + boot.initrd.availableKernelModules = + [ "ata_piix" "uhci_hcd" "virtio_pci" "sd_mod" "sr_mod" ]; + boot.loader.grub.device = "/dev/sda"; + fileSystems."/" = { + device = "/dev/sda1"; + fsType = "ext4"; + }; +} diff --git a/terranix/space-left/plops/configs/nixserver-host/jitsi.nix b/terranix/space-left/plops/configs/nixserver-host/jitsi.nix new file mode 100644 index 0000000..aaffea5 --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/jitsi.nix @@ -0,0 +1,61 @@ +{ + # + + + # | | + # | | + # v v + # 80, 443 TCP 443 TCP, 10000 UDP + # +--------------+ +---------------------+ + # | nginx | 5222, 5347 TCP | | + # | jitsi-meet |<-------------------+| jitsi-videobridge | + # | prosody | | | | + # | jicofo | | +---------------------+ + # +--------------+ | + # | +---------------------+ + # | | | + # +----------+| jitsi-videobridge | + # | | | + # | +---------------------+ + # | + # | +---------------------+ + # | | | + # +----------+| jitsi-videobridge | + # | | + # +---------------------+ + + # This is a one server setup + services.jitsi-meet = { + enable = true; + hostName = "meet.ingolf-wagner.de"; + + # JItsi COnference FOcus is a server side focus component used in Jitsi Meet conferences. + # https://github.com/jitsi/jicofo + jicofo.enable = true; + + # Whether to enable nginx virtual host that will serve the javascript application and act as a proxy for the XMPP server. + # Further nginx configuration can be done by adapting services.nginx.virtualHosts.. When this is enabled, ACME + # will be used to retrieve a TLS certificate by default. To disable this, set the + # services.nginx.virtualHosts..enableACME to false and if appropriate do the same for + # services.nginx.virtualHosts..forceSSL. + nginx.enable = true; + + # https://github.com/jitsi/jitsi-meet/blob/master/config.js + config = { + enableWelcomePage = false; + defaultLang = "en"; + + }; + + # https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js + interfaceConfig = { + SHOW_JITSI_WATERMARK = false; + SHOW_WATERMARK_FOR_GUESTS = false; + }; + + }; + + networking.firewall = { + allowedTCPPorts = [ 80 443 ]; + allowedUDPPorts = [ 10000 ]; + }; + +} diff --git a/terranix/space-left/plops/configs/nixserver-host/netdata.nix b/terranix/space-left/plops/configs/nixserver-host/netdata.nix new file mode 100644 index 0000000..7bfdfc6 --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/netdata.nix @@ -0,0 +1,26 @@ +{ + services.netdata = { + enable = true; + config = { + #"exporting:global" = { "enabled" = "yes"; }; + global = { + "memory mode" = "dbengine"; + "dbengine disk space" = 1024 * 10; # in MB + "debug log" = "none"; + "access log" = "none"; + "error log" = "syslog"; + }; + }; + }; + + services.nginx.enable = true; + services.nginx.virtualHosts."netdata.party.ingolf-wagner.de" = { + enableACME = true; + forceSSL = true; + basicAuth.admin = "NYsXfBKRwkkS60WIeZONtFTv3nz4tPy52uqLkzJzuc"; + locations."/" = { + proxyPass = "http://localhost:19999"; + proxyWebsockets = true; + }; + }; +} diff --git a/terranix/space-left/plops/configs/nixserver-host/ssh.nix b/terranix/space-left/plops/configs/nixserver-host/ssh.nix new file mode 100644 index 0000000..c794884 --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/ssh.nix @@ -0,0 +1,14 @@ +{ + # ssh configuration + # ----------------- + services.sshd.enable = true; + services.openssh.passwordAuthentication = false; + services.openssh.banner = '' + [ JITSI Server ] + ''; + + # the public ssh key used at deployment + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6uza62+Go9sBFs3XZE2OkugBv9PJ7Yv8ebCskE5WYPcahMZIKkQw+zkGI8EGzOPJhQEv2xk+XBf2VOzj0Fto4nh8X5+Llb1nM+YxQPk1SVlwbNAlhh24L1w2vKtBtMy277MF4EP+caGceYP6gki5+DzlPUSdFSAEFFWgN1WPkiyUii15Xi3QuCMR8F18dbwVUYbT11vwNhdiAXWphrQG+yPguALBGR+21JM6fffOln3BhoDUp2poVc5Qe2EBuUbRUV3/fOU4HwWVKZ7KCFvLZBSVFutXCj5HuNWJ5T3RuuxJSmY5lYuFZx9gD+n+DAEJt30iXWcaJlmUqQB5awcB1S2d9pJ141V4vjiCMKUJHIdspFrI23rFNYD9k2ZXDA8VOnQE33BzmgF9xOVh6qr4G0oEpsNqJoKybVTUeSyl4+ifzdQANouvySgLJV/pcqaxX1srSDIUlcM2vDMWAs3ryCa0aAlmAVZIHgRhh6wa+IXW8gIYt+5biPWUuihJ4zGBEwkyVXXf2xsecMWCAGPWPDL0/fBfY9krNfC5M2sqxey2ShFIq+R/wMdaI7yVjUCF2QIUNiIdFbJL6bDrDyHnEXJJN+rAo23jUoTZZRv7Jq3DB/A5H7a73VCcblZyUmwMSlpg3wos7pdw5Ctta3zQPoxoAKGS1uZ+yTeZbPMmdbw==" + ]; +} diff --git a/terranix/space-left/plops/configs/nixserver-host/workadventure.nix b/terranix/space-left/plops/configs/nixserver-host/workadventure.nix new file mode 100644 index 0000000..2126eac --- /dev/null +++ b/terranix/space-left/plops/configs/nixserver-host/workadventure.nix @@ -0,0 +1,165 @@ +{ config, pkgs, lib, ... }: +let + # If your Jitsi environment has authentication set up, + # you MUST set JITSI_PRIVATE_MODE to "true" and + # you MUST pass a SECRET_JITSI_KEY to generate the JWT secret + jitsiPrivateMode = "false"; + + secretJitsiKey = ""; + + jitsiISS = ""; + + workadventureSecretKey = "YXNkZnNkZmxranNhZGxma2phc2RsZmtqYXNsa2Zkago="; + + jitsiURL = "meet.ingolf-wagner.de"; + + domain = "party.ingolf-wagner.de"; + # domain will redirect to this map. (not play.${domain}) + defaultMap = "mrvandalo.github.io/workadventure-worlds/main.json"; + + apiURL = "api.${domain}"; + apiPort = 9002; + + frontURL = "play.${domain}"; + frontPort = 9004; + + pusherURL = "push.${domain}"; + pusherPort = 9005; + + uploaderURL = "upload.${domain}"; + uploaderPort = 9006; + + frontImage = "thecodingmachine/workadventure-front:develop"; + pusherImage = "thecodingmachine/workadventure-pusher:develop"; + apiImage = "thecodingmachine/workadventure-back:develop"; + uploaderImage = "thecodingmachine/workadventure-uploader:develop"; + +in { + + virtualisation.docker.enable = true; + boot.kernel.sysctl."net.ipv4.ip_forward" = true; + + networking.firewall = { + allowedTCPPorts = [ 80 443 ]; + allowedUDPPorts = [ 80 443 ]; + }; + + services.nginx.enable = true; + services.nginx.recommendedProxySettings = true; + + systemd.services.workadventure-network = { + enable = true; + wantedBy = [ "multi-user.target" ]; + script = '' + ${pkgs.docker}/bin/docker network create --driver bridge workadventure ||: + ''; + after = [ "docker" ]; + before = [ + "docker-workadventure-back.service" + "docker-workadventure-pusher.service" + "docker-workadventure-uploader.service" + "docker-workadventure-website.service" + ]; + }; + + virtualisation.oci-containers.backend = "docker"; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + return = "302 $scheme://play.${domain}/_/global/${defaultMap}"; + }; + }; + + virtualisation.oci-containers.containers.workadventure-front = { + image = frontImage; + environment = { + API_URL = pusherURL; + JITSI_PRIVATE_MODE = jitsiPrivateMode; + JITSI_URL = jitsiURL; + SECRET_JITSI_KEY = secretJitsiKey; + UPLOADER_URL = uploaderURL; + }; + ports = [ "127.0.0.1:${toString frontPort}:80" ]; + extraOptions = [ "--network=workadventure" ]; + }; + services.nginx.virtualHosts."${frontURL}" = { + enableACME = true; + forceSSL = true; + locations."/" = { proxyPass = "http://127.0.0.1:${toString frontPort}"; }; + }; + + virtualisation.oci-containers.containers.workadventure-pusher = { + image = pusherImage; + environment = { + API_URL = "workadventure-back:50051"; + JITSI_ISS = jitsiISS; + JITSI_URL = jitsiURL; + SECRET_KEY = workadventureSecretKey; + }; + ports = [ "127.0.0.1:${toString pusherPort}:8080" ]; + extraOptions = [ "--network=workadventure" ]; + }; + services.nginx.virtualHosts."${pusherURL}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString pusherPort}"; + proxyWebsockets = true; + }; + locations."/room" = { + proxyPass = "http://127.0.0.1:${toString pusherPort}"; + proxyWebsockets = true; + }; + }; + + virtualisation.oci-containers.containers.workadventure-back = { + image = apiImage; + environment = { + #DEBUG = "*"; + JITSI_ISS = jitsiISS; + JITSI_URL = jitsiURL; + SECRET_KEY = workadventureSecretKey; + }; + ports = [ "127.0.0.1:${toString apiPort}:8080" "50051" ]; + extraOptions = [ "--network=workadventure" ]; + }; + services.nginx.virtualHosts."${apiURL}" = { + enableACME = true; + forceSSL = true; + locations."/" = { proxyPass = "http://127.0.0.1:${toString apiPort}"; }; + }; + + virtualisation.oci-containers.containers.workadventure-uploader = { + image = uploaderImage; + ports = [ "127.0.0.1:${toString uploaderPort}:8080" ]; + extraOptions = [ "--network=workadventure" ]; + }; + services.nginx.virtualHosts."${uploaderURL}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString uploaderPort}"; + proxyWebsockets = true; + }; + }; + + systemd.services.docker-workadventure-front.serviceConfig = { + StandardOutput = lib.mkForce "journal"; + StandardError = lib.mkForce "journal"; + }; + systemd.services.docker-workadventure-uploader.serviceConfig = { + StandardOutput = lib.mkForce "journal"; + StandardError = lib.mkForce "journal"; + }; + systemd.services.docker-workadventure-pusher.serviceConfig = { + StandardOutput = lib.mkForce "journal"; + StandardError = lib.mkForce "journal"; + }; + systemd.services.docker-workadventure-back.serviceConfig = { + StandardOutput = lib.mkForce "journal"; + StandardError = lib.mkForce "journal"; + }; + +} diff --git a/terranix/space-left/plops/shell.nix b/terranix/space-left/plops/shell.nix new file mode 100644 index 0000000..22a6f94 --- /dev/null +++ b/terranix/space-left/plops/shell.nix @@ -0,0 +1,71 @@ +let + + # import plops with pkgs and lib + opsImport = import ((import { }).fetchgit { + url = "https://github.com/mrVanDalo/plops.git"; + rev = "9fabba016a3553ae6e13d5d17d279c4de2eb00ad"; + sha256 = "193pajq1gcd9jyd12nii06q1sf49xdhbjbfqk3lcq83s0miqfs63"; + }); + + ops = let + overlay = self: super: { + # overwrite ssh to use the generated ssh configuration + openssh = super.writeShellScriptBin "ssh" '' + ${super.openssh}/bin/ssh -F ${ + toString ./generated/ssh-configuration + } "$@" + ''; + }; + in opsImport { overlays = [ overlay ]; }; + + lib = ops.lib; + pkgs = ops.pkgs; + + # define all sources + source = { + + # nixpkgs (no need for channels anymore) + nixPkgs.nixpkgs.git = { + ref = "nixos-20.09"; + url = "https://github.com/NixOS/nixpkgs"; + }; + + # system configurations + system = name: { + configs.file = toString ./configs; + nixos-config.symlink = "configs/${name}/configuration.nix"; + }; + + # secrets which are hold and stored by pass + secrets = name: { + secrets.pass = { + dir = toString ./secrets; + name = name; + }; + }; + }; + + servers = import ./generated/nixos-machines.nix; + + deployServer = name: + { user ? "root", host, ... }: + with ops; + jobs "deploy-${name}" "${user}@${host.ipv4}" [ + # deploy secrets to /run/plops-secrets/secrets + # (populateTmpfs (source.secrets name)) + # deploy system to /var/src/system + (populate (source.system name)) + # deploy nixpkgs to /var/src/nixpkgs + (populate source.nixPkgs) + switch + ]; + +in pkgs.mkShell { + + buildInputs = lib.mapAttrsToList deployServer servers; + + shellHook = '' + export PASSWORD_STORE_DIR=./secrets + ''; + +} diff --git a/terranix/space-left/shell.nix b/terranix/space-left/shell.nix new file mode 100644 index 0000000..1b9722a --- /dev/null +++ b/terranix/space-left/shell.nix @@ -0,0 +1,47 @@ +{ pkgs ? import { } }: +let + + terranix = pkgs.callPackage (pkgs.fetchgit { + url = "https://github.com/mrVanDalo/terranix.git"; + rev = "2.3.0"; + sha256 = "030067h3gjc02llaa7rx5iml0ikvw6szadm0nrss2sqzshsfimm4"; + }) { }; + + terraform = pkgs.writers.writeBashBin "terraform" '' + export TF_VAR_hcloud_api_token=`${pkgs.pass}/bin/pass development/hetzner.com/api-token` + ${pkgs.terraform_0_12}/bin/terraform "$@" + ''; + +in pkgs.mkShell { + + buildInputs = [ + + terranix + terraform + + (pkgs.writers.writeBashBin "terraform-prepare" '' + ${pkgs.openssh}/bin/ssh-keygen -P "" -f ${toString ./.}/sshkey + '') + + (pkgs.writers.writeBashBin "terraform-build" '' + set -e + set -o pipefail + ${terranix}/bin/terranix | ${pkgs.jq}/bin/jq '.' > config.tf.json + ${terraform}/bin/terraform init + ${terraform}/bin/terraform apply + '') + + (pkgs.writers.writeBashBin "terraform-destroy" '' + ${terraform}/bin/terraform destroy + rm ${toString ./.}/config.tf.json + '') + + (pkgs.writers.writeBashBin "terraform-cleanup" '' + rm ${toString ./.}/sshkey + rm ${toString ./.}/sshkey.pub + rm ${toString ./.}/terraform.tfstate* + '') + + ]; +} +