mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-07-27 12:42:49 +02:00
Create and verify locators via CLI.
This commit is contained in:
parent
a0a79fa1b7
commit
68fe57decd
7 changed files with 103 additions and 72 deletions
|
@ -115,6 +115,7 @@ bool Endpoint::fromString(const char *s) noexcept
|
||||||
}
|
}
|
||||||
} else if (strchr(s, '/') != nullptr) {
|
} else if (strchr(s, '/') != nullptr) {
|
||||||
// IP/port is parsed as an IP_UDP endpoint for backward compatibility.
|
// IP/port is parsed as an IP_UDP endpoint for backward compatibility.
|
||||||
|
this->type = ZT_ENDPOINT_TYPE_IP_UDP;
|
||||||
return asInetAddress(this->value.ss).fromString(s);
|
return asInetAddress(this->value.ss).fromString(s);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -120,20 +120,12 @@ impl PartialEq for Endpoint {
|
||||||
impl Eq for Endpoint {}
|
impl Eq for Endpoint {}
|
||||||
|
|
||||||
impl serde::Serialize for Endpoint {
|
impl serde::Serialize for Endpoint {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
||||||
serializer.serialize_str(self.to_string().as_str())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EndpointVisitor;
|
struct EndpointVisitor;
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for EndpointVisitor {
|
impl<'de> serde::de::Visitor<'de> for EndpointVisitor {
|
||||||
type Value = Endpoint;
|
type Value = Endpoint;
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Endpoint value in string form") }
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("Endpoint value in string form")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||||
let id = Endpoint::new_from_string(s);
|
let id = Endpoint::new_from_string(s);
|
||||||
if id.is_err() {
|
if id.is_err() {
|
||||||
|
@ -142,9 +134,6 @@ impl<'de> serde::de::Visitor<'de> for EndpointVisitor {
|
||||||
return Ok(id.ok().unwrap() as Self::Value);
|
return Ok(id.ok().unwrap() as Self::Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for Endpoint {
|
impl<'de> serde::Deserialize<'de> for Endpoint {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(EndpointVisitor) }
|
||||||
deserializer.deserialize_str(EndpointVisitor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::{c_char, c_int, c_uint};
|
use std::os::raw::{c_char, c_int, c_uint};
|
||||||
|
use std::ptr::null;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use crate::capi as ztcore;
|
use crate::capi as ztcore;
|
||||||
use std::ptr::null;
|
|
||||||
|
|
||||||
pub struct Locator {
|
pub struct Locator {
|
||||||
pub(crate) capi: *const ztcore::ZT_Locator,
|
pub(crate) capi: *const ztcore::ZT_Locator,
|
||||||
|
@ -84,6 +84,20 @@ impl Locator {
|
||||||
}
|
}
|
||||||
eps
|
eps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn verify(&self, id: &Identity) -> bool {
|
||||||
|
unsafe { ztcore::ZT_Locator_verify(self.capi, id.capi) != 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signer(&self) -> Fingerprint {
|
||||||
|
unsafe {
|
||||||
|
let fp = ztcore::ZT_Locator_fingerprint(self.capi);
|
||||||
|
if fp.is_null() {
|
||||||
|
panic!("ZT_Locator_fingerprint() returned null, should not be allowed");
|
||||||
|
}
|
||||||
|
Fingerprint::new_from_capi(&*fp)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Locator {
|
impl Drop for Locator {
|
||||||
|
@ -125,20 +139,12 @@ impl PartialEq for Locator {
|
||||||
impl Eq for Locator {}
|
impl Eq for Locator {}
|
||||||
|
|
||||||
impl serde::Serialize for Locator {
|
impl serde::Serialize for Locator {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
||||||
serializer.serialize_str(self.to_string().as_str())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LocatorVisitor;
|
struct LocatorVisitor;
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for LocatorVisitor {
|
impl<'de> serde::de::Visitor<'de> for LocatorVisitor {
|
||||||
type Value = Locator;
|
type Value = Locator;
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Locator value in string form") }
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("Locator value in string form")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||||
let id = Locator::new_from_string(s);
|
let id = Locator::new_from_string(s);
|
||||||
if id.is_err() {
|
if id.is_err() {
|
||||||
|
@ -147,9 +153,6 @@ impl<'de> serde::de::Visitor<'de> for LocatorVisitor {
|
||||||
return Ok(id.ok().unwrap() as Self::Value);
|
return Ok(id.ok().unwrap() as Self::Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for Locator {
|
impl<'de> serde::Deserialize<'de> for Locator {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(LocatorVisitor) }
|
||||||
deserializer.deserialize_str(LocatorVisitor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ Common Operations:
|
||||||
Advanced Operations:
|
Advanced Operations:
|
||||||
|
|
||||||
service Start this node
|
service Start this node
|
||||||
(usually not run directly)
|
(usually not invoked directly)
|
||||||
|
|
||||||
controller <command> [option]
|
controller <command> [option]
|
||||||
· list List networks on controller
|
· list List networks on controller
|
||||||
|
@ -110,16 +110,8 @@ Advanced Operations:
|
||||||
|
|
||||||
An <address> may be specified as a 10-digit short ZeroTier address, a
|
An <address> may be specified as a 10-digit short ZeroTier address, a
|
||||||
fingerprint containing both an address and a SHA384 hash, or an identity.
|
fingerprint containing both an address and a SHA384 hash, or an identity.
|
||||||
The latter two options are equivalent in terms of specificity and may be
|
Identities and locators can be specified as either paths to files on the
|
||||||
used if stronger security guarantees are desired than those provided by
|
filesystem or verbatim objects in string format. This is auto-detected."###, ver.0, ver.1, ver.2)
|
||||||
the basic ZeroTier addressing system. Fields of type <identity> must be
|
|
||||||
full identities and may be specified either verbatim or as a path to a file.
|
|
||||||
|
|
||||||
An <endpoint> is a place where a peer may be reached. Currently these are
|
|
||||||
just 'IP/port' format addresses but other types may be added in the future.
|
|
||||||
|
|
||||||
The 'service' command starts a node. It will run until the node receives
|
|
||||||
an exit signal and is normally not used directly."###, ver.0, ver.1, ver.2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_help() {
|
pub(crate) fn print_help() {
|
||||||
|
|
|
@ -19,8 +19,6 @@ use dialoguer::Input;
|
||||||
use zerotier_core::*;
|
use zerotier_core::*;
|
||||||
|
|
||||||
use crate::store::Store;
|
use crate::store::Store;
|
||||||
use crate::utils::read_identity;
|
|
||||||
use futures::SinkExt;
|
|
||||||
|
|
||||||
fn list(store: &Store, auth_token: &Option<String>) -> i32 {
|
fn list(store: &Store, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
|
@ -98,7 +96,7 @@ fn newcsr<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<Stri
|
||||||
if identity.is_empty() {
|
if identity.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let identity = read_identity(identity.as_str(), true);
|
let identity = crate::utils::read_identity(identity.as_str(), true);
|
||||||
if identity.is_err() {
|
if identity.is_err() {
|
||||||
println!("ERROR: identity invalid or unable to read from file.");
|
println!("ERROR: identity invalid or unable to read from file.");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -110,19 +108,24 @@ fn newcsr<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<Stri
|
||||||
}
|
}
|
||||||
|
|
||||||
let locator: String = Input::with_theme(theme)
|
let locator: String = Input::with_theme(theme)
|
||||||
.with_prompt(format!(" [{}] Locator for {} (optional)", identities.len() + 1, identity.address.to_string()))
|
.with_prompt(format!(" [{}] Locator or path to locator for {} (optional)", identities.len() + 1, identity.address.to_string()))
|
||||||
.allow_empty(true)
|
.allow_empty(true)
|
||||||
.interact_text()
|
.interact_text()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let locator = if locator.is_empty() {
|
let locator = if locator.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let l = Locator::new_from_string(locator.as_str());
|
let l = crate::utils::read_locator(locator.as_str());
|
||||||
if l.is_err() {
|
if l.is_err() {
|
||||||
println!("ERROR: locator invalid: {}", l.err().unwrap().to_str());
|
println!("ERROR: locator invalid: {}", l.err().unwrap());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
l.ok()
|
let l = l.ok();
|
||||||
|
if !l.as_ref().unwrap().verify(&identity) {
|
||||||
|
println!("ERROR: locator not signed by this identity.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
l
|
||||||
};
|
};
|
||||||
|
|
||||||
identities.push(CertificateIdentity {
|
identities.push(CertificateIdentity {
|
||||||
|
|
|
@ -70,10 +70,38 @@ fn new_<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 {
|
fn verify<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 {
|
||||||
0
|
let identity = crate::utils::read_identity(cli_args.value_of("identity").unwrap(), true);
|
||||||
|
if identity.is_err() {
|
||||||
|
println!("ERROR: identity invalid: {}", identity.err().unwrap());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let identity = identity.unwrap();
|
||||||
|
let locator = crate::utils::read_locator(cli_args.value_of("locator").unwrap());
|
||||||
|
if locator.is_err() {
|
||||||
|
println!("ERROR: locator invalid: {}", locator.err().unwrap());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if locator.unwrap().verify(&identity) {
|
||||||
|
println!("OK");
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
println!("FAILED");
|
||||||
|
1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 {
|
fn show<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 {
|
||||||
|
let locator = crate::utils::read_locator(cli_args.value_of("locator").unwrap());
|
||||||
|
if locator.is_err() {
|
||||||
|
println!("ERROR: locator invalid: {}", locator.err().unwrap());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let locator = locator.unwrap();
|
||||||
|
println!("{} timestamp {}", locator.signer().to_string(), (locator.timestamp() as f64) / 1000.0);
|
||||||
|
let endpoints = locator.endpoints();
|
||||||
|
for ep in endpoints.iter() {
|
||||||
|
println!(" {}", (*ep).to_string())
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::mem::MaybeUninit;
|
||||||
use std::os::raw::c_uint;
|
use std::os::raw::c_uint;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use zerotier_core::Identity;
|
use zerotier_core::{Identity, Locator};
|
||||||
|
|
||||||
use crate::osdep;
|
use crate::osdep;
|
||||||
|
|
||||||
|
@ -57,38 +57,53 @@ pub(crate) fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Resu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read an identity as either a literal or from a file.
|
/// Read an identity as either a literal or from a file.
|
||||||
/// This is used in parsing command lines, allowing either a literal or a path
|
|
||||||
/// to be specified and automagically disambiguating.
|
|
||||||
pub(crate) fn read_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
pub(crate) fn read_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
||||||
let id = Identity::new_from_string(input);
|
let parse_func = |s: &str| {
|
||||||
if id.is_err() {
|
Identity::new_from_string(s).map_or_else(|e| {
|
||||||
let input = Path::new(input);
|
Err(format!("invalid identity: {}", e.to_str()))
|
||||||
if !input.exists() || !input.is_file() {
|
}, |id| {
|
||||||
return Err(format!("invalid identity: {}", id.err().unwrap().to_str()));
|
if !validate || id.validate() {
|
||||||
}
|
Ok(id)
|
||||||
|
} else {
|
||||||
|
Err(String::from("invalid identity: local validation failed"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if Path::new(input).exists() {
|
||||||
read_limit(input, 16384).map_or_else(|e| {
|
read_limit(input, 16384).map_or_else(|e| {
|
||||||
Err(e.to_string())
|
Err(e.to_string())
|
||||||
}, |v| {
|
}, |v| {
|
||||||
String::from_utf8(v).map_or_else(|e| {
|
String::from_utf8(v).map_or_else(|e| {
|
||||||
Err(e.to_string())
|
Err(e.to_string())
|
||||||
}, |s| {
|
}, |s| {
|
||||||
Identity::new_from_string(s.as_str()).map_or_else(|_| {
|
parse_func(s.as_str())
|
||||||
Err(format!("Invalid identity in file {}", input.to_str().unwrap_or("")))
|
|
||||||
}, |id| {
|
|
||||||
if validate && !id.validate() {
|
|
||||||
Err(String::from("invalid identity: local validation failed"))
|
|
||||||
} else {
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let id = id.ok().unwrap();
|
parse_func(input)
|
||||||
if validate && !id.validate() {
|
}
|
||||||
Err(String::from("invalid identity: local validation failed"))
|
}
|
||||||
} else {
|
|
||||||
Ok(id)
|
/// Read a locator as either a literal or from a file.
|
||||||
}
|
pub(crate) fn read_locator(input: &str) -> Result<Locator, String> {
|
||||||
|
let parse_func = |s: &str| {
|
||||||
|
Locator::new_from_string(s).map_or_else(|e| {
|
||||||
|
Err(format!("invalid locator: {}", e.to_str()))
|
||||||
|
}, |loc| {
|
||||||
|
Ok(loc)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if Path::new(input).exists() {
|
||||||
|
read_limit(input, 16384).map_or_else(|e| {
|
||||||
|
Err(e.to_string())
|
||||||
|
}, |v| {
|
||||||
|
String::from_utf8(v).map_or_else(|e| {
|
||||||
|
Err(e.to_string())
|
||||||
|
}, |s| {
|
||||||
|
parse_func(s.as_str())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
parse_func(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue