ipfs / ipfs-webui

A frontend for an IPFS Kubo and IPFS Desktop
https://webui.ipfs.io
MIT License
1.56k stars 488 forks source link

Make it possible to use Web UI with a remote IPFS API #836

Closed olizilla closed 4 years ago

olizilla commented 6 years ago

Lots of people want to use Web UI to monitor and manage IPFS nodes hosted on a remote server rather than on their local machine. That is totally reasonable.

But it is not currently supported. The IPFS API is not authenticated. It currently expects to be accessed by privileged users on the local machine, and should not be exposed to the internet. Once we have an authentication mechanism for the API we'll ensure that Web UI works with it.

Please see: https://github.com/ipfs/go-ipfs/issues/1532

In the meantime we'll make the subset of Web UI that can work against the read-only / gateway API that would be safe expose from a remote machine. See #835

A suitably motivated individual could set up secure proxy + firewall or vpn to access their api port securely from a remote machine. Pervious iterations of Web UI were hardcoded to use an API on localhost, but v2 will do nothing special to prevent trying to use a remote api address. It's worth reiterating that its not a currently supported configuration, and we don't have the bandwidth to deal with support requests if it doesn't work for you. We will make this work in the future, in a secure way, once core supports it.

Previous reports of this issue

lidel commented 6 years ago

It is worth noting what go-ipfs will bind API port to 127.0.0.1 by default. It is a safety feature.

If someone chooses to override that setting and expose it to the wide internet then we are not responsible for damage resulting from that.

Just like noted in https://github.com/ipfs-shipyard/ipfs-webui/issues/594#issue-238389525 we are not able to close such security hole by writing javascript that is refusing webui connection to remote API. Everyone with curl can easily work around that, and it makes things harder for people using VPN.

So I agree it is good idea to remove such limitation in v2.
People will finally be able to use WebUI over IP provided by VPN :)

Having that said, things we could do to improve situation:

MicahZoltu commented 5 years ago

This issue has the blocked label, but I don't see (from the description) what it is blocked on?

lidel commented 5 years ago

I think modern ipfs-webui will work with any remote API, as long it has properly set CORS headers (which is the blocker, unless you are admin of that node and can add headers on it).

@olizilla is CORS the blocker you had in mind? I think Web UI shows how to set up CORS now, is that enough to close this issue?

ps. fysa we are researching alternative ways of accessing "remote" API in https://github.com/ipfs/in-web-browsers/issues/137

MicahZoltu commented 5 years ago

The IPFS companion browser plugin correctly works with a non-local node out of the box, which implies to me that nothing needs to be done to IPFS API server to make this work. Unless perhaps browsers don't enforce CORS for requests made from plugins?

lidel commented 5 years ago

IIRC ipfs-companion is explicitly disabling CORS for user-specified API endpoint. If you want to use HTTP API without it, then you need to comply with CORS rules.

makew0rld commented 5 years ago

Can you give an example of the server side CORS rules needed to allow remote webui access, and how to set that up? Thanks.

MicahZoltu commented 5 years ago

CORS enforcement is done by the browser, and you can't disable it (allowing an app to ignore CORS would break the security model). So either the browser doesn't enforce CORS for extensions (possible, I have never looked into it) or CORS isn't the problem.

lidel commented 5 years ago

@makeworld-the-better-one see https://github.com/ipfs-shipyard/ipfs-webui#configure-ipfs-api-cors-headers

