{ 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; ''; }; }; }; }; }; }; }; }