Skip to content

Commit 598cd86

Browse files
author
test
committed
Implement mTLS
1 parent 79dde26 commit 598cd86

4 files changed

Lines changed: 136 additions & 31 deletions

File tree

Cargo.lock

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "1.0.0"
44
edition = "2024"
55

66
[dependencies]
7-
actix-web = "4"
7+
actix-web = { version = "4", features = ["openssl"] }
88
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
99
reqwest = { version = "0.12.15", features = ["json"] }
1010
futures-util = "0.3.31"
@@ -16,6 +16,7 @@ futures = "0.3.31"
1616
async-stream = "0.3.6"
1717
sha2 = "0.11.0-pre.5"
1818
hex = "0.4.3"
19+
openssl = { version = "0.10", features = ["vendored"] }
1920
chrono = "0.4.40"
2021
jsonwebtoken = "9.3.1"
2122
log = "0.4.27"

README.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,30 @@ With its advanced real‑time update capabilities, DynaRust pushes live changes
3939
* **Use Case Example:** Imagine a web UI needing push notifications. Store device IDs as keys in a `devices` table. Use a separate `status` key in the same table. The frontend listens to `devices/subscribe/status`. The backend iterates through device keys, performs actions, and updates the `status` key, instantly notifying all listening frontends. Simple and blazing fast! ⚡️
4040

4141
* **🔒 Security:**
42-
Each record needs a Authorization header for PUT, DELETE operations, each record has an owner field. Metodology is: anyone can read, only the owner can write or delete
43-
Each node requires a "secret" token (set by the `CLUSTER_SECRET` environment variable) to join a cluster.
42+
Certainly! Here’s a more polished and highlighted version of the **Security** section, emphasizing clarity and best practices:
4443

44+
---
45+
46+
### 🔒 **Security**
47+
48+
- **Access Control:**
49+
- **Read:** Anyone can read records.
50+
- **Write/Delete:** Only the record’s owner (as specified in the `owner` field) can modify or delete it.
51+
- **Enforcement:** All `PUT` and `DELETE` operations require an `Authorization` header. The server verifies that the requester matches the record’s owner.
52+
53+
- **Cluster Security:**
54+
- Each node must present a **secret token** (set via the `CLUSTER_SECRET` environment variable) to join the cluster, ensuring only trusted nodes participate.
55+
56+
- **Transport Security (HTTPS):**
57+
- All communication is secured with HTTPS by default.
58+
- **Easy Certificate Generation:**
59+
- Run `bash cert.sh`, provide a password, and a `.p12` certificate will be generated under the `cert/` directory.
60+
- **Testing Mode:**
61+
- Set `DYNA_MODE=http` to disable HTTPS (for testing only; **not recommended for production**).
62+
63+
---
64+
65+
**_Security is enforced at every layer: from user access to node-to-node communication, ensuring your data remains private and protected._**
4566
* **🌐 Distributed Storage:**
4667
Data is automatically partitioned and spread across all nodes in the cluster.
4768

src/main.rs

Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod storage;
22
mod network;
33
mod tokenizer;
44
mod security;
5+
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode};
56

67
use actix_web::{web, App, HttpServer};
78
use once_cell::sync::OnceCell;
@@ -81,6 +82,17 @@ async fn main() -> std::io::Result<()> {
8182
Ok(_) => println!("Cold storage loaded"),
8283
Err(e) => eprintln!("Error loading cold storage: {}", e),
8384
}
85+
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
86+
builder
87+
.set_private_key_file("./cert/server.key", SslFiletype::PEM)
88+
.unwrap();
89+
builder
90+
.set_certificate_chain_file("./cert/server.crt")
91+
.unwrap();
92+
builder
93+
.set_ca_file("./cert/ca.crt")
94+
.unwrap();
95+
builder.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT);
8496

