Yet another otaku

某ロリコン的自白


  • 首頁
  • 歸檔
  • 分類
  • 標籤
  • 連結
  • 關於
  •    

© 2023 SgDylan

Theme Typography by Makito

Proudly published with Hexo

为自建服务加上单点登录

發佈於 2022-09-29 評論 笔记  SSO 单点登录 

Basic Authentication 成为咱自建服务的主要鉴权方式已有快十个年头了。
在单点登录(SSO)普及的当下,自建服务的鉴权方式也该换了。

咱使用的 HTTP 服务器是 nginx,自建的服务通常由此反代后暴露在公网。
在 nginx 上使用 Basic Auth 非常简单,只需要一个密码文件及两行配置:

location /api {
    auth_basic           "Aki fukashi tonari wa nani mo shinai hito";
    auth_basic_user_file /etc/nginx/.htpasswd; 
}

经过一圈调研,发现其实单点登录的 nginx 实现也非常简单,同样是两行:

location /api {
    auth_request /auth;
    @error_page 401 = @error401;
}

熟悉 nginx 的选手应该已经意识到问题的不简单了:
/auth 是什么? @error401 又是啥?

答案揭晓:

location /auth {
    internal;

    proxy_pass https://sso.sgdylan.com/auth;

    # 反代 SSO 有 TLS 需带上这两行保证传递正确的 SNI 信息
    proxy_ssl_server_name on;
    proxy_ssl_name sso.sgdylan.com;

    proxy_pass_request_body off;
    proxy_set_header Content-Length "";

    proxy_set_header X-Origin-URI $host$request_uri;
    proxy_set_header X-Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}


location @error401 {
    return 302 https://sso.sgdylan.com/login?go=$scheme://$http_host$request_uri;
}

此处 sso.sgdylan.com 即是部署有单点登录服务的域名。
当用户访问到 /api 的时候,nginx 同时向 /auth 反代的 SSO 发送请求,此时得到响应码为 200 即允许用户访问。


不难发现,实现 SSO 服务其实非常简单,因此有着为数众多的轮子。
秉承最小化服务的原则,咱最终选定了 nginx-sso 作为 SSO 的实现。

选定这一方案的另外两点考量是为了完全兼容原有的 Basic Auth 及 Access Token,便于继续使用现有的配套自动化脚本,并同时支持 Yubikey 一键登录,实现现代化改造目标。

实现 Yubikey 或其他 Key 的一键登录有两种实现方法:
一是通过 WebAuthn,这需要一只 FIDO2/U2F 兼容的 Key(咱不是全部 Key 都支持);
二是使用 Key 的 2FA TOTP 作为身份验证使用(需要借助 Yubico 官方接口验证)。

WebAuthn 的好处是 Windows/Chrome 兼容性极佳,可以使用的介质方式非常多(指纹、人脸、手机、PIN 码等),但缺点是目前支持这一方式的实现都相对较重。

nginx-sso 使用的是第二种实现方法,有 Yubikey 的小伙伴是没问题的,但一大票廉价的国产 Key 就无缘了 (有生之年会实现的) 。


以下是无聊的配置环节:

nginx-sso 的配置文件 config.yaml:

建议配合 repo 下的 Wiki 参考修改,部分内容需按注释重新产生

---

login:
  title: "Loli Experiment - Login"
  default_method: "simple"
  hide_mfa_field: true
  names:
    simple: "Username / Password"
    yubikey: "Yubikey"

cookie:
  domain: ".sgdylan.com"
  # You'll want to regenerate this. Use something like: cat /dev/urandom | tr -dc 'A-Za-z0-9' | dd bs=1 count=60
  authentication_key: "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
  prefix: "sgdylan_sso"
  secure: true

listen:
  addr: "127.0.0.1"
  port: 8022

audit_log:
  targets:
    - fd://stdout
    - file:///var/log/nginx-sso/audit.jsonl
  events: ['access_denied', 'login_success', 'login_failure', 'logout', 'validate']
  headers: ['x-origin-uri']
  trusted_ip_headers: ["X-Forwarded-For", "RemoteAddr", "X-Real-IP"]

acl:
  rule_sets:
  - rules:
    - field: "x-origin-uri"
      regexp: "edge.sgdylan.com/download.*"
    # @_authenticated for all login user, @_anonymous for all user
    allow: ["@users"]
  - rules:
    - field: "x-origin-uri"
      equals: "edge.sgdylan.com/download.*"
    allow: ["@users"]

providers:
  simple:
    enable_basic_auth: true
    users:
      # Bcrypt Mode: https://hostingcanada.org/htpasswd-generator/
      admin: "$2y$10$gVMTIKKMJL5zstnlxgi9fOf6rOsNfVfswoC.yrjY3wKAnLX9O.dcy"
    groups:
      users: ["admin"]
  yubikey:
    # Get your client / secret from https://upgrade.yubico.com/getapikey/
    client_id: "CLIENT_ID"
    secret_key: "SECRET_KEY"
    devices:
      ccccdrfcvrhl: "admin"
    groups:
      users: ["admin"]
...

nginx 反代 nginx-sso:

server {
    listen 80;
    server_name sso.sgdylan.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2 default_server;
    server_name sso.sgdylan.com;

    location / {
        proxy_pass http://127.0.0.1:8022/;
    }
}

最后留意一点:

auth_request 所在的 location 块内如果是反代服务,需要设置允许 Referer 为 SSO 所在的域名,或者使用 proxy_set_header Referer ""; 删掉由 SSO 跳转至服务域名带上的 Referer。

分享到 

 上一篇: 全新 NAS 安装简单记录 下一篇: 2021,就这么结束了 

© 2023 SgDylan

Theme Typography by Makito

Proudly published with Hexo