This commit is contained in:
@@ -110,8 +110,10 @@ detect_distro() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for Ubuntu 24.04 (Noble Numbat)
|
# Check for Ubuntu 24.04 (Noble Numbat)
|
||||||
if [[ "$VERSION" != "24.04" ]] && [[ "$CODENAME" != "noble" ]]; then
|
# SECURITY: Use OR (||) to warn if EITHER version or codename doesn't match
|
||||||
echo -e "${YELLOW}Warning: This installer is optimized for Ubuntu 24.04${NC}"
|
# This ensures we catch all incompatible systems, not just when both are wrong
|
||||||
|
if [[ "$VERSION" != "24.04" ]] || [[ "$CODENAME" != "noble" ]]; then
|
||||||
|
echo -e "${YELLOW}Warning: This installer is optimized for Ubuntu 24.04 (Noble Numbat)${NC}"
|
||||||
echo " Detected: Ubuntu $VERSION ($CODENAME)"
|
echo " Detected: Ubuntu $VERSION ($CODENAME)"
|
||||||
echo " Continuing anyway, but some features may not work correctly"
|
echo " Continuing anyway, but some features may not work correctly"
|
||||||
read -p "Continue anyway? (y/n) " -n 1 -r
|
read -p "Continue anyway? (y/n) " -n 1 -r
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func (a *App) auditMiddleware(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip audit for public endpoints
|
// Skip audit for public endpoints
|
||||||
if a.isPublicEndpoint(r.URL.Path) {
|
if a.isPublicEndpoint(r.URL.Path, r.Method) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const (
|
|||||||
func (a *App) authMiddleware(next http.Handler) http.Handler {
|
func (a *App) authMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Skip auth for public endpoints (includes web UI pages and read-only GET endpoints)
|
// Skip auth for public endpoints (includes web UI pages and read-only GET endpoints)
|
||||||
if a.isPublicEndpoint(r.URL.Path) {
|
if a.isPublicEndpoint(r.URL.Path, r.Method) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,9 @@ func (a *App) requireRole(allowedRoles ...models.Role) func(http.Handler) http.H
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isPublicEndpoint checks if an endpoint is public (no auth required)
|
// isPublicEndpoint checks if an endpoint is public (no auth required)
|
||||||
func (a *App) isPublicEndpoint(path string) bool {
|
// It validates both path and HTTP method to prevent unauthenticated mutations
|
||||||
|
func (a *App) isPublicEndpoint(path, method string) bool {
|
||||||
|
// Always public paths (any method)
|
||||||
publicPaths := []string{
|
publicPaths := []string{
|
||||||
"/healthz",
|
"/healthz",
|
||||||
"/health",
|
"/health",
|
||||||
@@ -126,13 +128,14 @@ func (a *App) isPublicEndpoint(path string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static files are public
|
// Static files are public (any method)
|
||||||
if strings.HasPrefix(path, "/static/") {
|
if strings.HasPrefix(path, "/static/") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make read-only GET endpoints public for web UI (but require auth for mutations)
|
// Read-only GET endpoints are public for web UI (but require auth for mutations)
|
||||||
// This allows the UI to display data without login, but operations require auth
|
// SECURITY: Only GET requests are allowed without authentication
|
||||||
|
// POST, PUT, DELETE, PATCH require authentication
|
||||||
publicReadOnlyPaths := []string{
|
publicReadOnlyPaths := []string{
|
||||||
"/api/v1/dashboard", // Dashboard data
|
"/api/v1/dashboard", // Dashboard data
|
||||||
"/api/v1/disks", // List disks
|
"/api/v1/disks", // List disks
|
||||||
@@ -149,7 +152,9 @@ func (a *App) isPublicEndpoint(path string) bool {
|
|||||||
|
|
||||||
for _, publicPath := range publicReadOnlyPaths {
|
for _, publicPath := range publicReadOnlyPaths {
|
||||||
if path == publicPath {
|
if path == publicPath {
|
||||||
return true
|
// Only allow GET requests without authentication
|
||||||
|
// All mutation methods (POST, PUT, DELETE, PATCH) require authentication
|
||||||
|
return method == http.MethodGet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func (a *App) cacheMiddleware(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip caching for authenticated endpoints that may have user-specific data
|
// Skip caching for authenticated endpoints that may have user-specific data
|
||||||
if !a.isPublicEndpoint(r.URL.Path) {
|
if !a.isPublicEndpoint(r.URL.Path, r.Method) {
|
||||||
// Check if user is authenticated - if so, skip caching
|
// Check if user is authenticated - if so, skip caching
|
||||||
// In production, you might want per-user caching by including user ID in cache key
|
// In production, you might want per-user caching by including user ID in cache key
|
||||||
if _, ok := getUserFromContext(r); ok {
|
if _, ok := getUserFromContext(r); ok {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
func (a *App) httpsEnforcementMiddleware(next http.Handler) http.Handler {
|
func (a *App) httpsEnforcementMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Skip HTTPS enforcement for health checks and localhost
|
// Skip HTTPS enforcement for health checks and localhost
|
||||||
if a.isPublicEndpoint(r.URL.Path) || isLocalhost(r) {
|
if a.isPublicEndpoint(r.URL.Path, r.Method) || isLocalhost(r) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ func isLocalhost(r *http.Request) bool {
|
|||||||
func (a *App) requireHTTPSMiddleware(next http.Handler) http.Handler {
|
func (a *App) requireHTTPSMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Skip for health checks
|
// Skip for health checks
|
||||||
if a.isPublicEndpoint(r.URL.Path) {
|
if a.isPublicEndpoint(r.URL.Path, r.Method) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func (a *App) maintenanceMiddleware(next http.Handler) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.isPublicEndpoint(r.URL.Path) {
|
if a.isPublicEndpoint(r.URL.Path, r.Method) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ func (a *App) rateLimitMiddleware(next http.Handler) http.Handler {
|
|||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Skip rate limiting for public endpoints
|
// Skip rate limiting for public endpoints
|
||||||
if a.isPublicEndpoint(r.URL.Path) {
|
if a.isPublicEndpoint(r.URL.Path, r.Method) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func (a *App) validateContentTypeMiddleware(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip for public endpoints
|
// Skip for public endpoints
|
||||||
if a.isPublicEndpoint(r.URL.Path) {
|
if a.isPublicEndpoint(r.URL.Path, r.Method) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user