feat: implement basic auth using nginx and bcrypt password hashing

This commit is contained in:
Greg 2025-05-27 00:39:03 +02:00
parent bbd9c44259
commit b43cd62ca9
6 changed files with 100 additions and 36 deletions

View File

@ -31,10 +31,11 @@ COPY data-api.js /usr/share/nginx/api/
COPY backup-s3.js /usr/share/nginx/api/ COPY backup-s3.js /usr/share/nginx/api/
COPY auth-middleware.js /usr/share/nginx/api/ COPY auth-middleware.js /usr/share/nginx/api/
COPY login.html /usr/share/nginx/api/ COPY login.html /usr/share/nginx/api/
COPY inject-password-hash.js /usr/share/nginx/api/ COPY generate-htpasswd.js /usr/share/nginx/api/
# Copy a custom Nginx configuration that includes the data API proxy # Copy a custom Nginx configuration that includes the data API proxy
COPY nginx.conf /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY nginx-auth.conf /etc/nginx/auth.conf
# Copy supervisor configuration # Copy supervisor configuration
COPY supervisord.conf /etc/supervisord.conf COPY supervisord.conf /etc/supervisord.conf

33
generate-htpasswd.js Normal file
View File

@ -0,0 +1,33 @@
/**
* Generate .htpasswd file for Nginx basic authentication
*
* This script creates a .htpasswd file from the bcrypt hash provided in the
* PASSWORD_HASH environment variable.
*/
const fs = require('fs');
const path = require('path');
// Default username
const USERNAME = 'user';
// Get password hash from environment variable
const passwordHash = process.env.PASSWORD_HASH || '$2a$10$EgxHKjDDFcZKtQY9hl/N4.QvEQHCXVnQXw9dzFYlUDVKOcLMGp9eq';
// Format for .htpasswd: username:$2y$...hash...
// Note: Nginx requires $2y$ format instead of bcrypt's $2a$ format
const htpasswdContent = `${USERNAME}:${passwordHash.replace('$2a$', '$2y$')}`;
// Path to the .htpasswd file
const htpasswdPath = '/etc/nginx/.htpasswd';
// Write the .htpasswd file
try {
fs.writeFileSync(htpasswdPath, htpasswdContent);
console.log(`Generated .htpasswd file at ${htpasswdPath}`);
} catch (error) {
console.error(`Error generating .htpasswd file: ${error.message}`);
process.exit(1);
}
console.log('Basic authentication setup complete');

42
generate-password.js Normal file
View File

@ -0,0 +1,42 @@
/**
* Password Hash Generator for Weight Tracker
*
* This script generates a bcrypt hash for a given password that can be used
* with the Weight Tracker application's authentication system.
*
* Usage:
* node generate-password.js <your-password>
*/
const bcrypt = require('bcryptjs');
// Get password from command line arguments
const password = process.argv[2];
if (!password) {
console.error('Error: Password is required');
console.log('Usage: node generate-password.js <your-password>');
process.exit(1);
}
// Generate salt and hash
const saltRounds = 10;
bcrypt.genSalt(saltRounds, (err, salt) => {
if (err) {
console.error('Error generating salt:', err);
process.exit(1);
}
bcrypt.hash(password, salt, (err, hash) => {
if (err) {
console.error('Error generating hash:', err);
process.exit(1);
}
console.log('\nPassword Hash for Nginx Basic Authentication:');
console.log(hash);
console.log('\nUse this hash in your PASSWORD_HASH environment variable in Coolify.');
console.log('Example:');
console.log(`PASSWORD_HASH=${hash}`);
});
});

13
nginx-auth.conf Normal file
View File

@ -0,0 +1,13 @@
# Authentication configuration for Nginx
# This file will be included in the main Nginx configuration
# Define the authentication realm
auth_basic "Weight Tracker";
# Path to the .htpasswd file containing user credentials
auth_basic_user_file /etc/nginx/.htpasswd;
# CORS headers
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always;

View File

@ -1,29 +1,19 @@
server { server {
listen 80; listen 80;
server_name _; server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Enable compression # Enable gzip compression
gzip on; gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Authentication check - redirect to login if not authenticated # Protect all routes with basic authentication
location = / { location / {
# First try to use the API to check authentication # Include authentication configuration
auth_request /auth/check; include /etc/nginx/auth.conf;
# If auth passes, serve the main page # Serve static files
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
# CORS headers
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always;
# Error indicates not authenticated, redirect to login
error_page 401 = @error401;
# Handle preflight requests # Handle preflight requests
if ($request_method = 'OPTIONS') { if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Origin' '*';
@ -36,21 +26,6 @@ server {
} }
} }
# Serve static files directly
location / {
try_files $uri $uri/ /index.html;
# CORS headers
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always;
}
# Handle 401 unauthorized by redirecting to login
location @error401 {
return 302 /login;
}
# Proxy requests to the data API # Proxy requests to the data API
location /data/ { location /data/ {
proxy_pass http://localhost:3000/data/; proxy_pass http://localhost:3000/data/;

View File

@ -5,8 +5,8 @@ logfile=/dev/stdout
logfile_maxbytes=0 logfile_maxbytes=0
pidfile=/var/run/supervisord.pid pidfile=/var/run/supervisord.pid
[program:inject-password-hash] [program:generate-htpasswd]
command=node /usr/share/nginx/api/inject-password-hash.js command=node /usr/share/nginx/api/generate-htpasswd.js
directory=/usr/share/nginx/api directory=/usr/share/nginx/api
environment=PASSWORD_HASH="%(ENV_PASSWORD_HASH)s" environment=PASSWORD_HASH="%(ENV_PASSWORD_HASH)s"
autostart=true autostart=true