Upgrade packages and make sessions more secure
Sign session cookies with base64 encoded secret from .env
This commit is contained in:
parent
2fab68241e
commit
3f97c0e2ca
1500
Cargo.lock
generated
1500
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@ -12,14 +12,15 @@ path = "src/lib.rs"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ammonia = "3.3.0"
|
ammonia = "4"
|
||||||
ansi-to-html = "0.2"
|
ansi-to-html = "0.2"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
axum = { version = "0.7", features = ["form", "multipart", "query"] }
|
axum = { version = "0.7", features = ["form", "multipart", "query"] }
|
||||||
axum-client-ip = "0.5"
|
axum-client-ip = "0.6"
|
||||||
axum-extra = { version = "0.9", features = ["typed-header"] }
|
axum-extra = { version = "0.9", features = ["typed-header"] }
|
||||||
axum-login = "0.10"
|
axum-login = "0.15"
|
||||||
|
base64 = "0.22"
|
||||||
bytes = "1.4"
|
bytes = "1.4"
|
||||||
# TODO: replace chrono with time
|
# TODO: replace chrono with time
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
@ -39,8 +40,8 @@ notify = "6"
|
|||||||
once_cell = "1.18"
|
once_cell = "1.18"
|
||||||
opml = "1.1"
|
opml = "1.1"
|
||||||
password-auth = "1.0"
|
password-auth = "1.0"
|
||||||
readability = "0.2"
|
readability = "0.3"
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_with = "3"
|
serde_with = "3"
|
||||||
sqlx = { version = "0.7", features = [
|
sqlx = { version = "0.7", features = [
|
||||||
@ -59,13 +60,14 @@ tokio-stream = { version = "0.1", features = ["sync"] }
|
|||||||
tower = "0.4"
|
tower = "0.4"
|
||||||
tower-livereload = "0.9"
|
tower-livereload = "0.9"
|
||||||
tower-http = { version = "0.5", features = ["trace", "fs"] }
|
tower-http = { version = "0.5", features = ["trace", "fs"] }
|
||||||
tower-sessions = { version = "0.7", features = ["redis-store"] }
|
tower-sessions = { version = "0.12", features = ["signed"] }
|
||||||
|
tower-sessions-redis-store = "0.12"
|
||||||
tracing = { version = "0.1", features = ["valuable", "attributes"] }
|
tracing = { version = "0.1", features = ["valuable", "attributes"] }
|
||||||
tracing-appender = "0.2"
|
tracing-appender = "0.2"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
uuid = { version = "1.4", features = ["serde"] }
|
uuid = { version = "1.4", features = ["serde"] }
|
||||||
url = "2.4"
|
url = "2.4"
|
||||||
validator = { version = "0.16", features = ["derive"] }
|
validator = { version = "0.18", features = ["derive"] }
|
||||||
|
|
||||||
[profile.dev.package.sqlx-macros]
|
[profile.dev.package.sqlx-macros]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
37
README.md
37
README.md
@ -9,25 +9,26 @@ algorithm. Pining for the days of Google Reader. An excuse to write more Rust.
|
|||||||
|
|
||||||
Install these requirements to get started developing crawlnicle.
|
Install these requirements to get started developing crawlnicle.
|
||||||
|
|
||||||
* [rust](https://www.rust-lang.org/)
|
- [rust](https://www.rust-lang.org/)
|
||||||
* [postgres](https://www.postgresql.org/)
|
- [postgres](https://www.postgresql.org/)
|
||||||
* [redis](https://redis.io/)
|
- [redis](https://redis.io/)
|
||||||
* [sqlx-cli](https://crates.io/crates/sqlx-cli)
|
- [sqlx-cli](https://crates.io/crates/sqlx-cli)
|
||||||
* Only postgres needed. Install with:
|
|
||||||
|
|
||||||
```bash
|
- Only postgres needed. Install with:
|
||||||
cargo install sqlx-cli --no-default-features --features native-tls,postgres
|
|
||||||
```
|
|
||||||
|
|
||||||
* [just](https://github.com/casey/just#installation)
|
```bash
|
||||||
* [bun](https://bun.sh)
|
cargo install sqlx-cli --no-default-features --features native-tls,postgres
|
||||||
* An [SMTP server for sending
|
```
|
||||||
emails](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) (put
|
|
||||||
configuration in the `.env` file)
|
- [just](https://github.com/casey/just#installation)
|
||||||
* (optional) [cargo-watch](https://github.com/watchexec/cargo-watch#install) for
|
- [bun](https://bun.sh)
|
||||||
auto-recompiling the server in development
|
- An [SMTP server for sending
|
||||||
* (optional) [mold](https://github.com/rui314/mold#installation) for faster
|
emails](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) (put
|
||||||
builds
|
configuration in the `.env` file)
|
||||||
|
- (optional) [cargo-watch](https://github.com/watchexec/cargo-watch#install) for
|
||||||
|
auto-recompiling the server in development
|
||||||
|
- (optional) [mold](https://github.com/rui314/mold#installation) for faster
|
||||||
|
builds
|
||||||
|
|
||||||
### First-time setup
|
### First-time setup
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ builds
|
|||||||
SMTP_USER=user
|
SMTP_USER=user
|
||||||
SMTP_PASSWORD=password
|
SMTP_PASSWORD=password
|
||||||
EMAIL_FROM="crawlnicle <no-reply@mail.crawlnicle.com>"
|
EMAIL_FROM="crawlnicle <no-reply@mail.crawlnicle.com>"
|
||||||
SESSION_SECRET=64-bytes-of-secret
|
SESSION_SECRET=64-bytes-of-base64-encoded-secret
|
||||||
IP_SOURCE=ConnectInfo
|
IP_SOURCE=ConnectInfo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ pub struct Config {
|
|||||||
#[clap(long, env, default_value = "crawlnicle <no-reply@mail.crawlnicle.com>")]
|
#[clap(long, env, default_value = "crawlnicle <no-reply@mail.crawlnicle.com>")]
|
||||||
pub email_from: Mailbox,
|
pub email_from: Mailbox,
|
||||||
#[clap(long, env)]
|
#[clap(long, env)]
|
||||||
pub session_secret: String,
|
pub session_secret: String, // base64 encoded
|
||||||
#[clap(long, env, default_value = "ConnectInfo")]
|
#[clap(long, env, default_value = "ConnectInfo")]
|
||||||
pub ip_source: IpSource,
|
pub ip_source: IpSource,
|
||||||
#[clap(long, env, default_value = "100")]
|
#[clap(long, env, default_value = "100")]
|
||||||
|
@ -7,6 +7,7 @@ use crate::htmx::HXRedirect;
|
|||||||
|
|
||||||
pub async fn get(mut auth: AuthSession) -> Result<Response> {
|
pub async fn get(mut auth: AuthSession) -> Result<Response> {
|
||||||
auth.logout()
|
auth.logout()
|
||||||
|
.await
|
||||||
.context("failed to logout user from session")?;
|
.context("failed to logout user from session")?;
|
||||||
Ok(HXRedirect::to("/").reload(true).into_response())
|
Ok(HXRedirect::to("/").reload(true).into_response())
|
||||||
}
|
}
|
||||||
|
39
src/main.rs
39
src/main.rs
@ -2,19 +2,18 @@ use std::{collections::HashMap, net::SocketAddr, path::Path, sync::Arc};
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use axum::{
|
use axum::{
|
||||||
error_handling::HandleErrorLayer,
|
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
BoxError, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use axum_login::{
|
use axum_login::{
|
||||||
login_required,
|
login_required,
|
||||||
tower_sessions::{fred::prelude::*, Expiry, RedisStore, SessionManagerLayer},
|
tower_sessions::{Expiry, SessionManagerLayer},
|
||||||
AuthManagerLayerBuilder,
|
AuthManagerLayerBuilder,
|
||||||
};
|
};
|
||||||
|
use base64::prelude::*;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use http::StatusCode;
|
|
||||||
use lettre::transport::smtp::authentication::Credentials;
|
use lettre::transport::smtp::authentication::Credentials;
|
||||||
use lettre::SmtpTransport;
|
use lettre::SmtpTransport;
|
||||||
use notify::Watcher;
|
use notify::Watcher;
|
||||||
@ -26,6 +25,8 @@ use tokio::sync::Mutex;
|
|||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
use tower_http::{services::ServeDir, trace::TraceLayer};
|
use tower_http::{services::ServeDir, trace::TraceLayer};
|
||||||
use tower_livereload::LiveReloadLayer;
|
use tower_livereload::LiveReloadLayer;
|
||||||
|
use tower_sessions::cookie::Key;
|
||||||
|
use tower_sessions_redis_store::{fred::prelude::*, RedisStore};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use lib::config::Config;
|
use lib::config::Config;
|
||||||
@ -62,9 +63,6 @@ async fn main() -> Result<()> {
|
|||||||
let domain_locks = DomainLocks::new();
|
let domain_locks = DomainLocks::new();
|
||||||
let client = Client::builder().user_agent(USER_AGENT).build()?;
|
let client = Client::builder().user_agent(USER_AGENT).build()?;
|
||||||
|
|
||||||
// TODO: not needed anymore?
|
|
||||||
// let secret = config.session_secret.as_bytes();
|
|
||||||
|
|
||||||
let pool = PgPoolOptions::new()
|
let pool = PgPoolOptions::new()
|
||||||
.max_connections(config.database_max_connections)
|
.max_connections(config.database_max_connections)
|
||||||
.acquire_timeout(std::time::Duration::from_secs(3))
|
.acquire_timeout(std::time::Duration::from_secs(3))
|
||||||
@ -72,34 +70,25 @@ async fn main() -> Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let redis_config = RedisConfig::from_url(&config.redis_url)?;
|
let redis_config = RedisConfig::from_url(&config.redis_url)?;
|
||||||
// TODO: https://github.com/maxcountryman/tower-sessions/issues/92
|
let redis_pool = RedisPool::new(redis_config, None, None, None, config.redis_pool_size)?;
|
||||||
// let redis_pool = RedisPool::new(redis_config, None, None, config.redis_pool_size)?;
|
redis_pool.init().await?;
|
||||||
// redis_pool.connect();
|
|
||||||
// redis_pool.wait_for_connect().await?;
|
|
||||||
let redis_client = RedisClient::new(redis_config, None, None, None);
|
|
||||||
redis_client.connect();
|
|
||||||
redis_client.wait_for_connect().await?;
|
|
||||||
|
|
||||||
let session_store = RedisStore::new(redis_client);
|
let session_store = RedisStore::new(redis_pool);
|
||||||
let session_layer = SessionManagerLayer::new(session_store)
|
let session_layer = SessionManagerLayer::new(session_store)
|
||||||
.with_secure(!cfg!(debug_assertions))
|
.with_secure(!cfg!(debug_assertions))
|
||||||
.with_expiry(Expiry::OnInactivity(Duration::days(
|
.with_expiry(Expiry::OnInactivity(Duration::days(
|
||||||
config.session_duration_days,
|
config.session_duration_days,
|
||||||
)));
|
)))
|
||||||
|
.with_signed(Key::from(&BASE64_STANDARD.decode(&config.session_secret)?));
|
||||||
|
|
||||||
let backend = Backend::new(pool.clone());
|
let backend = Backend::new(pool.clone());
|
||||||
let auth_service = ServiceBuilder::new()
|
let auth_layer = AuthManagerLayerBuilder::new(backend, session_layer).build();
|
||||||
.layer(HandleErrorLayer::new(|_: BoxError| async {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}))
|
|
||||||
.layer(AuthManagerLayerBuilder::new(backend, session_layer).build());
|
|
||||||
|
|
||||||
let creds = Credentials::new(config.smtp_user.clone(), config.smtp_password.clone());
|
|
||||||
|
|
||||||
|
let smtp_creds = Credentials::new(config.smtp_user.clone(), config.smtp_password.clone());
|
||||||
// Open a remote connection to gmail
|
// Open a remote connection to gmail
|
||||||
let mailer = SmtpTransport::relay(&config.smtp_server)
|
let mailer = SmtpTransport::relay(&config.smtp_server)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.credentials(creds)
|
.credentials(smtp_creds)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
sqlx::migrate!().run(&pool).await?;
|
sqlx::migrate!().run(&pool).await?;
|
||||||
@ -163,7 +152,7 @@ async fn main() -> Result<()> {
|
|||||||
mailer,
|
mailer,
|
||||||
})
|
})
|
||||||
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
|
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
|
||||||
.layer(auth_service)
|
.layer(auth_layer)
|
||||||
.layer(ip_source_extension);
|
.layer(ip_source_extension);
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
|
Loading…
Reference in New Issue
Block a user