一直以来都想部署一个前置验证的服务,作为前置验证的网关(类似于Nginx Auth),用来保护一些开放于公网上但自用的服务。但 Nginx Auth 太过简单(丑),虽想着找到一个能够满足我的这些功能的一个身份验证服务,经过 @Shell 的安利,选择了 AuthenTik。

选择 AuthenTik 最主要的原因是因为其 UI 讨喜,另一个便是使用的人相对较多而且也在积极维护。但中文互联网上有关于 AuthenTik 的内容不多。

每次访问同一域名下的某个服务时,都会先跳转到 AuthenTik 的认证页面,认证成功后,才会跳转到服务本来的页面。在某一时间内访问同一域名下的其他服务时,则不需要认证,不仅加强了安全性,还保护了隐私。

AuthenTik 不仅可以作为 IdP 进行使用,更多的人是将其作为 SSO 进行使用,一个站点登录,即可于同域名下的其他自建服务进行免登录操作。


AuthenTik

项目地址:AuthenTik

AuthenTik 是一个 IdP(身份提供商)和 SSO(单点登录),其每段代码、每个功能都以安全性为首要构建,并强调灵活性和多功能性。

借助 AuthenTik,网站管理员、应用程序开发人员和安全工程师几乎可以在任何类型的环境中获得可靠且安全的身份验证解决方案。它为用户和应用程序提供了强大的恢复操作,包括用户配置文件和密码管理。您可以快速编辑、停用甚至模拟用户配置文件,并为新用户设置新密码或重置现有密码。

您可以在现有环境中使用 AuthenTik 来添加对新协议的支持,因此将 AuthenTik 引入您当前的技术堆栈不会带来重新架构的挑战。我们支持所有主要提供商,例如 OAuth2SAMLLDAPSCIM,因此您可以为每个应用程序选择所需的协议。

特性

  • 管理界面:用于创建和管理用户和组、令牌和凭据、应用程序集成、事件以及定义标准和可自定义登录和身份验证流程的流程的可视化工具。易于阅读的可视化仪表板显示系统状态、最近的登录和身份验证事件以及应用程序使用情况。
  • 用户界面:AuthenTik 中的控制台视图显示您已实施 AuthenTik 的所有应用程序和集成。单击要访问的应用程序以将其打开,或向下钻取以在管理界面中编辑其配置。
  • 流程流程是登录和身份验证过程的各个阶段发生的步骤。阶段代表登录过程中的单个验证或逻辑步骤。AuthenTik 允许自定义和精确定义这些流程。

配置要求

  • 2个 CPU 核心
  • 2 GB RAM

环境需求

  • Docker
  • Docker Compose(建议使用 Compose v3)

部署 AuthenTik

此处推荐,且仅演示使用 Docker 进行部署。

配置 ENV 文件

全新安装 Authentik 首先需要生成 .env 文件,并向文件中写入生成的密钥以及密码。

echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')" >> .env

建议开启电子邮件通知,如果系统有安全性以及功能性的更新,可以及时收到更新通知的邮件,将下列配置写入 .env文件。

如果长时间未能接收到邮箱,请查看开启的 TLS 或 SSL 加密对应的端口号是否填写正确!且 TLS 与 SSL 不可同时开启。

  • TLS:587
  • SSL:465

但是经过我实测发现,QQ 邮箱通过 SSL 进行加密会导致发信失败,使用 TLS 则一切正常,但据我推测,这个问题很可能是 AuthenTik 的 bug。

# SMTP 主机地址及端口号
AUTHENTIK_EMAIL__HOST=localhost
AUTHENTIK_EMAIL__PORT=25
# 邮箱服务的账号以及密码
AUTHENTIK_EMAIL__USERNAME=roywang
AUTHENTIK_EMAIL__PASSWORD=roywang
# 开启 TLS,与 SSL 不可同时开启。
AUTHENTIK_EMAIL__USE_TLS=false
# 开启 SSL,与 TLS 不可同时开启。
AUTHENTIK_EMAIL__USE_SSL=false
AUTHENTIK_EMAIL__TIMEOUT=10
# 邮箱地址
AUTHENTIK_EMAIL__FROM=authentik@localhost

可选的其他配置

# 配置服务所使用的端口
COMPOSE_PORT_HTTP=80
COMPOSE_PORT_HTTPS=443
# 启用错误报告
AUTHENTIK_ERROR_REPORTING__ENABLED=true

Docker 部署

将以下内容保存为docker-compose.yml文件,将其与上步中.env文件放于同一目录。

请注意,其中需要修改的各处。

services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    container_name: Authentik-Postgresql
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 5s
    volumes:
//PgSQL数据存放目录,此处需要修改。
      - /your-authentik-path/postgresql:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${PG_PASS:?database password required}
      POSTGRES_USER: ${PG_USER:-authentik}
      POSTGRES_DB: ${PG_DB:-authentik}
    env_file:
      - .env

  redis:
    image: docker.io/library/redis:alpine
    container_name: Authentik-Redis
    command: --save 60 1 --loglevel warning
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
//Redis缓存存放目录,此处需要修改。
      - /your-authentik-path/redis:/data

  server:
    image: ${AUTHENTIK_IMAGE:-docker.io/beryju/authentik}:${AUTHENTIK_TAG:-latest}
    restart: unless-stopped
    container_name: Authentik-Server
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    volumes:
//AuthenTik数据存放目录,此处需要修改。
      - /your-authentik-path/media:/media
      - /your-authentik-path/custom-templates:/templates
    env_file:
      - .env
    ports:
      - "9443:9443"
    depends_on:
      - postgresql
      - redis

  worker:
    image: ${AUTHENTIK_IMAGE:-docker.io/beryju/authentik}:${AUTHENTIK_TAG:-latest}
    restart: unless-stopped
    container_name: Authentik-Worker
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    user: root
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
//AuthenTik 数据存放目录,此处需要修改。
      - /your-authentik-path/media:/media
      - /your-authentik-path/certs:/certs
      - /your-authentik-path/custom-templates:/templates
    env_file:
      - .env
    depends_on:
      - postgresql
      - redis

