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

时间:2019-01-12 10:41:36

标签: reverse-proxy traefik

摘要

我正在尝试使用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标头响应。实际上,当我直接进入login.mydomain.com时,我可以登录并设置cookie,然后可以进入mydomain.com并跳过登录屏幕。

什么不起作用

提交登录表单时,POST请求将GET请求打到login-service(由该服务中的日志证明),并且POST请求中的数据似乎消失了。 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"

1 个答案:

答案 0 :(得分:0)

我找到了Traefik's auth forward code。当然,请求主体不会下游传递到身份验证服务。只有标题能做到这一点。默认表单提交行为就这么多了。

要解决此问题,我重新设计了客户端身份验证逻辑,以使用凭据{em>而不是正文(使用files_dict = {} with open(data_in) as fi: for line in fi: x = line.split(',')[1] if x not in files_dict: files_dict[x] = open(x + '.csv', 'w') files_dict[x].write(line) for file in files_dict.values(): file.close() 进行设置)在凭据中提交了POST请求。

要使其正常工作,还需要再抓一口。我需要使用从身份验证服务器返回的XMLHttpRequest.setRequestHeader标头在客户端设置cookie,但是如果登录成功后服务器返回Set-Cookie,Traefik将立即将原始请求传递给用户的预期目标-意味着200 OK标头将永远不会传递给用户。解决该问题的方法是,验证成功后返回Set-Cookie。这允许418 I'm a teapot标头将其返回到用户的浏览器,以便可以设置用户的auth令牌。然后,客户端会自动重新加载预期的页面,这一次设置了正确的cookie,现在,如果认证服务器看到所请求服务的有效cookie,则会返回Set-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中使用了另一种身份验证代理策略,那么我很想听听。