diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..dd4433d7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,27 @@ +# Local build and dev artifacts +target +sytest + +# Docker files +Dockerfile* +docker-compose* + +# IDE files +.vscode +.idea +*.iml + +# Git folder +.git +.gitea + +# Dot files +.env +.gitignore + +# Toml files +rustfmt.toml +Rocket-example.toml + +# Documentation +*.md diff --git a/DEPLOY_FROM_SOURCE.md b/DEPLOY_FROM_SOURCE.md new file mode 100644 index 00000000..2d6804db --- /dev/null +++ b/DEPLOY_FROM_SOURCE.md @@ -0,0 +1,100 @@ +# Deploy from source + +## Prerequisites + +Make sure you have `libssl-dev` and `pkg-config` installed and the [rust toolchain](https://rustup.rs) is available on at least on user. + + +## Install Conduit + +```bash +$ sudo useradd -m conduit +$ sudo -u conduit cargo install --git "https://git.koesters.xyz/timo/conduit.git" +``` + + +## Setup systemd service + +In this guide, we set up a systemd service for Conduit, so it's easy to start, stop Conduit and set it to autostart when your server reboots. Paste the default systemd service below and configure it to fit your setup (in /etc/systemd/system/conduit.service). + +```systemd +[Unit] +Description=Conduit +After=network.target + +[Service] +Environment="ROCKET_SERVER_NAME=conduit.rs" # EDIT THIS + +Environment="ROCKET_PORT=14004" # Reverse proxy port + +#Environment="ROCKET_REGISTRATION_DISABLED=true" +#Environment="ROCKET_LOG=normal" # Detailed logging + +Environment="ROCKET_ENV=production" +User=conduit +Group=conduit +Type=simple +Restart=always +ExecStart=/home/conduit/.cargo/bin/conduit + +[Install] +WantedBy=multi-user.target +``` + +Finally, run +```bash +$ sudo systemctl daemon-reload +``` + + +## Setup Reverse Proxy + +This depends on whether you use Apache, Nginx or something else. For Apache it looks like this (in /etc/apache2/sites-enabled/050-conduit.conf): +``` + + +ServerName conduit.koesters.xyz # EDIT THIS + +AllowEncodedSlashes NoDecode + +ServerAlias conduit.koesters.xyz # EDIT THIS + +ProxyPreserveHost On +ProxyRequests off +AllowEncodedSlashes NoDecode +ProxyPass / http://localhost:14004/ nocanon +ProxyPassReverse / http://localhost:14004/ nocanon + +Include /etc/letsencrypt/options-ssl-apache.conf + +# EDIT THESE: +SSLCertificateFile /etc/letsencrypt/live/conduit.koesters.xyz/fullchain.pem +SSLCertificateKeyFile /etc/letsencrypt/live/conduit.koesters.xyz/privkey.pem + +``` + +Then run +```bash +$ sudo systemctl reload apache2 +``` + + +## SSL Certificate + +The easiest way to get an SSL certificate for the domain is to install `certbot` and run this: +```bash +$ sudo certbot -d conduit.koesters.xyz +``` + + +## You're done! + +Now you can start Conduit with +```bash +$ sudo systemctl start conduit +``` + +and set it to start automatically when your system boots with +```bash +$ sudo systemctl enable conduit +``` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..7aa05c0f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,94 @@ +# Using multistage build: +# https://docs.docker.com/develop/develop-images/multistage-build/ +# https://whitfin.io/speeding-up-rust-docker-builds/ + + +########################## BUILD IMAGE ########################## +# Alpine build image to build Conduit's statically compiled binary +FROM alpine:3.12 as builder + +# Specifies if the local project is build or if Conduit gets build +# from the official git repository. Defaults to the git repo. +ARG LOCAL=false +# Specifies which revision/commit is build. Defaults to HEAD +ARG GIT_REF=HEAD + +# Add 'edge'-repository to get Rust 1.45 +RUN sed -i \ + -e 's|v3\.12|edge|' \ + /etc/apk/repositories + +# Install packages needed for building all crates +RUN apk add --no-cache \ + cargo \ + openssl-dev + + +# Copy project files from current folder +COPY . . +# Build it from the copied local files or from the official git repository +RUN if [[ $LOCAL == "true" ]]; then \ + cargo install --path . ; \ + else \ + cargo install --git "https://git.koesters.xyz/timo/conduit.git" --rev ${GIT_REF}; \ + fi + +########################## RUNTIME IMAGE ########################## +# Create new stage with a minimal image for the actual +# runtime image/container +FROM alpine:3.12 + +ARG CREATED +ARG VERSION +ARG GIT_REF=HEAD + +# Labels according to https://github.com/opencontainers/image-spec/blob/master/annotations.md +# including a custom label specifying the build command +LABEL org.opencontainers.image.created=${CREATED} \ + org.opencontainers.image.authors="Conduit Contributors" \ + org.opencontainers.image.title="Conduit" \ + org.opencontainers.image.version=${VERSION} \ + org.opencontainers.image.vendor="Conduit Contributors" \ + org.opencontainers.image.description="A Matrix homeserver written in Rust" \ + org.opencontainers.image.url="https://conduit.rs/" \ + org.opencontainers.image.revision=${GIT_REF} \ + org.opencontainers.image.source="https://git.koesters.xyz/timo/conduit.git" \ + org.opencontainers.image.documentation.="" \ + org.opencontainers.image.licenses="AGPL-3.0" \ + org.opencontainers.image.ref.name="" \ + org.label-schema.docker.build="docker build . -t conduit_homeserver:latest --build-arg CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') --build-arg VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml)" \ + maintainer="Weasy666" + +# Standard port on which Rocket launches +EXPOSE 8000 + +# Copy config files from context and the binary from +# the "builder" stage to the current stage into folder +# /srv/conduit and create data folder for database +RUN mkdir -p /srv/conduit/.local/share/conduit +COPY --from=builder /root/.cargo/bin/conduit /srv/conduit/ + +# Add www-data user and group with UID 82, as used by alpine +# https://git.alpinelinux.org/aports/tree/main/nginx/nginx.pre-install +RUN set -x ; \ + addgroup -Sg 82 www-data 2>/dev/null ; \ + adduser -S -D -H -h /srv/conduit -G www-data -g www-data www-data 2>/dev/null ; \ + addgroup www-data www-data 2>/dev/null && exit 0 ; exit 1 + +# Change ownership of Conduit files to www-data user and group +RUN chown -cR www-data:www-data /srv/conduit + +# Install packages needed to run Conduit +RUN apk add --no-cache \ + ca-certificates \ + libgcc + +# Create a volume for the database, to persist its contents +VOLUME ["/srv/conduit/.local/share/conduit"] + +# Set user to www-data +USER www-data +# Set container home directory +WORKDIR /srv/conduit +# Run Conduit +ENTRYPOINT [ "/srv/conduit/conduit" ] diff --git a/README.md b/README.md index 404636af..ad130892 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Liberapay](https://img.shields.io/liberapay/receives/timokoesters?logo=liberapay)](https://liberapay.com/timokoesters) [![Matrix](https://img.shields.io/matrix/conduit:koesters.xyz?server_fqdn=matrix.koesters.xyz&logo=matrix)](https://matrix.to/#/#conduit:koesters.xyz) -#### What is the goal +#### What is the goal? A fast Matrix homeserver that's easy to set up and just works. You can install it on a mini-computer like the Raspberry Pi to host Matrix for your family, friends or company. @@ -18,8 +18,14 @@ Yes! Just open a Matrix client ( or Element Android for #### How can I deploy my own? -You just have to clone the repo, build it with `cargo build --release` and call the binary (target/release/conduit) from somewhere like a systemd script. -It's explained in more detail [here](https://git.koesters.xyz/timo/conduit/wiki/Deploy). +##### From source + +Clone the repo, build it with `cargo build --release` and call the binary +(target/release/conduit) from somewhere like a systemd script. [Read more](DEPLOY_FROM_SOURCE.md) + +##### Using Docker + +Build the docker image and run it with docker or docker-compose. [Read more](docker/README.md) #### What is it build on? diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..afd36990 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,52 @@ +# Conduit +version: '3' + +services: + homeserver: + ### If you already built the Conduit image with 'docker build', then you can uncomment the + ### 'image' line and comment out the 'build' option. + # image: conduit_homeserver:latest + ### If you want meaningful labels in you built Conduit image, you should run docker-compose like this: + ### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d + build: + context: . + args: + CREATED: + VERSION: + LOCAL: "false" + GIT_REF: HEAD + restart: unless-stopped + ports: + - 8448:8000 + volumes: + - db:/srv/conduit/.local/share/conduit + ### Uncomment if you want to use Rocket.toml to configure Conduit + ### Note: Set env vars will override Rocket.toml values + # - ./Rocket.toml:/srv/conduit/Rocket.toml + environment: + ROCKET_SERVER_NAME: localhost:8000 # replace with your own name + ### Uncomment and change values as desired + # ROCKET_LOG: normal # Available levels are: off, debug, normal, critical + # ROCKET_PORT: 8000 + # ROCKET_REGISTRATION_DISABLED: 'true' + # ROCKET_ENCRYPTION_DISABLED: 'true' + # ROCKET_DATABASE_PATH: /srv/conduit/.local/share/conduit + # ROCKET_WORKERS: 10 + # ROCKET_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB + + ### Uncomment if you want to use your own Element-Web App. + ### Note: You need to provide a config.json for Element and you also need a second + ### Domain or Subdomain for the communication between Element and Conduit + ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md + # element-web: + # image: vectorim/riot-web:latest + # restart: unless-stopped + # ports: + # - 8009:80 + # volumes: + # - ./element_config.json:/app/config.json + # depends_on: + # - homeserver + +volumes: + db: diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..5a6ecdec --- /dev/null +++ b/docker/README.md @@ -0,0 +1,72 @@ +# Deploy using Docker + +> **Note:** To run and use Conduit you should probably use it with a Domain or Subdomain behind a reverse proxy (like Nginx, Traefik, Apache, ...) with a Lets Encrypt certificate. + + +## Docker + +### Build & Dockerfile + +The Dockerfile provided by Conduit has two stages, each of which creates an image. +1. **Builder:** Builds the binary from local context or by cloning a git revision from the official repository. +2. **Runtime:** Copies the built binary from **Builder** and sets up the runtime environment, like creating a volume to persist the database and applying the correct permissions. + +The Dockerfile includes a few build arguments that should be supplied when building it. + +``` Dockerfile +ARG LOCAL=false +ARG CREATED +ARG VERSION +ARG GIT_REF=HEAD +``` + +- **CREATED:** Date and time as string (date-time as defined by RFC 3339). Will be used to create the Open Container Initiative compliant label `org.opencontainers.image.created`. Supply by it like this `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` +- **VERSION:** The SemVer version of Conduit, which is in the image. Will be used to create the Open Container Initiative compliant label `org.opencontainers.image.version`. If you have a `Cargo.toml` in your build context, you can get it with `$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml)` +- **LOCAL:** *(Optional)* A boolean value, specifies if the local build context should be used, or if the official repository will be cloned. If not supplied with the build command, it will default to `false`. +- **GIT_REF:** *(Optional)* A git ref, like `HEAD` or a commit ID. The supplied ref will be used to create the Open Container Initiative compliant label `org.opencontainers.image.revision` and will be the ref that is cloned from the repository when not building from the local context. If not supplied with the build command, it will default to `HEAD`. + +To build the image you can use the following command + +``` bash +docker build . -t conduit_homeserver:latest --build-arg CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') --build-arg VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) +``` + +which also will tag the resulting image as `conduit_homeserver:latest`. +**Note:** it ommits the two optional `build-arg`s. + + +### Run + +After building the image you can simply run it with + +``` bash +docker run conduit_homeserver:latest -p 8448:8000 -v db:/srv/conduit/.local/share/conduit -e ROCKET_SERVER_NAME="localhost:8000" +``` + +For detached mode, you also need to use the `-d` flag. You can pass in more env vars as are shown here, for an overview of possible values, you can take a look at the `docker-compose.yml` file. +If you just want to test Conduit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it. + + +## Docker-compose + +If the docker command is not for you or your setup, you can also use one of the provided `docker-compose` files. Depending on your proxy setup, use the `docker-compose.traefik.yml` including `docker-compose.override.traefik.yml` or the normal `docker-compose.yml` for every other reverse proxy. + + +### Build + +To build the Conduit image with docker-compose, you first need to open and modify the `docker-compose.yml` file. There you need to comment the `image:` option and uncomment the `build:` option. Then call docker-compose with: + +``` bash +CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up +``` + +This will also start the container right afterwards, so if want it to run in detached mode, you also should use the `-d` flag. For possible `build-args`, please take a look at the above `Build & Dockerfile` section. + + +### Run + +If you already have built the image, you can just start the container and everything else in the compose file in detached mode with: + +``` bash +docker-compose up -d +``` diff --git a/docker/docker-compose.override.traefik.yml b/docker/docker-compose.override.traefik.yml new file mode 100644 index 00000000..2096d792 --- /dev/null +++ b/docker/docker-compose.override.traefik.yml @@ -0,0 +1,22 @@ +# Conduit - Traefik Reverse Proxy Labels +version: '3' + +services: + homeserver: + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" # Change this to the name of your Traefik docker proxy network + + - "traefik.http.routers.to-conduit.rule=Host(`.`)" # Change to the address on which Conduit is hosted + - "traefik.http.routers.to-conduit.tls=true" + - "traefik.http.routers.to-conduit.tls.certresolver=letsencrypt" + + ### Uncomment this if you uncommented Element-Web App in the docker-compose.yml + # element-web: + # labels: + # - "traefik.enable=true" + # - "traefik.docker.network=proxy" # Change this to the name of your Traefik docker proxy network + + # - "traefik.http.routers.to-element-web.rule=Host(`.`)" # Change to the address on which Element-Web is hosted + # - "traefik.http.routers.to-element-web.tls=true" + # - "traefik.http.routers.to-element-web.tls.certresolver=letsencrypt" diff --git a/docker/docker-compose.traefik.yml b/docker/docker-compose.traefik.yml new file mode 100644 index 00000000..ad1dad82 --- /dev/null +++ b/docker/docker-compose.traefik.yml @@ -0,0 +1,58 @@ +# Conduit - Behind Traefik Reverse Proxy +version: '3' + +services: + homeserver: + ### If you already built the Conduit image with 'docker build', then you can uncomment the + ### 'image' line and comment out the 'build' option. + # image: conduit_homeserver:latest + ### If you want meaningful labels in you built Conduit image, you should run docker-compose like this: + ### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d + build: + context: . + args: + CREATED: + VERSION: + LOCAL: false + GIT_REF: HEAD + restart: unless-stopped + volumes: + - db:/srv/conduit/.local/share/conduit + ### Uncomment if you want to use Rocket.toml to configure Conduit + ### Note: Set env vars will override Rocket.toml values + # - ./Rocket.toml:/srv/conduit/Rocket.toml + networks: + - proxy + environment: + ROCKET_SERVER_NAME: localhost:8000 # replace with your own name + ### Uncomment and change values as desired + # ROCKET_LOG: normal # Available levels are: off, debug, normal, critical + # ROCKET_PORT: 8000 + # ROCKET_REGISTRATION_DISABLED: 'true' + # ROCKET_ENCRYPTION_DISABLED: 'true' + # ROCKET_DATABASE_PATH: /srv/conduit/.local/share/conduit + # ROCKET_WORKERS: 10 + # ROCKET_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB + + ### Uncomment if you want to use your own Element-Web App. + ### Note: You need to provide a config.json for Element and you also need a second + ### Domain or Subdomain for the communication between Element and Conduit + ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md + # element-web: + # image: vectorim/riot-web:latest + # restart: unless-stopped + # volumes: + # - ./element_config.json:/app/config.json + # networks: + # - proxy + # depends_on: + # - homeserver + +volumes: + db: + +networks: + # This is the network Traefik listens to, if you network has a different + # name, don't forget to change it here and in the docker-compose.override.yml + proxy: + external: true