在 traefik.frontend.auth.forward.address 服务中提取参数

Extracting params in traefik.frontend.auth.forward.address service

总结

我正在尝试使用 Traefik 的 traefik.frontend.auth.forward.address 设置来设置身份验证直通。我的主要 Web 服务在容器上有 traefik.frontend.auth.forward.address=login.mydomain.com 标签。 Traefik 似乎正确地将针对 mydomain.com 的传入请求转发到 login.mydomain.com,但是当提交登录表单时,POST 请求在到达登录服务之前会变成 GET 请求,并且原始 POST 请求的参数似乎丢失了。用户永远无法登录。

容器

docker run -d \
    -l "traefik.frontend.rule=Host:login.mydomain.com; Method:GET, POST" \
    -l "traefik.enable=true" \
    login-service

docker run -d \
    -l "traefik.frontend.rule=Host:mydomain.com" \
    -l "traefik.frontend.auth.forward.address=https://login.mydomain.com" \
    -l "traefik.frontend.auth.forward.authResponseHeaders=cookie" \
    -l "traefik.enable=true" \
    web-service

问题

使用 auth.forward.address,我应该在我的登录服务中看到来自原始 POST 请求的参数吗?由于 Traefik 将其转换为 GET 请求,我应该在该请求的哪个位置查找参数?或者,也许我配置错误?可能缺少 authResponseHeaders 标志?

什么有效

请求 mydomain.com 显示来自 login-service 的登录表单,URL 继续显示 mydomain.com;重定向到 login.mydomain.com 是在幕后发生的,这是正确的。

登录服务我也自己测试过,好像可以。它承载一个向服务提交 POST 请求的表单,然后以 200 OK 和 Set-Cookie header 响应。事实上,当我直接进入 login.mydomain.com 时,我可以登录,这会设置我的 cookie,我可以进入 mydomain.com 并跳过登录屏幕。

什么不起作用

提交登录表单时,POST 请求作为 GET 请求和 POST 请求中的数据命中 login-service(由该服务中的日志证明)似乎不见了。 Traefik 添加了一个 x-forwarded-method 设置为 POST,但我找不到原始 POST 请求中的数据。我需要我的登录表单中的参数来验证它们,但它们似乎没有通过登录服务。

Traefik 配置

我认为我的 Traefik 配置与这里无关,但为了完整起见,我将其包括在内。

checkNewVersion = true
logLevel = "DEBUG"
defaultEntryPoints = ["https","http"]
sendAnonymousUsage = true

[api]
dashboard = true
debug = true

[entryPoints]
[entryPoints.http]
address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]

[retry]

[docker]
endpoint = "unix:///var/run/docker.sock"
watch = true
exposedbydefault = false

[acme]
email = "admin@mydomain.com"
storage = "acme.json"
entryPoint = "https"
OnHostRule = true

[acme.httpChallenge]
entryPoint = "http"

我找到了 Traefik's auth forward code。果然,请求 body 没有传递到下游的认证服务;只有 header 能做到这一点。默认表单提交行为就这么多了。

为了解决这个问题,我重新设计了我的 client-side 身份验证逻辑,以提交一个 POST 请求,其中包含 在 header 中的凭据的 body,使用 XMLHttpRequest.setRequestHeader.

设置

要让它发挥作用,还需要一个条件。我需要使用来自身份验证服务器的 Set-Cookie header return 设置 cookies client-side,但是如果服务器 return 是 200 OK登录成功,Traefik 将立即将原始请求传递到用户的预期目的地——这意味着 Set-Cookie header 永远不会到达用户。当身份验证成功时,我所做的就是 return a 418 I'm a teapot 来解决这个问题。这允许 Set-Cookie header 返回到用户的浏览器,以便可以设置用户的身份验证令牌。然后客户端自动重新加载预期的页面,这次使用正确的 cookie 集,现在身份验证服务器 return 如果它看到所请求服务的有效 cookie,则 200 OK

客户端代码如下所示:

<form id="form" method="post" action="/">
    Username: <input type="text" name="username" />
    Password: <input type="password" name="password" />
    <input type="submit" value="Submit" />
</form>
<script>
    // Override the default form submit behavior.
    // Traefik doesn't pass along body as part of proxying to the auth server,
    // so the credentials have to be put in the headers instead.
    const form = document.getElementById("form");
    form.addEventListener('submit', function(event) {
        const data = new FormData(form);
        var request = new XMLHttpRequest();
        request.open("POST", "/", true);
        request.setRequestHeader("Auth-Form", new URLSearchParams(data).toString());
        request.withCredentials = true;
        request.onload = function(e) {
            if (request.status == 418) {
                window.location = window.location.href;
            } else {
                alert("Login failed.");
            }
        };
        request.send(data);
        event.preventDefault();
    }, false);
</script>

我将至少在赏金用完之前将此问题悬而未决,因为我无法想象这是执行此操作的预期方式。我希望有人可以权衡 traefik.frontend.auth.forward.address 应该如何使用。或者,如果有人在 Traefik 中使用了另一种身份验证代理策略,我很想知道。