iotaledger / iota

Apache License 2.0
10 stars 4 forks source link

[Task (VM-Language)]: Split the Genesis Checkpoint into N independent checkpoints #2958

Open miker83z opened 1 month ago

miker83z commented 1 month ago

The rest/checkpoints/<id>/full endpoint of iota-rest-api serves the full checkpoint data.

This implies that there is still the possibility for the entire migration object set to be transferred through the wire, and for DDOS attacks depending on the access permissions of the rest api.

A solution would be to split the genesis checkpoint into more checkpoints.

cc @kodemartin

lzpap commented 1 month ago

A solution would be to split the genesis checkpoint into more checkpoints.

@miker83z @kodemartin

Yes, however that would mean refactoring the checkpoint logic conditionally (if there are migrated objects) as you would not start normal operation from checkpoint 1. We have seen that checkpoint 0 is treated specially, now we would need to treat checkpoints 1 to N differently. depending on the existence of migration objects.

I would first look at the rest api: where is it used? Which nodes enable it? What is the access control there? Why does it expose full checkpoint data while the RPC doesn't?

Where I'm getting at is that it might very well be an internal tool for trusted deployments like light clients, etc and default RPC provider full nodes never expose it, thereby mitigating the attack.

kodemartin commented 1 month ago

A solution would be to split the genesis checkpoint into more checkpoints.

@miker83z @kodemartin

Yes, however that would mean refactoring the checkpoint logic conditionally (if there are migrated objects) as you would not start normal operation from checkpoint 1. We have seen that checkpoint 0 is treated specially, now we would need to treat checkpoints 1 to N differently. depending on the existence of migration objects.

I would first look at the rest api: where is it used? Which nodes enable it? What is the access control there? Why does it expose full checkpoint data while the RPC doesn't?

Where I'm getting at is that it might very well be an internal tool for trusted deployments like light clients, etc and default RPC provider full nodes never expose it, thereby mitigating the attack.

The full checkpoint data is used for the indexer synchronization for sure.

I agree though, we have to investigate the usage of the rest api. If we can be sure that it doesn't have to be exposed, then this problem should be mitigated.

I will have a look.

kodemartin commented 1 month ago

Following up on my last comment, I see that the iota_rest_api::Client is only used in iota-data-ingestion-core and iota-analytics-indexer.

For the latter we know little about, but it seems like a specific-purpose indexer.

iota-data-ingestion-core sets the foundations for the framework to build custom indexers, and is currently used by iota-indexer in the rebased version. It has a more general purpose though as documented.

It relies on checkpoint streams that can be served by the fullnode rest service, or a remote store. We can opt in serving public streams from remote storage solutions, but I guess the fact remains that any fullnode operator would like to serve the checkpoint stream from the node itself would face the problem we are discussing here. Perhaps this is a marginal use-case, but nevertheless it has to be taken into account in the discussion.

miker83z commented 1 month ago

@kodemartin @lzpap Can we tweak the rest/checkpoints/<id>/full endpoint to treat the checkpoint 0 differently? I think at some point this already happens in the current version because checkpoint 0 includes a tx of kind GenesisTransaction that only includes a vector of objects and no commands. And this kind is only used in checkpoint 0.

kodemartin commented 1 month ago

@kodemartin @lzpap Can we tweak the rest/checkpoints/<id>/full endpoint to treat the checkpoint 0 differently? I think at some point this already happens in the current version because checkpoint 0 includes a tx of kind GenesisTransaction that only includes a vector of objects and no commands. And this kind is only used in checkpoint 0.

I would rather not go that route. It would break the semantics of the endpoint, as the type layout of the checkpoint data does not differentiate.

We could consider making this a streaming endpoint to subscribe for all checkpoints, but then again this would be more complex on the client side, plus it would require that the backend storage supports streaming. But the node collects all data in memory before serving either way

https://github.com/iotaledger/iota/blob/817b4c4f2d8431e99f4e8e9423a50273b2ab90d1/crates/iota-types/src/storage/read_store.rs#L167

I think that given that the synchronization logic relies on checkpoint data, we will always have this irregularity of having to transfer such a large value through the network.