Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network Segregation #770

Open
5 tasks
amydevs opened this issue Jul 12, 2024 · 12 comments
Open
5 tasks

Network Segregation #770

amydevs opened this issue Jul 12, 2024 · 12 comments
Assignees
Labels
development Standard development discussion Requires discussion research Requires research

Comments

@amydevs
Copy link
Contributor

amydevs commented Jul 12, 2024

Specification

In order to keep nodes from networks connecting to other networks that they are not supposed to, we need to implement some mechanism during the connection establishment process that will reject connections if say, a mainnet node tries to connect to the testnet network.

After talking with @tegefaulkes, we have come up with 2 solutions:

Using JWTs/Tokens with Verification Metadata

In order to show proof of access into a network, nodes should need to show some token to the seednodes, and peer nodes of that network in order to verify they are apart of that network.

These JWT tokens should contain information about:

From the point of view of proving that a node has access to a network we need the following.

  1. Some way to show proof of access. Ideally this will be a token, dumb (JWT) or otherwise (biscuits and capabilities) proving the following facts. This will provide verifiable proof of access without needing to ask anyone.
    a. The subject of the token, Who has access. This is the public key of the node asking for access
    b. The issuer of the token, Who gave access. This is the public key central issuer of the network.
    c. The network they have access to. This can be the public key of the network issuer. Really just the same as the issuer
    d. The signature of the above facts, signed by the issuers private key.
  2. An access list. Essentially a list of all the networks the current node allows connections to. In it's barest form its just a list of public keys of the networks. Can include extra metadata since a public key without context can be hard to audit. This will be checked against when verifying the network tokens.
  3. A mechanism for providing the token to prove access after connection establishment. To do this, we will need to rework add middleware to our node-to-node rpc calls to limit calls until a node is authenticated. Similarly, the NodeGraph needs to be modified to only add nodes after authentication as to not add out-of-network nodes. The NodeGraph may also need to have network metadata as to filter out what node connection information to give to a peer. Finally, RPC handlers will have to be made to allow for the request and distribution of authentication tokens from seednodes, as well as allow for authentication between nodes.

We have decided, much like the ACL logic of our nodes, the authentication procedure should be carried out within the application layer. This parallels with the separation of authorization and TLS verification seen in the web. Separation of concerns is good here as the certchain is an immutable and inflexible structure that is purely meant to represent the flow of identity.

As for a node having access to multiple networks. It should be possible with this. It just means a node provides multiple network tokens when connecting and the connected node can decide to accept the connection based on it's access list. The only problem I can see with this is that one network might want to be strict and only allow nodes that use that one network. It would be possible to enforce this by rejecting nodes that provide multiple network tokens. but there's not way to force a node to provide all network tokens it has.

Bootstrapping

In order to bootstrap into a network, a node will make an RPC call to a seecnode in order to request a token to be signed in order to prove that the node is a part of the network. Connections to other nodes must not be made before the signed token is returned.

There should be separate RPC calls for requesting the token and receiving the token. The reason for this is so that a node can attempt to join a network and get the signed claim much later even if the node restarts or stops. This allows for nodes to be potentially individually accepted into the network by a human operator. If the node is offline when the seednode attempts to send it the fully signed token, the seednode can schedule a task to attempt to resend the token whenever a connection with the node is established again.

image

Trust Anchors

In order for the bootstrapping to work, there needs to be some anchor where trust is derived from.

To solve this, we present Trust Anchor entities that simply hold a public-private key. A token can be generated from the public private keypair that grants authority for seednodes to distribute tokens that prove that nodes are in the network.

An entity can be a node, a person, or some autonomous system. All that matters is that it is able to give the token to seednodes during bootstrapping so that they have a way to distribute network membership tokens.

Network membership tokens should contain the trust anchor's token embedded within. By comparing the facts between the network membership tokens and the trust anchor tokens, we can determine if a peer node is apart of a network.

Connection Procedure

When a node joins a network, it receives a token with rules and facts that represent its permissions and memberships.

An example what it could look like would be

{
 "rules": [
   "can_connect_to_network($N, $Network) :- node($N), has_membership($N, $Network)."
 ],
 "facts": [
   "node(\"N1\").",
   "has_membership(\"N1\", \"mainnet.polykey.com\").",
   "has_membership(\"N1\", \"testnet.polykey.com\")."
 ]
}
  1. When Node A attempts to connect to Node B, it presents its JWT token.
  2. Node B verifies the token's signature and evaluates its rules and facts against its own policies.
  3. The security policies are evaluated using a Datalog engine or a similar logic programming interpreter.
  4. The connection is allowed or denied based on the evaluation results.

There needs to be a way to bootstrap seednodes of a network with a same private key to use to sign tokens to allow this.

Additional context

https://github.com/MatrixAI/Polykey-Enterprise/issues/11
Related: #779
Related: #780
Related: #781
Related: #782
Related: #783

Tasks

  1. Token request from seednodes RPC handler
  2. Token response from seednodes RPC handler
  3. Authentication RPC handler between seednodes
  4. Modify NodeGraph to only add nodes after authentication
  5. Limit RPC calls whilst peer node is unauthenticated
@amydevs amydevs added the development Standard development label Jul 12, 2024
Copy link

