From 3b0f26211a2d5ced747040c9f5cc4ebeb46d4f55 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Sun, 17 Aug 2025 23:43:45 +0200 Subject: [PATCH] sqlite page started --- README.md | 11 +++ .../ispmail-trixie/140-install-packages.md | 5 +- .../docs/ispmail-trixie/155-sqlite.mdx | 79 ++++++++++++++++++ .../ispmail-trixie/images/sqlite-schema.png | Bin 0 -> 16483 bytes 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/content/docs/ispmail-trixie/images/sqlite-schema.png diff --git a/README.md b/README.md index 0c7cf1d..b4c182f 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,14 @@ This repository holds the ISPmail guide as found on workaround.org. It was built using the static site generator [Starlight](https://starlight.astro.build). More information on how to contribute will follow. + +# Software used + +Static site generator: https://starlight.astro.build/ + +Diagrams/Illustrations: + +- DrawIO +- https://github.com/pascal-brand38/astro-splide +- https://pascal-brand38.github.io/astro-dev/packages/astro-splide/ +- https://splidejs.com/guides/options/ diff --git a/src/content/docs/ispmail-trixie/140-install-packages.md b/src/content/docs/ispmail-trixie/140-install-packages.md index 858688b..20052d2 100644 --- a/src/content/docs/ispmail-trixie/140-install-packages.md +++ b/src/content/docs/ispmail-trixie/140-install-packages.md @@ -20,7 +20,8 @@ DEBIAN_FRONTEND=noninteractive \ While the server is downloading and installing the packages, let me give you a quick explanation of each package: - postfix-sqlite \ - Postfix is the MTA (mail transport agent) that speaks SMTP to send and receive emails. This package installs Postfix with support for SQLite databases. + Postfix is the MTA (mail transport agent) that speaks SMTP to send and receive emails. This package installs Postfix + with support for SQLite databases. - dovecot \ Dovecot manages the emsrc/content/docs/ispmail-trixie/140-install-packages.mdx emails using IMAP. - -lmtpd \ @@ -52,3 +53,5 @@ While the server is downloading and installing the packages, let me give you a q Installs security updates automatically. TODO: move crowdsec into optional chapter + +TODO: postfix in no-chroot mode diff --git a/src/content/docs/ispmail-trixie/155-sqlite.mdx b/src/content/docs/ispmail-trixie/155-sqlite.mdx index 1318218..fb55a09 100644 --- a/src/content/docs/ispmail-trixie/155-sqlite.mdx +++ b/src/content/docs/ispmail-trixie/155-sqlite.mdx @@ -19,3 +19,82 @@ non-existent users. That information will be put into an SQLite database file th to set up, fast, and ideal for small projects. +The database will contain three tables: + +- virtual*domains: list of domains (\_name*) that your mail server is responsible for +- virtual_users: list of email addresses that lead to mailboxes. We store the email address, a hashed/salted password + and optionally a quota to limit the disk space usage. +- virtual*aliases: list of email addresses (\_source*) that forward to other addresses (_destination_) + +Creating and accessing the database is done using the _sqlite3_ command. The "3" is because SQLite has some breaking +changes in the past and _sqlite3_ is used to make it clear that this only works for version 3 database files. + +On your server run: + +```sh +sqlite3 /var/vmail/ispmail.sqlite +``` + +The database file is created and you will be greeted with: + +``` +SQLite version 3.46.1 2024-08-13 09:16:08 +Enter ".help" for usage hints. +sqlite> +``` + +At this point you can run SQL queries or use special commands that start with a dot like ".dump" or ".schema". Paste +this SQL block to create the necessary tables: + +```sql +CREATE TABLE IF NOT EXISTS virtual_domains ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS virtual_users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + domain_id INTEGER NOT NULL, + email TEXT NOT NULL UNIQUE, + password TEXT NOT NULL, + quota INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS virtual_aliases ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + domain_id INTEGER NOT NULL, + source TEXT NOT NULL, + destination TEXT NOT NULL, + FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE +); +``` + +You have now created the schema. It defines the _fields_ (or _columns_) of each row. + +Graphically it looks like: + +![ISPmail database schema](images/sqlite-schema.png) + +Paste the following block to create some test data to play with: + +```sql +REPLACE INTO virtual_domains (id, name) +VALUES (1, 'example.org'); + +REPLACE INTO virtual_users (id, domain_id, password, email) +VALUES ( + 1, + 1, + '{BLF-CRYPT}$2y$05$.WedBCNZiwxY1CG3aleIleu6lYjup2CIg0BP4M4YCZsO204Czz07W', + 'john@example.org' +); + +REPLACE INTO virtual_aliases (id, domain_id, source, destination) +VALUES ( + 1, + 1, + 'jack@example.org', + 'john@example.org' +); +``` diff --git a/src/content/docs/ispmail-trixie/images/sqlite-schema.png b/src/content/docs/ispmail-trixie/images/sqlite-schema.png new file mode 100644 index 0000000000000000000000000000000000000000..9075fb9dada7bd34dab154bb39c904ebb8aaa3ac GIT binary patch literal 16483 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 SzlZz=B`c{UQ6p{=^gjSk`b@$A literal 0 HcmV?d00001