Compare commits

..

No commits in common. "master" and "0.2.0" have entirely different histories.

10 changed files with 568 additions and 948 deletions

1191
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "brownpaper"
version = "0.4.0"
version = "0.2.0"
authors = ["shimun <shimun@shimun.net>"]
edition = "2018"
@ -16,8 +16,8 @@ rand = "0.4.2"
byteorder = "1.3.2"
chrono = "0.4.9"
sequoia-openpgp = "0.12.0"
sequoia-openpgp = "0.9.0"
lazy_static = "1.4.0"
c2-chacha = "0.3.3"
c2-chacha = "0.2.2"
sha2 = "0.8.0"
hex = "0.3.2"

View File

@ -1,21 +1,9 @@
FROM rust:1.41.0 as builder
COPY . /bp
RUN apt update
RUN apt install clang nettle-dev -y
RUN cargo install --path /bp --root /usr
FROM debian:buster-slim
VOLUME /snips
EXPOSE 3000
COPY --from=builder /usr/bin/brownpaper /bin/
WORKDIR /
COPY bin/brownpaper /bin/
ENTRYPOINT [ "/bin/brownpaper" ]

9
bp.sh
View File

@ -1,9 +0,0 @@
#!/usr/bin/env bash
BP_ENDPOINT=${BROWNPAPER_ENDPOINT:-https://shimun.net/bp}
CURL_ARGS="--write-out %{url_effective}\\n --silent -o /dev/null"
GPG_ARGS="$([ ! -z "$BROWNPAPER_KEY" ] && echo "--local-user $BROWNPAPER_KEY")"
if [ ! -z "$1" ]; then
printf "brownpaper$1" | gpg --sign -a $GPG_ARGS | curl -s --data @- -X POST $BP_ENDPOINT/new -Ls $CURL_ARGS
else
(printf "brownpaper"; cat) | gpg --sign -a $GPG_ARGS | curl -s --data @- -X POST $BP_ENDPOINT/new -Ls $CURL_ARGS
fi

View File

@ -1,31 +0,0 @@
{ pkgs ? (import <nixpkgs> { })
, callPackage ? pkgs.callPackage
, stdenv ? pkgs.stdenv
, fetchgit ? pkgs.fetchgit
, name ? "brownpaper"
, src ? ./.
}:
let
filteredSrc = src; # flake only includeds files tracked by git builtins.filterSource pkgs.lib.cleanSourceFilter src;
crate2nix_tools = callPackage (pkgs.crate2nix.src + "/tools.nix") { };
overrides = with pkgs.llvmPackages_10;
pkgs.defaultCrateOverrides // rec {
nettle-sys = attrs: rec {
nativeBuildInputs = [ clang pkgs.pkg-config ];
buildInputs = [ pkgs.nettle ];
LIBCLANG_PATH = "${libclang.lib}/lib";
};
brownpaper = attrs: {
buildInputs = [ pkgs.gmp ];
};
};
client = with pkgs; runCommandLocal "brownpaper"
{
script = "${filteredSrc}/bp.sh";
nativeBuildInputs = [ makeWrapper ];
} ''
makeWrapper $script $out/bin/brownpaper \
--prefix PATH : ${with pkgs; lib.makeBinPath [ bash curl gnupg ]}
'';
in
{ server = (callPackage (crate2nix_tools.generatedCargoNix { inherit name; src = filteredSrc; }) { inherit pkgs; defaultCrateOverrides = overrides; }).rootCrate.build; inherit client; }

42
flake.lock generated
View File

