Skip to content

Commit

Permalink
Merge pull request #171 from junkurihara/develop
Browse files Browse the repository at this point in the history
0.9.0
  • Loading branch information
junkurihara authored Jul 31, 2024
2 parents 03ea24c + 9ca81e9 commit d85b70f
Show file tree
Hide file tree
Showing 32 changed files with 977 additions and 189 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/release_docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- target: "s2n"
dockerfile: ./docker/Dockerfile
build-args: |
"CARGO_FEATURES=--no-default-features --features=http3-s2n,cache,rustls-backend"
"CARGO_FEATURES=--no-default-features --features=http3-s2n,cache,rustls-backend,acme"
"ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++"
platforms: linux/amd64,linux/arm64
tags-suffix: "-s2n"
Expand All @@ -58,7 +58,7 @@ jobs:
dockerfile: ./docker/Dockerfile
platforms: linux/amd64,linux/arm64
build-args: |
"CARGO_FEATURES=--no-default-features --features=http3-quinn,cache,webpki-roots"
"CARGO_FEATURES=--no-default-features --features=http3-quinn,cache,webpki-roots,acme"
tags-suffix: "-webpki-roots"
# Aliases must be used only for release builds
aliases: |
Expand All @@ -68,7 +68,7 @@ jobs:
- target: "slim-webpki-roots"
dockerfile: ./docker/Dockerfile-slim
build-args: |
"CARGO_FEATURES=--no-default-features --features=http3-quinn,cache,webpki-roots"
"CARGO_FEATURES=--no-default-features --features=http3-quinn,cache,webpki-roots,acme"
build-contexts: |
messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl
messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl
Expand All @@ -82,7 +82,7 @@ jobs:
- target: "s2n-webpki-roots"
dockerfile: ./docker/Dockerfile
build-args: |
"CARGO_FEATURES=--no-default-features --features=http3-s2n,cache,webpki-roots"
"CARGO_FEATURES=--no-default-features --features=http3-s2n,cache,webpki-roots,acme"
"ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++"
platforms: linux/amd64,linux/arm64
tags-suffix: "-s2n-webpki-roots"
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "submodules/rusty-http-cache-semantics"]
path = submodules/rusty-http-cache-semantics
url = [email protected]:junkurihara/rusty-http-cache-semantics.git
[submodule "submodules/rustls-acme"]
path = submodules/rustls-acme
url = [email protected]:junkurihara/rustls-acme.git
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
# CHANGELOG

## 0.9.0 (Unreleased)
## 0.10.0 (Unreleased)

## 0.9.0

### Important Changes

- Breaking: Experimental ACME support is added. Check the new configuration options and README.md for ACME support. Note that it is still under development and may have some issues.

### Improvement

- Refactor: lots of minor improvements
- Deps

### Bugfix

- Fix the bug that the dynamic config reload does not work properly.

## 0.8.1

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace.package]
version = "0.8.1"
version = "0.9.0"
authors = ["Jun Kurihara"]
homepage = "https://github.com/junkurihara/rust-rpxy"
repository = "https://github.com/junkurihara/rust-rpxy"
Expand All @@ -9,7 +9,7 @@ edition = "2021"
publish = false

[workspace]
members = ["rpxy-bin", "rpxy-lib", "rpxy-certs"]
members = ["rpxy-bin", "rpxy-lib", "rpxy-certs", "rpxy-acme"]
exclude = ["submodules"]
resolver = "2"

