The Core Image of the ibuttoncycle server
862
The app iButtonCycle can store all data on a self-hosted server.
The easiest way to run the iButtonCycle Server is with the Traefik edge router / reverse proxy.
The iButtonCycle Server setup requires to run four Docker containers
This image provides the API Backend
v1.0 but be aware that this version is no longer maintained (in fact it contains several packages that have critical vulnerabilities).308 Permanent Redirect from ibuttoncycle.<your-domain> to ibuttoncycle-api.<your-domain> is no longer included in the Admin Web Frontend docker image. You need to setup the redirect yourself. If you use the traefik reverse proxy, you can find the according labels in the docker-compose.yaml file below308 Permanent Redirect, the Content-Type HTTP header was not sent with the second request. Our solution is to add the Content-Type header in the reverse proxy when forwarding the request to the API backend docker container. If you use the Traefik reverse proxy, the docker-compose.yaml file below includes a middleware to do that.docker exec -it ibuttoncycle-api vendor/bin/phinx migrate -e production. The database migration tool requires extra environment variables: PHINX_DB_HOST, PHINX_DB_NAME, PHINX_DB_USER, and PHINX_DB_PASSWORD. They can be added via the docker-compose.yaml file as shown below. If you setup the server for the first time, you do not need to migrate.The compose setup is intended to run with traefik as an edge router / reverse proxy. In case you already have already another traefik instance running, some adaptions might be necessary.
The iButtonCycle setup involves two networks
ibuttoncyclewebThe web network is set up as external network, i.e. it is assumed to exist. To create the network use the command
docker network create web
If you already have an "outward facing" network defined, adapt the configuration accordingly.
The iButtonCycle setup depends on a middleware to add Security headers.
Create a file named traefik_dynamic_conf.yml with the following content:
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
sniStrict: true
http:
middlewares:
secHeaders:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
customFrameOptionsValue: "SAMEORIGIN"
The iButtonCycle docker compose setup (see next section) refrences the middleware via traefik.http.routers.ibuttoncyclewebadmin.middlewares=secHeaders@file
In case you are running your own Edge Router setup, make sure that you have a secHeaders Traefik middleware defined.
IMPORTANT
the setup uses Let's encrypt to generate TLS certificates for the server.
Let's encrypt requires a proper email address, which you need to store in a .env file.
To do so, create a file traefik/.env with the following content:
LETSENCRYPT_EMAIL=<[email protected]>
Configure the Traefik router with a docker-compose.yaml setup as follows:
networks:
web:
external: true
services:
router:
image: "traefik:latest"
container_name: "traefik"
command:
- "--log.level=ERROR"
- "--accesslog=false"
- "--api=false"
- "--providers.file.filename=/dynamic_conf.yml"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencryptresolver.acme.email=${LETSENCRYPT_EMAIL}"
- "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- ./letsencrypt:/letsencrypt
- ./traefik_dynamic_conf.yml:/dynamic_conf.yml
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- web
Start the traefik router using the command docker compose up -d in the directory in which the three previously created files are located
Running an iButtonCycle Server requires two subdomains:
ibuttoncycle.your.domainibuttoncycle-api.your.domainGo to your DNS provider and create DNS records to point the sub-domain to the server on which you are running iButtonCycle. It doesn't matter if you use A or CNAME records, as long as the subdomain resolves into the IP of your server.
Now as you have your edge router running, you can start all four iButtonCycle server containers using the following docker compose setup.
Create a directory in which you will manage the iButtonCycle Docker compose setup, e.g., ibuttoncycle.
Create a file called .env with the following content:
IBUTTONCYCLE_JWT_SECRET=jwt_secret
IBUTTONCYCLE_DB_ROOT_PWD=db_root_pwd
IBUTTONCYCLE_DB_USER_PWD=db_user_pwd
IBUTTONCYCLE_ADMIN_PWD=ibuttoncycle_admin
IBUTTONCYCLE_DB=ibuttoncycle
IBUTTONCYCLE_DB_USER=ibuttoncycle
IBUTTONCYCLE_BASE_DOMAIN=your.domain
But replace the values of each variable. The variables have the following meaning:
IBUTTONCYCLE_DB_ROOT_PWD - the password for the database root userIBUTTONCYCLE_DB_USER - the username of the ibuttoncycle database userIBUTTONCYCLE_DB_USER_PWD - the password for the ibuttoncycle database userIBUTTONCYCLE_DB - the name of the database to be usedIBUTTONCYCLE_JWT_SECRET - iButtonCycle uses JWT-based authorization, this is the private encryption secretIBUTTONCYCLE_ADMIN_PWD - the password of the admin user of the web administration interfaceIBUTTONCYCLE_BASE_DOMAIN - the hosts of ibuttoncycle will be established as subdomains, e.g., ibuttoncycle.orgThe Docker compose setup listed below relies on an "external" docker volume named ibuttoncycle_backup where the backup Docker container writes DB backups to. "external" means that the Docker volume will not be created from the docker-compose file but needs to be created separately. For example, if you want to store backups on an NFS share, you can create the volume by
docker volume create --driver local --opt type=nfs --opt o=addr=192.168.1.1,rw --opt device=:/path/to/dir ibuttoncycle_backup
where 192.168.1.1 is the IP address of your NFS server and /path/to/dir is the path of the NFS share.
To do so, create a directory ibuttoncycle and in that directory a docker-compose.yaml with the following content:
# the following environment variables need to be available:
# - IBUTTONCYCLE_DB_ROOT_PWD - the password for the database root user
# - IBUTTONCYCLE_DB_USER - the username of the ibuttoncycle database user
# - IBUTTONCYCLE_DB_USER_PWD - the password for the ibuttoncycle database user
# - IBUTTONCYCLE_DB - the name of the database to be used
# - IBUTTONCYCLE_JWT_SECRET - iButtonCycle uses JWT-based authorization, this is the private encryption secret
# - IBUTTONCYCLE_ADMIN_PWD - the password of the admin user of the web administration interface
# - IBUTTONCYCLE_BASE_DOMAIN - the hosts of ibuttoncycle will be established as subdomains, e.g., ibuttoncycle.{IBUTTONCYCLE_BASE_DOMAIN}
#
# the environment variables can be provided in a .env file in the same directory as this docker.compose.yaml file
#
# the following domain names need to be resolvable (have a DNS record)
# - ibuttoncycle-api.${IBUTTONCYCLE_BASE_DOMAIN}
# - ibuttoncycle.${IBUTTONCYCLE_BASE_DOMAIN}
networks:
web:
external: true
ibuttoncycle:
external: false
volumes:
ibuttoncycle_db:
ibuttoncycle_backup:
external: true
services:
ibuttoncycle-db:
image: fsalfnerdev/ibuttoncycle-db:prod
container_name: "ibuttoncycle-db"
restart: always
volumes:
- /etc/localtime:/etc/localtime:ro
- ibuttoncycle_db:/var/lib/mysql
networks:
- ibuttoncycle
environment:
- MARIADB_ROOT_PASSWORD=${IBUTTONCYCLE_DB_ROOT_PWD}
- MYSQL_PASSWORD=${IBUTTONCYCLE_DB_USER_PWD}
- MYSQL_DATABASE=${IBUTTONCYCLE_DB}
- MYSQL_USER=${IBUTTONCYCLE_DB_USER}
- MYSQL_INITDB_SKIP_TZINFO=1
ibuttoncycle-backup:
image: fsalfnerdev/ibuttoncycle-backup:prod
container_name: "ibuttoncycle-backup"
restart: always
depends_on:
- ibuttoncycle-db
volumes:
- ibuttoncycle_backup:/db_backup
networks:
- ibuttoncycle
environment:
- DB=${IBUTTONCYCLE_DB}
- DB_HOST=ibuttoncycle-db
- DB_ROOT_PASSWD=${IBUTTONCYCLE_DB_ROOT_PWD}
ibuttoncycle-api:
image: fsalfnerdev/ibuttoncycle-api:prod
container_name: "ibuttoncycle-api"
restart: always
networks:
- ibuttoncycle
- web
depends_on:
- ibuttoncycle-db
environment:
- DB=${IBUTTONCYCLE_DB}
- DB_USER=${IBUTTONCYCLE_DB_USER}
- DB_HOST=ibuttoncycle-db
- DB_PASSWD=${IBUTTONCYCLE_DB_USER_PWD}
- JWT_SECRET=${IBUTTONCYCLE_JWT_SECRET}
- ADMIN_PWD=${IBUTTONCYCLE_ADMIN_PWD}
- ADMIN_URL=ibuttoncycle.${IBUTTONCYCLE_BASE_DOMAIN}
- PHINX_DB_HOST=ibuttoncycle-db
- PHINX_DB_NAME=${IBUTTONCYCLE_DB}
- PHINX_DB_USER=${IBUTTONCYCLE_DB_USER}
- PHINX_DB_PASSWORD=${IBUTTONCYCLE_DB_USER_PWD}
labels:
- "traefik.enable=true"
- "traefik.docker.network=web"
- "traefik.http.middlewares.api-add-prefix.addprefix.prefix=index.php"
- "traefik.http.middlewares.admin-add-prefix.addprefix.prefix=admin.php"
- "traefik.http.middlewares.ibuttoncyclecorsheaders.headers.accessControlAllowMethods=GET,OPTIONS,PUT,PATCH"
- "traefik.http.middlewares.ibuttoncyclecorsheaders.headers.accessControlAllowOriginList=https://ibuttoncycle.${IBUTTONCYCLE_BASE_DOMAIN}"
- "traefik.http.middlewares.ibuttoncyclecorsheaders.headers.accessControlAllowCredentials=true"
- "traefik.http.middlewares.ibuttoncyclecorsheaders.headers.accessControlAllowHeaders=content-type,authorization"
# we need to add content-type headers since WebKit drops headers after a redirect
- "traefik.http.middlewares.ibuttoncycle-add-contenttype.headers.customRequestHeaders.Content-Type=application/json"
- "traefik.http.routers.ibuttoncycleapi.rule=Host(`ibuttoncycle-api.${IBUTTONCYCLE_BASE_DOMAIN}`) && PathPrefix(`/api`)"
- "traefik.http.routers.ibuttoncycleapi.entrypoints=websecure"
- "traefik.http.routers.ibuttoncycleapi.tls.certresolver=letsencryptresolver"
- "traefik.http.routers.ibuttoncycleapi.middlewares=secHeaders@file,api-add-prefix@docker,ibuttoncyclecorsheaders@docker,ibuttoncycle-add-contenttype@docker"
- "traefik.http.routers.ibuttoncycleadmin.rule=Host(`ibuttoncycle-api.${IBUTTONCYCLE_BASE_DOMAIN}`) && PathPrefix(`/admin`)"
- "traefik.http.routers.ibuttoncycleadmin.entrypoints=websecure"
- "traefik.http.routers.ibuttoncycleadmin.tls.certresolver=letsencryptresolver"
- "traefik.http.routers.ibuttoncycleadmin.middlewares=secHeaders@file,admin-add-prefix@docker,ibuttoncyclecorsheaders@docker,ibuttoncycle-add-contenttype@docker"
ibuttoncycle:
image: fsalfnerdev/ibuttoncycle-web:prod
container_name: "ibuttoncycle-webadmin"
restart: always
networks:
- ibuttoncycle
- web
depends_on:
- ibuttoncycle-db
- ibuttoncycle-api
environment:
- API_URL=ibuttoncycle-api.${IBUTTONCYCLE_BASE_DOMAIN}
labels:
- "traefik.enable=true"
- "traefik.docker.network=web"
- "traefik.http.routers.ibuttoncyclewebadmin.rule=Host(`ibuttoncycle.${IBUTTONCYCLE_BASE_DOMAIN}`)"
- "traefik.http.routers.ibuttoncyclewebadmin.entrypoints=websecure"
- "traefik.http.routers.ibuttoncyclewebadmin.tls.certresolver=letsencryptresolver"
- "traefik.http.routers.ibuttoncyclewebadmin.middlewares=secHeaders@file"
# redirect all routes with the /admin prefix to ibuttoncycle-api.ibuttoncycle.org/admin
- "traefik.http.middlewares.ibuttoncycle-admin-redirect.redirectRegex.regex=^https://ibuttoncycle.${IBUTTONCYCLE_BASE_DOMAIN}/admin/(.*)"
- "traefik.http.middlewares.ibuttoncycle-admin-redirect.redirectRegex.replacement=https://ibuttoncycle-api.${IBUTTONCYCLE_BASE_DOMAIN}/admin/${1}"
- "traefik.http.middlewares.ibuttoncycle-admin-redirect.redirectRegex.permanent=true"
- "traefik.http.routers.ibuttoncycle-admin-redirect.rule=Host(`ibuttoncycle.${IBUTTONCYCLE_BASE_DOMAIN}`) && PathPrefix(`/admin`)"
- "traefik.http.routers.ibuttoncycle-admin-redirect.middlewares=ibuttoncyclecorsheaders@docker,ibuttoncycle-admin-redirect@docker,secHeaders@file"
- "traefik.http.routers.ibuttoncycle-admin-redirect.entrypoints=websecure"
- "traefik.http.routers.ibuttoncycle-admin-redirect.tls.certresolver=letsencryptresolver"
To start the Docker compose setup for the iButtonCycleServer, run
docker compose up -d
Please note that the servers need some time to set up and initialize the DB (even after docker compose says "done").
To test if the server is running,
https://ibuttoncycle.your.domain.IBUTTONCYCLE_ADMIN_PWD) into the bottom input fieldFor more details, on how to use the admin interface, please continue reading at (https://ibuttoncycle.org/einen-eigenen-server-aufsetzen.html)
In case you run the iButtonCycle server on a Linux system that uses systemd, you can configure the iButtonCycle service with the following unit file called ibuttoncycle.service
[Unit]
Description=iButtonCycle docker compose setup
Requires=docker.service
After=docker.service
[Service]
Restart=always
User=root
Group=docker
WorkingDirectory=/etc/opt/containers/ibuttoncycle
ExecStartPre=/usr/bin/docker compose -f docker-compose.yaml down
ExecStart=/usr/bin/docker compose -f docker-compose.yaml up
ExecStartPre=/usr/bin/docker compose -f docker-compose.yaml down
[Install]
WantedBy=multi-user.target
Content type
Image
Digest
sha256:4eebd1221…
Size
174.3 MB
Last updated
4 months ago
docker pull fsalfnerdev/ibuttoncycle-api:prod