Server Deployment

This section covers how to deploy the Parsec server.

Requirements

Preamble

The Parsec server depends on the following external components in order to work properly:

Avertissement

For security reasons, the installation of these components is outside the scope of this guide. In order to securely configure and manage them, please refer to their official documentations.

This guide provides instructions for quickly settings up mock-ups or basic installs of those components. Keep in mind that these instructions are provided for convenience and should not be used in production.

Parsec testing infra

With Docker

Generating the required TLS certificates

For this guide, the required TLS certificates will be generated with a custom Certificate Authority (CA) created for this purpose.

 1# shellcheck disable=SC2148
 2function generate_cert_conf() {
 3    local name=$1
 4    local san=$2
 5
 6    echo "Generating $name.crt.conf"
 7
 8    cat << EOF > "$name".crt.conf
 9[req]
10distinguished_name = req_dist_name
11req_extensions = req_ext
12prompt = no
13
14[req_dist_name]
15CN = $name
16
17[req_ext]
18subjectAltName = $san
19EOF
20}
21
22function generate_certificate_request() {
23    local name=$1
24    echo "Generate certificate request $name.csr"
25    openssl req -batch \
26        -new -sha512 -noenc -newkey rsa:4096 \
27        -config "$name".crt.conf \
28        -keyout "$name".key -out "$name".csr
29}
30
31function sign_crt_with_ca() {
32    local ca_crt=$1
33    local ca_key=$2
34    local name=$3
35
36    echo "Sign certificate request $name.crt"
37
38    openssl x509 -req -in "$name".csr \
39        -CA "$ca_crt" -CAkey "$ca_key" \
40        -extfile "$name".crt.conf \
41        -extensions req_ext \
42        -CAcreateserial -out "$name".crt \
43        -days 10 -sha512
44}
45
46if [ ! -f custom-ca.key ]; then
47    echo "Generate a mini Certificate Authority"
48    openssl req -batch \
49        -x509 -sha512 -nodes -days 10 -newkey rsa:4096 \
50        -subj "/CN=Mini Certificate Authority" \
51        -keyout custom-ca.key -out custom-ca.crt
52fi
53
54for service in parsec-{s3,server,proxy}; do
55    if [ ! -f "$service".crt.conf ]; then
56        generate_cert_conf "$service" DNS:"$service",DNS:localhost,IP:127.0.0.1
57    fi
58
59    # Generate key + csr if missing or if the key is older than the conf
60    if [ ! -f "$service".key ] || [ "$service".key -ot "$service".crt.conf ]; then
61        generate_certificate_request "$service"
62    fi
63
64    # Generate crt if missing or if it's older than the csr or the custom CA
65    if [ ! -f "$service".crt ] || [ "$service".crt -ot "$service".csr ] || [ "$service".crt -ot custom-ca.key ]; then
66        sign_crt_with_ca custom-ca.{crt,key} "$service"
67    fi
68done
69
70if [ "$(stat -c %g parsec-server.key)" -ne 1234 ]; then
71    echo "Changing group id of parsec-server.key to 1234"
72    sudo chown "$USER":1234 parsec-server.key
73fi
74
75if [ "$(stat -c %a parsec-server.key)" -ne 640 ]; then
76    echo "Changing permission of parsec-server.key to 640"
77    chmod 640 parsec-server.key
78fi

The script will:

  1. Generate the CA key & self-signed certificate (custom-ca.{key,crt}).

  2. For parsec-s3 and parsec-server services:

    1. Generate the service key & Certificate Signing Request (CSR) parsec-{service}.{key,csr}.

    2. Generate the certificate using the CSR and the CA.

  3. For the service parsec-server:

    1. Change the group id of the key file to 1234 (That is the GID used by the parsec-server container).

    2. Change the file mode to give read permission to the group 1234.

    Note

    This is required because docker-compose does not allow to mount the file with the correct permissions in the container.

Avertissement

For production, you should use certificates issued from a trusted CA

The env files

We split the configuration of the parsec server into multiple env files so it’s simpler to understand how to configure each part.

The administration token