Expand Down
53 changes: 36 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# rpxy: A simple and ultrafast reverse-proxy serving multiple domain names with TLS termination, written in pure Rust
# rpxy: A simple and ultrafast reverse-proxy serving multiple domain names with TLS termination, written in Rust

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
![Unit Test](https://github.com/junkurihara/rust-rpxy/actions/workflows/ci.yml/badge.svg)
Expand All @@ -10,9 +10,11 @@
## Introduction

`rpxy` [ahr-pik-see] is an implementation of simple and lightweight reverse-proxy with some additional features. The implementation is based on [`hyper`](https://github.com/hyperium/hyper), [`rustls`](https://github.com/rustls/rustls) and [`tokio`](https://github.com/tokio-rs/tokio), i.e., written in pure Rust. Our `rpxy` routes multiple host names to appropriate backend application servers while serving TLS connections.
`rpxy` [ahr-pik-see] is an implementation of simple and lightweight reverse-proxy with some additional features. The implementation is based on [`hyper`](https://github.com/hyperium/hyper), [`rustls`](https://github.com/rustls/rustls) and [`tokio`](https://github.com/tokio-rs/tokio), i.e., written in Rust [^pure_rust]. Our `rpxy` routes multiple host names to appropriate backend application servers while serving TLS connections.

As default, `rpxy` provides the *TLS connection sanitization* by correctly binding a certificate used to establish a secure channel with the backend application. Specifically, it always keeps the consistency between the given SNI (server name indication) in `ClientHello` of the underlying TLS and the domain name given by the overlaid HTTP HOST header (or URL in Request line) [^1]. Additionally, as a somewhat unstable feature, our `rpxy` can handle the brand-new HTTP/3 connection thanks to [`quinn`](https://github.com/quinn-rs/quinn), [`s2n-quic`](https://github.com/aws/s2n-quic) and [`hyperium/h3`](https://github.com/hyperium/h3).[^h3lib]
[^pure_rust]: Doubtfully can be claimed to be written in pure Rust since current `rpxy` is based on `aws-lc-rs` for cryptographic operations.

By default, `rpxy` provides the *TLS connection sanitization* by correctly binding a certificate used to establish a secure channel with the backend application. Specifically, it always keeps the consistency between the given SNI (server name indication) in `ClientHello` of the underlying TLS and the domain name given by the overlaid HTTP HOST header (or URL in Request line) [^1]. Additionally, as a somewhat unstable feature, our `rpxy` can handle the brand-new HTTP/3 connection thanks to [`quinn`](https://github.com/quinn-rs/quinn), [`s2n-quic`](https://github.com/aws/s2n-quic) and [`hyperium/h3`](https://github.com/hyperium/h3).[^h3lib] Furthermore, `rpxy` supports the automatic issuance and renewal of certificates via [TLS-ALPN-01 (RFC8737)](https://www.rfc-editor.org/rfc/rfc8737) of [ACME protocol (RFC8555)](https://www.rfc-editor.org/rfc/rfc8555) thanks to [`rustls-acme`](https://github.com/FlorianUekermann/rustls-acme).

[^h3lib]: HTTP/3 libraries are mutually exclusive. You need to explicitly specify `s2n-quic` with `--no-default-features` flag. Also note that if you build `rpxy` with `s2n-quic`, then it requires `openssl` just for building the package.

Expand Down Expand Up @@ -236,20 +238,7 @@ Since it is currently a work-in-progress project, we are frequently adding new o

## Using Docker Image

You can also use `docker` image hosted on [Docker Hub](https://hub.docker.com/r/jqtype/rpxy) and [GitHub Container Registry](https://github.com/junkurihara/rust-rpxy/pkgs/container/rust-rpxy) instead of directly executing the binary. See [`./docker/README.md`](./docker/README.md) for the differences on image tags.

There are only several docker-specific environment variables.

- `HOST_USER` (default: `user`): User name executing `rpxy` inside the container.
- `HOST_UID` (default: `900`): `UID` of `HOST_USER`.
- `HOST_GID` (default: `900`): `GID` of `HOST_USER`
- `LOG_LEVEL=debug|info|warn|error`: Log level
- `LOG_TO_FILE=true|false`: Enable logging to the log file `/rpxy/log/rpxy.log` using `logrotate`. You should mount `/rpxy/log` via docker volume option if enabled. The log dir and file will be owned by the `HOST_USER` with `HOST_UID:HOST_GID` on the host machine. Hence, `HOST_USER`, `HOST_UID` and `HOST_GID` should be the same as ones of the user who executes the `rpxy` docker container on the host.
- `WATCH=true|false` (default: `false`): Activate continuous watching of the config file if true.

Then, all you need is to mount your `config.toml` as `/etc/rpxy.toml` and certificates/private keys as you like through the docker volume option. **If `WATCH=true`, You need to mount a directory, e.g., `./rpxy-config/`, including `rpxy.toml` on `/rpxy/config` instead of a file to correctly track file changes**. This is a docker limitation. Even if `WATCH=false`, you can mount the dir onto `/rpxy/config` rather than `/etc/rpxy.toml`. A file mounted on `/etc/rpxy` is prioritized over a dir mounted on `/rpxy/config`.

See [`docker/docker-compose.yml`](./docker/docker-compose.yml) for the detailed configuration. Note that the file path of keys and certificates must be ones in your docker container.
You can also use `docker` image hosted on [Docker Hub](https://hub.docker.com/r/jqtype/rpxy) and [GitHub Container Registry](https://github.com/junkurihara/rust-rpxy/pkgs/container/rust-rpxy) instead of directly executing the binary. See [`./docker`](./docker/README.md) directory for more details.

## Example

Expand Down Expand Up @@ -298,6 +287,32 @@ max_cache_each_size_on_memory = 4096 # optional. default is 4k if 0, it is alway

A *storable* (in the context of an HTTP message) response is stored if its size is less than or equal to `max_cache_each_size` in bytes. If it is also less than or equal to `max_cache_each_size_on_memory`, it is stored as an on-memory object. Otherwise, it is stored as a temporary file. Note that `max_cache_each_size` must be larger or equal to `max_cache_each_size_on_memory`. Also note that once `rpxy` restarts or the config is updated, the cache is totally eliminated not only from the on-memory table but also from the file system.

### Automated Certificate Issuance and Renewal via TLS-ALPN-01 ACME protocol

This is a brand-new feature and maybe still unstable. Thanks to the [`rustls-acme`](https://github.com/FlorianUekermann/rustls-acme), the automatic issuance and renewal of certificates are finally available in `rpxy`. To enable this feature, you need to specify the following entries in `config.toml`.

```toml
# ACME enabled domain name.
# ACME will be used to get a certificate for the server_name with ACME tls-alpn-01 protocol.
# Note that acme option must be specified in the experimental section.
[apps.localhost_with_acme]
server_name = 'example.org'
reverse_proxy = [{ upstream = [{ location = 'example.com', tls = true }] }]
tls = { https_redirection = true, acme = true } # do not specify tls_cert_path and/or tls_cert_key_path
```

For the ACME enabled domain, the following settings are referred to acquire a certificate.

```toml
# Global ACME settings. Unless specified, ACME is disabled.
[experimental.acme]
dir_url = "https://localhost:14000/dir" # optional. default is "https://acme-v02.api.letsencrypt.org/directory"
email = "[email protected]"
registry_path = "./acme_registry" # optional. default is "./acme_registry" relative to the current working directory
```

The above configuration is common to all ACME enabled domains. Note that the https port must be open to the public to verify the domain ownership.

## TIPS

### Using Private Key Issued by Let's Encrypt
Expand Down Expand Up @@ -379,6 +394,10 @@ However, we found that if you want to use the brand-new UDP-based protocol, HTTP

Your docker container can receive only TCP-based connection, i.e., HTTP/2 or before, unless you manually manage the port. We see that this is weird and expect that it is a kind of bug (of docker? ubuntu? or something else?). But at least for Ubuntu 22.04LTS, you need to handle it as above.

### Managing `rpxy` via web interface

Check a third party project [`Gamerboy59/rpxy-webui`](https://github.com/Gamerboy59/rpxy-webui) to manage `rpxy` via web interface.

### Other TIPS

todo!
Expand Down
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# TODO List

- Support of `rustls-0.22`.
- We need more sophistication on `Forwarder` struct to handle `h2c`.
- Cache using `lru` crate might be inefficient in terms of the speed.
- Consider more sophisticated architecture for cache
Expand Down
14 changes: 14 additions & 0 deletions config-example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ server_name = 'localhost.localdomain'
reverse_proxy = [{ upstream = [{ location = 'www.google.com', tls = true }] }]
######################################################################

######################################################################
# ACME enabled example. ACME will be used to get a certificate for the server_name with ACME tls-alpn-01 protocol.
# Note that acme option must be specified in the experimental section.
[apps.localhost_with_acme]
server_name = 'kubernetes.docker.internal'
reverse_proxy = [{ upstream = [{ location = 'example.com', tls = true }] }]
tls = { https_redirection = true, acme = true }

###################################
# Experimantal settings #
###################################
Expand Down Expand Up @@ -119,3 +127,9 @@ cache_dir = './cache' # optional. default is "./cache" relative t
max_cache_entry = 1000 # optional. default is 1k
max_cache_each_size = 65535 # optional. default is 64k
max_cache_each_size_on_memory = 4096 # optional. default is 4k if 0, it is always file cache.

# ACME settings. Unless specified, ACME is disabled.
[experimental.acme]
dir_url = "https://localhost:14000/dir" # optional. default is "https://acme-v02.api.letsencrypt.org/directory"
email = "[email protected]"
registry_path = "./acme_registry" # optional. default is "./acme_registry" relative to the current working directory
27 changes: 24 additions & 3 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
# Docker Images of `rpxy`

The `rpxy` docker images are hosted both on [Docker Hub](https://hub.docker.com/r/jqtype/rpxy) and [GitHub Container Registry](https://github.com/junkurihara/rust-rpxy/pkgs/container/rust-rpxy). Differences among tags are summarized as follows.
The `rpxy` docker images are hosted both on [Docker Hub](https://hub.docker.com/r/jqtype/rpxy) and [GitHub Container Registry](https://github.com/junkurihara/rust-rpxy/pkgs/container/rust-rpxy).

## Latest Builds
## Usage

There are several docker-specific environment variables.

- `HOST_USER` (default: `user`): User name executing `rpxy` inside the container.
- `HOST_UID` (default: `900`): `UID` of `HOST_USER`.
- `HOST_GID` (default: `900`): `GID` of `HOST_USER`
- `LOG_LEVEL=debug|info|warn|error`: Log level
- `LOG_TO_FILE=true|false`: Enable logging to the log file `/rpxy/log/rpxy.log` using `logrotate`. You should mount `/rpxy/log` via docker volume option if enabled. The log dir and file will be owned by the `HOST_USER` with `HOST_UID:HOST_GID` on the host machine. Hence, `HOST_USER`, `HOST_UID` and `HOST_GID` should be the same as ones of the user who executes the `rpxy` docker container on the host.
- `WATCH=true|false` (default: `false`): Activate continuous watching of the config file if true.

Then, all you need is to mount your `config.toml` as `/etc/rpxy.toml` and certificates/private keys as you like through the docker volume option. **If `WATCH=true`, You need to mount a directory, e.g., `./rpxy-config/`, including `rpxy.toml` on `/rpxy/config` instead of a file to correctly track file changes**. This is a docker limitation. Even if `WATCH=false`, you can mount the dir onto `/rpxy/config` rather than `/etc/rpxy.toml`. A file mounted on `/etc/rpxy` is prioritized over a dir mounted on `/rpxy/config`.

See [`docker-compose.yml`](./docker-compose.yml) for the detailed configuration. Note that the file path of keys and certificates must be ones in your docker container.

## Differences among image tags of Docker Hub and GitHub Container Registry

Differences among tags are summarized as follows.

### Latest Builds

- `latest`: Built from the `main` branch with default features, running on Ubuntu.
- `latest-slim`, `slim`: Built by `musl` from the `main` branch with default features, running on Alpine.
- `latest-s2n`, `s2n`: Built from the `main` branch with the `http3-s2n` feature, running on Ubuntu.

## Nightly Builds
### Nightly Builds

- `nightly`: Built from the `develop` branch with default features, running on Ubuntu.
- `nightly-slim`: Built by `musl` from the `develop` branch with default features, running on Alpine.
Expand All @@ -17,3 +36,5 @@ The `rpxy` docker images are hosted both on [Docker Hub](https://hub.docker.com/
## Caveats

Due to some compile errors of `s2n-quic` subpackages with `musl`, `nightly-s2n-slim` or `latest-s2n-slim` are not yet provided.

See [`./docker/README.md`](./docker/README.md) for the differences on image tags.
1 change: 1 addition & 0 deletions docker/docker-compose-slim.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ services:
volumes:
- ./log:/rpxy/log:rw
- ./cache:/rpxy/cache:rw
- ./acme_registry:/rpxy/acme_registry:rw
- ../example-certs/server.crt:/certs/server.crt:ro
- ../example-certs/server.key:/certs/server.key:ro
- ../config-example.toml:/etc/rpxy.toml:ro
Expand Down
1 change: 1 addition & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ services:
volumes:
- ./log:/rpxy/log:rw
- ./cache:/rpxy/cache:rw
- ./acme_registry:/rpxy/acme_registry:rw
- ../example-certs/server.crt:/certs/server.crt:ro
- ../example-certs/server.key:/certs/server.key:ro
- ../config-example.toml:/etc/rpxy.toml:ro
Expand Down
34 changes: 34 additions & 0 deletions rpxy-acme/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "rpxy-acme"
description = "ACME manager library for `rpxy`"
version.workspace = true
authors.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
readme.workspace = true
edition.workspace = true
publish.workspace = true

[dependencies]
url = { version = "2.5.2" }
rustc-hash = "2.0.0"
thiserror = "1.0.63"
tracing = "0.1.40"
async-trait = "0.1.81"
base64 = "0.22.1"
aws-lc-rs = { version = "1.8.1", default-features = false, features = [
"aws-lc-sys",
] }
blocking = "1.6.1"
rustls = { version = "0.23.12", default-features = false, features = [
"std",
"aws_lc_rs",
] }
rustls-platform-verifier = { version = "0.3.3" }
rustls-acme = { path = "../submodules/rustls-acme/", default-features = false, features = [
"aws-lc-rs",
] }
tokio = { version = "1.39.2", default-features = false }
tokio-util = { version = "0.7.11", default-features = false }
tokio-stream = { version = "0.1.15", default-features = false }
8 changes: 8 additions & 0 deletions rpxy-acme/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// ACME directory url
pub const ACME_DIR_URL: &str = "https://acme-v02.api.letsencrypt.org/directory";

/// ACME registry path that stores account key and certificate
pub const ACME_REGISTRY_PATH: &str = "./acme_registry";

/// ACME accounts directory, subdirectory of ACME_REGISTRY_PATH
pub(crate) const ACME_ACCOUNT_SUBDIR: &str = "accounts";
Loading

0 comments on commit d85b70f

Please sign in to comment.