Custom error pages for nginx

This commit is contained in:
Thorsten Schubert 2024-08-18 16:53:39 +02:00
parent 598ab36b44
commit 5a992dde48
Signed by: Thorsten Schubert
GPG key ID: F74F74BB63C458FD
12 changed files with 202 additions and 77 deletions
.forgejo/workflows
Cargo.lock
contrib/test-data
oci
Containerfile.httpdbuild.sh
rootfs/http
etc/nginx
usr/share/nginx/error_pages
tcpaste/src
admin
daemon/erased

View file

@ -5,6 +5,8 @@ on:
- "**/*.sh"
- "**/*.sh.in"
- "**/*.yml"
- "contrib/**"
- "oci/**"
env:
CARGO_TERM_COLOR: always

32
Cargo.lock generated
View file

@ -271,9 +271,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "blake3"
version = "1.5.3"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9ec96fe9a81b5e365f9db71fe00edc4fe4ca2cc7dcb7861f0603012a7caa210"
checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
dependencies = [
"arrayref",
"arrayvec",
@ -354,7 +354,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_json",
"syn 2.0.74",
"syn 2.0.75",
"zstd",
]
@ -372,9 +372,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "camino"
version = "1.1.8"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3054fea8a20d8ff3968d5b22cc27501d2b08dc4decdb31b184323f00c5ef23bb"
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
dependencies = [
"serde",
]
@ -463,7 +463,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.74",
"syn 2.0.75",
]
[[package]]
@ -534,7 +534,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.74",
"syn 2.0.75",
]
[[package]]
@ -909,9 +909,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.156"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libgit2-sys"
@ -1311,7 +1311,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
"syn 2.0.75",
]
[[package]]
@ -1374,9 +1374,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.74"
version = "2.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
dependencies = [
"proc-macro2",
"quote",
@ -1438,7 +1438,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
"syn 2.0.75",
]
[[package]]
@ -1556,7 +1556,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.74",
"syn 2.0.75",
"wasm-bindgen-shared",
]
@ -1590,7 +1590,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
"syn 2.0.75",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1825,7 +1825,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
"syn 2.0.75",
]
[[package]]

View file

