IPFS 构建去中心化网站
XiaO / 2021-01-23
IPFS 简介
IPFS,又称“星际文件系统”。简单点说,它是一个点对点的分布式文件系统(和比特币技术一样),通过底层协议,可以让存储在 IPFS 系统上的文件,在全世界任何一个地方快速获取,且不受防火墙的影响(无需网络代理)。
现在所使用的互联网协议被称作——超文本协议 HTTP,是一种超中心化特性。当从互联网上下载文件或者是浏览网页,一次只能从一个数据中心获取你所需要的资料。如果这个数据中心出现故障,或者被限制或是攻击,就会出现文件丢失或者网页无法打开的问题。
HTTP 的痛点
HTTP 协议已经用了 20 年的历史,从 HTTP 1.0 到现在的 HTTP5,网页的展示越来越美观丰富,但它背后的 Browser/Server 模式是从来没有改变过。
- HTTP效率低下,服务器成本昂贵
- 历史文件被删除
- 网络应用太依赖骨干网
- 中心化的网络限制了机会
IPFS 特质
- 互联网信息永久存储
IPFS 像一个分布式存储网络,任何存储在系统里的资源,包括文字、图片、声音、视频,以及网站代码,通过 IPFS 进行哈希运算后,都会生成唯一的地址。今后,你只要通过这个地址就可以打开它们。并且这个地址是可以被分享的。
而由于加密算法的保护,该地址具备了不可篡改和删除的特性(在某种意义上,如果破解密码还是有可能被篡改或删除,但概率极低)。所以,一旦数据存储在 IPFS 网络中,它就会是永久性的。即便我们把该站点撤销,只要存储该站点信息的网络依然存在,该网页就可以被正常访问。存储站点的分布式网络越多,它的可靠性也就越强。
IPFS 存储的一般是公共信息,普通大众都可以获得的。有一种说法认为,如果 IPFS 完全取代 HTTP,那么此后,人类历史将会被永久保存,且不会被篡改。这也就意味着,人类所做的每一件事情都会被记录,不管是正确的、抑或是错误的。
- 解决过度冗余问题,实现共享经济
IPFS 会把存储文件,做一次哈希计算,只字不差的两个文件哈希值相同。所以,用户只需要使用相同的哈希值,就可以访问那个文件,这个哈希值就是文件的地址。只要获取这个地址,就可以共享资源了。
基于上面的永久存储特性,再也不用担心某个电影找不到了,也不用备份,因为全球电脑上只要有那么几个人存储着,你就能拿到它,并且不依赖于中心网络。
- 基于内容(而非域名)寻址
IPFS 的网络上运行着一条区块链,即用来存储互联网文件的哈希值表,每次有网络访问,即要在链上查询该内容(文件)的地址。
文件(内容)具有存在的唯一性,一个文件加入了IPFS的网络,将基于计算对内容赋予一个唯一加密的哈希值。这将改变我们使用域名访问网络的习惯。
提供文件的历史版本控制器(类似Git),并且让多节点使用保存不同版本的文件。
- 节点存储激励,代币分成
通过使用代币(FileCoin)的激励作用,让各节点有动力去存储数据。 Filecoin 是一个由加密货币驱动的存储网络。矿工通过为网络提供开放的硬盘空间获得 Filecoin,而用户则用 Filecoin 来支付在去中心化网络中储存加密文件的费用。
IPFS 工作原理
- 每个文件及其中的所有块都被赋予一个称为加密散列的唯一指纹。 IPFS 通过网络删除重复具有相同哈希值的文件,通过计算是可以判断哪些文件是冗余重复的。并跟踪每个文件的版本历史记录;
- 每个网络节点只存储它感兴趣的内容,以及一些索引信息,有助于弄清楚谁在存储什么;
- 查找文件时,你通过文件的哈希值就可以在网络查找到储存改文件的节点,找到想要的文件;
- 使用称为 IPNS(去中心化命名系统),每个文件都可以被协作命名为易读的名字。通过搜索,就能很容易地找到想要查看的文件。
- 从 IPFS 的介绍可以看出, IPFS 设想的是让所有的网络终端节点不仅仅只充当 Browser 或 Client 的角色,其实人人都可以作为这个网络的运营者,人人都可以是服务器。
IPFS 配置
1. 下载安装
下载 Mac 版 IPFS 安装文件 go-ipfs_v0.7.0_darwin-amd64.tar.gz
,解压缩该文件,得到 go-ipfs
文件夹,包含如下文件:
├── LICENSE
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── install.sh
└── ipfs
安装 IPFS
cd go-ipfs
install.sh
> Moved ./ipfs to /usr/local/bin
ipfs --version
> ipfs version 0.7.0 # 出现版本信息,表示安装成功
升级 IPFS,下载 ipfs-update 工具,解压缩
cd ipfs-update
install.sh
> Moved ./ipfs-update to /usr/local/bin
ipfs-update --version # 出现版本信息,表示安装成功
2. IPFS 本地部署
- 创建节点
- 或生成新节点(文件仓库)
- 或迁移现有节点,需要 fs-repo-migrations 工具。解压下载的 fs-repo-migrations 工具,在终端中运行
fs-repo-migrations
即可完成版本迁移
ipfs init # 创建新节点,所生成的新文件夹`.ipfs` 所含文件如下
.
├── blocks
├── config
├── datastore
├── datastore_spec
├── keystore
└── version
fs-repo-migrations -revert-ok -to 10 # 将现有节点迁移到指定版本,比如 10
- 节点配置
{
"API": {
"HTTPHeaders": {
"Server": [
"go-ipfs/0.4.14"
]
}
},
"Addresses": {
"API": "/ip4/127.0.0.1/tcp/5001",
"Announce": [],
"Gateway": "/ip4/127.0.0.1/tcp/8080",
"NoAnnounce": [],
"Swarm": [
"/ip4/0.0.0.0/tcp/4001",
"/ip6/::/tcp/4001",
"/ip4/0.0.0.0/udp/4001/quic",
"/ip6/::/udp/4001/quic"
]
},
"Bootstrap": [
"/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
"/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
"/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
"/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
"/ip4/104.236.179.241/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM",
"/ip4/128.199.219.111/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu",
"/ip4/104.236.76.40/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64",
"/ip4/178.62.158.247/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd",
"/ip6/2604:a880:1:20::203:d001/tcp/4001/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM",
"/ip6/2400:6180:0:d0::151:6001/tcp/4001/p2p/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu",
"/ip6/2604:a880:800:10::4a:5001/tcp/4001/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64",
"/ip6/2a03:b0c0:0:1010::23:1001/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd",
"/ip4/104.131.131.82/udp/4001/quic/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"
],
"Datastore": {
"BloomFilterSize": 0,
"GCPeriod": "1h",
"HashOnRead": false,
"Spec": {
"mounts": [
{
"child": {
"path": "blocks",
"shardFunc": "/repo/flatfs/shard/v1/next-to-last/2",
"sync": true,
"type": "flatfs"
},
"mountpoint": "/blocks",
"prefix": "flatfs.datastore",
"type": "measure"
},
{
"child": {
"compression": "none",
"path": "datastore",
"type": "levelds"
},
"mountpoint": "/",
"prefix": "leveldb.datastore",
"type": "measure"
}
],
"type": "mount"
},
"StorageGCWatermark": 90,
"StorageMax": "2GB"
},
"Discovery": {
"MDNS": {
"Enabled": true,
"Interval": 10
}
},
"Experimental": {
"FilestoreEnabled": false,
"Libp2pStreamMounting": false,
"ShardingEnabled": false
},
"Gateway": {
"HTTPHeaders": {
"Access-Control-Allow-Headers": [
"X-Requested-With",
"Range"
],
"Access-Control-Allow-Methods": [
"GET"
],
"Access-Control-Allow-Origin": [
"*"
]
},
"PathPrefixes": [],
"RootRedirect": "",
"Writable": false
},
"Identity": {
"PeerID": "QmXXXXXXXXXXXXXXX",
"PrivKey": "XXXXXXXXXXXX"
},
"Ipns": {
"RecordLifetime": "",
"RepublishPeriod": "",
"ResolveCacheSize": 128
},
"Mounts": {
"FuseAllowOther": false,
"IPFS": "/ipfs",
"IPNS": "/ipns"
},
"Reprovider": {
"Interval": "12h",
"Strategy": "all"
},
"Swarm": {
"AddrFilters": null,
"ConnMgr": {
"GracePeriod": "20s",
"HighWater": 900,
"LowWater": 600,
"Type": "basic"
},
"DisableBandwidthMetrics": false,
"DisableNatPortMap": false,
"DisableRelay": false,
"EnableRelayHop": false
}
}
- 节点 ID
每个节点都会存在一个唯一标识。
ipfs id # 查看节点 ID
{
"ID": "QmXXXXXXXXXXXXXXX",
"PublicKey": "XXXXXXXXXXXXXXX",
"Addresses": null,
"AgentVersion": "go-ipfs/0.7.0/",
"ProtocolVersion": "ipfs/0.1.0",
"Protocols": null
}
- 启动节点,启动后可通过
http://127.0.0.1:5001/webui
在浏览器中打开 IPFS。
ipfs daemon # 启动节点
Initializing daemon...
go-ipfs version: 0.7.0
Repo version: 10
System version: amd64/darwin
Golang version: go1.14.4
Swarm listening on /ip4/127.0.0.1/tcp/4001
Swarm listening on /ip4/127.0.0.1/udp/4001/quic
Swarm listening on /ip4/192.168.0.102/tcp/4001
Swarm listening on /ip4/192.168.0.102/udp/4001/quic
Swarm listening on /ip6/::1/tcp/4001
Swarm listening on /ip6/::1/udp/4001/quic
Swarm listening on /p2p-circuit
Swarm announcing /ip4/127.0.0.1/tcp/4001
Swarm announcing /ip4/127.0.0.1/udp/4001/quic
Swarm announcing /ip4/192.168.0.102/tcp/4001
Swarm announcing /ip4/192.168.0.102/udp/4001/quic
Swarm announcing /ip6/::1/tcp/4001
Swarm announcing /ip6/::1/udp/4001/quic
API server listening on /ip4/127.0.0.1/tcp/5001
WebUI: http://127.0.0.1:5001/webui
Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080
Daemon is ready
执行命令后,最后出现 Daemon is ready
,就代表节点启动成功了,并已经作为节点加入了 IPFS 网络了。
检查本机的种子节点命令:ipfs bootstrap
检查本机加入的集群信息:ipfs swarm peers
。
GATEWAY: http://localhost:8080
API: http://127.0.0.1:5001
- 本地发布站点文件到 IPFS
注意,使用带有 -r
参数的 ipfs add
命令,即 ipfs add -r
,以便将文件夹下所有文件递归地添加到 IPFS。
ipfs add -r Path/to/webpage/Folder
ipfs add -r ~/Downloads/me
added QmdNsncGJ35Uy247gSW9JG2oqvUjNwfQNcLC9BzGKMtZP7 me/assets/css/me.css
added QmSGwLaWfAF8VURTUdjLrYJLuCfeSM4DNkouPH5622v6rP me/assets/sass/00-reset.scss
added QmPHZrugucjRVuRQVHVh47cBZ47mC3ch2ktJP7wFDUNPQv me/assets/sass/01-content.scss
added QmdmeTQHUXbjhhfKVYsYutRPwTve7xqaFQx1Jzp7KFUzKc me/assets/sass/me.scss
added QmVhTyfjPvPjVm9Qr2HzHhEcPHuUTdwyFoGSbrun26eqge me/images/background.jpg
added QmS9GrnuFH7316vxbygdTWjsQidTheWbLq9W8ESyGY715H me/images/favicon.png
added QmarPeQ5brfhdstyJFnY6PmK6TZFDkfbktwCwt4eHcaTkV me/images/logo.png
added QmcML7GAxYfbfQoHUFWpYDEBXfoagVkGE4enWiWCJymwBa me/images/social.jpg
added QmVEJXxaPnGqaXH6wEmLonKNzbBi6svU5b8CcokKLUQyPA me/index-static.html
added QmeSGXLwY9Nc3Zwtrv2JiCSS5hDhFfwDwZaJKekdwdqVn7 me/index-youtube.html
added QmNZWBqCrhNinmfq8yUxxPteyubAGmMfzL6CubKgeeeT59 me/index.html
added QmTWxfA4jaSujHPVhLu1VVY888kLgbxnzVjcXp369Ge3eS me/readme.md
added QmWadsLVLLCyrMxJ6owQQRGpunQHcjeVpF3Z4rbFanm8it me/readme.txt
added QmZQZSEA5Cgyn3iCW4qTtDAjt2f25RRqFyPDPyeBJPCcNF me/videos/background.mp4
added QmV29PcMMYZ5SVe1SWJWw8VdwDj6xmBKVJmF4pykviDz5t me/assets/css
added QmeES6u2o7LpCdkh72KreSQ8Cea16e6LdvSHhV1K5KoNBE me/assets/sass
added QmbshznhgJn5MMi1nLzSyAeKb9VUtD9HLP57LoPgXPQurd me/assets
added QmcvWkbm2nUFNSfaWyo1Rc4UQvndSgeHbJd6rrFs4QRrXM me/images
added QmaZ8B21dkpTVAMpdkcYQsaqfetsotKQxeb6ug7xmJ4CFP me/videos
added QmNWsJM4c2ESKmwhVvz3nh1h87jrtxfvTVXViUSzE7m9dV me
1.34 MiB / 1.34 MiB [=====================================================] 100.00%
- 托管站点文件到 pinata.cloud
我们需要一种服务,将内容放入其中之后,它可以保证内容一直在线, 供他人访问或复制。 这种服务并不需要承担所有访问流量, 但它需要承诺持续在线。 在 IPFS 世界中, 这种将文件“钉”在网络中的服务被称为"pin”。
虽然也可以自己搭建 IPFS 节点来完成上述任务,但对于个人网站这种简单场景, 更好的选择是使用现成的"pin"服务。Pinata 就是这样一个服务。 使用简便, 而且1G以下的存储容量是完全免费的。
注册 Pinata 之后, 访问 (https://pinata.cloud/pinataupload) 即可执行上传操作。 选择 “Upload Directory”后,可将包含网站的文件夹整体上传。上传成功以后将得到 CID (即此文件夹在 IPFS 中的内容 ID),也可以在 (https://pinata.cloud/pinexplorer) 管理已上传的文件或文件夹。
- 通过 IPFS 访问站点
https://ipfs.io/ipfs/your-webpage-hash # 使用 `IPFS hash` 访问站点;
IPFS 中,网站的哈希值和网站的内容是一一对应的,网页中任何的改变都将导致其生成新的哈希值。所以,每次更新网站之后,网站的哈希值都会变,旧的链接不能访问到新的内容。
通过 IPNS,将网站的 IPFS 哈希值与 Peer ID 绑定,完后更新完网站,可通过 IPNS 使用 Peer ID 来访问站点,而每个用户的 Peer ID 可保持不变。
ipfs name publish your-webpage-hash # 将网站的 IPFS 哈希值发布到 IPNS
ipfs name publish QmNWsJM4c2ESKmwhVvz3nh1h87jrtxfvTVXViUSzE7m9dV # 将上述网站发布到 IPNS
Published to k2k4r8ot14v7ncfnxhvq4hx86thrxkn2sghvmjcrksas8fyqojwmnrq7: /ipfs/QmNWsJM4c2ESKmwhVvz3nh1h87jrtxfvTVXViUSzE7m9dV # 将 IPFS 哈希值发布到 IPNS,生成了一个新的 IPNS 哈希值;
ipfs name resolve your-peer-id # 验证 Peer ID 和网站的哈希已经绑定
/ipfs/your-webpage-hash
ipfs name resolve QmctjMf6xLZGiiXSoK9JAmvVdXwgpLBGLcShPE9fGpDbxr
/ipfs/QmNWsJM4c2ESKmwhVvz3nh1h87jrtxfvTVXViUSzE7m9dV
- 通过 IPNS 使用 Peer ID 来访问站点
https://ipfs.io/ipns/your-peer-id
https://ipfs.io/ipns/QmctjMf6xLZGiiXSoK9JAmvVdXwgpLBGLcShPE9fGpDbxr
- 绑定域名(避免使用复杂的哈希值访问网站)
我们已经有一个域名sub.your.domain
,可创建一个 DNS TXT 记录(DNSLink),这条记录的键(name 或者 key)为 _dnslink.sub.your.domain
,值(data)为 dnslink=/ipns/$SITE_CID
,其中 $SITE_CID 为站点的 Peer ID,将 Peer ID 与域名绑定。
TXT _dnslink.www.urz.one dnslink=/ipns/QmctjMf6xLZGiiXSoK9JAmvVdXwgpLBGLcShPE9fGpDbxr
在域名解析里面建立一条 CNAME 记录,将 sub.your.domain
解析指向 cloudflare-ipfs.com
.
CNAME www.urz.one cloudflare-ipfs.com
- HTTPS 保护
访问 Cloudflare IPFS Gateway,在页面最底部文本框中输入想要保护的域名(在本例中即 www.urz.one), 等待几分钟, 就会提示 “Certificate is live”。
至此,我们就可以通过sub.your.domain
直接访问 ipfs 网站了。
- 网站更新
网站本地更新后,将内容托管到 pinata.cloud,将 pinata.cloud 生成的内容 CID,发布到 IPNS ipfs name publish site-hash
即可。
nslookup cloudflare-ipfs.com. # 查询服务节点的 IP
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: cloudflare-ipfs.com
Address: 104.244.46.5
dig +noall +answer TXT www.urz.one
www.urz.one. 21599 IN CNAME cloudflare-ipfs.com.