Origin Isolation for IPFS Gateway

发布于 2021-06-23 12:08:55

Proxy IPFS in path style

为什么使用代理方式,而不是运行 ipfs daemon,为了省内存!

使用 nginx 就可以轻松实现了。

Proxy IPFS in subdomain style

DNS

Cloudflare 支持泛域名解析

HTTPS 证书

acme.sh 安装简单(相比 certbot),使用简单。

curl https://get.acme.sh | sh -s email=[email protected]
# 关掉nginx释放443、80端口
# 安装
~/.acme.sh/acme.sh --issue --standalone -d ipfs.drink.cafe -d '*.ipns.ipfs.drink.cafe' -d '*.ipfs.ipfs.drink.cafe' --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force
# 添加 DNS TXT 记录后,验证 wildcard 域名
~/.acme.sh/acme.sh --issue --standalone -d ipfs.drink.cafe -d '*.ipns.ipfs.drink.cafe' -d '*.ipfs.ipfs.drink.cafe' --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force --renew

正确代理上游的内容(过时)

[
  {"from": "ipfs.ipfs.drink.cafe", "to": "https://ipfs.dweb.link"},
  {"from": "ipfs.drink.cafe", "to": "https://ipfs.dweb.link"}
]

正确代理上游内容 2

proxy_pass 到 ipfs.dweb.link 的时候出现 502 问题。

阅读 How to Use NGINX as an HTTPS Forward Proxy Server 后发现不应该 nginx 反向代理不成功啊。

查看 /var/log/nginx/error.log 发现问题是 no resolver defined to resolve ipfs.dweb.link

在 nginx 的 location 内添加 resolver 1.1.1.1; 就好了。

location / {
    add_header x-source https://$subdomain.ipfs.dweb.link$uri;
    #return 302          https://$subdomain.ipfs.dweb.link$uri;

    resolver 1.1.1.1;
    proxy_pass    https://$subdomain.ipfs.dweb.link;
    proxy_set_header Host $subdomain.ipfs.dweb.link;

    # proxy_cache_bypass $http_upgrade;
    proxy_pass_request_headers      on;
    client_max_body_size 50M;
}

跳转 path style 的请求

查看 gateway checker 的 JS 源码,可以看到必须实现跳转。

function expectSubdomainRedirect(url) {
  // Detecting redirects on remote Origins is extra tricky,
  // but we seem to be able to access xhr.responseURL which is enough to see
  // if paths are redirected to subdomains.
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url, true)
    xhr.onload = function () {
      // expect to be redirected to subdomain where first DNS label is CID
      if (new URL(xhr.responseURL).hostname.startsWith(IMG_HASH)) {
        resolve()
      } else {
        reject()
      }
    }
    xhr.onerror = function (err) {
      console.error(url, err)
      reject()
    }
    xhr.send(null)
  })
}

通过 nginx 的 location 正则匹配即可实现。

不支持 CIDv0

访问 https://QmfEw63k5VZP2ffAiGZTvZfUgkRfzuKddLWi8SC78cuS4R.ipfs.dweb.link 报错

invalid CID: selected encoding not supported (possible lowercased CIDv0; consider converting to a case-agnostic CIDv1, such as base32)

解决方式 1:转化为 CIDv1

$ ipfs cid base32 QmfEw63k5VZP2ffAiGZTvZfUgkRfzuKddLWi8SC78cuS4R
bafybeih3d63d53eggrlibuhwdpofajzvrbjw2ounv55uo755smzpr2h57i

解决方式 2:不跳转 CIDv0

https://ipfs.drink.cafe/ipfs/QmfEw63k5VZP2ffAiGZTvZfUgkRfzuKddLWi8SC78cuS4R

最终 nginx 配置结果

server {
    include requestid.conf;
    include https.conf;
    server_name ipfs.drink.cafe;
    ssl_certificate     /etc/letsencrypt/live/drink.cafe/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/drink.cafe/privkey.pem;
    ssl_dhparam /usr/local/nginx/dhparams.pem;

    location ~ ^/ipfs/(?<cid>[0-9a-z]+)(?<extra>(.|/)*)$ {
        client_max_body_size 5M;
        add_header access-control-allow-origin '*';
        return 302 'https://$cid.ipfs.ipfs.drink.cafe$extra';
    }

    location / {
        client_max_body_size 5M;
        add_header access-control-allow-origin '*';
        proxy_pass https://ipfs.io;
        proxy_set_header Host 'ipfs.io';
        proxy_cache_bypass $http_upgrade;
    }

    location = / {
        return 302 '/ipfs/bafybeih3d63d53eggrlibuhwdpofajzvrbjw2ounv55uo755smzpr2h57i';
    }
}
server {
    include requestid.conf;
    include https.conf;
    server_name ~^(?<subdomain>.+)\.ipfs\.ipfs\.drink\.cafe$;
    ssl_certificate     /root/.acme.sh/ipfs.drink.cafe/ipfs.drink.cafe.cer;
    ssl_certificate_key /root/.acme.sh/ipfs.drink.cafe/ipfs.drink.cafe.key;
    ssl_dhparam /usr/local/nginx/dhparams.pem;

    location / {
        client_max_body_size 50M;
        # proxy_cache_bypass $http_upgrade;
        proxy_pass_request_headers      on;

        add_header x-source https://$subdomain.ipfs.dweb.link$uri;
        resolver 1.1.1.1;
        proxy_pass    https://$subdomain.ipfs.dweb.link;
        proxy_set_header Host $subdomain.ipfs.dweb.link;
    }
}
comments powered by Disqus