Security hardening: Fix critical JWT, rate limiting, and deployment issues
This commit is contained in:
parent
586d491b83
commit
1b46222300
@ -33,6 +33,7 @@
|
||||
<PackageReference Include="NBitcoin" Version="7.0.37" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="WebPush" Version="1.0.12" />
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -6,6 +6,7 @@ using LittleShop.Data;
|
||||
using LittleShop.Services;
|
||||
using FluentValidation;
|
||||
using Serilog;
|
||||
using AspNetCoreRateLimit;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -42,8 +43,58 @@ else
|
||||
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
}
|
||||
|
||||
// Rate Limiting - protect anonymous endpoints
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.Configure<AspNetCoreRateLimit.IpRateLimitOptions>(options =>
|
||||
{
|
||||
options.EnableEndpointRateLimiting = true;
|
||||
options.StackBlockedRequests = false;
|
||||
options.HttpStatusCode = 429;
|
||||
options.RealIpHeader = "X-Real-IP";
|
||||
options.ClientIdHeader = "X-ClientId";
|
||||
options.GeneralRules = new List<AspNetCoreRateLimit.RateLimitRule>
|
||||
{
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*/api/orders/by-identity/*",
|
||||
Period = "1m",
|
||||
Limit = 10
|
||||
},
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*/api/orders/by-customer/*",
|
||||
Period = "1m",
|
||||
Limit = 10
|
||||
},
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*",
|
||||
Period = "1s",
|
||||
Limit = 10
|
||||
},
|
||||
new AspNetCoreRateLimit.RateLimitRule
|
||||
{
|
||||
Endpoint = "*",
|
||||
Period = "1m",
|
||||
Limit = 100
|
||||
}
|
||||
};
|
||||
});
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IIpPolicyStore, AspNetCoreRateLimit.MemoryCacheIpPolicyStore>();
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IRateLimitCounterStore, AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore>();
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IRateLimitConfiguration, AspNetCoreRateLimit.RateLimitConfiguration>();
|
||||
builder.Services.AddSingleton<AspNetCoreRateLimit.IProcessingStrategy, AspNetCoreRateLimit.AsyncKeyLockProcessingStrategy>();
|
||||
|
||||
// Authentication - Cookie for Admin Panel, JWT for API
|
||||
var jwtKey = builder.Configuration["Jwt:Key"] ?? "ThisIsASuperSecretKeyForJWTAuthenticationThatIsDefinitelyLongerThan32Characters!";
|
||||
var jwtKey = builder.Configuration["Jwt:Key"];
|
||||
if (string.IsNullOrEmpty(jwtKey))
|
||||
{
|
||||
Log.Fatal("🚨 SECURITY: Jwt:Key configuration is missing. Application cannot start securely.");
|
||||
throw new InvalidOperationException(
|
||||
"JWT:Key must be configured in environment variables or user secrets. " +
|
||||
"Set the Jwt__Key environment variable or use: dotnet user-secrets set \"Jwt:Key\" \"<your-secure-key>\"");
|
||||
}
|
||||
|
||||
var jwtIssuer = builder.Configuration["Jwt:Issuer"] ?? "LittleShop";
|
||||
var jwtAudience = builder.Configuration["Jwt:Audience"] ?? "LittleShop";
|
||||
|
||||
@ -251,6 +302,9 @@ if (!app.Environment.IsDevelopment())
|
||||
app.UseHsts(); // Use HSTS for production security
|
||||
}
|
||||
|
||||
// Add rate limiting middleware (after CORS, before authentication)
|
||||
app.UseIpRateLimiting();
|
||||
|
||||
app.UseStaticFiles(); // Enable serving static files
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
@ -299,8 +353,26 @@ using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var context = scope.ServiceProvider.GetRequiredService<LittleShopContext>();
|
||||
|
||||
// Ensure database is created (temporary while fixing migrations)
|
||||
context.Database.EnsureCreated();
|
||||
// Use proper migrations in production, EnsureCreated only for development/testing
|
||||
if (app.Environment.IsProduction())
|
||||
{
|
||||
Log.Information("Production environment: Applying database migrations...");
|
||||
try
|
||||
{
|
||||
context.Database.Migrate();
|
||||
Log.Information("Database migrations applied successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Database migration failed. Application cannot start.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("Development/Testing environment: Using EnsureCreated");
|
||||
context.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
// Seed default admin user
|
||||
var authService = scope.ServiceProvider.GetRequiredService<IAuthService>();
|
||||
@ -310,10 +382,14 @@ using (var scope = app.Services.CreateScope())
|
||||
var dataSeeder = scope.ServiceProvider.GetRequiredService<IDataSeederService>();
|
||||
await dataSeeder.SeedSampleDataAsync();
|
||||
|
||||
// Seed system settings - enable test currencies for development
|
||||
var systemSettings = scope.ServiceProvider.GetRequiredService<ISystemSettingsService>();
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TBTC", true);
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TLTC", true);
|
||||
// Seed system settings - enable test currencies only in development
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
Log.Information("Development environment: Enabling test currencies");
|
||||
var systemSettings = scope.ServiceProvider.GetRequiredService<ISystemSettingsService>();
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TBTC", true);
|
||||
await systemSettings.SetTestCurrencyEnabledAsync("TLTC", true);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Information("LittleShop API starting up...");
|
||||
|
||||
@ -1,28 +1,44 @@
|
||||
#!/bin/bash
|
||||
# Force upgrade production database - WARNING: This will DELETE all data!
|
||||
# Use this script when data is not critical and you need a clean migration
|
||||
# Force upgrade production database - Preserves configuration and wallet data
|
||||
# This script will recreate the database structure while keeping critical config
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
PRODUCTION_DB_PATH="/opt/littleshop/littleshop-production.db"
|
||||
BACKUP_PATH="/opt/littleshop/backups/littleshop-production-backup-$(date +%Y%m%d_%H%M%S).db"
|
||||
CONFIG_EXPORT="/tmp/littleshop-config-export.sql"
|
||||
|
||||
echo "🔧 Force Upgrade Production Database"
|
||||
echo "====================================="
|
||||
echo "🔧 Force Upgrade Production Database (Preserving Config)"
|
||||
echo "========================================================"
|
||||
echo ""
|
||||
echo "⚠️ WARNING: This will DELETE all existing data!"
|
||||
echo "📍 Database: $PRODUCTION_DB_PATH"
|
||||
echo "💾 Will preserve: SystemSettings, Users, BotRegistrations"
|
||||
echo ""
|
||||
|
||||
# Create backups directory if it doesn't exist
|
||||
mkdir -p /opt/littleshop/backups
|
||||
|
||||
# Backup existing database if it exists
|
||||
# Backup and export configuration if database exists
|
||||
if [ -f "$PRODUCTION_DB_PATH" ]; then
|
||||
echo "📦 Backing up existing database..."
|
||||
cp "$PRODUCTION_DB_PATH" "$BACKUP_PATH"
|
||||
echo "✅ Backup saved to: $BACKUP_PATH"
|
||||
echo ""
|
||||
|
||||
echo "💾 Exporting configuration data..."
|
||||
|
||||
# Export critical tables to SQL
|
||||
sqlite3 "$PRODUCTION_DB_PATH" <<'EXPORT_SQL' > "$CONFIG_EXPORT"
|
||||
.mode insert SystemSettings
|
||||
SELECT * FROM SystemSettings WHERE 1=1;
|
||||
.mode insert Users
|
||||
SELECT * FROM Users WHERE 1=1;
|
||||
.mode insert BotRegistrations
|
||||
SELECT * FROM BotRegistrations WHERE 1=1;
|
||||
EXPORT_SQL
|
||||
|
||||
echo "✅ Configuration exported to: $CONFIG_EXPORT"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Remove old database files
|
||||
@ -39,20 +55,42 @@ cd /opt/littleshop
|
||||
dotnet ef database update --project LittleShop.csproj
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Migrations applied successfully"
|
||||
echo ""
|
||||
|
||||
# Restore configuration data if export exists
|
||||
if [ -f "$CONFIG_EXPORT" ]; then
|
||||
echo "♻️ Restoring configuration data..."
|
||||
|
||||
# Import the exported data
|
||||
sqlite3 "$PRODUCTION_DB_PATH" < "$CONFIG_EXPORT"
|
||||
|
||||
echo "✅ Configuration data restored"
|
||||
echo " - SystemSettings (wallet addresses, API keys)"
|
||||
echo " - Users (admin accounts)"
|
||||
echo " - BotRegistrations (Telegram bot config)"
|
||||
echo ""
|
||||
|
||||
# Clean up export file
|
||||
rm -f "$CONFIG_EXPORT"
|
||||
fi
|
||||
|
||||
echo "✅ Database successfully upgraded!"
|
||||
echo "📋 All migrations applied successfully"
|
||||
echo ""
|
||||
echo "📊 Database info:"
|
||||
echo " Path: $PRODUCTION_DB_PATH"
|
||||
echo " Backup: $BACKUP_PATH"
|
||||
echo ""
|
||||
echo "🎉 Production database is now ready!"
|
||||
echo ""
|
||||
echo "⚠️ Note: Product catalog and orders were reset"
|
||||
echo " Configuration and users were preserved"
|
||||
else
|
||||
echo ""
|
||||
echo "❌ Migration failed!"
|
||||
echo "🔄 Restoring from backup..."
|
||||
cp "$BACKUP_PATH" "$PRODUCTION_DB_PATH"
|
||||
echo "✅ Backup restored"
|
||||
rm -f "$CONFIG_EXPORT"
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user