300 lines
10 KiB
Nix
300 lines
10 KiB
Nix
{
|
|
description = "A minimal pastebin which also accepts binary files like Images, PDFs and ships multiple clients";
|
|
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
flake-utils.url = "github:numtide/flake-utils";
|
|
rust-overlay = {
|
|
url = "github:oxalica/rust-overlay";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
};
|
|
|
|
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
|
|
flake-utils.lib.eachDefaultSystem (system:
|
|
let
|
|
overlays = [ (import rust-overlay) ];
|
|
pkgs = import nixpkgs {
|
|
inherit system overlays;
|
|
};
|
|
|
|
rustToolchain = pkgs.rust-bin.stable.latest.default.override {
|
|
extensions = [ "rust-src" ];
|
|
targets = [ "x86_64-unknown-linux-musl" "aarch64-unknown-linux-musl" ];
|
|
};
|
|
|
|
in
|
|
{
|
|
packages = {
|
|
default = self.packages.${system}.bin;
|
|
|
|
bin = pkgs.rustPlatform.buildRustPackage rec {
|
|
pname = "bin";
|
|
version = "2.4.0";
|
|
|
|
src = pkgs.fetchFromGitHub {
|
|
owner = "wantguns";
|
|
repo = "bin";
|
|
rev = "v${version}";
|
|
hash = "sha256-gUTDe9LPJ0Y2JLnbLv2FQaUjeNj6a1LQ3V5Z9SVnxkc=";
|
|
};
|
|
|
|
cargoHash = "sha256-2SiHrc0L9YiL2vLzKPnJ4BREPyeODJlLNReJPPpKlAE=";
|
|
|
|
nativeBuildInputs = with pkgs; [
|
|
pkg-config
|
|
];
|
|
|
|
buildInputs = with pkgs; [
|
|
openssl
|
|
] ++ lib.optionals stdenv.isDarwin [
|
|
darwin.apple_sdk.frameworks.Security
|
|
];
|
|
|
|
# Enable optimizations
|
|
CARGO_BUILD_INCREMENTAL = "false";
|
|
RUST_BACKTRACE = "full";
|
|
|
|
meta = with pkgs.lib; {
|
|
description = "A minimal pastebin which also accepts binary files";
|
|
homepage = "https://github.com/wantguns/bin";
|
|
license = licenses.lgpl3Only;
|
|
maintainers = [];
|
|
platforms = platforms.all;
|
|
};
|
|
};
|
|
|
|
docker = pkgs.dockerTools.buildImage {
|
|
name = "bin";
|
|
tag = "latest";
|
|
created = "now";
|
|
contents = [ self.packages.${system}.bin pkgs.cacert ];
|
|
config = {
|
|
Cmd = [ "${self.packages.${system}.bin}/bin/bin" ];
|
|
ExposedPorts = {
|
|
"6162/tcp" = {};
|
|
};
|
|
Env = [
|
|
"BIN_ADDRESS=0.0.0.0"
|
|
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
|
];
|
|
WorkingDir = "/";
|
|
Volumes = {
|
|
"/upload" = {};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
devShells.default = pkgs.mkShell {
|
|
buildInputs = with pkgs; [
|
|
rustToolchain
|
|
pkg-config
|
|
openssl
|
|
cargo-watch
|
|
rust-analyzer
|
|
clippy
|
|
rustfmt
|
|
];
|
|
|
|
RUST_BACKTRACE = 1;
|
|
};
|
|
}) // {
|
|
nixosModules = {
|
|
default = self.nixosModules.bin;
|
|
|
|
bin = { config, lib, pkgs, ... }:
|
|
with lib;
|
|
let
|
|
cfg = config.services.bin;
|
|
in
|
|
{
|
|
options.services.bin = {
|
|
enable = mkEnableOption "bin pastebin service";
|
|
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = self.packages.${pkgs.system}.bin;
|
|
defaultText = literalExpression "pkgs.bin";
|
|
description = "The bin package to use";
|
|
};
|
|
|
|
user = mkOption {
|
|
type = types.str;
|
|
default = "bin";
|
|
description = "User under which bin runs";
|
|
};
|
|
|
|
group = mkOption {
|
|
type = types.str;
|
|
default = "bin";
|
|
description = "Group under which bin runs";
|
|
};
|
|
|
|
address = mkOption {
|
|
type = types.str;
|
|
default = "127.0.0.1";
|
|
description = "Address on which the webserver runs";
|
|
};
|
|
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 6162;
|
|
description = "Port on which the webserver runs";
|
|
};
|
|
|
|
uploadDir = mkOption {
|
|
type = types.path;
|
|
default = "/var/lib/bin/upload";
|
|
description = "Path to the uploads folder";
|
|
};
|
|
|
|
binaryUploadLimit = mkOption {
|
|
type = types.int;
|
|
default = 100;
|
|
description = "Binary uploads file size limit (in MiB)";
|
|
};
|
|
|
|
clientDescription = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Include client description on landing page";
|
|
};
|
|
|
|
extraArgs = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "Extra command-line arguments to pass to bin";
|
|
};
|
|
|
|
environmentFile = mkOption {
|
|
type = types.nullOr types.path;
|
|
default = null;
|
|
description = ''
|
|
File containing environment variables for bin.
|
|
Useful for setting Rocket-specific configurations.
|
|
'';
|
|
example = literalExpression ''
|
|
pkgs.writeText "bin-env" '''
|
|
BIN_LIMITS={form="16 MiB"}
|
|
BIN_WORKERS=8
|
|
BIN_IDENT=false
|
|
'''
|
|
'';
|
|
};
|
|
|
|
openFirewall = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to open the firewall for the bin port";
|
|
};
|
|
|
|
nginx = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to enable nginx reverse proxy for bin";
|
|
};
|
|
|
|
domain = mkOption {
|
|
type = types.str;
|
|
default = "paste.example.com";
|
|
description = "Domain name for the bin service";
|
|
};
|
|
|
|
enableSSL = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Whether to enable SSL (uses ACME/Let's Encrypt)";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
systemd.services.bin = {
|
|
description = "bin pastebin service";
|
|
after = [ "network.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
ExecStart = ''
|
|
${cfg.package}/bin/bin \
|
|
--address ${cfg.address} \
|
|
--port ${toString cfg.port} \
|
|
--upload ${cfg.uploadDir} \
|
|
--binary-upload-limit ${toString cfg.binaryUploadLimit} \
|
|
${optionalString cfg.clientDescription "--client-desc"} \
|
|
${concatStringsSep " " cfg.extraArgs}
|
|
'';
|
|
Restart = "on-failure";
|
|
RestartSec = 5;
|
|
StateDirectory = "bin";
|
|
WorkingDirectory = "/var/lib/bin";
|
|
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
|
|
|
|
# Security hardening
|
|
NoNewPrivileges = true;
|
|
PrivateTmp = true;
|
|
ProtectSystem = "strict";
|
|
ProtectHome = true;
|
|
ReadWritePaths = [ cfg.uploadDir ];
|
|
ProtectKernelTunables = true;
|
|
ProtectKernelModules = true;
|
|
ProtectControlGroups = true;
|
|
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
|
LockPersonality = true;
|
|
RestrictRealtime = true;
|
|
SystemCallFilter = "@system-service";
|
|
SystemCallErrorNumber = "EPERM";
|
|
PrivateDevices = true;
|
|
RestrictNamespaces = true;
|
|
RestrictSUIDSGID = true;
|
|
RemoveIPC = true;
|
|
};
|
|
};
|
|
|
|
users.users = mkIf (cfg.user == "bin") {
|
|
bin = {
|
|
isSystemUser = true;
|
|
group = cfg.group;
|
|
home = "/var/lib/bin";
|
|
createHome = true;
|
|
};
|
|
};
|
|
|
|
users.groups = mkIf (cfg.group == "bin") {
|
|
bin = {};
|
|
};
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d ${cfg.uploadDir} 0755 ${cfg.user} ${cfg.group} -"
|
|
];
|
|
|
|
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
|
|
|
|
services.nginx = mkIf cfg.nginx.enable {
|
|
enable = true;
|
|
virtualHosts.${cfg.nginx.domain} = {
|
|
forceSSL = cfg.nginx.enableSSL;
|
|
enableACME = cfg.nginx.enableSSL;
|
|
locations."/" = {
|
|
proxyPass = "http://${cfg.address}:${toString cfg.port}";
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
client_max_body_size ${toString cfg.binaryUploadLimit}M;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|