mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-26 02:04:53 +09:00
ebe18cebff
acmeLogger drained and closed the body of every ACME error response before
handing the response back to golang.org/x/crypto/acme. The client parses that
body to classify errors, so badNonce was no longer recognised: isBadNonce
returned false, clearNonces was never called and Client.post treated the 400 as
non-retriable.
One badNonce reply therefore stops certificate renewal for good. autocert
retries every 30-60 minutes, each attempt reuses a nonce stored from the
previous failed response, that nonce has already expired, and the loop repeats
until the process is restarted or the certificate expires.
Restore the body with a fresh reader after logging it.
(cherry picked from commit 7472e98f6c)
54 lines
1.6 KiB
Go
54 lines
1.6 KiB
Go
package hscontrol
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestSecurityHeaders(t *testing.T) {
|
|
handler := securityHeaders(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
h := rec.Result().Header
|
|
assert.Equal(t, "DENY", h.Get("X-Frame-Options"))
|
|
assert.Equal(t, "frame-ancestors 'none'", h.Get("Content-Security-Policy"))
|
|
assert.Equal(t, "nosniff", h.Get("X-Content-Type-Options"))
|
|
assert.Equal(t, "no-referrer", h.Get("Referrer-Policy"))
|
|
}
|
|
|
|
func TestACMELoggerKeepsErrorBodyReadable(t *testing.T) {
|
|
const problem = `{"type":"urn:ietf:params:acme:error:badNonce","detail":"JWS has an invalid anti-replay nonce","status":400}`
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "application/problem+json")
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
_, _ = w.Write([]byte(problem))
|
|
}))
|
|
defer server.Close()
|
|
|
|
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, server.URL, nil)
|
|
require.NoError(t, err)
|
|
|
|
resp, err := (&acmeLogger{rt: http.DefaultTransport}).RoundTrip(req)
|
|
require.NoError(t, err)
|
|
|
|
defer resp.Body.Close()
|
|
|
|
// acme parses the body to classify errors such as badNonce, so it has to
|
|
// survive the logging middleware.
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, problem, string(body))
|
|
}
|