feat(agent,server): v0.2.7 - mu-plugin WordPress robuste, réparation wp-config, proxy cookies/headers
- Agent: mu-plugin embarqué amélioré (HTTPS forcé, filtres URL, localhost:port) - Agent: suppression des WP_HOME/WP_SITEURL hardcodés au démarrage des instances - Server/proxy: envoi X-Forwarded-Port, réécriture headers/body élargie - Server/proxy: sanitization des Set-Cookie (Secure, SameSite, Domain) - Dashboard: version agent 0.2.7, action Supprimer complète - Cleanup: binaires agent 0.2.3-0.2.6 remplacés par 0.2.7
This commit is contained in:
@@ -25,12 +25,22 @@ async function proxyRequest(req: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
const publicUrl = `https://${cleanHost}`;
|
||||
const targetUrl = new URL(req.url);
|
||||
const upstream = `http://${instance.node.tailscaleIp}:${instance.port}${targetUrl.pathname}${targetUrl.search}`;
|
||||
// The middleware rewrites /foo to /api/proxy/foo; strip the prefix before forwarding
|
||||
let pathname = targetUrl.pathname;
|
||||
if (pathname.startsWith("/api/proxy")) {
|
||||
pathname = pathname.slice("/api/proxy".length) || "/";
|
||||
}
|
||||
const upstream = `http://${instance.node.tailscaleIp}:${instance.port}${pathname}${targetUrl.search}`;
|
||||
|
||||
const headers = new Headers(req.headers);
|
||||
headers.delete("host");
|
||||
headers.set("host", cleanHost);
|
||||
headers.set("x-forwarded-host", cleanHost);
|
||||
headers.set("x-forwarded-proto", "https");
|
||||
headers.set("x-forwarded-port", "443");
|
||||
headers.set("x-forwarded-for", req.headers.get("x-forwarded-for") || "unknown");
|
||||
|
||||
const upstreamRes = await fetch(upstream, {
|
||||
method: req.method,
|
||||
@@ -47,7 +57,78 @@ async function proxyRequest(req: NextRequest) {
|
||||
responseHeaders.delete("content-encoding");
|
||||
responseHeaders.delete("content-length");
|
||||
|
||||
return new Response(upstreamRes.body, {
|
||||
// Rewrite any header values that point to localhost or the internal Tailscale address
|
||||
const localPatterns = [
|
||||
`http://${instance.node.tailscaleIp}:${instance.port}`,
|
||||
`https://${instance.node.tailscaleIp}:${instance.port}`,
|
||||
`http://localhost:${instance.port}`,
|
||||
`https://localhost:${instance.port}`,
|
||||
`http://localhost`,
|
||||
`https://localhost`,
|
||||
];
|
||||
responseHeaders.forEach((value, key) => {
|
||||
let newValue = value;
|
||||
for (const pattern of localPatterns) {
|
||||
newValue = newValue.replaceAll(pattern, publicUrl);
|
||||
}
|
||||
if (newValue !== value) {
|
||||
responseHeaders.set(key, newValue);
|
||||
}
|
||||
});
|
||||
|
||||
// Sanitize Set-Cookie headers so sessions work through the public domain.
|
||||
// WordPress may issue cookies for "localhost" or without Secure flag; fix it.
|
||||
const setCookies = responseHeaders.getSetCookie();
|
||||
if (setCookies.length > 0) {
|
||||
responseHeaders.delete("set-cookie");
|
||||
for (let cookie of setCookies) {
|
||||
// Drop any Domain=... set for localhost/internal domains
|
||||
cookie = cookie.replace(/;\s*Domain=[^;]+/gi, "");
|
||||
// Ensure Secure is present for HTTPS public URLs
|
||||
if (!/;\s*Secure\b/i.test(cookie)) {
|
||||
cookie += "; Secure";
|
||||
}
|
||||
// Make sure cookies are sent on sub-domain navigations
|
||||
if (!/;\s*SameSite\b/i.test(cookie)) {
|
||||
cookie += "; SameSite=Lax";
|
||||
}
|
||||
responseHeaders.append("set-cookie", cookie);
|
||||
}
|
||||
}
|
||||
|
||||
const contentType = responseHeaders.get("content-type") || "";
|
||||
const shouldRewriteBody =
|
||||
contentType.includes("text/html") ||
|
||||
contentType.includes("text/css") ||
|
||||
contentType.includes("application/javascript") ||
|
||||
contentType.includes("application/json");
|
||||
|
||||
if (!shouldRewriteBody) {
|
||||
return new Response(upstreamRes.body, {
|
||||
status: upstreamRes.status,
|
||||
statusText: upstreamRes.statusText,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
// For text responses, rewrite localhost/internal URLs to the public URL
|
||||
let body = await upstreamRes.text();
|
||||
const localBase = `http://${instance.node.tailscaleIp}:${instance.port}`;
|
||||
const localBaseHttps = `https://${instance.node.tailscaleIp}:${instance.port}`;
|
||||
const localLocalhostHttp = `http://localhost:${instance.port}`;
|
||||
const localLocalhostHttps = `https://localhost:${instance.port}`;
|
||||
const localLocalhostPlainHttp = `http://localhost`;
|
||||
const localLocalhostPlainHttps = `https://localhost`;
|
||||
|
||||
body = body
|
||||
.replaceAll(localBase, publicUrl)
|
||||
.replaceAll(localBaseHttps, publicUrl)
|
||||
.replaceAll(localLocalhostHttp, publicUrl)
|
||||
.replaceAll(localLocalhostHttps, publicUrl)
|
||||
.replaceAll(localLocalhostPlainHttp, publicUrl)
|
||||
.replaceAll(localLocalhostPlainHttps, publicUrl);
|
||||
|
||||
return new Response(body, {
|
||||
status: upstreamRes.status,
|
||||
statusText: upstreamRes.statusText,
|
||||
headers: responseHeaders,
|
||||
|
||||
Reference in New Issue
Block a user