8597
// (Optionally) Ensure the default table exists in memory.
8698
{
@@ -173,37 +185,82 @@ async fn main() -> std::io::Result<()> {
173185
tokio::spawn(membership_sync(cluster_clone, current_clone, 60));
174186
let subscription_manager = web::Data::new(SubscriptionManager::new());
175187

176-
println!("Starting distributed DB engine at http://{}", current_node);
177188

178-
// Build and run the HTTP server.
179-
HttpServer::new(move || {
189+
let use_https = match env::var("DYNA_MODE").unwrap_or_default().as_str() {
190+
"http" => false,
191+
_ => true
192+
};
193+
match use_https {
194+
true => {
195+
println!("Starting distributed DB engine at https://{}", current_node);
196+
197+
// Build and run the HTTP server.
198+
HttpServer::new(move || {
199+
App::new()
200+
.app_data(subscription_manager.clone())
201+
.app_data(state.clone())
202+
.app_data(cluster_data.clone())
203+
.app_data(web::Data::new(current_node.clone()))
204+
// Cluster management endpoints.
205+
.route("/join", web::post().to(join_cluster))
206+
.route("/membership", web::get().to(get_membership))
207+
.route("/update_membership", web::post().to(update_membership))
208+
.route("/heartbeat", web::get().to(heartbeat))
209+
// Key–value endpoints with multi‑table support.
210+
.route("/{table}/key/{key}", web::get().to(get_value))
211+
.route("/{table}/key/{key}", web::put().to(put_value))
212+
.route("/{table}/key/{key}", web::delete().to(delete_value))
213+
// Endpoint to fetch a table’s entire in‑memory store.
214+
.route("/{table}/store", web::get().to(get_table_store))
215+
// Global endpoint returning the entire in‑memory store.
216+
.route("/store", web::get().to(get_global_store))
217+
// Endpoints to get keys from a table.
218+
.route("/{table}/keys", web::get().to(get_all_keys))
219+
.route("/{table}/keys", web::post().to(get_multiple_keys))
220+
.route("/{table}/subscribe/{key}", web::get().to(
221+
storage::subscription::subscribe_to_key
222+
))
223+
.route("/auth/{user}", web::post().to(security::authentication::access))
224+
})
225+
.bind_openssl(bind_addr.as_str(), builder)?
226+
.run()
227+
.await
228+
}
229+
false => {
230+
println!("Starting distributed DB engine at http://{}", current_node);
231+
232+
// Build and run the HTTP server.
233+
HttpServer::new(move || {
180234
App::new()
181-
.app_data(subscription_manager.clone())
182-
.app_data(state.clone())
183-
.app_data(cluster_data.clone())
184-
.app_data(web::Data::new(current_node.clone()))
185-
// Cluster management endpoints.
186-
.route("/join", web::post().to(join_cluster))
187-
.route("/membership", web::get().to(get_membership))
188-
.route("/update_membership", web::post().to(update_membership))
189-
.route("/heartbeat", web::get().to(heartbeat))
190-
// Key–value endpoints with multi‑table support.
191-
.route("/{table}/key/{key}", web::get().to(get_value))
192-
.route("/{table}/key/{key}", web::put().to(put_value))
193-
.route("/{table}/key/{key}", web::delete().to(delete_value))
194-
// Endpoint to fetch a table’s entire in‑memory store.
195-
.route("/{table}/store", web::get().to(get_table_store))
196-
// Global endpoint returning the entire in‑memory store.
197-
.route("/store", web::get().to(get_global_store))
198-
// Endpoints to get keys from a table.
199-
.route("/{table}/keys", web::get().to(get_all_keys))
200-
.route("/{table}/keys", web::post().to(get_multiple_keys))
201-
.route("/{table}/subscribe/{key}", web::get().to(
202-
storage::subscription::subscribe_to_key
203-
))
204-
.route("/auth/{user}", web::post().to(security::authentication::access))
205-
})
235+
.app_data(subscription_manager.clone())
236+
.app_data(state.clone())
237+
.app_data(cluster_data.clone())
238+
.app_data(web::Data::new(current_node.clone()))
239+
// Cluster management endpoints.
240+
.route("/join", web::post().to(join_cluster))
241+
.route("/membership", web::get().to(get_membership))
242+
.route("/update_membership", web::post().to(update_membership))
243+
.route("/heartbeat", web::get().to(heartbeat))
244+
// Key–value endpoints with multi‑table support.
245+
.route("/{table}/key/{key}", web::get().to(get_value))
246+
.route("/{table}/key/{key}", web::put().to(put_value))
247+
.route("/{table}/key/{key}", web::delete().to(delete_value))
248+
// Endpoint to fetch a table’s entire in‑memory store.
249+
.route("/{table}/store", web::get().to(get_table_store))
250+
// Global endpoint returning the entire in‑memory store.
251+
.route("/store", web::get().to(get_global_store))
252+
// Endpoints to get keys from a table.
253+
.route("/{table}/keys", web::get().to(get_all_keys))
254+
.route("/{table}/keys", web::post().to(get_multiple_keys))
255+
.route("/{table}/subscribe/{key}", web::get().to(
256+
storage::subscription::subscribe_to_key
257+
))
258+
.route("/auth/{user}", web::post().to(security::authentication::access))
259+
})
206260
.bind(bind_addr.as_str())?
207261
.run()
208262
.await
263+
}
264+
}
265+
209266
}

0 commit comments

Comments
 (0)