src | ||
Readme.md |
wireguard dynamic mesh (without central/3rd party servers)
This project aims to create a specification and implementation of a "wg-dynamic-mesh" protocol.
Using the protocol peers supporting the protocol can exchange endpoint addresses and general peer reachability,
so the peers could create a (full) mesh network without requiring a relay node
(but another node could be used as relay when peers can't reach each other )
ideas:
- protocol running over an ipv6 linklocal (
fe80::
) addresses inside the tunnel (addresses may be automatically generated using e.g the wireguard pubkey likefe80::<last 64 bits of pubkey>
) - push or pull (or both) based (TBD), peer to peer connection (inside tunnel)
- should also work with peers with don't implement the protocol (mobile devices which don't have an "app" for this protocol yet)
- in protocol message containing list of peers identified by pubkey with endpoint address and (optionally) allowed ips (allowedIPs could be dangerous as it could be used to install (arbitrary) routes onto the host)
future (version2):
- make selected nodes act as relay for cases where one peer has only ipv4 and another only has ipv6 (or those 2 peers can't reach eachother for any reason)
- make selected nodes be "authorative" for a given prefix (i.e. node1 and its direct peers owned/operated by person1 is authorative for 2001:db8:0001::/48 and node2 and its peers by person2 has 2001:db8:0002::/48 ) where the central-ish node(s) of the specific person can be trusted with the specific prefix(es) and any subnets
- ...
(api) spec / protocol version 1.0:
The messages are to be send over plain TCP on port 51820 (by default).
As encoding JSON MUST be used with any message containing the messageType
key to identify the type of the message with the other keys and values as described below for that messageType.
The JSON objects MUST be seperated by a NUL byte (as thats an invalid byte for JSON)
a program implementing the protocol SHOULD attempt to initiate this protocol with
- all peers of the wireguard interface OR
- only a sub- or superset of the peers configured in an additional (optional) config file
during startup or shortly after a (configured) peer successfully made a handshake
establishing a connection / handshake:
SYN
:
To start the initiating peer sends a SYN
message containing the protocol
string: WG-DYNAMIC-MESH
and 'version' string with major and minor number("1.0")
(TODO: decide on protocol string)
the version should be the highest version of the protocol the peer supports
when a SYN
gets received by a peer and the protocol string matches, it should reply with a SYNACK
message.
example:
{
"messageType": "SYN",
"protocol": "WG-DYNAMIC-MESH",
"version": "1.0"
}
SYNACK
:
send as a reply to the SYN
message containing the protocol string and version, while the version is the minimum of the peers version received by the SYN
and the own highest supported protocol version
example:
{
"messageType": "SYNACK",
"protocol": "WG-DYNAMIC-MESH",
"version": "1.0"
}
NAK
(in handshake):
may be send by either peer after SYN
or SYNACK
if a peer is not be backwards compatible with peer's highest protocol version (e.g. only supports 2.0 but not 1.0).
Not implementing backwards compatibility should only be done if previous versions of the protocol had serious (security) issues.
attributes:
- reason (optional, string): reason why the connection got closed (useful for debugging)
after sending or receiving this message the connection SHOULD get closed and may be reattempted at a later time (but without changes it is likely that another NAK will get received again on the new connection)
example:
{
"messageType": "NAK",
"reason": "invalid message"
}
session
SELFINFO
:
send after the handshake or whenver one of the attributes changes(optional, the only thing that even can change is the addresses
)
the message contains:
- the senders wireguard
pubkey
(base64 encoded as string), addresses
: (list of strings) of the addresses on the interface (TBD),listenport
: (int) of its wireguard interface,hostname
: (string) optional hostname with A and/or AAAA records where the peer is supposed to be reachable atreadonly
: (bool) true if (for some reason) the communicating process can't (or isn't configured to) write the required configs to the wireguard interface, but can still read the required data
the receiving peer MUST verify that the connection came from the peer with the pubkey that got specified, if not it should reply with a NAK
PEERREQUEST
:
request a the peer to send a PEERINFO
message
the receiving peer SHOULD then send a PEERINFO
message
PEERINFO
:
notifies the peer about the state of the own interface, may be send at any time if something changed or during the first exchange or on PEERREQUEST
attributes:
- list of peers of the wireguard interface with
pubkey
: (string) base64 encoded wireguard public keyendpoint
: (string) containing<hostname>:<port>
, may be an empty string orNull
/Nil
/None
if not yet knownallowedIps
: (list of strings) with<ip>/<subnet_length>
lastHandshake
: (int) seconds since last handshake in unix epoch (e.g. output ofwg show $interface latest-handshakes
)hostname
: (string) optional either the hostname from theSELFINFO
from that peer (SHOULD get verified) OR from initial wireguard configwgDynamic
: (string/enum) whether that peer supports this protocol,one offull
(can change interface config),readonly
(can only read interface config, but not write) orno
(default, peer hasn't yet initiated a session with the sender peer using the protocol)full
/readonly
is set according to thereadonly
attribute from that peersSELFINFO
peers
: (list of strings) containing base64 encoded public keys of that peer's peers (as send by that peer using this protocol), may be empty if that peer hasn't been communicated with yet
the peer receiving this message may attempt to initiate connections with the listed peers by configuring the wireguard interface accordingly (if enabled/supported by the platform)
depending on the implementation the program MAY add additional peers to the wireguard interface, (but note that that may be a security concern), or just update the endpoints of the existing peers where feasable
(e.g. if the local lastHandshake
to the given peer is a while ago, and but the received lastHandshake
attribute has a more recent timestamp(e.g. within the last ~5mins))
for updating the wireguard interface config other peer's PEERINFO
may also be taken into account
FIN
no fields
send when one peer wants to close the session, normal socket.close()
could also be used if TCP gets used. (only added for the case the protocol is over UDP, which doesn't have connection tracking)
after sending the sender MUST close the connection within a reasonable time, and incase any further packets get received those should be ignored
NAK
(in session)
send in reply to invalid messages
attributes:
- reason (optional, string): reason why the connection got closed (useful for debugging)
after sending or receiving this message the connection SHOULD get closed and may be reattempted at a later time (but without changes it is likely that another NAK will get received again on the new connection)
Inspitations:
- polynet
- wg-dynamic
- tailscale/headscale
- zerotier
- (other similar services)