Support for TLS encryption
This commit is contained in:
42
Cargo.lock
generated
42
Cargo.lock
generated
@@ -240,6 +240,28 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-server"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab"
|
||||||
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
|
"bytes",
|
||||||
|
"fs-err",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.75"
|
version = "0.3.75"
|
||||||
@@ -815,6 +837,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs-err"
|
||||||
|
version = "3.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88d7be93788013f265201256d58f04936a8079ad5dc898743aa20525f503b683"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -2240,6 +2272,15 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
@@ -3802,6 +3843,7 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"axum-messages",
|
"axum-messages",
|
||||||
|
"axum-server",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
"claims",
|
"claims",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ argon2 = { version = "0.5.3", features = ["std"] }
|
|||||||
axum = { version = "0.8.4", features = ["macros"] }
|
axum = { version = "0.8.4", features = ["macros"] }
|
||||||
axum-extra = { version = "0.10.1", features = ["query", "cookie"] }
|
axum-extra = { version = "0.10.1", features = ["query", "cookie"] }
|
||||||
axum-messages = "0.8.0"
|
axum-messages = "0.8.0"
|
||||||
|
axum-server = { version = "0.7.2", features = ["tls-rustls-no-provider"] }
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
|
chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
|
||||||
config = "0.15.14"
|
config = "0.15.14"
|
||||||
|
|||||||
10
Dockerfile
10
Dockerfile
@@ -7,6 +7,8 @@ RUN cargo chef prepare --recipe-path recipe.json
|
|||||||
|
|
||||||
FROM chef AS builder
|
FROM chef AS builder
|
||||||
COPY --from=planner /app/recipe.json recipe.json
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
RUN apt update -y \
|
||||||
|
&& apt install -y --no-install-recommends clang mold
|
||||||
RUN cargo chef cook --release --recipe-path recipe.json
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV SQLX_OFFLINE=true
|
ENV SQLX_OFFLINE=true
|
||||||
@@ -15,10 +17,10 @@ RUN cargo build --release --bin zero2prod
|
|||||||
FROM debian:bookworm-slim AS runtime
|
FROM debian:bookworm-slim AS runtime
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apt update -y \
|
RUN apt update -y \
|
||||||
&& apt install -y --no-install-recommends openssl ca-certificates \
|
&& apt install -y --no-install-recommends openssl ca-certificates \
|
||||||
&& apt autoremove -y \
|
&& apt autoremove -y \
|
||||||
&& apt clean -y \
|
&& apt clean -y \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
COPY --from=builder /app/target/release/zero2prod zero2prod
|
COPY --from=builder /app/target/release/zero2prod zero2prod
|
||||||
COPY configuration configuration
|
COPY configuration configuration
|
||||||
ENV APP_ENVIRONMENT=production
|
ENV APP_ENVIRONMENT=production
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
application:
|
|
||||||
port: 8000
|
|
||||||
database:
|
|
||||||
database_name: "newsletter"
|
|
||||||
email_client:
|
email_client:
|
||||||
timeout_milliseconds: 10000
|
timeout_milliseconds: 10000
|
||||||
|
base_url: "https://api.mailersend.com"
|
||||||
|
sender_email: "MS_PTrumQ@test-r6ke4n1mmzvgon12.mlsender.net"
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
application:
|
application:
|
||||||
|
port: 8000
|
||||||
host: "127.0.0.1"
|
host: "127.0.0.1"
|
||||||
base_url: "http://127.0.0.1:8000"
|
base_url: "http://127.0.0.1:8000"
|
||||||
|
require_tls: false
|
||||||
database:
|
database:
|
||||||
host: "127.0.0.1"
|
host: "127.0.0.1"
|
||||||
port: 5432
|
port: 5432
|
||||||
|
database_name: "newsletter"
|
||||||
username: "postgres"
|
username: "postgres"
|
||||||
password: "password"
|
password: "password"
|
||||||
require_ssl: false
|
require_ssl: false
|
||||||
email_client:
|
email_client:
|
||||||
base_url: "https://api.mailersend.com"
|
|
||||||
sender_email: "MS_PTrumQ@test-r6ke4n1mmzvgon12.mlsender.net"
|
|
||||||
authorization_token: "secret-token"
|
authorization_token: "secret-token"
|
||||||
redis_uri: "redis://127.0.0.1:6379"
|
redis_uri: "redis://127.0.0.1:6379"
|
||||||
|
|||||||
@@ -1,7 +1,2 @@
|
|||||||
application:
|
application:
|
||||||
host: "0.0.0.0"
|
host: "0.0.0.0"
|
||||||
database:
|
|
||||||
require_ssl: true
|
|
||||||
email_client:
|
|
||||||
base_url: "https://api.mailersend.com"
|
|
||||||
sender_email: "MS_PTrumQ@test-r6ke4n1mmzvgon12.mlsender.net"
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ pub struct ApplicationSettings {
|
|||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
|
pub require_tls: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize)]
|
#[derive(Clone, Deserialize)]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use zero2prod::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), std::io::Error> {
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
init_subscriber(std::io::stdout);
|
init_subscriber(std::io::stdout);
|
||||||
|
|
||||||
let configuration = get_configuration().expect("Failed to read configuration");
|
let configuration = get_configuration().expect("Failed to read configuration");
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ impl IntoResponse for SubscribeError {
|
|||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
name = "Adding a new subscriber",
|
name = "Adding a new subscriber",
|
||||||
skip(connection_pool, email_client, base_url, form),
|
skip(messages, connection_pool, email_client, base_url, form),
|
||||||
fields(
|
fields(
|
||||||
subscriber_email = %form.email,
|
subscriber_email = %form.email,
|
||||||
subscriber_name = %form.name
|
subscriber_name = %form.name
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ use axum::{
|
|||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
};
|
};
|
||||||
use axum_messages::MessagesManagerLayer;
|
use axum_messages::MessagesManagerLayer;
|
||||||
|
use axum_server::tls_rustls::RustlsConfig;
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
use sqlx::{PgPool, postgres::PgPoolOptions};
|
use sqlx::{PgPool, postgres::PgPoolOptions};
|
||||||
use std::sync::Arc;
|
use std::{net::TcpListener, sync::Arc};
|
||||||
use tokio::net::TcpListener;
|
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tower_sessions::SessionManagerLayer;
|
use tower_sessions::SessionManagerLayer;
|
||||||
use tower_sessions_redis_store::{
|
use tower_sessions_redis_store::{
|
||||||
@@ -21,11 +21,6 @@ use tower_sessions_redis_store::{
|
|||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub struct Application {
|
|
||||||
listener: TcpListener,
|
|
||||||
router: Router,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub connection_pool: PgPool,
|
pub connection_pool: PgPool,
|
||||||
@@ -33,13 +28,19 @@ pub struct AppState {
|
|||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Application {
|
||||||
|
listener: TcpListener,
|
||||||
|
router: Router,
|
||||||
|
tls_config: Option<RustlsConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
pub async fn build(configuration: Settings) -> Result<Self, std::io::Error> {
|
pub async fn build(configuration: Settings) -> Result<Self, anyhow::Error> {
|
||||||
let address = format!(
|
let address = format!(
|
||||||
"{}:{}",
|
"{}:{}",
|
||||||
configuration.application.host, configuration.application.port
|
configuration.application.host, configuration.application.port
|
||||||
);
|
);
|
||||||
let listener = TcpListener::bind(address).await?;
|
// let listener = TcpListener::bind(address).await?;
|
||||||
let connection_pool =
|
let connection_pool =
|
||||||
PgPoolOptions::new().connect_lazy_with(configuration.database.with_db());
|
PgPoolOptions::new().connect_lazy_with(configuration.database.with_db());
|
||||||
let email_client = EmailClient::build(configuration.email_client).unwrap();
|
let email_client = EmailClient::build(configuration.email_client).unwrap();
|
||||||
@@ -61,17 +62,46 @@ impl Application {
|
|||||||
configuration.application.base_url,
|
configuration.application.base_url,
|
||||||
redis_store,
|
redis_store,
|
||||||
);
|
);
|
||||||
Ok(Self { listener, router })
|
let tls_config = if configuration.application.require_tls {
|
||||||
|
Some(
|
||||||
|
RustlsConfig::from_pem_file(
|
||||||
|
"/home/alphonse/.certs/fullchain.pem",
|
||||||
|
"/home/alphonse/.certs/privkey.pem",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let listener = TcpListener::bind(address).unwrap();
|
||||||
|
Ok(Self {
|
||||||
|
listener,
|
||||||
|
router,
|
||||||
|
tls_config,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_until_stopped(self) -> Result<(), std::io::Error> {
|
pub async fn run_until_stopped(self) -> Result<(), std::io::Error> {
|
||||||
tracing::debug!("listening on {}", self.listener.local_addr().unwrap());
|
tracing::debug!("listening on {}", self.local_addr());
|
||||||
axum::serve(self.listener, self.router).await
|
if let Some(tls_config) = self.tls_config {
|
||||||
|
axum_server::from_tcp_rustls(self.listener, tls_config)
|
||||||
|
.serve(self.router.into_make_service())
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
axum_server::from_tcp(self.listener)
|
||||||
|
.serve(self.router.into_make_service())
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_addr(&self) -> String {
|
pub fn local_addr(&self) -> String {
|
||||||
self.listener.local_addr().unwrap().to_string()
|
self.listener.local_addr().unwrap().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
self.listener.local_addr().unwrap().port()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app(
|
pub fn app(
|
||||||
|
|||||||
@@ -87,19 +87,14 @@ impl TestApp {
|
|||||||
c.email_client.base_url = email_server.uri();
|
c.email_client.base_url = email_server.uri();
|
||||||
c
|
c
|
||||||
};
|
};
|
||||||
|
let local_addr = configuration.application.host.clone();
|
||||||
let connection_pool = configure_database(&configuration.database).await;
|
let connection_pool = configure_database(&configuration.database).await;
|
||||||
let email_client = EmailClient::build(configuration.email_client.clone()).unwrap();
|
let email_client = EmailClient::build(configuration.email_client.clone()).unwrap();
|
||||||
let application = Application::build(configuration)
|
let application = Application::build(configuration)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to build application");
|
.expect("Failed to build application");
|
||||||
let local_addr = application.local_addr();
|
let port = application.port();
|
||||||
let port = local_addr
|
let address = format!("http://{}:{}", local_addr, port);
|
||||||
.split(":")
|
|
||||||
.last()
|
|
||||||
.unwrap()
|
|
||||||
.parse::<u16>()
|
|
||||||
.unwrap();
|
|
||||||
let address = format!("http://{}", application.local_addr());
|
|
||||||
let test_user = TestUser::generate();
|
let test_user = TestUser::generate();
|
||||||
test_user.store(&connection_pool).await;
|
test_user.store(&connection_pool).await;
|
||||||
let api_client = reqwest::Client::builder()
|
let api_client = reqwest::Client::builder()
|
||||||
|
|||||||
Reference in New Issue
Block a user