# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
#
# SPDX-License-Identifier: Apache-2.0

{ depot, lib, pkgs, rebuilder, config, ... }:
let
  inherit (depot.ops) secrets;
  machineSecrets = secrets.machineSpecific.clouvider-lon01;
in {
  imports = [
    ../lib/zfs.nix
    ../lib/bgp.nix
    ../lib/whitby-distributed.nix
    ../lib/quotes.bfob.gg.nix
  ];

  boot.initrd = {
    availableKernelModules = [
      "xhci_pci"
      "ahci"
      "nvme"
      "usbhid"
      "usb_storage"
      "sd_mod"
      "sr_mod"
      "igb"
    ];
    network = {
      enable = true;
      ssh = {
        enable = true;
        hostKeys = ["/persist/etc/ssh/ssh_host_ed25519_key"];
        authorizedKeys = map builtins.readFile config.users.users.lukegb.openssh.authorizedKeys.keyFiles;
      };
      postCommands = ''
        echo "zfs load-key -a; killall zfs" >> /root/.profile
      '';
    };
  };
  boot.kernelParams = [
    "ip=185.198.188.29::185.198.188.28:255.255.255.254:clouvider-lon01:enp1s0f0:none"
  ];
  boot.kernelModules = [ "kvm-intel" ];

  powerManagement.cpuFreqGovernor = lib.mkDefault "performance";

  fileSystems = let
    zfs = device: {
      device = device;
      fsType = "zfs";
    };
  in {
    "/" = zfs "tank/local/root";
    "/nix" = zfs "tank/local/nix";
    "/persist" = zfs "tank/safe/persist";
    "/home" = zfs "tank/safe/home";

    "/boot1" = {
      device = "/dev/disk/by-partlabel/boota";
      fsType = "vfat";
    };
    "/boot2" = {
      device = "/dev/disk/by-partlabel/bootb";
      fsType = "vfat";
    };
  };

  nix.maxJobs = lib.mkDefault 8;

  # Use GRUB, so we can have mirrored bootloaders.
  boot.loader.efi.canTouchEfiVariables = true;
  boot.loader.grub = {
    enable = true;
    version = 2;
    zfsSupport = true;
    efiSupport = true;
    mirroredBoots = map (path: {
      inherit path;
      devices = ["nodev"];
      efiSysMountPoint = path;
    }) ["/boot1" "/boot2"];
  };

  # Networking!
  networking = {
    hostName = "clouvider-lon01";
    domain = "as205479.net";
    hostId = "29aaa324";

    nameservers = [
      "2001:4860:4860::8888"
      "2001:4860:4860::8844"
      "8.8.8.8"
      "8.8.4.4"
    ];
    useDHCP = false;
    defaultGateway = {
      address = "185.198.188.28";
      interface = "br-ext";
    };
    defaultGateway6 = {
      address = "2a0a:54c0:0:17::1";
      interface = "br-ext";
    };
    bridges.br-ext.interfaces = ["enp1s0f0"];
    interfaces.br-ext = {
      ipv4.addresses = [{ address = "185.198.188.29"; prefixLength = 31; }];
      ipv6.addresses = [{ address = "2a0a:54c0:0:17::2"; prefixLength = 126; }];
    };
    interfaces.lo = {
      ipv4.addresses = [{ address = "127.0.0.1"; prefixLength = 8; }] ++ (
        map (n: { address = "92.118.29.${toString n}"; prefixLength = 32; }) (lib.range 225 253));
      ipv6.addresses = [{ address = "::1"; prefixLength = 128; }];
    };
    firewall = {
      allowPing = true;
      allowedTCPPorts = [
        80 443  # HTTP/nginx
        6697    # znc
        34197   # factorio
      ];
      allowedUDPPorts = [
        34197   # factorio
      ];
      extraCommands = ''
        # Allow SSH on public interfaces.
        iptables -A INPUT -p tcp --dport 22 --dst 185.198.188.29 -j ACCEPT
        iptables -A INPUT -p tcp --dport 22 --dst ${config.my.ip.tailscale} -j ACCEPT 
        ip6tables -A INPUT -p tcp --dport 22 --dst 2a0a:54c0:0:17::2 -j ACCEPT
      '';
    };
  };
  my.ip.tailscale = "100.79.173.25";

  services.openssh.openFirewall = false;  # allowed by networking.firewall.extraCommands
  services.openssh.hostKeys = [
    {
      path = "/persist/etc/ssh/ssh_host_ed25519_key";
      type = "ed25519";
    }
    {
      path = "/persist/etc/ssh/ssh_host_rsa_key";
      type = "rsa";
      bits = 4096;
    }
  ];

  users.users = {
    lukegb.extraGroups = [ "bird2" ];
    minotarproxy = {
      isSystemUser = true;
    };
  };
  users.groups = {
    znc-acme = {
      members = [ "znc" "nginx" ];
    };
  };

  services.lukegbgp = let local = {
    asn = 205479;
  }; in {
    enable = true;
    config = {
      local = {
        routerID = "185.198.188.29";
      };
      export = {
        v4 = ["92.118.28.0/24" "92.118.29.0/24"];
      };
      peering = {
        clouvider = {
          local = local // {
            v4 = "185.198.188.29";
            v6 = "2a0a:54c0:0:17::2";
          };
          remote = {
            asn = 62240;
            export_community = 3000;
            routers = [{
              v4 = "185.198.188.28";
              v6 = "2a0a:54c0:0:17::1";
            }];
          };
        };
      };
    };
  };

  services.znc = {
    enable = true;
    mutable = true;
    dataDir = "/persist/etc/znc";
    useLegacyConfig = false;
  };
  security.acme = {
    acceptTerms = true;
    email = "letsencrypt@lukegb.com";
    certs."znc.lukegb.com" = {
      webroot = "/var/lib/acme/.challenges";
      group = "znc-acme";
      extraDomainNames = ["akiichiro.lukegb.com"];
    };
  };
  services.nginx = {
    enable = true;
    virtualHosts = {
      "clouvider-lon01.as205479.net" = {
        default = true;
        listen = [
          {addr = "185.198.188.29"; port = 80; ssl = false;}
          {addr = "[2a0a:54c0:0:17::2]"; port = 80; ssl = false;}
        ];
        locations."/.well-known/acme-challenge" = {
          root = "/var/lib/acme/.challenges";
        };
        locations."/" = {
          return = "301 https://$host$request_uri";
        };
      };
    };
  };

  systemd.services.minotarproxy = {
    description = "Minotar proxy";
    wants = ["network-online.target"];
    wantedBy = ["multi-user.target"];
    serviceConfig = {
      ExecStart = ''${depot.go.minotarproxy}/bin/minotarproxy --logtostderr --server_bind=92.118.29.225:443 --autocert_insecure_bind=92.118.29.225:80 --autocert_domain=minotarproxy.lukegb.xyz --outbound_bind="92.118.29.225,92.118.29.226,92.118.29.227,92.118.29.228,92.118.29.229,92.118.29.230,92.118.29.231,92.118.29.232,92.118.29.233,92.118.29.234,92.118.29.235,92.118.29.236,92.118.29.237,92.118.29.238,92.118.29.239,92.118.29.240,92.118.29.241,92.118.29.242,92.118.29.243,92.118.29.244,92.118.29.245,92.118.29.246,92.118.29.247,92.118.29.248,92.118.29.249,92.118.29.250,92.118.29.251,92.118.29.252,92.118.29.253" --autocert_cache_dir=/run/minotarproxy'';
      User = "minotarproxy";
      Restart = "always";
      AmbientCapabilities = "CAP_NET_BIND_SERVICE";
    };
  };
  systemd.tmpfiles.rules = [
    "d /run/minotarproxy 0700 minotarproxy - -"
  ];
  systemd.mounts = let
    bindMount' = dir: {
      unitConfig.RequiresMountsFor = dir;
      options = "bind";
      what = "/persist${dir}";
      where = dir;
    };
    bindMountSvc = dir: svc: (bindMount' dir) // {
      bindsTo = [svc];
      partOf = [svc];
    };
    bindMountSvcDynamic = dir: svc: (bindMount' "/var/lib/private/${dir}") // {
      requiredBy = [svc];
      before = [svc];
      wantedBy = ["multi-user.target"];
    };
    bindMount = dir: (bindMount' dir) // {
      wantedBy = ["multi-user.target"];
    };
  in [
    (bindMountSvcDynamic "factorio" "factorio.service")
  ];


  environment.etc."secrets/gitlab-runner-registration" = {
    text = ''
      CI_SERVER_URL=https://hg.lukegb.com
      REGISTRATION_TOKEN=${depot.ops.secrets.deployer.registrationToken}
    '';
    mode = "0600";
  };
  services.gitlab-runner = {
    enable = true;
    concurrent = 1;
    services = {
      deployer = {
        registrationConfigFile = "/etc/secrets/gitlab-runner-registration";
        executor = "shell";
        tagList = [ "cacher" ];
      };
    };
    gracefulTermination = true;
    gracefulTimeout = "4min";
    package = depot.nix.pkgs.heptapod-runner;
  };
  users.users.gitlab-runner = {
    createHome = true;
    home = "/srv/gitlab-runner";
  };

  nix.gc.automatic = false;

  services.factorio = {
    inherit (secrets.factorio) username token;
    enable = true;
    package = pkgs.factorio-headless-experimental;
    saveName = "lukegb20201124";
    game-name = "Briefcase Full of Bees";
    extraSettings = {
      admins = ["lukegb"];
      auto_pause = true;
      only_admins_can_pause_the_game = false;
      game_password = secrets.factorioServerPassword;
      non_blocking_saving = true;
      autosave_only_on_server = true;
      autosave_interval = 5;
      autosave_slots = 60;
    };
  };

  my.quotesdb.listen = [
    "185.198.188.29"
    "[2a0a:54c0:0:17::2]"
  ];

  system.stateVersion = "20.09";
}