此处的文档已经归档,请移步 新的文档站 ,非常感谢您的理解与支持。
Skip to content

部署 MiSSO ​

项目已经归档

喵窝在 Misskey 2023.9.0 引入的 OAuth2 上衍生出了一套基本可用的认证方案,这个项目也正式宣布归档,此处的文档仅供如果需要类似外部 OAuth2 关联身份认证的实例参考。

请注意我们不保证这个项目在未来也一直能用,如果遇到使用上的问题,您可能需要考虑二次开发。例如,因 Hydra 更新了数据接口,目前该 MiSSO 仓库支持的最大 Hydra 版本号为 v2.1.1 。

项目仓库 ​

您可以参见我们 GitHub 上的仓库: MiSSO - GitHub

开发工作 ​

技术栈 ​

本体的开发使用的是 Go + Gin + go-redis + zap

另外引入了 Hydra 作为 OAuth2 管理核心。

二次开发 ​

如果您对其中任何的代码感到不满意,或是想加入自己喜欢的部分,欢迎随时 fork 一份代码进行修改。

目前存在的问题

现在的 MiSSO 对登录状态的管理没有一个较好的方法,解决方案比较妥协折中(记住,但是只记住 10 分钟),不知道有没有什么更好的思路。

国际化(i18n)支持 ​

因为该项目以喵窝私用为主,暂时没有相关支持的计划。如果您有兴趣研究其他语言的翻译与实现,我们也非常乐意一同研究国际化的支持。

工作流程 ​

MiSSO 工作流程

工作原理 ​

主要参考了 hydra-login-consent-node 这个样例项目

OAuth2 相关的管理都是 Hydra 在负责, MiSSO 本体仅处理 4 种请求:

  1. 用户登录
  2. 用户同意信息授权
  3. 第三方应用获取用户信息
  4. 第三方应用登出

其中针对用户登录和第三方应用登出的请求进行了简化,即直接默认用户记住登录状态、用户确认登出。

在用户登录处,使用了 Misskey 的 应用 Auth 授权模式。

准备工作 ​

域名 ​

因为该连接系统需要让用户来回跳转,您需要准备一个域名用于完成用户请求。

这个域名会被前端表现出来,如果需要注重域名的整洁度,您需要分配一个干净规整的域名。

例如,喵窝的 MiSSO 部署使用的域名是 sso.nya.one 。

严格来讲,您将 MiSSO 与 Hydra 分别放置在不同的域名下也没有问题,但是考虑到单个域名可以复用连接带来的性能优势,且其本身功能一体、路由不冲突,部署在一起或许是一种不错的方案。

请注意,由于开发时的便利考虑,不推荐您将服务部署在子目录下(可能会导致各种异常问题的出现),请使用根目录。

服务器 ​

MiSSO 构建后为二进制格式的可执行文件,您可以自由部署在各种虚拟私有服务器(VPS)或是实体服务器(Dedicated Server)等平台上。

需要注意的是,一些传统的面板服务器可能不支持运行 Go 语言构建的这种可执行文件,请与您的服务商联系取得确认,避免产生误会导致相关工期的延误。

我们提供了基于 Docker 的容器配置方案,以方便您的使用,您可以参考项目文档中给出的 docker-compose.yml 文件进行构建部署。

请自行构建镜像

由于 ncr.pub 服务已经停止,请不要使用文件中配置的 ncr.pub/nyaone/misso 镜像,以避免可能出现的意外情况。

Misskey 应用 ​

您需要参照 开发者创建应用 中描述的步骤,创建一个应用并记录下它的应用私钥,以从 Misskey 获得授权的用户信息。

其中, callbackUrl 请设置成 MiSSO 的 /misskey 路径。

样例

喵窝创建的应用请求体如下:

json
{
    "name": "MiSSO",
    "description": "使用 Misskey 来一键登录",
    "callbackUrl": "https://sso.nya.one/misskey",
    "permission": []
}

参数配置 ​

在仓库的 .config 目录下提供了针对 Hydra 和 MiSSO 的配置样例,可供您参考使用。

Hydra ​

Hydra 具体的配置参数请参见 Configuration | Ory ,此处的描述可能会存在时效性问题,因而不再赘述。

需要注意的是 urls 部分的配置,需要指向您生产环境中具体使用的域名及路径,其中生产环境下的 self.issuer 要求使用 https 协议以确保安全。

yaml
urls:
  self:
    issuer: Hydra 自己
  consent: MiSSO 的 /consent
  login: MiSSO 的 /login
  logout: MiSSO 的 /logout
样例

喵窝部署时使用的参数指定如下:

yaml
dsn: "postgres://nyaone:misso+nya.1@db:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4"

serve:
  cookies:
    same_site_mode: Strict