To be able to perform admin tasks (like creating an organization) on the server, an administration token is required. Below you will find a simple script to generate a token:

 1# shellcheck disable=SC2148
 2set -euo pipefail
 3
 4ENV_FILE=parsec-admin-token.env
 5if [ ! -f $ENV_FILE ]; then
 6    PARSEC_ADMINISTRATION_TOKEN=$(openssl rand -hex 32)
 7    echo "PARSEC_ADMINISTRATION_TOKEN=$PARSEC_ADMINISTRATION_TOKEN" > $ENV_FILE
 8
 9    PARSEC_FAKE_ACCOUNT_PASSWORD_ALGORITHM_SEED=$(openssl rand -hex 32)
10    echo "PARSEC_FAKE_ACCOUNT_PASSWORD_ALGORITHM_SEED=$PARSEC_FAKE_ACCOUNT_PASSWORD_ALGORITHM_SEED" >> $ENV_FILE
11
12    echo "Parsec administration token generated in: $ENV_FILE"
13else
14    echo "Parsec administration token already exists in: $ENV_FILE"
15fi

The script will generate a random token (openssl rand -hex 32) and create the env file parsec-admin-token.env

Note

The step TOKEN=$(openssl rand -hex 32) could also be replaced by a value generated by a password-generator for example.

The token doesn’t have to be a valid hexadecimal value: any string with enough entropy can be used.

On top of the administration token, gen-admin-token.sh also generates FAKE_ACCOUNT_PASSWORD_ALGORITHM_SEED which is a secret used to make unpredictable the password algorithm configuration returned for non-existing accounts.

Database configuration

Create the file parsec-db.env with the following content to configure the access to the PostgreSQL database:

1# The Database url.
2PARSEC_DB=postgresql://DB_USER:DB_PASS@parsec-postgres:5432/parsec
3# The minimum number of connections to the database.
4PARSEC_DB_MIN_CONNECTIONS=5
5# The maximum number of connections to the database.
6PARSEC_DB_MAX_CONNECTIONS=7
SMTP configuration

Create the file parsec-smtp.env to configure the access to the SMTP server (mailhog in this case).

We need to set the connection information, the sender information, the default language the emails are sent in:

 1# The SMTP host to use for sending email.
 2PARSEC_EMAIL_HOST=parsec-smtp
 3# The port to use when connecting to the SMTP server.
 4PARSEC_EMAIL_PORT=1025
 5# The username to use for the SMTP server.
 6PARSEC_EMAIL_HOST_USER=SMTP_USER
 7# The password to use for the SMTP server.
 8PARSEC_EMAIL_HOST_PASSWORD=SMTP_PASS
 9PARSEC_EMAIL_SENDER=parsec@test.xyz
10
11# PARSEC_EMAIL_USE_SSL
12# PARSEC_EMAIL_USE_TLS
13PARSEC_EMAIL_LANGUAGE=en
S3 service configuration

Create the file parsec-s3.env with the following content to set the URL for the S3-like service:

1# The blockstore URL.
2# Can be S3, Switch or POSTGRESQL URL
3PARSEC_BLOCKSTORE=s3:parsec-s3\:9000:region1:parsec:S3_ROOT_USER:S3_ROOT_PASS

Note

We need to escape the : with a \ when specifying the port of the service.

Parsec server configuration

Create the file parsec.env with the following content to configure the parsec-server service:

 1# Host to listen to.
 2PARSEC_HOST=0.0.0.0
 3
 4# The SSL key file.
 5PARSEC_SSL_KEYFILE=/run/secrets/parsec-pem-key
 6
 7# The SSL certificate file.
 8PARSEC_SSL_CERTFILE=/run/secrets/parsec-pem-crt
 9
