From c4df0d3fdfd0726d9b400d1e6569c848897c10b0 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Mon, 4 Jan 2021 02:01:15 +0100 Subject: [PATCH] Merge pull request #26 from HenningHolmDE/redirect_http_server feat: http to https redirection support --- README.md | 10 ++++++++++ src/config.rs | 6 ++++++ src/main.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2091523..bb1056b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ - First-class [Docker](https://docs.docker.com/get-started/overview/) support. [Scratch](https://hub.docker.com/_/scratch) and latest [Alpine Linux](https://hub.docker.com/_/alpine) Docker images available. - Server configurable via environment variables or CLI arguments. - MacOs binary support (`x86_64-apple-darwin`) thanks to [Rust Linux / Darwin Builder](https://github.com/joseluisq/rust-linux-darwin-builder). +- Additional HTTP redirect server for redirecting HTTP traffic to HTTPS site. ## Releases @@ -47,6 +48,8 @@ Server can be configured either via environment variables or their equivalent co | `SERVER_TLS` | Enables TLS/SSL support. Make sure also to adjust current server port. | Default `false` | | `SERVER_TLS_PKCS12` | A cryptographic identity [PKCS #12](https://docs.rs/native-tls/0.2.3/native_tls/struct.Identity.html#method.from_pkcs12) bundle file path containing a [X509 certificate](https://en.wikipedia.org/wiki/X.509) along with its corresponding private key and chain of certificates to a trusted root. | Default empty | | `SERVER_TLS_PKCS12_PASSWD` | A specified password to decrypt the private key. | Default empty | +| `SERVER_TLS_REDIRECT_FROM` | Host port for redirecting HTTP requests to HTTPS. This option enables the HTTP redirect feature | Default empty (disabled) | +| `SERVER_TLS_REDIRECT_HOST` | Host name of HTTPS site for redirecting HTTP requests to. | Default host address | | `SERVER_CORS_ALLOW_ORIGINS` | Specify a CORS list of allowed origin hosts separated by comas with no whitespaces. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host. See [Iron CORS crate](https://docs.rs/iron-cors/0.8.0/iron_cors/#mode-1-whitelist). | Default empty (which means CORS is disabled) | ### Command-line arguments @@ -92,6 +95,13 @@ OPTIONS: corresponding private key and chain of certificates to a trusted root [env: SERVER_TLS_PKCS12=] [default: ] --tls-pkcs12-passwd A specified password to decrypt the private key [env: SERVER_TLS_PKCS12_PASSWD=] [default: ] + + --tls-redirect-from + Host port for redirecting HTTP requests to HTTPS. This option enables the HTTP redirect feature [env: + SERVER_TLS_REDIRECT_FROM=] + --tls-redirect-host + Host name of HTTPS site for redirecting HTTP requests to. Defaults to host address [env: + SERVER_TLS_REDIRECT_HOST=] ``` ## TLS/SSL diff --git a/src/config.rs b/src/config.rs index 4b7448d..a3ce7b6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -41,6 +41,12 @@ pub struct Options { #[structopt(long, default_value = "", env = "SERVER_TLS_PKCS12_PASSWD")] /// A specified password to decrypt the private key. pub tls_pkcs12_passwd: String, + #[structopt(long, env = "SERVER_TLS_REDIRECT_FROM")] + /// Host port for redirecting HTTP requests to HTTPS. This option enables the HTTP redirect feature. + pub tls_redirect_from: Option, + #[structopt(long, env = "SERVER_TLS_REDIRECT_HOST")] + /// Host name of HTTPS site for redirecting HTTP requests to. Defaults to host address. + pub tls_redirect_host: Option, #[structopt(long, default_value = "error", env = "SERVER_LOG_LEVEL")] /// Specify a logging level in lower case. pub log_level: String, diff --git a/src/main.rs b/src/main.rs index fcd2e8a..50cfd90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,8 @@ extern crate log; use crate::config::Options; use hyper_native_tls::NativeTlsServer; -use iron::prelude::*; +use iron::{prelude::*, Listening}; +use iron_staticfile_middleware::HttpToHttpsRedirect; use staticfiles::*; use structopt::StructOpt; @@ -19,12 +20,21 @@ mod logger; mod signal_manager; mod staticfiles; -fn on_server_running(server_name: &str, proto: &str, addr: &str) { +/// Struct for holding a reference to a running iron server instance +#[derive(Debug)] +struct RunningServer { + listening: Listening, + server_type: String, +} + +fn on_server_running(server_name: &str, running_servers: &Vec) { // Notify when server is running - logger::log_server(&format!( - "Static {} Server \"{}\" is listening on {}", - proto, server_name, addr - )); + running_servers.iter().for_each(|server| { + logger::log_server(&format!( + "{} Server \"{}\" is listening on {}", + server.server_type, server_name, server.listening.socket + )) + }); // Wait for incoming signals (E.g Ctrl+C (SIGINT), SIGTERM, etc signal_manager::wait_for_signal(|sig: signal::Signal| { @@ -42,7 +52,6 @@ fn main() { logger::init(&opts.log_level); let addr = &format!("{}{}{}", opts.host, ":", opts.port); - let proto = if opts.tls { "HTTPS" } else { "HTTP" }; // Configure & launch the HTTP server @@ -54,19 +63,47 @@ fn main() { cors_allow_origins: opts.cors_allow_origins, }); + let mut running_servers = Vec::new(); if opts.tls { + // Launch static HTTPS server let ssl = NativeTlsServer::new(opts.tls_pkcs12, &opts.tls_pkcs12_passwd).unwrap(); match Iron::new(files.handle()).https(addr, ssl) { - Result::Ok(_) => on_server_running(&opts.name, &proto, addr), + Result::Ok(listening) => running_servers.push(RunningServer { + listening, + server_type: "Static HTTPS".to_string(), + }), Result::Err(err) => panic!("{:?}", err), } + + // Launch redirect HTTP server (if requested) + if let Some(port_redirect) = opts.tls_redirect_from { + let addr_redirect = &format!("{}{}{}", opts.host, ":", port_redirect); + let host_redirect = match opts.tls_redirect_host.as_ref() { + Some(host) => host, + None => &opts.host, + }; + let handler = + Chain::new(HttpToHttpsRedirect::new(&host_redirect, opts.port).permanent()); + match Iron::new(handler).http(addr_redirect) { + Result::Ok(listening) => running_servers.push(RunningServer { + listening, + server_type: "Redirect HTTP".to_string(), + }), + Result::Err(err) => panic!("{:?}", err), + } + } } else { + // Launch static HTTP server match Iron::new(files.handle()).http(addr) { - Result::Ok(_) => on_server_running(&opts.name, &proto, addr), + Result::Ok(listening) => running_servers.push(RunningServer { + listening, + server_type: "Static HTTP".to_string(), + }), Result::Err(err) => panic!("{:?}", err), } } + on_server_running(&opts.name, &running_servers); } #[cfg(test)] -- libgit2 1.7.2