urls:
  self:
    issuer: https://sso.nya.one/
  consent: https://sso.nya.one/consent
  login: https://sso.nya.one/login
  logout: https://sso.nya.one/logout

secrets:
  system:
    - "xxxxxxxxxxxxxxxxxxxxxx"

MiSSO ​

MiSSO 的配置参数解释如下:

yaml
system:
  debug: 是否开启调试模式,生产环境中请保持此项关闭
  redis: 程式 Redis 数据库的连接地址
misskey:
  instance: 您的目标实例地址,请使用公开域名
  application:
    secret: Misskey 应用的私钥
hydra:
  admin_url: 指向 Hydra 的 Admin 端口的链接
time: (这里的单位全都是秒)
  request_valid: 登录和同意请求的有效时间,超时则认为是无效的请求(需要大于 0 )
  login_remember: 记住用户登录状态的时间( 0 表示不记住,每次授权都跳转 Misskey 请求验证)
  consent_remember: 记住用户同意应用使用信息时记住的时间( 0 表示永远记住,再也不询问,除非应用请求了更多权限)
  userinfo_cache: 用户信息的缓存时间(需要大于 0 )
  1. misskey.instance 这个参数不能包含 schema 头,因为它会被用作生成用户邮箱的域名部分。这个邮箱可以使用 email2misskey 之类的工具来让其真的有效。
  2. 请注意 login_remember 和 consent_remember 设置成 0 的效果并不一致!不要弄反了。
样例

喵窝部署时使用的参数指定如下:

yaml
system:
  debug: false
  redis: "redis://redis:6379/0"
misskey:
  instance: "nya.one"
  application:
    secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
hydra:
  admin_url: "http://hydra:4445"
time:
  request_valid: 3600
  login_remember: 600
  consent_remember: 0
  userinfo_cache: 3600

部署与启动 ​

我们提供了使用 Docker 与 Docker-Compose 进行部署的方案供您参考。

  1. 上传 docker-compose.yml 与您配置完成的 hydra.yml 和 misso.yml 配置文件至工作目录中
  2. 调整 docker-compose.yml 文件中的卷映射,确保宿主机内的配置文件位置正确(例如在 .config 目录下,或是自行调整)
  3. 使用 docker-compose pull 命令拉取最新的镜像
  4. 使用 docker-compose up -d 命令启动实例
  5. 根据您的网页服务器需求,配置对应的反向代理规则。

健康状况检查

MiSSO 有自己的健康状况检查端点 /healthcheck , Hydra 也有自己的 /health/ready ,您可能需要分别设置健康监控。

MiSSO 运行使用的端口

MiSSO 默认使用 Gin 自带的端口配置,也就是 8080 端口。但考虑到这个端口可能也会被其他服务占用,这里给出使用自定义端口的方案。

  • 如果您使用 docker 容器启动,且工作在非 host 网络上,那么只需要修改向外映射的端口号就可以。

  • 如果您使用二进制模式启动,或是让容器工作在 host 网络上,您可以通过指定环境变量 PORT 为您需要的端口号,来让 Gin 工作在您指定的端口上。

MiSSO 的配置文件位置

MiSSO 默认会尝试读取当前工作目录下的 config.yml 作为配置文件。

如果您使用二进制方式运行,且的配置文件并不在当前工作目录下,您可以使用 CONFIG_FILE_PATH 这个环境变量来指定目标配置文件的位置。

如果您使用我们提供的 docker-compose 方式运行,那么不需要做其他的调整,我们已经将目录映射过去了。

反向代理配置 ​

喵窝在部署时是将 MiSSO 与 Hydra 部署在同一域名下的,以保证用户不会被来回跳转影响体验。喵窝使用的 Web 服务器是 nginx ,配置文件如下,可以供您参考使用:

upstream misso_backend {
    server 127.0.0.1:8080;
    keepalive 64;
}

upstream hydra_public_backend {
    server 127.0.0.1:4444;
    keepalive 64;
}

server {
    # nginx listens to this
    listen 443 ssl http2;

    # the virtual host name of this
    server_name sso.nya.one;

    ssl_certificate     /etc/nginx/ssl/nyaone/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/nyaone/key.pem;

    # Cloudflare Pull-authenticate
    ssl_client_certificate /etc/nginx/ssl/cf-pull.pem;
    ssl_verify_client on;

  ## MiSSO

    location / {
        proxy_pass http://misso_backend;

        include conf.d/shared/revproxy.conf;
        proxy_store off;
    }

  ## Hydra

    location ~* ^(\/.well-known|\/oauth2|\/health\/) {
        proxy_pass http://hydra_public_backend;

        include conf.d/shared/revproxy.conf;
        proxy_store off;
    }

}

其中 conf.d/shared/revproxy.conf 为:

proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header   Upgrade             $http_upgrade;
proxy_set_header   Connection          "upgrade";
proxy_set_header   Host                $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;
proxy_set_header   X-Forwarded-Host    $host;
proxy_set_header   X-Forwarded-Port    $server_port;

