1
0
mirror of https://github.com/actix/examples synced 2025-06-26 17:17:42 +02:00

restructure folders

This commit is contained in:
Rob Ede
2022-02-18 02:01:48 +00:00
parent 4d8573c3fe
commit cc3d356209
201 changed files with 52 additions and 49 deletions

View File

@ -0,0 +1,14 @@
[package]
name = "awc_https"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = "4.0.0-rc.1"
awc = { version = "3.0.0-beta.19", features = ["rustls"] }
env_logger = "0.9"
log = "0.4"
mime = "0.3"
rustls = "0.20"
webpki-roots = "0.22"

View File

@ -0,0 +1,5 @@
The goal of this example is to show you how to use the `awc` for secure HTTPS communication using Rustls.
It uses best practices for efficient client set up and demonstrates how to increase the default payload limit.
This example downloads a 10MB image from Wikipedia when hitting a server route. `cargo run` this example and go to <http://localhost:8080/> in your browser.

View File

@ -0,0 +1,83 @@
use std::{sync::Arc, time::Instant};
use actix_web::{get, middleware, web::Data, App, HttpResponse, HttpServer};
use awc::{http::header, Client, Connector};
use rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore};
const MAP_URL: &str =
"https://upload.wikimedia.org/wikipedia/commons/f/ff/Pizigani_1367_Chart_10MB.jpg";
#[get("/")]
async fn fetch_image(client: Data<Client>) -> HttpResponse {
let start = Instant::now();
let mut res = client.get(MAP_URL).send().await.unwrap();
if !res.status().is_success() {
log::error!("Wikipedia did not return expected image");
return HttpResponse::InternalServerError().finish();
}
let payload = res
.body()
// expected image is larger than default body limit
.limit(20_000_000) // 20MB
.await
.unwrap();
log::info!(
"it took {}ms to download image to memory",
start.elapsed().as_millis()
);
HttpResponse::Ok()
.content_type(mime::IMAGE_JPEG)
.body(payload)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let client_tls_config = Arc::new(rustls_config());
log::info!("starting HTTP server at http://localhost:8080");
HttpServer::new(move || {
// create client _inside_ `HttpServer::new` closure to have one per worker thread
let client = Client::builder()
// Wikipedia requires a User-Agent header to make requests
.add_default_header((header::USER_AGENT, "awc-example/1.0"))
// a "connector" wraps the stream into an encrypted connection
.connector(Connector::new().rustls(Arc::clone(&client_tls_config)))
.finish();
App::new()
.wrap(middleware::Logger::default())
.app_data(Data::new(client))
.service(fetch_image)
})
.bind(("127.0.0.1", 8080))?
.workers(2)
.run()
.await
}
/// Create simple rustls client config from root certificates.
fn rustls_config() -> ClientConfig {
let mut root_store = RootCertStore::empty();
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(
|ta| {
OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
},
));
rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth()
}

View File

@ -0,0 +1,16 @@
[package]
name = "openssl-auto-le"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = { version = "4.0.0-rc.2", features = ["openssl"] }
actix-files = "0.6.0-beta.16"
acme-micro = "0.12"
anyhow = "1"
env_logger = "0.9"
futures-util = { version = "0.3.7", default-features = false, features = ["std"] }
log = "0.4"
openssl = { version = "0.10", features = ["v110"] }
reqwest = "0.11"

View File

@ -0,0 +1,5 @@
# Automatic Let's Encrypt TLS/SSL (via OpenSSL)
We use (acme-micro)[https://github.com/kpcyrd/acme-micro] to auto-generate TLS/SSL certificates via OpenSSL for a given domain.
Process is explained in code.

View File

@ -0,0 +1,198 @@
use std::{fs, time::Duration};
use acme_micro::{create_p384_key, Certificate, Directory, DirectoryUrl};
use actix_files::Files;
use actix_web::{rt, web, App, HttpRequest, HttpServer, Responder};
use anyhow::anyhow;
use openssl::{
pkey::PKey,
ssl::{SslAcceptor, SslMethod},
x509::X509,
};
pub async fn gen_tls_cert(
user_email: &str,
user_domain: &str,
) -> anyhow::Result<Certificate> {
// Create acme-challenge dir.
fs::create_dir("./acme-challenge").unwrap();
let domain = user_domain.to_string();
// Create temporary Actix Web server for ACME challenge.
let srv = HttpServer::new(|| {
App::new().service(
Files::new(
// HTTP route
"/.well-known/acme-challenge",
// Server's dir
"acme-challenge",
)
.show_files_listing(),
)
})
.bind((domain, 80))?
.shutdown_timeout(0)
.run();
let srv_handle = srv.handle();
let srv_task = rt::spawn(srv);
// Use DirectoryUrl::LetsEncryptStaging for dev/testing.
let url = DirectoryUrl::LetsEncrypt;
// Create a directory entrypoint.
let dir = Directory::from_url(url)?;
// Our contact addresses; note the `mailto:`
let user_email_mailto: String = "mailto:{email}".replace("{email}", user_email);
let contact = vec![user_email_mailto];
// Generate a private key and register an account with our ACME provider.
// We should write it to disk any use `load_account` afterwards.
let acc = dir.register_account(contact.clone())?;
// Load an account from string
let privkey = acc.acme_private_key_pem()?;
let acc = dir.load_account(&privkey, contact)?;
// Order a new TLS certificate for the domain.
let mut ord_new = acc.new_order(user_domain, &[])?;
// If the ownership of the domain have already been
// authorized in a previous order, we might be able to
// skip validation. The ACME API provider decides.
let ord_csr = loop {
// Are we done?
if let Some(ord_csr) = ord_new.confirm_validations() {
break ord_csr;
}
// Get the possible authorizations (for a single domain
// this will only be one element).
let auths = ord_new.authorizations()?;
// For HTTP, the challenge is a text file that needs to
// be placed in our web server's root:
//
// <mydomain>/acme-challenge/<token>
//
// The important thing is that it's accessible over the
// web for the domain we are trying to get a
// certificate for:
//
// http://mydomain.io/.well-known/acme-challenge/<token>
let chall = auths[0]
.http_challenge()
.ok_or(anyhow!("no HTTP challenge accessible"))?;
// The token is the filename.
let token = chall.http_token();
// The proof is the contents of the file
let proof = chall.http_proof()?;
// Place the file/contents in the correct place.
let path = format!("acme-challenge/{}", token);
fs::write(&path, &proof)?;
// After the file is accessible from the web, the calls
// this to tell the ACME API to start checking the
// existence of the proof.
//
// The order at ACME will change status to either
// confirm ownership of the domain, or fail due to the
// not finding the proof. To see the change, we poll
// the API with 5000 milliseconds wait between.
chall.validate(Duration::from_millis(5000))?;
// Update the state against the ACME API.
ord_new.refresh()?;
};
// Ownership is proven. Create a private key for
// the certificate. These are provided for convenience; we
// could provide our own keypair instead if we want.
let pkey_pri = create_p384_key()?;
// Submit the CSR. This causes the ACME provider to enter a
// state of "processing" that must be polled until the
// certificate is either issued or rejected. Again we poll
// for the status change.
let ord_cert = ord_csr.finalize_pkey(pkey_pri, Duration::from_millis(5000))?;
// Now download the certificate. Also stores the cert in
// the persistence.
let cert = ord_cert.download_cert()?;
// Stop temporary server for ACME challenge
srv_handle.stop(true).await;
srv_task.await??;
// Delete acme-challenge dir
fs::remove_dir_all("./acme-challenge")?;
Ok(cert)
}
// "Hello world" example
async fn index(_req: HttpRequest) -> impl Responder {
"Hello world!"
}
#[actix_web::main]
async fn main() -> anyhow::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
// IMPORTANT: Use your own email and domain!
let email = "example@example.com";
let domain = "mydomain.io";
// Load keys
// ==============================================
// = IMPORTANT: =
// = This process has to be repeated =
// = before the certificate expires (< 90 days) =
// ==============================================
// Obtain TLS certificate
let cert = gen_tls_cert(email, domain).await?;
let mut ssl_builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
// Get and add private key
let pkey_der = PKey::private_key_from_der(&cert.private_key_der()?)?;
ssl_builder.set_private_key(&pkey_der)?;
// Get and add certificate
let cert_der = X509::from_der(&cert.certificate_der()?)?;
ssl_builder.set_certificate(&cert_der)?;
// Get and add intermediate certificate to the chain
let icert_url = "https://letsencrypt.org/certs/lets-encrypt-r3.der";
let icert_bytes = reqwest::get(icert_url).await?.bytes().await?;
let intermediate_cert = X509::from_der(&icert_bytes)?;
ssl_builder.add_extra_chain_cert(intermediate_cert)?;
// NOTE:
// Storing pkey_der, cert_der and intermediate_cert somewhere
// (in order to avoid unnecessarily regeneration of TLS/SSL) is recommended
log::info!("starting HTTP server at http://localhost:443");
// Start HTTP server!
let srv = HttpServer::new(|| App::new().route("/", web::get().to(index)))
.bind_openssl((domain, 443), ssl_builder)?
.run();
let srv_handle = srv.handle();
let _auto_shutdown_task = rt::spawn(async move {
// Shutdown server every 4 weeks so that TLS certs can be regenerated if needed.
// This is only appropriate in contexts like Kubernetes which can orchestrate restarts.
rt::time::sleep(Duration::from_secs(60 * 60 * 24 * 28)).await;
srv_handle.stop(true).await;
});
srv.await?;
Ok(())
}

View File

@ -0,0 +1,9 @@
[package]
name = "openssl-example"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = { version = "4.0.0-beta.21", features = ["openssl"] }
env_logger = "0.9"
openssl = "0.10"

View File

@ -0,0 +1,34 @@
# tls example
## Usage
### Certificate
We put the self-signed certificate in this directory as an example
but your browser would complain that it isn't secure.
So we recommend to use [`mkcert`] to trust it. To use local CA, you should run:
```bash
mkcert -install
```
If you want to generate your own cert/private key file, then run:
```bash
mkcert 127.0.0.1
```
[`mkcert`]: https://github.com/FiloSottile/mkcert
### server
```bash
cd security/openssl
cargo run (or ``cargo watch -x run``)
# Started http server: 127.0.0.1:8443
```
### web client
- curl: ``curl -v https://127.0.0.1:8443/index.html --compressed -k``
- browser: [https://127.0.0.1:8443/index.html](https://127.0.0.1:8443/index.html)

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEDDCCAnSgAwIBAgIQLu2TV80hCgYgZe18ovEhmzANBgkqhkiG9w0BAQsFADBZ
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExFzAVBgNVBAsMDmh1eXV1
bWlAcmlyaWthMR4wHAYDVQQDDBVta2NlcnQgaHV5dXVtaUByaXJpa2EwHhcNMTkw
NjAxMDAwMDAwWhcNMzAwNTE5MDM0MjI2WjBCMScwJQYDVQQKEx5ta2NlcnQgZGV2
ZWxvcG1lbnQgY2VydGlmaWNhdGUxFzAVBgNVBAsMDmh1eXV1bWlAcmlyaWthMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5fRUIbEv2DjBmK7+syGVvh3I
FWDlVjU9N7ypxauQbXPHAzpATzghLnpm5CqQFoTnJwA4//A85775djcVlsAUqen2
ZYi+4jTYeuRLrAJ0dkrUS8/7+T0fGzGZ8obCsII5iSE2BMS7AxbqlQtClDdkNwcK
rCuzrmIyMA8Bc2V231xIgcWFJ7en8OaZJRlYYK7kp2cJ8g0PbPnVq+9TAfFYcKEy
FWqJsYYY36bLbWyqYXGMOtAh2bhy+YGYL3Jhk+cw7iMCjye4FbDAIQzt9cH1KGGM
2VWZFiwn6VJquX1Z+n9KAhfzxuzYQHSrlJ+Rt++gezpTtNw8q15Ko78oiu7CLQID
AQABo2cwZTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYD
VR0TAQH/BAIwADAfBgNVHSMEGDAWgBSljCjB0QNrBG+8BV3nFnUyBn54jjAPBgNV
HREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBgQAsUrfA8deCaHYy7wB1jEVK
pNZKRNcDKxqr/PXJQlfwwlq1qZTBzloMNTzfVBRkn/I7y+Bj/b1uYFmjQoQ3qG9s
tIXFCYOop1cLltmWXC479/UtbEmhz0t+mzK0MFkLhxtbKqwvMGbcGGDFI/2/MGZN
XFZXL1bclFieZxO5ePEkZSDkPcWvh9uYWCp8r7H6aAd/iwH4lDxfajyhDneRmd/v
Mq0PgqTZhVHOP7JdVNA+6cewROyPL7ElLs66ujE9hsRvs6eXLjgLZrHOZShnoQxK
JJv8UfoE90FX1uDt9w9i3raig/O3oePNkU263kJlR+J1rdVdYV+pCCb7L4Vk+1l3
S4VFVGVHN8x35dISCJwZrtnqPlfpCiLjtEJOu1zJUEY2Q0n7Km3z3zQcs6iCeOQi
O9MVJ4aiALdNvyCG7lL4+AJ/kWbwHFM6wOAKSrkpZ20msMuEgIlhCOi8PgYlKb+b
V/lV6IJPVrAOOclgcvtfZ/LdsTxn15yLIieqgR0Lf/s=
-----END CERTIFICATE-----

28
https-tls/openssl/key.pem Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDl9FQhsS/YOMGY
rv6zIZW+HcgVYOVWNT03vKnFq5Btc8cDOkBPOCEuembkKpAWhOcnADj/8Dznvvl2
NxWWwBSp6fZliL7iNNh65EusAnR2StRLz/v5PR8bMZnyhsKwgjmJITYExLsDFuqV
C0KUN2Q3BwqsK7OuYjIwDwFzZXbfXEiBxYUnt6fw5pklGVhgruSnZwnyDQ9s+dWr
71MB8VhwoTIVaomxhhjfpsttbKphcYw60CHZuHL5gZgvcmGT5zDuIwKPJ7gVsMAh
DO31wfUoYYzZVZkWLCfpUmq5fVn6f0oCF/PG7NhAdKuUn5G376B7OlO03DyrXkqj
vyiK7sItAgMBAAECggEBAIabZmAukz4zwwe4cDm1kC0wy73P8Y9sLMCivJKMYkff
vQBjqd91kN7fIbmwPJYiCBlpZPRU0aIqxWZwyj9rgu0Pmn9G884AdzRAzRcMfNX9
6ZXTUsFMCRhnCaHRRsgCAuIFwdQ6wOoHERxb8gZHAm+/vHyaPFz4+D3vmr7NBy+p
fgpdDCGwkltKI73efk6H4oAeyztDwNev/TZ3Y+O3UKuAUfVReBX0us/lYgEf/KXV
USd7envxACy+PDcqmn/HL6IUnbrc1zB92dmSSUUtLjOz//z1zM05ME/E2keMQwsW
7LDen0Lm0Nh6AcOCxmnN4u0lJ3nWzU7PsHJKY/LznPUCgYEA5kQq/AG6LlL9Iu1E
Y55AB1rkvseof41liaqXVccB5tr55IF75d0wPd6jF04W+x6LwmL6EUYPRGkkOdHE
raz9CDE3a7hWbAghxIwLrI1s+faT5aaHG9o6mTeDyfgEEoBg8X2nQHQJPayDJZcW
kiXQyHEtj/G4m/Y+WDFXastZz8MCgYEA/6c8+cI3Slvg3CZLG9f6rdAlrOSIQynF
muXyVeUaxrU0OPC9H3WEwWv7n4adQU2g0L6TBTdlOOiv5SPIsOPQVN2JZVdxwg0V
n5+7/WRrI9rAXnmu1x0q1e/TZ9Msggmrn5SdHaEfuug4DnHv4nct356joDwYB2i3
xYl+yCSMd08CgYEAqduvOaasiG9/e7w6rqGV6dcK1hDCIxVSyXKloAjlRj5SCFXb
53x6kakh9ZcNLMEjp4kLnqJnsLc+mcg7pUHuhZSIpVWdqqN1BV+pXOgWc22JO+bT
05/vigaBmQLzPhKlcH6YWds+1dfkBl6lr7llgfa6/Wv6GlJTOwtqyMSow7ECgYEA
zGQ8j8ICymRihh/ndL9cH5KGTI/5kRjYb1rgQGQG4E8HDW8LBRfDp5BZf9Tz7L3P
kJSMnmMHflQqLJxLW4EHkpH7wxYCUQ589z2R4qhiMCw4GFBYxIsBMEGpVxyyPNTW
baM3afTjlV8LUiEtlHWMK3h9gSIKZAIIytl+jy0JUGkCgYA3wrpcG3wgXeuEtoie
ve/kFS5JRaOeV/9OLE2JGaGaumPlN0L14kCVvb6uqLa/P88BwBUxvGQ7FDBdh4sk
ypuSe9ZPCNDgnsbnfM8QgFqIW6MDdizLtj7no1SKeaUU3JWWc0kH2KWMw/sYZ7ec
0tcEInxEd7FbssGfMqF9fQtnNw==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,44 @@
use std::io;
use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
/// simple handle
async fn index(req: HttpRequest) -> Result<HttpResponse, Error> {
println!("{:?}", req);
Ok(HttpResponse::Ok()
.content_type("text/plain")
.body("Welcome!"))
}
#[actix_web::main]
async fn main() -> io::Result<()> {
std::env::set_var("RUST_LOG", "actix_web=debug");
env_logger::init();
println!("Started http server: 127.0.0.1:8443");
// load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder
.set_private_key_file("key.pem", SslFiletype::PEM)
.unwrap();
builder.set_certificate_chain_file("cert.pem").unwrap();
HttpServer::new(|| {
App::new()
// enable logger
.wrap(middleware::Logger::default())
// register simple handler, handle all methods
.service(web::resource("/index.html").to(index))
// with path parameters
.service(web::resource("/").route(web::get().to(|| async {
HttpResponse::Found()
.append_header(("LOCATION", "/index.html"))
.finish()
})))
})
.bind_openssl("127.0.0.1:8443", builder)?
.run()
.await
}

View File

@ -0,0 +1,12 @@
[package]
name = "rustls-client-cert"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-tls = "3.0"
actix-web = { version = "4.0.0-rc.1", features = ["rustls"] }
env_logger = "0.9"
log = "0.4"
rustls = "0.20.2"
rustls-pemfile = "0.2.1"

View File

@ -0,0 +1,33 @@
# Access Client Certificate (via Rustls)
## Usage
### Certificate
All the self-signed certificate are in the ./certs directory, including the CA certificate
generated by [`mkcert`] that was used to create the server and client certs.
### Server
```sh
cd security/rustls-client-cert
cargo run
```
The server runs HTTP on port 8080 and HTTPS on port 8443.
### Providing Client Cert
Using [HTTPie]:
```sh
http https://127.0.0.1:8443/ --verify=certs/rootCA.pem --cert=certs/client-cert.pem --cert-key=certs/client-key.pem
```
Using [cURL]:
```sh
curl https://127.0.0.1:8443/ --cacert certs/rootCA.pem --cert certs/client-cert.pem --key certs/client-key.pem
```
[`mkcert`]: https://github.com/FiloSottile/mkcert
[cURL]: https://curl.haxx.se/
[HTTPie]: https://httpie.org/

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEVDCCArygAwIBAgIRAOAgy58Y3ViVpV9G8DTyKzMwDQYJKoZIhvcNAQELBQAw
bTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSEwHwYDVQQLDBhyb2JA
c29tYnJhLng1Mi5kZXYgKFJvYikxKDAmBgNVBAMMH21rY2VydCByb2JAc29tYnJh
Lng1Mi5kZXYgKFJvYikwHhcNMTkwNjAxMDAwMDAwWhcNMzAxMDI1MTczODMxWjBM
MScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxITAfBgNV
BAsMGHJvYkBzb21icmEueDUyLmRldiAoUm9iKTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAJsp9QFEpfEWFB3CyFTA2Rv2tUMwnpQcDtiB1hwH03EYcMlG
pEkMh1tPTK8WZo2igMJrBtP2Vf2AN0/hmFWUZV1ZEUUNXXW0QD2mHS8Rgz7nAgmq
V5XvmLLeeo2vMdw1B2qsRxCPTjbInDsZsBqv2GyXWo5/9o3PD32h4LNk3w0VyA47
f/jdpMWlcIXQoyJJV1U1FPLf92xYZvWc9Vf/+K6mStESEpoFll+b4uqjPpwrEz9Q
KBY4eyXwhGCrjQC0+jJFNlIcbV5FgQSYd4DVMcw6SWdMeV/+VtQs+JQrENjNB3am
nJ5xpoZ7mmNDOkWg4zvoYRL6o7LtqT+8EXptzMMCAwEAAaOBjzCBjDAOBgNVHQ8B
Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB
/wQCMAAwHwYDVR0jBBgwFoAUfpWkO5lYpQJz4omoVdVzuuZnn80wLAYDVR0RBCUw
I4IJbG9jYWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEB
CwUAA4IBgQB8ulsrStRha8SRxoPRlTNIb9WMjNl5KdZF+ePpUctBX0lFmxqn0upu
l3BoZkhMrpPRRcGlDPImuL7tfxk/IuTA6S1AEQtEHCC8WyZbq3RODPzVY6J/IOUv
H2ZwZYo0c714FyNx8igBpVSjHT6yCIeQkSQlTXsWxddToSeKYvXg8VI+M5L0DH1Y
jRb2u3GpMPdLhMqGNZPcwbLkVyMe5aj4hx334fa3uLf/CK6/5ev4+ozSNz3Qr8S7
iPE8WXrny/qEJmTEme7eq6K/QPTC9ly3j+5Ms3Uepnk0Jez9/ksOrlbYrWXhwD0P
Nwvn3HtiYy8q1HwRw5U+LMNyh6lIyfJUsu2tRmYz1fiH74tMDFb2pjDpcmvgaSJY
eSwKksOiX1No6K980ECEkCX9iQFwD5edTCnD2lz+AVGDzZPkY/551Ohl+KMh/mFG
uZNtgjUeMh0btfc9D+PLLLHjpSMVkRMEEmtm/Zi2nKeVCAdIomFfk+EFIEOdaMyk
SUZMUFzBP+4=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCbKfUBRKXxFhQd
wshUwNkb9rVDMJ6UHA7YgdYcB9NxGHDJRqRJDIdbT0yvFmaNooDCawbT9lX9gDdP
4ZhVlGVdWRFFDV11tEA9ph0vEYM+5wIJqleV75iy3nqNrzHcNQdqrEcQj042yJw7
GbAar9hsl1qOf/aNzw99oeCzZN8NFcgOO3/43aTFpXCF0KMiSVdVNRTy3/dsWGb1
nPVX//iupkrREhKaBZZfm+Lqoz6cKxM/UCgWOHsl8IRgq40AtPoyRTZSHG1eRYEE
mHeA1THMOklnTHlf/lbULPiUKxDYzQd2ppyecaaGe5pjQzpFoOM76GES+qOy7ak/
vBF6bczDAgMBAAECggEAOAzN/mlH8HDjT2TfbQ5E3z8dd2oGvsXr7vXn3DRPgxkr
FOo5ylGGS8u3jb/vx9Yd4R08WDEyGuLIGEkL+E4k17Wm8WTEkBJO6nYpMKmI2Z4l
NEyeWwE0DdkkOC5DOqLFTRlyil7aCBwro4XCAcHn0kJSagt7ivpzRBTH0MXtdRIL
L37nOInga2puSeHLOGj9W1Q0AX0EIiecqLPpCLmz+xotlevFBmlQ4iFT7xkoKH+s
NeqWEd0HTNYkYQZtZ9husDzNK4x59MKUYS1oWB8A8JKu5E4ovdb6R6LjyN/Y5lNl
3Z4Mg2DJ4iBN4DRnEBwiY8bdeqYboMNNyAbCU+nPYQKBgQDLP0lEYsdcSNF5Uv0c
fe5pUvAzyTUzVhP7LLOk2jdRxs85MNbYJkays40rROqm2+StSFgih3NpA9D1pl7f
ucJABlK9OkTtOshFSR6Q+0NomKLHhBGvYpouC2tIHjGsE/KjiQa4I1Pia72xpIWT
QK3isM2c11924RqdoJ0mpBKpOwKBgQDDb8k4U512u+exVvFQ8JEbro4oy4jsc74L
BZG6PbHZcIADKANmB15ahN3tCkDwJQ8iJYX7bmzn+8jR2HtznchgJwWN9aGtEP4C
qTmmyrJd09pMMs5W4/BucD7wJ7s32PwVUwj0nvW+BHE4LQk5td4a3VE1wj1EZ8bR
+JCK081yGQKBgQCG1vKtuvbWY70NR6CCwY1lNNAFG0z/RPE2pz57dQZUa5hZMeyn
NCWdMv/KIAupLaN1ztQh/Ej198Eu5/RzaxLTVR6ZScomtOPfC5aKyxe6keEUiqsM
91agi5TeIKIFrqpAEo1xpoZlZeNQsnyn57Y0+eU/U6eS/96fisoXs6xptwKBgQCy
loDqnsXCTR0AmbE0RFBlWlH/h1Ycs/tSeQlchanyR3JC8BqC7nLTBp0BUaBbkZHN
ozpWsuaD6jntQAQ3d1Q2QRI7Ud0ml+N1rQvIlWr+gGv3u2mATxRS5vEsZTIYa4iX
N+R0HJn0xHxtWAIZIU7Pf7QJCvEPhEFTd+8lXTxcaQKBgG5d4vG5AyYXuiKKmm+d
MM4PPMJNw3+u5G50hgI4D/1WlirP9m6HIpFouNNGoggxpBBUeokIBPsXbq/fnkq2
uZmkAkWZeMeWHbI0lHSsE98ZTQHg36dNJ9hRHVRJ9PA2/YTSnr3WzDaaA1v5aoTv
xJyGdLlEy+RiIZCPk9RKtVc4
-----END PRIVATE KEY-----

View File

@ -0,0 +1,40 @@
-----BEGIN PRIVATE KEY-----
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
nctls65dJGslcxgwD6O1rA==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEqTCCAxGgAwIBAgIQL82W4ilLJ4cs8zMbhJ+7aTANBgkqhkiG9w0BAQsFADBt
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExITAfBgNVBAsMGHJvYkBz
b21icmEueDUyLmRldiAoUm9iKTEoMCYGA1UEAwwfbWtjZXJ0IHJvYkBzb21icmEu
eDUyLmRldiAoUm9iKTAeFw0yMDEwMjUxNzIyMzlaFw0zMDEwMjUxNjIyMzlaMG0x
HjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEhMB8GA1UECwwYcm9iQHNv
bWJyYS54NTIuZGV2IChSb2IpMSgwJgYDVQQDDB9ta2NlcnQgcm9iQHNvbWJyYS54
NTIuZGV2IChSb2IpMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAq8NA
5lGYNYUxCknpFKQ14RBJlbEgjcfHIi8hp9TobzUigyOQoZ9W50WmChRuuAO/fXIG
i8ioejoteNPVbJj7ggMKx13256cvDKVR79NKeoHBSYUUjs2NyZZ3zPUeUe0tXPHR
J4RMWLmpYKdI6NCYjDX6ouCIeh6sinTOSV+0dbnfHvFGESFpUeiUdsnCRorsa/oB
n+hIn5dvftuxSDYDbADsSLDqhxY4xsQJKP00uNysLP0vewBqTSULNeHMNf0Yhpiy
ot/tbcRqQAzSzRIvWeCgw0GcjqPW27/p/x/4Sh3XzjT6w1xVtF7ZniW37M4gQktN
HnMWVgceqo0TaCwo+B37XJa/fBRH0x8HnMda1JeZ7X8/Szyc7YEa1M6ZkKONeCEg
C7+8jp/aTM/0DuvIIhxHwK/9PGvWYDFJny2Jf8pq1jJ66Gr0uWxRJHobf2kB+aF3
KUs5nK49Kw+TmZcB+SbU256e2wPhusxqxXnYi9WjdIyCqF3B1hYxEiaRiFRTAgMB
AAGjRTBDMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud
DgQWBBR+laQ7mVilAnPiiahV1XO65mefzTANBgkqhkiG9w0BAQsFAAOCAYEAe3A5
5z5JWHSXEcQI/LstAVJfiloehyhvYgcNpImlsm5A41VzMd1iWdpLY+WSKWjHKFs0
u5Id+sGEZRHBicYDts0azH3Sklj0FrNrpRdvf8jJuMvVC1EoUFijG+V/RdL14bxq
kyp5dtxVcvtvULT+ID5vSVYBwGOBp36Fg5Qs4QOuSPkVGwjaEdJvzqb1XDLncsve
ap6ALzwOXUsN8Icrq4/GlVDX11rOnfWhNQRiWKJtin4vzxL002rPWwSsekcuLEFQ
NCBYaBBDlp4fLh2XVuB7xvKXTPm8K9hIxrTBEh8kj8p5p95DOTWpLgXuBcdXmNDV
/4NBzsWwuO3I7/ilHbH+gEsM390p056Bmv2cF0bWb+xT2vyNymhmSCn77QEmcsOF
LDqsjDc7JsQGUVIAgtM50md/QvaXxTVoAMXtSWeyK8QsC2syZqWlXiYkrwYjp+KI
6jX4EpLwKdQOzZdjgnIZvG94o5lR8cPyZlaUKVunOTvAXiT3AWeBppCk6SEH
-----END CERTIFICATE-----

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIESjCCArKgAwIBAgIRAKNTHdWsrKKCpKt19C/sI4UwDQYJKoZIhvcNAQELBQAw
bTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSEwHwYDVQQLDBhyb2JA
c29tYnJhLng1Mi5kZXYgKFJvYikxKDAmBgNVBAMMH21rY2VydCByb2JAc29tYnJh
Lng1Mi5kZXYgKFJvYikwHhcNMTkwNjAxMDAwMDAwWhcNMzAxMDI1MTczODM4WjBM
MScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxITAfBgNV
BAsMGHJvYkBzb21icmEueDUyLmRldiAoUm9iKTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBANDv0oRupAGtAl9ZZTeTwdToYh/Gtaj3bAyhQQz+dQ5GzLS3
+Zvo3daepwnoNABoxuPwptb/jJ6Ec8rdvUiJkKb+jAuZ0vSuEqPTcgvbnqsV+7UT
8WleCUnOIY6FB+uTaLEptu5k+Pf7i3m1RqW1gRVlYcHsLuv3NBQW0bz+XUZvcirW
KVCxz2ex2aFzWXrAZEuOwMX+x+Wicd4tRfcio4mOI+jCRCsbwW0TsyWStrimcbl0
ldbRi8DeADf3VPQCMYmcJARYsTfRwK75OFMgmD0GTi8WURWQCbTJnwzop7Famno0
/I/Ef65Dngleb6HwG91EtEVU7QHqSXlUK41Au9kCAwEAAaOBhTCBgjAOBgNVHQ8B
Af8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAfBgNV
HSMEGDAWgBR+laQ7mVilAnPiiahV1XO65mefzTAsBgNVHREEJTAjgglsb2NhbGhv
c3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggGBAJJ0
7oDkVmdB/P0PX0vIn+CPmiE8IrO8stItOGlVRy9TDWFRdMpY13BOjlNY2efNU2Tz
or+lyfV7D8PiCk5q3e8sLtOlIAT32IRIrGXl5E4q7zDnOckQjZIUZwSFAFVygy4F
rPgJCS9uqz9vz086SyRD0krTM5u9yMAvG2uJLEk5oaVWAKdDRplStmMx7QQHMZ4G
iNYMDpj4dU4gkrvZeC+JKwjbSJ3hje8CZCA1atzz/5WWEt2D8Yf9tw27T/hinrMi
rrjPpEcA6C8wvHxqYpiptQC1FF5vaUyRqjF+irHgQdNUCE0nJSwAsIDW0TDiVGDD
OJKs6EYuzJ8OcWw6IFx8YNvdxZJSr8SaavrH76myHieTkqhWvjSQERUw71iUwe0q
5Nev+J2N9U7oZBTCIsD/qKOQTD9mRjJpYLnXNqEyJhzRgCA5+TQIXbTa10eo9Svg
CTkqxsixmdTKD8ZIlUPhLI9ehr0Spbt+2Xh3yAtlSrfAt5p2hgVCoxalEwWpwA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDQ79KEbqQBrQJf
WWU3k8HU6GIfxrWo92wMoUEM/nUORsy0t/mb6N3WnqcJ6DQAaMbj8KbW/4yehHPK
3b1IiZCm/owLmdL0rhKj03IL256rFfu1E/FpXglJziGOhQfrk2ixKbbuZPj3+4t5
tUaltYEVZWHB7C7r9zQUFtG8/l1Gb3Iq1ilQsc9nsdmhc1l6wGRLjsDF/sflonHe
LUX3IqOJjiPowkQrG8FtE7Mlkra4pnG5dJXW0YvA3gA391T0AjGJnCQEWLE30cCu
+ThTIJg9Bk4vFlEVkAm0yZ8M6KexWpp6NPyPxH+uQ54JXm+h8BvdRLRFVO0B6kl5
VCuNQLvZAgMBAAECggEAZ8PzZVodkcd+uiQHpMZ5KWgP84AYkEXxH4SmbfGh6Ok4
mTgVe1z3b7NzefQcf2eJ/4JxGa3pBID7kPD3JKNNd+JLYyeOIX6iO0K0DTeRYZ6V
V6UD7DSLoGADQmjHOjQHdpUlCNiU3XPMHTHuWDYAWcPz/P9zfjVTY/bBS394i5W/
nFLHMEGD/9KETUm2Wdmu+84fhAg+gSbDOHKOmrqiRoSmBInfUEjTL22gU1hPkjWY
17i9veMGVPQeA89XYmFavgWIMHwiOD0Fom2y26OIDWDXAJf7oOoONElWF2OjiSAa
7IHl8EED/aDIt9Xyv8J9qXzkAtLPXO8vFG2Uq0/BQQKBgQD1MlCsnMVf3dck8yg6
YwFkFHZxnFBzBPwuDoMD2OBIZcrwcBhUQo0u8M0NohgjvBB7XUrW+/5ucF9BaecC
3Rz/JwPk7oPafYVbEFGv+CeGJFKL/lq7NwF62N7EYo3N2ijYjGYiZFmxgOVEfzSd
WRSH+b6Q923lEpWoFaeFww+5HQKBgQDaJIRTr1stXXFXVj/ETJG4uT3I7qlfR7zp
/t/YHX6QnV5dFdBCEXSW41YoqvUn12Ox28nvQMDFWVkQGfAELYqkf/ozBQ9d6ee8
3bU9V14YWaGSK1RT83cgWUtDnUjtLuk9MG+5x3EQcfq5ng6ud+Hr8ut+gz0PbLcK
gR3uTQkM7QKBgENUSarccaZdeFKBIq7FuQAOTmfsEHtFBypuebN4vj2jFhLn9QZO
MA6PuP7hX9eQMMZvW7mlALy6xq1jszeqF+hIgl0+0Z4Rkajr1kKH1fKTzsb6VVfL
RvDA1IsAtQetvEGabAFNpQOE7W/drkj2yRh9j8Km7tpUPBwnthSY95xRAoGAL5uu
oZDwxjrRhK0XJ6FKYFVVTagAoQHIEg2FDuaI+8jMkmYzWGf02QVuhVuiO3q/kE6W
iLfKGogAbwoqHs9Npc8kbMQa8XUeFVMRvfx2VbwiDgOU2OmWwyGZ0nzeMJ1/W9JZ
X+NOIretb5s2Ow+A5/zRNoAv8FDatdkuDhcT9ZUCgYBtEI9pmWYXxGfaVQErJcsS
3a8PU875sE06DiEcvooljcLp6dCmGocRQOGpSXfy3zjX4F/HZjDK+GKPg2bCirKr
nRPWf0t3DSeSyDINj+a0fg4KvYR4FcocJDzTFJh/9CrtNktqwHAqrQUSokBbW8n6
Kj/AYLWOIXATfCrOgdoAAg==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,120 @@
//! This example shows how to use `actix_web::HttpServer::on_connect` to access client certificates
//! pass them to a handler through connection-local data.
use std::{any::Any, env, fs::File, io::BufReader, net::SocketAddr};
use actix_tls::accept::rustls::{reexports::ServerConfig, TlsStream};
use actix_web::{
dev::Extensions, rt::net::TcpStream, web, App, HttpRequest, HttpResponse,
HttpServer, Responder,
};
use log::info;
use rustls::{
server::AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey,
RootCertStore,
};
use rustls_pemfile::{certs, pkcs8_private_keys};
const CA_CERT: &str = "certs/rootCA.pem";
const SERVER_CERT: &str = "certs/server-cert.pem";
const SERVER_KEY: &str = "certs/server-key.pem";
#[allow(dead_code)] // it is debug printed
#[derive(Debug, Clone)]
struct ConnectionInfo {
bind: SocketAddr,
peer: SocketAddr,
ttl: Option<u32>,
}
async fn route_whoami(req: HttpRequest) -> impl Responder {
let conn_info = req.conn_data::<ConnectionInfo>().unwrap();
let client_cert = req.conn_data::<Certificate>();
if let Some(cert) = client_cert {
HttpResponse::Ok().body(format!("{:?}\n\n{:?}", &conn_info, &cert))
} else {
HttpResponse::Unauthorized().body("No client certificate provided.")
}
}
fn get_client_cert(connection: &dyn Any, data: &mut Extensions) {
if let Some(tls_socket) = connection.downcast_ref::<TlsStream<TcpStream>>() {
info!("TLS on_connect");
let (socket, tls_session) = tls_socket.get_ref();
data.insert(ConnectionInfo {
bind: socket.local_addr().unwrap(),
peer: socket.peer_addr().unwrap(),
ttl: socket.ttl().ok(),
});
if let Some(certs) = tls_session.peer_certificates() {
info!("client certificate found");
// insert a `rustls::Certificate` into request data
data.insert(certs.last().unwrap().clone());
}
} else if let Some(socket) = connection.downcast_ref::<TcpStream>() {
info!("plaintext on_connect");
data.insert(ConnectionInfo {
bind: socket.local_addr().unwrap(),
peer: socket.peer_addr().unwrap(),
ttl: socket.ttl().ok(),
});
} else {
unreachable!("socket should be TLS or plaintext");
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "info");
}
env_logger::init();
let mut cert_store = RootCertStore::empty();
// import CA cert
let ca_cert = &mut BufReader::new(File::open(CA_CERT)?);
let ca_cert = Certificate(certs(ca_cert).unwrap()[0].clone());
cert_store
.add(&ca_cert)
.expect("root CA not added to store");
// set up client authentication requirements
let client_auth = AllowAnyAnonymousOrAuthenticatedClient::new(cert_store);
let config = ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(client_auth);
// import server cert and key
let cert_file = &mut BufReader::new(File::open(SERVER_CERT)?);
let key_file = &mut BufReader::new(File::open(SERVER_KEY)?);
let cert_chain = certs(cert_file)
.unwrap()
.into_iter()
.map(Certificate)
.collect();
let mut keys: Vec<PrivateKey> = pkcs8_private_keys(key_file)
.unwrap()
.into_iter()
.map(PrivateKey)
.collect();
let config = config.with_single_cert(cert_chain, keys.remove(0)).unwrap();
// start server
HttpServer::new(|| App::new().default_service(web::to(route_whoami)))
.on_connect(get_client_cert)
.bind(("localhost", 8080))?
.bind_rustls(("localhost", 8443), config)?
.workers(1)
.run()
.await
}

