* security: redact secret names from logs, bind port to localhost - handlers: replace secret names in Warn/Error logs with client IP (prevents information disclosure via log access/injection) - client: downgrade secret name logs from Info to Debug level (Debug is discarded in production via io.Discard) - docker-compose: bind port to 127.0.0.1:8080 instead of 0.0.0.0:8080 (prevents direct access bypassing reverse proxy) Found during security pen test of vault.turboot.com * Enhance logging for secret retrieval and cache access
74 lines
1.8 KiB
Go
74 lines
1.8 KiB
Go
// Package handlers provides HTTP request handlers for the API
|
|
package handlers
|
|
|
|
import (
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/thijsherman/vaultwarden-api/internal/validators"
|
|
"github.com/thijsherman/vaultwarden-api/internal/vaultwarden"
|
|
"github.com/thijsherman/vaultwarden-api/pkg/logger"
|
|
)
|
|
|
|
// Handler contains all HTTP handlers
|
|
type Handler struct {
|
|
vaultClient *vaultwarden.Client
|
|
}
|
|
|
|
// NewHandler creates a new handler instance
|
|
func NewHandler(vaultClient *vaultwarden.Client) *Handler {
|
|
return &Handler{
|
|
vaultClient: vaultClient,
|
|
}
|
|
}
|
|
|
|
// HealthCheck handles GET /health
|
|
func (h *Handler) HealthCheck(c *fiber.Ctx) error {
|
|
return c.JSON(fiber.Map{
|
|
"status": "ok",
|
|
"service": "vaultwarden-api",
|
|
})
|
|
}
|
|
|
|
// GetSecret handles GET /secret/:name
|
|
func (h *Handler) GetSecret(c *fiber.Ctx) error {
|
|
secretName := c.Params("name")
|
|
|
|
if secretName == "" {
|
|
logger.Warn.Println("Secret name not provided")
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"error": "secret name is required",
|
|
})
|
|
}
|
|
|
|
if !validators.IsValidSecretName(secretName) {
|
|
logger.Warn.Printf("Invalid secret name format attempted from IP: %s", c.IP())
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"error": "invalid secret name format",
|
|
})
|
|
}
|
|
|
|
value, err := h.vaultClient.GetSecret(secretName)
|
|
if err != nil {
|
|
logger.Error.Printf("Failed to fetch secret (requested by IP: %s): %v", c.IP(), err)
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
|
"error": "secret not found",
|
|
})
|
|
}
|
|
|
|
// Return the secret value
|
|
return c.JSON(fiber.Map{
|
|
"name": secretName,
|
|
"value": value,
|
|
})
|
|
}
|
|
|
|
// RefreshCache handles POST /refresh
|
|
func (h *Handler) RefreshCache(c *fiber.Ctx) error {
|
|
h.vaultClient.ClearCache()
|
|
|
|
logger.Info.Println("Cache refresh requested")
|
|
return c.JSON(fiber.Map{
|
|
"status": "ok",
|
|
"message": "cache cleared successfully",
|
|
})
|
|
}
|