OAuth2 客户端创建 ​

该部分属于 Hydra 的 API 范围,这里只做简单介绍,具体的细节信息请参照 Create OAuth 2.0 Client 中的描述。

首先,我们需要能访问到 Hydra 的 admin API 。默认配置下 admin API 工作在 4445 端口,而我们的 docker-compsoe.yml 文件中出于安全因素的考虑,并没有暴露这个端口。所以我们调整 hydra 的 ports 段,增加 "4445:4445" 的端口映射选项,使其暂时暴露在公网环境下以便操作。

暴露在公网

这是一个极其危险的做法,此处只是为了偷懒。

  1. 一种更为推荐的做法是使用 "127.0.0.1:4445:4445" 的内网映射选项,仅接收来自本机的请求,然后使用命令行请求发送数据;或者在反向代理服务器上设置验证,由反向代理服务器来保证数据的安全。
  2. 另一种更安全的做法是不暴露端口,而是使用 docker-compose exec hydra sh 直接进入容器执行操作。容器中有 wget 可以使用。
  3. 但我实在太懒了。

センパーセントの安全

Hydra 在启动时候可以指定其工作模式,默认提供的 serve all 表示同时开启 admin 和 public 端口的监听。

如果您使用的是分布式系统的方案(即通过共享数据库在多个 Hydra 之间同步数据),设置公开节点的启动命令为 serve public 能彻底关闭 admin 模式的端口。对于连接 MiSSO 使用的 Hydra ,则可以设置 serve admin 来关闭不必要的 public 端口。

像喵窝这样单点式部署的话就还是算了。

当端口打开后,我们就可以创建客户端了。

使用的 API 地址为 /admin/clients ,该接口为使用 POST 方式提交 json 数据。

例如,我们创建一个用于给 Matrix 使用的客户端(其中的 scope 请参见下文 关于 scope 的设置):

json
{
    "client_name": "Matrix",
    "scope": "id name username",
    "redirect_uris": [
        "https://matrix.nya.one/_synapse/client/oidc/callback"
    ]
}

可以得到如下的返回结果:

json
{
    "client_id": "uuuuuuuu-uuuu-iiii-dddd-dddddddddddd",
    "client_name": "Matrix",
    "client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
    "redirect_uris": [
        "https://matrix.nya.one/_synapse/client/oidc/callback"
    ],
    "grant_types": null,
    "response_types": null,
    "scope": "id name username",
    "audience": [],
    "owner": "",
    "policy_uri": "",
    "allowed_cors_origins": [],
    "tos_uri": "",
    "client_uri": "",
    "logo_uri": "",
    "contacts": null,
    "client_secret_expires_at": 0,
    "subject_type": "public",
    "jwks": {},
    "token_endpoint_auth_method": "client_secret_basic",
    "userinfo_signed_response_alg": "none",
    "created_at": "2023-01-25T03:54:59Z",
    "updated_at": "2023-01-25T03:54:58.593996Z",
    "metadata": {},
    "registration_access_token": "ory_at_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "registration_client_uri": "https://sso.nya.one/oauth2/register/uuuuuuuu-uuuu-iiii-dddd-dddddddddddd",
    "authorization_code_grant_access_token_lifespan": null,
    "authorization_code_grant_id_token_lifespan": null,
    "authorization_code_grant_refresh_token_lifespan": null,
    "client_credentials_grant_access_token_lifespan": null,
    "implicit_grant_access_token_lifespan": null,
    "implicit_grant_id_token_lifespan": null,
    "jwt_bearer_grant_access_token_lifespan": null,
    "refresh_token_grant_id_token_lifespan": null,
    "refresh_token_grant_access_token_lifespan": null,
    "refresh_token_grant_refresh_token_lifespan": null
}

其中的 client_id 和 client_secret 就是我们需要的 OAuth2 Client 用于连接使用的凭证,请妥善保管,避免泄露。

Hydra 支持传入的参数还有很多,具体可以参见上文提到的文档。

关于 scope 的设置 ​

从现在已经发布的 v0.2 版本开始, scope 将会被定义为 Misskey 返回的用户信息 Key-Value 结构体中的 key 值,用以指引需要使用的字段。

例如, Misskey 的 i 端点返回的结构体形式如下:

json
{
    "id": "8837yxdz1d",
    "name": "糖喵💕🍭(◍•ᴗ•◍)✧*",
    "username": "Candinya",
    "host": null,
    "avatarUrl": "https://file.nya.one/misskey/thumbnail-a4fa0124-cdc3-4b58-b0bd-9debfaeb92d2.webp",
    "avatarBlurhash": "yVPPT0oz}kxBH@r=KM=:RlaIsnohVZx[nyoIbcNJRPjcxt=]s,ESM}xaa1Io=Vn~M|oyk8bH%LV|R.R:s+WANHRiM~j]rrs+ogn+bc",
    "isBot": false,
    "isCat": false,
    "onlineStatus": "online",
    // ...还有很多其他字段
}

