feat(dns): support local static records (#2708)

* feat(dns): support local static records

* fix(dns): normalize zone names with LowerName, preload static zones, and deprecate tld_dns_zone

- Normalize DNS zone names to LowerName across the server catalog and RPC boundaries to prevent uppercase/non-canonical zone suffixes from clearing node records.
- Preload all configured static zones during server initialization before registering the resolver and applying system DNS configuration.
- Deprecate tld_dns_zone in favor of [dns] domain, keeping tld_dns_zone as a hidden fallback without exposing it in help or schema defaults.
- Keep MagicDnsRecordStore using LowerName and restrict dns_route_identity to node hostname and IP.
This commit is contained in:
Luna Yao authored and GitHub committed 2026-11-08 06:58:24 +08:00
1 parent ebca444374
commit 105f44d322
38 files changed
+2338 -332

No files matched your search

Generated
+10 -9
View File
@@ -2675,6 +2675,7 @@ dependencies = [
"getrandom 0.2.17",
"getrandom 0.3.4",
"guarden",
"hickory-proto",
"hmac 0.12.1",
"http-body-util",
"hyper",
@@ -5323,6 +5324,15 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f"
[[package]]
name = "lzma-rust2"
version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b"
dependencies = [
"sha2 0.11.0",
]
[[package]]
name = "lzokay-native"
version = "0.2.0"
@@ -5333,15 +5343,6 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "lzma-rust2"
version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b"
dependencies = [
"sha2 0.11.0",
]
[[package]]
name = "mac_address"
version = "1.1.8"
+94
View File
@@ -0,0 +1,94 @@
# Local Magic DNS records
Configure records through a TOML file. Enable Magic DNS as before and add
static records using DNS zone file syntax:
```toml
[flags]
accept_dns = true
[dns]
domain = "et.net."
[[dns.zone]]
origin = "et.net."
ttl = 60
records = [
"nas IN A 10.0.0.8",
"nas IN AAAA fd00::8",
"files IN CNAME nas",
'info IN TXT "local records"',
]
[[dns.zone]]
origin = "example.test."
records = ["service 120 IN A 10.0.0.9"]
```
`domain` under `[dns]` sets the default Magic DNS domain, defaulting to `"et.net."`
when omitted, and supersedes the deprecated `flags.tld_dns_zone`.
`origin` must be a non-root DNS name. Record names are relative to it;
absolute names must belong to that zone.
`ttl` supplies the default TTL in seconds, or zero when omitted. Individual
records may specify their own TTL. Zone file directives such as `$INCLUDE` are
not accepted.
Static records come from the local configuration of the EasyTier instance that
wins the local Magic DNS server election. They coexist with automatically
generated node records and survive route updates. Configuration changes take
effect when the instance restarts. If the elected instance has no `[dns]`
configuration, no static records are served, even when another local instance
has some. Records are not distributed to other machines.
Only the elected instance's configured zones are added to the system resolver
match list, after the default zone. Zones configured by other local instances
are neither served nor used for system name resolution.
Configured zones are authoritative: a name missing from a configured zone does
not fall back to system DNS. Queries outside those zones retain the existing
system DNS forwarding behavior. This does not change Magic DNS activation,
the fixed DNS address, TUN requirements, or platform DNS setup. Systems without
automatic DNS setup must continue to configure their resolver manually.
## Migration and configuration definitions
The file and management API share `core_config.DnsConfigRaw`. `NetworkConfig.dns`
uses field 77. These DNS inputs were not released as a public string contract.
The local `UpdateDnsRecordRequest` is unchanged: static records stay local to the
server instance and are never carried over RPC.
Optional arrays use reusable messages: `common.RepeatedString` for record strings and
`core_config.RepeatedDnsZoneConfigRaw` for zones. An absent wrapper means the array was
omitted; a present wrapper with no items means an explicit empty array. TOML still
uses `[[dns.zone]]`, `zone = []`, and `records = []`. The collection wrappers use
ordinary Serde with `serde(transparent)`. Both `DnsConfigRaw` and
`DnsZoneConfigRaw` use pbjson and delegate their collection fields to these
transparent wrappers. TOML and DNS management JSON both use `zone`, with no
field renaming:
```json
{"dns":{"zone":[{"origin":"example.test.","ttl":0,"records":["nas IN A 192.0.2.1"]}]}}
```
The protobuf field numbers, field types and wrapper nesting define the binary
contract. The DNS root uses zone at tag 1 and domain at tag 2; each zone uses
origin 1, ttl 2 and records 3; collection wrappers use items at tag 1. A future
configuration generator can rename helper messages while preserving this layout.
Existing TOML and JSON field names remain part of their respective formats.
`ConfigLoader::get_dns` and `set_dns` now use this Raw message directly. Native
DNS input is parsed directly into runtime records. Defaults, collection unwrapping,
domain normalization and record validation stay in the native DNS parser. Management
projections and browser form merging preserve the Raw collection presence.
When replacing the legacy DNS runtime, replace the interim
`ConfigLoader::get_dns`/`set_dns` methods and their internal call sites as needed.
dns-policy removes the legacy `magic_dns.proto` along with the old DNS server;
its node RPC and zone distribution protocol replace this local transport. This
interim feature does not extend that transport.
The existing `accept_dns` switch and this interim runtime's authoritative-zone
behavior remain in use for now. On migration, the full DNS implementation and
the shared definition own activation and fallthrough defaults; these temporary
behaviors are not additional compatibility requirements for that implementation.
+1
View File
@@ -34,6 +34,7 @@ cidr = { workspace = true, features = ["serde"] }
crossbeam-utils.workspace = true
dashmap.workspace = true
bon.workspace = true
hickory-proto = { version = "0.25.2", default-features = false }
easytier-proto = { workspace = true, features = ["core"] }
futures = { workspace = true, features = ["std", "async-await"] }
guarden.workspace = true
+1
View File
@@ -14,6 +14,7 @@ use super::{
pub fn network_config_from_toml(config: &TomlConfig) -> NetworkConfig {
let default_config = TomlConfig::default();
let mut result = NetworkConfig {
dns: config.get_dns(),
instance_id: Some(config.get_id().to_string()),
dhcp: Some(config.get_dhcp()),
dhcp_ipv4_cidr: config.get_dhcp_config().cidr().map(|cidr| cidr.to_string()),
+123 -32
View File
@@ -87,7 +87,10 @@ pub(crate) fn set_network_flags(result: &mut NetworkConfig, flags: FlagsPatch) {
result.enable_udp_broadcast_relay = flags.enable_udp_broadcast_relay;
result.disable_sym_hole_punching = flags.disable_sym_hole_punching;
result.enable_magic_dns = flags.accept_dns;
result.tld_dns_zone = flags.tld_dns_zone;
#[allow(deprecated)]
{
result.tld_dns_zone = flags.tld_dns_zone;
}
result.mtu = flags.mtu.map(|mtu| mtu as i32);
result.data_compress_algo = flags.data_compress_algo;
result.encryption_algorithm = flags.encryption_algorithm;
@@ -116,6 +119,7 @@ pub trait NetworkConfigExt {
))]
const FORM_MANAGED_TOML_FIELDS: &[&str] = &[
"hostname",
"dns",
"instance_id",
"ipv4",
"ipv6_public_addr_provider",
@@ -244,6 +248,7 @@ impl NetworkConfigExt for NetworkConfig {
#[allow(deprecated)]
fn gen_config(&self) -> Result<TomlConfigLoader, anyhow::Error> {
let cfg = TomlConfigLoader::default();
cfg.set_dns(self.dns.clone());
cfg.set_id(
self.instance_id
.clone()
@@ -514,6 +519,7 @@ impl NetworkConfigExt for NetworkConfig {
}
}),
accept_dns: self.enable_magic_dns,
#[allow(deprecated)]
tld_dns_zone: self.tld_dns_zone.clone(),
mtu: self
.mtu
@@ -559,6 +565,7 @@ impl NetworkConfigExt for NetworkConfig {
..Default::default()
};
result.dns = config.get_dns();
result.instance_id = Some(config.get_id().to_string());
if config.get_hostname() != default_config.get_hostname() {
result.hostname = Some(config.get_hostname());
@@ -698,6 +705,121 @@ mod tests {
use super::*;
use easytier_proto::common::Flags;
#[test]
fn dns_records_survive_toml_and_form_edits() {
for dns in [
"",
"[dns]\n",
"[dns]\ndomain = 'mesh.test.'\n",
"[[dns.zone]]\norigin = 'example.test.'\nttl = 0\nrecords = ['nas IN A 10.0.0.8', 'nas IN AAAA fd00::8']\n",
] {
let config = TomlConfigLoader::new_from_str(dns).unwrap();
let expected = config.get_dns();
config.set_hostname(Some("changed".to_owned()));
assert_eq!(config.get_dns(), expected);
#[cfg(feature = "config-write")]
assert_eq!(
TomlConfigLoader::new_from_str(&config.dump())
.unwrap()
.get_dns(),
expected
);
#[cfg(feature = "browser-config")]
{
let api = NetworkConfig::new_from_config(&config).unwrap();
let merged = merge_network_config_toml(dns, &api).unwrap();
let restored = TomlConfigLoader::new_from_str(&merged).unwrap();
assert_eq!(restored.get_dns(), expected);
assert_eq!(restored.get_hostname(), "changed");
}
}
}
#[cfg(feature = "browser-config")]
#[test]
fn dns_raw_presence_survives_management_json_and_file_roundtrips() {
for input in [
"",
"[dns]\n",
"[dns]\ndomain = 'mesh.test.'\n",
"[dns]\nzone = []\n",
"[[dns.zone]]\norigin = 'example.test.'\n",
"[[dns.zone]]\norigin = 'example.test.'\nttl = 0\nrecords = []\n",
"[[dns.zone]]\norigin = 'example.test.'\nrecords = ['@ IN A 192.0.2.1']\n",
] {
let config = TomlConfigLoader::new_from_str(input).unwrap();
let expected = config.get_dns();
let network = NetworkConfig::new_from_config(&config).unwrap();
assert_eq!(network.dns, expected, "{input}");
let json = serde_json::to_value(&network).unwrap();
if let Some(records) = expected.as_ref().and_then(|dns| dns.zone.as_ref()) {
assert_eq!(
json["dns"]["zone"].as_array().unwrap().len(),
records.items.len()
);
}
if let Some(domain) = expected.as_ref().and_then(|dns| dns.domain.as_deref()) {
assert_eq!(json["dns"]["domain"].as_str(), Some(domain));
}
let network: NetworkConfig = serde_json::from_value(json).unwrap();
let restored = network.gen_config().unwrap();
assert_eq!(restored.get_dns(), expected, "{input}");
assert_eq!(
TomlConfigLoader::new_from_str(&restored.dump())
.unwrap()
.get_dns(),
expected
);
let merged = merge_network_config_toml(input, &network).unwrap();
assert_eq!(
TomlConfigLoader::new_from_str(&merged).unwrap().get_dns(),
expected
);
#[cfg(feature = "web-client")]
assert_eq!(
crate::config::api::network_config_from_toml(&config).dns,
expected
);
}
let missing = TomlConfigLoader::new_from_str("[dns]\n")
.unwrap()
.get_dns()
.unwrap();
let empty = TomlConfigLoader::new_from_str("[dns]\nzone = []\n")
.unwrap()
.get_dns()
.unwrap();
assert!(missing.zone.is_none());
assert!(empty.zone.unwrap().items.is_empty());
let input = "[[dns.zone]]\norigin = 'example.test.'\nrecords = []\n";
let mut network =
NetworkConfig::new_from_config(TomlConfigLoader::new_from_str(input).unwrap()).unwrap();
network.dns = None;
let cleared = merge_network_config_toml(input, &network).unwrap();
assert!(
TomlConfigLoader::new_from_str(&cleared)
.unwrap()
.get_dns()
.is_none()
);
}
#[test]
fn dns_file_input_rejects_unknown_fields_and_invalid_shapes() {
for input in [
"[dns]\nzoen = []\n",
"[[dns.zone]]\norigin = 'example.test.'\nrecrods = []\n",
"[[dns.zone]]\norigin = 'example.test.'\nrecords = {}\n",
"[[dns.zone]]\norigin = 'example.test.'\nttl = -1\n",
] {
assert!(
TomlConfigLoader::new_from_str(input).is_err(),
"accepted {input}"
);
}
}
fn api_portal_config() -> manage::VpnPortalConfig {
manage::VpnPortalConfig {
enabled: None,
@@ -931,21 +1053,6 @@ mod tests {
);
}
#[test]
fn magic_dns_zone_round_trips_missing_default_and_custom_values() {
for zone in [None, Some("et.net."), Some("nas.internal.")] {
let input = NetworkConfig {
tld_dns_zone: zone.map(str::to_owned),
..standalone_config()
};
let config = input.gen_config().unwrap();
let config = TomlConfigLoader::new_from_str(&config.dump()).unwrap();
assert_eq!(config.get_flags().tld_dns_zone, zone.unwrap_or("et.net."));
let output = NetworkConfig::new_from_config(&config).unwrap();
assert_eq!(output.tld_dns_zone.as_deref(), zone);
}
}
#[test]
fn vpn_portal_api_config_round_trips_through_toml_model() {
let input = NetworkConfig {
@@ -1062,22 +1169,6 @@ mod tests {
assert!(config.get_vpn_portal_config().is_none());
}
#[cfg(feature = "browser-config")]
#[test]
fn browser_merge_updates_and_clears_magic_dns_zone() {
let original = "[flags]\ntld_dns_zone = \"old.internal.\"\n";
for zone in [None, Some("et.net."), Some("nas.internal.")] {
let input = NetworkConfig {
tld_dns_zone: zone.map(str::to_owned),
..standalone_config()
};
let merged = merge_network_config_toml(original, &input).unwrap();
let output = TomlConfigLoader::new_from_str(&merged).unwrap();
assert_eq!(output.get_flags().tld_dns_zone, zone.unwrap_or("et.net."));
assert_eq!(output.get_flags_patch().tld_dns_zone.as_deref(), zone);
}
}
#[cfg(feature = "browser-config")]
#[test]
fn browser_merge_reconciles_renamed_flags() {
+15
View File
@@ -20,6 +20,9 @@ pub const DEFAULT_ET_DNS_ZONE: &str = "et.net.";
pub use crate::proto::common::{Flags, FlagsPatch};
// File and management inputs share these protobuf Raw messages.
pub use crate::proto::core_config::{DnsConfigRaw as DnsConfig, DnsZoneConfigRaw as DnsZoneConfig};
pub(crate) fn default_instance_name() -> String {
"default".to_owned()
}
@@ -32,6 +35,9 @@ pub trait ConfigLoader: Send + Sync {
fn get_hostname(&self) -> String;
fn set_hostname(&self, name: Option<String>);
fn get_dns(&self) -> Option<DnsConfig>;
fn set_dns(&self, dns: Option<DnsConfig>);
fn get_inst_name(&self) -> String;
fn set_inst_name(&self, name: String);
@@ -409,6 +415,7 @@ impl std::fmt::Debug for ManagedCredentialConfig {
struct Config {
netns: Option<String>,
hostname: Option<String>,
dns: Option<DnsConfig>,
instance_name: Option<String>,
instance_id: Option<uuid::Uuid>,
ipv4: Option<String>,
@@ -672,6 +679,14 @@ impl ConfigLoader for TomlConfig {
self.config.lock().unwrap().hostname = name;
}
fn get_dns(&self) -> Option<DnsConfig> {
self.config.lock().unwrap().dns.clone()
}
fn set_dns(&self, dns: Option<DnsConfig>) {
self.config.lock().unwrap().dns = dns;
}
fn get_netns(&self) -> Option<String> {
self.config.lock().unwrap().netns.clone()
}
+26 -39
View File
@@ -1,6 +1,7 @@
use std::{collections::BTreeMap, net::Ipv4Addr, sync::Mutex, time::Duration};
use async_trait::async_trait;
use hickory_proto::rr::LowerName;
use quanta::Instant;
use crate::peers::peer_manager::PeerManagerCore;
@@ -15,7 +16,6 @@ pub struct MagicDnsRoute {
pub struct MagicDnsRouteSnapshot {
pub revision: Instant,
pub routes: Vec<MagicDnsRouteAdvertisement>,
pub zone: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -42,22 +42,18 @@ fn magic_dns_route_advertisement(
fn magic_dns_route_snapshot(
revision: Instant,
routes: Vec<crate::proto::core_peer::peer::Route>,
local_identity: (String, Option<crate::proto::common::Ipv4Inet>, String),
local_identity: (String, Option<crate::proto::common::Ipv4Inet>),
) -> MagicDnsRouteSnapshot {
let mut routes = routes
.into_iter()
.map(magic_dns_route_advertisement)
.collect::<Vec<_>>();
let (hostname, ipv4_addr, zone) = local_identity;
let (hostname, ipv4_addr) = local_identity;
routes.push(MagicDnsRouteAdvertisement {
hostname,
ipv4_addr,
});
MagicDnsRouteSnapshot {
revision,
routes,
zone,
}
MagicDnsRouteSnapshot { revision, routes }
}
#[async_trait]
@@ -112,12 +108,12 @@ where
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MagicDnsRecordSnapshot {
pub zones: BTreeMap<String, Vec<MagicDnsRoute>>,
pub zones: BTreeMap<LowerName, Vec<MagicDnsRoute>>,
}
#[derive(Debug, Default)]
pub struct MagicDnsRecordStore {
zones: Mutex<BTreeMap<String, BTreeMap<String, Vec<MagicDnsRoute>>>>,
zones: Mutex<BTreeMap<LowerName, BTreeMap<String, Vec<MagicDnsRoute>>>>,
}
impl MagicDnsRecordStore {
@@ -128,7 +124,7 @@ impl MagicDnsRecordStore {
/// authoritative.
pub fn replace_client_routes(
&self,
zone: String,
zone: LowerName,
client: String,
routes: Vec<MagicDnsRoute>,
) -> bool {
@@ -153,7 +149,7 @@ impl MagicDnsRecordStore {
/// Removes a disconnected client from every zone and returns the zones
/// that became empty.
pub fn remove_client(&self, client: &str) -> Vec<String> {
pub fn remove_client(&self, client: &str) -> Vec<LowerName> {
let mut zones = self.zones.lock().unwrap();
let mut removed_zones = Vec::new();
zones.retain(|zone, routes_by_client| {
@@ -215,14 +211,9 @@ mod tests {
let snapshot = magic_dns_route_snapshot(
Instant::now(),
Vec::new(),
(
"portable-node".to_owned(),
Some(ipv4_addr),
"et.net.".to_owned(),
),
("portable-node".to_owned(), Some(ipv4_addr)),
);
assert_eq!(snapshot.zone, "et.net.");
assert_eq!(
snapshot.routes,
[MagicDnsRouteAdvertisement {
@@ -245,7 +236,6 @@ mod tests {
hostname: "node-a".to_owned(),
ipv4_addr: Some("10.1.0.1/24".parse::<cidr::Ipv4Inet>().unwrap().into()),
}],
zone: "et.net.".to_owned(),
}
}
@@ -351,24 +341,25 @@ mod tests {
#[test]
fn replaces_routes_for_the_same_client_without_touching_other_clients() {
let zone: LowerName = "et.net.".parse().unwrap();
let store = MagicDnsRecordStore::default();
assert!(!store.replace_client_routes(
"et.net.".to_owned(),
zone.clone(),
"tcp://client-a".to_owned(),
vec![route("old-a", [10, 0, 0, 1])],
));
assert!(!store.replace_client_routes(
"et.net.".to_owned(),
zone.clone(),
"tcp://client-b".to_owned(),
vec![route("peer-b", [10, 0, 0, 2])],
));
assert!(!store.replace_client_routes(
"et.net.".to_owned(),
zone.clone(),
"tcp://client-a".to_owned(),
vec![route("new-a", [10, 0, 0, 3])],
));
let routes = &store.snapshot().zones["et.net."];
let routes = &store.snapshot().zones[&zone];
assert_eq!(routes.len(), 2);
assert!(routes.iter().any(|route| route.hostname == "new-a"));
assert!(routes.iter().any(|route| route.hostname == "peer-b"));
@@ -377,53 +368,49 @@ mod tests {
#[test]
fn empty_update_removes_only_the_target_client_and_reports_empty_zone() {
let zone: LowerName = "et.net.".parse().unwrap();
let store = MagicDnsRecordStore::default();
store.replace_client_routes(
"et.net.".to_owned(),
zone.clone(),
"tcp://client-a".to_owned(),
vec![route("peer-a", [10, 0, 0, 1])],
);
store.replace_client_routes(
"et.net.".to_owned(),
zone.clone(),
"tcp://client-b".to_owned(),
vec![route("peer-b", [10, 0, 0, 2])],
);
assert!(!store.replace_client_routes(
"et.net.".to_owned(),
zone.clone(),
"tcp://client-a".to_owned(),
Vec::new(),
));
assert!(store.replace_client_routes(
"et.net.".to_owned(),
"tcp://client-b".to_owned(),
Vec::new(),
));
assert!(store.replace_client_routes(zone, "tcp://client-b".to_owned(), Vec::new()));
assert!(store.snapshot().zones.is_empty());
}
#[test]
fn disconnect_removes_client_from_all_zones() {
let store = MagicDnsRecordStore::default();
for zone in ["a.et.net.", "b.et.net."] {
let zone_a: LowerName = "a.et.net.".parse().unwrap();
let zone_b: LowerName = "b.et.net.".parse().unwrap();
for zone in [&zone_a, &zone_b] {
store.replace_client_routes(
zone.to_owned(),
zone.clone(),
"tcp://client-a".to_owned(),
vec![route("peer-a", [10, 0, 0, 1])],
);
}
store.replace_client_routes(
"b.et.net.".to_owned(),
zone_b.clone(),
"tcp://client-b".to_owned(),
vec![route("peer-b", [10, 0, 0, 2])],
);
assert_eq!(
store.remove_client("tcp://client-a"),
vec!["a.et.net.".to_owned()]
);
assert_eq!(store.remove_client("tcp://client-a"), vec![zone_a]);
let snapshot = store.snapshot();
assert_eq!(snapshot.zones.len(), 1);
assert_eq!(snapshot.zones["b.et.net."][0].hostname, "peer-b");
assert_eq!(snapshot.zones[&zone_b][0].hostname, "peer-b");
}
}
+2 -9
View File
@@ -1493,14 +1493,8 @@ impl PeerManagerCore {
&self.network_name
}
pub(crate) fn dns_route_identity(
&self,
) -> (String, Option<crate::proto::common::Ipv4Inet>, String) {
(
self.context.hostname(),
self.context.ipv4().map(Into::into),
self.context.flags().tld_dns_zone,
)
pub(crate) fn dns_route_identity(&self) -> (String, Option<crate::proto::common::Ipv4Inet>) {
(self.context.hostname(), self.context.ipv4().map(Into::into))
}
pub fn p2p_policy_flags(&self) -> P2pPolicyFlags {
@@ -4390,7 +4384,6 @@ mod tests {
(
"portable-node".to_owned(),
Some("10.20.0.91/16".parse::<cidr::Ipv4Inet>().unwrap().into()),
String::new(),
)
);
}
+143 -118
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v7.35.1
// protoc v7.36.2
// source: api_manage.proto
package manage
@@ -10,6 +10,7 @@ import (
acl "github.com/easytier/easytier/easytier-go/proto/acl"
instance "github.com/easytier/easytier/easytier-go/proto/api/instance"
common "github.com/easytier/easytier/easytier-go/proto/common"
core_config "github.com/easytier/easytier/easytier-go/proto/core_config"
peer_rpc "github.com/easytier/easytier/easytier-go/proto/peer_rpc"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -147,10 +148,12 @@ type NetworkConfig struct {
AdvancedSettings *bool `protobuf:"varint,16,opt,name=advanced_settings,json=advancedSettings,proto3,oneof" json:"advanced_settings,omitempty"`
ListenerUrls []string `protobuf:"bytes,17,rep,name=listener_urls,json=listenerUrls,proto3" json:"listener_urls,omitempty"`
// optional int32 rpc_port = 18;
LatencyFirst *bool `protobuf:"varint,19,opt,name=latency_first,json=latencyFirst,proto3,oneof" json:"latency_first,omitempty"`
DevName *string `protobuf:"bytes,20,opt,name=dev_name,json=devName,proto3,oneof" json:"dev_name,omitempty"`
UseSmoltcp *bool `protobuf:"varint,21,opt,name=use_smoltcp,json=useSmoltcp,proto3,oneof" json:"use_smoltcp,omitempty"`
DisableIpv6 *bool `protobuf:"varint,47,opt,name=disable_ipv6,json=disableIpv6,proto3,oneof" json:"disable_ipv6,omitempty"`
LatencyFirst *bool `protobuf:"varint,19,opt,name=latency_first,json=latencyFirst,proto3,oneof" json:"latency_first,omitempty"`
DevName *string `protobuf:"bytes,20,opt,name=dev_name,json=devName,proto3,oneof" json:"dev_name,omitempty"`
UseSmoltcp *bool `protobuf:"varint,21,opt,name=use_smoltcp,json=useSmoltcp,proto3,oneof" json:"use_smoltcp,omitempty"`
DisableIpv6 *bool `protobuf:"varint,47,opt,name=disable_ipv6,json=disableIpv6,proto3,oneof" json:"disable_ipv6,omitempty"`
// Deprecated: Marked as deprecated in api_manage.proto.
TldDnsZone *string `protobuf:"bytes,73,opt,name=tld_dns_zone,json=tldDnsZone,proto3,oneof" json:"tld_dns_zone,omitempty"`
EnableKcpProxy *bool `protobuf:"varint,22,opt,name=enable_kcp_proxy,json=enableKcpProxy,proto3,oneof" json:"enable_kcp_proxy,omitempty"`
DisableKcpInput *bool `protobuf:"varint,23,opt,name=disable_kcp_input,json=disableKcpInput,proto3,oneof" json:"disable_kcp_input,omitempty"`
DisableP2P *bool `protobuf:"varint,24,opt,name=disable_p2p,json=disableP2p,proto3,oneof" json:"disable_p2p,omitempty"`
@@ -201,9 +204,10 @@ type NetworkConfig struct {
ManagedCredentials []*ManagedCredentialConfig `protobuf:"bytes,71,rep,name=managed_credentials,json=managedCredentials,proto3" json:"managed_credentials,omitempty"`
PreferPeerRelay *bool `protobuf:"varint,72,opt,name=prefer_peer_relay,json=preferPeerRelay,proto3,oneof" json:"prefer_peer_relay,omitempty"`
// Bootstrap subnet used when DHCP has not learned a subnet from peers.
DhcpIpv4Cidr *string `protobuf:"bytes,74,opt,name=dhcp_ipv4_cidr,json=dhcpIpv4Cidr,proto3,oneof" json:"dhcp_ipv4_cidr,omitempty"`
Dhcpv6 *bool `protobuf:"varint,75,opt,name=dhcpv6,proto3,oneof" json:"dhcpv6,omitempty"`
DhcpIpv6Cidr *string `protobuf:"bytes,76,opt,name=dhcp_ipv6_cidr,json=dhcpIpv6Cidr,proto3,oneof" json:"dhcp_ipv6_cidr,omitempty"`
DhcpIpv4Cidr *string `protobuf:"bytes,74,opt,name=dhcp_ipv4_cidr,json=dhcpIpv4Cidr,proto3,oneof" json:"dhcp_ipv4_cidr,omitempty"`
Dhcpv6 *bool `protobuf:"varint,75,opt,name=dhcpv6,proto3,oneof" json:"dhcpv6,omitempty"`
DhcpIpv6Cidr *string `protobuf:"bytes,76,opt,name=dhcp_ipv6_cidr,json=dhcpIpv6Cidr,proto3,oneof" json:"dhcp_ipv6_cidr,omitempty"`
Dns *core_config.DnsConfigRaw `protobuf:"bytes,77,opt,name=dns,proto3" json:"dns,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -389,6 +393,14 @@ func (x *NetworkConfig) GetDisableIpv6() bool {
return false
}
// Deprecated: Marked as deprecated in api_manage.proto.
func (x *NetworkConfig) GetTldDnsZone() string {
if x != nil && x.TldDnsZone != nil {
return *x.TldDnsZone
}
return ""
}
func (x *NetworkConfig) GetEnableKcpProxy() bool {
if x != nil && x.EnableKcpProxy != nil {
return *x.EnableKcpProxy
@@ -747,6 +759,13 @@ func (x *NetworkConfig) GetDhcpIpv6Cidr() string {
return ""
}
func (x *NetworkConfig) GetDns() *core_config.DnsConfigRaw {
if x != nil {
return x.Dns
}
return nil
}
type ManagedCredentialConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
CredentialId string `protobuf:"bytes,1,opt,name=credential_id,json=credentialId,proto3" json:"credential_id,omitempty"`
@@ -2211,7 +2230,7 @@ var File_api_manage_proto protoreflect.FileDescriptor
const file_api_manage_proto_rawDesc = "" +
"\n" +
"\x10api_manage.proto\x12\n" +
"api.manage\x1a\fcommon.proto\x1a\x0epeer_rpc.proto\x1a\x12api_instance.proto\x1a\tacl.proto\"\xa4%\n" +
"api.manage\x1a\fcommon.proto\x1a\x0epeer_rpc.proto\x1a\x12api_instance.proto\x1a\tacl.proto\x1a\x11core_config.proto\"\x8d&\n" +
"\rNetworkConfig\x12$\n" +
"\vinstance_id\x18\x01 \x01(\tH\x00R\n" +
"instanceId\x88\x01\x01\x12\x17\n" +
@@ -2238,64 +2257,67 @@ const file_api_manage_proto_rawDesc = "" +
"\bdev_name\x18\x14 \x01(\tH\x0fR\adevName\x88\x01\x01\x12$\n" +
"\vuse_smoltcp\x18\x15 \x01(\bH\x10R\n" +
"useSmoltcp\x88\x01\x01\x12&\n" +
"\fdisable_ipv6\x18/ \x01(\bH\x11R\vdisableIpv6\x88\x01\x01\x12-\n" +
"\x10enable_kcp_proxy\x18\x16 \x01(\bH\x12R\x0eenableKcpProxy\x88\x01\x01\x12/\n" +
"\x11disable_kcp_input\x18\x17 \x01(\bH\x13R\x0fdisableKcpInput\x88\x01\x01\x12$\n" +
"\vdisable_p2p\x18\x18 \x01(\bH\x14R\n" +
"\fdisable_ipv6\x18/ \x01(\bH\x11R\vdisableIpv6\x88\x01\x01\x12)\n" +
"\ftld_dns_zone\x18I \x01(\tB\x02\x18\x01H\x12R\n" +
"tldDnsZone\x88\x01\x01\x12-\n" +
"\x10enable_kcp_proxy\x18\x16 \x01(\bH\x13R\x0eenableKcpProxy\x88\x01\x01\x12/\n" +
"\x11disable_kcp_input\x18\x17 \x01(\bH\x14R\x0fdisableKcpInput\x88\x01\x01\x12$\n" +
"\vdisable_p2p\x18\x18 \x01(\bH\x15R\n" +
"disableP2p\x88\x01\x01\x12$\n" +
"\vbind_device\x18\x19 \x01(\bH\x15R\n" +
"\vbind_device\x18\x19 \x01(\bH\x16R\n" +
"bindDevice\x88\x01\x01\x12\x1a\n" +
"\x06no_tun\x18\x1a \x01(\bH\x16R\x05noTun\x88\x01\x01\x12-\n" +
"\x10enable_exit_node\x18\x1b \x01(\bH\x17R\x0eenableExitNode\x88\x01\x01\x120\n" +
"\x12relay_all_peer_rpc\x18\x1c \x01(\bH\x18R\x0frelayAllPeerRpc\x88\x01\x01\x12&\n" +
"\fmulti_thread\x18\x1d \x01(\bH\x19R\vmultiThread\x88\x01\x01\x12H\n" +
"\x1eenable_relay_network_whitelist\x18\x1e \x01(\bH\x1aR\x1benableRelayNetworkWhitelist\x88\x01\x01\x126\n" +
"\x06no_tun\x18\x1a \x01(\bH\x17R\x05noTun\x88\x01\x01\x12-\n" +
"\x10enable_exit_node\x18\x1b \x01(\bH\x18R\x0eenableExitNode\x88\x01\x01\x120\n" +
"\x12relay_all_peer_rpc\x18\x1c \x01(\bH\x19R\x0frelayAllPeerRpc\x88\x01\x01\x12&\n" +
"\fmulti_thread\x18\x1d \x01(\bH\x1aR\vmultiThread\x88\x01\x01\x12H\n" +
"\x1eenable_relay_network_whitelist\x18\x1e \x01(\bH\x1bR\x1benableRelayNetworkWhitelist\x88\x01\x01\x126\n" +
"\x17relay_network_whitelist\x18\x1f \x03(\tR\x15relayNetworkWhitelist\x125\n" +
"\x14enable_manual_routes\x18 \x01(\bH\x1bR\x12enableManualRoutes\x88\x01\x01\x12\x16\n" +
"\x14enable_manual_routes\x18 \x01(\bH\x1cR\x12enableManualRoutes\x88\x01\x01\x12\x16\n" +
"\x06routes\x18! \x03(\tR\x06routes\x12\x1d\n" +
"\n" +
"exit_nodes\x18\" \x03(\tR\texitNodes\x12:\n" +
"\x17proxy_forward_by_system\x18# \x01(\bH\x1cR\x14proxyForwardBySystem\x88\x01\x01\x122\n" +
"\x12disable_encryption\x18$ \x01(\bH\x1dR\x11disableEncryption\x88\x01\x01\x12(\n" +
"\renable_socks5\x18% \x01(\bH\x1eR\fenableSocks5\x88\x01\x01\x12$\n" +
"\vsocks5_port\x18& \x01(\x05H\x1fR\n" +
"\x17proxy_forward_by_system\x18# \x01(\bH\x1dR\x14proxyForwardBySystem\x88\x01\x01\x122\n" +
"\x12disable_encryption\x18$ \x01(\bH\x1eR\x11disableEncryption\x88\x01\x01\x12(\n" +
"\renable_socks5\x18% \x01(\bH\x1fR\fenableSocks5\x88\x01\x01\x12$\n" +
"\vsocks5_port\x18& \x01(\x05H R\n" +
"socks5Port\x88\x01\x01\x12>\n" +
"\x19disable_udp_hole_punching\x18' \x01(\bH R\x16disableUdpHolePunching\x88\x01\x01\x12\x15\n" +
"\x03mtu\x18( \x01(\x05H!R\x03mtu\x88\x01\x01\x12)\n" +
"\x19disable_udp_hole_punching\x18' \x01(\bH!R\x16disableUdpHolePunching\x88\x01\x01\x12\x15\n" +
"\x03mtu\x18( \x01(\x05H\"R\x03mtu\x88\x01\x01\x12)\n" +
"\x10mapped_listeners\x18) \x03(\tR\x0fmappedListeners\x12-\n" +
"\x10enable_magic_dns\x18* \x01(\bH\"R\x0eenableMagicDns\x88\x01\x01\x123\n" +
"\x13enable_private_mode\x18+ \x01(\bH#R\x11enablePrivateMode\x88\x01\x01\x12/\n" +
"\x11enable_quic_proxy\x18- \x01(\bH$R\x0fenableQuicProxy\x88\x01\x01\x121\n" +
"\x12disable_quic_input\x18. \x01(\bH%R\x10disableQuicInput\x88\x01\x01\x121\n" +
"\x10quic_listen_port\x182 \x01(\x05B\x02\x18\x01H&R\x0equicListenPort\x88\x01\x01\x12B\n" +
"\x10enable_magic_dns\x18* \x01(\bH#R\x0eenableMagicDns\x88\x01\x01\x123\n" +
"\x13enable_private_mode\x18+ \x01(\bH$R\x11enablePrivateMode\x88\x01\x01\x12/\n" +
"\x11enable_quic_proxy\x18- \x01(\bH%R\x0fenableQuicProxy\x88\x01\x01\x121\n" +
"\x12disable_quic_input\x18. \x01(\bH&R\x10disableQuicInput\x88\x01\x01\x121\n" +
"\x10quic_listen_port\x182 \x01(\x05B\x02\x18\x01H'R\x0equicListenPort\x88\x01\x01\x12B\n" +
"\rport_forwards\x180 \x03(\v2\x1d.api.manage.PortForwardConfigR\fportForwards\x12>\n" +
"\x19disable_sym_hole_punching\x181 \x01(\bH'R\x16disableSymHolePunching\x88\x01\x01\x12\x1e\n" +
"\bp2p_only\x183 \x01(\bH(R\ap2pOnly\x88\x01\x01\x12L\n" +
"\x12data_compress_algo\x184 \x01(\x0e2\x19.common.CompressionAlgoPbH)R\x10dataCompressAlgo\x88\x01\x01\x126\n" +
"\x14encryption_algorithm\x185 \x01(\tH*R\x13encryptionAlgorithm\x88\x01\x01\x12>\n" +
"\x19disable_tcp_hole_punching\x186 \x01(\bH+R\x16disableTcpHolePunching\x88\x01\x01\x129\n" +
"\x19disable_sym_hole_punching\x181 \x01(\bH(R\x16disableSymHolePunching\x88\x01\x01\x12\x1e\n" +
"\bp2p_only\x183 \x01(\bH)R\ap2pOnly\x88\x01\x01\x12L\n" +
"\x12data_compress_algo\x184 \x01(\x0e2\x19.common.CompressionAlgoPbH*R\x10dataCompressAlgo\x88\x01\x01\x126\n" +
"\x14encryption_algorithm\x185 \x01(\tH+R\x13encryptionAlgorithm\x88\x01\x01\x12>\n" +
"\x19disable_tcp_hole_punching\x186 \x01(\bH,R\x16disableTcpHolePunching\x88\x01\x01\x129\n" +
"\vsecure_mode\x187 \x01(\v2\x18.common.SecureModeConfigR\n" +
"secureMode\x12\x1f\n" +
"\x03acl\x188 \x01(\v2\b.acl.AclH,R\x03acl\x88\x01\x01\x12,\n" +
"\x0fcredential_file\x189 \x01(\tH-R\x0ecredentialFile\x88\x01\x01\x12\x1e\n" +
"\blazy_p2p\x18: \x01(\bH.R\alazyP2p\x88\x01\x01\x12\x1e\n" +
"\bneed_p2p\x18; \x01(\bH/R\aneedP2p\x88\x01\x01\x12:\n" +
"\x17instance_recv_bps_limit\x18< \x01(\x04H0R\x14instanceRecvBpsLimit\x88\x01\x01\x12&\n" +
"\fdisable_upnp\x18= \x01(\bH1R\vdisableUpnp\x88\x01\x01\x12>\n" +
"\x19ipv6_public_addr_provider\x18> \x01(\bH2R\x16ipv6PublicAddrProvider\x88\x01\x01\x126\n" +
"\x15ipv6_public_addr_auto\x18? \x01(\bH3R\x12ipv6PublicAddrAuto\x88\x01\x01\x12:\n" +
"\x17ipv6_public_addr_prefix\x18@ \x01(\tH4R\x14ipv6PublicAddrPrefix\x88\x01\x01\x121\n" +
"\x12disable_relay_data\x18A \x01(\bH5R\x10disableRelayData\x88\x01\x01\x12@\n" +
"\x1aenable_udp_broadcast_relay\x18B \x01(\bH6R\x17enableUdpBroadcastRelay\x88\x01\x01\x12$\n" +
"\vsocket_mark\x18C \x01(\rH7R\n" +
"\x03acl\x188 \x01(\v2\b.acl.AclH-R\x03acl\x88\x01\x01\x12,\n" +
"\x0fcredential_file\x189 \x01(\tH.R\x0ecredentialFile\x88\x01\x01\x12\x1e\n" +
"\blazy_p2p\x18: \x01(\bH/R\alazyP2p\x88\x01\x01\x12\x1e\n" +
"\bneed_p2p\x18; \x01(\bH0R\aneedP2p\x88\x01\x01\x12:\n" +
"\x17instance_recv_bps_limit\x18< \x01(\x04H1R\x14instanceRecvBpsLimit\x88\x01\x01\x12&\n" +
"\fdisable_upnp\x18= \x01(\bH2R\vdisableUpnp\x88\x01\x01\x12>\n" +
"\x19ipv6_public_addr_provider\x18> \x01(\bH3R\x16ipv6PublicAddrProvider\x88\x01\x01\x126\n" +
"\x15ipv6_public_addr_auto\x18? \x01(\bH4R\x12ipv6PublicAddrAuto\x88\x01\x01\x12:\n" +
"\x17ipv6_public_addr_prefix\x18@ \x01(\tH5R\x14ipv6PublicAddrPrefix\x88\x01\x01\x121\n" +
"\x12disable_relay_data\x18A \x01(\bH6R\x10disableRelayData\x88\x01\x01\x12@\n" +
"\x1aenable_udp_broadcast_relay\x18B \x01(\bH7R\x17enableUdpBroadcastRelay\x88\x01\x01\x12$\n" +
"\vsocket_mark\x18C \x01(\rH8R\n" +
"socketMark\x88\x01\x01\x123\n" +
"\x05peers\x18D \x03(\v2\x1d.api.manage.NetworkPeerConfigR\x05peers\x12L\n" +
"\x11vpn_portal_config\x18E \x01(\v2\x1b.api.manage.VpnPortalConfigH8R\x0fvpnPortalConfig\x88\x01\x01\x12T\n" +
"\x11vpn_portal_config\x18E \x01(\v2\x1b.api.manage.VpnPortalConfigH9R\x0fvpnPortalConfig\x88\x01\x01\x12T\n" +
"\x13managed_credentials\x18G \x03(\v2#.api.manage.ManagedCredentialConfigR\x12managedCredentials\x12/\n" +
"\x11prefer_peer_relay\x18H \x01(\bH9R\x0fpreferPeerRelay\x88\x01\x01\x12)\n" +
"\x0edhcp_ipv4_cidr\x18J \x01(\tH:R\fdhcpIpv4Cidr\x88\x01\x01\x12\x1b\n" +
"\x06dhcpv6\x18K \x01(\bH;R\x06dhcpv6\x88\x01\x01\x12)\n" +
"\x0edhcp_ipv6_cidr\x18L \x01(\tH<R\fdhcpIpv6Cidr\x88\x01\x01B\x0e\n" +
"\x11prefer_peer_relay\x18H \x01(\bH:R\x0fpreferPeerRelay\x88\x01\x01\x12)\n" +
"\x0edhcp_ipv4_cidr\x18J \x01(\tH;R\fdhcpIpv4Cidr\x88\x01\x01\x12\x1b\n" +
"\x06dhcpv6\x18K \x01(\bH<R\x06dhcpv6\x88\x01\x01\x12)\n" +
"\x0edhcp_ipv6_cidr\x18L \x01(\tH=R\fdhcpIpv6Cidr\x88\x01\x01\x12+\n" +
"\x03dns\x18M \x01(\v2\x19.core_config.DnsConfigRawR\x03dnsB\x0e\n" +
"\f_instance_idB\a\n" +
"\x05_dhcpB\x0f\n" +
"\r_virtual_ipv4B\x11\n" +
@@ -2313,7 +2335,8 @@ const file_api_manage_proto_rawDesc = "" +
"\x0e_latency_firstB\v\n" +
"\t_dev_nameB\x0e\n" +
"\f_use_smoltcpB\x0f\n" +
"\r_disable_ipv6B\x13\n" +
"\r_disable_ipv6B\x0f\n" +
"\r_tld_dns_zoneB\x13\n" +
"\x11_enable_kcp_proxyB\x14\n" +
"\x12_disable_kcp_inputB\x0e\n" +
"\f_disable_p2pB\x0e\n" +
@@ -2533,16 +2556,17 @@ var file_api_manage_proto_goTypes = []any{
(common.CompressionAlgoPb)(0), // 30: common.CompressionAlgoPb
(*common.SecureModeConfig)(nil), // 31: common.SecureModeConfig
(*acl.Acl)(nil), // 32: acl.Acl
(*common.Ipv4Inet)(nil), // 33: common.Ipv4Inet
(*peer_rpc.GetIpListResponse)(nil), // 34: peer_rpc.GetIpListResponse
(*common.StunInfo)(nil), // 35: common.StunInfo
(*common.Url)(nil), // 36: common.Url
(*common.Ipv6Inet)(nil), // 37: common.Ipv6Inet
(*instance.Route)(nil), // 38: api.instance.Route
(*instance.PeerInfo)(nil), // 39: api.instance.PeerInfo
(*instance.PeerRoutePair)(nil), // 40: api.instance.PeerRoutePair
(*peer_rpc.RouteForeignNetworkSummary)(nil), // 41: peer_rpc.RouteForeignNetworkSummary
(*common.UUID)(nil), // 42: common.UUID
(*core_config.DnsConfigRaw)(nil), // 33: core_config.DnsConfigRaw
(*common.Ipv4Inet)(nil), // 34: common.Ipv4Inet
(*peer_rpc.GetIpListResponse)(nil), // 35: peer_rpc.GetIpListResponse
(*common.StunInfo)(nil), // 36: common.StunInfo
(*common.Url)(nil), // 37: common.Url
(*common.Ipv6Inet)(nil), // 38: common.Ipv6Inet
(*instance.Route)(nil), // 39: api.instance.Route
(*instance.PeerInfo)(nil), // 40: api.instance.PeerInfo
(*instance.PeerRoutePair)(nil), // 41: api.instance.PeerRoutePair
(*peer_rpc.RouteForeignNetworkSummary)(nil), // 42: peer_rpc.RouteForeignNetworkSummary
(*common.UUID)(nil), // 43: common.UUID
}
var file_api_manage_proto_depIdxs = []int32{
0, // 0: api.manage.NetworkConfig.networking_method:type_name -> api.manage.NetworkingMethod
@@ -2553,60 +2577,61 @@ var file_api_manage_proto_depIdxs = []int32{
7, // 5: api.manage.NetworkConfig.peers:type_name -> api.manage.NetworkPeerConfig
6, // 6: api.manage.NetworkConfig.vpn_portal_config:type_name -> api.manage.VpnPortalConfig
3, // 7: api.manage.NetworkConfig.managed_credentials:type_name -> api.manage.ManagedCredentialConfig
3, // 8: api.manage.ManagedCredentialSet.entries:type_name -> api.manage.ManagedCredentialConfig
5, // 9: api.manage.VpnPortalConfig.clients:type_name -> api.manage.VpnPortalClientConfig
33, // 10: api.manage.MyNodeInfo.virtual_ipv4:type_name -> common.Ipv4Inet
34, // 11: api.manage.MyNodeInfo.ips:type_name -> peer_rpc.GetIpListResponse
35, // 12: api.manage.MyNodeInfo.stun_info:type_name -> common.StunInfo
36, // 13: api.manage.MyNodeInfo.listeners:type_name -> common.Url
37, // 14: api.manage.MyNodeInfo.virtual_ipv6:type_name -> common.Ipv6Inet
9, // 15: api.manage.NetworkInstanceRunningInfo.my_node_info:type_name -> api.manage.MyNodeInfo
38, // 16: api.manage.NetworkInstanceRunningInfo.routes:type_name -> api.instance.Route
39, // 17: api.manage.NetworkInstanceRunningInfo.peers:type_name -> api.instance.PeerInfo
40, // 18: api.manage.NetworkInstanceRunningInfo.peer_route_pairs:type_name -> api.instance.PeerRoutePair
41, // 19: api.manage.NetworkInstanceRunningInfo.foreign_network_summary:type_name -> peer_rpc.RouteForeignNetworkSummary
29, // 20: api.manage.NetworkInstanceRunningInfoMap.map:type_name -> api.manage.NetworkInstanceRunningInfoMap.MapEntry
42, // 21: api.manage.NetworkMeta.inst_id:type_name -> common.UUID
1, // 22: api.manage.NetworkMeta.source:type_name -> api.manage.ConfigSource
2, // 23: api.manage.ValidateConfigRequest.config:type_name -> api.manage.NetworkConfig
42, // 24: api.manage.RunNetworkInstanceRequest.inst_id:type_name -> common.UUID
2, // 25: api.manage.RunNetworkInstanceRequest.config:type_name -> api.manage.NetworkConfig
1, // 26: api.manage.RunNetworkInstanceRequest.source:type_name -> api.manage.ConfigSource
42, // 27: api.manage.RunNetworkInstanceResponse.inst_id:type_name -> common.UUID
42, // 28: api.manage.RetainNetworkInstanceRequest.inst_ids:type_name -> common.UUID
42, // 29: api.manage.RetainNetworkInstanceResponse.remain_inst_ids:type_name -> common.UUID
42, // 30: api.manage.CollectNetworkInfoRequest.inst_ids:type_name -> common.UUID
11, // 31: api.manage.CollectNetworkInfoResponse.info:type_name -> api.manage.NetworkInstanceRunningInfoMap
42, // 32: api.manage.ListNetworkInstanceResponse.inst_ids:type_name -> common.UUID
42, // 33: api.manage.DeleteNetworkInstanceRequest.inst_ids:type_name -> common.UUID
42, // 34: api.manage.DeleteNetworkInstanceResponse.remain_inst_ids:type_name -> common.UUID
42, // 35: api.manage.GetNetworkInstanceConfigRequest.inst_id:type_name -> common.UUID
2, // 36: api.manage.GetNetworkInstanceConfigResponse.config:type_name -> api.manage.NetworkConfig
1, // 37: api.manage.GetNetworkInstanceConfigResponse.source:type_name -> api.manage.ConfigSource
42, // 38: api.manage.ListNetworkInstanceMetaRequest.inst_ids:type_name -> common.UUID
12, // 39: api.manage.ListNetworkInstanceMetaResponse.metas:type_name -> api.manage.NetworkMeta
10, // 40: api.manage.NetworkInstanceRunningInfoMap.MapEntry.value:type_name -> api.manage.NetworkInstanceRunningInfo
13, // 41: api.manage.WebClientService.ValidateConfig:input_type -> api.manage.ValidateConfigRequest
15, // 42: api.manage.WebClientService.RunNetworkInstance:input_type -> api.manage.RunNetworkInstanceRequest
17, // 43: api.manage.WebClientService.RetainNetworkInstance:input_type -> api.manage.RetainNetworkInstanceRequest
19, // 44: api.manage.WebClientService.CollectNetworkInfo:input_type -> api.manage.CollectNetworkInfoRequest
21, // 45: api.manage.WebClientService.ListNetworkInstance:input_type -> api.manage.ListNetworkInstanceRequest
23, // 46: api.manage.WebClientService.DeleteNetworkInstance:input_type -> api.manage.DeleteNetworkInstanceRequest
25, // 47: api.manage.WebClientService.GetNetworkInstanceConfig:input_type -> api.manage.GetNetworkInstanceConfigRequest
27, // 48: api.manage.WebClientService.ListNetworkInstanceMeta:input_type -> api.manage.ListNetworkInstanceMetaRequest
14, // 49: api.manage.WebClientService.ValidateConfig:output_type -> api.manage.ValidateConfigResponse
16, // 50: api.manage.WebClientService.RunNetworkInstance:output_type -> api.manage.RunNetworkInstanceResponse
18, // 51: api.manage.WebClientService.RetainNetworkInstance:output_type -> api.manage.RetainNetworkInstanceResponse
20, // 52: api.manage.WebClientService.CollectNetworkInfo:output_type -> api.manage.CollectNetworkInfoResponse
22, // 53: api.manage.WebClientService.ListNetworkInstance:output_type -> api.manage.ListNetworkInstanceResponse
24, // 54: api.manage.WebClientService.DeleteNetworkInstance:output_type -> api.manage.DeleteNetworkInstanceResponse
26, // 55: api.manage.WebClientService.GetNetworkInstanceConfig:output_type -> api.manage.GetNetworkInstanceConfigResponse
28, // 56: api.manage.WebClientService.ListNetworkInstanceMeta:output_type -> api.manage.ListNetworkInstanceMetaResponse
49, // [49:57] is the sub-list for method output_type
41, // [41:49] is the sub-list for method input_type
41, // [41:41] is the sub-list for extension type_name
41, // [41:41] is the sub-list for extension extendee
0, // [0:41] is the sub-list for field type_name
33, // 8: api.manage.NetworkConfig.dns:type_name -> core_config.DnsConfigRaw
3, // 9: api.manage.ManagedCredentialSet.entries:type_name -> api.manage.ManagedCredentialConfig
5, // 10: api.manage.VpnPortalConfig.clients:type_name -> api.manage.VpnPortalClientConfig
34, // 11: api.manage.MyNodeInfo.virtual_ipv4:type_name -> common.Ipv4Inet
35, // 12: api.manage.MyNodeInfo.ips:type_name -> peer_rpc.GetIpListResponse
36, // 13: api.manage.MyNodeInfo.stun_info:type_name -> common.StunInfo
37, // 14: api.manage.MyNodeInfo.listeners:type_name -> common.Url
38, // 15: api.manage.MyNodeInfo.virtual_ipv6:type_name -> common.Ipv6Inet
9, // 16: api.manage.NetworkInstanceRunningInfo.my_node_info:type_name -> api.manage.MyNodeInfo
39, // 17: api.manage.NetworkInstanceRunningInfo.routes:type_name -> api.instance.Route
40, // 18: api.manage.NetworkInstanceRunningInfo.peers:type_name -> api.instance.PeerInfo
41, // 19: api.manage.NetworkInstanceRunningInfo.peer_route_pairs:type_name -> api.instance.PeerRoutePair
42, // 20: api.manage.NetworkInstanceRunningInfo.foreign_network_summary:type_name -> peer_rpc.RouteForeignNetworkSummary
29, // 21: api.manage.NetworkInstanceRunningInfoMap.map:type_name -> api.manage.NetworkInstanceRunningInfoMap.MapEntry
43, // 22: api.manage.NetworkMeta.inst_id:type_name -> common.UUID
1, // 23: api.manage.NetworkMeta.source:type_name -> api.manage.ConfigSource
2, // 24: api.manage.ValidateConfigRequest.config:type_name -> api.manage.NetworkConfig
43, // 25: api.manage.RunNetworkInstanceRequest.inst_id:type_name -> common.UUID
2, // 26: api.manage.RunNetworkInstanceRequest.config:type_name -> api.manage.NetworkConfig
1, // 27: api.manage.RunNetworkInstanceRequest.source:type_name -> api.manage.ConfigSource
43, // 28: api.manage.RunNetworkInstanceResponse.inst_id:type_name -> common.UUID
43, // 29: api.manage.RetainNetworkInstanceRequest.inst_ids:type_name -> common.UUID
43, // 30: api.manage.RetainNetworkInstanceResponse.remain_inst_ids:type_name -> common.UUID
43, // 31: api.manage.CollectNetworkInfoRequest.inst_ids:type_name -> common.UUID
11, // 32: api.manage.CollectNetworkInfoResponse.info:type_name -> api.manage.NetworkInstanceRunningInfoMap
43, // 33: api.manage.ListNetworkInstanceResponse.inst_ids:type_name -> common.UUID
43, // 34: api.manage.DeleteNetworkInstanceRequest.inst_ids:type_name -> common.UUID
43, // 35: api.manage.DeleteNetworkInstanceResponse.remain_inst_ids:type_name -> common.UUID
43, // 36: api.manage.GetNetworkInstanceConfigRequest.inst_id:type_name -> common.UUID
2, // 37: api.manage.GetNetworkInstanceConfigResponse.config:type_name -> api.manage.NetworkConfig
1, // 38: api.manage.GetNetworkInstanceConfigResponse.source:type_name -> api.manage.ConfigSource
43, // 39: api.manage.ListNetworkInstanceMetaRequest.inst_ids:type_name -> common.UUID
12, // 40: api.manage.ListNetworkInstanceMetaResponse.metas:type_name -> api.manage.NetworkMeta
10, // 41: api.manage.NetworkInstanceRunningInfoMap.MapEntry.value:type_name -> api.manage.NetworkInstanceRunningInfo
13, // 42: api.manage.WebClientService.ValidateConfig:input_type -> api.manage.ValidateConfigRequest
15, // 43: api.manage.WebClientService.RunNetworkInstance:input_type -> api.manage.RunNetworkInstanceRequest
17, // 44: api.manage.WebClientService.RetainNetworkInstance:input_type -> api.manage.RetainNetworkInstanceRequest
19, // 45: api.manage.WebClientService.CollectNetworkInfo:input_type -> api.manage.CollectNetworkInfoRequest
21, // 46: api.manage.WebClientService.ListNetworkInstance:input_type -> api.manage.ListNetworkInstanceRequest
23, // 47: api.manage.WebClientService.DeleteNetworkInstance:input_type -> api.manage.DeleteNetworkInstanceRequest
25, // 48: api.manage.WebClientService.GetNetworkInstanceConfig:input_type -> api.manage.GetNetworkInstanceConfigRequest
27, // 49: api.manage.WebClientService.ListNetworkInstanceMeta:input_type -> api.manage.ListNetworkInstanceMetaRequest
14, // 50: api.manage.WebClientService.ValidateConfig:output_type -> api.manage.ValidateConfigResponse
16, // 51: api.manage.WebClientService.RunNetworkInstance:output_type -> api.manage.RunNetworkInstanceResponse
18, // 52: api.manage.WebClientService.RetainNetworkInstance:output_type -> api.manage.RetainNetworkInstanceResponse
20, // 53: api.manage.WebClientService.CollectNetworkInfo:output_type -> api.manage.CollectNetworkInfoResponse
22, // 54: api.manage.WebClientService.ListNetworkInstance:output_type -> api.manage.ListNetworkInstanceResponse
24, // 55: api.manage.WebClientService.DeleteNetworkInstance:output_type -> api.manage.DeleteNetworkInstanceResponse
26, // 56: api.manage.WebClientService.GetNetworkInstanceConfig:output_type -> api.manage.GetNetworkInstanceConfigResponse
28, // 57: api.manage.WebClientService.ListNetworkInstanceMeta:output_type -> api.manage.ListNetworkInstanceMetaResponse
50, // [50:58] is the sub-list for method output_type
42, // [42:50] is the sub-list for method input_type
42, // [42:42] is the sub-list for extension type_name
42, // [42:42] is the sub-list for extension extendee
0, // [0:42] is the sub-list for field type_name
}
func init() { file_api_manage_proto_init() }
+63 -10
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v7.35.1
// protoc v7.36.2
// source: common.proto
package common
@@ -242,6 +242,8 @@ type Flags struct {
// disable symmetric nat hole punching, treat symmetric as cone when enabled
DisableSymHolePunching bool `protobuf:"varint,30,opt,name=disable_sym_hole_punching,json=disableSymHolePunching,proto3" json:"disable_sym_hole_punching,omitempty"`
// tld dns zone for magic dns
//
// Deprecated: Marked as deprecated in common.proto.
TldDnsZone string `protobuf:"bytes,31,opt,name=tld_dns_zone,json=tldDnsZone,proto3" json:"tld_dns_zone,omitempty"`
P2POnly bool `protobuf:"varint,32,opt,name=p2p_only,json=p2pOnly,proto3" json:"p2p_only,omitempty"`
DisableTcpHolePunching bool `protobuf:"varint,34,opt,name=disable_tcp_hole_punching,json=disableTcpHolePunching,proto3" json:"disable_tcp_hole_punching,omitempty"`
@@ -519,6 +521,7 @@ func (x *Flags) GetDisableSymHolePunching() bool {
return false
}
// Deprecated: Marked as deprecated in common.proto.
func (x *Flags) GetTldDnsZone() string {
if x != nil {
return x.TldDnsZone
@@ -2144,11 +2147,57 @@ func (x *SecureModeConfig) GetLocalPublicKey() string {
return ""
}
// Collection wrappers preserve omitted versus explicitly empty input.
// The items field number and wire type are part of the configuration contract.
type RepeatedString struct {
state protoimpl.MessageState `protogen:"open.v1"`
Items []string `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RepeatedString) Reset() {
*x = RepeatedString{}
mi := &file_common_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RepeatedString) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RepeatedString) ProtoMessage() {}
func (x *RepeatedString) ProtoReflect() protoreflect.Message {
mi := &file_common_proto_msgTypes[24]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RepeatedString.ProtoReflect.Descriptor instead.
func (*RepeatedString) Descriptor() ([]byte, []int) {
return file_common_proto_rawDescGZIP(), []int{24}
}
func (x *RepeatedString) GetItems() []string {
if x != nil {
return x.Items
}
return nil
}
var File_common_proto protoreflect.FileDescriptor
const file_common_proto_rawDesc = "" +
"\n" +
"\fcommon.proto\x12\x06common\x1a\x11annotations.proto\x1a\verror.proto\"\x8a\x14\n" +
"\fcommon.proto\x12\x06common\x1a\x11annotations.proto\x1a\verror.proto\"\x87\x14\n" +
"\x05Flags\x124\n" +
"\x10default_protocol\x18\x01 \x01(\tB\t\x8a\xb5\x18\x05\n" +
"\x03tcpR\x0fdefaultProtocol\x12#\n" +
@@ -2226,9 +2275,10 @@ const file_common_proto_rawDesc = "" +
"\x14encryption_algorithm\x18\x1d \x01(\tB\x0f\x8a\xb5\x18\v\n" +
"\aaes-gcm\x18\x01R\x13encryptionAlgorithm\x12F\n" +
"\x19disable_sym_hole_punching\x18\x1e \x01(\bB\v\x8a\xb5\x18\a\n" +
"\x05falseR\x16disableSymHolePunching\x12/\n" +
"\ftld_dns_zone\x18\x1f \x01(\tB\r\x8a\xb5\x18\t\n" +
"\aet.net.R\n" +
"\x05falseR\x16disableSymHolePunching\x12,\n" +
"\ftld_dns_zone\x18\x1f \x01(\tB\n" +
"\x8a\xb5\x18\x04\n" +
"\x00 \x01\x18\x01R\n" +
"tldDnsZone\x12&\n" +
"\bp2p_only\x18 \x01(\bB\v\x8a\xb5\x18\a\n" +
"\x05falseR\ap2pOnly\x12F\n" +
@@ -2381,7 +2431,9 @@ const file_common_proto_rawDesc = "" +
"\x11local_private_key\x18\x02 \x01(\tH\x00R\x0flocalPrivateKey\x88\x01\x01\x12-\n" +
"\x10local_public_key\x18\x03 \x01(\tH\x01R\x0elocalPublicKey\x88\x01\x01B\x14\n" +
"\x12_local_private_keyB\x13\n" +
"\x11_local_public_key*4\n" +
"\x11_local_public_key\"&\n" +
"\x0eRepeatedString\x12\x14\n" +
"\x05items\x18\x01 \x03(\tR\x05items*4\n" +
"\x11CompressionAlgoPb\x12\v\n" +
"\aInvalid\x10\x00\x12\b\n" +
"\x04None\x10\x01\x12\b\n" +
@@ -2416,7 +2468,7 @@ func file_common_proto_rawDescGZIP() []byte {
}
var file_common_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
var file_common_proto_goTypes = []any{
(CompressionAlgoPb)(0), // 0: common.CompressionAlgoPb
(NatType)(0), // 1: common.NatType
@@ -2445,14 +2497,15 @@ var file_common_proto_goTypes = []any{
(*PortForwardConfigPb)(nil), // 24: common.PortForwardConfigPb
(*ProxyDstInfo)(nil), // 25: common.ProxyDstInfo
(*SecureModeConfig)(nil), // 26: common.SecureModeConfig
(*error1.Error)(nil), // 27: error.Error
(*RepeatedString)(nil), // 27: common.RepeatedString
(*error1.Error)(nil), // 28: error.Error
}
var file_common_proto_depIdxs = []int32{
0, // 0: common.Flags.data_compress_algo:type_name -> common.CompressionAlgoPb
4, // 1: common.RpcRequest.descriptor:type_name -> common.RpcDescriptor
6, // 2: common.HostManagementRequest.rpc:type_name -> common.DirectRpcRequest
12, // 3: common.HostManagementRequest.prepared_instance_id:type_name -> common.UUID
27, // 4: common.RpcResponse.error:type_name -> error.Error
28, // 4: common.RpcResponse.error:type_name -> error.Error
0, // 5: common.RpcCompressionInfo.algo:type_name -> common.CompressionAlgoPb
0, // 6: common.RpcCompressionInfo.accepted_algo:type_name -> common.CompressionAlgoPb
4, // 7: common.RpcPacket.descriptor:type_name -> common.RpcDescriptor
@@ -2508,7 +2561,7 @@ func file_common_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)),
NumEnums: 3,
NumMessages: 24,
NumMessages: 25,
NumExtensions: 0,
NumServices: 0,
},
@@ -0,0 +1,845 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v7.36.2
// source: core_config.proto
package core_config
import (
common "github.com/easytier/easytier/easytier-go/proto/common"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type CoreConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
Node *NodeConfig `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
Routes *RouteConfig `protobuf:"bytes,2,opt,name=routes,proto3" json:"routes,omitempty"`
PeerPolicy *PeerPolicyConfig `protobuf:"bytes,3,opt,name=peer_policy,json=peerPolicy,proto3" json:"peer_policy,omitempty"`
Traffic *TrafficConfig `protobuf:"bytes,4,opt,name=traffic,proto3" json:"traffic,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CoreConfig) Reset() {
*x = CoreConfig{}
mi := &file_core_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CoreConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CoreConfig) ProtoMessage() {}
func (x *CoreConfig) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CoreConfig.ProtoReflect.Descriptor instead.
func (*CoreConfig) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{0}
}
func (x *CoreConfig) GetNode() *NodeConfig {
if x != nil {
return x.Node
}
return nil
}
func (x *CoreConfig) GetRoutes() *RouteConfig {
if x != nil {
return x.Routes
}
return nil
}
func (x *CoreConfig) GetPeerPolicy() *PeerPolicyConfig {
if x != nil {
return x.PeerPolicy
}
return nil
}
func (x *CoreConfig) GetTraffic() *TrafficConfig {
if x != nil {
return x.Traffic
}
return nil
}
type NodeConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
PeerId *uint32 `protobuf:"varint,1,opt,name=peer_id,json=peerId,proto3,oneof" json:"peer_id,omitempty"`
InstanceId *common.UUID `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3,oneof" json:"instance_id,omitempty"`
Hostname *string `protobuf:"bytes,3,opt,name=hostname,proto3,oneof" json:"hostname,omitempty"`
NetworkName string `protobuf:"bytes,4,opt,name=network_name,json=networkName,proto3" json:"network_name,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *NodeConfig) Reset() {
*x = NodeConfig{}
mi := &file_core_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NodeConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NodeConfig) ProtoMessage() {}
func (x *NodeConfig) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NodeConfig.ProtoReflect.Descriptor instead.
func (*NodeConfig) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{1}
}
func (x *NodeConfig) GetPeerId() uint32 {
if x != nil && x.PeerId != nil {
return *x.PeerId
}
return 0
}
func (x *NodeConfig) GetInstanceId() *common.UUID {
if x != nil {
return x.InstanceId
}
return nil
}
func (x *NodeConfig) GetHostname() string {
if x != nil && x.Hostname != nil {
return *x.Hostname
}
return ""
}
func (x *NodeConfig) GetNetworkName() string {
if x != nil {
return x.NetworkName
}
return ""
}
type RouteConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
Ipv4 *IpPrefix `protobuf:"bytes,1,opt,name=ipv4,proto3,oneof" json:"ipv4,omitempty"`
Ipv6 *IpPrefix `protobuf:"bytes,2,opt,name=ipv6,proto3,oneof" json:"ipv6,omitempty"`
AdvertisedRoutes []*IpPrefix `protobuf:"bytes,3,rep,name=advertised_routes,json=advertisedRoutes,proto3" json:"advertised_routes,omitempty"`
ProxyNetworks []*ProxyNetworkConfig `protobuf:"bytes,4,rep,name=proxy_networks,json=proxyNetworks,proto3" json:"proxy_networks,omitempty"`
ForeignNetworks []*ForeignNetworkConfig `protobuf:"bytes,5,rep,name=foreign_networks,json=foreignNetworks,proto3" json:"foreign_networks,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RouteConfig) Reset() {
*x = RouteConfig{}
mi := &file_core_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RouteConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RouteConfig) ProtoMessage() {}
func (x *RouteConfig) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RouteConfig.ProtoReflect.Descriptor instead.
func (*RouteConfig) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{2}
}
func (x *RouteConfig) GetIpv4() *IpPrefix {
if x != nil {
return x.Ipv4
}
return nil
}
func (x *RouteConfig) GetIpv6() *IpPrefix {
if x != nil {
return x.Ipv6
}
return nil
}
func (x *RouteConfig) GetAdvertisedRoutes() []*IpPrefix {
if x != nil {
return x.AdvertisedRoutes
}
return nil
}
func (x *RouteConfig) GetProxyNetworks() []*ProxyNetworkConfig {
if x != nil {
return x.ProxyNetworks
}
return nil
}
func (x *RouteConfig) GetForeignNetworks() []*ForeignNetworkConfig {
if x != nil {
return x.ForeignNetworks
}
return nil
}
type IpPrefix struct {
state protoimpl.MessageState `protogen:"open.v1"`
Address *common.IpAddr `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
PrefixLen uint32 `protobuf:"varint,2,opt,name=prefix_len,json=prefixLen,proto3" json:"prefix_len,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *IpPrefix) Reset() {
*x = IpPrefix{}
mi := &file_core_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *IpPrefix) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*IpPrefix) ProtoMessage() {}
func (x *IpPrefix) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use IpPrefix.ProtoReflect.Descriptor instead.
func (*IpPrefix) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{3}
}
func (x *IpPrefix) GetAddress() *common.IpAddr {
if x != nil {
return x.Address
}
return nil
}
func (x *IpPrefix) GetPrefixLen() uint32 {
if x != nil {
return x.PrefixLen
}
return 0
}
type ProxyNetworkConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
Real *IpPrefix `protobuf:"bytes,1,opt,name=real,proto3" json:"real,omitempty"`
Mapped *IpPrefix `protobuf:"bytes,2,opt,name=mapped,proto3,oneof" json:"mapped,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ProxyNetworkConfig) Reset() {
*x = ProxyNetworkConfig{}
mi := &file_core_config_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ProxyNetworkConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ProxyNetworkConfig) ProtoMessage() {}
func (x *ProxyNetworkConfig) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ProxyNetworkConfig.ProtoReflect.Descriptor instead.
func (*ProxyNetworkConfig) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{4}
}
func (x *ProxyNetworkConfig) GetReal() *IpPrefix {
if x != nil {
return x.Real
}
return nil
}
func (x *ProxyNetworkConfig) GetMapped() *IpPrefix {
if x != nil {
return x.Mapped
}
return nil
}
type ForeignNetworkConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Cidrs []*IpPrefix `protobuf:"bytes,2,rep,name=cidrs,proto3" json:"cidrs,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ForeignNetworkConfig) Reset() {
*x = ForeignNetworkConfig{}
mi := &file_core_config_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ForeignNetworkConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ForeignNetworkConfig) ProtoMessage() {}
func (x *ForeignNetworkConfig) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ForeignNetworkConfig.ProtoReflect.Descriptor instead.
func (*ForeignNetworkConfig) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{5}
}
func (x *ForeignNetworkConfig) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ForeignNetworkConfig) GetCidrs() []*IpPrefix {
if x != nil {
return x.Cidrs
}
return nil
}
type PeerPolicyConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
P2PEnabled *bool `protobuf:"varint,1,opt,name=p2p_enabled,json=p2pEnabled,proto3,oneof" json:"p2p_enabled,omitempty"`
RelayPeerRpc *bool `protobuf:"varint,2,opt,name=relay_peer_rpc,json=relayPeerRpc,proto3,oneof" json:"relay_peer_rpc,omitempty"`
RelayData *bool `protobuf:"varint,3,opt,name=relay_data,json=relayData,proto3,oneof" json:"relay_data,omitempty"`
LatencyFirst *bool `protobuf:"varint,4,opt,name=latency_first,json=latencyFirst,proto3,oneof" json:"latency_first,omitempty"`
EncryptionRequired *bool `protobuf:"varint,5,opt,name=encryption_required,json=encryptionRequired,proto3,oneof" json:"encryption_required,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PeerPolicyConfig) Reset() {
*x = PeerPolicyConfig{}
mi := &file_core_config_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PeerPolicyConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PeerPolicyConfig) ProtoMessage() {}
func (x *PeerPolicyConfig) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PeerPolicyConfig.ProtoReflect.Descriptor instead.
func (*PeerPolicyConfig) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{6}
}
func (x *PeerPolicyConfig) GetP2PEnabled() bool {
if x != nil && x.P2PEnabled != nil {
return *x.P2PEnabled
}
return false
}
func (x *PeerPolicyConfig) GetRelayPeerRpc() bool {
if x != nil && x.RelayPeerRpc != nil {
return *x.RelayPeerRpc
}
return false
}
func (x *PeerPolicyConfig) GetRelayData() bool {
if x != nil && x.RelayData != nil {
return *x.RelayData
}
return false
}
func (x *PeerPolicyConfig) GetLatencyFirst() bool {
if x != nil && x.LatencyFirst != nil {
return *x.LatencyFirst
}
return false
}
func (x *PeerPolicyConfig) GetEncryptionRequired() bool {
if x != nil && x.EncryptionRequired != nil {
return *x.EncryptionRequired
}
return false
}
type TrafficConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
Mtu *uint32 `protobuf:"varint,1,opt,name=mtu,proto3,oneof" json:"mtu,omitempty"`
InstanceRecvBpsLimit *uint64 `protobuf:"varint,2,opt,name=instance_recv_bps_limit,json=instanceRecvBpsLimit,proto3,oneof" json:"instance_recv_bps_limit,omitempty"`
ForeignRelayBpsLimit *uint64 `protobuf:"varint,3,opt,name=foreign_relay_bps_limit,json=foreignRelayBpsLimit,proto3,oneof" json:"foreign_relay_bps_limit,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TrafficConfig) Reset() {
*x = TrafficConfig{}
mi := &file_core_config_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *TrafficConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TrafficConfig) ProtoMessage() {}
func (x *TrafficConfig) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TrafficConfig.ProtoReflect.Descriptor instead.
func (*TrafficConfig) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{7}
}
func (x *TrafficConfig) GetMtu() uint32 {
if x != nil && x.Mtu != nil {
return *x.Mtu
}
return 0
}
func (x *TrafficConfig) GetInstanceRecvBpsLimit() uint64 {
if x != nil && x.InstanceRecvBpsLimit != nil {
return *x.InstanceRecvBpsLimit
}
return 0
}
func (x *TrafficConfig) GetForeignRelayBpsLimit() uint64 {
if x != nil && x.ForeignRelayBpsLimit != nil {
return *x.ForeignRelayBpsLimit
}
return 0
}
// Raw file/management input. Domain parsing and defaults belong to Raw -> Parsed.
// Future generators may rename these messages, preserving field numbers, types
// and collection wrapper nesting.
type DnsConfigRaw struct {
state protoimpl.MessageState `protogen:"open.v1"`
Zone *RepeatedDnsZoneConfigRaw `protobuf:"bytes,1,opt,name=zone,proto3" json:"zone,omitempty"`
Domain *string `protobuf:"bytes,2,opt,name=domain,proto3,oneof" json:"domain,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DnsConfigRaw) Reset() {
*x = DnsConfigRaw{}
mi := &file_core_config_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DnsConfigRaw) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DnsConfigRaw) ProtoMessage() {}
func (x *DnsConfigRaw) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DnsConfigRaw.ProtoReflect.Descriptor instead.
func (*DnsConfigRaw) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{8}
}
func (x *DnsConfigRaw) GetZone() *RepeatedDnsZoneConfigRaw {
if x != nil {
return x.Zone
}
return nil
}
func (x *DnsConfigRaw) GetDomain() string {
if x != nil && x.Domain != nil {
return *x.Domain
}
return ""
}
type RepeatedDnsZoneConfigRaw struct {
state protoimpl.MessageState `protogen:"open.v1"`
Items []*DnsZoneConfigRaw `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RepeatedDnsZoneConfigRaw) Reset() {
*x = RepeatedDnsZoneConfigRaw{}
mi := &file_core_config_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RepeatedDnsZoneConfigRaw) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RepeatedDnsZoneConfigRaw) ProtoMessage() {}
func (x *RepeatedDnsZoneConfigRaw) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RepeatedDnsZoneConfigRaw.ProtoReflect.Descriptor instead.
func (*RepeatedDnsZoneConfigRaw) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{9}
}
func (x *RepeatedDnsZoneConfigRaw) GetItems() []*DnsZoneConfigRaw {
if x != nil {
return x.Items
}
return nil
}
type DnsZoneConfigRaw struct {
state protoimpl.MessageState `protogen:"open.v1"`
Origin string `protobuf:"bytes,1,opt,name=origin,proto3" json:"origin,omitempty"`
Ttl *uint32 `protobuf:"varint,2,opt,name=ttl,proto3,oneof" json:"ttl,omitempty"`
Records *common.RepeatedString `protobuf:"bytes,3,opt,name=records,proto3" json:"records,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DnsZoneConfigRaw) Reset() {
*x = DnsZoneConfigRaw{}
mi := &file_core_config_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DnsZoneConfigRaw) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DnsZoneConfigRaw) ProtoMessage() {}
func (x *DnsZoneConfigRaw) ProtoReflect() protoreflect.Message {
mi := &file_core_config_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DnsZoneConfigRaw.ProtoReflect.Descriptor instead.
func (*DnsZoneConfigRaw) Descriptor() ([]byte, []int) {
return file_core_config_proto_rawDescGZIP(), []int{10}
}
func (x *DnsZoneConfigRaw) GetOrigin() string {
if x != nil {
return x.Origin
}
return ""
}
func (x *DnsZoneConfigRaw) GetTtl() uint32 {
if x != nil && x.Ttl != nil {
return *x.Ttl
}
return 0
}
func (x *DnsZoneConfigRaw) GetRecords() *common.RepeatedString {
if x != nil {
return x.Records
}
return nil
}
var File_core_config_proto protoreflect.FileDescriptor
const file_core_config_proto_rawDesc = "" +
"\n" +
"\x11core_config.proto\x12\vcore_config\x1a\fcommon.proto\"\xe1\x01\n" +
"\n" +
"CoreConfig\x12+\n" +
"\x04node\x18\x01 \x01(\v2\x17.core_config.NodeConfigR\x04node\x120\n" +
"\x06routes\x18\x02 \x01(\v2\x18.core_config.RouteConfigR\x06routes\x12>\n" +
"\vpeer_policy\x18\x03 \x01(\v2\x1d.core_config.PeerPolicyConfigR\n" +
"peerPolicy\x124\n" +
"\atraffic\x18\x04 \x01(\v2\x1a.core_config.TrafficConfigR\atraffic\"\xcb\x01\n" +
"\n" +
"NodeConfig\x12\x1c\n" +
"\apeer_id\x18\x01 \x01(\rH\x00R\x06peerId\x88\x01\x01\x122\n" +
"\vinstance_id\x18\x02 \x01(\v2\f.common.UUIDH\x01R\n" +
"instanceId\x88\x01\x01\x12\x1f\n" +
"\bhostname\x18\x03 \x01(\tH\x02R\bhostname\x88\x01\x01\x12!\n" +
"\fnetwork_name\x18\x04 \x01(\tR\vnetworkNameB\n" +
"\n" +
"\b_peer_idB\x0e\n" +
"\f_instance_idB\v\n" +
"\t_hostname\"\xd9\x02\n" +
"\vRouteConfig\x12.\n" +
"\x04ipv4\x18\x01 \x01(\v2\x15.core_config.IpPrefixH\x00R\x04ipv4\x88\x01\x01\x12.\n" +
"\x04ipv6\x18\x02 \x01(\v2\x15.core_config.IpPrefixH\x01R\x04ipv6\x88\x01\x01\x12B\n" +
"\x11advertised_routes\x18\x03 \x03(\v2\x15.core_config.IpPrefixR\x10advertisedRoutes\x12F\n" +
"\x0eproxy_networks\x18\x04 \x03(\v2\x1f.core_config.ProxyNetworkConfigR\rproxyNetworks\x12L\n" +
"\x10foreign_networks\x18\x05 \x03(\v2!.core_config.ForeignNetworkConfigR\x0fforeignNetworksB\a\n" +
"\x05_ipv4B\a\n" +
"\x05_ipv6\"S\n" +
"\bIpPrefix\x12(\n" +
"\aaddress\x18\x01 \x01(\v2\x0e.common.IpAddrR\aaddress\x12\x1d\n" +
"\n" +
"prefix_len\x18\x02 \x01(\rR\tprefixLen\"~\n" +
"\x12ProxyNetworkConfig\x12)\n" +
"\x04real\x18\x01 \x01(\v2\x15.core_config.IpPrefixR\x04real\x122\n" +
"\x06mapped\x18\x02 \x01(\v2\x15.core_config.IpPrefixH\x00R\x06mapped\x88\x01\x01B\t\n" +
"\a_mapped\"W\n" +
"\x14ForeignNetworkConfig\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12+\n" +
"\x05cidrs\x18\x02 \x03(\v2\x15.core_config.IpPrefixR\x05cidrs\"\xc3\x02\n" +
"\x10PeerPolicyConfig\x12$\n" +
"\vp2p_enabled\x18\x01 \x01(\bH\x00R\n" +
"p2pEnabled\x88\x01\x01\x12)\n" +
"\x0erelay_peer_rpc\x18\x02 \x01(\bH\x01R\frelayPeerRpc\x88\x01\x01\x12\"\n" +
"\n" +
"relay_data\x18\x03 \x01(\bH\x02R\trelayData\x88\x01\x01\x12(\n" +
"\rlatency_first\x18\x04 \x01(\bH\x03R\flatencyFirst\x88\x01\x01\x124\n" +
"\x13encryption_required\x18\x05 \x01(\bH\x04R\x12encryptionRequired\x88\x01\x01B\x0e\n" +
"\f_p2p_enabledB\x11\n" +
"\x0f_relay_peer_rpcB\r\n" +
"\v_relay_dataB\x10\n" +
"\x0e_latency_firstB\x16\n" +
"\x14_encryption_required\"\xde\x01\n" +
"\rTrafficConfig\x12\x15\n" +
"\x03mtu\x18\x01 \x01(\rH\x00R\x03mtu\x88\x01\x01\x12:\n" +
"\x17instance_recv_bps_limit\x18\x02 \x01(\x04H\x01R\x14instanceRecvBpsLimit\x88\x01\x01\x12:\n" +
"\x17foreign_relay_bps_limit\x18\x03 \x01(\x04H\x02R\x14foreignRelayBpsLimit\x88\x01\x01B\x06\n" +
"\x04_mtuB\x1a\n" +
"\x18_instance_recv_bps_limitB\x1a\n" +
"\x18_foreign_relay_bps_limit\"q\n" +
"\fDnsConfigRaw\x129\n" +
"\x04zone\x18\x01 \x01(\v2%.core_config.RepeatedDnsZoneConfigRawR\x04zone\x12\x1b\n" +
"\x06domain\x18\x02 \x01(\tH\x00R\x06domain\x88\x01\x01B\t\n" +
"\a_domain\"O\n" +
"\x18RepeatedDnsZoneConfigRaw\x123\n" +
"\x05items\x18\x01 \x03(\v2\x1d.core_config.DnsZoneConfigRawR\x05items\"{\n" +
"\x10DnsZoneConfigRaw\x12\x16\n" +
"\x06origin\x18\x01 \x01(\tR\x06origin\x12\x15\n" +
"\x03ttl\x18\x02 \x01(\rH\x00R\x03ttl\x88\x01\x01\x120\n" +
"\arecords\x18\x03 \x01(\v2\x16.common.RepeatedStringR\arecordsB\x06\n" +
"\x04_ttlb\x06proto3"
var (
file_core_config_proto_rawDescOnce sync.Once
file_core_config_proto_rawDescData []byte
)
func file_core_config_proto_rawDescGZIP() []byte {
file_core_config_proto_rawDescOnce.Do(func() {
file_core_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_core_config_proto_rawDesc), len(file_core_config_proto_rawDesc)))
})
return file_core_config_proto_rawDescData
}
var file_core_config_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_core_config_proto_goTypes = []any{
(*CoreConfig)(nil), // 0: core_config.CoreConfig
(*NodeConfig)(nil), // 1: core_config.NodeConfig
(*RouteConfig)(nil), // 2: core_config.RouteConfig
(*IpPrefix)(nil), // 3: core_config.IpPrefix
(*ProxyNetworkConfig)(nil), // 4: core_config.ProxyNetworkConfig
(*ForeignNetworkConfig)(nil), // 5: core_config.ForeignNetworkConfig
(*PeerPolicyConfig)(nil), // 6: core_config.PeerPolicyConfig
(*TrafficConfig)(nil), // 7: core_config.TrafficConfig
(*DnsConfigRaw)(nil), // 8: core_config.DnsConfigRaw
(*RepeatedDnsZoneConfigRaw)(nil), // 9: core_config.RepeatedDnsZoneConfigRaw
(*DnsZoneConfigRaw)(nil), // 10: core_config.DnsZoneConfigRaw
(*common.UUID)(nil), // 11: common.UUID
(*common.IpAddr)(nil), // 12: common.IpAddr
(*common.RepeatedString)(nil), // 13: common.RepeatedString
}
var file_core_config_proto_depIdxs = []int32{
1, // 0: core_config.CoreConfig.node:type_name -> core_config.NodeConfig
2, // 1: core_config.CoreConfig.routes:type_name -> core_config.RouteConfig
6, // 2: core_config.CoreConfig.peer_policy:type_name -> core_config.PeerPolicyConfig
7, // 3: core_config.CoreConfig.traffic:type_name -> core_config.TrafficConfig
11, // 4: core_config.NodeConfig.instance_id:type_name -> common.UUID
3, // 5: core_config.RouteConfig.ipv4:type_name -> core_config.IpPrefix
3, // 6: core_config.RouteConfig.ipv6:type_name -> core_config.IpPrefix
3, // 7: core_config.RouteConfig.advertised_routes:type_name -> core_config.IpPrefix
4, // 8: core_config.RouteConfig.proxy_networks:type_name -> core_config.ProxyNetworkConfig
5, // 9: core_config.RouteConfig.foreign_networks:type_name -> core_config.ForeignNetworkConfig
12, // 10: core_config.IpPrefix.address:type_name -> common.IpAddr
3, // 11: core_config.ProxyNetworkConfig.real:type_name -> core_config.IpPrefix
3, // 12: core_config.ProxyNetworkConfig.mapped:type_name -> core_config.IpPrefix
3, // 13: core_config.ForeignNetworkConfig.cidrs:type_name -> core_config.IpPrefix
9, // 14: core_config.DnsConfigRaw.zone:type_name -> core_config.RepeatedDnsZoneConfigRaw
10, // 15: core_config.RepeatedDnsZoneConfigRaw.items:type_name -> core_config.DnsZoneConfigRaw
13, // 16: core_config.DnsZoneConfigRaw.records:type_name -> common.RepeatedString
17, // [17:17] is the sub-list for method output_type
17, // [17:17] is the sub-list for method input_type
17, // [17:17] is the sub-list for extension type_name
17, // [17:17] is the sub-list for extension extendee
0, // [0:17] is the sub-list for field type_name
}
func init() { file_core_config_proto_init() }
func file_core_config_proto_init() {
if File_core_config_proto != nil {
return
}
file_core_config_proto_msgTypes[1].OneofWrappers = []any{}
file_core_config_proto_msgTypes[2].OneofWrappers = []any{}
file_core_config_proto_msgTypes[4].OneofWrappers = []any{}
file_core_config_proto_msgTypes[6].OneofWrappers = []any{}
file_core_config_proto_msgTypes[7].OneofWrappers = []any{}
file_core_config_proto_msgTypes[8].OneofWrappers = []any{}
file_core_config_proto_msgTypes[10].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_core_config_proto_rawDesc), len(file_core_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 11,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_core_config_proto_goTypes,
DependencyIndexes: file_core_config_proto_depIdxs,
MessageInfos: file_core_config_proto_msgTypes,
}.Build()
File_core_config_proto = out.File
file_core_config_proto_goTypes = nil
file_core_config_proto_depIdxs = nil
}
+4 -2
View File
@@ -11,8 +11,8 @@ if [[ ! -f "${proto_root}/api_instance.proto" ]]; then
exit 1
fi
if [[ "$(protoc --version)" != "libprotoc 35.1" && "$(protoc --version)" != "libprotoc 36.1" ]]; then
echo "protoc 35.1 or 36.1 is required" >&2
if [[ "$(protoc --version)" != "libprotoc 35.1" && "$(protoc --version)" != "libprotoc 36.1" && "$(protoc --version)" != "libprotoc 36.2" ]]; then
echo "protoc 35.1, 36.1 or 36.2 is required" >&2
exit 1
fi
if [[ "$(protoc-gen-go --version)" != "protoc-gen-go v1.36.11" ]]; then
@@ -25,6 +25,7 @@ protoc \
--go_out="${repository_root}" \
--go_opt=module=github.com/easytier/easytier/easytier-go \
--go_opt=Mannotations.proto=github.com/easytier/easytier/easytier-go/proto/easytier \
--go_opt=Mcore_config.proto=github.com/easytier/easytier/easytier-go/proto/core_config \
--go_opt=Mcommon.proto=github.com/easytier/easytier/easytier-go/proto/common \
--go_opt=Merror.proto=github.com/easytier/easytier/easytier-go/proto/error \
--go_opt=Macl.proto=github.com/easytier/easytier/easytier-go/proto/acl \
@@ -35,6 +36,7 @@ protoc \
--go_opt=Mweb.proto=github.com/easytier/easytier/easytier-go/proto/web \
"${proto_root}/annotations.proto" \
"${proto_root}/common.proto" \
"${proto_root}/core_config.proto" \
"${proto_root}/error.proto" \
"${proto_root}/acl.proto" \
"${proto_root}/peer_rpc.proto" \
+11
View File
@@ -178,6 +178,16 @@ fn main() -> anyhow::Result<()> {
)
.field_attribute(".common.CompressionAlgoPb.Invalid", "#[strum(disabled)]");
let repeated_types = [
".common.RepeatedString",
".core_config.RepeatedDnsZoneConfigRaw",
];
for ty in repeated_types {
config
.type_attribute(ty, "#[derive(serde::Serialize, serde::Deserialize)]")
.type_attribute(ty, "#[serde(transparent)]");
}
let mut descriptor_set = config.load_fds(&proto_files, &["proto/"])?;
// What protoc wrote (this set carries every file, imports included), read
// before anything else may touch it: the annotations are extension fields,
@@ -240,6 +250,7 @@ fn main() -> anyhow::Result<()> {
pbjson_build::Builder::new()
.register_descriptors(&descriptors)?
.preserve_proto_field_names()
.exclude(repeated_types)
.btree_map(["."])
.build(&["."])?;
+3 -1
View File
@@ -4,6 +4,7 @@ import "common.proto";
import "peer_rpc.proto";
import "api_instance.proto";
import "acl.proto";
import "core_config.proto";
package api.manage;
@@ -50,7 +51,7 @@ message NetworkConfig {
optional bool use_smoltcp = 21;
optional bool disable_ipv6 = 47;
optional string tld_dns_zone = 73;
optional string tld_dns_zone = 73 [deprecated = true];
optional bool enable_kcp_proxy = 22;
optional bool disable_kcp_input = 23;
optional bool disable_p2p = 24;
@@ -111,6 +112,7 @@ message NetworkConfig {
optional string dhcp_ipv4_cidr = 74;
optional bool dhcpv6 = 75;
optional string dhcp_ipv6_cidr = 76;
core_config.DnsConfigRaw dns = 77;
}
message ManagedCredentialConfig {
+6 -2
View File
@@ -65,8 +65,8 @@ message Flags {
// disable symmetric nat hole punching, treat symmetric as cone when enabled
bool disable_sym_hole_punching = 30 [(easytier.flag) = { default: "false" }];
// tld dns zone for magic dns
string tld_dns_zone = 31 [(easytier.flag) = { default: "et.net.", form: true }];
// tld dns zone for magic dns
string tld_dns_zone = 31 [deprecated = true, (easytier.flag) = { default: "", deprecated: true }];
bool p2p_only = 32 [(easytier.flag) = { default: "false" }];
@@ -284,3 +284,7 @@ message SecureModeConfig {
// base64(X25519 public key), required if local_private_key is set
optional string local_public_key = 3;
}
// Collection wrappers preserve omitted versus explicitly empty input.
// The items field number and wire type are part of the configuration contract.
message RepeatedString { repeated string items = 1; }
+16
View File
@@ -54,3 +54,19 @@ message TrafficConfig {
optional uint64 instance_recv_bps_limit = 2;
optional uint64 foreign_relay_bps_limit = 3;
}
// Raw file/management input. Domain parsing and defaults belong to Raw -> Parsed.
// Future generators may rename these messages, preserving field numbers, types
// and collection wrapper nesting.
message DnsConfigRaw {
RepeatedDnsZoneConfigRaw zone = 1;
optional string domain = 2;
}
message RepeatedDnsZoneConfigRaw { repeated DnsZoneConfigRaw items = 1; }
message DnsZoneConfigRaw {
string origin = 1;
optional uint32 ttl = 2;
common.RepeatedString records = 3;
}
+6
View File
@@ -595,6 +595,12 @@ impl SecureModeConfig {
}
}
impl From<Vec<String>> for RepeatedString {
fn from(items: Vec<String>) -> Self {
Self { items }
}
}
#[cfg(test)]
mod tests {
use super::{TunnelInfo, Url, normalize_tunnel_url};
+6 -1
View File
@@ -1,3 +1,8 @@
include!(concat!(env!("OUT_DIR"), "/core_config.rs"));
#[cfg(feature = "json-rpc")]
include!(concat!(env!("OUT_DIR"), "/core_config.serde.rs"));
impl From<Vec<DnsZoneConfigRaw>> for RepeatedDnsZoneConfigRaw {
fn from(items: Vec<DnsZoneConfigRaw>) -> Self {
Self { items }
}
}
@@ -17,6 +17,7 @@ const protobufTsPluginRoot = dirname(require.resolve('@protobuf-ts/plugin/packag
const protoFiles = [
'annotations.proto',
'common.proto',
'core_config.proto',
'acl.proto',
'api_instance.proto',
'api_manage.proto',
@@ -61,11 +61,12 @@ const BOOLEAN_CONFIG_FIELDS = [
'disable_tcp_hole_punching',
]
const LEGACY_VPN_PORTAL_FIELDS = [
const LEGACY_NETWORK_CONFIG_FIELDS = [
'enable_vpn_portal',
'vpn_portal_listen_port',
'vpn_portal_client_network_addr',
'vpn_portal_client_network_len',
'tld_dns_zone',
]
function readGeneratedNetworkConfigFields() {
@@ -168,7 +169,11 @@ function allFieldFixture() {
mtu: 1280,
mapped_listeners: ['tcp://127.0.0.1:13010'],
enable_magic_dns: true,
tld_dns_zone: 'nas.internal.',
dns: { zone: [{
origin: 'example.test.',
ttl: 0,
records: ['nas IN A 192.0.2.1'],
}] },
enable_private_mode: true,
enable_quic_proxy: true,
disable_quic_input: true,
@@ -268,7 +273,7 @@ function allFieldFixture() {
function assertFixtureCoversGeneratedFields() {
const generatedFields = readGeneratedNetworkConfigFields()
.filter((field) => !LEGACY_VPN_PORTAL_FIELDS.includes(field))
.filter((field) => !LEGACY_NETWORK_CONFIG_FIELDS.includes(field))
const fixtureFields = new Set(Object.keys(allFieldFixture()))
const missing = generatedFields.filter((field) => !fixtureFields.has(field))
@@ -289,7 +294,7 @@ function assertFullFieldRoundTrip() {
expectNoCamelCaseKeys(backend)
for (const field of readGeneratedNetworkConfigFields()
.filter((field) => !LEGACY_VPN_PORTAL_FIELDS.includes(field))) {
.filter((field) => !LEGACY_NETWORK_CONFIG_FIELDS.includes(field))) {
assert.ok(field in backend, `backend JSON should include fixture field ${field}`)
}
@@ -606,3 +611,27 @@ for (const test of tests) {
test()
console.log(`ok ${test.name}`)
}
// A collection wrapper, including an empty one, must survive form normalization.
for (const dns of [
undefined,
{},
{ domain: 'mesh.test.' },
{ zone: [] },
{ domain: 'mesh.test.', zone: [{ origin: 'example.test.' }] },
{ zone: [{ origin: 'example.test.', ttl: 0, records: [] }] },
]) {
const normalized = normalizeNetworkConfig({ ...DEFAULT_NETWORK_CONFIG(), dns });
const backend = toBackendNetworkConfig(normalized);
const restored = normalizeNetworkConfig(backend);
assert.deepEqual(restored.dns, normalized.dns);
assert.equal(restored.dns !== undefined, dns !== undefined);
assert.equal(restored.dns?.domain, dns?.domain);
assert.equal(restored.dns?.zone !== undefined, dns?.zone !== undefined);
const before = dns?.zone?.[0];
const after = restored.dns?.zone?.[0];
if (before) {
assert.equal(after.records !== undefined, before.records !== undefined);
assert.equal(after.ttl, before.ttl);
}
}
@@ -387,18 +387,6 @@ function removeVpnPortalClient(index: number) {
</div>
</div>
<div class="flex flex-row gap-x-9 flex-wrap">
<div class="flex flex-col gap-2 basis-5/12 grow">
<div class="flex">
<label for="tld_dns_zone">{{ t('tld_dns_zone') }}</label>
<span class="pi pi-question-circle ml-2 self-center" v-tooltip="t('tld_dns_zone_help')"></span>
</div>
<InputText id="tld_dns_zone" :model-value="curNetwork.tld_dns_zone"
@update:model-value="curNetwork.tld_dns_zone = $event?.trim() || undefined"
aria-describedby="tld_dns_zone-help" :placeholder="t('tld_dns_zone_placeholder')" />
</div>
</div>
<div class="flex flex-row gap-x-9 flex-wrap">
<div class="flex flex-col gap-2 basis-5/12 grow">
<label for="hostname">{{ t('hostname') }}</label>
@@ -255,11 +255,6 @@ disable_sym_hole_punching_help: 禁用对称NAT的打洞(生日攻击),将
enable_magic_dns: 启用魔法DNS
enable_magic_dns_help: |
启用魔法DNS,允许通过EasyTier的DNS服务器访问其他节点的虚拟IPv4地址, 如 node1.et.net。
tld_dns_zone: DNS域名后缀
tld_dns_zone_help: |
自定义魔法DNS的顶级域名(TLD)后缀。默认为 "et.net."。
如需使用其他域名后缀,可改为 "et.internal." 或 "easytier.lan" 等。
tld_dns_zone_placeholder: 例如 et.net.
enable_private_mode: 启用私有模式
enable_private_mode_help: |
启用私有模式,则不允许使用了与本网络不相同的网络名称和密码的节点通过本节点进行握手或中转。
@@ -254,11 +254,6 @@ disable_sym_hole_punching_help: Disable special hole punching handling for symme
enable_magic_dns: Enable Magic DNS
enable_magic_dns_help: |
Enable magic dns, all nodes in the network can access each other by domain name, e.g.: node1.et.net.
tld_dns_zone: DNS Zone TLD
tld_dns_zone_help: |
Custom top-level domain (TLD) zone for Magic DNS hostnames. Default is "et.net.".
Change this if you need a different domain suffix, e.g. "et.internal." or "easytier.lan".
tld_dns_zone_placeholder: e.g. et.net.
enable_private_mode: Enable Private Mode
enable_private_mode_help: |
Enable private mode, nodes with different network names or passwords from this network are not allowed to perform handshake or relay through this node.
+19 -4
View File
@@ -55,14 +55,24 @@ export const PEER_PROTOCOLS: { [proto: string]: number } = {
srv: 0,
}
// Configuration JSON uses transparent repeated wrappers, as Rust Serde does.
// Derive the value types from the protocol schema rather than copying fields.
type ConfigJson<T> = T extends { items: Array<infer Item> }
? ConfigJson<Item>[]
: T extends object ? { [Key in keyof T]: ConfigJson<T[Key]> } : T
export type DnsConfig = ConfigJson<NonNullable<ProtoNetworkConfig['dns']>>
export type NetworkConfig = Omit<
ProtoNetworkConfig,
| 'instance_id'
| 'instance_recv_bps_limit'
| 'mtu'
| 'networking_method'
| 'dns'
> & {
instance_id: string
dns?: DnsConfig
mtu: number | null
instance_recv_bps_limit: number | string | null
networking_method: NetworkingMethod | string
@@ -170,6 +180,7 @@ function newFlagValues(): Record<string, boolean> {
export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
return {
...NetworkConfigPb.create(),
dns: undefined,
instance_id: uuidv4(),
@@ -207,7 +218,6 @@ export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
mtu: null,
instance_recv_bps_limit: null,
mapped_listeners: [],
tld_dns_zone: 'et.net.',
port_forwards: [],
acl: emptyAcl(),
}
@@ -360,9 +370,11 @@ function applyNetworkingMethod(
}
export function normalizeNetworkConfig(config: NetworkConfig): NetworkConfig {
const normalized = NetworkConfigPb.fromJson(prepareNetworkConfigForProtoJson(config) as any, {
const { dns, ...protoConfig } = prepareNetworkConfigForProtoJson(config)
const normalized = NetworkConfigPb.fromJson(protoConfig as any, {
ignoreUnknownFields: true,
}) as unknown as NetworkConfig
normalized.dns = dns
applyNetworkingMethod(normalized, { fillPeerUrlsFromPeers: true })
normalized.mtu = normalizeNumberForInput(normalized.mtu)
@@ -397,7 +409,8 @@ export function normalizeNetworkConfig(config: NetworkConfig): NetworkConfig {
}
export function toBackendNetworkConfig(config: NetworkConfig): NetworkConfig {
const backend = NetworkConfigPb.fromJson(prepareNetworkConfigForProtoJson(config) as any, {
const { dns, ...protoConfig } = prepareNetworkConfigForProtoJson(config)
const backend = NetworkConfigPb.fromJson(protoConfig as any, {
ignoreUnknownFields: true,
})
@@ -417,9 +430,11 @@ export function toBackendNetworkConfig(config: NetworkConfig): NetworkConfig {
backend.secure_mode = { enabled: true, local_private_key: credentialSecret }
}
return NetworkConfigPb.toJson(backend, {
const result = NetworkConfigPb.toJson(backend, {
useProtoFieldName: true,
}) as unknown as NetworkConfig
if (dns !== undefined) result.dns = dns
return result
}
export function normalizeVpnPortalInfo(info: unknown): VpnPortalInfo {
@@ -416,7 +416,6 @@ describe('Config.vue network config projection', () => {
expect(input(wrapper, '#no_tun').checked).toBe(true)
expect(input(wrapper, '#hostname').value).toBe('host-a')
expect(input(wrapper, '#tld_dns_zone').value).toBe('et.net.')
expect(input(wrapper, '#subnet-proxy').value).toBe('10.10.0.0/16,172.16.1.0/24')
expect(input(wrapper, '#vpn_portal_wireguard_listen').value).toBe('0.0.0.0:22023')
expect(input(wrapper, '#dev_name').value).toBe('tun-test')
@@ -447,7 +446,6 @@ describe('Config.vue network config projection', () => {
await wrapper.find('#no_tun').setValue(false)
await wrapper.find('#disable_ipv6').setValue(false)
await setInput(wrapper, '#hostname', 'host-edited')
await setInput(wrapper, '#tld_dns_zone', 'nas.internal.')
await setInput(wrapper, '#subnet-proxy', '10.7.0.0/16,172.17.0.0/16')
await setInput(wrapper, '#vpn_portal_wireguard_listen', '[::]:23000')
await setInput(wrapper, 'input[data-add-label="add_listener_url"]', 'tcp://0.0.0.0:13010')
@@ -476,7 +474,6 @@ describe('Config.vue network config projection', () => {
no_tun: false,
disable_ipv6: false,
hostname: 'host-edited',
tld_dns_zone: 'nas.internal.',
proxy_cidrs: ['10.7.0.0/16', '172.17.0.0/16'],
vpn_portal_config: {
wireguard_listen: '[::]:23000',
@@ -512,7 +509,6 @@ describe('Config.vue network config projection', () => {
network_secret: 'secret-edited',
peer_urls: ['tcp://peer-x:11010', 'udp://peer-y:11010'],
listener_urls: ['tcp://0.0.0.0:13010'],
tld_dns_zone: 'nas.internal.',
mtu: 1260,
instance_recv_bps_limit: '9007199254740993',
vpn_portal_config: {
@@ -532,9 +528,6 @@ describe('Config.vue network config projection', () => {
dst_port: 9090,
}],
})
await setInput(wrapper, '#tld_dns_zone', '')
expect(toBackendNetworkConfig(curNetwork).tld_dns_zone).toBeUndefined()
})
it('shows a flag the configuration does not state as the value the core resolves', async () => {
+1 -7
View File
@@ -1086,7 +1086,6 @@ mod tests {
assert_eq!(updated.disable_upnp, Some(true));
assert_eq!(updated.disable_relay_data, Some(true));
assert_eq!(updated.enable_magic_dns, Some(true));
assert_eq!(updated.tld_dns_zone.as_deref(), Some("nas.internal."));
assert_eq!(updated.enable_private_mode, Some(true));
assert_eq!(updated.mtu, Some(1360));
assert_eq!(
@@ -1113,7 +1112,6 @@ mod tests {
"relay_all_peer_rpc": false,
"multi_thread": false,
"disable_relay_data": false,
"tld_dns_zone": "initial.internal.",
"mtu": 1380
})
}
@@ -1164,7 +1162,6 @@ mod tests {
disable_upnp: Some(true),
disable_relay_data: Some(true),
enable_magic_dns: Some(true),
tld_dns_zone: Some("nas.internal.".to_owned()),
enable_private_mode: Some(true),
mtu: Some(1360),
data_compress_algo: Some(CompressionAlgoPb::Zstd as i32),
@@ -1654,11 +1651,10 @@ mod tests {
.await
.unwrap();
let initial = wait_for_runtime_config(&core_manager, instance_id, |config| {
wait_for_runtime_config(&core_manager, instance_id, |config| {
config.network_name.as_deref() == Some("managed-initial")
})
.await;
assert_eq!(initial.tld_dns_zone.as_deref(), Some("initial.internal."));
wait_for_applied_revision(&mgr, user_id, machine_id, "rev-initial").await;
// Runtime-only mutations do not change SQLite. Invalidate the Session
@@ -1775,7 +1771,6 @@ mod tests {
Some("managed-redelivered")
);
assert_eq!(redelivered.enable_kcp_proxy, Some(true));
assert_eq!(redelivered.tld_dns_zone, None);
assert_eq!(redelivered.instance_recv_bps_limit, Some(654321));
assert_eq!(
core_manager.config_source(instance_id),
@@ -1811,7 +1806,6 @@ mod tests {
Some("managed-redelivered")
);
assert_eq!(replayed.enable_kcp_proxy, Some(true));
assert_eq!(replayed.tld_dns_zone, None);
assert_eq!(replayed.instance_recv_bps_limit, Some(654321));
webhook_server.abort();
@@ -730,26 +730,6 @@ mod tests {
.collect()
}
#[test]
fn magic_dns_zone_change_requires_recreate() {
let current = config_with_port_forwards(Vec::new());
let mut desired = current.clone();
desired.tld_dns_zone = Some("et.net.".to_owned());
let action =
prepare_web_source_runtime_reconcile_from_current(&current, desired.clone(), false)
.unwrap();
assert!(matches!(action, RuntimeReconcileAction::Unchanged(_)));
desired.tld_dns_zone = Some("nas.internal.".to_owned());
let action =
prepare_web_source_runtime_reconcile_from_current(&current, desired, false).unwrap();
let RuntimeReconcileAction::Run { config, overwrite } = action else {
panic!("changing the DNS zone must recreate the instance");
};
assert!(overwrite);
assert_eq!(config.tld_dns_zone.as_deref(), Some("nas.internal."));
}
#[test]
fn vpn_portal_client_changes_produce_hot_patches() {
let current = config_with_vpn_portal(
-3
View File
@@ -248,9 +248,6 @@ core_clap:
accept_dns:
en: "if true, enable magic dns. with magic dns, you can access other nodes with a domain name, e.g.: <hostname>.et.net. magic dns will modify your system dns settings, enable it carefully."
zh-CN: "如果为true,则启用魔法DNS。使用魔法DNS,您可以使用域名访问其他节点,例如:<hostname>.et.net。魔法DNS将修改您的系统DNS设置,请谨慎启用。"
tld_dns_zone:
en: "specify the top-level domain zone for magic DNS. if not provided, defaults to the value from dns_server module (et.net.). only used when accept_dns is true."
zh-CN: "指定魔法DNS的顶级域名区域。如果未提供,默认使用dns_server模块中的值(et.net.)。仅在accept_dns为true时使用。"
private_mode:
en: "if true, foreign networks are only allowed when this node can verify they use the same network secret, or when a foreign credential node is already trusted via admin-issued credential propagation; different or missing secrets are otherwise rejected."
zh-CN: "如果为true,则仅允许两类 foreign network 接入:本节点能验证其使用相同 network secret 的节点,或已通过 foreign network 管理节点传播而被信任的 credential 节点;否则 secret 不同或缺失时会被拒绝。"
+29 -1
View File
@@ -453,6 +453,9 @@ struct NetworkOptions {
help = t!("core_clap.credential_file").to_string()
)]
credential_file: Option<PathBuf>,
#[arg(long, env = "ET_TLD_DNS_ZONE", hide = true)]
tld_dns_zone: Option<String>,
}
#[derive(Parser, Debug)]
@@ -945,7 +948,12 @@ impl NetworkOptions {
cfg.set_secure_mode(Some(normalize_secure_mode_config(c)?));
}
cfg.patch_flags(self.flags.clone());
let mut flags = self.flags.clone();
#[allow(deprecated)]
if flags.tld_dns_zone.is_none() {
flags.tld_dns_zone = self.tld_dns_zone.clone();
}
cfg.patch_flags(flags);
#[cfg(not(feature = "tun"))]
cfg.patch_flags(FlagsPatch {
no_tun: Some(true),
@@ -1949,6 +1957,26 @@ enabled = true
// Deprecated flag is not accepted.
assert!(parse_flags(&["easytier", "--quic-listen-port", "1234"]).is_err());
assert!(
!cli_command()
.render_help()
.to_string()
.contains("tld-dns-zone")
);
assert!(
!cli_command()
.render_long_help()
.to_string()
.contains("tld-dns-zone")
);
let legacy_cli =
Cli::try_parse_from(["easytier-core", "--tld-dns-zone", "custom.local."]).unwrap();
let cfg = TomlConfigLoader::default();
legacy_cli.network_options.merge_into(&cfg).unwrap();
#[allow(deprecated)]
{
assert_eq!(cfg.get_flags().tld_dns_zone, "custom.local.");
}
// MTU tests: u16 range validation
let patch = parse_flags(&["easytier", "--mtu", "1400"]).unwrap();
@@ -4,6 +4,7 @@ use easytier_core::gateway::magic_dns::{
MagicDnsRoutePublisher, MagicDnsRouteSnapshot, run_magic_dns_route_publisher,
};
use easytier_core::instance::CorePacketPlane;
use hickory_proto::rr::LowerName;
use tokio::task::JoinSet;
use crate::proto::{
@@ -22,11 +23,13 @@ pub struct MagicDnsClientInstance {
rpc_client: RuntimeRpcClient,
rpc_stub: Option<Box<dyn MagicDnsServerRpc<Controller = BaseController> + Send>>,
route_source: Arc<CorePacketPlane>,
zone: LowerName,
tasks: JoinSet<()>,
}
struct RpcMagicDnsRoutePublisher {
rpc_stub: Box<dyn MagicDnsServerRpc<Controller = BaseController> + Send>,
zone: LowerName,
}
#[async_trait::async_trait]
@@ -56,7 +59,7 @@ impl MagicDnsRoutePublisher for RpcMagicDnsRoutePublisher {
..Default::default()
})
.collect(),
zone: snapshot.zone.clone(),
zone: self.zone.to_string(),
};
tracing::debug!(
"MagicDnsClientInstance::update_dns_task: update dns records: {:?}",
@@ -70,7 +73,10 @@ impl MagicDnsRoutePublisher for RpcMagicDnsRoutePublisher {
}
impl MagicDnsClientInstance {
pub(crate) async fn new(route_source: Arc<CorePacketPlane>) -> Result<Self, anyhow::Error> {
pub(crate) async fn new(
route_source: Arc<CorePacketPlane>,
zone: LowerName,
) -> Result<Self, anyhow::Error> {
let mut rpc_client = runtime_rpc_client(MAGIC_DNS_INSTANCE_ADDR.parse().unwrap());
let rpc_stub = rpc_client
.scoped_client::<MagicDnsServerRpcClientFactory<BaseController>>("".to_string())
@@ -79,6 +85,7 @@ impl MagicDnsClientInstance {
rpc_client,
rpc_stub: Some(rpc_stub),
route_source,
zone,
tasks: JoinSet::new(),
})
}
@@ -86,8 +93,9 @@ impl MagicDnsClientInstance {
async fn update_dns_task(
route_source: Arc<CorePacketPlane>,
rpc_stub: Box<dyn MagicDnsServerRpc<Controller = BaseController> + Send>,
zone: LowerName,
) -> Result<(), anyhow::Error> {
let mut publisher = RpcMagicDnsRoutePublisher { rpc_stub };
let mut publisher = RpcMagicDnsRoutePublisher { rpc_stub, zone };
run_magic_dns_route_publisher(
route_source.as_ref(),
&mut publisher,
@@ -99,8 +107,9 @@ impl MagicDnsClientInstance {
pub async fn run_and_wait(&mut self) {
let rpc_stub = self.rpc_stub.take().unwrap();
let route_source = self.route_source.clone();
let zone = self.zone.clone();
self.tasks.spawn(async move {
let ret = Self::update_dns_task(route_source, rpc_stub).await;
let ret = Self::update_dns_task(route_source, rpc_stub, zone).await;
if let Err(e) = ret {
tracing::error!("MagicDnsServerInstanceData::run_and_wait: {:?}", e);
}
+2
View File
@@ -3,6 +3,8 @@
pub(crate) mod config;
#[cfg(all(feature = "magic-dns", feature = "tun"))]
pub(crate) mod server;
#[cfg(all(feature = "magic-dns", feature = "tun"))]
pub(crate) mod zone;
#[cfg(all(feature = "magic-dns", feature = "tun"))]
pub mod client_instance;
+2 -1
View File
@@ -143,7 +143,8 @@ impl DnsRunner {
tokio::select! {
_ = cancel_token.cancelled() => Ok(()),
ret = async {
let client = MagicDnsClientInstance::new(self.packet_plane.clone()).await?;
let zone = super::zone::dns_domain(&self.global_ctx.config)?;
let client = MagicDnsClientInstance::new(self.packet_plane.clone(), zone).await?;
self.client = Some(client);
self.client.as_mut().unwrap().run_and_wait().await;
Err(anyhow::anyhow!("Client instance exit"))
+5 -4
View File
@@ -60,8 +60,8 @@ impl RequestHandler for CatalogRequestHandler {
}
}
pub fn build_authority(domain: &str, records: &[Record]) -> Result<InMemoryAuthority> {
let zone = rr::Name::from_str(domain)?;
pub fn build_authority(origin: &rr::LowerName, records: &[Record]) -> Result<InMemoryAuthority> {
let zone = rr::Name::from(origin);
let mut authority = InMemoryAuthority::empty(zone, ZoneType::Primary, false);
for record in records.iter() {
let r = record.try_into()?;
@@ -79,8 +79,9 @@ impl Server {
let mut catalog = Catalog::new();
for (domain, records) in config.zones().iter() {
let zone = rr::Name::from_str(domain.as_str())?;
let authroty = build_authority(domain, records)?;
catalog.upsert(zone.clone().into(), vec![Arc::new(authroty)]);
let lower = zone.clone().into();
let authroty = build_authority(&lower, records)?;
catalog.upsert(lower, vec![Arc::new(authroty)]);
}
// use forwarder authority for the root zone
@@ -11,6 +11,7 @@ use super::{
config::{GeneralConfigBuilder, RunConfigBuilder},
server::Server,
system_config::{OSConfig, SystemConfig},
zone::{self, ZoneGroup},
};
use crate::{
common::{
@@ -48,16 +49,26 @@ use hickory_proto::serialize::binary::{BinDecodable, BinEncoder};
use hickory_server::authority::{MessageRequest, MessageResponse};
use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo};
use std::sync::Mutex;
use std::{collections::BTreeMap, io, net::Ipv4Addr, str::FromStr, sync::Arc, time::Duration};
use std::{
collections::{BTreeMap, BTreeSet},
io,
net::Ipv4Addr,
sync::Arc,
time::Duration,
};
pub(super) struct MagicDnsServerInstanceData {
dns_server: Server,
pub(super) dns_server: Server,
tun_dev: Option<String>,
net_ns: NetNS,
fake_ip: Ipv4Addr,
manage_fake_ip_route: bool,
route_store: MagicDnsRecordStore,
record_apply: tokio::sync::Mutex<()>,
default_zone: LowerName,
/// Static records from this instance's local configuration, grouped once
/// at startup and merged into every refresh of the matching zone.
configured: BTreeMap<LowerName, ZoneGroup>,
system_config: Option<Box<dyn SystemConfig>>,
}
@@ -66,7 +77,7 @@ impl MagicDnsServerInstanceData {
pub async fn update_dns_records<'a, T: Iterator<Item = &'a MagicDnsRoute>>(
&self,
routes: T,
zone: &str,
zone: &LowerName,
) -> Result<(), anyhow::Error> {
let mut records: Vec<Record> = vec![];
for route in routes {
@@ -105,14 +116,13 @@ impl MagicDnsServerInstanceData {
.build()?;
records.push(soa_record);
let authority = build_authority(zone, &records)?;
let mut authority = build_authority(zone, &records)?;
if let Some(group) = self.configured.get(zone) {
group.extend_authority(&mut authority);
}
self.dns_server
.upsert(
LowerName::from_str(zone)
.with_context(|| "Invalid zone name, expect format like \"et.net.\"")?,
Arc::new(authority),
)
.upsert(zone.clone(), Arc::new(authority))
.await;
tracing::debug!("Updated DNS records for zone {}: {:?}", zone, records);
@@ -120,16 +130,34 @@ impl MagicDnsServerInstanceData {
Ok(())
}
pub async fn update(&self) {
pub async fn update(&self) -> Result<(), anyhow::Error> {
let snapshot = self.route_store.snapshot();
for (zone, routes) in &snapshot.zones {
if let Err(e) = self.update_dns_records(routes.iter(), zone).await {
// Route zones from connected clients, plus the default zone and any
// statically configured origins, must all stay authoritative.
let mut zones: BTreeSet<LowerName> = snapshot.zones.keys().cloned().collect();
zones.insert(self.default_zone.clone());
zones.extend(self.configured.keys().cloned());
let mut first_err = None;
for zone in zones {
let routes = snapshot.zones.get(&zone).into_iter().flatten();
if let Err(e) = self.update_dns_records(routes, &zone).await {
tracing::error!("Failed to update DNS records for zone {}: {:?}", zone, e);
if first_err.is_none() {
first_err = Some(e);
}
}
}
first_err.map_or(Ok(()), Err)
}
async fn keep_zone_authoritative(&self, zone: &str) {
/// Initializes the default zone and all configured static zones before
/// queries are accepted.
pub async fn init_zones(&self) -> Result<(), anyhow::Error> {
let _apply = self.record_apply.lock().await;
self.update().await
}
async fn keep_zone_authoritative(&self, zone: &LowerName) {
if let Err(e) = self
.update_dns_records(std::iter::empty::<&MagicDnsRoute>(), zone)
.await
@@ -142,12 +170,23 @@ impl MagicDnsServerInstanceData {
}
}
fn do_system_config(&self, zone: &str) -> Result<(), anyhow::Error> {
fn do_system_config(&self) -> Result<(), anyhow::Error> {
if let Some(c) = &self.system_config {
// The default zone goes first: Windows uses this list as the
// adapter's search list, so short names must prefer the default
// suffix.
let default_zone = self.default_zone.to_string();
let mut domains = vec![default_zone.clone()];
domains.extend(
self.configured
.keys()
.filter(|&origin| origin != &self.default_zone)
.map(ToString::to_string),
);
c.set_dns(&OSConfig {
nameservers: vec![self.fake_ip.to_string()],
search_domains: vec![zone.to_string()],
match_domains: vec![zone.to_string()],
search_domains: vec![default_zone],
match_domains: domains,
})?;
}
Ok(())
@@ -184,8 +223,8 @@ impl MagicDnsServerRpc for MagicDnsServerInstanceData {
let Some(remote_addr) = &tunnel_info.remote_addr else {
return Err(anyhow::anyhow!("No remote addr").into());
};
let zone = zone::parse_origin(&input.zone)?;
let _apply = self.record_apply.lock().await;
let zone = input.zone.clone();
let remote_addr: url::Url = remote_addr.clone().into();
let routes = input
.routes
@@ -203,7 +242,7 @@ impl MagicDnsServerRpc for MagicDnsServerInstanceData {
self.keep_zone_authoritative(&zone).await;
}
self.update().await;
let _ = self.update().await;
Ok(Default::default())
}
@@ -224,7 +263,7 @@ impl MagicDnsServerRpc for MagicDnsServerInstanceData {
})),
});
}
ret.insert(zone, dns_records);
ret.insert(zone.to_string(), dns_records);
}
Ok(GetDnsRecordResponse { records: ret })
}
@@ -328,7 +367,7 @@ impl RpcServerHook for MagicDnsServerInstanceData {
for zone in self.route_store.remove_client(remote_addr.as_ref()) {
self.keep_zone_authoritative(&zone).await;
}
self.update().await;
let _ = self.update().await;
}
}
@@ -421,6 +460,13 @@ impl MagicDnsServerInstance {
fake_ip: Ipv4Addr,
manage_fake_ip_route: bool,
) -> Result<Self, anyhow::Error> {
// Validate the local DNS configuration before attempting the election:
// an invalid configuration must not win the listener and then fail or
// tear down the shared server.
let default_zone = zone::dns_domain(&global_ctx.config)?;
let configured = zone::configured_zones(global_ctx.config.get_dns().as_ref())
.context("Invalid local DNS configuration")?;
let tcp_listener = runtime_rpc_listener(MAGIC_DNS_INSTANCE_SOCKET_ADDR.parse()?);
let mut rpc_server = StandAloneServer::new(tcp_listener);
rpc_server.serve().await?;
@@ -452,6 +498,8 @@ impl MagicDnsServerInstance {
manage_fake_ip_route,
route_store: MagicDnsRecordStore::default(),
record_apply: tokio::sync::Mutex::new(()),
default_zone: default_zone.clone(),
configured,
system_config: get_system_config(tun_dev.as_deref())?,
});
@@ -460,16 +508,12 @@ impl MagicDnsServerInstance {
.register(MagicDnsServerRpcServer::new_arc(data.clone()), "");
rpc_server.set_hook(data.clone());
// Use configured tld_dns_zone or fall back to DEFAULT_ET_DNS_ZONE if empty
let flags = global_ctx.get_flags();
let tld_dns_zone_clone = flags.tld_dns_zone.clone();
data.update_dns_records(std::iter::empty(), &tld_dns_zone_clone)
data.init_zones()
.await
.context("Failed to initialize DNS zone")?;
.context("Failed to initialize DNS zones")?;
let data_clone = data.clone();
tokio::task::spawn_blocking(move || data_clone.do_system_config(&tld_dns_zone_clone))
tokio::task::spawn_blocking(move || data_clone.do_system_config())
.await
.context("Failed to configure system")??;
@@ -512,3 +556,399 @@ impl Drop for MagicDnsServerInstance {
println!("MagicDnsServerInstance dropped");
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::proto::api::instance::Route;
use easytier_core::config::toml::{DnsConfig, DnsZoneConfig};
use hickory_proto::{
op::{Message, Query},
rr::{RData, RecordType},
};
use std::str::FromStr;
type DomainConfigRecord = (Vec<String>, Vec<String>);
type DomainConfigSpyCalls = Arc<Mutex<Vec<DomainConfigRecord>>>;
struct SystemConfigSpy(DomainConfigSpyCalls);
impl SystemConfig for SystemConfigSpy {
fn set_dns(&self, config: &OSConfig) -> io::Result<()> {
self.0
.lock()
.unwrap()
.push((config.search_domains.clone(), config.match_domains.clone()));
Ok(())
}
fn close(&self) -> io::Result<()> {
Ok(())
}
}
fn config(zones: &[(&str, &[&str])]) -> DnsConfig {
DnsConfig {
domain: None,
zone: Some(
zones
.iter()
.map(|(origin, records)| DnsZoneConfig {
origin: (*origin).to_owned(),
ttl: Some(60),
records: Some(
records
.iter()
.map(|record| (*record).to_owned())
.collect::<Vec<_>>()
.into(),
),
})
.collect::<Vec<_>>()
.into(),
),
}
}
async fn data_with_default_zone(
default_zone: &str,
dns: Option<&DnsConfig>,
) -> MagicDnsServerInstanceData {
let data = MagicDnsServerInstanceData {
dns_server: Server::new(
RunConfigBuilder::default()
.general(GeneralConfigBuilder::default().build().unwrap())
.build()
.unwrap(),
),
tun_dev: None,
net_ns: NetNS::new(None),
fake_ip: "100.100.100.101".parse().unwrap(),
manage_fake_ip_route: false,
route_store: MagicDnsRecordStore::default(),
record_apply: tokio::sync::Mutex::new(()),
default_zone: zone::parse_origin(default_zone).unwrap(),
configured: zone::configured_zones(dns).unwrap(),
system_config: None,
};
data.init_zones().await.unwrap();
data
}
async fn data_with(dns: Option<&DnsConfig>) -> MagicDnsServerInstanceData {
data_with_default_zone("et.net.", dns).await
}
fn controller(port: u16) -> BaseController {
let mut ctrl = BaseController::default();
ctrl.set_tunnel_info(Some(TunnelInfo {
remote_addr: Some(
url::Url::parse(&format!("tcp://127.0.0.1:{port}"))
.unwrap()
.into(),
),
..Default::default()
}));
ctrl
}
fn request(ip: &str) -> UpdateDnsRecordRequest {
UpdateDnsRecordRequest {
zone: "et.net.".to_owned(),
routes: vec![Route {
hostname: "node".to_owned(),
ipv4_addr: Some(Ipv4Inet::from_str(ip).unwrap().into()),
..Default::default()
}],
}
}
async fn query(
data: &MagicDnsServerInstanceData,
name: &str,
record_type: RecordType,
) -> Message {
let mut request = Message::new();
request.add_query(Query::query(name.parse().unwrap(), record_type));
let response = data
.resolve_query_inner(MagicDnsQuery {
source: "127.0.0.1:5353".parse().unwrap(),
payload: request.to_vec().unwrap(),
})
.await
.unwrap();
Message::from_vec(&response).unwrap()
}
async fn a(data: &MagicDnsServerInstanceData, name: &str) -> Vec<Ipv4Addr> {
let mut answers = query(data, name, RecordType::A)
.await
.answers()
.iter()
.filter_map(|record| match record.data() {
RData::A(ip) => Some(ip.0),
_ => None,
})
.collect::<Vec<_>>();
answers.sort();
answers
}
fn route(hostname: &str, addr: [u8; 4]) -> MagicDnsRoute {
MagicDnsRoute {
hostname: hostname.to_owned(),
ipv4_addr: Some(addr.into()),
}
}
#[test]
fn replaces_routes_for_the_same_client_without_touching_other_clients() {
let store = MagicDnsRecordStore::default();
let zone = zone::parse_origin("et.net.").unwrap();
assert!(!store.replace_client_routes(
zone.clone(),
"tcp://client-a".to_owned(),
vec![route("old-a", [10, 0, 0, 1])],
));
assert!(!store.replace_client_routes(
zone.clone(),
"tcp://client-b".to_owned(),
vec![route("peer-b", [10, 0, 0, 2])],
));
assert!(!store.replace_client_routes(
zone.clone(),
"tcp://client-a".to_owned(),
vec![route("new-a", [10, 0, 0, 3])],
));
let routes = &store.snapshot().zones[&zone];
assert_eq!(routes.len(), 2);
assert!(routes.iter().any(|route| route.hostname == "new-a"));
assert!(routes.iter().any(|route| route.hostname == "peer-b"));
assert!(!routes.iter().any(|route| route.hostname == "old-a"));
}
#[test]
fn empty_update_removes_only_the_target_client_and_reports_empty_zone() {
let store = MagicDnsRecordStore::default();
let zone = zone::parse_origin("et.net.").unwrap();
store.replace_client_routes(
zone.clone(),
"tcp://client-a".to_owned(),
vec![route("peer-a", [10, 0, 0, 1])],
);
store.replace_client_routes(
zone.clone(),
"tcp://client-b".to_owned(),
vec![route("peer-b", [10, 0, 0, 2])],
);
assert!(!store.replace_client_routes(
zone.clone(),
"tcp://client-a".to_owned(),
Vec::new(),
));
assert!(store.replace_client_routes(zone, "tcp://client-b".to_owned(), Vec::new(),));
assert!(store.snapshot().zones.is_empty());
}
#[test]
fn disconnect_removes_client_from_all_zones() {
let store = MagicDnsRecordStore::default();
for zone in ["a.et.net.", "b.et.net."] {
store.replace_client_routes(
zone::parse_origin(zone).unwrap(),
"tcp://client-a".to_owned(),
vec![route("peer-a", [10, 0, 0, 1])],
);
}
store.replace_client_routes(
zone::parse_origin("b.et.net.").unwrap(),
"tcp://client-b".to_owned(),
vec![route("peer-b", [10, 0, 0, 2])],
);
assert_eq!(
store.remove_client("tcp://client-a"),
vec![zone::parse_origin("a.et.net.").unwrap()]
);
let snapshot = store.snapshot();
assert_eq!(snapshot.zones.len(), 1);
assert_eq!(
snapshot.zones[&zone::parse_origin("b.et.net.").unwrap()][0].hostname,
"peer-b"
);
}
#[tokio::test]
async fn custom_records_survive_route_refresh_and_support_common_types() {
let dns = config(&[(
"ET.NET",
&[
"nas IN A 10.0.0.8",
"nas IN AAAA fd00::8",
"alias IN CNAME nas",
"info IN TXT \"local record\"",
],
)]);
let data = data_with(Some(&dns)).await;
for ip in ["10.0.0.1/24", "10.0.0.2/24"] {
data.update_dns_record(controller(54001), request(ip))
.await
.unwrap();
assert_eq!(
a(&data, "nas.et.net.").await,
vec![Ipv4Addr::new(10, 0, 0, 8)]
);
assert!(query(&data, "alias.et.net.", RecordType::A).await.answers().iter()
.any(|record| matches!(record.data(), RData::CNAME(name) if name.0.to_string() == "nas.et.net.")));
assert_eq!(
a(&data, "node.et.net.").await,
vec![Ipv4Inet::from_str(ip).unwrap().address()]
);
}
assert!(matches!(
query(&data, "nas.et.net.", RecordType::AAAA)
.await
.answers()[0]
.data(),
RData::AAAA(_)
));
assert!(matches!(
query(&data, "info.et.net.", RecordType::TXT)
.await
.answers()[0]
.data(),
RData::TXT(_)
));
assert_eq!(
query(&data, "nas.et.net.", RecordType::A).await.answers()[0].ttl(),
60
);
// Disconnecting a client removes its node records, not the server's
// statically configured ones.
data.on_client_disconnected(controller(54001).get_tunnel_info().cloned())
.await;
assert!(
query(&data, "node.et.net.", RecordType::A)
.await
.answers()
.is_empty()
);
assert_eq!(
a(&data, "nas.et.net.").await,
vec![Ipv4Addr::new(10, 0, 0, 8)]
);
assert!(
data.dns_server
.read_catalog()
.await
.contains(&"et.net.".parse().unwrap())
);
}
#[tokio::test]
async fn configured_zones_with_distinct_origins_are_served() {
let dns = config(&[("et.net.", &[]), ("example.test.", &["svc IN A 10.0.0.9"])]);
let data = data_with(Some(&dns)).await;
assert_eq!(
a(&data, "svc.example.test.").await,
vec![Ipv4Addr::new(10, 0, 0, 9)]
);
// Refreshing routes on the default zone must not drop static zones.
data.update_dns_record(controller(54001), request("10.0.0.1/24"))
.await
.unwrap();
assert_eq!(
a(&data, "svc.example.test.").await,
vec![Ipv4Addr::new(10, 0, 0, 9)]
);
assert_eq!(
a(&data, "node.et.net.").await,
vec![Ipv4Addr::new(10, 0, 0, 1)]
);
}
#[tokio::test]
async fn uppercase_zone_and_route_updates_preserve_node_records() {
let data = data_with_default_zone("ET.NET.", None).await;
for zone in ["ET.NET.", "et.net"] {
let req = UpdateDnsRecordRequest {
zone: zone.to_owned(),
routes: vec![Route {
hostname: "node".to_owned(),
ipv4_addr: Some(Ipv4Inet::from_str("10.0.0.1/24").unwrap().into()),
..Default::default()
}],
};
data.update_dns_record(controller(54001), req)
.await
.unwrap();
assert_eq!(
a(&data, "node.et.net.").await,
vec![Ipv4Addr::new(10, 0, 0, 1)]
);
}
let dns = config(&[("et.net", &["nas IN A 10.0.0.8"])]);
let data = data_with_default_zone("ET.NET.", Some(&dns)).await;
let req = UpdateDnsRecordRequest {
zone: "ET.NET".to_owned(),
routes: vec![Route {
hostname: "node".to_owned(),
ipv4_addr: Some(Ipv4Inet::from_str("10.0.0.1/24").unwrap().into()),
..Default::default()
}],
};
data.update_dns_record(controller(54001), req)
.await
.unwrap();
assert_eq!(
a(&data, "node.et.net.").await,
vec![Ipv4Addr::new(10, 0, 0, 1)]
);
assert_eq!(
a(&data, "nas.et.net.").await,
vec![Ipv4Addr::new(10, 0, 0, 8)]
);
}
#[tokio::test]
async fn static_zones_are_loaded_during_init_before_any_rpc() {
let dns = config(&[("example.test.", &["svc IN A 10.0.0.9"])]);
let data = data_with(Some(&dns)).await;
assert_eq!(
a(&data, "svc.example.test.").await,
vec![Ipv4Addr::new(10, 0, 0, 9)]
);
}
#[tokio::test]
async fn system_config_keeps_default_zone_first() {
let dns = config(&[("aaa.test.", &["@ IN A 10.0.0.10"]), ("et.net.", &[])]);
let mut data = data_with(Some(&dns)).await;
let calls = Arc::new(Mutex::new(Vec::new()));
data.system_config = Some(Box::new(SystemConfigSpy(calls.clone())));
data.do_system_config().unwrap();
assert_eq!(
*calls.lock().unwrap(),
vec![(
vec!["et.net.".to_owned()],
vec!["et.net.".to_owned(), "aaa.test.".to_owned()]
)]
);
}
#[tokio::test]
async fn invalid_local_configuration_is_rejected_before_startup() {
for dns in [
config(&[("", &[])]),
config(&[("example.test.", &["nas IN A invalid"])]),
config(&[("example.test.", &["outside.invalid. IN A 10.0.0.8"])]),
config(&[("example.test.", &["$INCLUDE /etc/hosts"])]),
] {
assert!(zone::configured_zones(Some(&dns)).is_err());
}
assert!(zone::configured_zones(None).unwrap().is_empty());
}
}
+42 -5
View File
@@ -5,6 +5,7 @@ use std::time::Duration;
use cidr::Ipv4Inet;
use easytier_core::{
config::toml::{DnsConfig, DnsZoneConfig},
gateway::magic_dns::MagicDnsRoute,
host::packet::{HostPacket, HostPacketChannelSink, HostPacketReceiver},
process_runtime::CoreProcessRuntime,
@@ -58,13 +59,13 @@ pub async fn prepare_env_with_tld_dns_zone(
ctx.set_hostname(dns_name.to_owned());
ctx.set_ipv4(Some(tun_ip));
if tld_dns_zone.is_some() {
if let Some(zone) = tld_dns_zone {
let mut flags = ctx.config.get_flags();
flags.accept_dns = true; // Enable DNS
if let Some(zone) = tld_dns_zone {
flags.tld_dns_zone = zone.to_string();
}
ctx.set_flags(flags);
let mut dns = ctx.config.get_dns().unwrap_or_default();
dns.domain = Some(zone.to_string());
ctx.config.set_dns(Some(dns));
}
let (core_instance, host_packet_rx) = build_test_core(ctx.clone()).await;
@@ -119,6 +120,18 @@ async fn test_magic_dns_server_instance() {
let (global_ctx, core_instance, virtual_nic) = prepare_env("test1", tun_ip).await;
let tun_name = virtual_nic.ifname().await.unwrap();
let fake_ip = Ipv4Addr::from_str("100.100.100.101").unwrap();
let mut dns = global_ctx.config.get_dns().unwrap_or_default();
dns.zone = Some(
vec![DnsZoneConfig {
origin: "example.test.".to_owned(),
ttl: Some(60),
records: Some(vec!["svc IN A 10.0.0.9".to_owned()].into()),
}]
.into(),
);
global_ctx.config.set_dns(Some(dns));
let dns_server_inst = MagicDnsServerInstance::new(
core_instance.packet_plane(),
global_ctx,
@@ -129,6 +142,8 @@ async fn test_magic_dns_server_instance() {
.await
.unwrap();
check_dns_record(&fake_ip, "svc.example.test", "10.0.0.9").await;
let routes = [
MagicDnsRoute {
hostname: "test1".to_string(),
@@ -145,7 +160,10 @@ async fn test_magic_dns_server_instance() {
];
dns_server_inst
.data
.update_dns_records(routes.iter(), DEFAULT_ET_DNS_ZONE)
.update_dns_records(
routes.iter(),
&super::zone::parse_origin(DEFAULT_ET_DNS_ZONE).unwrap(),
)
.await
.unwrap();
@@ -224,6 +242,17 @@ async fn test_magic_dns_update_replaces_records_for_same_client() {
let ctx = get_mock_global_ctx();
ctx.set_hostname("test1".to_string());
ctx.set_ipv4(Some(tun_ip));
ctx.config.set_dns(Some(DnsConfig {
domain: None,
zone: Some(
vec![DnsZoneConfig {
origin: "example.test.".to_owned(),
ttl: Some(60),
records: Some(vec!["svc IN A 10.0.0.9".to_owned()].into()),
}]
.into(),
),
}));
let (core_instance, _packet_receiver) = build_test_core(ctx.clone()).await;
@@ -232,6 +261,14 @@ async fn test_magic_dns_update_replaces_records_for_same_client() {
MagicDnsServerInstance::new(core_instance.packet_plane(), ctx, None, tun_ip, fake_ip)
.await
.unwrap();
assert!(
dns_server_inst
.data
.dns_server
.read_catalog()
.await
.contains(&"example.test.".parse().unwrap())
);
let mut ctrl = BaseController::default();
ctrl.set_tunnel_info(Some(crate::proto::common::TunnelInfo {
+285
View File
@@ -0,0 +1,285 @@
use crate::proto::utils::RepeatedMessageModel;
use anyhow::{Context, ensure};
use easytier_core::config::toml::{ConfigLoader, DEFAULT_ET_DNS_ZONE, DnsConfig, DnsZoneConfig};
use hickory_proto::rr::{DNSClass, LowerName, Name, Record, RecordSet, RrKey};
use hickory_proto::serialize::txt::Parser;
use hickory_server::store::in_memory::InMemoryAuthority;
use std::collections::BTreeMap;
pub(crate) fn parse_origin(origin: &str) -> anyhow::Result<LowerName> {
ensure!(
!origin.trim().is_empty(),
"DNS zone origin must not be empty"
);
let mut name: Name = origin.parse().context("invalid DNS zone origin")?;
ensure!(!name.is_root(), "DNS zone origin must not be root");
name.set_fqdn(true);
Ok(LowerName::new(&name))
}
/// Resolves the default Magic DNS zone origin from `dns.domain`, falling back
/// to the deprecated `flags.tld_dns_zone` patch when `dns.domain` is unset, and
/// finally to [`DEFAULT_ET_DNS_ZONE`].
#[allow(deprecated)]
pub(crate) fn dns_domain(config: &impl ConfigLoader) -> anyhow::Result<LowerName> {
let dns = config.get_dns();
if let Some(domain) = dns.as_ref().and_then(|dns| dns.domain.as_deref()) {
return parse_origin(domain).context("invalid dns.domain");
}
if let Some(legacy) = config.get_flags_patch().tld_dns_zone.as_deref()
&& !legacy.is_empty()
{
return parse_origin(legacy).context("invalid flags.tld_dns_zone");
}
parse_origin(DEFAULT_ET_DNS_ZONE)
}
#[derive(Debug, Clone, Default)]
pub(crate) struct Zone {
origin: LowerName,
records: BTreeMap<RrKey, RecordSet>,
}
impl Zone {
#[cfg(test)]
pub fn origin(&self) -> &LowerName {
&self.origin
}
pub fn iter_records(&self) -> impl Iterator<Item = &Record> {
self.records
.values()
.flat_map(RecordSet::records_without_rrsigs)
}
}
impl TryFrom<&DnsZoneConfig> for Zone {
type Error = anyhow::Error;
fn try_from(value: &DnsZoneConfig) -> Result<Self, Self::Error> {
let origin = parse_origin(&value.origin)?;
let ttl = value.ttl.unwrap_or_default();
let lines = value
.records
.as_ref()
.map(|list| list.items.as_slice())
.unwrap_or_default();
ensure!(
!lines
.iter()
.flat_map(|record| record.lines())
.any(|line| line.trim_start().starts_with('$')),
"zone file directives are not supported in DNS records"
);
let content = format!(
"$ORIGIN {origin}\n$TTL {ttl}\n{}\n",
// Hickory 0.25 carries an explicit record TTL into later records.
lines.join(&format!("\n$TTL {ttl}\n"))
);
let name = Name::from(&origin);
let (_, records) = Parser::new(content, None, Some(name.clone()))
.parse()
.with_context(|| format!("invalid DNS records for zone {origin}"))?;
for record in records.values().flat_map(RecordSet::records_without_rrsigs) {
ensure!(
name.zone_of(record.name()),
"DNS record {} is outside zone {origin}",
record.name()
);
ensure!(
record.dns_class() == DNSClass::IN,
"DNS records must use class IN"
);
}
Ok(Self { origin, records })
}
}
impl From<Zone> for DnsZoneConfig {
fn from(value: Zone) -> Self {
let records = value
.iter_records()
.map(ToString::to_string)
.collect::<Vec<_>>();
Self {
origin: value.origin.to_string(),
ttl: None,
records: Some(records.into()),
}
}
}
pub(crate) type ZoneGroup = RepeatedMessageModel<Zone>;
impl ZoneGroup {
pub fn into_groups(self) -> BTreeMap<LowerName, ZoneGroup> {
self.into_iter().fold(BTreeMap::new(), |mut map, zone| {
map.entry(zone.origin.clone()).or_default().push(zone);
map
})
}
/// Merges records of every zone into one authority, since an in-memory
/// authority answers authoritatively and never falls through to another.
pub fn extend_authority(&self, authority: &mut InMemoryAuthority) {
for record in self.iter().flat_map(Zone::iter_records) {
authority.upsert_mut(record.clone(), 0);
}
}
}
/// Parses the local static records once, grouped by origin. Called before the
/// server election so an invalid local configuration cannot win the listener.
pub(crate) fn configured_zones(
dns: Option<&DnsConfig>,
) -> anyhow::Result<BTreeMap<LowerName, ZoneGroup>> {
let items = dns
.and_then(|dns| dns.zone.as_ref())
.map(|zones| zones.items.as_slice())
.unwrap_or_default();
Ok(ZoneGroup::try_from(items)?.into_groups())
}
#[cfg(test)]
mod tests {
use super::*;
use easytier_core::config::toml::{DnsConfig, TomlConfigLoader};
use hickory_proto::rr::RecordType;
fn zones(config: &DnsConfig) -> anyhow::Result<ZoneGroup> {
let items = config
.zone
.as_ref()
.map(|zones| zones.items.as_slice())
.unwrap_or_default();
ZoneGroup::try_from(items)
}
fn config(records: &[&str]) -> DnsConfig {
DnsConfig {
domain: None,
zone: Some(
vec![DnsZoneConfig {
origin: "EXAMPLE.test".to_owned(),
ttl: Some(60),
records: Some(
records
.iter()
.map(|record| (*record).to_owned())
.collect::<Vec<_>>()
.into(),
),
}]
.into(),
),
}
}
#[test]
fn custom_records_parse_common_types_and_ttls() {
let config = config(&[
"nas IN A 10.0.0.8",
"nas IN AAAA fd00::8",
"alias 120 IN CNAME nas",
"@ IN TXT \"local records\"",
]);
let parsed = zones(&config).unwrap();
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].origin().to_string(), "example.test.");
let records = parsed[0].iter_records().collect::<Vec<_>>();
assert_eq!(records.len(), 4);
for record in records {
assert!(
record.name().to_string().ends_with(".example.test.")
|| record.name().to_string() == "example.test."
);
assert_eq!(
record.ttl(),
if record.record_type() == RecordType::CNAME {
120
} else {
60
}
);
}
let mut config = config;
config.zone.as_mut().unwrap().items[0].ttl = Some(0);
let parsed = zones(&config).unwrap();
assert_eq!(parsed[0].iter_records().next().unwrap().ttl(), 0);
}
#[test]
fn custom_records_reject_invalid_records_and_directives() {
for record in [
"nas IN A not-an-address",
"outside.invalid. IN A 10.0.0.8",
"$INCLUDE /etc/hosts",
"nas IN A 10.0.0.8\n$ORIGIN other.test.",
] {
assert!(zones(&config(&[record])).is_err(), "accepted {record}");
}
for origin in ["", "."] {
let mut config = config(&[]);
config.zone.as_mut().unwrap().items[0].origin = origin.to_owned();
assert!(zones(&config).is_err(), "accepted origin {origin}");
}
assert!(
zones(&DnsConfig {
domain: None,
zone: None
})
.unwrap()
.is_empty()
);
}
#[test]
fn zone_group_into_groups_merges_same_origin() {
let mut config = config(&["nas IN A 10.0.0.8"]);
let mut second = config.zone.as_ref().unwrap().items[0].clone();
second.origin = "example.test.".to_owned();
config.zone.as_mut().unwrap().items.push(second);
let groups = zones(&config).unwrap().into_groups();
assert_eq!(groups.len(), 1);
assert_eq!(
groups[&"example.test.".parse::<LowerName>().unwrap()].len(),
2
);
}
#[test]
fn dns_domain_prefers_dns_domain_and_falls_back_to_legacy_flag() {
let loader = TomlConfigLoader::new_from_str("").unwrap();
assert_eq!(dns_domain(&loader).unwrap().to_string(), "et.net.");
let loader = TomlConfigLoader::new_from_str("[dns]\ndomain = 'MESH.Example'\n").unwrap();
assert_eq!(dns_domain(&loader).unwrap().to_string(), "mesh.example.");
let loader =
TomlConfigLoader::new_from_str("[flags]\ntld_dns_zone = 'LEGACY.Example'\n").unwrap();
assert_eq!(dns_domain(&loader).unwrap().to_string(), "legacy.example.");
let loader = TomlConfigLoader::new_from_str(
"[dns]\ndomain = 'mesh.example'\n[flags]\ntld_dns_zone = 'legacy.example'\n",
)
.unwrap();
assert_eq!(dns_domain(&loader).unwrap().to_string(), "mesh.example.");
let loader = TomlConfigLoader::new_from_str(
"[dns]\ndomain = ''\n[flags]\ntld_dns_zone = 'legacy.example'\n",
)
.unwrap();
assert!(dns_domain(&loader).is_err());
let loader = TomlConfigLoader::new_from_str(
"[dns]\ndomain = '.'\n[flags]\ntld_dns_zone = 'legacy.example'\n",
)
.unwrap();
assert!(dns_domain(&loader).is_err());
let loader = TomlConfigLoader::new_from_str("[flags]\ntld_dns_zone = '.'\n").unwrap();
assert!(dns_domain(&loader).is_err());
}
}
+1
View File
@@ -12,3 +12,4 @@ pub use easytier_proto::magic_dns;
pub mod tests;
pub mod rpc;
pub mod utils;
+63
View File
@@ -0,0 +1,63 @@
use derive_more::{AsMut, AsRef, Deref, DerefMut, From, IntoIterator};
pub trait MessageModel<Message: prost::Message>:
Into<Message> + for<'m> TryFrom<&'m Message>
{
}
impl<Message, Model> MessageModel<Message> for Model
where
Message: prost::Message,
Model: Into<Message> + for<'m> TryFrom<&'m Message>,
{
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, From, Deref, DerefMut, AsRef, AsMut, IntoIterator)]
#[as_ref(forward)]
#[as_mut(forward)]
#[into_iterator(owned, ref, ref_mut)]
pub struct RepeatedMessageModel<Model> {
pub models: Vec<Model>,
}
impl<Model> Default for RepeatedMessageModel<Model> {
fn default() -> Self {
Self { models: Vec::new() }
}
}
impl<Model> FromIterator<Model> for RepeatedMessageModel<Model> {
fn from_iter<I: IntoIterator<Item = Model>>(iter: I) -> Self {
Self {
models: iter.into_iter().collect(),
}
}
}
impl<Model> Extend<Model> for RepeatedMessageModel<Model> {
fn extend<T: IntoIterator<Item = Model>>(&mut self, iter: T) {
self.models.extend(iter);
}
}
impl<'m, Message, Model> TryFrom<&'m [Message]> for RepeatedMessageModel<Model>
where
Message: prost::Message,
Model: MessageModel<Message>,
{
type Error = <Model as TryFrom<&'m Message>>::Error;
fn try_from(value: &'m [Message]) -> Result<Self, Self::Error> {
value.iter().map(TryInto::try_into).collect()
}
}
impl<Message, Model> From<RepeatedMessageModel<Model>> for Vec<Message>
where
Message: prost::Message,
Model: MessageModel<Message>,
{
fn from(value: RepeatedMessageModel<Model>) -> Self {
value.into_iter().map(Into::into).collect()
}
}