security: remove config.json from tracking, add example configs and nginx guard
- config.json now in .gitignore — it contained admin password hash and secret_key placeholder which should never be in version control - Add config.example.json as a safe, secrets-free template for new deployments - Add nginx.conf.example with proper reverse-proxy guards: - Static files served by Nginx directly (not Flask) - Explicit deny on .py, .json, .db, .log, .env and other sensitive extensions - Block dot-files and __pycache__ paths - No directory listing (autoindex off) - HTTPS enforcement with HSTS
This commit is contained in:
parent
24d70aeb08
commit
9aeed2af50
|
|
@ -60,5 +60,9 @@ Thumbs.db
|
|||
*.tmp
|
||||
*.temp
|
||||
|
||||
# User uploads (if you add file upload functionality)
|
||||
uploads/gunicorn.pid
|
||||
# Gunicorn
|
||||
gunicorn.pid
|
||||
|
||||
# Runtime config — contains secrets (SECRET_KEY, admin password hash).
|
||||
# Copy config.example.json → config.json and fill in your values.
|
||||
config.json
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"_comment": "Bastebin configuration. Restart the server after changing values.",
|
||||
"_comment": "Bastebin configuration. Copy this file to config.json, fill in your values, and restart the server.",
|
||||
|
||||
"site": {
|
||||
"name": "Bastebin",
|
||||
|
|
@ -13,11 +13,13 @@
|
|||
"host": "0.0.0.0",
|
||||
"port": 5500,
|
||||
"debug": false,
|
||||
"secret_key": "ce81867ddcc16729b42d7ae0564140e6bcc83bcf5dbbe77b7e5b6c5aa4199347"
|
||||
"use_https": true,
|
||||
"_secret_key_note": "Prefer the SECRET_KEY environment variable. This fallback is used only if the env var is not set.",
|
||||
"secret_key": "CHANGE-ME-use-a-long-random-string-at-least-32-chars"
|
||||
},
|
||||
|
||||
"database": {
|
||||
"path": "pastebin.db"
|
||||
"path": "bastebin.db"
|
||||
},
|
||||
|
||||
"pastes": {
|
||||
|
|
@ -133,9 +135,12 @@
|
|||
{"value": "toml", "name": "TOML"},
|
||||
{"value": "ini", "name": "INI / Config"}
|
||||
],
|
||||
|
||||
"admin": {
|
||||
"enabled": true,
|
||||
"user": "admin",
|
||||
"pass": "scrypt:32768:8:1$WKz9I6qE4hh0paUQ$6fd34e7f0195280f81301a92f5bac26d247f95d64744cb2c6e44108a3d8420eba5343b7b2ba657f39404f4ef102ce2e62a689e7797a43f3169fd69dca7b5b3c7"
|
||||
"_user_note": "Set a strong username — not 'admin'.",
|
||||
"user": "your-admin-username",
|
||||
"_pass_note": "Generate with: python generate_hash.py yourpassword",
|
||||
"pass": "REPLACE-WITH-SCRYPT-HASH-FROM-generate_hash.py"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
# ── Bastebin — Nginx reverse-proxy configuration ─────────────────────────────
|
||||
#
|
||||
# Copy to /etc/nginx/sites-available/bastebin and symlink:
|
||||
# sudo ln -s /etc/nginx/sites-available/bastebin /etc/nginx/sites-enabled/
|
||||
# sudo nginx -t && sudo systemctl reload nginx
|
||||
#
|
||||
# Assumes:
|
||||
# - Gunicorn is bound to a Unix socket at /run/bastebin/gunicorn.sock
|
||||
# - Your TLS certificates are managed by Certbot / Let's Encrypt
|
||||
# - Static files live at /path/to/bastebin/static/
|
||||
#
|
||||
# Replace every occurrence of:
|
||||
# bastebin.com → your actual domain
|
||||
# /path/to/bastebin → absolute path to your project directory
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name bastebin.com www.bastebin.com;
|
||||
|
||||
# Redirect all HTTP → HTTPS
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name bastebin.com www.bastebin.com;
|
||||
|
||||
# ── TLS (managed by Certbot — do not edit manually) ──────────────────────
|
||||
ssl_certificate /etc/letsencrypt/live/bastebin.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/bastebin.com/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
# ── Security headers (supplement Flask's own headers) ────────────────────
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
|
||||
# ── Block access to sensitive paths ──────────────────────────────────────
|
||||
# These should NEVER be served, even accidentally.
|
||||
location ~* \.(py|json|db|sqlite|sqlite3|log|cfg|ini|toml|env|sh|conf|bak|orig)$ {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
# Block dot-files / hidden files (e.g. .env, .git)
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
# Block __pycache__ and similar Python internals
|
||||
location ~* /__pycache__/ {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
# ── Static files served directly by Nginx (fast path) ───────────────────
|
||||
# Nginx serves these without hitting Gunicorn at all.
|
||||
location /static/ {
|
||||
alias /path/to/bastebin/static/;
|
||||
|
||||
# No directory listings
|
||||
autoindex off;
|
||||
|
||||
# Cache busting — serve with long expiry; filename includes hash in prod
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, max-age=604800, immutable";
|
||||
|
||||
# Only allow known file extensions; block anything else
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
# Passthrough — let the alias serve it
|
||||
}
|
||||
location / {
|
||||
# Anything under /static/ that doesn't match above is denied
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
# ── Gunicorn (Flask application) ─────────────────────────────────────────
|
||||
location / {
|
||||
proxy_pass http://unix:/run/bastebin/gunicorn.sock;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
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;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_read_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
|
||||
# Body size limit — match config.json max_size_bytes (2 MB) + overhead
|
||||
client_max_body_size 3m;
|
||||
}
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────────
|
||||
access_log /var/log/nginx/bastebin.access.log;
|
||||
error_log /var/log/nginx/bastebin.error.log warn;
|
||||
}
|
||||
Loading…
Reference in New Issue