HTTPS и TLS в Axum
Защита соединений с помощью HTTPS (HTTP Secure) и TLS (Transport Layer Security) является необходимым аспектом для современных веб-приложений. В этом разделе рассмотрим, как настроить и оптимизировать защищенные соединения в приложениях на Axum.
Содержание
- Основы HTTPS и TLS
- Настройка HTTPS сервера
- Автоматическое перенаправление с HTTP на HTTPS
- Let's Encrypt и автоматические сертификаты
- HTTP/2 поддержка
- Оптимизация TLS
- Лучшие практики безопасности
Основы HTTPS и TLS
HTTPS - это протокол HTTP поверх TLS/SSL, который обеспечивает:
- Шифрование данных между клиентом и сервером
- Аутентификацию сервера для клиента
- Защиту от атак "человек посередине"
- Проверку целостности передаваемых данных
Настройка HTTPS сервера
Для настройки HTTPS в Axum необходимо использовать библиотеку rustls
или native-tls
.
Установка зависимостей
[dependencies]
axum = "0.7.2"
tokio = { version = "1.32.0", features = ["full"] }
hyper = { version = "1.0.1", features = ["full"] }
hyper-util = { version = "0.1.1", features = ["tokio"] }
rustls = "0.21.7"
rustls-pemfile = "1.0.3"
tokio-rustls = "0.24.1"
Базовая настройка HTTPS
use axum::{routing::get, Router};
use hyper::server::conn::http1;
use hyper_util::rt::TokioIo;
use rustls::{Certificate, PrivateKey, ServerConfig};
use std::{
fs::File,
io::BufReader,
net::SocketAddr,
path::{Path, PathBuf},
sync::Arc,
};
use tokio::net::TcpListener;
use tokio_rustls::TlsAcceptor;
// Обработчик маршрута
async fn hello() -> &'static str {
"Привет, HTTPS!"
}
// Загрузка сертификата и ключа
fn load_rustls_config() -> Result<ServerConfig, Box<dyn std::error::Error>> {
// Загрузка сертификата
let cert_file = File::open("path/to/cert.pem")?;
let mut cert_reader = BufReader::new(cert_file);
let certs = rustls_pemfile::certs(&mut cert_reader)?
.into_iter()
.map(Certificate)
.collect();
// Загрузка приватного ключа
let key_file = File::open("path/to/key.pem")?;
let mut key_reader = BufReader::new(key_file);
// Приоритет на PKCS8 ключи
let mut keys = rustls_pemfile::pkcs8_private_keys(&mut key_reader)?;
if keys.is_empty() {
// Попробовать RSA ключи, если PKCS8 не найдены
key_reader = BufReader::new(File::open("path/to/key.pem")?);
keys = rustls_pemfile::rsa_private_keys(&mut key_reader)?;
}
let Some(key) = keys.into_iter().next() else {
return Err("Приватный ключ не найден".into());
};
// Создание конфигурации TLS
let config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(certs, PrivateKey(key))?;
Ok(config)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Создание маршрутов
let app = Router::new().route("/", get(hello));
// Загрузка TLS конфигурации
let tls_config = load_rustls_config()?;
let tls_acceptor = TlsAcceptor::from(Arc::new(tls_config));
// Привязка сервера к порту
let addr = SocketAddr::from(([127, 0, 0, 1], 443));
let listener = TcpListener::bind(addr).await?;
println!("HTTPS сервер запущен на {}", addr);
// Обработка входящих соединений
loop {
let (stream, addr) = listener.accept().await?;
let acceptor = tls_acceptor.clone();
let app = app.clone();
tokio::spawn(async move {
let tls_stream = match acceptor.accept(stream).await {
Ok(tls_stream) => tls_stream,
Err(err) => {
eprintln!("Ошибка установки TLS: {:?}", err);
return;
}
};
let service = app.into_make_service_with_connect_info::<SocketAddr>();
let connection = http1::Builder::new()
.serve_connection(TokioIo::new(tls_stream), service)
.await;
if let Err(err) = connection {
eprintln!("Ошибка соединения: {}", err);
}
});
}
}
Автоматическое перенаправление с HTTP на HTTPS
Для автоматического перенаправления запросов с HTTP на HTTPS:
use axum::{
http::{StatusCode, Uri},
response::{IntoResponse, Redirect},
routing::get,
Router,
};
use std::{net::SocketAddr, time::Duration};
use tokio::net::TcpListener;
// HTTP обработчик - перенаправляет на HTTPS
async fn redirect_http_to_https(uri: Uri) -> impl IntoResponse {
let https_uri = format!("https://example.com{}", uri.path());
Redirect::permanent(&https_uri)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Маршруты для HTTPS сервера
let https_app = Router::new().route("/", get(hello));
// Маршруты для HTTP сервера - все перенаправляют на HTTPS
let http_app = Router::new().fallback(redirect_http_to_https);
// Запуск HTTP сервера для перенаправления
let http_addr = SocketAddr::from(([127, 0, 0, 1], 80));
tokio::spawn(async move {
let listener = TcpListener::bind(http_addr).await.unwrap();
println!("HTTP сервер запущен на {}", http_addr);
axum::serve(listener, http_app).await.unwrap();
});
// Запуск HTTPS сервера (как в предыдущем примере)
// ...
Ok(())
}
Let's Encrypt и автоматические сертификаты
Для автоматического получения и обновления сертификатов с помощью Let's Encrypt можно использовать библиотеку rustls-acme
:
[dependencies]
# ... предыдущие зависимости
rustls-acme = "0.7.6"
use axum::{routing::get, Router};
use hyper::server::conn::http1;
use hyper_util::rt::TokioIo;
use rustls_acme::{
AcmeConfig, ChallengeType, CertResolver,
ResolvesServerCertArc, rustls::ServerConfig
};
use std::{
net::SocketAddr,
sync::Arc,
time::Duration,
};
use tokio::net::TcpListener;
use tokio_rustls::TlsAcceptor;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Создание маршрутов
let app = Router::new().route("/", get(hello));
// Настройка Let's Encrypt
let domains = vec!["example.com".to_string(), "www.example.com".to_string()];
let mut state = AcmeConfig::new(domains)
.contact(vec!["mailto:admin@example.com".to_string()])
.directory_lets_encrypt_production() // или .directory_lets_encrypt_staging() для тестирования
.challenge_type(ChallengeType::Http01) // Тип проверки владения доменом
.state();
// Кэширование сертификатов
state.persist_to_directory("./acme-cache");
// Создание TLS акцептора с ACME
let cert_resolver = CertResolver::new(state);
let config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_cert_resolver(Arc::new(cert_resolver));
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
// Привязка к порту 443
let addr = SocketAddr::from(([0, 0, 0, 0], 443));
let listener = TcpListener::bind(addr).await?;
println!("HTTPS сервер запущен на {}", addr);
// Обработка входящих соединений
// ... (как в предыдущем примере)
}
HTTP/2 поддержка
Axum поддерживает HTTP/2 при использовании TLS. Для активации:
use hyper::server::conn::http2;
// В основном цикле обработки соединений:
let service = app.into_make_service_with_connect_info::<SocketAddr>();
let connection = http2::Builder::new(
hyper_util::rt::TokioExecutor::new(),
)
.serve_connection(TokioIo::new(tls_stream), service)
.await;
Для поддержки как HTTP/1.1, так и HTTP/2:
use hyper::server::conn::http1;
use hyper::server::conn::http2;
use hyper_util::rt::TokioExecutor;
// Определение протокола
let io = TokioIo::new(tls_stream);
let protocol = match io.peek(5).await {
Ok(buf) if buf.len() >= 5 => {
if &buf[..2] == b"\x16\x03" {
// TLS handshake, возможно HTTP/2
"https"
} else if &buf[..5] == b"PRI *" {
// HTTP/2 preface
"h2"
} else {
// По умолчанию HTTP/1.1
"http/1.1"
}
},
_ => "http/1.1",
};
let service = app.into_make_service_with_connect_info::<SocketAddr>();
match protocol {
"h2" => {
http2::Builder::new(TokioExecutor::new())
.serve_connection(io, service)
.await
},
_ => {
http1::Builder::new()
.serve_connection(io, service)
.await
}
}
Оптимизация TLS
Для улучшения производительности TLS:
use rustls::{ServerConfig, ServerSessionMemoryCache};
// В функции создания конфигурации TLS
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(certs, PrivateKey(key))?;
// Настройка повторного использования сессий
config.session_storage = ServerSessionMemoryCache::new(1024);
// Настройка ALPN для поддержки HTTP/2
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
// Включение 0-RTT (может иметь последствия для безопасности)
// config.enable_early_data = true;
Ok(config)
Лучшие практики безопасности
Регулярное обновление сертификатов
- Автоматизируйте процесс обновления с помощью Let's Encrypt
- Настройте мониторинг срока действия сертификатов
Настройка HSTS (HTTP Strict Transport Security)
rustuse tower_http::set_header::SetResponseHeaderLayer; use http::header::{HeaderName, HeaderValue}; let app = Router::new() .route("/", get(hello)) .layer( SetResponseHeaderLayer::if_not_present( HeaderName::from_static("strict-transport-security"), HeaderValue::from_static("max-age=31536000; includeSubDomains; preload"), ) );
Современные протоколы шифрования
- Используйте TLS 1.3, отключите поддержку устаревших протоколов
rust// При настройке rustls ServerConfig::builder() .with_safe_defaults() // Включает TLS 1.3 и отключает устаревшие шифры
Правильная настройка сертификатов
- Используйте полную цепочку сертификатов
- Добавьте все доменные имена, включая субдомены
- Применяйте OCSP Stapling для проверки отзыва сертификатов
Мониторинг и тестирование
- Регулярно проверяйте настройки TLS с помощью инструментов вроде SSL Labs
- Настройте мониторинг истечения срока действия сертификатов
Правильная настройка HTTPS и TLS является фундаментальной для безопасности веб-приложений на Axum. Использование современных библиотек и следование лучшим практикам позволит обеспечить надежную защиту передаваемых данных.