Docker install

InterDiode provides Docker/OCI images that are available in its private repository. It is possible to always use the latest stable tag or to use another service that handles updating Docker images.

This guide explains the setup based on docker-compose, but the installation of docker-compose itself is out of scope of this documentation. To do this, please follow the official install instructions.

If you want to use the built-in air gap protocol using UDP packets, you are responsible to inject the MAC address in the ARP cache of the host machine, before using the integrated air gap protocol. This must be done on the host machine, not in the Docker container.

Docker-compose file

Here are the Docker-compose files for the black and red instances.

compose.yaml for the black instance
version: '3'
services:
  web_interdiodeblack:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/web:/data/transfers
    - ./logs/web:/data/logs
    - ./tmp/web:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: /bin/sh -c "interdiode-ctl configuration apply --no-input && interdiode-ctl configuration show && interdiode-ctl run http"
    depends_on:
    - redis_interdiodeblack
    - db_interdiodeblack
    env_file:
    - ./compose-black.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-http.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
  background_interdiodeblack:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/shared:/data/transfers
    - ./logs/background:/data/logs
    - ./tmp/background:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: interdiode-ctl run background
    depends_on:
    - web_interdiodeblack
    - redis_interdiodeblack
    - db_interdiodeblack
    env_file:
    - ./compose-black.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-background.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 10G
  beat_interdiodeblack:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/beat:/data/transfers
    - ./logs/beat:/data/logs
    - ./tmp/beat:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: interdiode-ctl run beat
    depends_on:
    - web_interdiodeblack
    - redis_interdiodeblack
    - db_interdiodeblack
    env_file:
    - ./compose-black.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-beat.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
  hairgap_interdiodeblack:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/shared:/data/transfers
    - ./logs/hairgap:/data/logs
    - ./tmp/hairgap:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: interdiode-ctl hairgap send
    depends_on:
    - web_interdiodeblack
    - redis_interdiodeblack
    - db_interdiodeblack
    env_file:
    - ./compose-black.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-hairgap.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
  db_interdiodeblack:
    environment:
    - POSTGRES_HOST_AUTH_METHOD=trust
    - POSTGRES_DB=interdiode_db
    - POSTGRES_PASSWORD=my-postgres-password
    - POSTGRES_USER=interdiode
    - PGDATA=/var/lib/postgresql/data/postgresql
    healthcheck:
      interval: 60s
      retries: 10
      test: [CMD, pg_isready, -U, interdiode, -d, interdiode_db]
      timeout: 5s
    image: postgres:16
    restart: always
    volumes:
    - ./postgres:/var/lib/postgresql/data/postgresql
  redis_interdiodeblack:
    command: redis-server --requirepass 'my-redis-password' --save 60 1 --loglevel warning
    healthcheck:
      interval: 60s
      retries: 10
      test: [CMD, redis-cli, ping]
      timeout: 5s
    image: redis:alpine
    restart: always
    volumes:
    - ./redis:/data
compose.yaml for the red instance
version: '3'
services:
  web_interdiodered:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/web:/data/transfers
    - ./logs/web:/data/logs
    - ./tmp/web:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: /bin/sh -c "interdiode-ctl configuration apply --no-input && interdiode-ctl configuration show && interdiode-ctl run http"
    depends_on:
    - redis_interdiodered
    - db_interdiodered
    env_file:
    - ./compose.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-http.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
  background_interdiodered:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/shared:/data/transfers
    - ./logs/background:/data/logs
    - ./tmp/background:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: interdiode-ctl run background
    depends_on:
    - web_interdiodered
    - redis_interdiodered
    - db_interdiodered
    env_file:
    - ./compose.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-background.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 10G
  beat_interdiodered:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/beat:/data/transfers
    - ./logs/beat:/data/logs
    - ./tmp/beat:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: interdiode-ctl run beat
    depends_on:
    - web_interdiodered
    - redis_interdiodered
    - db_interdiodered
    env_file:
    - ./compose.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-beat.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
  hairgap_interdiodered:
    image: gitea.interdiode.fr/interdiode/interdiode:latest
    volumes:
    - ./files:/data/files
    - ./transfers/shared:/data/transfers
    - ./logs/hairgap:/data/logs
    - ./tmp/hairgap:/data/tmp
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
    command: interdiode-ctl hairgap receive
    depends_on:
    - web_interdiodered
    - redis_interdiodered
    - db_interdiodered
    env_file:
    - ./compose.env
    healthcheck:
      test: [CMD, python, -c, "import os; fd = open('/tmp/interdiode-hairgap.pid'); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()"]
      interval: 60s
      timeout: 5s
      retries: 10
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
  db_interdiodered:
    environment:
    - POSTGRES_HOST_AUTH_METHOD=trust
    - POSTGRES_DB=interdiode_db
    - POSTGRES_PASSWORD=my-postgres-password
    - POSTGRES_USER=interdiode
    - PGDATA=/var/lib/postgresql/data/postgresql
    healthcheck:
      interval: 60s
      retries: 10
      test: [CMD, pg_isready, -U, interdiode, -d, interdiode_db]
      timeout: 5s
    image: postgres:16
    restart: always
    volumes:
    - ./postgres:/var/lib/postgresql/data/postgresql
  redis_interdiodered:
    command: redis-server --requirepass 'my-redis-password' --save 60 1 --loglevel warning
    healthcheck:
      interval: 60s
      retries: 10
      test: [CMD, redis-cli, ping]
      timeout: 5s
    image: redis:alpine
    restart: always
    volumes:
    - ./redis:/data
