使用 NGINX 将环境变量注入静态网站

静态站点生成非常适合发布文档。在最近的一个项目中,我们选择使用 NGINX 作为 Web 服务器来托管 HTML 和 CSS 文件。但是,我们还希望使用 SSO 保护站点。这就是事情变得有点困难的地方。

当然,当使用 HTTP 基本身份验证以外的 SSO 机制时,我们需要向用户显示“退出”按钮。但是当与静态站点生成 (SSG) 结合使用时,我们还不知道注销 URI,因为它可能取决于部署的特定配置。

为了实际渲染按钮,我们面临两个选择:

  1. 不在 CI 中运行 SSG 工具,而是在 Docker 容器启动时运行。
  2. 呈现没有 URI 的链接,并让 Web 服务器在运行时注入它。

在我们的例子中,SSG 工具是 Jekyll,所以添加它的整个 Ruby 工具链会违背使用 NGINX 的目的:尽可能轻量级。此外,这个巨大的图像会创造更多的攻击面。[1] 所以,我们很快就取消了这个选项。

但是第二个选择如何实施呢?幸运的是,服务器端包含可以在这里提供帮助。简而言之,生成的页面包含几乎所有内容,再加上一些给 Web 服务器的小指令,以额外插入更多内容。SSI 可用于从其他服务器获取内容,有条件地启用或禁用页面中的区域,或仅打印某处变量的内容(如当前日期和时间)。

启用配置选项后,NGINX 支持 SSI:

location / {
  root /var/www;
  ssi on;
}

在 Jekyll 模板文件中,我们现在可以添加以下代码段:

<a href="<!--#echo var="ssisignouturl"-->">Sign out</a>

当使用 本地浏览页面时jekyll serve,这会产生无效的 HTML 标记(虽然它不会破坏页面,因为浏览器是宽松的),所以我建议只在生产模式下(即在 CI 中)呈现它:

{% if jekyll.environment == "production" %}
  <a href="<!--#echo var="ssisignouturl"-->">Sign out</a>
{% endif %}

其他 SSG 工具也有类似的方法来区分不同的环境。

现在唯一未解决的问题是 NGINX 不会将为进程设置的环境变量传递给 SSI 引擎。更糟糕的是,NGINX 甚至不支持读取配置文件中的环境变量。

根据官方NGINX Docker 镜像,将此类变量传递到正在运行的容器中的方法是使用模板。该映像包含一个启动脚本,envsubst该脚本在标记为“模板”的配置文件上运行。启动容器时,会处理模板并将环境变量复制到实际的 NGINX 配置中。

为了利用这种机制,请将 NGINX 配置的相关部分移动到名为default.conf.template(或以 结尾的任何其他名称.conf.template)的模板文件中。相应地Dockerfile进行了调整:

FROM nginx:1.19.9


COPY default.conf.template /etc/nginx/templates/


ENV SIGN_OUT_URL="#"

这是必要的,以便启动脚本选取文件并运行它envsubst

现在,在 NGINX 配置中,您可以访问变量:

location / {
  root /var/www;
  ssi on;
  set $ssisignouturl "${SIGN_OUT_URL}";
}

当容器启动时,脚本会生成以下配置:

location / {
  root /var/www;
  ssi on;
  set $ssisignouturl "#";

}

这是因为我们为Dockerfile指定了默认值SIGN_OUT_URL

非常重要的是,在 NGINX 模板中,SSI 变量名 ( ssisignouturl) 与环境变量名 ( SIGN_OUT_URL) 不同,否则envsubst会替换这两种情况。脚本保持未定义的变量出现不变。

最后,在如上更改配置后,SSI 引擎可以替换 URL。构建并启动 Docker 镜像,如下所示:

docker build -t docs .
docker run --rm -it -p 80:80 -e SIGN_OUT_URL="https://sso.bigcorp.com/signout" docs

这将呈现以下 HTML:

<a href="https://sso.bigcorp.com/signout">Sign out</a>

我们在这里发现令人惊讶的是,NGINX 没有传递环境变量的内置机制,我们不得不依赖 Docker 镜像提供的启动脚本。在无法使用官方 Docker 映像的情况下,我建议将注销链接生成为文本文件并通过 包含该文件include,而不是使用echo. 更多文档可以在 NGINX 站点上找到

  1. Kubernetes Init Containers可以在这里提供帮助,但我们希望避免将工作从 CI 转移到运行时。 ↩

 

via https://www.innoq.com/de/blog/nginx-ssi-env/

使用 NGINX 将环境变量注入静态网站