Skip to content

Latest commit

 

History

History
177 lines (120 loc) · 6.73 KB

File metadata and controls

177 lines (120 loc) · 6.73 KB

Quickstart - Setting up an fmsg host with fmsg-docker

This quickstart gets the docker compose stack from this repository up and running on your public fmsg server.

TLS provisioning is included and an HTTPS API is exposed so you can start sending and receiving fmsg messages for your domain. TCP port 4930 is also exposed for fmsg host-to-host communication.

Other Docs

Name Description
README.md Full README for this code repository.
README_LOCAL_DEV.md Run the stack locally for development purposes.

Requirements

  1. A domain you control, e.g example.com
  2. A server with a public IP and
    1. TCP port 4930 open to the internet (fmsg TLS)
    2. TCP port 443 open to the internet (fmsg-webapi HTTPS)
    3. TCP port 80 open to the internet (only first start - required for initial Let's Encrypt certificate issuance)
  3. Docker and Docker Compose, or Podman and podman-compose

Steps

NOTE This quickstart uses docker compose throughout. If you are using Podman, replace each occurrence with podman compose.

0. Server Setup

Make sure Docker is installed and running. Create a dedicated non-root fmsg operator account, create its checkout location, and shallow-clone the repository's default branch:

sudo useradd --create-home --shell /bin/bash fmsg
sudo install -d --owner=fmsg --group=fmsg /opt/fmsg-docker
sudo -u fmsg git clone --depth 1 https://github.com/markmnl/fmsg-docker.git /opt/fmsg-docker
sudo -u fmsg -H bash

1. Configure DNS

Create A (or AAAA if your public IP is IPv6) DNS records to resolve to your server IP for:

  1. fmsg.<your-domain>
  2. fmsgapi.<your-domain>

NOTE Ensure DNS is kept up-to-date with your server's IP so you can send and receive messages! e.g. using dig, nslookup or gent hosts:

getent hosts fmsg.<your-domain>
getent hosts fmsgapi.<your-domain>

2. Configure FMSG

As the fmsg user, configure the checkout in /opt/fmsg-docker. Copy the example env file:

cd /opt/fmsg-docker
cp .env.example compose/.env

Edit compose/.env and set at least:

FMSG_DOMAIN=example.com
CERTBOT_EMAIL=
FMSG_API_TOKEN_ED25519_PRIVATE_KEY=<base64-ed25519-seed>
FMSGD_READER_PGPASSWORD=<strong-password>
FMSGD_WRITER_PGPASSWORD=<strong-password>
FMSGID_READER_PGPASSWORD=<strong-password>
FMSGID_WRITER_PGPASSWORD=<strong-password>

NOTE

  • FMSG_DOMAIN is the domain part of fmsg addresses e.g. in @user@example.com would be example.com. This server you are setting up is located at the subdomain fmsg.<your-domain> but addresses will be at <your-domain>, you should only specify <your-domain> for FMSG_DOMAIN here.
  • CERTBOT_EMAIL is an email address supplied to Let's Encrypt for e.g. TLS expiry warnings.
  • Generate FMSG_API_TOKEN_ED25519_PRIVATE_KEY with openssl rand -base64 32.
  • For all secrets and passwords env vars create your own.

Exit the fmsg login shell and start the stack for the first time by changing into /opt/fmsg-docker/compose and passing the one-time postgres super user password on the command line. (Generate and keep PGPASSWORD yourself, this will only be needed first time running compose up).

exit
cd /opt/fmsg-docker/compose
sudo env PGPASSWORD='<postgres-password>' docker compose up -d

First time will take a few minutes to pull docker images and initalise the database. After than check everything started with:

sudo docker compose ps

If fmsgd is running and port 4930 is reachable on fmsg.<your domain>, the host is up.

On first start, certbot will request Let's Encrypt TLS certificates for fmsg.<your-domain> and fmsgapi.<your-domain>. If certificate issuance fails (e.g. the domains do not resolve to the server or port 80 is blocked by firewall, or already in use), the stack will not start. Certificates are persisted in a Docker volume and reused on subsequent starts. Once certificates are issued port 80 is no longer needed until certificates need to be renewed - usually 90 days.

Next Steps

Add Users

Create users (message stores, analoguous to mailboxes) by placing a CSV file in the fmsgid_data volume at /opt/fmsgid/data/addresses.csv. The format is:

address,display_name,accepting_new,limit_recv_size_total,limit_recv_size_per_msg,limit_recv_size_per_1d,limit_recv_count_per_1d,limit_send_size_total,limit_send_size_per_msg,limit_send_size_per_1d,limit_send_count_per_1d
@alice@<your-domain>,Alice,true,102400000,10240,102400,1000,102400000,10240,102400,1000

Replace <your-domain> with the value of your domain.

You can copy it into the volume with (file changes will sync automatically):

sudo docker compose -f /opt/fmsg-docker/compose/docker-compose.yml cp /path/to/your/addresses.csv fmsgid:/opt/fmsgid/data/addresses.csv

Connect a Client

Create an API key for a user, then use it with fmsg-cli or access the fmsg-webapi API directly at https://fmsgapi.<your-domain>.

To create an API key for @alice@<your-domain>:

sudo docker compose exec fmsg-webapi /opt/fmsg-webapi/fmsg-webapi api-key create-delegation \
  -owner @alice@<your-domain> \
  -agent cli \
  -addr @alice@<your-domain> \
  -cidr 0.0.0.0/0,::/0 \
  -expires 2027-12-31T00:00:00Z
  • The command prints the plaintext API key only once. Store it securely.
  • owner and addr are the same, so the key authenticates as @alice@example.com in the above example.
  • CIDR values: -cidr 0.0.0.0/0,::/0 permit connections from any IPv4 or IPv6 address, restrict per your requirements.

Then use it from fmsg-cli:

# save this export line to your ~/.bash_profile for future use
export FMSG_API_URL=https://fmsgapi.<your-domain>

fmsg login <fmsg-key>

fmsg list
fmsg send @recipient@example.com "Hello, world!"
fmsg send @recipient@example.com ./message.txt
echo "Hello via stdin" | fmsg send @recipient@example.com -

To use the API directly instead of fmsg-cli (which uses the API under the hood), exchange the API key for a short-lived JWT:

export FMSG_API_URL=https://fmsgapi.<your-domain>
export FMSG_API_KEY=fmsgk_<key_id>_<secret>

curl --fail --silent --show-error \
  -X POST \
  -H "Authorization: Bearer $FMSG_API_KEY" \
  "$FMSG_API_URL/fmsg/token"

The response contains an access_token. Send it as a Bearer token with API requests:

curl --fail --silent --show-error \
  -H "Authorization: Bearer <access_token>" \
  "$FMSG_API_URL/fmsg"