@MicahZoltu the browser extension removes Origin header from API requests (ipfs-request.js#L118-L144), taking CORS checks out of the picture.

MicahZoltu commented 5 years ago

Ugh, so apparently Firefox and Chrome do add the header but then proceed to allow the author to change the header! That is just annoying because it means any potential security benefits aren't present, yet everyone has to suffer with dealing with CORS.

Speaking of which, what is the threat model that IPFS is trying to prevent by requiring same origin? The threat model protected by same origin policies is pretty narrow, and it is not clear to me how IPFS benefits.

0zAND1z commented 5 years ago

@chiragg6 and I might be interested in adding the creds support to web-ui.

Please let us know if we can expedite this & close for betterment of all.

lidel commented 5 years ago

@MicahZoltu are you able to elaborate? afaik only WebExtension with permission to <all_urls> is able to work around CORS. I was under impression regular JS won't cut it.

The requirement of same origin on the API port aims to protect users from things like random websites changing node configuration via 127.0.0.1:5001/api/v0/config etc

@0zAND1z what do you mean by creds? Designing native access controls for IPFS API and implementing it in go-ipfs? Or a reverse-proxy (eg with OAuth2) to sit in front of API, similar to https://github.com/ory/hydra ?

MicahZoltu commented 5 years ago

@lidel The point I was trying to make was that if the extension is able to change the Origin header, then there is no point in applying a Origin header at all (especially when Origin header doesn't make much sense, such as for an extension). In the case of this extension, it has the ability to change the Origin header, thus it is silly for the browser to set the Origin header in the first place. If the browser would have simply not set the Origin header, then the extension wouldn't have to go through the annoyance of having to unset it.

IIUC, anyone who can reach 127.0.0.1:5001/api/v0/config can edit configuration, as long as they have the ability to set the Origin header on the request? Without CORS, then anyone would be able to edit configuration, even if they didn't have the ability to set the Origin header?

lidel commented 5 years ago

Without CORS, then anyone would be able to edit configuration, even if they didn't have the ability to set the Host header?

Host header is not relevant here, its the Origin one. And regular JS can't set or modify Origin header.

MicahZoltu commented 5 years ago

I'm back, running into the same problem (essentially) again. I have a remote IPFS node that I want to use and manage from a machine I am on. The remote API is sitting behind a reverse proxy that does Basic Authentication, so the user needs to enter a username/password to access the API. If I manually navigate to the API in a browser window, I'll get a browser HTTP auth pop-up that I can fill in and the IPFS Companion extension then is able to access the API without problem, because it seems to inherit the authorization.

However, the WebUI does not have the ability to inherit the Authorization, so all of the API requests made by the WebUI fail with a 401 Unauthorized.

As an interim solution to remote node management, can a section be added to the UI that allows the user to supply HTTP Basic Authorization credentials or perhaps add a custom header to outgoing API requests? This would allow me to use the UI, even if it isn't an ideal scenario, without having to run an SSH tunnel on every machine I want to connect with. SSH tunneling is significantly more complicated IMO than basic Auth credentials, especially if you are running IPFS in docker and already have a reverse proxy setup (which is pretty common in dockerized environments).

hacdias commented 5 years ago

This is an interesting discussion and perhaps simple basic auth could be implemented. I just wonder about where would we store the password? Should the credentials be set every time the page was reloaded? Should they be persisted somewhere?

@lidel @autonome what do you think about this?

0zAND1z commented 5 years ago

Shoot, this thread went under my octobox carpet of notifications for a very long time.

@0zAND1z what do you mean by creds? Designing native access controls for IPFS API and implementing it in go-ipfs? Or a reverse-proxy (eg with OAuth2) to sit in front of API, similar to https://github.com/ory/hydra ?

@lidel yes. This works best. But for people looking to wall the access, Basic Auth is more suitable.

@hacdias , we could store it in encrypted format on IPFS itself? And once the user wishes to login, could that keystore be decrypted at browser level with a simple password or 2-step verification? I am very sure that there are better ways of solving this. This is me thinking aloud on decentralising the credentials storage.

Security has to be first-class, of course.

lidel commented 5 years ago

IIRC right now user can specify custom API address in multiaddr format using this UI. It saves multiaddr in ipfsApi variable in window.localStorage, and gets picked up by the logic responsible for init of API client. So what is missing, is the capability of passing/storing Basic Auth credentials the same way. I don't think we need to overengineer this, credentials would remain secure within Origin-based sandbox.

This means we are left with two options:

Personally, I feel we should go with (B) because its implementation has smaller surface and makes HTTP Basic Auth easy to set up while keeping UI minimalist.

Thoughts?

hacdias commented 4 years ago

I believe B is the way to go. I can try implementing it out as it seems easy.

lidel commented 4 years ago

I think bugs (1) and (2) from https://github.com/ipfs-shipyard/ipfs-redux-bundle/pull/28#pullrequestreview-310399308 need to be addressed before we close this

hacdias commented 4 years ago

Will be available on next release!

davispuh commented 4 years ago

I've set it up behind Nginx and it works fine except there isn't any option to configure Gateway port for WebUI. if try any "View on IPFS Gateway" it uses 127.0.0.1 instead of my external Nginx address:port.

This is my config

    upstream ipfs_api {
        server 127.0.0.1:5001;
    }

    upstream ipfs_gateway {
        server 127.0.0.1:8080;
    }

    rewrite ^/$ /webui/ last;

    location / {
        proxy_pass http://ipfs_gateway;
        proxy_read_timeout 180s;

        include proxy.conf;
    }

    location ~ ^/(webui|api)/ {
        proxy_pass http://ipfs_api;

        include proxy.conf;
    }
hacdias commented 4 years ago

/cc @rafaelramalho19

jessicaschilling commented 4 years ago

I'm missing the continuity of this thread but are we suggesting adding another box in the UI along the same lines of the API box? If so, is this starting to get close to discussions in other issues about UI-ifying the entire config file?

davispuh commented 4 years ago

Actually this doesn't need any configuration. Like currently there is already auto detect for API as I didn't need to change it. So basically what is needed is that if I access UI with domain like https://ipfs.domain.tld/ then that same domain:port should be used also for Gateway. Currently it uses 127.0.0.1 even if I access with this domain.

jessicaschilling commented 4 years ago

Aha - got it, thanks for the details! I'll leave this in @lidel or @rafaelramalho19 's court.

lidel commented 4 years ago

I suspect this was kinda fixed in https://github.com/ipfs-shipyard/ipfs-webui/pull/1559 where we default to the public gateway (https://ipfs.io)

@davispuh After above fix is released, you can use IPFS Companion browser extension to redirect IPFS requests to the gateway of your choice.

So basically what is needed is that if I access UI with domain like https://ipfs.domain.tld/ then that same domain:port should be used also for Gateway.

AFAIK this won't really work in all contexts. Namely, when WebUI was loaded from API port (http://127.0.0.1:5001/webui), you won't be able to load arbitrary /ipfs/* paths because we only allow specific CIDs of well-known webui releases to be loaded via API port. Could be done, but additional heuristic needs to be implemented. Defaulting to canonical gateway + ipfs-companion works everywhere and is less brittle imo.

davispuh commented 4 years ago

It seems silly to use browser extension to just get this use case working. I have my own API and Gateway both running behind Nginx which I would say is pretty standard configuration. So if it can't be auto-detected then it does need configuration option. Also I don't see it as that complicated, first time accessing can try several combinations (eg. same host:port or whatever API config returns) and if any work then save in local storage. Also if location.host is not IP then most likely it's properly hosted.

lidel commented 4 years ago

I'm reopening this issue as we still get reports of misconfiguration issues when accessing remote API (eg. #1565). Additionally, go-ipfs changed the way API port is exposed to POST-only. It is time to audit current UX vs existing security constraints, and come up with better UI or docs.


@davispuh how would you solve for situation when different ports are used on each side of Nginx (eg. 443 and 8080)? Are you up for submitting a PR with proposed heuristic?

PeterHindes commented 2 years ago

Can i password protect the api on port 5001?

eccentricexit commented 1 year ago

I will document I how work around this to manage and use my remote IPFS node without exposing it to the internet. I often forget it and it may be useful for others.

  1. Create a SOCKS ssh tunnel to the node e.g.: ssh -N -D 9090 user@host

  2. Set firefox to the proxy you just created: image 3- On firefox about:config enable network.proxy.allow_hijacking_localhost.

    Don't forget to disable this after using the node.

  3. Enjoy your "local" ipfs node -> localhost:5001/webui

  4. Again, don't forget to disable localhost hijacking after doing what you need.

Please also comment if this is a Bad Idea™