1 个不稳定版本
0.1.0 | 2022年1月26日 |
---|
在 #ssl 中排名第 31
81KB
2K SLoC
艾卡莫 - SSL 图像代理与 JWT 认证
艾卡莫(嵌入式艾卡莫)是一个受 atmos/camo 严重影响而创建的 HTTP 反向代理。
原始 Camo 的目的是为了在明文 HTTP 服务器后面加载图像,以避免混合内容问题。除此之外,艾卡莫还旨在在加载其他来源的图像时避免使用第三方cookie(例如需要适当会话cookie的公司内部截图服务器)。
我们已经在内部维基中使用 Camo 很长时间来提供一些外部图像,但由于最近关于第三方cookie的活动,我们需要一个类似的系统,以允许在不使用第三方cookie的情况下嵌入内部截图服务。
工作原理(简要说明)
基本上,与 Camo 一样,艾卡莫接收 URL 数据并代表用户检索 URL 上的资源。
与 Camo 不同,必须在一个具有艾卡莫预定义前缀的路径下为艾卡莫设置 源 的路由。例如,当将 艾卡莫前缀 设置为 /.ecamo/
时,则任何以 /.ecamo/
前缀开头的源请求应路由到艾卡莫服务器。单个艾卡莫服务器适用于多个应用程序(= 源)。
艾卡莫设计如下。
<!-- On origin https://wiki.corp.example.com -->
<img src="/.ecamo/v1/r/{URL_TOKEN}...">
在此示例中,一个 源 是 wiki.corp.example.com
并期望为艾卡莫设置一个 授权cookie。并且 URL_TOKEN
指定了实际内容的 URL。授权cookie和URL令牌格式为JWT,并由源拥有的P-256私钥签名。
然后艾卡莫将此URL重定向到基于授权cookie的短期令牌的艾卡莫的 规范源。用户将从 源 接收指定的URL的实际内容。
部署
配置
配置通过环境变量完成。
ECAMO_BIND
(默认:[::]:3000
):绑定地址ECAMO_CANONICAL_HOST
(必需):规范源的 HTTP Host 标头值。用于提供实际内容。您可能需要指定非标准端口号。ECAMO_SERVICE_PUBLIC_KEYS
(必需):JSON对象,键为"${SERVICE_ORIGIN} ${kid}"
,值为JWK对象,由服务用于签名授权cookie和ecamo URL。支持ES256密钥。例如:{"https://service.test.invalid key_1": {"kid": "key_1", ...}}
ECAMO_PRIVATE_KEYS
(必需):JSON对象,键为tokenkid
,值为JWK对象,由Ecamo用于在URL和请求头中签名短暂授权令牌。支持ES256密钥。ECAMO_SIGNING_KID
:主私钥kid
,通过$ECAMO_PRIVATE_KEYS
提供。ECAMO_SERVICE_HOST_REGEXP
:用于验证服务源 Host头的正则表达式。当未指定时,任何来源都作为服务源。ECAMO_SOURCE_ALLOWED_REGEXP
:用于验证源URL的正则表达式。当指定时,任何不匹配的源URL将被拒绝。ECAMO_SOURCE_BLOCKED_REGEXP
:用于拒绝源URL的正则表达式。当指定时,任何匹配的源URL将被拒绝。ECAMO_PRIVATE_SOURCE_ALLOWED_REGEXP
:用于验证目标IP地址解析为私有IP地址时的源URL;URL可能仅包括方案、主机和端口。任何不匹配的连接到私有IP地址的源URL将被拒绝。当未指定时,任何尝试连接到私有IP地址的连接尝试将被拒绝。注意,URL还必须通过ECAMO_SOURCE_ALLOWED_REGEXP
允许。ECAMO_PREFIX
(默认:.ecamo
):前面解释过的ecamo前缀。这是一个特别用于嵌入在服务源中的URL前缀,更确切地说,用于从规范源重定向请求到服务源。ECAMO_MAX_REDIRECTS
(默认:0
):在单个HTTP请求中允许的HTTP重定向的最大数量,用于获取源URL。当允许时,在跟随重定向时,任何URL都将被允许(当ECAMO_*_REGEXP
不起作用但ECAMO_PRIVATE_SOURCE_ALLOWED_REGEXP
起作用时)ECAMO_MAX_LENGTH
:从源URL允许代理的最大Content-Length
数。如果分块响应超出限制,则此类代理响应将被终止(客户端将看到意外的EOF)ECAMO_CONTENT_TYPE_ALLOWED
(默认:常见image/*类型):允许代理的Content-Type
。指定为逗号分隔的值。ECAMO_TIMEOUT
:获取源URL的秒数超时。ECAMO_AUTH_COOKIE
(默认:__Host-ecamo_token
,当不安全模式=ecamo_token
时):用于存储授权令牌的Cookie名称。ECAMO_DEFUALT_CACHE_CONTROL
(默认=public, max-age=3600
):当源URL响应中缺少缓存控制头时,响应的缓存控制头值ECAMO_INSECURE
:当启用时,某些功能将使用纯HTTP进行开发。
sec-x-ecamo-service-host
头
您可以使用sec-x-ecamo-service-host
请求头来向Ecamo服务器显式指定一个服务来源Host头。这特别适用于您将Ecamo服务器放在反向代理后面,并且需要设置特定的Host
(:authority
)头的情况。
(另一方面,这与X-Forwarded-Host
请求头类似)
用法
要使用Ecamo,服务来源必须生成一个URL令牌和一个授权令牌。
生成ecamo URL
使用以下格式向Ecamo发送请求:
https://${SERVICE_HOST}/${PREFIX}/v1/r/${URL_TOKEN}
其中
SERVICE_HOST
:允许任何字符串,但需符合$ECAMO_SERVICE_HOST_REGEXP
PREFIX
:建议与$ECAMO_PREFIX
相同,但允许任何字符串。URL_TOKEN
:JWT(JSON Web Token),具有以下约束- 头部
alg
:必须是ES256
kid
:必须设置为在$ECAMO_SIGNING_PUBLIC_KEYS
- 声明
iss
:必须与服务来源的Web来源相同。(例如:https://service.test.invalid
)ecamo:url
:源URLecamo:send-token
(可选):设置为true
以向源URL发送匿名ID令牌。
- 头部
注意
- 请注意,每个签名操作生成的URL令牌都是不确定的。如果您打算启用边缘缓存,请确保您的应用程序尽可能不频繁地生成ecamo URL。
exp
和nbf
声明未进行验证。
生成授权cookie
授权cookie是由$ECAMO_SIGNING_PUBLIC_KEYS
中指定的密钥签名的JWT,具有以下约束。它应存储在名为$ECAMO_AUTH_COOKIE
(默认为__Host-ecamo_token
)的cookie中。
- 头部
alg
:必须是ES256
kid
:必须设置为在$ECAMO_SIGNING_PUBLIC_KEYS
- 声明
exp
必须提供。iss
必须与服务来源的Web来源相同。(例如:https://service.test.invalid
)aud
必须设置为规范来源的Web来源。(例如:https://$ECAMO_CANONICAL_HOST
)
- 建议
- 使cookie过期时间和令牌生命周期保持一致。
- 尽可能缩短生命周期。一般来说,在Cookies中使用JWT是一个糟糕的想法使用JWT在Cookies中,但假设应用程序在其自己的地方进行身份验证,并且
ecamo-token
是从其会话存储中派生的。如果可以从其他会话令牌中派生出ecamo-token
,那么其生命周期可以很短。
杂项
重定向到源地址
为了使用户能够识别请求内容的规范URL,当最终用户直接打开/.ecamo/...
URL时,Ecamo会直接将其重定向到源图像(更确切地说,当请求没有授权Cookie或请求Sec-Fetch-Dest=document时)。
匿名ID令牌
如果URL令牌的ecamo:send-token
设置为true,Ecamo将设置一个ID令牌作为Bearer令牌(Authorization: Bearer ...
)。
该令牌不包含用户信息,例如
{
"iss": "https://ecamo.test.invalid",
"sub": "anonymous",
"aud": "https://source.test.invalid",
"exp": ...,
"iat": ...,
"ecamo:svc": "https://service.test.invalid"
}
与CDN一起使用
- Fastly Compute@Edge集成:见./contrib/fastlyce
SSRF预防(私有IP地址)
由于reqwest当前API的限制,当配置了$ECAMO_PRIVATE_SOURCE_ALLOWED_REGEXP
时,Ecamo将启动一个SOCKS5代理来限制连接到私有IP地址。内部代理仅用于不匹配$ECAMO_PRIVATE_SOURCE_ALLOWED_REGEXP
的请求。对于此类请求,将拒绝以下地址的任何尝试
许可
MIT许可证
版权所有 2021 Cookpad Inc.
依赖项
~11–29MB
~440K SLoC