10# The ciphers suite to use (it's a comma separated list)
11# Here we use the recommended suite by ANSSI:
12# https://cyber.gouv.fr/sites/default/files/2017/07/anssi-guide-recommandations_de_securite_relatives_a_tls-v1.2.pdf
13PARSEC_SSL_CIPHERS=TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_AES_128_CCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES256-CCM,ECDHE-ECDSA-AES128-CCM,ECDHE-ECDSA-CHACHA20-POLY1305,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-RSA-CHACHA20-POLY1305
14
15# The granularity of Error log outputs.
16PARSEC_LOG_LEVEL=WARNING
17
18# The log formatting to use (`CONSOLE` or `JSON`).
19PARSEC_LOG_FORMAT=CONSOLE
20
21# The log file to write to (default to `stderr`).
22# PARSEC_LOG_FILE
23
24# List of proxy addresses to trust
25PARSEC_PROXY_TRUSTED_ADDRESS=parsec-proxy
26
27# The URL to reach Parsec server.
28PARSEC_SERVER_ADDR=parsec3://example.com
29
30# Keep SSE connection open by sending keepalive messages to client (pass <=0 to disable).
31PARSEC_SSE_KEEPALIVE=30
32
33# Sentry Data Source Name for telemetry report.
34# PARSEC_SENTRY_DSN
35
36# Sentry environment for telemetry report.
37PARSEC_SENTRY_ENVIRONMENT=production

Note

To see the full list of environment variables that you can use to configure Parsec, you can run:

python -m parsec run --help

Look for the sections [env var: VARIABLE] next to each configuration option. For example:

--administration-token TOKEN    Secret token to access the Administration API
                                [env var: PARSEC_ADMINISTRATION_TOKEN; required]

The docker-compose file

You can use the following docker-compose file (parsec-server.docker.yaml) to deploy the Parsec server for testing:

 1services:
 2  parsec-proxy:
 3    depends_on:
 4      - parsec-server
 5    image: nginx:1.27-alpine
 6    container_name: parsec-proxy
 7    ports:
 8      - 443:443
 9      - 80:80
10    volumes:
11      - ./parsec-nginx.conf:/etc/nginx/nginx.conf:ro
12      - ./parsec-proxy.crt:/certs/proxy.crt:ro
13      - ./parsec-proxy.key:/certs/proxy.key:ro
14
15  parsec-postgres:
16    image: postgres:16.10-alpine
17    container_name: parsec-postgres
18    environment:
19      POSTGRES_USER: DB_USER
20      POSTGRES_PASSWORD: DB_PASS
21      POSTGRES_DB: parsec
22    ports:
23      # Expose PostgreSQL to localhost
24      - 127.0.0.1:5432:5432
25    volumes:
26      - parsec-db-data:/var/lib/postgresql/data
27
28  parsec-s3:
29    image: quay.io/minio/minio:RELEASE.2024-09-13T20-26-02Z
30    container_name: parsec-s3
31    command: server --console-address ":9090" --certs-dir /opts/certs /data
32    environment:
33      MINIO_ROOT_USER: S3_ROOT_USER
34      MINIO_ROOT_PASSWORD: S3_ROOT_PASS
35    ports:
36      # Admin console exposed to https://127.0.0.1:9090
37      - 127.0.0.1:9090:9090
38      # Expose S3 API to localhost
39      - 127.0.0.1:9000:9000
40    volumes:
41      - parsec-object-data:/data
42      - ./parsec-s3.key:/opts/certs/private.key:ro
43      - ./parsec-s3.crt:/opts/certs/public.crt:ro
44      - ./custom-ca.crt:/opts/certs/CAs/ca.test.crt:ro
45
46  parsec-smtp:
47    image: mailhog/mailhog:v1.0.1
48    container_name: parsec-smtp
49    ports:
50      - 1025:1025
51      # Web interface exposed to http://127.0.0.1:8025
52      - 127.0.0.1:8025:8025
53
54  parsec-server:
55    depends_on:
56      - parsec-smtp
57      - parsec-s3
58      - parsec-postgres
59    image: ghcr.io/scille/parsec-cloud/parsec-server:3.8.2
60    container_name: parsec-server
61    env_file:
62      - parsec.env
63      - parsec-s3.env
64      - parsec-db.env
65      - parsec-smtp.env
66      - parsec-admin-token.env
67    environment:
68      AWS_CA_BUNDLE: /run/secrets/mini-ca-crt
69    secrets:
70      - mini-ca-crt
71      - parsec-pem-crt
72      - parsec-pem-key
73    ports:
74      - 127.0.0.1:6777:6777
75
76volumes:
77  parsec-db-data: {}
78  parsec-object-data: {}
79
80secrets:
81  parsec-pem-crt:
82    file: ./parsec-server.crt
83  parsec-pem-key:
84    file: ./parsec-server.key
85  mini-ca-crt:
86    file: ./custom-ca.crt