@ -63,7 +63,7 @@ RUN apk add --no-cache --virtual .build-deps shadow \
&& chmod -R g+w /var/cache/nginx /etc/nginx /var/log/nginx
COPY --chown=nginx:root oci/rootfs/http/etc/nginx/nginx.conf /etc/nginx/nginx.conf
COPY --chown=nginx:root oci/rootfs/http/usr/share/nginx/html/* /usr/share/nginx/html
COPY --chown=nginx:root oci/rootfs/http/usr/share/nginx /usr/share/nginx
USER $PUID

View file

@ -65,6 +65,7 @@ if [[ $1 == "--tag-all" ]]; then
fi
build_args=(
'--pull=always'
--file="${project_root}/oci/Containerfile.${BUILD_TYPE}"
--label=org.opencontainers.image.title="tcpasters-${BUILD_TYPE}"
--label=org.opencontainers.image.authors="Thorsten Schubert <tschubert@bafh.org>"

View file

@ -99,6 +99,9 @@ http {
add_header Expect-CT "enforce, max-age=63072000" always;
add_header X-XSS-Protection "1; mode=block" always;
error_page 403 /403.html;
error_page 404 /404.html;
if ($http_user_agent ~* (google|archive|bing|yahoo|yandex|teoma|trident|baidu) ) {
return 403;
}
@ -108,65 +111,70 @@ http {
# $request_uri is url encoded
if ($uri ~* "^\/([\w\d\-_.]{1,16}\.?)\/([\w\d\.]{1,16})(?:\/([^\/&?#]+))?\/?$)" {
set $file_fragment $1;
set $path_fragment $2;
set $dl_fragment $3;
rewrite "^/([^\/]*)/([^\/]{1,16})(?:/.*)?$" /$1 last;
}
set $file_fragment $1;
set $path_fragment $2;
set $dl_fragment $3;
rewrite "^/([^\/]*)/([^\/]{1,16})(?:/.*)?$" /$1 last;
}
location = /robots.txt {
add_header Content-Type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
location ~ "^/40(3|4).html$" {
internal;
root /usr/share/nginx/error_pages;
add_header Content-Security-Policy "default-src 'none'; style-src-elem 'unsafe-inline'; img-src 'self'; frame-ancestors 'none'; block-all-mixed-content" always;
}
}
location = /robots.txt {
add_header Content-Type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
}
location = / {}
location = / {}
location = /index.html {}
location = /index.html {}
location ~ ^/.var/.*$ {
return 403;
}
location ~ ^/.var/.*$ {
return 403;
}
location / {
root /var/lib/pasted;
location / {
root /var/lib/pasted;
etag off;
expires 1h;
disable_symlinks on;
autoindex off;
etag off;
expires 1h;
disable_symlinks on;
autoindex off;
default_type 'text/plain';
default_type 'text/plain';
include /etc/nginx/mime.types;
types {
text/plain md markdown;
text/plain css js xml;
text/plain sh bash zsh fish;
text/plain awk sed;
text/plain yaml yml;
text/plain pl py lua rb;
text/plain nim hs;
text/plain java kt;
text/plain c cpp cxx h hpp hxx;
text/plain go mod sum;
text/plain patch txt;
}
include /etc/nginx/mime.types;
types {
text/plain md markdown;
text/plain css js xml;
text/plain sh bash zsh fish;
text/plain awk sed;
text/plain yaml yml;
text/plain pl py lua rb;
text/plain nim hs;
text/plain java kt;
text/plain c cpp cxx h hpp hxx;
text/plain go mod sum;
text/plain patch txt;
}
set $mtype $map_mime;
if ($mtype) {
more_set_headers "Content-Type: $mtype";
}
set $mtype $map_mime;
if ($mtype) {
more_set_headers "Content-Type: $mtype";
}
if ($path_fragment ~ '^(bin|dat)$') {
add_header Content-Disposition "attachment; filename=$file_fragment";
}
if ($path_fragment ~ '^(bin|dat)$') {
add_header Content-Disposition "attachment; filename=$file_fragment";
}
if ($map_dl_fragment) {
more_set_headers "Content-Type: application/octet-stream";
add_header Content-Disposition "attachment; filename=$file_name";
}
}
if ($map_dl_fragment) {
more_set_headers "Content-Type: application/octet-stream";
add_header Content-Disposition "attachment; filename=$file_name";
}
}
}
}

View file

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>403 - Forbidden</title>
<style>
body {
font-family: sans-serif;
background-color: #f0f0f0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: center;
background-color: white;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
font-size: 4rem;
color: #d9534f;
margin-bottom: 0;
}
p {
font-size: 1.2rem;
color: #666;
}
a {
color: #007bff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h1>403</h1>
<p>Access Forbidden</p>
<p>You don't have permission to access this resource.</p>
<p><a href="/">Return to homepage</a></p>
</div>
</body>
</html>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Paste Not Found</title>
<style>
body {
font-family: sans-serif;
background-color: #f0f0f0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: center;
background-color: white;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
font-size: 4rem;
color: #333;
margin-bottom: 0;
}
p {
font-size: 1.2rem;
color: #666;
}
a {
color: #007bff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<p>Oops! The paste you're looking for doesn't exist.</p>
<p>Let's get you back on track. <a href="/">Return to homepage</a></p>
</div>
</body>
</html>

View file

@ -6,7 +6,6 @@
use std::{
collections::HashSet,
ffi::OsStr,
fs::{self, DirEntry},
time::{Duration, SystemTime},
};
@ -32,10 +31,14 @@ fn main() {
.duration_since(SystemTime::UNIX_EPOCH)
.expect("System time anomaly");
let excluded: HashSet<&OsStr> = cfg.exclude.iter().map(AsRef::as_ref).collect();
let excluded = cfg
.exclude
.iter()
.map(AsRef::as_ref)
.collect::<HashSet<_>>();
let mut do_expire = |dir: &DirEntry| {
let path = dir.path();
let mut do_expire = |entry: &DirEntry| {
let path = entry.path();
let file = path.display();
let mut deleted = false;
@ -44,9 +47,9 @@ fn main() {
return Ok(deleted);
}
match xattr::get(&path, &cfg.xattr_sunset) {
match &xattr::get(&path, &cfg.xattr_sunset) {
Ok(Some(x)) => {
let timestamp = String::from_utf8_lossy(&x);
let timestamp = String::from_utf8_lossy(x);
let epoch = match timestamp.parse::<u64>() {
Err(ref e) => {
warn!("{file}: Unable to parse xattr value for: {e}, skipping");
@ -66,7 +69,7 @@ fn main() {
}
}
}
Err(ref e) => {
Err(e) => {
warn!("Error: {file} - {e}");
}
Ok(None) => {

View file

@ -17,7 +17,7 @@ use odht::{Config, HashTableOwned as HTO};
use tcpaste::{
admin::filter::{
config::{Admin, Commands},
csum::{Blake3, FileSum, PREFIX_LEN},
csum::{Blake3, FileSum, LOAD_FACTOR, PREFIX_LEN},
hex,
},
DynResult,
@ -36,7 +36,7 @@ fn main() -> DynResult<()> {
let serialized = fs::read(db)?;
HTO::<Blake3>::from_raw_bytes(&serialized[..])?
} else {
HTO::<Blake3>::with_capacity(20, 95)
HTO::<Blake3>::with_capacity(20, LOAD_FACTOR)
};
let mut modified = Modified::No;
@ -46,11 +46,13 @@ fn main() -> DynResult<()> {
add(&mut table, buf);
modified = Modified::Yes;
}
Commands::Del { hashes } => {
let buf = hashes.iter().filter_map(hex::string_to_byte_array);
table = del(&table, buf);
modified = Modified::Yes;
}
Commands::File { hashlist } => {
if let Some(path) = hashlist {
let file = BufReader::new(File::open(path)?);
@ -69,6 +71,7 @@ fn main() -> DynResult<()> {
modified = Modified::Yes;
}
}
Commands::Sample { samples } => {
let hashes: Vec<_> = samples
.iter()
@ -85,14 +88,17 @@ fn main() -> DynResult<()> {
modified = Modified::Yes;
}
}
Commands::List => {
table.iter().for_each(|(buf, _)| {
println!("{}", hex::byte_array_to_string(&buf));
});
}
Commands::Entries => {
println!("{}", table.len());
}
Commands::Query { hash } => {
// At this point hash is already verified to have the proper format by clap,
// but check anyways.
@ -154,5 +160,8 @@ where
}
// Return a new HashTable without the items
HTO::<Blake3>::from_iterator(table.iter().filter(|(k, _)| !lookup.contains(&k[..])), 95)
HTO::<Blake3>::from_iterator(
table.iter().filter(|(k, _)| !lookup.contains(&k[..])),
LOAD_FACTOR,
)
}

View file

@ -22,6 +22,7 @@ use odht::{Config, UnHashFn};
use crate::str_from_hex_bytes;
pub const PREFIX_LEN: usize = BLOCK_LEN / 2;
pub const LOAD_FACTOR: u8 = 75;
#[derive(PartialEq, Eq, Hash)]
pub struct Blake3;
@ -217,8 +218,10 @@ mod tests {
macro_rules! odht_serialize {
($serialized:ident, $hashes:expr, $value:expr) => {
let count = $hashes.len();
let mut builder =
HashTableOwned::<Blake3>::with_capacity((count as f32 * 1.25) as usize, 95);
let mut builder = HashTableOwned::<Blake3>::with_capacity(
(count as f32 * 1.25) as usize,
LOAD_FACTOR,
);
for hash in $hashes {
builder.insert(

View file

@ -319,7 +319,7 @@ async fn handle_connection(cfg: &'static ServerConfig, mut client: Connection) {
#[must_use]
pub fn json_response(status: bool, msg: &str, id: Option<&str>) -> Vec<u8> {
let status = if status { "success" } else { "false" };
let status = if status { "success" } else { "failure" };
let json = id.map_or_else(
|| {
json!({