summaryrefslogtreecommitdiff
path: root/hosts/ahmed/minecraft-log-server/default.nix
blob: 9ed5e90e495283ba64b18b276be64cd137bd7d8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# This module implements a really simple and shitty WSGI app which streams
# journald logs to the client. I don't expect it to hang around for long, so
# it's just quickly hacked together.
#
# FIXME: There's still the issue with broken connections. Perhaps heartbeat would fix.
{
  pkgs,
  config,
  ...
}: let
  # Enable HTTPS stuff.
  useACME = true;

  socket-path = "/run/minecraft-log-server.sock";

  python = pkgs.python3.withPackages (ps:
    with ps; [
      gevent
      gunicorn
    ]);
in {
  users.users.minecraft-log-server = {
    description = "Runs minecraft-log-server";
    group = "minecraft-log-server";
    isSystemUser = true;
  };
  users.groups.minecraft-log-server = {};

  systemd.sockets.minecraft-log-server = {
    description = "Socket where the service of the same name answers HTTP requests.";

    socketConfig = {
      ListenStream = socket-path;

      # TODO: wtf apple maps
      SocketUser = "nginx";
      SocketGroup = "nginx";
      SocketMode = "600";
    };

    wantedBy = ["sockets.target"];
  };

  # See: https://docs.gunicorn.org/en/23.0.0/deploy.html
  systemd.services.minecraft-log-server = {
    description = "Minecraft log server";

    serviceConfig = {
      # Using a non-sync worker class is super important because we have such long-running connections.
      ExecStart = "${python}/bin/gunicorn --worker-class=gevent --chdir ${./.} minecraft_log_server:app";

      ExecReload = "kill -s HUP $MAINPID";
      KillMode = "mixed";

      User = config.users.users.minecraft-log-server.uid;
      Group = config.users.users.minecraft-log-server.group;

      # gunicorn can let systemd know when it is ready
      Type = "notify";
      NotifyAccess = "main";

      # Harden
      ProtectSystem = "strict";
      PrivateTmp = true;
    };

    requires = ["minecraft-log-server.socket"]; # Refuse to start without.
    after = ["network.target"];
  };

  services.nginx = {
    virtualHosts."minecraft.linus.onl" = {
      enableACME = useACME;
      forceSSL = useACME;

      # Let's be safe and pass-word protect it just in case the logs contain some sensitive data.
      basicAuthFile = ./.htpasswd;

      # First try resolving files statically, before falling back to the CGI server.
      locations."/" = {
        alias = "${./public}/";
        index = "index.html";
        tryFiles = "$uri $uri/ @minecraft_log_server";
      };

      locations."@minecraft_log_server" = {
        recommendedProxySettings = true;

        # In addition to the important stuff set indirectly via `recommendedProxySettings`
        # (especially `proxy_http_version`), we need these options for SSE.
        extraConfig = ''
          # Disable buffering. This is crucial for SSE to ensure that
          # messages are sent immediately without waiting for a buffer to
          # fill.
          proxy_buffering off;

          # Disable caching to ensure that all messages are sent and received
          # in real-time without being cached by the proxy.
          proxy_cache off;

          # Set a long timeout for reading from the proxy to prevent the
          # connection from timing out. You may need to adjust this value
          # based on your specific requirements.
          proxy_read_timeout 86400;
        '';

        proxyPass = "http://unix:${socket-path}:$request_uri";
      };
    };
  };

  services.cloudflare-dyndns.domains = ["minecraft.linus.onl"];
}