It will setup 4 services:

Service name

Description

parsec-postgres

The PostgreSQL database

parsec-s3

The Object Storage service

parsec-smtp

A mock SMTP server

parsec-server

The Parsec server

parsec-proxy

A Nginx proxy server, used as an example to configure a reverse proxy.

Learn more about using parsec behind a reverse proxy

Starting the services

The docker containers can be started as follows:

docker compose -f parsec-server.docker.yaml up

Initial configuration

On the first start, a one-time configuration is required for the database and s3 services.

Applying the database migration

(optional) Check that the database is accessible with:

set -a
source parsec-db.env
docker exec -t parsec-postgres psql 'postgresql://DB_USER:DB_PASS@0.0.0.0:5432/parsec' -c "\conninfo"

Note

You should have something like display on your console:

You are connected to database "parsec" as user "parsec" on host "0.0.0.0" at port "5432".

To bootstrap the database we just need to apply the migrations with:

docker compose -f parsec-server.docker.yaml run parsec-server migrate
Create the S3 Bucket

Access the console at https://127.0.0.1:9090, you will need to use the credential specified in the docker-compose file at services.parsec-s3.environment.MINIO_ROOT_{USER,PASSWORD}.

Go to https://127.0.0.1:9090/buckets/add-bucket to create a new bucket named parsec with the features object locking toggled on.

After that you will need to restart the parsec-server (that likely exited because it wasn’t able to access the S3 bucket):

docker compose -f parsec-server.docker.yaml restart parsec-server
Test the SMTP configuration & server

You can test mailhog with:

 1# shellcheck disable=SC2148
 2set -a
 3source parsec-smtp.env
 4
 5curl \
 6    --url "smtp://127.0.0.1:$PARSEC_EMAIL_PORT" \
 7    --user "$PARSEC_EMAIL_HOST_USER@localhost:$PARSEC_EMAIL_HOST_PASSWORD" \
 8    --mail-from "$PARSEC_EMAIL_SENDER" \
 9    --mail-rcpt rcpt@test.com \
10    --upload-file <(date --rfc-3339=seconds)

You can then check if the email is present in the web interface at http://127.0.0.1:8025

On Bare Metal

If you don’t want to install Parsec server via Docker, you can install it on bare metal.

Requirements

  • Python v3.12 with pip and venv modules

  • A S3 like object storage endpoint.

  • A postgres-16 database endpoint.

  • A TLS certificate & key for the server.

Configure the environment

  1. Configure the env files, follow the env files.

Installation

  1. Set up a virtual env:

python -m venv venv
  1. Configure your shell to use the virtual env:

source venv/bin/activate
  1. Install parsec-server

The parsec-server is available as a python package hosted on https://pypi.org/project/parsec-cloud/.

You need to install it with the extra backend enabled.

python -m pip install 'parsec-cloud==3.8.2'
  1. Prepare the database by applying the migrations:

source venv/bin/activate
set -a
source parsec-db.env
python -m parsec migrate

Start the server

  1. Create a wrapper script run-parsec-server

# Load the virtualenv.
source venv/bin/activate

# Load the env file into the environment table.
set -a
source parsec-admin-token.env
source parsec-db.env
source parsec-smtp.env
source parsec-s3.env
source parsec.env
set +a

# Start the parsec server.
python -m parsec run
  1. Execute the wrapper script run-parsec-server

Note

To run the wrapper with only run-parsec-server you need to have set the executable mode on the script file (chmod +x run-parsec-server). Otherwise, you need to execute it with the bash shell (bash run-parsec-server).

Start using Parsec server

Create the first organization

set -a
source parsec-admin-token.env
export SSL_CAFILE=$PWD/custom-ca.crt
parsec-cli organization create --addr parsec3://127.0.0.1:6777 <orgname>

Note

Change <orgname> to the organization’s name that suit you.

Save the link after Bootstrap organization url: you will need it to create the first user (owner) of the organization.

Add the first user to the organization

First, start parsec with the custom CA:

export SSL_CAFILE=$PWD/custom-ca.crt
parsec

After that go to Menu/Join an organization (or CTRL+O) and paste the link from before (should already be filled in the text field). Follow the instructions to create the first user of the organization.

Running behind a reverse proxy

To run Parsec behind a reverse proxy you will need to add the option --proxy-trusted-address or set the environment variable PARSEC_PROXY_TRUSTED_ADDRESS to the address of the reverse proxy (e.g.: localhost).

If this option is not set, the gunicorn/uvicorn FORWARDED_ALLOW_IPS environment variable is used, defaulting to trusting only localhost if absent.

Astuce

You can provide multiple addresses by separating them with a comma.

Example: Use the option --proxy-trusted-address '::1,10.0.0.42' will trust the address ::1 and 10.0.0.42

An example of a reverse proxy configuration for nginx can be found in the docker compose file:

 1  parsec-proxy:
 2    depends_on:
 3      - parsec-server
 4    image: nginx:1.27-alpine
 5    container_name: parsec-proxy
 6    ports:
 7      - 443:443
 8      - 80:80
 9    volumes:
10      - ./parsec-nginx.conf:/etc/nginx/nginx.conf:ro
11      - ./parsec-proxy.crt:/certs/proxy.crt:ro
12      - ./parsec-proxy.key:/certs/proxy.key:ro

The provided configuration for nginx is:

 1events {
 2    worker_connections 128;
 3}
 4
 5
 6http {
 7    server {
 8        listen 80;
 9        listen 443 ssl;
10        server_name example.com;
11        http2 on;
12        # Hide version number
13        server_tokens off;
14
15        # Only provide tlsv1.3
16        ssl_protocols       TLSv1.3;
17        ssl_certificate     /certs/proxy.crt;
18        ssl_certificate_key /certs/proxy.key;
19
20        location / {
21            proxy_pass https://parsec-server:6777;
22
23            # Add X-Forwarded headers to the proxied request
24            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
25            proxy_set_header X-Forwarded-Proto $scheme;
26            proxy_set_header X-Forwarded-Host $host;
27            proxy_set_header X-Forwarded-Port $server_port;
28
29            # Remove the Forwarded header
30            proxy_set_header Forwarded "";
31
32            # Overwrite the Host header
33            proxy_set_header Host example.com;
34        }
35    }
36}

It configures Nginx to serve the domain example.com by listening on port 80 and 443, and proxy the requests to the Parsec server.

The important takeaways are:

  • Set the headers X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Port.

    Note

    Currently, Parsec only uses the X-Forwarded-For and X-Forwarded-Proto headers. But it better to overwrite all of them to avoid any issue.

  • Remove the header Forwarded.

    Note

    The Forwarded header (RFC-7239) is not used by Parsec, but it may be in the future.

  • Set the header host to the accessible address. Here we force the value to be example.com, but you can set it to $host like for X-Forwarded-Host.

TLS Recommendation

We recommend that connections to the service are made using a TLS layer. If you are using a reverse proxy refer to it’s documentation on how to configure TLS:

Or if you do not use a reverse proxy, see how to configure TLS on the server

TLS Server configuration

We recommend that when user directly connects to the server (i.e. without using a reverse proxy) to configure the TLS settings on the server.

We provide 3 options to configure the TLS connection:

  • --ssl-keyfile [env var: PARSEC_SSL_KEYFILE]: The TLS key file

  • --ssl-certfile [env var: PARSEC_SSL_CERTFILE]: The TLS certificate file

  • --ssl-ciphers [env var: PARSEC_SSL_CIPHERS]: A list of ciphers that can be used when the client & server negotiate which algorithm to use when doing the TLS handcheck

    Note

    You are not required to provide the ciphers list as we use a default list that was recommended by the French Cybersecurity Agency (ANSSI) in Recommandations de sécurité relatives à TLS

Indication

If you followed the installation using docker, you should only have to replace the file parsec-server.crt and parsec-server.key that where generated during Generating the required TLS certificates. The env variable PARSEC_SSL_KEYFILE and PARSEC_SSL_CERTFILE are already configured in parsec.env that was defined in Parsec server configuration.