fedimint / fedimint

Federated E-Cash Mint
https://fedimint.org/
MIT License
554 stars 211 forks source link

feat: private federations #5322

Closed dpc closed 3 weeks ago

dpc commented 4 weeks ago

Motivation

Running public federation has multiple risk, some of which are related to the sheer amount of usage it might bring.

As mints are perfectly private, there is no way to tell the users apart and control the usage.

However the mint can agree on a shared secret and circulate it among the intended users. Imagine a handful of families or a small community running federation for themselves and their friends.

The users and the guardians semi-know and trust each other, and would like to avoid strangers using their federation, even if just to limit resource usage. They would like to keep thing private between themselves though.

The scheme is as follows: guardians agree on a shared api secret. Api secret is enforced on HTTP level. From the outside perspective, public Federation endpoints appear as just inaccessible HTTP servers, and there's little to no sign that they are running Fedimint.

Guardian share the secret with intended users. Users can share it further if they want - there is no way for guardians to control it. But the expectation is that the users will have an incentive to keep it private.

On top if - guardians at any time (possibly periodically) can rotate the secret. E.g. if they detect unusally high activity, they can generate a new secret, circulate it with intended users again and thus shut-off the access for the non-intended users.

If the abuse persist, they can even rotate the secret and have the users get the secret one by one to reclaim their money, ultimately confiscating funds of people that were not supposed to be used.

Just this possibility alone should scare off strangers using private Federations.

Note: this does not ruin privacy of the intended users.

Technical details

Secret are enforced on HTTP level using Basic http authorization scheme.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization#basic

This makes private mints look mostly like normal http servers. Similiar mechanism can be done using Nginx config, but having it inside fedimintd allows better integration with Invite Codes, testing, etc.

While working on it it occured to me that I wish it was possible to authenticate guardian APIs on the HTTP level as well, but it seems that currently there's no way to pass any data from jsonrpsee http middleware to the jsonrpc middleware.

Each fedimintd can optionally set one main api_secret, and multiple api_extra_secrets. The extra secrets are intended for phasing out secrets and allowing users who did not receive the new secret yet, to continue using the existing one.

Invite codes now carry a new field that contains the secret.

OOB Notes carry the api secret as well. It's unclear if not doing it would be a better idea. Then sending OOB would not consist being an invitation.

After joining private federation Client stores the api_secret from the invitation in the database, just like the config. In the future we can add cli and methods to update it, e.g. when receiving newer invitiation. This will allow end-user app integrators to e.g. update secret by re-invitation.

The changes are mostly simple, but touch a lot of places, as the api_secret needs to be passed from the the cli options all the way to api handlers and clients.

Testing

env FM_DEVIMINT_CMD_INHERIT_STDERR=1 RUST_LOG=fm=debug FM_API_SECRET="asfasdfasdfasdf" j devimint-env

will make everything (both fedimintd and fedimint-cli and other clients) use the api secret.

If you find in the logs api addresses used by one of the peers you can check that public access is not allowed with:

devimint> curl -i http://127.0.0.1:14172/
HTTP/1.1 401 Unauthorized
www-authenticate: Basic realm="Authentication needed"
content-length: 12
date: Sun, 19 May 2024 06:33:48 GMT

Unauthorized
dpc commented 4 weeks ago
       > error[E0599]: no method named `set_headers` found for struct `WasmClientBuilder` in the current scope
       >     --> fedimint-api-client/src/api.rs:1350:29
       >      |
       > 1350 |             client = client.set_headers(headers);
       >      |                             ^^^^^^^^^^^ method not found in `WasmClientBuilder`

Damn it wasm, why?

I think I can workaround it by just modifying the URL, which the underlying browser should translate to the same thing. Will look into it when I can.

maan2003 commented 4 weeks ago

ACK concept

(EDIT: I didn't read fully)

dpc commented 4 weeks ago

I think I can workaround it by just modifying the URL, which the underlying browser should translate to the same thing.

AFAICT this should work, but I don't have a good way to test it RN.

elsirion commented 3 weeks ago

Each fedimintd can optionally set one main api_secret, and multiple api_extra_secrets

Why not just one list of secrets?

Invite codes now carry a new field that contains the secret.

Is that a good idea? I think it's better if the secret has to be communicated out of band, otherwise users might be surprised when it gets rotated. When entering the password apps can warn users of the risk of being locked out if they don't know someone in the inner circle.

dpc commented 3 weeks ago

Is that a good idea? I think it's better if the secret has to be communicated out of band, otherwise users might be surprised when it gets rotated.

Prevents nothing, UX suffers.

dpc commented 3 weeks ago

How do we communicate to the user/integrator when auth fails and needs to be renewed?

In case of bad auth the requests return 401 error code. It comes down to error handling and how to structure it to bubble up all the way to end app ux.

But I'd worry about in a follow-up. The whole thing will need some end user app integration (like in Fedi) to figure out all the details and UX.

dpc commented 3 weeks ago

Rebased.

@elsirion If there is not fundamental dissagrement about the idea, can we land it? This change is merge conflict magnet.

Things to argue about that we can still change:

Things I think need to change anyway:

dpc commented 3 weeks ago

Should probably also migrate admin auth to this scheme. Could use guardian:<secret> to distinguish.

Just posted a comment about it in https://github.com/fedimint/fedimint/issues/2420#issuecomment-2128772984 . It's harder to get done, so I think I'll just ignore it for now. @elsirion