Skip to content

Commit 52396f6

Browse files
committed
Test implementation of ZAuth + CURVE authentication
1 parent 22b02bd commit 52396f6

File tree

8 files changed

+198
-10
lines changed

8 files changed

+198
-10
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Cargo.lock
2+
target/

Cargo.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ default = ["remote-run"]
1818
local-run = []
1919

2020
# Run API endpoints against a remote agent
21-
remote-run = ["zmq", "zmq-sys"]
21+
remote-run = ["czmq", "zmq"]
2222

2323
[build-dependencies]
2424

2525
regex = "0.1.*"
2626

27+
[dev-dependencies]
28+
29+
tempdir = "0.3"
30+
2731
[dependencies]
2832

2933
lazy_static = "0.1.*"
@@ -32,8 +36,8 @@ rustc-serialize = "0.3.*"
3236
regex = "0.1.*"
3337

3438
# Feature `remote-run`
35-
zmq = { git = "https://github.com/petehayes102/rust-zmq.git", branch = "feature/socket-raw", optional = true }
36-
zmq-sys = { version = "*", optional = true }
39+
czmq = { git = "https://github.com/petehayes102/rust-czmq.git", branch = "dev", optional = true }
40+
zmq = { git = "https://github.com/petehayes102/rust-zmq.git", branch = "tmp-master", optional = true }
3741

3842
[lib]
3943

src/error.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
77
// modified, or distributed except according to those terms.
88