compose-black.env
DATABASE_URL=postgres://username:password@localhost:5432/database
# URL of the database: (postgresql|mysql)://user:password@host:port/database
HAIRGAP_DESTINATION_IP=172.20.10.13
# IP address of your red-side InterDiode server
HAIRGAP_DESTINATION_MAC=00:00:00:00:00:00
# MAC address of your red-side InterDiode server
HAIRGAP_DESTINATION_PORT=15124
# Port number of your red-side InterDiode server
LISTEN_ADDRESS=0.0.0.0:8000
# Address listen by your web server (like 127.0.0.1:8000 or :8000).
REDIS_URL=redis://:password@localhost:6379/1
# Redis database URL.
SERVER_BASE_URL=https://interdiode-black.your.domain/
# Public URL of your website. 
# Default to "http://{listen_address}/" but should be different if you use a reverse proxy like Apache or Nginx. Example: https://www.example.org/.
SHARED_TRANSFER_KEY=secret_key
# Secret shared between black and red instances for authenticating transfers.
TRANSFER_MODE=udp
# Transfer method: UDP, TCP or manually transfer files. Valid choices: "udp", "tcp", "file"
compose-red.env
DATABASE_URL=postgres://username:password@localhost:5432/database
# URL of the database: (postgresql|mysql)://user:password@host:port/database
HAIRGAP_DESTINATION_PORT=15124
# Port number of your red-side InterDiode server
LISTEN_ADDRESS=0.0.0.0:8000
# Address listen by your web server (like 127.0.0.1:8000 or :8000).
REDIS_URL=redis://:password@localhost:6379/1
# Redis database URL.
SERVER_BASE_URL=https://interdiode-red.local/
# Public URL of your website. 
# Default to "http://{listen_address}/" but should be different if you use a reverse proxy like Apache or Nginx. Example: https://www.example.org/.
SHARED_TRANSFER_KEY=secret_key
# Secret shared between black and red instances for authenticating transfers.
TRANSFER_MODE=udp
# Transfer method: UDP, TCP or manually transfer files. Valid choices: "udp", "tcp", "file"

Environment

Black side-only parameters:

black.env
DATABASE_URL=postgresql://interdiode:interdiode@sql_database_black:5432/interdiode_db
REDIS_URL=redis://:interdiode@redis_database_black/1
INTERDIODE_INTERDIODE_MODE=black
INTERDIODE_SERVER_BASE_URL=http://localhost:8080/

Red side-only parameters:

red.env
DATABASE_URL=postgresql://interdiode:interdiode@sql_database_red:5432/interdiode_db
REDIS_URL=redis://:interdiode@redis_database_red/1
INTERDIODE_INTERDIODE_MODE=red
INTERDIODE_SERVER_BASE_URL=http://localhost:8081/

For this demonstration, most settings are common to both instances:

.env
DATABASE_URL=postgresql://interdiode:interdiode@localhost:5432/interdiode_db
EMAIL_HOST_URL=''
INTERDIODE_PLUGINS=''
REDIS_URL=redis://:interdiode@localhost:6379/1
INTERDIODE_USE_HTTP_BASIC_AUTH=true
INTERDIODE_ALLOW_USER_CREATION=true
INTERDIODE_GROUP_DISABLED_ATTRIBUTES=''
INTERDIODE_ALLOW_LOCAL_USERS=true
INTERDIODE_REMOTE_USER_DEFAULT_GROUPS=Users
INTERDIODE_HTTP_REMOTE_USER_HEADER=''
INTERDIODE_USE_AUTHORIZATION_TOKEN=true
INTERDIODE_USER_DISABLED_ATTRIBUTES=''
INTERDIODE_REQUIRE_NEW_USER_VALIDATION=true
INTERDIODE_EMAIL_FROM=admin@localhost
INTERDIODE_ADMIN_EMAIL=admin@localhost
INTERDIODE_DAILY_UPKEEP=true
DATA_ROOT=/data
INTERDIODE_HTML_TITLE=InterDiode
http_proxy=''
INTERDIODE_LANGUAGE_CODE=fr
INTERDIODE_LISTEN_ADDRESS=0.0.0.0:9000
LOG_DIRECTORY=/data/logs/
LOG_LEVEL=warn
INTERDIODE_LOG_REMOTE_ACCESS=true
LOG_REMOTE_URL=''
SENTRY_DSN=''
LOG_SLOW_QUERY_DURATION_IN_S=10.0
MAIN_STORAGE_DIR=/data/files/
INTERDIODE_FILE_UPLOAD_MAX_MEMORY_SIZE=10000000000
INTERDIODE_RUN_DATA_DIR=/tmp/
S3_REGION=''
INTERDIODE_TIME_ZONE=Europe/Paris
INTERDIODE_GNUPG_PATH=gpg
INTERDIODE_SHARED_TRANSFER_KEY=shared_secret_key
INTERDIODE_GIT_ALLOW_LFS=true
INTERDIODE_GIT_PATH=git
INTERDIODE_HIDDEN_CSS_SELECTORS=''
INTERDIODE_KEEP_HAIRGAP_SIZE=10000000000
INTERDIODE_KEEP_SOURCE_ACTION_COUNT=100
INTERDIODE_PURGE_RETENTION_DAYS=30
INTERDIODE_SSH_PATH=ssh
INTERDIODE_HAIRGAP_ERROR_CHUNK_SIZE=''
INTERDIODE_HAIRGAP_DESTINATION_IP=hairgap_red
INTERDIODE_HAIRGAP_DESTINATION_MAC=f0:18:98:a4:38:e2
INTERDIODE_HAIRGAP_DESTINATION_PORT=48102
INTERDIODE_DOWNLOAD_FILE_ANALYZER=''
INTERDIODE_HAIRGAP_END_DELAY_S=5
INTERDIODE_EXPORT_FILE_ANALYZER=''
INTERDIODE_IMPORT_FILE_ANALYZER=''
INTERDIODE_TRANSFER_KEEP_CORRUPTED_IMPORTS=false
INTERDIODE_TRANSFER_KEEP_FAILED_IMPORTS=false
INTERDIODE_TRANSFER_KEEP_IMPORTS=false
INTERDIODE_HAIRGAP_KEEP_TRANSFERS=true
INTERDIODE_KEEP_HAIRGAP_COUNT=100
INTERDIODE_TRANSFER_MODE=udp
INTERDIODE_HAIRGAP_REDUNDANCY=6.0
INTERDIODE_HAIRGAP_TIMEOUT_S=''
INTERDIODE_TRANSFER_DIR=/data/transfers/
INTERDIODE_TRANSFER_EXPORT_CHECKSUMS=true
INTERDIODE_TRANSFER_IMPORT_CHECKSUMS=true
INTERDIODE_DEFAULT_TWITTER_CONSUMER_KEY=''
INTERDIODE_DEFAULT_TWITTER_CONSUMER_SECRET=''
INTERDIODE_DEFAULT_TWITTER_ACCESS_TOKEN_KEY=''
INTERDIODE_DEFAULT_TWITTER_ACCESS_TOKEN_SECRET=''
INTERDIODE_USB_SOURCE_CONFIG_DIR=''
INTERDIODE_WORKER_PROCESSES=2