此时运行 docker compose up -d启动容器,官方默认的docker-compose.yml 的文件中,Server以及Worker均使用的是 ghcr,国内服务器根本拉不到镜像。

待容器均已创建启动完毕,初始化设置时需访问:

https://your-ip:9443/if/flow/initial-setup

默认用户名为akadmin

使用

在此主要是介绍一下如何给服务添加验证。

OIDC

如果需要通过 OIDC 接入 SSO,可以参考 Shell 的博客:

https://blog.ning.moe/tags/Authentik/

正向代理

首先需要进入管理员页面。

创建提供程序

进入管理页面后,左侧菜单栏依次点击应用程序-提供程序,点击创建,选择Proxy Provider

然后点击下一步,在此处分别输入程序名称,选择身份验证流程授权流程(默认选择第一个即可),然后选择Forward Auth(单应用)

在下方输入外部主机,也就是服务的域名(需要带 http or https 协议头)以及设置令牌有效期。

高级设置

证书选择,可做作用域全选。

不验证身份的路径,有些服务还会向外提供 API,这个验证路径就是为该部分服务准备的。使用的 Go 语言正则。但是这个不建议使用,有较为严重的性能问题。

具体解决方案可以期待后续文章。

创建应用程序

位于左侧菜单栏应用程序-应用程序,点击创建

输入名称以及Slug,选择刚刚创建的提供程序。策略引擎模式 选择 all

添加前哨

位于左侧菜单栏应用程序-前哨,右侧的操作选择编辑,选择已有的应用程序,将其添加至右侧,点击更新。

Nginx 配置文件

因为是正向代理,所以需要修改一下 Nginx 的配置文件,此配置通用。

在配置文件最上部添加:

# Upgrade WebSocket if requested, otherwise use keepalive
map $http_upgrade $connection_upgrade_keepalive {
    default upgrade;
    ''      '';
}

在监听的根目录下location /{}中添加配置:

	proxy_pass          $forward_scheme://$server:$port;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection $connection_upgrade_keepalive;
	auth_request     /outpost.goauthentik.io/auth/nginx;
	error_page       401 = @goauthentik_proxy_signin;
	auth_request_set $auth_cookie $upstream_http_set_cookie;
	add_header       Set-Cookie $auth_cookie;

    auth_request_set $authentik_username $upstream_http_x_authentik_username;
    auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
    auth_request_set $authentik_email $upstream_http_x_authentik_email;
    auth_request_set $authentik_name $upstream_http_x_authentik_name;
    auth_request_set $authentik_uid $upstream_http_x_authentik_uid;

    proxy_set_header X-authentik-username $authentik_username;
    proxy_set_header X-authentik-groups $authentik_groups;
    proxy_set_header X-authentik-email $authentik_email;
    proxy_set_header X-authentik-name $authentik_name;
    proxy_set_header X-authentik-uid $authentik_uid;

在配置文件底部添加,请注意的是,需要修改服务地址:

location /outpost.goauthentik.io {
		# 修改 AuthenTik 地址
    proxy_pass              http://authentik.company:9000/outpost.goauthentik.io;

    proxy_set_header        Host $host;

    proxy_set_header        X-Original-URL $scheme://$http_host$request_uri;
    add_header              Set-Cookie $auth_cookie;
    auth_request_set        $auth_cookie $upstream_http_set_cookie;
    proxy_pass_request_body off;
    proxy_set_header        Content-Length "";
}

location @goauthentik_proxy_signin {
    internal;
    add_header Set-Cookie $auth_cookie;
    return 302 /outpost.goauthentik.io/start?rd=$request_uri;
}

此时访问需要验证的服务,跳转到了 AuthenTik 页面。

后语

AuthenTik 在正常使用的时候,坑还是比较多的,整个过程折腾了接近半个月,才完全达到效果。

在部署这个服务时,陆续碰到了以下问题:

  • 访问发生 500 错误
  • 给 API 放行的路径性能太差
  • 放行正则非常难写

详细要说一下,关于配置完成后访问服务发生 500 错误的问题。首先检查一下是否为该应用添加到前哨之中,尝试将 Nginx 配置文件中的外部的 AuthenTik 服务域名更换为内网IP。

如果使用内网IP一切正常,使用公网域名访问不正常,恭喜你咱们碰到了一样的问题。后续我会水一篇新的文章以解决这个问题。

另外我部署了一个 AList用于通过 WebDAV 协议去同步文件,但经过实测,性能差距能有10倍…..这让我放弃了使用 AuthenTik 中放行路径的服务。 这个也会后续水一篇新的文章以解决这个问题。

这里只是写了个大概的使用情况,其他的更多设置,推荐一下 ECWU 大佬关于 AuthenTik 的系列教程,对我有很大的帮助

参考

AuthenTik 官方文档

ECWU AuthenTik 系列教程