Restic用户数据的映射

5 篇文章 0 订阅
1 篇文章 0 订阅

目录

引言 

Restic数据概念

Restic数据分布

Restic仓库的布局

config文件

keys文件夹

data文件夹

index文件夹

snapshots文件夹

Pack文件格式

实例

创建restic repo及数据备份

通过restic命令查看备份文件内容

解密config文件数据

Pack文件数据分布分析


引言 

本文内容基于restic v0.12.0

从以下几个方面介绍了Restic对于用户数据分布的映射:

  • Restic的基本数据单元概念

    repository,pack,blob,storage ID,snapshot

  • Restic repository下数据分布及格式

    config, keys, data, index, snapshots

  • 通过实例讲解Repository中数据的映射及分布关系

    • Repository的创建及备份

    • 通过Restic命令查看文件信息

    • 解密一个文件

    • 解密一个pack文件中的一个blob

Restic数据概念

仓库(Repository)

存放Restic备份数据的位置,可以是本地文件系统的某个路径,也可以使用远程存储服务, 如sftp server,rest server,存储提供商等。每个仓库相互独立,不同仓库的备份数据相互独立。

v0.12.0中已支持的存储服务包括:aws s3,minio server,Wasabi, Aliyun OSS, OpenStack Swift,Backlbaze B2,Azure Blob Storage,Google Cloud Storage,rclone

数据块(Blob)

加密的数据块及元数据,其中元数据包括长度,SHA-256 哈希信息。

数据块可以存放文件数据(data),也可以存放目录结构数据(tree)。

Blob的大小在512KiB到8MiB之间,因此小于512KB的文件不会被拆分。Restic的实现目标是让Blob平均大小为1MiB。

数据包(Pack)

Restic中的单个数据文件,包括一个或多个Blob,一旦创建不再修改。

一般只创建不删除,仅prune操作会删除不再被引用的数据。

Storage ID

Pack文件的SHA256哈希值,通过这个ID可以在仓库中加载需要的数据文件。Restic将这个ID作为Pack的文件名,也就是文件的SHA256哈希值。Pack文件名即哈希值的设计也可以方便的检验数据文件是否被改动过。

快照(Snapshot)

备份文件/文件夹数据在某一时间点的状态,包括数据的内容和元数据信息。

Restic数据分布

Restic仓库的布局

除了keys文件夹下的文件以外,所有文件都经过AES-256 CTR加密,数据完整性则通过Poly1305-AES (Message Authentication Code, MAC)校验。

config文件

解密后的config文件:

{
 "version": 1,
 "id": "5956a3f67a6230d4a92cefb29529f10196c7d92582ec305fd71ff6d331d6271b",
 "chunker_polynomial": "25b468838dcb75"
}

id是决定repository的唯一性标识。

chunker_polynomial用于把大文件分割成小文件。

keys文件夹

Restic仓库中唯一不使用加密的文件。

光有Key文件无法解密Restic仓库中的任何文件。

配合restic仓库的password才能获得AES key。

同一个仓库可以创建多个key,但是解析出来的masterkey是一样的。

Key文件示例:

$ cat keys/16bce2ed63bed663197fc07b90b38501743ebff7a7f86653cbb77b5f8474a55e  | python -m json.tool
{
  "N": 32768,
  "created": "2021-04-18T22:47:36.620939749+08:00",
  "data": "hIdz1aIXuGWa1ILNma/4Ke+VvHTteKsGdeKBHxFafdgY1htKOekBY8gEZuHWsKn0vyLeXx3VUeVfGWcl3SKHCyiHkwa0k8wPluUdP60EbxtgiT+hNsDz47GcriOmH/CUk/8sEx+GOSiEyl8iTklyq0L9yD/BH3TEUeF9Ry4AUaufh+vEhZbGqe+7OTSs5muOJAvAVCwZAX337HZoeGielw==",
  "hostname": "k8sdev",
  "kdf": "scrypt",
  "p": 5,
  "r": 8,
  "salt": "xj2mRxc9zEEplLGNQJ+jUw62zdx5eqGAHqA2CHpZiiorsrSTFRlUQ8ToYqKvi8r3cNCYpHDRy6PfPdaJ5Nqchw==",
  "username": "root"
}

data文件夹

所有Pack文件存放路径,每个Pack文件可以包含多个加密后的Blob数据,Pack文件的文件名为文件内容的sha256sum值。

index文件夹

存放Pack文件信息,包括每个Pack文件有哪些Blob,Blob原始数据的类型和哈希值信息。

解密后的数据示例:

{
  "packs": [
    {
      "blobs": [
        {
          "id": "c18d92ccf1b69fa1c0a2854e6e2967c8f374215f33fd4df6bab838e35c4174a6",
          "length": 60,
          "offset": 0,
          "type": "data"
        }
      ],
      "id": "984a09e24565201fd71de9276d9050e17e408e25adf6beefa7accf2039c0040c"
    },
    {
      "blobs": [
        {
          "id": "81963a42c7d01564b7893e17d7ff30a00f5f745252b8b54ce2aec27be78b970f",
          "length": 392,
          "offset": 0,
          "type": "tree"
        },
        {
          "id": "01a2dd0625bbc6ec8676d1166010e74198e06437717a333b9239ed29b0bae583",
          "length": 489,
          "offset": 771,
          "type": "tree"
        },
        {
          "id": "6bd1574077efd77fe5705bbaca090e69c1c77967faac198b03aa85dde9fe703d",
          "length": 379,
          "offset": 392,
          "type": "tree"
        }
      ],
      "id": "25d5ac46e385ea523ee96571290d70d1381eb3be5c897f775d770a49a9c2bcba"
    }
  ]
}

snapshots文件夹

存放所有snapshot的信息

解密后的数据示例:

{
  "hostname": "k8sdev",
  "paths": [
    "/mnt/data/test"
  ],
  "time": "2021-04-18T23:02:07.59377716+08:00",
  "tree": "01a2dd0625bbc6ec8676d1166010e74198e06437717a333b9239ed29b0bae583",
  "username": "root"
}

Pack文件格式

  • 数据Blob格式

图片

e.g. openssl解密aes-256-ctr

echo <ciphertext> | openssl aes-256-ctr -d -iv <IV> -K <AES Key>

  • Header格式

图片

Type_Blob: 1 byte

Length: 4 bytes

Hash: 32 byte

通过定长4Byte的Header_Length得到加密后的Header长度,解密后的Header存放Pack中所有Blob的元数据信息,包括Blob类型(数据 / 树),加密后Blob长度,原始Blob数据哈希值。通过Header可以获取所有Blob信息。

实例

创建restic repo及数据备份

$ restic -r s3:http://localhost:31605/mapping/test init

创建restic repo时会要求用户输入密码,此密码将用于产生restic的masterkey。而masterkey用于解密数据。因此如果没有repo的password,repo中的所有数据都无法解析。

$ echo "Test Restic Data @2021.4.18" > /mnt/data/test

$ restic -r s3:http://localhost:31605/mapping/restictest backup /mnt/data/test

调试时可以通过全局变量RESTIC_PASSWORD和RESTIC_REPOSITORY来简化命令行输入,生产环境不建议把信息放在全局变量中。

通过restic命令查看备份文件内容

  • 查找快照ID

$ restic -r s3:http://localhost:31605/mapping/restictest find test
repository 293f88eb opened successfully, password is correct
found 2 old cache directories in /root/.cache/restic, run restic cache --cleanup to remove them
Found matching entries in snapshot f30f99e6 from 2021-04-18 23:02:07
/mnt/data/test

或:

$ restic -r s3:http://localhost:31605/mapping/restictest snapshots
repository 293f88eb opened successfully, password is correct
found 2 old cache directories in /root/.cache/restic, run restic cache --cleanup to remove them
ID     Time         Host     Tags     Paths
---------------------------------------------------------------------
f30f99e6 2021-04-18 23:02:07  k8sdev          /mnt/data/test
---------------------------------------------------------------------
1 snapshots

获得snapshot id:f30f99e6

  • 打印快照信息树

$ restic -r s3:http://localhost:31605/mapping/restictest cat snapshot f30f99e6
repository 293f88eb opened successfully, password is correct
found 2 old cache directories in /root/.cache/restic, run restic cache --cleanup to remove them
{
 "time": "2021-04-18T23:02:07.59377716+08:00",
 "tree": "01a2dd0625bbc6ec8676d1166010e74198e06437717a333b9239ed29b0bae583",
 "paths": [
  "/mnt/data/test"
 ],
 "hostname": "k8sdev",
 "username": "root"
}

信息树:文件夹mnt -> 文件夹data -> 文件test

$ restic -r s3:http://localhost:31605/mapping/restictest cat blob 01a2dd0625bbc6ec8676d1166010e74198e06437717a333b9239ed29b0bae583 | python -m json.tool
{
  "nodes": [
    {
...
      "name": "mnt",
      "subtree": "6bd1574077efd77fe5705bbaca090e69c1c77967faac198b03aa85dde9fe703d",
      "type": "dir",
...
    }
  ]
}
$ restic -r s3:http://localhost:31605/mapping/restictest cat blob 6bd1574077efd77fe5705bbaca090e69c1c77967faac198b03aa85dde9fe703d | python -m json.tool
{
  "nodes": [
    {
...
      "name": "data",
      "subtree": "81963a42c7d01564b7893e17d7ff30a00f5f745252b8b54ce2aec27be78b970f",
      "type": "dir",
...
    }
  ]
}
$ restic -r s3:http://localhost:31605/mapping/restictest cat blob  81963a42c7d01564b7893e17d7ff30a00f5f745252b8b54ce2aec27be78b970f | python -m json.tool
{
  "nodes": [
    {
      "content": [
        "c18d92ccf1b69fa1c0a2854e6e2967c8f374215f33fd4df6bab838e35c4174a6"
      ],
...
      "name": "test",
      "type": "file",
...                   
    }
  ]
}
  • 打印备份文件信息

