This page looks best with JavaScript enabled

Servidor de calendarios en FreeBSD

 ·  🎃 kr0m

This time we will install and configure a calendar hosting server, specifically Baikal.

First, we will create a clean jail where we will install the basic configuration and tools I usually set up, and then proceed with PHP, Nginx, and MySQL.

The tutorial consists of the following steps:


Jail and installation of basic tools:

We create the jail and assign an IP:

bastille create -T TimeGuard 14.3-RELEASE 192.168.69.18/24 nfe0

We install the basic tools using the template system :

bastille template TimeGuard datadyne.alfaexploit.com/bastille-basicconfiguration

Load balancer configuration:

If you have a load balancer, the best way to encrypt traffic is on the load balancer itself. First, create the DNS entry for the domain we will use for the calendar server.

In my case:

baikal.alfaexploit.com

My previous certificates were issued via ACME, so I just need to add one more domain:

/root/.acme.sh/acme.sh --issue --standalone --httpport 88 -d alfaexploit.com -d www.alfaexploit.com -d badguys.alfaexploit.com -d mail.alfaexploit.com -d grafana.alfaexploit.com -d redbulltank.alfaexploit.com -d baikal.alfaexploit.com --renew-hook 'cat /root/.acme.sh/alfaexploit.com_ecc/fullchain.cer /root/.acme.sh/alfaexploit.com_ecc/alfaexploit.com.key > /usr/local/etc/haproxy_certs.pem && haproxy -c -f /usr/local/etc/haproxy.conf && service haproxy restart && cp /root/.acme.sh/alfaexploit.com_ecc/fullchain.cer /root/.acme.sh/alfaexploit.com_ecc/alfaexploit.com.key /root/.acme.sh/alfaexploit.com_ecc/ca.cer /usr/local/www/alfaexploit_certs_mail/ && chmod 644 /usr/local/www/alfaexploit_certs_mail/* && service nginx restart' --force

We check that the certificate has the correct names:

openssl x509 -in /usr/local/etc/haproxy_certs.pem -noout -text | egrep -A1 "Subject:|Subject Alternative Name"
DNS:alfaexploit.com, DNS:badguys.alfaexploit.com, DNS:baikal.alfaexploit.com, DNS:grafana.alfaexploit.com, DNS:mail.alfaexploit.com, DNS:redbulltank.alfaexploit.com, DNS:www.alfaexploit.com

We configure HAProxy; pay attention to detail, the origin connection information is sent via ProxyProtocol (send-proxy-v2):

vi /usr/local/etc/haproxy.conf
backend baikal
    server baikal 192.168.69.18:80 check send-proxy-v2

Restart the service:

service haproxy restart

PHP-FPM installation:

Install PHP8.4 and the extensions needed for Baikal to work properly:

pkg install -y php84 php84-session php84-pdo php84-pdo_mysql php84-filter php84-xml php84-dom php84-xmlreader php84-xmlwriter

Configure the TimeZone:

cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
vi /usr/local/etc/php.ini
date.timezone = Europe/Madrid

Enable and start the PHP-FPM service:

sysrc php_fpm_enable=yes
service php_fpm start

Nginx installation:

Install Nginx:

pkg install -y nginx

Configure a Vhost for Baikal, paying attention to the proxyprotocol detail (proxy_protocol/set_real_ip_from/real_ip_header):

vi /usr/local/etc/nginx/baikal.conf
server {
  listen 80 proxy_protocol;
  server_name  baikal.alfaexploit.com;
    
  set_real_ip_from 192.168.69.19;
  real_ip_header proxy_protocol;

  root  /usr/local/www/baikal/html;
  index index.php;

  rewrite ^/.well-known/caldav /dav.php redirect;
  rewrite ^/.well-known/carddav /dav.php redirect;

  charset utf-8;

  location ~ /(\.ht|Core|Specific|config) {
    deny all;
    return 404;
  }

  location ~ ^(.+\.php)(.*)$ {
    try_files $fastcgi_script_name =404;
    include        fastcgi_params;
    fastcgi_split_path_info  ^(.+\.php)(.*)$;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO        $fastcgi_path_info;
  }
}

Include the configuration from the main file:

vi /usr/local/etc/nginx/nginx.conf
http {
    include       mime.types;
    default_type  application/octet-stream;

    include baikal.conf;
    ....

Check that PHP execution works:

mkdir -p /usr/local/www/baikal/html/
echo "<?php phpinfo(); ?>" > /usr/local/www/baikal/html/info.php

Enable and start the Nginx service:

sysrc nginx_enable=YES
service nginx restart

Access PHP, you should see something like this:
https://baikal.alfaexploit.com/info.php

Remove the PHP test file once verified:

rm /usr/local/www/baikal/html/info.php

Reload the Nginx configuration:

service nginx reload

MySQL installation:

Install MySQL:

pkg install -y mysql80-server percona-toolkit

Enable and start the MySQL service:

sysrc mysql_enable=YES
service mysql-server start

Secure the installation:

mysql_secure_installation

Create a client configuration file to avoid entering the password each time:

vi .my.cnf
[client]
user     = root
password = PASSWORD

Restrict file permissions:

chmod 600 .my.cnf

Create the database and user:

mysql
CREATE DATABASE baikal;
CREATE USER baikal@'192.168.69.18' IDENTIFIED WITH mysql_native_password BY 'PASSWORD';
GRANT ALL PRIVILEGES ON baikal.* TO baikal@'192.168.69.18';
FLUSH PRIVILEGES;
exit;

Baikal installation:

Keep in mind that Baikal is only intended to manage calendars, not to edit events. Therefore, do not expect to create or edit events from the web interface.

Install Baikal:

cd /usr/local/www/
rm -rf baikal
wget https://github.com/sabre-io/Baikal/releases/download/0.10.1/baikal-0.10.1.zip
unzip baikal-0.10.1.zip
rm baikal-0.10.1.zip

Set permissions for the Specific and config directories:

chown -R www:www baikal/Specific
chown -R www:www baikal/config

Access the Baikal web interface and perform the initial setup:
http://baikal.alfaexploit.com

Log in and create a user:

Edit the default calendar:


PC client:

We will use Khal as the client, which is available for both Debian and FreeBSD:

apt install khal vdirsyncer
pkg install -y py311-khal

In my case, I am on Debian, so configuration may differ slightly on FreeBSD, especially paths:

mkdir -p ~/.config/vdirsyncer
vi ~/.config/vdirsyncer/config
[general]
status_path = "~/.local/share/vdirsyncer/status/"

[pair baikal]
a = "baikal_local"
b = "baikal_remote"
collections = ["from b"]
conflict_resolution = "b wins"

[storage baikal_local]
type = "filesystem"
path = "~/.local/share/khal/calendars/"
fileext = ".ics"

[storage baikal_remote]
type = "caldav"
url = "https://baikal.alfaexploit.com/dav.php/calendars/kr0m/default/"
username = "kr0m"
password = "PASSWORD"
auth = "digest"

Khal is just the calendar viewer and editor, while Vdirsyncer is responsible for keeping them synchronized.

Discover the baikal server defined in the configuration:

vdirsyncer discover baikal
Discovering collections for pair baikal
baikal_local:
baikal_remote:
  - "default" ("Default calendar")
warning: No collection "default" found for storage baikal_local.
Should vdirsyncer attempt to create it? [y/N]: y
Saved for baikal: collections = ["default"]

Synchronize:

vdirsyncer sync
Syncing baikal/default

Configure Khal:

khal configure
What ordering of year, month, date do you want to use?
[0] year-month-day (today: 2025-08-25)
[1] day/month/year (today: 25/08/2025)
[2] month/day/year (today: 08/25/2025)
[3] Custom
Please choose one of the above options: 1
Date format: %d/%m/%Y (today as an example: 25/08/2025)

What timeformat do you want to use?
[0] 24 hour clock (recommended)
[1] 12 hour clock
Please choose one of the above options [0]: 0
Time format: %H:%M (current time as an example: 11:50)

[0] Create a new calendar on this computer
[1] Use a calendar already on this computer (vdir format)
[2] Sync a calendar from the internet (CalDAV format, requires vdirsyncer)
Please choose one of the above options: 1
The following calendars were found:
Found 1 calendars from vdirsyncer
  baikal_local: ~/.local/share/khal/calendars/*
Use these calendars for khal? [Y/n]: Y

Which calendar do you want as a default calendar?
(The default calendar is used when no calendar is specified.)
Configured calendars: default
Please type one of the above options (default) [default]:
Do you want to write the config to ~/.config/khal/config? (Choosing `No` will abort) [Y/n]: Y
Successfully wrote configuration to ~/.config/khal/config

Now we can create events on the calendar:

khal new 25/08/2025 12:00 25/08/2025 13:00 "Coders meeting" ":: Arguee about some code issues"

Check the events:

khal list
Today, 25/08/2025
12:00-13:00 Coders meeting :: Arguee about some code issues

If synchronized, changes will be uploaded to the server:

vdirsyncer sync
Syncing baikal/default
Copying (uploading) item WU9QARLFCN7O0Q9WPC5KE9TIUQTOBE4O0HJ2 to baikal_remote/default

We can also use a console interface that responds to clicks to create events:

ikhal

To create a new event on the selected day, press n:

From the CLI we can also view events, e.g., the next 30 days:

khal list today 30d
Today, 25/08/2025
12:00-13:00 Coders meeting :: Arguee about some code issues
Friday, 29/08/2025
 Test event ⟳ :: Test description

Synchronizing uploads items to the server:

vdirsyncer sync
Syncing baikal/default
Copying (uploading) item Q4MFESW0UBYV89RLTW7E5Y2QJ5ILY02VC1ZB to baikal_remote/default

To avoid manually synchronizing, schedule it with crontab:

crontab -e
* * * * * vdirsyncer sync

Android client:

As on the PC, on Android we need software to synchronize calendars and another to manage events.

For synchronization, we use DAVx⁵ from F-Droid .

Follow the initial setup:

Add an account:

url: https://baikal.alfaexploit.com/dav.php/calendars/kr0m/default/
username: kr0m
password: PASSWORD

Change synchronization times and disable the VPN:

Keep both the Default calendar and Default Address Book enabled:

For event management, we use Etar from GooglePlay, granting permissions:

When creating an event, ensure it is on the correct calendar:

Synchronize on the PC and list events

vdirsyncer sync
Syncing baikal/default
Copying (uploading) item 180d4f84-3e1b-47cf-8428-470448b9fd74 to baikal_local/default
Deleting item f9ae1b68-9517-4e43-b8ea-ed2329a7d400 from baikal_local/default
khal list today 30d
Today, 25/08/2025
 Test etar :: Test etar
Wednesday, 27/08/2025
 Hhhhh ⏰ :: Hhhhhh
Friday, 29/08/2025
 test ⟳ :: AAA

Integration with AwesomeWM:

Simply clone the repository of widgets I have on GitHub:

cd ~/.config/awesome
git clone https://github.com/ARPABoy/kr0mWidgets.git

And follow the instructions described on GitHub.

The final result will look like this:


Troubleshooting:

First, make sure the event is being created on the correct calendar from Etar.

If you want to reset the configuration on the PC, run the following commands:

rm -rf ~/.local/share/khal/calendars/*
rm ~/.config/vdirsyncer/config