TiUP 镜像参考指南

    • 本地磁盘上的目录:用于服务本地的 TiUP 客户端,文档中将称之为本地镜像
    • 基于远程的磁盘目录启动的 HTTP 镜像:服务远程的 TiUP 客户端,文档中将称之为远程镜像

    镜像可以通过以下两种方式创建:

    • 通过命令 从零生成
    • 通过命令 tiup mirror clone 从已有镜像克隆

    在创建镜像之后,可以通过 tiup mirror 相关命令来给镜像添加组件或删除组件,无论是通过何种方式更新镜像,TiUP 都不会从镜像中删除任何文件,而是通过增加文件并分配新版本号的方式更新。

    一个典型的镜像目录结构如下:

    • commits 目录是在更新镜像过程中产生的日志,用于回滚镜像,磁盘空间不足时可以定期删除旧的文件夹
    • keys 文件夹中存放的私钥较敏感,建议单独妥善保管

    在 TiUP 镜像中,根证书用于存放其他元信息文件的公钥,每次获取到任何元信息文件(*.json)都需要根据其文件类型(root,index,snapshot,timestamp)在当前已安装的 root.json 中找到对应的公钥,然后用公钥验证其签名是否合法。

    根证书文件格式如下:

    1. {
    2. "signatures": [ # 每个元信息文件有一系列的签名,签名由该文件对应的几个私钥签出
    3. {
    4. "keyid": "{id-of-root-key-1}", # 第一个参与签名私钥的 ID,该 ID 由私钥对应的公钥内容哈希得到
    5. "sig": "{signature-by-root-key-1}" # 该私钥对此文件 signed 部分签名的结果
    6. },
    7. ...
    8. {
    9. "keyid": "{id-of-root-key-N}", # 第 N 个参与签名私钥的 ID
    10. "sig": "{signature-by-root-key-N}" # 该私钥对此文件 signed 部分签名的结果
    11. }
    12. ],
    13. "signed": { # 被签名的部分
    14. "_type": "root", # 该字段说明本文件的类型,root.json 的类型就是 root
    15. "expires": "{expiration-date-of-this-file}", # 该文件的过期时间,过期后客户端会拒绝此文件
    16. "roles": { # root.json 中的 roles 用来记录对各个元文件签名的密钥
    17. "{role:index,root,snapshot,timestamp}": { # 涉及的元文件类型包括 index, root, snapshot, timestamp
    18. "keys": { # 只有 keys 中记录的密钥签名才是合法的
    19. "{id-of-the-key-1}": { # 用于签名 {role} 的第 1 个密钥 ID
    20. "keytype": "rsa", # 密钥类型,目前固定为 rsa
    21. "keyval": { # 密钥的 payload
    22. "public": "{public-key-content}" # 表示公钥内容
    23. },
    24. "scheme": "rsassa-pss-sha256" # 目前固定为 rsassa-pss-sha256
    25. },
    26. "{id-of-the-key-N}": { # 用于签名 {role} 的第 N 个密钥 ID
    27. "keytype": "rsa",
    28. "keyval": {
    29. "public": "{public-key-content}"
    30. "scheme": "rsassa-pss-sha256"
    31. }
    32. },
    33. "url": "/{role}.json" # url 是指该文件的获取地址,对于 index 文件,需要在前面加上版本号,即 /{N}.index.json
    34. }
    35. },
    36. "spec_version": "0.1.0", # 本文件遵循的规范版本,未来变更文件结构需要升级版本号,目前为 0.1.0
    37. "version": {N} # 本文件的版本号,每次更新文件需要创建一个新的 {N+1}.root.json,并将其 version 设置为 N + 1
    38. }
    39. }

    索引文件记录了镜像中所有的组件以及组件的所有者信息。

    其格式如下:

    其格式如下:

    1. {
    2. "signatures": [ # 该文件的签名
    3. {
    4. "keyid": "{id-of-index-key-1}", # 第一个参与签名的 key 的 ID
    5. "sig": "{signature-by-index-key-1}", # 该私钥对此文件 signed 部分签名的结果
    6. },
    7. ...
    8. {
    9. "keyid": "{id-of-root-key-N}", # 第 N 个参与签名私钥的 ID
    10. "sig": "{signature-by-root-key-N}" # 该私钥对此文件 signed 部分签名的结果
    11. }
    12. ],
    13. "signed": {
    14. "_type": "component", # 指示该文件类型
    15. "description": "{description-of-the-component}", # 该组件的描述信息
    16. "expires": "{expiration-date-of-this-file}", # 该文件的过期时间,过期后客户端会拒绝此文件
    17. "id": "{component-id}", # 该组件的 ID,具有全局唯一性
    18. "nightly": "{nightly-cursor}", # nightly 游标,值为最新的 nightly 的版本号(如 v5.0.0-nightly-20201209)
    19. "platforms": { # 该组件支持的平台(如 darwin/amd64,linux/arm64 等)
    20. "{platform-pair-1}": {
    21. "{version-1}": { # Semantic Version 版本号(如 v1.0.0 等)
    22. "dependencies": null, # 用于约定组件之间的依赖关系,该字段尚未使用,固定为 null
    23. "entry": "{entry}", # 入口二进制文件位于 tar 包的相对路径
    24. "hashs": { # tar 包的 checksum,我们使用 sha256 和 sha512
    25. "sha256": "{sum-of-sha256}",
    26. "sha512": "{sum-of-sha512}",
    27. },
    28. "length": {length-of-tar}, # tar 包的长度
    29. "url": "{url-of-tar}", # tar 包的下载地址
    30. "yanked": {bool} # 该版本是否已被禁用
    31. }
    32. ...
    33. "{platform-pair-N}": {
    34. ...
    35. }
    36. },
    37. "spec_version": "0.1.0", # 本文件遵循的规范版本,未来变更文件结构需要升级版本号,目前为 0.1.0
    38. "version": {N} # 本文件的版本号,每次更新文件需要创建一个新的 {N+1}.{component}.json,并将其 version 设置为 N + 1
    39. }

    快照文件记录了各个元文件当前的版本号。

    其格式如下:

    时间戳文件记录了当前快照 checksum。

    1. {
    2. "signatures": [ # 该文件的签名
    3. {
    4. "keyid": "{id-of-index-key-1}", # 第一个参与签名的 key 的 ID
    5. "sig": "{signature-by-index-key-1}", # 该私钥对此文件 signed 部分签名的结果
    6. },
    7. ...
    8. {
    9. "keyid": "{id-of-root-key-N}", # 第 N 个参与签名私钥的 ID
    10. "sig": "{signature-by-root-key-N}" # 该私钥对此文件 signed 部分签名的结果
    11. }
    12. ],
    13. "signed": {
    14. "_type": "timestamp", # 指示该文件类型
    15. "expires": "{expiration-date-of-this-file}", # 该文件的过期时间,过期后客户端会拒绝此文件
    16. "meta": { # snapshot.json 的信息
    17. "/snapshot.json": {
    18. "hashes": {
    19. "sha256": "{sum-of-sha256}" # snapshot.json 的 sha256
    20. },
    21. "length": {length-of-json-file} # snapshot.json 的长度
    22. }
    23. },
    24. "version": {N} # 本文件的版本号,每次更新文件需要覆盖 timestamp.json,并将其 version 设置为 N + 1

    客户端通过以下逻辑保证从镜像下载到的文件是安全的:

    • 客户端安装时随 binary 附带了一个 root.json
    • 客户端运行时以已有的 root.json 为基础,做如下操作:
      1. 获取 root.json 中的 version,记为 N
      2. 向镜像请求 {N+1}.root.json,若成功,使用 root.json 中记录的公钥验证该文件是否合法
    • 向镜像请求 timestamp.json,并使用 root.json 中记录的公钥验证该文件是否合法
    • 检查 timestamp.json 中记录的 snapshot.json 的 checksum 和本地的 snapshot.json 的 checksum 是否吻合
      • 若不吻合,则向镜像请求最新的 snapshot.json 并使用 root.json 中记录的公钥验证该文件是否合法
    • 对于 index.json 文件,从 snapshot.json 中获取其版本号 N,并请求 {N}.index.json,然后使用 root.json 中记录的公钥验证该文件是否合法
    • 对于组件(如 tidb.json,tikv.json),从 snapshot.json 中获取其版本号 N,并请求 {N}.{component}.json,然后使用 index.json 中记录的公钥验证该文件是否合法
    • 对于组件 tar 文件,从 {component}.json 中获取其 url 及 checksum,请求 url 得到 tar 包,并验证 checksum 是否正确