$ restic -r s3:http://localhost:31605/mapping/restictest cat blob  c18d92ccf1b69fa1c0a2854e6e2967c8f374215f33fd4df6bab838e35c4174a6
repository 293f88eb opened successfully, password is correct
found 2 old cache directories in /root/.cache/restic, run restic cache --cleanup to remove them
Test Restic Data @2021.4.18

解密config文件数据

  • 进入Restic仓库路径

$ pwd
/var/lib/kubelet/pods/8a4e972b-890c-430b-98a5-cea49b9e8c19/volumes/kubernetes.io~empty-dir/storage/mapping/restictest
  • 取得AES Key

$ restic -r s3:http://localhost:31605/mapping/restictest cat masterkey
repository 293f88eb opened successfully, password is correct
found 2 old cache directories in /root/.cache/restic, run restic cache --cleanup to remove them
{
 "mac": {
  "k": "RGylXomY4XuPErQ2akibdQ==",
  "r": "5PqZDNhWXA2wb0QCBGuACg=="
 },
 "encrypt": "AKQMbh9rlNgi00iZdSIeeYq9e+z/Tf/LO/2LomA6/Xs="
}

$ echo "AKQMbh9rlNgi00iZdSIeeYq9e+z/Tf/LO/2LomA6/Xs=" | base64 -d | xxd -p -c32
00a40c6e1f6b94d822d3489975221e798abd7becff4dffcb3bfd8ba2603afd7b
  • 从config文件的头16 Byte获取IV值

$ cat config | head -c 16 | xxd -p
23c13620928f7abd72a8374b88142dff
  • 解密config文件

$ cat config | head -c -16 | tail -c +17 | openssl aes-256-ctr -d -iv 23c13620928f7abd72a8374b88142dff -K 00a40c6e1f6b94d822d3489975221e798abd7becff4dffcb3bfd8ba2603afd7b | python -m json.tool
{
  "chunker_polynomial": "25cb5ee4aa0503",
  "id": "293f88eb74731e997b413d08462210e09f2266f641ace775f835b005a651b73a",
  "version": 1
}

Pack文件数据分布分析

图片

  • 解析Pack文件Header长度:

root@k8sdev restic $ cat data/98/984a09e24565201fd71de9276d9050e17e408e25adf6beefa7accf2039c0040c | tail -c -4 | hexdump -C
00000000  45 00 00 00                    |E...|
00000004

4 Bytes小端存储 => 4 * 16 + 5 = 69 bytes

69 bytes中,头16 bypes IV, 最后16 bytes MAC

  • 截取pack文件中Header数据

$ cat data/98/984a09e24565201fd71de9276d9050e17e408e25adf6beefa7accf2039c0040c | head -c -4 | tail -c 69 > /tmp/encryted_header

  • 解密Header数据

IV信息:

$ cat /tmp/encryted_header | head -c 16 | xxd -p
dbcff665ec849258563837f9144c6798

解密后Header信息:

图片

Restic代码中Header信息的定义:

// headerEntry describes the format of header entries. It serves only as
// documentation.
type headerEntry struct {
    Type   uint8
    Length uint32
    ID     restic.ID
}

所以,在这个pack文件的header里的信息是:

  1. 只有一个blob

  2. 是一个数据blob

  3. 加密后的Blob数据长度:3c => 3 * 16 + 12 = 60

  4. 原始数据的sha256 哈希值是 c18d92ccf1b69fa1c0a2854e6e2967c8f374215f33fd4df6bab838e35c4174a6

对比实际数据:

$ echo "Test Restic Data @2021.4.18" | sha256sum
c18d92ccf1b69fa1c0a2854e6e2967c8f374215f33fd4df6bab838e35c4174a6  -

解密数据:

IV值:

$ cat data/98/984a09e24565201fd71de9276d9050e17e408e25adf6beefa7accf2039c0040c | head -c 16 | xxd -p
b6a4171e2da7239c30ce41f8707eaef7

解密:

$ cat data/98/984a09e24565201fd71de9276d9050e17e408e25adf6beefa7accf2039c0040c | head -c -89 | tail -c +17 | openssl aes-256-ctr -d -iv b6a4171e2da7239c30ce41f8707eaef7 -K 00a40c6e1f6b94d822d3489975221e798abd7becff4dffcb3bfd8ba2603afd7b
Test Restic Data @2021.4.18

<== 其中 head -c 89, 89 = MAC长度(16) + 加密后header长度(69) + header长度(4)

目标是截取数据中红色部分:

图片

综上,给定一个想要查看的文件和snapshot的时间点,Restic可以通过snapshot及文件所在目录找到文件信息,从而获取文件信息content中Blob的哈希值,通过index找到每一个数据blob对应的pack和偏移量,或者直接从pack的header信息中获得改信息。解密后即可获得文件的原始数据。     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值