--- /dev/null
+{
+ "nodes": {
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1631561581,
+ "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1631570365,
+ "narHash": "sha256-vc6bfo0hijpicdUDiui2DvZXmpIP2iqOFZRcpMOuYPo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "df7113c0727881519248d4c7d080324e0ee3327b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
--- /dev/null
+{
+ inputs.flake-utils.url = "github:numtide/flake-utils";
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs";
+
+ description = "Pastebin-like service";
+
+ outputs = { self, flake-utils, nixpkgs }: flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = import nixpkgs { inherit system; overlays = []; };
+ in rec {
+ hydraJobs = checks;
+ checks = pkgs.lib.optionalAttrs (builtins.elem system pkgs.lib.systems.doubles.linux) {
+ test =
+ let testing = import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; };
+ in testing.makeTest {
+ nodes = {
+ server = { pkgs, ... }: {
+ imports = [ self.nixosModule ];
+ config = {
+ environment.systemPackages = [ pkgs.curl ];
+ services.httpd = {
+ enable = true;
+ adminAddr = "foo@example.org";
+ extraConfig = ''
+ ProxyPass / unix:///run/paste/gunicorn.sock|http://localhost/
+ ProxyPassReverse / unix:///run/paste/gunicorn.sock|http://localhost/
+ '';
+ };
+ services.paste.enable = true;
+ };
+ };
+ };
+ testScript = ''
+ start_all()
+ server.wait_for_unit("httpd.service")
+ server.wait_for_unit("paste.service")
+ server.wait_until_succeeds("[ -S /run/paste/gunicorn.sock ]", 10)
+ server.succeed("curl -f http://localhost/")
+ server.succeed("curl -f http://localhost/ | grep -q 'Get the source'")
+ '';
+ };
+ };
+ }) // rec {
+ nixosModule = { config, lib, pkgs, ... }:
+ let
+ cfg = config.services.paste;
+ in {
+ options = {
+ services.paste = {
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = "Whether to enable the pastebin service";
+ };
+
+ dataDir = lib.mkOption {
+ type = lib.types.path;
+ default = "/var/lib/paste";
+ description = ''
+ The directory where Paste stores its data.
+ '';
+ };
+
+ socketsDir = lib.mkOption {
+ type = lib.types.str;
+ default = "/run/paste";
+ description = "Socket which is used for communication with Paste.";
+ };
+
+ webDirectory = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ default = null;
+ description = "Subdirectory url to which the app will be served";
+ };
+
+ # Output variables
+ systemdStateDirectory = lib.mkOption {
+ type = lib.types.str;
+ # Use ReadWritePaths= instead if varDir is outside of /var/lib
+ default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir;
+ lib.strings.removePrefix "/var/lib/" cfg.dataDir;
+ description = ''
+ Adjusted paste data directory for systemd
+ '';
+ readOnly = true;
+ };
+ systemdRuntimeDirectory = lib.mkOption {
+ type = lib.types.str;
+ # Use ReadWritePaths= instead if socketsDir is outside of /run
+ default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir;
+ lib.strings.removePrefix "/run/" cfg.socketsDir;
+ description = ''
+ Adjusted paste sockets directory for systemd
+ '';
+ readOnly = true;
+ };
+ sockets = lib.mkOption {
+ type = lib.types.attrsOf lib.types.path;
+ default = {
+ pid = "${cfg.socketsDir}/gunicorn.pid";
+ gunicorn = "${cfg.socketsDir}/gunicorn.sock";
+ };
+ readOnly = true;
+ description = ''
+ Paste sockets
+ '';
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.services.paste = {
+ description = "Pastebin like service";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig = {
+ Environment = pkgs.lib.optionals (cfg.webDirectory != null) [ "SCRIPT_NAME=${cfg.webDirectory}" ];
+ Type = "simple";
+ User = config.services.httpd.user;
+ ExecStart = let
+ python = pkgs.python3.withPackages (p: [p.gunicorn p.flask p.pygments p.python_magic ]);
+ in
+ "${python}/bin/gunicorn -w4 -p ${cfg.sockets.pid} -e PASTE_DIRECTORY=${cfg.dataDir} --bind unix:${cfg.sockets.gunicorn} --chdir ${./paste} paste:app";
+ Restart = "always";
+ RestartSec = "5s";
+ PIDFile = cfg.sockets.pid;
+ RuntimeDirectory = cfg.systemdRuntimeDirectory;
+ StateDirectory = cfg.systemdStateDirectory;
+ StandardOutput = "journal";
+ StandardError = "inherit";
+ };
+
+ };
+ };
+ };
+ };
+}
--- /dev/null
+import os
+import secrets
+from flask import Flask, abort, request, Response, url_for
+from pygments.formatters.html import HtmlFormatter
+from pygments import highlight
+import pygments.lexers as lexers
+import base64
+import magic
+import mimetypes
+
+magic = magic.Magic(mime=True)
+
+config = {
+ "directory": os.environ["PASTE_DIRECTORY"],
+ "self_paste_id": "abcd123",
+ "max_content_length": 16 * 1000 * 1000
+ }
+
+app = Flask(__name__)
+app.config['MAX_CONTENT_LENGTH'] = config["max_content_length"]
+
+def file_location(paste_id):
+ if paste_id == config["self_paste_id"]:
+ return os.path.realpath(__file__)
+ else:
+ return os.path.join(config['directory'], paste_id + ".dat")
+
+def read_paste(paste_id):
+ file = file_location(paste_id)
+ if os.path.isfile(file):
+ content = open(file, "rb").read()
+ mime = magic.from_buffer(content)
+ if mime.startswith("text/x-script."):
+ mime="text/plain"
+ return (content, mime)
+ else:
+ abort(404)
+
+def generate_paste_id(n=3, attempts=0):
+ path = secrets.token_hex(n)[:n]
+ file = file_location(path)
+ if os.path.isfile(file):
+ attempts = attempts + 1
+ if attempts > 5:
+ n = n + 1
+ return generate_paste_id(n, attempts)
+ return path
+
+@app.route('/', methods=["GET"])
+def index():
+ return Response('''<pre>
+$ curl -X POST --data-binary @{self} {host}
+{paste}
+
+-> GET {paste}
+ guesses mimetype
+-> GET {paste}/raw
+ text/plain
+-> GET {paste}/bin
+ application/octet-stream
+-> GET {paste}/b64
+ base64 encoded
+-> GET {paste}/ub64
+ tries to decode base64
+-> GET {paste}/python
+ pretty-print python (replace with anything known by pygments)
+-> GET {paste}/guess
+ pretty-print (language guessed by pygments)
+-> GET {paste}/download
+ force download of file
+</pre>
+<a href="{paste}/py">Get the source</a>
+'''.format(host=url_for('post_paste', _external=True, _scheme="https"),
+ paste=url_for('get_paste', _external=True, _scheme="https", paste_id=config["self_paste_id"]),
+ self=os.path.basename(__file__)
+ ), mimetype="text/html")
+
+@app.route('/', methods=["POST"])
+def post_paste():
+ content = request.get_data()
+ paste_id = generate_paste_id()
+ with open(file_location(paste_id), "wb") as f:
+ f.write(content)
+ return url_for('get_paste', _external=True, _scheme="https", paste_id=paste_id) + "\n"
+
+
+@app.route('/<paste_id>', methods=["GET"])
+def get_paste(paste_id):
+ content, mime = read_paste(paste_id)
+ return Response(content, mimetype=mime)
+
+@app.route('/<paste_id>/<lang>', methods=["GET"])
+def get_paste_with(paste_id, lang):
+ content, mime = read_paste(paste_id)
+ if lang == "raw":
+ return Response(content, mimetype="text/plain")
+ elif lang == "bin":
+ return Response(content, mimetype="application/octet-stream")
+ elif lang == "b64":
+ return Response(base64.encodebytes(content), mimetype="text/plain")
+ elif lang == "download":
+ extension = mimetypes.guess_extension(mime, strict=False)
+ if extension is None:
+ cd = "attachment"
+ else:
+ cd = 'attachment; filename="{}{}"'.format(paste_id, extension)
+ return Response(content, mimetype=mime,
+ headers={"Content-Disposition": cd})
+ elif lang == "ub64":
+ try:
+ return base64.b64decode(content)
+ except:
+ abort(400)
+ try:
+ if lang == "guess":
+ lexer = lexers.guess_lexer(content)
+ else:
+ lexer = lexers.find_lexer_class_by_name(lang)()
+ except:
+ abort(400)
+ fmter = HtmlFormatter(
+ noclasses=True, full=True, style="colorful", linenos="table")
+
+ return Response(highlight(content, lexer, fmter), mimetype="text/html")
--- /dev/null
+{
+ "nodes": {
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1631561581,
+ "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nix-lib": {
+ "locked": {
+ "lastModified": 1631655525,
+ "narHash": "sha256-8U7zAdbjNItXo6eqI/rhtOa3LUPGD6yE9PTZQkrSGHo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "cf0caf529c33c140863ebfa43691f7b69fe2233c",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1631570365,
+ "narHash": "sha256-vc6bfo0hijpicdUDiui2DvZXmpIP2iqOFZRcpMOuYPo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "df7113c0727881519248d4c7d080324e0ee3327b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "paste": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "narHash": "sha256-oSabBrUGIkY8lKktXlIM4uYSVYI54wKnIjjVZwMOd70=",
+ "path": "../../paste",
+ "type": "path"
+ },
+ "original": {
+ "path": "../../paste",
+ "type": "path"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nix-lib": "nix-lib",
+ "paste": "paste"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
--- /dev/null
+{
+ inputs.paste = {
+ path = "../../paste";
+ type = "path";
+ };
+ inputs.nix-lib.url = "github:NixOS/nixpkgs";
+
+ description = "Private configuration for paste";
+ outputs = { self, nix-lib, paste }:
+ let
+ cfg = name': { config, lib, pkgs, name, ... }: {
+ config = lib.mkIf (name == name') {
+ services.paste = {
+ enable = true;
+ webDirectory = "/paste";
+ };
+ };
+ };
+ in
+ paste.outputs //
+ { nixosModules = paste.nixosModules or {} // nix-lib.lib.genAttrs ["eldiron"] cfg; };
+}
+
mediagoblin = ./webapps/mediagoblin.nix;
peertube = (flakeCompat ../flakes/peertube).nixosModule;
fiche = ./webapps/fiche.nix;
+ paste = (flakeCompat ../flakes/paste).nixosModule;
opendmarc = (flakeCompat ../flakes/opendmarc).nixosModule;
openarc = (flakeCompat ../flakes/openarc).nixosModule;
{ lib, pkgs, config, ... }:
let
+ flakeCompat = import ../../../../../lib/flake-compat.nix;
+
adminer = pkgs.callPackage ./adminer.nix {
inherit (pkgs.webapps) adminer;
};
cfg = config.myServices.websites.tools.tools;
pcfg = config.services.phpfpm.pools;
in {
+ imports =
+ builtins.attrValues (flakeCompat ../../../../../flakes/private/paste).nixosModules;
+
options.myServices.websites.tools.tools = {
enable = lib.mkEnableOption "enable tools website";
};
(phpbb.apache.vhostConf pcfg.phpbb.socket)
(dmarc-reports.apache.vhostConf pcfg.dmarc-reports.socket)
''
- Alias /paste /var/lib/fiche
- <Directory "/var/lib/fiche">
- DirectoryIndex index.txt index.html
- AllowOverride None
- Require all granted
- Options -Indexes
- </Directory>
+ <Location "/paste/">
+ ProxyPass unix://${config.services.paste.sockets.gunicorn}|http://tools.immae.eu/paste/
+ ProxyPassReverse unix://${config.services.paste.sockets.gunicorn}|http://tools.immae.eu/paste/
+ ProxyPreserveHost on
+ </Location>
+ <Location "/paste">
+ ProxyPass unix://${config.services.paste.sockets.gunicorn}|http://tools.immae.eu/paste/
+ ProxyPassReverse unix://${config.services.paste.sockets.gunicorn}|http://tools.immae.eu/paste/
+ ProxyPreserveHost on
+ </Location>
Alias /BIP39 /var/lib/buildbot/outputs/immae/bip39
<Directory "/var/lib/buildbot/outputs/immae/bip39">
paths = [ "/var/secrets/webapps/tools-wallabag" ];
};
- services.fiche = {
- enable = true;
- port = config.myEnv.ports.fiche;
- domain = "tools.immae.eu/paste";
- https = true;
- };
};
}
ProxyPass ws://${config.webPort}/ws
</Location>
<Location "/mpd/music.mp3">
- ProxyPass unix:///run/mpd/mp3.sock|http://tools.immae.eu/
- ProxyPassReverse unix:///run/mpd/mp3.sock|http://tools.immae.eu/
+ ProxyPass unix:///run/mpd/mp3.sock|http://tools.immae.eu/mpd/mp3
+ ProxyPassReverse unix:///run/mpd/mp3.sock|http://tools.immae.eu/mpd/mp3
</Location>
<Location "/mpd/music.ogg">
- ProxyPass unix:///run/mpd/ogg.sock|http://tools.immae.eu/
- ProxyPassReverse unix:///run/mpd/ogg.sock|http://tools.immae.eu/
+ ProxyPass unix:///run/mpd/ogg.sock|http://tools.immae.eu/mpd/ogg
+ ProxyPassReverse unix:///run/mpd/ogg.sock|http://tools.immae.eu/mpd/ogg
</Location>
'';
};