其中 MiSSO 会将用户的 用户名@实例 组合成用户的邮箱地址 email ,如果获取全量数据时,就会得到这样的结果:

diff
{
    "id": "8837yxdz1d",
    "name": "糖喵💕🍭(◍•ᴗ•◍)✧*",
    "username": "Candinya",
    "host": null,
    "avatarUrl": "https://file.nya.one/misskey/thumbnail-a4fa0124-cdc3-4b58-b0bd-9debfaeb92d2.webp",
    "avatarBlurhash": "yVPPT0oz}kxBH@r=KM=:RlaIsnohVZx[nyoIbcNJRPjcxt=]s,ESM}xaa1Io=Vn~M|oyk8bH%LV|R.R:s+WANHRiM~j]rrs+ogn+bc",
    "isBot": false,
    "isCat": false,
    "onlineStatus": "online",
    // ...还有很多其他字段
+    "email": "Candinya@nya.one"
}

那么当一个应用申请的 scope 为 id name username email 时, MiSSO 返回的结果将会如下:

json
{
    "id": "8837yxdz1d",
    "name": "糖喵💕🍭(◍•ᴗ•◍)✧*",
    "username": "Candinya",
    "email": "Candinya@nya.one"
}

这样一方面可以将具体内容格式的处理交给客户端去进行,避免 MiSSO 中硬编码强类型导致出现跨版本兼容性困难; 另一方面也能实现更细粒度的权限管理,尽可能保证用户隐私信息不被未授权的第三方应用获取,以确保用户的信息与使用安全。

出于安全与适应性因素考虑,我们推荐您在升级 v0.2 开始的版本时,重新创建所有应用的 OAuth2 客户端。

喵窝使用的两个 OAuth2 客户端配置参数

Grafana:

json
{
  "client_name": "Grafana",
  "scope": "name username email",
  "redirect_uris": [
    "https://stats.nya.one/login/generic_oauth"
  ],
  "logo_uri": "https://stats.nya.one/public/img/grafana_icon.svg"
}

Matrix:

json
{
    "client_name": "Matrix",
    "scope": "id name username",
    "redirect_uris": [
        "https://matrix.nya.one/_synapse/client/oidc/callback"
    ]
}

安全生产小贴士

完成 OAuth2 客户端的创建工作之后,请记得关闭刚刚打开的管理员端口。

OAuth2 客户端配置 ​

此处是一些应用自己独有的配置,虽然基本都大差不差,重要的基本就是这几个参数:

关键参数来源
OAuth2 客户端的凭证( Client ID 和 Client Secret )刚刚创建的 OAuth2 客户端
OAuth2 的端点( auth 端点和 token 端点)Hydra 负责的部分,分别是 /oauth2/auth 和 /oauth2/token
使用 Access Token 拉取信息的端点MiSSO 负责的部分,按照现行的设计是 /userinfo

如果您使用的是喵窝这种两个服务共享一个域名的方案,那么它们使用的域名就都一样。

针对喵窝正在使用的 Grafana 和 Matrix Synapse ,我们依然列出对应的样例以供您参考:

Grafana ​

ini
[auth.generic_oauth]
name = MiSSO
icon = signin
enabled = true
client_id = uuuuuuuu-uuuu-iiii-dddd-dddddddddddd
client_secret = xxxxxxxxxxxxxxxxxxxxxxxxxx
empty_scopes = false
scopes = name username email
auth_url = https://sso.nya.one/oauth2/auth
token_url = https://sso.nya.one/oauth2/token
api_url = https://sso.nya.one/userinfo
allow_sign_up = true
team_ids =

Matrix Synapse ​

yaml
oidc_providers:
  - idp_id: misso
    idp_name: MiSSO
    idp_brand: "one.nya.misso"  # optional: styling hint for clients
    discover: false
    issuer: "https://nya.one/@Candinya"
    client_id: uuuuuuuu-uuuu-iiii-dddd-dddddddddddd
    client_secret: xxxxxxxxxxxxxxxxxxxxxxxxxx
    authorization_endpoint: "https://sso.nya.one/oauth2/auth"
    token_endpoint: "https://sso.nya.one/oauth2/token"
    userinfo_endpoint: "https://sso.nya.one/userinfo"
    scopes: ["id", "name", "username"]
    user_mapping_provider:
      config:
        subject_claim: "id"
        localpart_template: "{{ user.username }}"
        display_name_template: "{{ user.name }}"
sso:
  client_whitelist:
    - https://im.nya.one/
password_config:
  enabled: false

基于 CC-BY-SA-4.0 授权