@ -1,42 +0,0 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1642700792,
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1642847654,
"narHash": "sha256-0rwMCG/xGoVlV01qMZSQu/1GdhKNpWPWyu2Xr5CNhg4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7534942c6a009e8035d24c273b77a0b275db9bf1",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,64 +0,0 @@
{
description = "Brownpaper pastebin";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
inputs.naersk.url = "github:nix-community/naersk";
outputs = { self, nixpkgs, flake-utils, naersk }:
(flake-utils.lib.eachSystem [ "x86_64-linux" ]
(system:
let
pkgs = import nixpkgs { inherit system; };
naersk-lib = naersk.lib."${system}";
bp = pkgs.callPackage ./default.nix { inherit pkgs; src = ./.; };
in
rec {
apps = builtins.mapAttrs (_n: pkg: flake-utils.lib.mkApp { drv = pkg; }) packages;
defaultApp = apps.brownpaper;
packages = rec {
brownpaper = bp.client;
brownpaper-server = bp.server;
brownpaper-server-nk = naersk-lib.buildPackage (with pkgs; with llvmPackages_10; {
pname = "brownpaper";
root = self;
nativeBuildInputs = [ clang pkg-config ];
buildInputs = [ gmp nettle ];
LIBCLANG_PATH = "${libclang.lib}/lib";
});
brownpaper-server-docker = pkgs.dockerTools.buildLayeredImage {
name = "brownpaper";
config = {
Volume = "/snips";
Port = "3000";
Cmd = [ "${brownpaper-server}/bin/brownpaper" ];
};
};
};
defaultPackage = packages.brownpaper;
devShell = with pkgs; mkShell {
buildInputs = [ cargo rustfmt nettle llvmPackages_10.clang llvmPackages_10.libclang pkgconfig ];
LIBCLANG_PATH = "${llvmPackages_10.libclang}/lib";
};
hydraJobs.build = defaultPackage;
}
) // rec {
nixosModules.brownpaper = import ./mod.nix;
nixosModule = nixosModules.brownpaper;
}) // rec {
overlay = final: prev:
let
bp = final.callPackage ./default.nix { pkgs = final; src = self; };
in
{
brownpaper = bp.client;
brownpaper-server = bp.server;
};
overlays = {
pinned = final: prev: (overlay (import nixpkgs { inherit (final) system; }) { });
};
};
}

102
mod.nix
View File

@ -1,102 +0,0 @@
{ lib, pkgs, config, ... }:
with lib;
let
cfg = config.services.brownpaper;
cfgc = config.programs.brownpaper;
package = if pkgs ? brownpaper-server && pkgs ? brownpaper then { server = pkgs.brownpaper-server; client = pkgs.brownpaper; } else (pkgs.callPackage ./. { inherit pkgs; src = ./.; });
keyDir = pkgs.runCommand "brownpaper-keys" { } ''
mkdir -p $out
${concatStringsSep " && " (builtins.map (key: "cp ${key} $out/") cfg.pgpKeys)}
'';
in
{
options.services.brownpaper = {
enable = mkEnableOption "brownpaper service";
listen = mkOption {
type = types.str;
default = "127.0.0.1";
};
port = mkOption {
type = types.int;
default = 3000;
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/brownpaper";
};
user = mkOption {
type = types.str;
default = "brownpaper";
};
pgpKeys = mkOption {
type = with types; listOf path;
default = [ ];
};
gc = {
enable = mkEnableOption "delete old snippets";
dates = mkOption {
type = types.str;
default = "00:00";
description = ''
Specification (in the format described by
<citerefentry><refentrytitle>systemd.time</refentrytitle>
<manvolnum>7</manvolnum></citerefentry>) of the time at
which the garbage collector will run.
'';
};
maxAge = mkOption {
type = types.ints.positive;
default = 60 * 24 * 30;
description = "maximum age in minutes after which snippets will be garbage collected. Defaults to 30 days";
};
};
};
options.programs.brownpaper = {
enable = mkEnableOption "brownpaper client";
endpoint = mkOption {
type = types.str;
default = "http://${cfg.listen}:${toString cfg.port}";
};
};
config = {
users.users = mkIf (cfg.enable && cfg.user == "brownpaper") { ${cfg.user} = { isSystemUser = true; group = "brownpaper"; }; };
systemd.services = mkIf cfg.enable {
brownpaper-init.script = ''
mkdir -p '${cfg.dataDir}'
chown ${cfg.user} -R '${cfg.dataDir}'
'' + (optionalString (cfg.pgpKeys != [ ]) ''
DATADIR='${toString cfg.dataDir}'
([ ! -s "$DATADIR/keys" ] && [ -d "$DATADIR/keys" ]) && mv "$DATADIR/keys" "$DATADIR/keys.bak"
[ -s "$DATADIR/keys" ] && rm "$DATADIR/keys"
ln -s ${keyDir} "$DATADIR/keys"
'');
brownpaper = {
wantedBy = [ "multi-user.target" ];
wants = [ "brownpaper-init.service" ];
after = [ "brownpaper-init.service" "network-online.target" ];
path = [ pkgs.coreutils ];
environment.BROWNPAPER_STORAGE_DIR = "${toString cfg.dataDir}";
confinement = {
enable = true;
packages = with pkgs; [ bash coreutils findutils tzdata keyDir ];
};
script = ''
${package.server}/bin/brownpaper ${cfg.listen}:${toString cfg.port}
'';
serviceConfig = {
BindPaths = [ cfg.dataDir ] ++ (optional (cfg.pgpKeys != [ ]) keyDir);
User = cfg.user;
};
};
brownpaper-gc = mkIf cfg.gc.enable {
startAt = cfg.gc.dates;
script = "${pkgs.findutils}/bin/find ${cfg.dataDir} -maxdepth 1 -type f -mmin +${toString cfg.gc.maxAge} -delete";
};
};
environment.systemPackages = optionals cfgc.enable [
(pkgs.writeShellScriptBin "brownpaper" ''
BROWNPAPER_ENDPOINT='${cfgc.endpoint}' ${package.client}/bin/brownpaper "$@"
'')
];
};
}