View File

@ -0,0 +1,15 @@
[package]
name = "rustls-example"
version = "1.0.0"
edition = "2021"
[[bin]]
name = "rustls-server"
path = "src/main.rs"
[dependencies]
env_logger = "0.9"
rustls = "0.20.2"
rustls-pemfile = "0.2.1"
actix-web = { version = "4.0.0-beta.21", features = ["rustls"] }
actix-files = "0.6.0-beta.15"

View File

@ -0,0 +1,43 @@
# TLS / HTTPS (via Rustls)
## Usage
### Certificate
We put the self-signed certificate in this directory as an example
but your browser would complain that it isn't secure.
So we recommend to use [`mkcert`] to trust it. To use local CA, you should run:
```bash
mkcert -install
```
If you want to generate your own cert/private key file, then run:
```bash
mkcert 127.0.0.1 localhost
```
For `rsa` keys use `rsa_private_keys` function instead `pkcs8_private_keys`
```rs
let mut keys = pkcs8_private_keys(key_file).unwrap(); // pkcs8
let mut keys = rsa_private_keys(key_file).unwrap(); // rsa
```
[`mkcert`]: https://github.com/FiloSottile/mkcert
### Running the Example Server
```bash
cd security/rustls
cargo run # (or ``cargo watch -x run``)
# Started http server: 127.0.0.1:8443
```
If you prefer reloading you can substitute `cargo watch -x run`.
That requires you install the `cargo-watch` crate.
### web client
- curl: ``curl -v https://127.0.0.1:8443/index.html --compressed -k``
- browser: [https://127.0.0.1:8443/index.html](https://127.0.0.1:8443/index.html)

25
https-tls/rustls/cert.pem Normal file
View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEJzCCAo+gAwIBAgIQKu5MWHrdyO4HsnfIu8alTDANBgkqhkiG9w0BAQsFADBt
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExITAfBgNVBAsMGHJvYkBz
b21icmEueDUyLmRldiAoUm9iKTEoMCYGA1UEAwwfbWtjZXJ0IHJvYkBzb21icmEu
eDUyLmRldiAoUm9iKTAeFw0yMTEwMDYyMTMxMzNaFw0yNDAxMDYyMjMxMzNaMEwx
JzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEhMB8GA1UE
CwwYcm9iQHNvbWJyYS54NTIuZGV2IChSb2IpMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAoI9BHaflPrNfnGKO6WmaEwhXfKKBH9sWlo4NKdP9ECZTC2Ef
ubvQzhjcJsPWIwYj1NDiAa11WfD6ayKG7YleoNynsDKnsOEBfXtFHU2IPWaESX4Q
rO8OaTXx001qdjwE3j/+K0AD43umXdnCeks3JYYlyG4/XxKa62pmpwu6KMgKbygA
MS3dIMe7WcYbKX+qPNl4xoF5xkeqlp2urO3SWPkgIYB+cDNsWRHb5vsMWw9s7Zos
W4mWAPZz0bLKw6w6imfo0rq0j5aoPJLNAyuH3/qhZIZC13tUCAxymIq0+pCeO+lZ
f0OC05dB/Hw1zSLxAxHgDzpOsaq9/NXSkIwEzwIDAQABo2QwYjAOBgNVHQ8BAf8E
BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUto/ox0MqZShm
QpViV/gjfJKrMDkwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3
DQEBCwUAA4IBgQCxMiND9F3xsyGlIyqIgHc+fp+wzFI5Yz9qD/02RP558qXHAj2o
6zGECzc4PeiBLh7Y7wHjcu4TTLIXnRtrdVFBUT/s58l/uoK8NGVjky74Rc4A+djt
zwcHS0snuj+FJ859Y+uS3rGKAmBAKWD22wmhB96UNRiZjG1QdJ/Or6hMZ3PVbELs
Hgv69UG1jJiL8y7cn4foBXC6Wgb10tPXNoz7TpD3B14+Pd82yergAHswCp3nj9Ip
D+9Ohko26OItO1dJYeDZWi0CurWdjP7xnEsZo2OaLIlSMiUbSyJOCMk/xWJCjuLW
BEc1VzaFwhkGZJUa1F6TOIc70geLC4wQWOaqZoLbsQfihYgRoUMZJOmjcDXJrNZz
wZofnBI+0tDsZfKjwXFyA4bzUD1I3lFY5Zy3wgQprUrZCm69uo8G4RtMWP9DmXCc
SEw6CxBVPu/l/ljYoxdqCyJTLvdQ97OlGgLv3b0DDcWqi7e0zB8NqT0aCTPm7J/M
OBWicNgMJ+1qL8M=
-----END CERTIFICATE-----

28
https-tls/rustls/key.pem Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgj0Edp+U+s1+c
Yo7paZoTCFd8ooEf2xaWjg0p0/0QJlMLYR+5u9DOGNwmw9YjBiPU0OIBrXVZ8Ppr
IobtiV6g3KewMqew4QF9e0UdTYg9ZoRJfhCs7w5pNfHTTWp2PATeP/4rQAPje6Zd
2cJ6SzclhiXIbj9fEprramanC7ooyApvKAAxLd0gx7tZxhspf6o82XjGgXnGR6qW
na6s7dJY+SAhgH5wM2xZEdvm+wxbD2ztmixbiZYA9nPRssrDrDqKZ+jSurSPlqg8
ks0DK4ff+qFkhkLXe1QIDHKYirT6kJ476Vl/Q4LTl0H8fDXNIvEDEeAPOk6xqr38
1dKQjATPAgMBAAECggEAVBfTvgmSuw1NtWW1fjDuHqvOzpt6T8n7Aa2y3UaHk67O
7fXXnPruuRMyMyd8/2kW2T7yMHi+LvZU4kn6K204X75SIanWRIEEu8kVgOx7v9Ty
0l8xsrGedaJoXwh8CyMSValkoRhtMPcxQpRsFItSfdfN8DU2AcCH3WckDrfIr9SJ
qvag8VsYeg/PH3rP3bNAh4xousaJzcvr8ifuNcN7NmoUDMoTXk3Pxhxeryj+sACS
cFxt777edShuYqL2BAziY/cTl0zcvCarX27NUS+q9exF7VYvMCuqiWHYcYkLlkH1
UfrwPXQmdX5/CUBqt36xBsKyub5j74KoEk7shzOmkQKBgQDKTr0vc+53QNUR1mUD
7a8Pw+oWW1ddcd9SYtvzEJeNqb7s2aZsEzTRk4Pxdx3wrm8PAaPqjzJWwx1SmazU
iLt55SRFu3sPw8gTwNQj01fy2roae/ZzMP4MJRzw6vFtNPPcevLQK9JN9uKBQep+
NU3xHYNYnT2I+X7QVJi6AsMwxwKBgQDLLA6iOwN+3aQmLlW1A4reRpIkFQ75RD92
BtCnYQwXCqOtU4uUz3fIlmcuCI5jhqAYWG0m9IL+rxQD2SdFu9UaG1pEsMkapjUh
+mPLAm3UcoqnhKygGiiQ8iPL9zMFai3dfbBYrmBMsYgFxT7wkPuAgjWM0bvfyUqA
lwKrkykTuQKBgHdSZacdW6MerA0vRLlCcSR9Sw4QpcDJrwwqnswIFztIyQFthgjs
cxTBSusadKBGYd6Z+xIXj3s47YyQcy2Pz/OfQPuYDodH1DRCYV0YBCGK/IUuZDeg
x9Zl9WHrUKY2uzZpldlOX2X4nbPbKvFxgx0ZaSTU6Txm23MI0mOzyWh1AoGBAJYu
jvKkpMTWmUwP3BLd93yutcAuQM9I/5ADIaFYP1OY7bxlkTwC0AxaARMqB/bRwO2+
D5FIFLymNilSD5GgcrnFlkhIVZ95VLU1HScnOIBd2thRXjlKnMnn80YGCJTsE9Mx
4XTsEQsf/+gkEY5J3V704RiiwDl/1a6P8c1aDnchAoGALEDzByXeADMiYjKi6M19
1WK3+TDD9Sy8fu4x2qmTho9Z9nk5bw6ZPHbXDTaQ+jxnOD4Io6iZIQLEYMwzbXnO
951+ck9E5mwWo/IyNROOMo0aNT9yqLANu5Hp1CliQ5Yqmb1R1Qhuk4SZTWmUGjo/
3I+uWHi2Foc2FU8LSAb4hLk=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,64 @@
use std::fs::File;
use std::io::BufReader;
use actix_files::Files;
use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer};
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls_pemfile::{certs, pkcs8_private_keys};
/// simple handle
async fn index(req: HttpRequest) -> HttpResponse {
println!("{:?}", req);
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body("<!DOCTYPE html><html><body><p>Welcome!</p></body></html>")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "actix_web=info");
}
env_logger::init();
// load ssl keys
let config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth();
let cert_file = &mut BufReader::new(File::open("cert.pem").unwrap());
let key_file = &mut BufReader::new(File::open("key.pem").unwrap());
let cert_chain = certs(cert_file)
.unwrap()
.into_iter()
.map(Certificate)
.collect();
let mut keys: Vec<PrivateKey> = pkcs8_private_keys(key_file)
.unwrap()
.into_iter()
.map(PrivateKey)
.collect();
if keys.is_empty() {
eprintln!("Could not locate PKCS 8 private keys.");
std::process::exit(1);
}
let config = config.with_single_cert(cert_chain, keys.remove(0)).unwrap();
println!("Starting https server: 127.0.0.1:8443");
HttpServer::new(|| {
App::new()
// enable logger
.wrap(middleware::Logger::default())
// register simple handler, handle all methods
.service(web::resource("/index.html").to(index))
// with path parameters
.service(web::resource("/").route(web::get().to(|| async {
HttpResponse::Found()
.append_header(("LOCATION", "/index.html"))
.finish()
})))
.service(Files::new("/static", "static"))
})
.bind_rustls("127.0.0.1:8443", config)?
.run()
.await
}