linear bot commented Jul 12, 2024

@amydevs amydevs changed the title Mainnet/Testnet/Private Network Segregation Network Segregation Jul 12, 2024
@amydevs amydevs self-assigned this Jul 14, 2024
@CryptoTotalWar CryptoTotalWar added research Requires research discussion Requires discussion labels Jul 15, 2024
@amydevs amydevs mentioned this issue Jul 18, 2024
7 tasks
@amydevs
Copy link
Contributor Author

amydevs commented Jul 18, 2024

Alternate idea:

Using Multiple Sigchains (sigchain/network)

Otherwise, we can maintain multiple certchains on a single node, one for each network that we want to connect to.

The certchain would work rather much the same. However, by utilising QUICs TLS verification callbacks, we can select a specific certchain to issue when establishing a connection with a new node.

We can do this by

  1. analyzing the certchain of the other node and finding the the signing authority of the root cert, comparing it with the signing authorities of the root certs of our local node.
  2. if a certchain matches, we send that certchain back during the TLS handshake.
  3. If not, we reject the connection.

image

Caveats I've thought of with this could be:

  • It could make nodes that are bootstrapped without internet access impossible to join a network. If the root certchain of a node is supposed to be signed by the certificate authority, when we are offline with no access to the seednodes, we must self sign the root cert of the chain. Once we regain internet connectivity and wish to join the mainnet, there is no way to modify the certchain so that our root cert is signed by seednode certificate authority. Therefore, if a node is bootstrapped offline without a network, it's existing certchain will essentially be useless once it starts connecting to a network.

@amydevs
Copy link
Contributor Author

amydevs commented Jul 19, 2024

an example of a CSR over the wire protocol:

https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment

going to take this into consideration

@amydevs
Copy link
Contributor Author

amydevs commented Jul 19, 2024

The only example i could find of performing a CSR, and signing using the peculiar libraries:

https://gist.github.com/fmonniot/e94ed40e3902415ccb9ca5a1d932297e

I believe this code completely ignores everything in the CSR other than the public key. But i'm pretty sure in our case, we would want to include all the extensions in the CSR, and then have the CA node simply pass the extensions to the X509Certificate generator.

@amydevs
Copy link
Contributor Author

amydevs commented Jul 19, 2024

Peculiar example of using CSR:
https://codesandbox.io/s/generate-csr-0qhed

@amydevs
Copy link
Contributor Author

amydevs commented Jul 22, 2024

mayhaps we should instead have the network be a parameter for the agent constructor, so that we can actually have context of that metadata when placing nodes into the nodegraph? Otherwise there has to be a way to tag nodes to be apart of the same network, even with different seednodes.

@CMCDragonkai
Copy link
Member

The "network" must be a string like "microsoft.enterprise.polykey.com" or "mainnet.polykey.com". It's like a hostname that is claimed by the certificate. Thus in a way one has to "prove" one is allowed to be part of the hostname.

The way we can do that is similar to the ACME protocol... one basically checks the DNS records or HTTP request (although I don't think HTTP makes sense for us here). In our case, it's actually the DNS-SD records.

I'm not sure but how would DNSSEC factor into this from a trust perspective. At some point the trust anchor is going to be self-recursive.

From a philosophy POV, or a mathematical theory perspective. All constructive theory decomposes to a fundamental self-recursive axiom. And so it is with trust as well.

@CMCDragonkai
Copy link
Member

@tegefaulkes should be subscribed here.

Copy link
Contributor

I think the scope of this is getting a little large. So we may need to split it up into sub-issues and specs.

I don't have the full picture yet, but from what I can tell we can break it up into the following.

  1. Expanding the NodeConnection creation and NodeConnectionManager logic to allow for an authenticating stage for a connection. Where access to a network is negotiated. A node is not added to the node graph unless the authentication stage is cleared.
  2. Work on structuring the network claim tokens and how they are issued and added to the Sigchain and the authentication logic.
  3. Work on a smarter claim/token system. Either going with the biscuit capability system, federated ID or something in between. For now we can use much simpler basic JWTs since tokens can be drop in replacements and expanded on later.
  4. The NodeConnectionManager and NodeGraph need to be network aware now. So they need to track what networks connections are apart of and when lists of connected or known nodes are requested, only respond with nodes related to a specific network.

There may be more to add here. we also need to go over the spec and pin down a final design.

@CMCDragonkai
Copy link
Member

@tegefaulkes @amydevs spec separation is needed here. Make sure all PKE related specs should be here: https://github.com/MatrixAI/Polykey-Enterprise/issues/11. And only the open-source PK related work goes here.

@tegefaulkes
Copy link
Contributor

Noted, but I think all of the stuff discussed here so far are related for implementation in Polykey.

That said, we need to work out how to Mint a trust anchor token and distribute it to each seed node. That would be a enterprise specific problem.

@CMCDragonkai
Copy link
Member

PKE is the progenitor of this. We simply inject the hostname of the network into the prime node and it just starts as is. Since we control the DNS as well the only issue is DNS poisoning. We may maintain our own PKE prime node that acts as a parent maintaining a chain. It always ends somewhere as an anchor though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
development Standard development discussion Requires discussion research Requires research
Development

No branches or pull requests

4 participants