View File

@ -21,20 +21,22 @@ use iron::url::Url;
use iron::mime::Mime;
use sha2::Digest;
use std::env::{self, args};
use std::io;
use std::io::prelude::*;
use std::iter::Iterator;
use std::net::SocketAddr;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
#[cfg(not(debug_assertions))]
const STORAGE_DIR: &str = "/snips";
#[cfg(debug_assertions)]
const STORAGE_DIR: &str = "/tmp";
lazy_static! {
static ref STORAGE_DIR: String =
env::var("BROWNPAPER_STORAGE_DIR").unwrap_or("/snips".to_string());
static ref KNOWN_KEYS: Arc<Mutex<KnownKeys>> = Arc::new(Mutex::new(
KnownKeys::load_dir([&*STORAGE_DIR, "keys"].join("/")).expect("Failed to load pubkeys")
KnownKeys::load_dir([STORAGE_DIR, "keys"].join("/")).expect("Failed to load pubkeys")
));
}
@ -42,7 +44,7 @@ const VERSION: &str = env!("CARGO_PKG_VERSION");
fn handle(req: &mut Request) -> IronResult<Response> {
println!("{}", req.url);
let storage = SnippetStorage::new(&Path::new(&*STORAGE_DIR));
let storage = SnippetStorage::new(&Path::new(STORAGE_DIR));
let segments: Vec<&str> = req.url.path();
match (&req.method, segments.first()) {
(Method::Get, Some(&"version")) => Ok(Response::with((iron::status::Ok, VERSION))),
@ -124,17 +126,6 @@ fn handle(req: &mut Request) -> IronResult<Response> {
fn main() {
let chain = Chain::new(handle);
println!("Starting brownpaper: {}", &*STORAGE_DIR);
Iron::new(chain).http(
args()
.skip(1)
.next()
.map(|ip| {
ip.parse::<SocketAddr>()
.expect("can't parse socket address")
})
.unwrap_or("0.0.0.0:3000".parse::<SocketAddr>().unwrap())
.to_string()
.as_str(),
);
println!("Starting brownpaper: {}", STORAGE_DIR);
Iron::new(chain).http("0.0.0.0:3000").unwrap();
}

View File

@ -37,32 +37,8 @@ impl KnownKeys {
pub fn verify(&mut self, r: impl Read) -> io::Result<Vec<u8>> {
let mut content = Vec::with_capacity(2048);
let helper = &*self;
let mut v = Verifier::<&KnownKeys>::from_reader(r, helper, None).map_err(|e| {
io::Error::new(io::ErrorKind::InvalidData, "Failed to verify signature")
})?;
let mut buf = [0u8; 512];
let bp = "brownpaper".as_bytes();
loop {
match v.read(&mut buf)? {
0 => break,
read => {
// first buffer read
if content.len() == 0 {
if !(buf.len() > bp.len() && bp == &buf[0..bp.len()]) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Failed to verify signature(prefix)",
));
} else {
// remove prefix
content.extend_from_slice(&buf[bp.len()..read])
}
} else {
content.extend_from_slice(&buf[0..read]);
}
}
}
}
let mut v = Verifier::<&KnownKeys>::from_reader(r, helper, None)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, "Failed to verify signature"))?;
if v.read_to_end(&mut content).is_err() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,