IPFS是什么
IPFS(InterPlanetary File System)是一个内容寻址、版本化、点对点的分布式文件系统。
- 内容寻址 - 文件不是以URL寻址,而是根据文件内容的Hash值寻址,一旦内容确定,文件地址也就确定
- 版本化 - 类似于git,可以跟踪文件的历史变化情况
- 点对点 - 所有运行ipfs的设备点对点组成一个去中心化网络
安装启动IPFS
bin包安装
在官方网站下载对应平台的安装包,本文使用linux-64位版本
或直接使用网址下载
> wget https://dist.ipfs.io/go-ipfs/v0.4.15/go-ipfs_v0.4.15_linux-amd64.tar.gz
解压安装包,将可执行文件放在环境变量目录
> tar xvfz go-ipfs_v0.4.15_linux-amd64.tar.gz
> mv ipfs /usr/local/bin/
初始化ipfs节点
> ipfs init
initializing ipfs node at /root/.go-ipfs
generating 2048-bit RSA keypair...done
peer identity: QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR
to get started, enter:
ipfs cat /ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/readme
根据提示,检查安装状态
> ipfs cat /ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/readme
Hello and Welcome to IPFS!
██╗██████╗ ███████╗███████╗
██║██╔══██╗██╔════╝██╔════╝
██║██████╔╝█████╗ ███████╗
██║██╔═══╝ ██╔══╝ ╚════██║
██║██║ ██║ ███████║
╚═╝╚═╝ ╚═╝ ╚══════╝
If you're seeing this, you have successfully installed
IPFS and are now interfacing with the ipfs merkledag!
-------------------------------------------------------
| Warning: |
| This is alpha software. use at your own discretion! |
| Much is missing or lacking polish. There are bugs. |
| Not yet secure. Read the security notes for more. |
-------------------------------------------------------
Check out some of the other files in this directory:
./about
./help
./quick-start <-- usage examples
./readme <-- this file
./security-notes
前面完成了ipfs节点的配置,接下来就是启动ipfs守护进程,可以在另一个终端启动,也可以在本终端加上&
后台启动
> ipfs daemon
Initializing daemon...
Successfully raised file descriptor limit to 2048.
Swarm listening on /ip4/127.0.0.1/tcp/4001
Swarm listening on /ip4/172.17.0.1/tcp/4001
Swarm listening on /ip4/172.22.16.1/tcp/4001
Swarm listening on /ip4/192.168.0.116/tcp/4001
Swarm listening on /ip4/192.168.122.1/tcp/4001
Swarm listening on /ip6/::1/tcp/4001
Swarm listening on /p2p-circuit/ipfs/QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR
Swarm announcing /ip4/10.103.255.235/tcp/48240
Swarm announcing /ip4/127.0.0.1/tcp/4001
Swarm announcing /ip4/172.17.0.1/tcp/4001
Swarm announcing /ip4/172.22.16.1/tcp/4001
Swarm announcing /ip4/192.168.0.116/tcp/4001
Swarm announcing /ip4/192.168.122.1/tcp/4001
Swarm announcing /ip6/::1/tcp/4001
API server listening on /ip4/127.0.0.1/tcp/5001
Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080
Daemon is ready
在另一个终端输入以下命令确保连接进ipfs网络
> ipfs swarm peers
/ip4/103.214.109.178/tcp/4001/ipfs/QmfZeNauweuRYWNXMjPhB4Tu2CPU6aD5eide9PEhVtHP6A
/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
/ip4/104.223.59.174/tcp/4001/ipfs/QmeWdgoZezpdHz1PX8Ly8AeDQahFkBNtHn6qKeNtWP1jB6
/ip4/104.233.106.156/tcp/4001/ipfs/QmSaLHB9dK7UphxcSWSdySVQyayux2yVMUELnAN8VV96x2
/ip4/106.38.77.82/tcp/49777/ipfs/QmfT3BCVYjLiVVB197mR1P4uhdR7zsKZsE7HLzCV1qSbHJ
/ip4/107.172.55.210/tcp/4001/ipfs/QmcYfPreozdQMKyo5omUhvTShCKRjdeiQhS7328J7CLLXe
/ip4/109.129.112.32/tcp/56478/ipfs/QmRYXX6mvvQQWR1kB1pC5c5dbcwASqyoQ6sjxH2czzPNBs
......
到现在,已经可以从ipfs网络中获取文件了,以一个官方图片为例
> ipfs cat /ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg > cat.jpg
另外,可以在浏览器中访问http://localhost:5001/webui来使用ipfs的控制台
使用add
将一个文件添加到ipfs网络(可以被其他节点访问到)
> cat 187J3X1.txt
Hello!187J3X1!
> ipfs add 187J3X1.txt
added QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj 187J3X1.txt
文件要能被网络中所有节点访问需要一定时间,在某个运行ipfs的节点,使用get
命令可以获取到本文件
> ipfs get QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj
更多命令可查看帮助信息
> ipfs --help
源码安装
可以从Github下载源码,自行编译安装
使用GPG完成私密文件分享
在ipfs文件系统中,只要知道文件的Hash,就可以从节点获取文件。所以ipfs天生缺乏隐私性,加入需要分享私密的文件给别人。通常的做法是分享者将文件用接收者的公钥加密,接收者从ipfs网络下载后用私钥解密。GPG可以完成加解密的功能。
GPG同样可以使用bin包安装或源码安装
bin包安装
以ubuntu 为例
> sudo apt-get install gnupg
源码安装
(TODO)
密钥生成
接收者使用gen-key
生成密钥对
> gpg --gen-key
gpg (GnuPG) 1.4.20; Copyright (C) 2015 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
加密方案使用默认RSA就行
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection?
密钥长度依然选择默认
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
设置密钥过期时间,选择永远不过期
Please specify how long the key should be valid.
0 = key does not expire
= key expires in n days
w = key expires in n weeks
m = key expires in n months
y = key expires in n years
Key is valid for? (0)
输入秘钥创建者一些信息
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
"Heinrich Heine (Der Dichter) "
Real name: chenmo
Email address: [email protected]
Comment:
You selected this USER-ID:
"chenmo <[email protected]>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?
创建私钥密码(非常重要)
You need a Passphrase to protect your secret key.
Enter passphrase:
Repeat passphrase:
接下来,程序将开始生成密钥,为了使生成过程使用的随机数更加随机,你可以采用敲键盘、移动鼠标等方式增加随机数的随机特性。
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
f
Not enough random bytes available. Please do some other work to give
the OS a chance to collect more entropy! (Need 186 more bytes)
本机生成的秘钥对可以用--list-keys
命令查看
gpg --list-keys
/root/.gnupg/pubring.gpg
------------------------
pub 2048R/F0AA19A1 2018-07-08
uid chenmo <[email protected]>
sub 2048R/54C6ABC4 2018-07-08
其中的F0AA19A1
就是公钥id, 54C6ABC4
是私钥id
可以使用armor
命令将秘钥导出为filename文件
gpg --armor --output [filename] --export [密钥id]
密钥分享
接收者需要告诉发送者自己的公钥,以便发送者加密。这可以通过公钥服务器完成。本文使用
hkp://pgp.mit.edu
> gpg --send-keys [公钥id] --keyserver hkp://pgp.mit.edu
gpg: "--keyserver" not a key ID: skipping
gpg: "hkp://pgp.mit.edu" not a key ID: skipping
gpg: sending key [公钥id] to hkp server keys.gnupg.net
发送方搜索该密钥
> gpg --keyserver hkp://pgp.mit.edu -search-keys [公钥id]
加密文件
发送方加密待分享的文件ipfs_demo.txt
> cat ipfs_demo.txt
This file is just for test --- 187J3X1
> gpg --encrypt --recipient [公钥id] ipfs_demo.txt
将加密后的文件ipfs_demo.txt.gpg 添加到ipfs网络中
> ipfs add ipfs_demo.txt.gpg
added QmbvjL7BnCpUgzrJj2E2QkhqRBJeWKmo1TCfpMmKEQMYYc ipfs_demo.txt.gpg
解密文件
接收方获取文件
>ipfs get QmQRLCz3c6BwtkcACxwwraDX62g4ZAS7fQmrn3Y3rJh1xN
Saving file(s) to QmQRLCz3c6BwtkcACxwwraDX62g4ZAS7fQmrn3Y3rJh1xN
24 B / 24 B [======================================================] 100.00% 0s
该操作可能会有一定时间延时,也可通过网页访问文件地址来接收文件
接下来用私钥解密出原文件
gpg --decrypt QmbvjL7BnCpUgzrJj2E2QkhqRBJeWKmo1TCfpMmKEQMYYc > ipfs_demo.txt
You need a passphrase to unlock the secret key for
user: "chenmo <[email protected]>"
2048-bit RSA key, ID 54C6ABC4, created 2018-07-08 (main key ID F0AA19A1)
gpg: encrypted with 2048-bit RSA key, ID 54C6ABC4, created 2018-07-08
"chenmo <[email protected]>"
> cat ipfs_demo.txt
This file is just for test --- 187J3X1
添加文件
在ipfs守护进程启动之后,可以使用ipfs add
命令将文件添加到ipfs网络
> echo "Hello!187J3X1!" > 187J3X1.txt
> ipfs add 187J3X1.txt
added QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj 187J3X1.txt
其中QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj
就是文件内容的Hash值,一旦文件内容改变,这个值就会改变。需要注意,这个值与文件名完全没有关系,它只是内容的Hash,不管文件名是什么,只要内容一样,它的Hash值就是一样的。
本地位置
ipfs的本地存储空间在~/.ipfs
,所有ipfs自身的文件和本地添加到ipfs网络的文件都存放在此目录
> ls /.ipfs
api blocks config datastore datastore_spec keystore repo.lock version
从此目录下这里可以找到刚才添加的文件
grep -rn "187J3X1"
Binary file blocks/ZF/CIQIXJZ7NCVBEU67XHWSTICDNGNRD2SIAE33W4SSDI6LZTE4PLRSZFA.data matches
读取文件
使用ipfs cat
命令可以获取添加到ipfs网络中的文件,如果文件内容就在本地,那么显示将会很快,如果是在另一个节点,那么要等本节点从网络中获得该文件内容后,才会显示
> ipfs cat QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj
Hello!187J3X1!
可以将文件内容重定向到一个文件,文件名完全不必与原来的一样,因为在ipfs网络中存储的只有内容,没有文件名
> ipfs cat QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj > recv.txt
> cat recv.txt
Hello!187J3X1!
添加文件名信息
向ipfs网站中添加单个文件是没有文件名等信息的,通过在ipfs add
命令中添加-w
选项,可以将该文件的文件名和所处文件夹的信息一同添加到ipfs网络
> pwd
/home/yc/workspace/ipfs/tmp
> ipfs add -w 187J3X1.txt
added QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj 187J3X1.txt
added Qmdjqtwaj2vFkQ4P49qbnR9ksFphkSvUEf3S5rPNDVo7Kn
与刚才仅添加一个文件不同的是,这里会得到两个Hash值,第一个QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj
是文件内容的Hash值,与之前的一样,另一个Qmdjqtwaj2vFkQ4P49qbnR9ksFphkSvUEf3S5rPNDVo7Kn
就是文件所处目录的信息的Hash值。
使用ipfs ls
命令可以列出这种层次关系(不能使用ipfs cat
)
ipfs ls -v Qmdjqtwaj2vFkQ4P49qbnR9ksFphkSvUEf3S5rPNDVo7Kn
Hash Size Name
QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj 23 187J3X1.txt
可以利用目录和文件关系找到显示文件的内容
> ipfs cat Qmdjqtwaj2vFkQ4P49qbnR9ksFphkSvUEf3S5rPNDVo7Kn/187J3X1.txt
Hello!187J3X1!
添加目录
通过在ipfs add
命令中添加-r
选项,可以将一个目录和其中的内容添加到ipfs网络中
> pwd
/home/yc/workspace/ipfs/tmp
> echo "My Second file" > Second_file.txt
> ls
187J3X1.txt Second_file.txt
> ipfs add -r .
added QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj tmp/187J3X1.txt
added QmT96ZpTMV9kjtfEySjovvnfi92NxRqkogVtiwtD5Xs7rX tmp/Second_file.txt
added QmZ813ofgBrNuUpH8VFCR4A8LVkp1vSZcmxnET5PqCtj65 tmp
可以看到该命令将整个目录的内容都添加到了ipfs网络,前两个Hash值是文件,最后一个Hash值是目录。通过'ipfs ls'查看
> ipfs ls -v QmZ813ofgBrNuUpH8VFCR4A8LVkp1vSZcmxnET5PqCtj65
Hash Size Name
QmRTxTkdXeUmG9GxJBA7B8Ad8hticjNwkucCY96mkzusXj 23 187J3X1.txt
QmT96ZpTMV9kjtfEySjovvnfi92NxRqkogVtiwtD5Xs7rX 23 Second_file.txt
IPNS是什么
在ipfs中,一个文件的Hash值完全取决于其内容,修改它的内容,其相应的Hash值也会发生改变。如果我们把修改前后的文件都通过ipfs add
添加到ipfs网络中,那么我们将可以通过这两个Hash值访问到前后两个版本的内容。这种静态特性有利于提高数据的安全,比如 Alice 可以将一份自己签名(私钥加密)的文件放到ipfs中,那么即使她后来对文件进行了修改并重新签名和发布,那么之前的文件依然存在,她不能抵赖曾经发布过老版本的文件。但对于一些需要保持动态性的文件来说,比如网页,在新版本出现后,旧版本的内容将毫无意义。并且,总不能要求网页访问者每次要在浏览器中输入不同的ipfs地址来访问不同时期的网页吧。
IPNS(Inter-Planetary Naming System)提供了一种为文件增加动态性的解决方案。它允许节点的PeerID限定的命名空间提供一个指向具体ipfs文件(目录)Hash的指针,通过改变这个指针每次都指向最新的文件内容,可以使得其他人始终访问最新的内容。
例子
> echo "This is old version file" > myfile.txt
> ipfs add myfile.txt
added QmQECo2p8LdVcjtkEWDVNVM7Hrsc7arW52P5vz5BVuvEgR myfile.txt
使用ipfs publish
命令发布本节点ipns到ipfs的映射
> ipfs name publish /ipfs/QmQECo2p8LdVcjtkEWDVNVM7Hrsc7arW52P5vz5BVuvEgR
Published to QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR: /ipfs/QmQECo2p8LdVcjtkEWDVNVM7Hrsc7arW52P5vz5BVuvEgR
而QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR
正是本节点的ID,这一点可以通过ipfs id
看到
ipfs id
{
"ID": "QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR",
......
使用命令ipfs resolve
可以解析ipns到ipfs的映射关系
> ipfs name resolve QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR
/ipfs/QmQECo2p8LdVcjtkEWDVNVM7Hrsc7arW52P5vz5BVuvEgR
在本地执行该命令,很快就可以得到结果。如果在其他节点执行,会花费一些时间。
可以看出解析的结果正是myfile.txt文件的内容。
这一点也可以在浏览器中验证
现在对文件进行修改,并添加到ipfs网络
> echo "This is new version file" > myfile.txt
> ipfs add myfile.txt
added Qmdzqe3yA4JZM5wQe2hV8pfi9CA7aWUq8vHAJd7p7USJQb myfile.txt
再修改ipns到ipfs的映射关系,使本节点的id映射到新的文件内容Hash
> ipfs name publish Qmdzqe3yA4JZM5wQe2hV8pfi9CA7aWUq8vHAJd7p7USJQb
Published to QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR: /ipfs/Qmdzqe3yA4JZM5wQe2hV8pfi9CA7aWUq8vHAJd7p7USJQb
验证其映射关系
> ipfs name resolve QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR
/ipfs/Qmdzqe3yA4JZM5wQe2hV8pfi9CA7aWUq8vHAJd7p7USJQb
在浏览器中验证:
上面的例子中,我们修改ipns下的节点ID和ipfs文件内容的映射关系,使得虽然访问的是同一个地址但实际访问的文件内容却不相同,这不仅适用于单个文件,我们完全可以对一个目录作这种操作。
但是一个节点的ID只有一个,假设我需要同时保留多个这样的映射实例,该怎么办呢?
其实ipns的映射关系除了节点ID
<->文件内容
,还有一种是key
<->文件内容
通过ipfs key list
命令可以看到本节点的key
> ipfs key list -l
QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR self
可以看到,节点默认具有一个名为self
的key,它的值正是节点ID。
而在ipfs name publish
命令的完整形式是
ipfs name publish [--resolve=false] [--lifetime= | -t] [--ttl=] [--key= | -k] [--]
注意其中的key,如果不带这个参数,那么久表示使用默认的key, 也就是节点ID。
可以使用ipfs key gen
创建新的key,这里创建一个名为another
的key
> ipfs key gen --type=rsa --size=2048 another
QmYTpEqtNYvNFUwFysuRsEufNDJJBBEJfqsqrFXDT93sEE
> ipfs key list -l
QmQQ5t88W44Je5WgvmgpV1xSZTg1y5UXdYFHcQQ7EayxwR self
QmYTpEqtNYvNFUwFysuRsEufNDJJBBEJfqsqrFXDT93sEE another
尝试用新的key,映射一个ipfs文件内容
> echo "This is another file" > another.txt
> ipfs add another.txt
added QmPoyokqso3BKYCqwiU1rspLE59CPCv5csYhcPkEd6xvtm another.txt
> ipfs name publish --key=another QmPoyokqso3BKYCqwiU1rspLE59CPCv5csYhcPkEd6xvtm
Published to QmYTpEqtNYvNFUwFysuRsEufNDJJBBEJfqsqrFXDT93sEE: /ipfs/QmPoyokqso3BKYCqwiU1rspLE59CPCv5csYhcPkEd6xvtm
用ipfs resolve
解析
> ipfs resolve /ipns/QmYTpEqtNYvNFUwFysuRsEufNDJJBBEJfqsqrFXDT93sEE
/ipfs/QmPoyokqso3BKYCqwiU1rspLE59CPCv5csYhcPkEd6xvtm
小结
IPNS可以为ipfs的动态内容提供映射,用户通过publish
操作,使得其他人访问固定的IPNS地址就可以最终访问到不同的ipfs文件内容。