Skip to content

Commit

Permalink
Merge pull request #185 from NordSecurity/LLT-4375_meshmap_config_cus…
Browse files Browse the repository at this point in the history
…tom_names_support

Add custom peer names to meshnet config
  • Loading branch information
lcruz99 authored Oct 26, 2023
2 parents 15f6896 + 0455b52 commit d20e959
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* LLT-4432: Use forked system-configuration crate to prevent iOS linking errors
* LLT-3951: IPv6 analytics.
* LLT-4409: Wait for listen port only if meshnet is enabled
* LLT-4375: Add meshmap config support for custom peer names

<br>

Expand Down
8 changes: 8 additions & 0 deletions crates/telio-model/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub struct PeerBase {
pub hostname: String,
/// Ip address of peer
pub ip_addresses: Option<Vec<IpAddr>>,
/// Nickname for the peer
pub nickname: Option<String>,
}

/// Description of a peer
Expand Down Expand Up @@ -223,6 +225,7 @@ mod tests {
"ip_addresses": [
"198.51.100.42"
],
"nickname": "bunnyg",
"discovery_key": "63fe62d704226c770004d1ec31de1192fba88dffc2539ddda29543be878fb6ea",
"relay_address": "disco.nordmesh:12345",
"peers": [
Expand All @@ -235,6 +238,7 @@ mod tests {
"ip_addresses": [
"198.51.100.43"
],
"nickname": "",
"is_local": true,
"user_email": "[email protected]",
"allow_incoming_connections": true,
Expand Down Expand Up @@ -268,6 +272,7 @@ mod tests {
"ip_addresses": [
"198.51.100.43"
],
"nickname": "",
"is_local": true,
"user_email": "[email protected]",
"allow_incoming_connections": true,
Expand Down Expand Up @@ -329,6 +334,7 @@ mod tests {
.unwrap(),
hostname: "everest-alice.nord".to_owned(),
ip_addresses: Some(vec!["198.51.100.42".parse().unwrap()]),
nickname: Some("bunnyg".to_owned()),
},
peers: Some(vec![
Peer {
Expand All @@ -339,6 +345,7 @@ mod tests {
.unwrap(),
hostname: "everest-bob.nord".to_owned(),
ip_addresses: Some(vec!["198.51.100.43".parse().unwrap()]),
nickname: Some("".to_owned()),
},
is_local: true,
allow_incoming_connections: true,
Expand All @@ -352,6 +359,7 @@ mod tests {
.unwrap(),
hostname: "everest-alice.nord".to_owned(),
ip_addresses: Some(vec!["198.51.100.43".parse().unwrap()]),
nickname: None,
},
is_local: false,
allow_incoming_connections: false,
Expand Down
52 changes: 52 additions & 0 deletions nat-lab/tests/mesh_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ def __init__(self, node_id) -> None:
self.node_id = node_id


class NicknameError(Exception):
nickname: str

def __init__(self, nickname) -> None:
self.nickname = nickname


class DuplicateNodeError(NodeError):
pass

Expand All @@ -56,6 +63,14 @@ class AddressCollisionError(NodeError):
pass


class NicknameInvalidError(NicknameError):
pass


class NicknameCollisionError(NodeError):
pass


class FirewallRule:
allow_incoming_connections: bool
allow_peer_send_files: bool
Expand All @@ -77,6 +92,7 @@ class Node:

hostname: str
ip_addresses: List[str]
nickname: Optional[str]
endpoints: List[str]
is_local: bool
allow_connections: bool
Expand All @@ -91,6 +107,7 @@ def __init__(self):
self.public_key = ""
self.hostname = ""
self.ip_addresses = []
self.nickname = None
self.endpoints = []
self.is_local = False
self.allow_connections = False
Expand Down Expand Up @@ -130,6 +147,7 @@ def to_peer_config_for_node(self, node) -> Dict[str, Any]:
"public_key": self.public_key,
"hostname": self.hostname,
"ip_addresses": self.ip_addresses,
"nickname": self.nickname,
"endpoints": self.endpoints,
"is_local": node.is_local and self.is_local,
"allow_connections": self.allow_connections,
Expand Down Expand Up @@ -200,6 +218,39 @@ def assign_ip(self, node_id: str, address: str) -> None:
node = self._get_node(node_id)
node.ip_addresses.append(address)

def assign_nickname(self, node_id: str, nickname: Optional[str]) -> None:
if nickname is None:
raise NicknameInvalidError(nickname)
if len(nickname) > 25:
raise NicknameInvalidError(nickname)
if nickname == "":
raise NicknameInvalidError(nickname)
if " " in nickname:
raise NicknameInvalidError(nickname)
if "--" in nickname:
raise NicknameInvalidError(nickname)
if nickname.endswith("-"):
raise NicknameInvalidError(nickname)
if nickname.startswith("-"):
raise NicknameInvalidError(nickname)
if not nickname.islower():
raise NicknameInvalidError(nickname)

for _, node in self.nodes.items():
if nickname == node.nickname:
raise NicknameCollisionError(node.id)
if nickname == node.name:
raise NicknameCollisionError(node.id)
if nickname == node.hostname:
raise NicknameCollisionError(node.id)

node = self._get_node(node_id)
node.nickname = nickname.lower()

def reset_nickname(self, node_id: str) -> None:
node = self._get_node(node_id)
node.nickname = None

def get_meshmap(
self, node_id: str, derp_servers: Optional[List[Dict[str, Any]]] = None
) -> Dict[str, Any]:
Expand All @@ -215,6 +266,7 @@ def get_meshmap(
"public_key": node.public_key,
"hostname": node.hostname,
"ip_addresses": node.ip_addresses,
"nickname": node.nickname,
"endpoints": node.endpoints,
"peers": peers,
"derp_servers": derp_servers if derp_servers is not None else DERP_SERVERS,
Expand Down
7 changes: 7 additions & 0 deletions nat-lab/tests/telio.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class PeerInfo(DataClassJsonMixin):
is_vpn: bool = False
ip_addresses: List[str] = field(default_factory=lambda: [])
allowed_ips: List[str] = field(default_factory=lambda: [])
nickname: Optional[str] = None
endpoint: Optional[str] = None
hostname: Optional[str] = None
allow_incoming_connections: bool = False
Expand All @@ -95,6 +96,7 @@ def __hash__(self):
self.is_vpn,
tuple(self.ip_addresses),
tuple(self.allowed_ips),
self.nickname,
self.endpoint,
self.hostname,
self.allow_incoming_connections,
Expand Down Expand Up @@ -124,6 +126,11 @@ def __eq__(self, other):
or other.hostname is None
or self.hostname == other.hostname
)
and (
self.nickname is None
or other.nickname is None
or self.nickname == other.nickname
)
and self.allow_incoming_connections == other.allow_incoming_connections
and self.allow_peer_send_files == other.allow_peer_send_files
and self.path == other.path
Expand Down
11 changes: 11 additions & 0 deletions nat-lab/tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ async def test_event_content_meshnet(
is_vpn=False,
ip_addresses=beta.ip_addresses,
allowed_ips=get_allowed_ip_list(beta.ip_addresses),
nickname=None,
endpoint=None,
hostname=beta.name + ".nord",
allow_incoming_connections=True,
Expand All @@ -131,6 +132,7 @@ async def test_event_content_meshnet(
is_vpn=False,
ip_addresses=alpha.ip_addresses,
allowed_ips=get_allowed_ip_list(alpha.ip_addresses),
nickname=None,
endpoint=None,
hostname=alpha.name + ".nord",
allow_incoming_connections=True,
Expand All @@ -156,6 +158,7 @@ async def test_event_content_meshnet(
is_vpn=False,
ip_addresses=beta.ip_addresses,
allowed_ips=get_allowed_ip_list(beta.ip_addresses),
nickname=None,
endpoint=None,
hostname=beta.name + ".nord",
allow_incoming_connections=True,
Expand Down Expand Up @@ -254,6 +257,7 @@ async def test_event_content_vpn_connection(
"100.64.0.1",
],
allowed_ips=["0.0.0.0/0", "::/0"],
nickname=None,
endpoint=f'{wg_server["ipv4"]}:{wg_server["port"]}',
hostname=None,
allow_incoming_connections=False,
Expand Down Expand Up @@ -293,6 +297,7 @@ async def test_event_content_vpn_connection(
"100.64.0.1",
],
allowed_ips=["0.0.0.0/0", "::/0"],
nickname=None,
endpoint=f'{wg_server["ipv4"]}:{wg_server["port"]}',
hostname=None,
allow_incoming_connections=False,
Expand Down Expand Up @@ -391,6 +396,7 @@ async def test_event_content_exit_through_peer(
is_vpn=False,
ip_addresses=beta.ip_addresses,
allowed_ips=get_allowed_ip_list(beta.ip_addresses),
nickname=None,
endpoint=None,
hostname=beta.name + ".nord",
allow_incoming_connections=False,
Expand Down Expand Up @@ -426,6 +432,7 @@ async def test_event_content_exit_through_peer(
is_vpn=False,
ip_addresses=beta.ip_addresses,
allowed_ips=["0.0.0.0/0", "::/0"],
nickname=None,
endpoint=None,
hostname=beta.name + ".nord",
allow_incoming_connections=False,
Expand Down Expand Up @@ -540,6 +547,7 @@ async def test_event_content_meshnet_node_upgrade_direct(
is_vpn=False,
ip_addresses=beta.ip_addresses,
allowed_ips=get_allowed_ip_list(beta.ip_addresses),
nickname=None,
endpoint=None,
hostname=beta.name + ".nord",
allow_incoming_connections=True,
Expand All @@ -560,6 +568,7 @@ async def test_event_content_meshnet_node_upgrade_direct(
is_vpn=False,
ip_addresses=alpha.ip_addresses,
allowed_ips=get_allowed_ip_list(alpha.ip_addresses),
nickname=None,
endpoint=None,
hostname=alpha.name + ".nord",
allow_incoming_connections=True,
Expand Down Expand Up @@ -615,6 +624,7 @@ async def test_event_content_meshnet_node_upgrade_direct(
is_vpn=False,
ip_addresses=beta.ip_addresses,
allowed_ips=get_allowed_ip_list(beta.ip_addresses),
nickname=None,
endpoint=None,
hostname=beta.name + ".nord",
allow_incoming_connections=True,
Expand All @@ -633,6 +643,7 @@ async def test_event_content_meshnet_node_upgrade_direct(
is_vpn=False,
ip_addresses=alpha.ip_addresses,
allowed_ips=get_allowed_ip_list(alpha.ip_addresses),
nickname=None,
endpoint=None,
hostname=alpha.name + ".nord",
allow_incoming_connections=True,
Expand Down
73 changes: 73 additions & 0 deletions nat-lab/tests/test_mesh_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ def test_to_peer_config(self) -> None:
node.public_key = "bb"
node.hostname = "cc"
node.ip_addresses = ["dd"]
node.nickname = "ff"
node.endpoints = ["ee"]

expected = {
"identifier": "aa",
"public_key": "bb",
"hostname": "cc",
"ip_addresses": ["dd"],
"nickname": "ff",
"endpoints": ["ee"],
"is_local": False,
"allow_connections": False,
Expand Down Expand Up @@ -90,6 +92,7 @@ def test_get_meshmap(self) -> None:
alpha.hostname = "aaa"
alpha.ip_addresses = ["bbb"]
alpha.endpoints = ["ccc"]
alpha.nickname = "fff"

beta = api.register(
name="beta", node_id="id-beta", private_key="sk-beta", public_key="pk-beta"
Expand All @@ -102,6 +105,7 @@ def test_get_meshmap(self) -> None:
"public_key": pk_alpha,
"hostname": "aaa",
"ip_addresses": ["bbb"],
"nickname": "fff",
"endpoints": ["ccc"],
"peers": [beta.to_peer_config_for_node(alpha)],
"derp_servers": mesh_api.DERP_SERVERS,
Expand Down Expand Up @@ -138,3 +142,72 @@ def test_assign_ip(self):
api.assign_ip("id2", "3.3.3.3")
assert node1.ip_addresses == ["1.1.1.1"]
assert node2.ip_addresses == ["2.2.2.2", "3.3.3.3"]

def test_assign_nickname(self):
api = API()
alpha = api.register(
name="alpha", node_id="id1", private_key="sk", public_key="pk"
)
beta = api.register(
name="beta", node_id="id2", private_key="sk", public_key="pk"
)

api.assign_nickname("id1", "john")
api.assign_nickname("id2", "jane")

assert alpha.name == "alpha"
assert beta.name == "beta"
assert alpha.nickname == "john"
assert beta.nickname == "jane"

api.reset_nickname("id1")
api.reset_nickname("id2")

assert alpha.nickname is None
assert beta.nickname is None

def test_assign_invalid_nickname(self):
api = API()
alpha = api.register(
name="alpha", node_id="id1", private_key="sk", public_key="pk"
)

with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", None)
with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", "")
with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", "-john")
with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", "john-")
with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", "john doe")
with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", "johnsomethingsomethingsomething")
with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", "joh--n")
with pytest.raises(mesh_api.NicknameInvalidError):
api.assign_nickname("id1", "jOhN")
with pytest.raises(mesh_api.NicknameCollisionError):
api.assign_nickname("id1", "alpha")
with pytest.raises(mesh_api.NicknameCollisionError):
api.assign_nickname("id1", "alpha.nord")

assert alpha.nickname is None

def test_assign_duplicated_nickname(self):
api = API()
alpha = api.register(
name="alpha", node_id="id1", private_key="sk", public_key="pk"
)
beta = api.register(
name="beta", node_id="id2", private_key="sk", public_key="pk"
)

api.assign_nickname("id1", "john")
with pytest.raises(mesh_api.NicknameCollisionError) as e:
api.assign_nickname("id2", "john")

assert e.value.node_id == "id1"
assert alpha.nickname == "john"
assert beta.nickname is None
Loading

0 comments on commit d20e959

Please sign in to comment.