Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pterodactyl-wings and nixos/pterodactyl-wings: init at 1.11.8 #290495

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions maintainers/maintainer-list.nix
Expand Up @@ -12493,6 +12493,15 @@
githubId = 15093162;
name = "Melanie B. Sigl";
};
melvyn2 = {
email = "melvyn2@dnsense.pub";
github = "melvyn2";
githubId = 9157412;
name = "melvyn";
keys = [{
fingerprint = "232B 9F00 2153 CA86 849C 9224 25A2 B728 0CE3 AFF6";
}];
};
mephistophiles = {
email = "mussitantesmortem@gmail.com";
name = "Maxim Zhukov";
Expand Down
3 changes: 3 additions & 0 deletions nixos/doc/manual/release-notes/rl-2405.section.md
Expand Up @@ -105,6 +105,9 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m

- [Pretix](https://pretix.eu/about/en/), an open source ticketing software for events. Available as [services.pretix]($opt-services-pretix.enable).

- The wings daemon for [Pterodactyl](https://pterodactyl.io/), a game server management service.
Available as [services.pterodactyl-wings](#opt-services.pterodactyl-wings.enable).

- [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).

- [armagetronad](https://wiki.armagetronad.org), a mid-2000s 3D lightcycle game widely played at iD Tech Camps. You can define multiple servers using `services.armagetronad.<server>.enable`.
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Expand Up @@ -525,6 +525,7 @@
./services/games/quake3-server.nix
./services/games/teeworlds.nix
./services/games/terraria.nix
./services/games/pterodactyl-wings.nix
./services/games/xonotic.nix
./services/hardware/acpid.nix
./services/hardware/actkbd.nix
Expand Down
218 changes: 218 additions & 0 deletions nixos/modules/services/games/pterodactyl-wings.nix
@@ -0,0 +1,218 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.pterodactyl-wings;
settingsFormat = pkgs.formats.yaml {};
in
{
options.services.pterodactyl-wings = with lib; with lib.types; {
enable = mkEnableOption "the Pterodactyl wings server manager";
melvyn2 marked this conversation as resolved.
Show resolved Hide resolved

tokenFile = mkOption {
type = types.path;
description = ''
Path to the panel-generated token for this wings instance.
'';
};
settings = mkOption {
description = ''
Configuration options for the wings daemon.
Some required values must be generated from the controlling panel.
See [reference](https://github.com/pterodactyl/wings/blob/develop/config/config.go)
for all options.
'';
default = {};
example = {
remote = "https://example.com";
uuid = "e851b74c-2e88-4d5c-8c6e-381218c7119d";
token_id = "fdZUuYH0k4aa8OGJ";
melvyn2 marked this conversation as resolved.
Show resolved Hide resolved
api = {
host = "127.0.0.1";
port = 6868;
upload_limit = 1000;
};
system = {
sftp.bind_port = 6869;
root_directory = "/var/lib/pterodactyl";
data = "/var/lib/pterodactyl/volumes";
archive_directory = "/var/lib/pterodactyl/archives";
backup_directory = "/var/lib/pterodactyl/backups";
};
allowed_origins = [ "https://panel.example.com" ];
};
type = submodule {
freeformType = settingsFormat.type;
options = {
uuid = mkOption {
type = str;
description = ''
A unique identifier for this node in the Panel.
Input this from the configuration generated on the panel.
'';
};
token_id = mkOption {
type = str;
description = ''
An identifier for the token which must be included in any requests
to the panel so that the token can be looked up correctly.
Input this from the configuration generated on the panel.
'';
};
api = {
host = mkOption {
type = str;
default = "0.0.0.0";
example = "[::1]";
description = "The interface that the internal webserver should bind to.";
};
port = mkOption {
type = port;
default = 8080;
description = "The port that the internal webserver should bind to.";
};
ssl = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should nudge people to use the acme module

enabled = mkEnableOption ''
HTTPS for the API interface.
Consider using security.acme to create certificates,
or using a reverse proxy like nginx to secure the connection.
'';
cert = mkOption {
type = nullOr path;
default = null;
description = "Path to certificate to use for HTTPS.";
};
key = mkOption {
type = nullOr path;
default = null;
description = "Path to key to use for HTTPS.";
};
};
};
system = {
root_directory = mkOption {
type = path;
default = "/var/lib/pterodactyl";
description = "Root directory where daemon data is stored.";
};
log_directory = mkOption {
type = path;
default = "/var/log/pterodactyl";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot log to journald?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The daemon also logs to stdout, and so journald captures the output. I didn't see a way to disable file logging, so I left the option.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we redirect the file logging to /dev/log which ends up in journald?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would end up double-logging because it would duplicate the stdout logging. The daemon also logs to arbitrary files in the directory, which I'm not sure how to redirect.

description = "Directory where logs for server installations and other wings events are logged.";
};
data = mkOption {
type = path;
defaultText = "/var/lib/pterodactyl/volumes";
description = "Directory where game server data is stored.";
};
archive_directory = mkOption {
type = path;
defaultText = "/var/lib/pterodactyl/archives";
description = "Directory where server archives for cross-node transferring will be stored.";
};
backup_directory = mkOption {
type = path;
defaultText = "/var/lib/pterodactyl/backups";
description = "Directory where local backups will be stored.";
};
username = mkOption {
type = str;
default = "pterodactyl";
description = ''
The user that should own all of the server files, and be used for containers.
NixOS will create this user and group if left at the default.
'';
};
sftp = {
bind_address = mkOption {
type = str;
default = "0.0.0.0";
example = "[::1]";
description = "The bind address of the SFTP server.";
};
bind_port = mkOption {
type = port;
default = 8080;
description = "The bind port of the SFTP server.";
};
read_only = mkOption {
type = bool;
default = false;
description = "If set to true, no write actions will be allowed on the SFTP server.";
};
};
};
docker = {
network = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we assert this with the options for docker/podman?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how to handle this with services that don't use the nixos options to manage their networks (like this one), and even then which nixos options would show the locally assigned options.

interface = mkOption {
type = str;
default = "172.18.0.1";
description = "The interface that should be used to create the network. Must not conflict with any other interfaces in use by Docker or on the system.";
};
dns = mkOption {
type = listOf str;
default = [ "1.1.1.1" "1.0.0.1" ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not default to the hosts DNS settings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to implement that, so I left it as the defaults of the configuration file. I thought of using networking.nameservers, but it's empty by default because most will probably use DHCP. Any way to evaluate the host's servers in the module?

description = "The DNS settings for containers.";
};
};
};
remote = mkOption {
type = str;
description = "The URL where the panel is running that this daemon should connect to, to collect data and send events.";
};
allowed_origins = mkOption {
type = listOf str;
default = [];
description = "AllowedOrigins is a list of allowed request origins. The Panel URL is automatically allowed, this is only needed for adding additional origins.";
};
};
};
};
};
config = lib.mkIf cfg.enable {
assertions = [ {
assertion = !cfg.settings.api.ssl.enabled || (cfg.settings.api.ssl.cert != null && cfg.settings.api.ssl.key != null);
message = "Certificate and key path must be set in config.services.pterodactyl-wings.settings.api.ssl to enable TLS on the API server";
} ];
warnings = lib.lists.optional (builtins.hasAttr "token" cfg.settings)
''
A token has been defined at services.wings.settings.token.
This exposes it in the world-readable nix store, and will be
overridden by services.wings.tokenFile regardless.
'';

virtualisation.docker.enable = true;

users = lib.optionalAttrs (cfg.settings.system.username == "pterodactyl") {
users.pterodactyl = {
isSystemUser = true;
group = "pterodactyl";
};
groups.pterodactyl = {};
};

# https://pterodactyl.io/wings/1.0/installing.html#daemonizing-using-systemd
systemd.services.pterodactyl-wings = let
cfgFile = settingsFormat.generate "wings.yaml" cfg.settings;
in {
requires = [ "docker.service" ];
after = [ "docker.service" ];
partOf = [ "docker.service" ];
description = "Pterodactyl Wings Daemon";
serviceConfig = {
WorkingDirectory = "/etc/pterodactyl";
PIDFile = "/run/wings/daemon.pid";
RuntimeDirectory = "wings";
RuntimeDirectoryMode = "0700";
ExecStartPre = [
"${pkgs.coreutils}/bin/cp ${cfgFile} \${RUNTIME_DIRECTORY}/wings.yaml"
"${pkgs.yq-go}/bin/yq -i '.token = (load_str(\"${cfg.tokenFile}\") | trim)' \${RUNTIME_DIRECTORY}/wings.yaml"
];
ExecStart = "${pkgs.pterodactyl-wings}/bin/wings --config \${RUNTIME_DIRECTORY}/wings.yaml";
Restart = "on-failure";
RestartSec = "5s";
};
startLimitIntervalSec = 30;
startLimitBurst = 5;
wantedBy = [ "multi-user.target" ];
};
};
}
26 changes: 26 additions & 0 deletions pkgs/by-name/pt/pterodactyl-wings/package.nix
@@ -0,0 +1,26 @@
{ lib
, fetchFromGitHub
, buildGoModule
}:
buildGoModule rec {
name = "pterodactyl-wings";
version = "1.11.8";

src = fetchFromGitHub {
owner = "pterodactyl";
repo = "wings";
rev = "v${version}";
hash = "sha256-JzbxROashDAL4NSeqMcWR9PgFqe9peBNpeofA347oE4=";
};

ldflags = [ "-s" "-w" "-X" "github.com/pterodactyl/wings/system.Version=${version}" ];
vendorHash = "sha256-fn+U91jX/rmL/gdMwRAIDEj/m0Zqgy81BUyv4El7Qxw=";

meta = with lib; {
description = "The server control plane for Pterodactyl Panel. Written from the ground-up with security, speed, and stability in mind.";
homepage = "https://github.com/pterodactyl/wings";
license = licenses.mit;
platforms = platforms.linux;
maintainers = with maintainers; [ melvyn2 ];
};
}