Compare commits
2 Commits
34041089d7
...
122f6dc617
| Author | SHA1 | Date | |
|---|---|---|---|
| 122f6dc617 | |||
| aa912492c7 |
74
src/main.rs
74
src/main.rs
@@ -1,8 +1,41 @@
|
||||
use aes_gcm::{
|
||||
aead::{Aead, AeadCore, KeyInit, OsRng},
|
||||
Aes256Gcm, Key, Nonce,
|
||||
};
|
||||
use axum::{Router, routing::get};
|
||||
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
pool: SqlitePool,
|
||||
master_key: String,
|
||||
}
|
||||
|
||||
fn get_master_key(master_key: &str) -> Key<Aes256Gcm> {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(master_key.as_bytes());
|
||||
let result = hasher.finalize();
|
||||
*Key::<Aes256Gcm>::from_slice(&result)
|
||||
}
|
||||
|
||||
fn encrypt_secret(master_key: &str, plaintext: &str) -> String {
|
||||
let key = get_master_key(master_key);
|
||||
let cipher = Aes256Gcm::new(&key);
|
||||
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
|
||||
let ciphertext = cipher
|
||||
.encrypt(&nonce, plaintext.as_bytes())
|
||||
.expect("encryption failure");
|
||||
|
||||
let mut payload = nonce.to_vec();
|
||||
payload.extend_from_slice(&ciphertext);
|
||||
BASE64.encode(payload)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt()
|
||||
@@ -14,9 +47,15 @@ async fn main() {
|
||||
|
||||
dotenvy::dotenv().ok();
|
||||
|
||||
let db_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite://data.db?mode=rwc".to_string());
|
||||
tracing::info!("Connecting to database at {}", db_url);
|
||||
let master_key = std::env::var("SERVER_MASTER_KEY")
|
||||
.expect("SERVER_MASTER_KEY environment variable must be set");
|
||||
|
||||
let db_url = std::env::var("DATABASE_URL")
|
||||
.unwrap_or_else(|_| "sqlite://data.db?mode=rwc".to_string());
|
||||
|
||||
let port = std::env::var("SERVER_PORT").unwrap_or_else(|_| "3000".to_string());
|
||||
|
||||
tracing::info!("Connecting to database at {}", db_url);
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&db_url)
|
||||
@@ -31,13 +70,38 @@ async fn main() {
|
||||
|
||||
tracing::info!("Migrations successful.");
|
||||
|
||||
if let Ok(file_content) = std::fs::read_to_string("secrets.json") {
|
||||
tracing::info!("Found secrets.json, provisioning");
|
||||
let secrets: HashMap<String, String> =
|
||||
serde_json::from_str(&file_content).expect("Invalid secrets.json format");
|
||||
|
||||
for (key, value) in secrets {
|
||||
let encrypted_val = encrypt_secret(&master_key, &value);
|
||||
sqlx::query(
|
||||
"INSERT INTO secrets (key_name, encrypted_value) VALUES (?, ?) ON CONFLICT(key_name) DO UPDATE SET encrypted_value = excluded.encrypted_value"
|
||||
)
|
||||
.bind(key)
|
||||
.bind(encrypted_val)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("Failed to insert secret");
|
||||
}
|
||||
|
||||
std::fs::rename("secrets.json", "secrets.json.bak")
|
||||
.expect("Failed to rename secrets.json");
|
||||
tracing::info!("Provisioned secrets and renamed to secrets.json.bak");
|
||||
}
|
||||
|
||||
let state = AppState {
|
||||
pool,
|
||||
master_key,
|
||||
};
|
||||
|
||||
let app = Router::new()
|
||||
.route("/health", get(|| async { "OK" }))
|
||||
.with_state(pool);
|
||||
.with_state(state);
|
||||
|
||||
let port = std::env::var("SERVER_PORT").unwrap_or_else(|_| "3000".to_string());
|
||||
let addr: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
|
||||
tracing::info!("Listening on {}", addr);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user