9+
use czmq;
910
use rustc_serialize::json;
1011
use std::{convert, error, fmt, io, num, str, string};
1112
#[cfg(feature = "remote-run")]
@@ -15,6 +16,8 @@ use zmq;
1516
pub enum Error {
1617
/// An error string returned from the host's Intecture Agent
1718
Agent(String),
19+
/// CZMQ error
20+
Czmq(czmq::Error),
1821
/// JSON decoder error
1922
JsonDecoder(json::DecoderError),
2023
/// Message frames missing in the response from host's Intecture Agent
@@ -41,6 +44,7 @@ impl fmt::Display for Error {
4144
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4245
match *self {
4346
Error::Agent(ref e) => write!(f, "Agent error: {}", e),
47+
Error::Czmq(ref e) => write!(f, "CZMQ error: {}", e),
4448
Error::JsonDecoder(ref e) => write!(f, "JSON decoder error: {}", e),
4549
#[cfg(feature = "remote-run")]
4650
Error::Frame(ref e) => write!(f, "Missing frame {} in message: {}", e.order, e.name),
@@ -60,6 +64,7 @@ impl error::Error for Error {
6064
fn description(&self) -> &str {
6165
match *self {
6266
Error::Agent(ref e) => e,
67+
Error::Czmq(ref e) => e.description(),
6368
Error::JsonDecoder(ref e) => e.description(),
6469
#[cfg(feature = "remote-run")]
6570
Error::Frame(_) => "The Agent's reply was missing a part ('frame') of the expected message",
@@ -75,6 +80,12 @@ impl error::Error for Error {
7580
}
7681
}
7782

83+
impl convert::From<czmq::Error> for Error {
84+
fn from(err: czmq::Error) -> Error {
85+
Error::Czmq(err)
86+
}
87+
}
88+
7889
impl convert::From<json::DecoderError> for Error {
7990
fn from(err: json::DecoderError) -> Error {
8091
Error::JsonDecoder(err)

src/host/ffi.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
//! FFI interface for Host
1010
1111
#[cfg(feature = "remote-run")]
12-
use libc::{c_char, c_void, uint32_t};
12+
use libc::{c_char, uint32_t};
1313
use std::convert;
1414
#[cfg(feature = "remote-run")]
1515
use std::{ptr, str};
1616
#[cfg(feature = "remote-run")]
1717
use std::ffi::{CStr, CString};
18+
#[cfg(feature = "remote-run")]
19+
use std::os::raw::c_void;
1820
use super::*;
1921
#[cfg(feature = "remote-run")]
2022
use zmq;
@@ -152,15 +154,15 @@ mod tests {
152154
#[test]
153155
fn test_convert_host_connected() {
154156
let mut host = Host::new();
155-
assert!(host.connect("127.0.0.1", 7101, 7102, 7103).is_ok());
157+
assert!(host.connect("localhost", 7101, 7102, 7103).is_ok());
156158
Ffi__Host::from(host);
157159
}
158160

159161
#[cfg(feature = "remote-run")]
160162
#[test]
161163
fn test_convert_ffi_host() {
162164
let mut ctx = zmq::Context::new();
163-
let mut sock = ctx.socket(zmq::REQ).unwrap();
165+
let sock = ctx.socket(zmq::REQ).unwrap();
164166

165167
let ffi_host = Ffi__Host {
166168
hostname: CString::new("localhost").unwrap().into_raw(),

src/host/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use zmq;
4646
pub struct Host;
4747
#[cfg(feature = "remote-run")]
4848
pub struct Host {
49-
// Hostname or IP of managed host
49+
/// Hostname or IP of managed host
5050
hostname: Option<String>,
5151
/// API socket
5252
api_sock: Option<zmq::Socket>,

src/host/remote.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
//! The host wrapper for communicating with a remote host.
1010
11+
use czmq::ZCert;
1112
use error::{Error, MissingFrame};
1213
use file::FileOpts;
1314
use Result;
15+
use runtime_helpers::RUNTIME_ARGS;
1416
use std::sync::Mutex;
1517
use std::thread::sleep;
1618
use std::time::Duration;
@@ -47,11 +49,17 @@ impl Host {
4749
pub fn connect(&mut self, hostname: &str, api_port: u32, upload_port: u32, download_port: u32) -> Result<()> {
4850
self.hostname = Some(hostname.to_string());
4951

52+
let server_cert = try!(ZCert::load(&format!("{}/{}.crt", RUNTIME_ARGS.server_cert_path, hostname)));
53+
5054
self.api_sock = Some(ZMQCTX.lock().unwrap().socket(zmq::REQ).unwrap());
55+
RUNTIME_ARGS.user_cert.apply(self.api_sock.as_mut().unwrap());
56+
try!(self.api_sock.as_mut().unwrap().set_curve_serverkey(server_cert.public_txt()));
5157
try!(self.api_sock.as_mut().unwrap().set_linger(5000));
5258
try!(self.api_sock.as_mut().unwrap().connect(&format!("tcp://{}:{}", hostname, api_port)));
5359

5460
self.upload_sock = Some(ZMQCTX.lock().unwrap().socket(zmq::PUB).unwrap());
61+
RUNTIME_ARGS.user_cert.apply(self.api_sock.as_mut().unwrap());
62+
try!(self.upload_sock.as_mut().unwrap().set_curve_serverkey(server_cert.public_txt()));
5563
try!(self.upload_sock.as_mut().unwrap().connect(&format!("tcp://{}:{}", hostname, upload_port)));
5664

5765
self.download_port = Some(download_port);
@@ -86,6 +94,9 @@ impl Host {
8694
#[doc(hidden)]
8795
pub fn send_file(&mut self, endpoint: &str, path: &str, hash: u64, size: u64, total_chunks: u64, options: Option<&[FileOpts]>) -> Result<zmq::Socket> {
8896
let mut download_sock = ZMQCTX.lock().unwrap().socket(zmq::SUB).unwrap();
97+
RUNTIME_ARGS.user_cert.apply(&mut download_sock);
98+
let server_cert = try!(ZCert::load(&format!("{}/{}.crt", RUNTIME_ARGS.server_cert_path, self.hostname.as_mut().unwrap())));
99+
try!(download_sock.set_curve_serverkey(server_cert.public_txt()));
89100
try!(download_sock.connect(&format!("tcp://{}:{}", self.hostname.as_mut().unwrap(), self.download_port.unwrap())));
90101
try!(download_sock.set_subscribe(path.as_bytes()));
91102

@@ -174,7 +185,7 @@ mod tests {
174185
#[test]
175186
fn test_host_connect() {
176187
let mut host = Host::new();
177-
assert!(host.connect("127.0.0.1", 7101, 7102, 7103).is_ok());
188+
assert!(host.connect("localhost", 7101, 7102, 7103).is_ok());
178189
}
179190

180191
#[test]

src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@
2424
//! Intecture API primitives, or the program will hang while it
2525
//! attempts to connect to a non-existent socket.
2626
27+
#[cfg(feature = "remote-run")]
28+
extern crate czmq;
2729
#[macro_use]
2830
extern crate lazy_static;
2931
extern crate libc;
3032
extern crate regex;
3133
extern crate rustc_serialize;
34+
#[cfg(test)]
35+
extern crate tempdir;
3236
#[cfg(feature = "remote-run")]
3337
extern crate zmq;
34-
#[cfg(all(test, feature = "remote-run"))]
35-
extern crate zmq_sys;
3638

3739
pub mod command;
3840
pub mod directory;
@@ -41,6 +43,8 @@ mod ffi_helpers;
4143
pub mod file;
4244
pub mod host;
4345
pub mod package;
46+
#[cfg(feature = "remote-run")]
47+
mod runtime_helpers;
4448
pub mod service;
4549
mod target;
4650
pub mod telemetry;
@@ -52,6 +56,8 @@ pub use file::{File, FileOpts, FileOwner};
5256
pub use host::Host;
5357
pub use package::{Package, PackageResult};
5458
pub use package::providers::{Provider, ProviderFactory, Providers};
59+
#[cfg(feature = "remote-run")]
60+
pub use runtime_helpers::RuntimeArgs;
5561
pub use service::{Service, ServiceRunnable};
5662
pub use telemetry::{Cpu, FsMount, Netif, NetifStatus, NetifIPv4, NetifIPv6, Os, Telemetry};
5763

src/runtime_helpers.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
2+
// top-level directory of this distribution and at
3+
// https://intecture.io/COPYRIGHT.
4+
//
5+
// Licensed under the Mozilla Public License 2.0 <LICENSE or
6+
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
7+
// modified, or distributed except according to those terms.
8+
9+
use czmq::ZCert;
10+
use {Error, Result};
11+
#[cfg(not(test))]
12+
use std::env::{self, Args};
13+
use std::process::exit;
14+
#[cfg(test)]
15+
use tempdir::TempDir;
16+
17+
#[cfg(test)]
18+
lazy_static! {
19+
static ref TMP_DIR: TempDir = TempDir::new("test_runtime_args").unwrap();
20+
pub static ref RUNTIME_ARGS: RuntimeArgs = RuntimeArgs::new_test();
21+
}
22+
#[cfg(not(test))]
23+
lazy_static! {
24+
pub static ref RUNTIME_ARGS: RuntimeArgs = RuntimeArgs::new_usage(env::args());
25+
}
26+
27+
pub struct RuntimeArgs {
28+
pub user_cert: ZCert,
29+
pub server_cert_path: String,
30+
pub user_args: Vec<String>,
31+
}
32+
33+
unsafe impl Sync for RuntimeArgs {}
34+
35+
impl RuntimeArgs {
36+
#[cfg(test)]
37+
fn new_test() -> RuntimeArgs {
38+
let server_cert = ZCert::new().unwrap();
39+
server_cert.save(&format!("{}/localhost.crt", TMP_DIR.path().to_str().unwrap())).unwrap();
40+
41+
let user_cert = ZCert::new().unwrap();
42+
let user_path = format!("{}/user.crt", TMP_DIR.path().to_str().unwrap());
43+
user_cert.save(&user_path).unwrap();
44+
45+
RuntimeArgs::new(vec![
46+
"/fake/runnable".to_string(),
47+
user_path,
48+
TMP_DIR.path().to_str().unwrap().to_string(),
49+
]).unwrap()
50+
}
51+
52+
#[cfg(not(test))]
53+
fn new_usage(args: Args) -> RuntimeArgs {
54+
let args: Vec<String> = args.collect();
55+
let ra = Self::new(args);
56+
57+
if ra.is_err() {
58+
println!("You should not invoke this project manually!");
59+
println!("Usage: incli run [<script_args>]");
60+
exit(1);
61+
}
62+
63+
ra.unwrap()
64+
}
65+
66+
fn new(args: Vec<String>) -> Result<RuntimeArgs> {
67+
let mut args = args;
68+
69+
if args.len() < 3 {
70+
return Err(Error::Generic("Missing args".to_string()));
71+
}
72+
73+
// Remove filename
74+
args.remove(0);
75+
76+
Ok(RuntimeArgs {
77+
user_cert: try!(ZCert::load(&args.remove(0))),
78+
server_cert_path: args.remove(0),
79+
user_args: args,
80+
})
81+
}
82+
83+
pub fn expect_user_args<'a>(&'a mut self, required_args: &[&str]) -> &'a [String] {
84+
if self.user_args.len() != required_args.len() {
85+
// Generate usage string
86+
let mut usage = String::from("Usage: incli run");
87+
for arg in required_args {
88+
usage.push_str(" <");
89+
usage.push_str(arg);
90+
usage.push_str(">");
91+
}
92+
93+
println!("{}", usage);
94+
exit(1);
95+
}
96+
97+
&self.user_args
98+
}
99+
}
100+
101+
#[cfg(test)]
102+
mod tests {
103+
use czmq::ZCert;
104+
use super::*;
105+
use tempdir::TempDir;
106+
107+
#[test]
108+
fn test_new_ok() {
109+
let dir = TempDir::new("test_new_ok").unwrap();
110+
111+
let user_path = format!("{}/user.crt", dir.path().to_str().unwrap());
112+
let server_path = format!("{}/localhost.crt", dir.path().to_str().unwrap());
113+
114+
let user_cert = ZCert::new().unwrap();
115+
user_cert.save(&user_path).unwrap();
116+
117+
let runtime_args = RuntimeArgs::new(vec![
118+
"/path/to/project/runnable".to_string(),
119+
user_path,
120+
server_path
121+
]);
122+
assert!(runtime_args.is_ok());
123+
assert_eq!(runtime_args.unwrap().user_args.len(), 0);
124+
}
125+
126+
#[test]
127+
fn test_new_noargs() {
128+
let runtime_args = RuntimeArgs::new(vec!["/path/to/project/runnable".to_string()]);
129+
assert!(runtime_args.is_err());
130+
}
131+
132+
#[test]
133+
fn test_new_usrargs() {
134+
let dir = TempDir::new("test_new_usrargs").unwrap();
135+
136+
let user_path = format!("{}/user.crt", dir.path().to_str().unwrap());
137+
let server_path = format!("{}/localhost.crt", dir.path().to_str().unwrap());
138+
139+
let user_cert = ZCert::new().unwrap();
140+
user_cert.save(&user_path).unwrap();
141+
142+
let runtime_args = RuntimeArgs::new(vec![
143+
"/path/to/project/runnable".to_string(),
144+
user_path,
145+
server_path,
146+
"user1".to_string(),
147+
"user2".to_string(),
148+
]).unwrap();
149+
assert_eq!(runtime_args.user_args.get(0).unwrap(), "user1");
150+
assert_eq!(runtime_args.user_args.get(1).unwrap(), "user2");
151+
}
152+
}

0 commit comments

Comments
 (0)