A secure-by-default Rest API using hyper, tokio, bb8, kafka-threadpool, postgres, and prometheus for monitoring.
user.events
) and partition key (key default: user-{user.id}
)Component | Status ---------------- | ------ Rest API Server | Listening for encrypted client connections on tcp port 3000 Postgres | Listening for encrypted client connections on tcp port 5432 (tls Certificate Authority required) pgAdmin | Listening for encrypted HTTP client connections on tcp port 5433
bash
git clone https://github.com/jay-johnson/restapi
cd restapi
Generate new tls assets under the ./tls
directory with these commands:
bash
cd tls
./create-tls-assets.sh
cd ..
Please refer to the Generating TLS Assets with CFSSL for more information.
Generate new signing JWT keys under the ./jwt
directory with these commands:
bash
cd jwt
./recreate-jwt.sh
cd ..
Please refer to the How to build JWT private and public keys for the jsonwebtokens crate doc for more information.
Please refer to the Build and Deploy a Secured Postgres backend doc for more information.
bash
cargo build --example server
bash
export RUST_BACKTRACE=1 && export RUST_LOG=info,kafka_threadpool=info && ./target/debug/examples/server
Environment Variable | Default --------------------- | ------- SERVERNAMEAPI | api SERVERNAMELABEL | rust-restapi APIENDPOINT | 0.0.0.0:3000 APITLSDIR | ./tls/api APITLSCA | ./tls/ca/ca.pem APITLSCERT | ./tls/api/server.pem APITLS_KEY | ./tls/api/server-key.pem
Environment Variable | Default -------------------------------------- | ------- USEREMAILVERIFICATIONREQUIRED | "0" USEREMAILVERIFICATIONENABLED | "1" USEREMAILVERIFICATIONEXPIN_SECONDS | "2592000"
Environment Variable | Default ----------------------- | ------- USEROTPEXPINSECONDS | "2592000"
Environment Variable | Default --------------------- | ------- POSTGRESUSERNAME | datawriter POSTGRESPASSWORD | "123321" POSTGRESENDPOINT | 0.0.0.0:5432 POSTGRESTLSDIR | ./tls/postgres POSTGRESTLSCA | ./tls/ca/ca.pem POSTGRESTLSCERT | ./tls/postgres/client.pem POSTGRESTLSKEY | ./tls/postgres/client-key.pem POSTGRESDBCONNTYPE | postgresql
Please refer to the kafka_threadpool docs for more information.
Environment Variable | Purpose / Value
-------------------------------- | ---------------
KAFKAPUBLISHEVENTS | if set to true
or 1
publish all user events to kafka
KAFKAENABLED | toggle the kafkathreadpool on with: true
or 1
anything else disables the threadpool
KAFKALOGLABEL | tracking label that shows up in all crate logs
KAFKABROKERS | comma-delimited list of brokers (host1:port,host2:port,host3:port
)
KAFKATOPICS | comma-delimited list of supported topics
KAFKAPUBLISHRETRYINTERVALSEC | number of seconds to sleep before each publish retry
KAFKAPUBLISHIDLEINTERVALSEC | number of seconds to sleep if there are no message to process
KAFKANUMTHREADS | number of threads for the threadpool
KAFKATLSCLIENTKEY | optional - path to the kafka mTLS key (./tls/kafka-cluster-0/client-key.pem)
KAFKATLSCLIENTCERT | optional - path to the kafka mTLS certificate (./tls/kafka-cluster-0/client.pem)
KAFKATLSCLIENTCA | optional - path to the kafka mTLS certificate authority (CA) (./tls/ca/ca.pem)
KAFKAMETADATACOUNTMSG_OFFSETS | optional - set to anything but true
to bypass counting the offsets
```bash
export KAFKAENABLED=1 export KAFKALOGLABEL="ktp" export KAFKABROKERS="host1:port,host2:port,host3:port" export KAFKATOPICS="testing" export KAFKAPUBLISHRETRYINTERVALSEC="1.0" export KAFKANUMTHREADS="5" export KAFKATLSCLIENTCA="./tls/ca/ca.pem" export KAFKATLSCLIENTCERT="./tls/kafka-cluster-0/client.pem" export KAFKATLSCLIENTKEY="./tls/kafka-cluster-0/client-key.pem"
export KAFKAMETADATACOUNTMSGOFFSETS="true" ```
Environment Variable | Default -------------------- | ------- S3DATABUCKET | YOURBUCKET S3DATAPREFIX | /rust-restapi/tests S3STORAGECLASS | STANDARD S3DATAUPLOADTO_S3 | "0"
Environment Variable | Default ------------------------------------ | ------- TOKENEXPIRATIONSECONDSINTOFUTURE | "2592000" TOKENORG | example.org TOKENHEADER | Bearer TOKENALGOPRIVATEKEY | ./jwt/private-key-pkcs8.pem TOKENALGOPUBLICKEY | ./jwt/public-key.pem SERVERPKIDIRJWT | ./jwt SERVERPASSWORD_SALT | 78197b60-c950-4339-a52c-053165a04764
Environment Variable | Default -------------------- | ------- RUSTBACKTRACE | "1" RUSTLOG | info
Environment Variable | Default -------------------- | ------- DEBUG | "1"
This will build an initial base image using podman. Note: this base image will not work on a different cpu chipset because the openssl libraries are compiled within the image for this base image.
bash
./build-base.sh
By reusing the base image, this derived image only needs to recompile the server. With minimal code changes, this is a much faster build than the base image build.
bash
./build-derived.sh
If you do not have a running Kafka cluster, you can deploy your own with:
https://github.com/jay-johnson/rust-with-strimzi-kafka-and-tls
This command will deploy all jwt keys, tls assets and credentials into the dev
namespace:
bash
./deploy-kubernetes-assets.sh -e dev
Please refer to the Deploying the Rust Rest API helm chart into kubernetes guide for deploying the example helm chart into a kubernetes cluster.
By default this uses the jayjohnson/rust-restapi
container image
bash
helm upgrade --install -n dev dev-api ./charts/rust-restapi -f ./charts/rust-restapi/values.yaml
This section assumes you have a working prometheus instance already running inside kubernetes. Below is the Prometheus scrape_config
to monitor the rest api deployment replica(s) within kubernetes. Note this config also assumes the api chart is running in the dev
namespace:
yaml
scrape_configs:
- job_name: rust-restapi
scrape_interval: 10s
scrape_timeout: 5s
metrics_path: /metrics
scheme: https
tls_config:
insecure_skip_verify: true
static_configs:
- targets:
- dev-api.dev.svc.cluster.local:3000
Here are the supported json contracts for each Request
and Response
based off the url. Each client request is handled by the ./src/handle_requests.rs module and returned as a response back to the client (serialization using serde_json
)
Create a single users
record for the new user
/user
POST
Update supported users
fields (including change user email and password)
/user
PUT
Get a single user by users.id
- by default, a user can only get their own account details
/user/USERID
GET
Delete a single users
record (note: this does not delete the db record, just sets the users.state
to inactive 1
)
/user
DELETE
Search for matching users
records in the db
/user/search
POST
Create a one-time-use password reset token that allows a user to change their users.password
value by presenting the token
/user/password/reset
POST
Consume a one-time-use password and change the user's users.password
value to the new argon2-hashed password
/user/password/change
POST
Consume a one-time-use verification token and change the user's users.verified
value verified (1
)
/user/verify
GET
Upload a local file on disk to AWS S3 asynchronously and store a tracking record in the users_data
table. The documentation refers to this as a user data
or user data file
record.
/user/data
POST
Update the users_data
tracking record for a file that exists in AWS S3
/user/data
PUT
Search for matching records in the users_data
db based off the request's values
/user/data/search
POST
Log the user in and get a json web token (jwt) back for authentication on subsequent client requests
/login
POST
This project focused on integration tests for v1 instead of only rust tests (specifically everything has been tested with curl):
Please refer to the Integration Tests Using curl Guide
bash
cur_tag=$(cat Cargo.toml | grep version | head -1 | sed -e 's/"//g' | awk '{print $NF}')
podman tag IMAGE_ID "docker://docker.io/jayjohnson/rust-restapi:${cur_tag}"
podman tag IMAGE_ID "docker://docker.io/jayjohnson/rust-restapi:latest"
podman push "docker.io/jayjohnson/rust-restapi:${cur_tag}"
podman push "docker.io/jayjohnson/rust-restapi:latest"
bash
cargo doc --example server
bash
source ./env/api.env && source ./env/kafka.env && source ./env/postgres.env && cargo build --example server && export RUST_BACKTRACE=1 && export RUST_LOG=info,kafka_threadpool=info,rdkafka=error && ./target/debug/examples/server