mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-11 13:58:54 +00:00
Merge branch 'fix/m2-pr-f-scheduler-contextcheck-audit-closeout'
This commit is contained in:
@@ -6,6 +6,7 @@ run:
|
|||||||
linters:
|
linters:
|
||||||
default: none
|
default: none
|
||||||
enable:
|
enable:
|
||||||
|
- contextcheck
|
||||||
- govet
|
- govet
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- unused
|
- unused
|
||||||
|
|||||||
@@ -132,6 +132,15 @@ func (a *AuditMiddleware) Middleware(next http.Handler) http.Handler {
|
|||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
status := wrapped.statusCode
|
status := wrapped.statusCode
|
||||||
|
|
||||||
|
// Derive a detached context that preserves request-scoped values
|
||||||
|
// (trace IDs, auth info carried via context keys) but is not cancelled
|
||||||
|
// when the HTTP server finalizes the request. Using r.Context()
|
||||||
|
// directly would cause the async audit write to observe ctx.Done()
|
||||||
|
// as soon as the response completes; using context.Background() would
|
||||||
|
// discard useful observability metadata. WithoutCancel gives us both
|
||||||
|
// (M-2 / D-3).
|
||||||
|
auditCtx := context.WithoutCancel(r.Context())
|
||||||
|
|
||||||
// Record audit event asynchronously (best-effort, don't block response).
|
// Record audit event asynchronously (best-effort, don't block response).
|
||||||
// SECURITY: We intentionally use r.URL.Path (not r.URL.String() or r.RequestURI)
|
// SECURITY: We intentionally use r.URL.Path (not r.URL.String() or r.RequestURI)
|
||||||
// to prevent query parameters from being recorded in the immutable audit trail.
|
// to prevent query parameters from being recorded in the immutable audit trail.
|
||||||
@@ -147,7 +156,7 @@ func (a *AuditMiddleware) Middleware(next http.Handler) http.Handler {
|
|||||||
go func() {
|
go func() {
|
||||||
defer a.wg.Done()
|
defer a.wg.Done()
|
||||||
if err := a.recorder.RecordAPICall(
|
if err := a.recorder.RecordAPICall(
|
||||||
context.Background(),
|
auditCtx,
|
||||||
method,
|
method,
|
||||||
path,
|
path,
|
||||||
actor,
|
actor,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -78,10 +79,17 @@ func NewLogging(logger *slog.Logger) func(http.Handler) http.Handler {
|
|||||||
// Recovery middleware recovers from panics and returns a 500 error.
|
// Recovery middleware recovers from panics and returns a 500 error.
|
||||||
func Recovery(next http.Handler) http.Handler {
|
func Recovery(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) {
|
||||||
|
ctx := r.Context()
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
requestID := getRequestID(r.Context())
|
requestID := getRequestID(ctx)
|
||||||
log.Printf("[%s] PANIC: %v", requestID, err)
|
// Use slog.ErrorContext so the panic log carries the same
|
||||||
|
// request-scoped trace/auth metadata as normal request logs
|
||||||
|
// (M-2 / D-3 — preserve ctx propagation on the panic path).
|
||||||
|
slog.ErrorContext(ctx, "panic recovered in HTTP handler",
|
||||||
|
"request_id", requestID,
|
||||||
|
"panic", fmt.Sprintf("%v", err),
|
||||||
|
)
|
||||||
http.Error(w, `{"error":"Internal Server Error"}`, http.StatusInternalServerError)
|
http.Error(w, `{"error":"Internal Server Error"}`, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -547,7 +547,11 @@ func (c *Connector) solveAuthorizationsHTTP01(ctx context.Context, authzURLs []s
|
|||||||
return fmt.Errorf("failed to start challenge server: %w", err)
|
return fmt.Errorf("failed to start challenge server: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
// Derive the challenge-server shutdown context from the parent ctx so
|
||||||
|
// values (trace IDs, deadlines) propagate, but detach from its
|
||||||
|
// cancellation so Shutdown always gets its full budget even when the
|
||||||
|
// parent was cancelled (M-2 / D-3).
|
||||||
|
shutdownCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
_ = srv.Shutdown(shutdownCtx)
|
_ = srv.Shutdown(shutdownCtx)
|
||||||
c.logger.Debug("challenge server stopped")
|
c.logger.Debug("challenge server stopped")
|
||||||
|
|||||||
Reference in New Issue
Block a user