Skip to content

Commit 9e95b6a

Browse files
authored
Merge pull request talaia-labs#48 from tee8z/tor-endpoint-http-api
Tor endpoint http api
2 parents e1dbff0 + d2999c2 commit 9e95b6a

File tree

7 files changed

+210
-2
lines changed

7 files changed

+210
-2
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,28 @@ For regtest, it should look like:
6767
btc_network = regtest
6868
```
6969

70+
### Running TEOS with tor
71+
72+
This requires a tor deamon running on the same machine as teos and a control port open on that deamon.
73+
74+
Download tor from the torproject site here: [torproject](https://www.torproject.org/download/)
75+
76+
To open tor's control port, follow the instructions here on how to update the configuration file [tor conf](https://2019.www.torproject.org/docs/faq.html.en#torrc)
77+
78+
Add the following lines to the file:
79+
```
80+
## The port on which Tor will listen for local connections from Tor
81+
## controller applications, as documented in control-spec.txt.
82+
ControlPort 9051
83+
84+
## If you enable the controlport, be sure to enable one of these
85+
## authentication methods, to prevent attackers from accessing it.
86+
CookieAuthentication 1
87+
CookieAuthFileGroupReadable 1
88+
```
89+
90+
Once the tor deamon is running, and the control port is open, make sure to change `tor_support` flag to true in teos conf.
91+
7092
### Tower id and signing key
7193

7294
`teos` needs a pair of keys that will serve as tower id and signing key. The former can be used by users to identify the tower, whereas the latter is used by the tower to sign responses. These keys are automatically generated on the first run, and can be refreshed by running `teos` with the `--overwritekey` flag. Notice that once a key is overwritten you won't be able to use the previous key again*.

teos/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ tonic = "0.6"
3030
tokio = { version = "1.5", features = [ "rt-multi-thread" ] }
3131
triggered = "0.1.2"
3232
warp = "0.3.2"
33+
torut = "0.2.1"
3334

3435
# Bitcoin and Lightning
3536
bitcoin = { version = "0.27", features = [ "base64" ] }

teos/src/api/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod http;
22
pub mod internal;
3+
pub mod tor;
34

45
pub mod serde_status {
56
use serde::de::{self, Deserializer};

teos/src/api/tor.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use std::io::{Error, ErrorKind};
2+
use std::net::SocketAddr;
3+
use tokio::net::TcpStream;
4+
use tokio::time::{sleep, Duration};
5+
use torut::control::UnauthenticatedConn;
6+
use torut::onion::TorSecretKeyV3;
7+
use triggered::Listener;
8+
9+
/// Expose an onion service that re-directs to the public api.
10+
pub async fn expose_onion_service(
11+
tor_control_port: u16,
12+
api_port: u16,
13+
onion_port: u16,
14+
shutdown_signal_tor: Listener,
15+
) -> Result<(), Error> {
16+
let stream = connect_tor_cp(format!("127.0.0.1:{}", tor_control_port).parse().unwrap())
17+
.await
18+
.map_err(|e| Error::new(ErrorKind::ConnectionRefused, e))?;
19+
20+
let mut unauth_conn = UnauthenticatedConn::new(stream);
21+
22+
let pre_auth = unauth_conn
23+
.load_protocol_info()
24+
.await
25+
.map_err(|e| Error::new(ErrorKind::ConnectionRefused, e))?;
26+
27+
let auth_data = pre_auth
28+
.make_auth_data()?
29+
.expect("failed to make auth data");
30+
31+
unauth_conn.authenticate(&auth_data).await.map_err(|_| {
32+
Error::new(
33+
ErrorKind::PermissionDenied,
34+
"failed to authenticate with Tor",
35+
)
36+
})?;
37+
38+
let mut auth_conn = unauth_conn.into_authenticated().await;
39+
40+
auth_conn.set_async_event_handler(Some(|_| async move { Ok(()) }));
41+
42+
let key = TorSecretKeyV3::generate();
43+
44+
auth_conn
45+
.add_onion_v3(
46+
&key,
47+
false,
48+
false,
49+
false,
50+
None,
51+
&mut [(
52+
onion_port,
53+
format!("127.0.0.1:{}", api_port).parse().unwrap(),
54+
)]
55+
.iter(),
56+
)
57+
.await
58+
.map_err(|e| {
59+
Error::new(
60+
ErrorKind::Other,
61+
format!("failed to create onion hidden service: {}", e),
62+
)
63+
})?;
64+
65+
print_onion_service(key.clone(), onion_port);
66+
67+
// NOTE: Needed to keep connection with control port & hidden service running, as soon as we leave
68+
// this function the control port stream is dropped and the hidden service is killed
69+
loop {
70+
sleep(Duration::from_secs(1)).await;
71+
if shutdown_signal_tor.is_triggered() {
72+
break;
73+
}
74+
}
75+
76+
auth_conn
77+
.del_onion(
78+
&key.public()
79+
.get_onion_address()
80+
.get_address_without_dot_onion(),
81+
)
82+
.await
83+
.unwrap();
84+
Ok(())
85+
}
86+
87+
async fn connect_tor_cp(addr: SocketAddr) -> Result<TcpStream, Error> {
88+
let sock = TcpStream::connect(addr).await.map_err(|_| {
89+
Error::new(
90+
ErrorKind::ConnectionRefused,
91+
"failed to connect to tor control port",
92+
)
93+
})?;
94+
Ok(sock)
95+
}
96+
97+
fn print_onion_service(key: TorSecretKeyV3, onion_port: u16) {
98+
let onion_addr = key.public().get_onion_address();
99+
let onion = format!("{}:{}", onion_addr, onion_port);
100+
log::info!("onion service: {}", onion);
101+
}
102+
103+
#[cfg(test)]
104+
mod tests {
105+
use super::*;
106+
107+
#[tokio::test]
108+
async fn test_connect_tor_cp_fail() {
109+
let tor_control_port = 9000;
110+
let addr = format!("127.0.0.1:{}", tor_control_port).parse().unwrap();
111+
match connect_tor_cp(addr).await {
112+
Ok(_) => {}
113+
Err(e) => {
114+
assert_eq!("failed to connect to tor control port", e.to_string())
115+
}
116+
}
117+
}
118+
}

teos/src/conf_template.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# API
22
api_bind = "127.0.0.1"
33
api_port = 9814
4+
tor_control_port = 9051
5+
onion_hidden_service_port = 2121
6+
tor_support = false
47

58
# RPC
69
rpc_bind = "127.0.0.1"

teos/src/config.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ pub struct Opt {
104104
/// Overwrites the tower secret key. THIS IS IRREVERSIBLE AND WILL CHANGE YOUR TOWER ID
105105
#[structopt(long)]
106106
pub overwrite_key: bool,
107+
108+
/// If set, creates a tor endpoint to serve API data. This endpoint is additional to the clearnet HTTP API
109+
#[structopt(long)]
110+
pub tor_support: bool,
111+
112+
/// tor control port [default: 9051]
113+
#[structopt(long)]
114+
pub tor_control_port: Option<u16>,
115+
116+
/// Port for the onion hidden service to listen on [default: 2121]
117+
#[structopt(long)]
118+
pub onion_hidden_service_port: Option<u16>,
107119
}
108120

109121
/// Holds all configuration options.
@@ -144,6 +156,11 @@ pub struct Config {
144156
// Internal API
145157
pub internal_api_bind: String,
146158
pub internal_api_port: u32,
159+
160+
// Tor
161+
pub tor_support: bool,
162+
pub tor_control_port: u16,
163+
pub onion_hidden_service_port: u16,
147164
}
148165

149166
impl Config {
@@ -176,7 +193,14 @@ impl Config {
176193
if options.btc_rpc_port.is_some() {
177194
self.btc_rpc_port = options.btc_rpc_port.unwrap();
178195
}
196+
if options.tor_control_port.is_some() {
197+
self.tor_control_port = options.tor_control_port.unwrap();
198+
}
199+
if options.onion_hidden_service_port.is_some() {
200+
self.onion_hidden_service_port = options.onion_hidden_service_port.unwrap();
201+
}
179202

203+
self.tor_support |= options.tor_support;
180204
self.debug |= options.debug;
181205
self.overwrite_key = options.overwrite_key;
182206
}
@@ -230,6 +254,9 @@ impl Default for Config {
230254
Self {
231255
api_bind: "127.0.0.1".into(),
232256
api_port: 9814,
257+
tor_support: false,
258+
tor_control_port: 9051,
259+
onion_hidden_service_port: 2121,
233260
rpc_bind: "127.0.0.1".into(),
234261
rpc_port: 8814,
235262
btc_network: "bitcoin".into(),
@@ -260,6 +287,9 @@ mod tests {
260287
Self {
261288
api_bind: None,
262289
api_port: None,
290+
tor_support: false,
291+
tor_control_port: None,
292+
onion_hidden_service_port: None,
263293
rpc_bind: None,
264294
rpc_port: None,
265295
btc_network: None,
@@ -324,4 +354,16 @@ mod tests {
324354
};
325355
assert!(matches!(config.verify(), Err(ConfigError { .. })));
326356
}
357+
358+
#[test]
359+
fn test_config_verify_tor_set() {
360+
let mut config = Config {
361+
btc_rpc_user: "user".to_owned(),
362+
btc_rpc_password: "password".to_owned(),
363+
tor_support: true,
364+
..Default::default()
365+
};
366+
367+
config.verify().unwrap()
368+
}
327369
}

teos/src/main.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use lightning_block_sync::poll::{
1717
};
1818
use lightning_block_sync::{BlockSource, SpvClient, UnboundedCache};
1919

20-
use teos::api::http;
2120
use teos::api::internal::InternalAPI;
21+
use teos::api::{http, tor};
2222
use teos::bitcoin_cli::BitcoindClient;
2323
use teos::carrier::Carrier;
2424
use teos::chain_monitor::ChainMonitor;
@@ -201,6 +201,7 @@ async fn main() {
201201
let shutdown_signal_internal_rpc_api = shutdown_signal_rpc_api.clone();
202202
let shutdown_signal_http = shutdown_signal_rpc_api.clone();
203203
let shutdown_signal_cm = shutdown_signal_rpc_api.clone();
204+
let shutdown_signal_tor = shutdown_signal_rpc_api.clone();
204205

205206
// The ordering here actually matters. Listeners are called by order, and we want the gatekeeper to be called
206207
// last, so both the Watcher and the Responder can query the necessary data from it during data deletion.
@@ -265,11 +266,31 @@ async fn main() {
265266
internal_rpc_api_uri,
266267
shutdown_signal_http,
267268
));
269+
270+
// Add Tor Onion Service for public API
271+
let mut tor_task = Option::None;
272+
if conf.tor_support {
273+
log::info!("Starting up hidden tor service");
274+
let tor_control_port = conf.tor_control_port;
275+
let api_port = conf.api_port;
276+
let onion_port = conf.onion_hidden_service_port;
277+
278+
tor_task = Some(task::spawn(async move {
279+
tor::expose_onion_service(tor_control_port, api_port, onion_port, shutdown_signal_tor)
280+
.await
281+
.unwrap();
282+
}));
283+
}
284+
268285
chain_monitor.monitor_chain().await;
269286

270287
// Wait until shutdown
271288
http_api_task.await.unwrap();
272289
private_api_task.await.unwrap();
273290
public_api_task.await.unwrap();
274-
log::info!("Shutting down tower")
291+
if conf.tor_support {
292+
tor_task.unwrap().await.unwrap();
293+
}
294+
295+
log::info!("Shutting down tower");
275296
}

0 commit comments

Comments
 (0)