wk2: account management tooling

This commit is contained in:
ari melody 2026-02-05 14:02:32 +00:00
parent 37eeeb2467
commit f864d9c84e
Signed by: ari
GPG key ID: CF99829C92678188
34 changed files with 379 additions and 350 deletions

View file

@ -6,14 +6,24 @@ FROM debian:bookworm-slim AS compile
RUN <<EOF
apt update
apt install -y clang
apt install -y clang golang
apt clean
EOF
# TODO: consider removing if not needed
# COPY target/scripts/build/libpam-pgsql.sh .
# RUN bash libpam-pgsql.sh
WORKDIR /src/
COPY src .
# log
RUN clang -O2 -o /usr/local/bin/log /src/log/log.c
# accounts
WORKDIR /src/account-manager
RUN go build -o /usr/local/bin/accounts .
# ================================
# Build Stage
# ================================
@ -26,8 +36,14 @@ WORKDIR /
COPY target/scripts/build/packages.sh /build/
RUN /bin/bash /build/packages.sh && rm -r /build
# TODO: consider removing if not needed
# COPY --from=compile /pam-pgsql/.libs/pam_pgsql.so /pam-pgsql/.libs/pam_pgsql.la /usr/local/lib/security/
# Install compiled binaries
COPY --from=compile /usr/local/bin/log /usr/local/bin/log
COPY --from=compile \
/usr/local/bin/log \
/usr/local/bin/accounts \
/usr/local/bin/
# Install helper scripts
COPY target/scripts/*.sh /usr/local/bin/
@ -39,7 +55,11 @@ COPY target/supervisor/supervisord.conf /etc/supervisor/supervisord.conf
COPY target/supervisor/conf.d/* /etc/supervisor/conf.d/
# Dovecot
COPY target/dovecot/*.inc target/dovecot/*.conf /etc/dovecot/conf.d/
COPY target/dovecot/*.inc target/dovecot/*.conf target/dovecot/*.ext /etc/dovecot/conf.d/
RUN <<EOF
useradd -u 5000 -d /home/jupiter -s /bin/bash -p "$(echo jupiter | openssl passwd -1 -stdin)" jupiter
EOF
EXPOSE 25/tcp 143/tcp 465/tcp 993/tcp

View file

@ -0,0 +1,3 @@
module jupiter-mail.org/account-manager
go 1.23

136
src/account-manager/main.go Normal file
View file

@ -0,0 +1,136 @@
package main
import (
_ "embed"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"path"
"strings"
"jupiter-mail.org/account-manager/model"
)
//go:embed res/help.txt
var helpText string
var USERDB_PATH = path.Clean("/etc/dovecot/userdb")
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, helpText)
os.Exit(1)
}
switch os.Args[1] {
case "create":
account, err := createAccount(os.Args[2], os.Args[3])
if err != nil {
log.Fatalf("Failed to create account: %v", err)
os.Exit(1)
}
log.Printf("Account \"%s\" created successfully.", account.FullUsername())
case "list":
log.Fatalf("list: not implemented")
os.Exit(1)
case "update":
log.Fatalf("update: not implemented")
os.Exit(1)
case "delete":
log.Fatalf("delete: not implemented")
os.Exit(1)
default:
log.Fatalf("Unrecognised action \"%s\"", os.Args[1])
os.Exit(1)
}
}
func openUserDB() (*os.File, error) {
err := os.MkdirAll(path.Dir(USERDB_PATH), 0755)
if err != nil { return nil, err }
file, err := os.OpenFile(USERDB_PATH, os.O_CREATE | os.O_RDWR, 0640)
if err != nil && errors.Is(err, os.ErrExist) { return nil, err }
return file, nil
}
func getMailHome(user string, domain string) string {
return fmt.Sprintf("/var/mail/%s/%s/home", domain, user)
}
func checkAccountExists(username string) (bool, error) {
file, err := openUserDB()
if err != nil { return false, fmt.Errorf("open userdb: %v", err) }
data, err := io.ReadAll(file)
if err != nil { return false, fmt.Errorf("read userdb: %v", err) }
file.Close()
lines := strings.Split(string(data), "\n")
for _, line := range lines {
if strings.HasPrefix(line, username) { return true, nil }
}
return false, nil
}
func createAccount(fullUsername string, password string) (*model.MailAccount, error) {
exists, err := checkAccountExists(fullUsername)
if err != nil { return nil, fmt.Errorf("failed to check if account exists: %v", err) }
if exists { return nil, fmt.Errorf("account already exists: %s", fullUsername) }
usernameSplits := strings.Split(fullUsername, "@")
if len(usernameSplits) != 2 {
return nil, fmt.Errorf("invalid email format: %s", fullUsername)
}
username := usernameSplits[0]
domain := usernameSplits[1]
passwordHash, err := hashPassword(username, password)
if err != nil { return nil, fmt.Errorf("hash password: %v", err) }
account := model.NewMailAccount(username, domain, []byte(passwordHash))
uid := 5000; gid := 5000
// <username>:<password>:5000:5000::/var/mail/<domain>/<user>/home::
userDbLine := fmt.Sprintf(
"%s:%s:%d:%d::%s::\n",
account.FullUsername(),
account.PasswordHash,
uid, gid,
getMailHome(account.Username, account.Domain),
)
file, err := openUserDB()
if err != nil { return nil, fmt.Errorf("open userdb: %v", err) }
buf := make([]byte, 1024)
for n, err := file.Read(buf); n > 0; {
if err != nil {
return nil, fmt.Errorf("read userdb: %v", err)
}
}
_, err = file.Write([]byte(userDbLine))
if err != nil { return nil, err }
err = file.Close()
if err != nil { return nil, err }
return &account, nil
}
func hashPassword(username string, password string) (string, error) {
// doveadm pw -s SHA512-CRYPT -u "$USERNAME" -p "$PASSWORD"
hashCmd := exec.Command(
"doveadm", "pw",
"-s", "SHA512-CRYPT",
"-u", username,
"-p", password,
)
out, err := hashCmd.Output()
if err != nil { return "", err }
return string(out), nil
}

View file

@ -0,0 +1,23 @@
package model
import "fmt"
type (
MailAccount struct {
Username string `json:"username"`
Domain string `json:"domain"`
PasswordHash []byte `json:"password"`
}
)
func NewMailAccount(username string, domain string, passwordHash []byte) MailAccount {
return MailAccount{
Username: username,
Domain: domain,
PasswordHash: passwordHash,
}
}
func (account *MailAccount) FullUsername() string {
return fmt.Sprintf("%s@%s", account.Username, account.Domain)
}

View file

@ -0,0 +1,7 @@
usage: accounts <action> [...opts]
actions:
create <username> <password>
list
update <username> <password>
delete <username>

View file

@ -121,9 +121,9 @@ auth_mechanisms = plain
#!include auth-deny.conf.ext
#!include auth-master.conf.ext
!include auth-system.conf.ext
#!include auth-system.conf.ext
#!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
#!include auth-passwdfile.conf.ext
!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-static.conf.ext

View file

@ -1,60 +0,0 @@
##
## Director-specific settings.
##
# Director can be used by Dovecot proxy to keep a temporary user -> mail server
# mapping. As long as user has simultaneous connections, the user is always
# redirected to the same server. Each proxy server is running its own director
# process, and the directors are communicating the state to each others.
# Directors are mainly useful with NFS-like setups.
# List of IPs or hostnames to all director servers, including ourself.
# Ports can be specified as ip:port. The default port is the same as
# what director service's inet_listener is using.
#director_servers =
# List of IPs or hostnames to all backend mail servers. Ranges are allowed
# too, like 10.0.0.10-10.0.0.30.
#director_mail_servers =
# How long to redirect users to a specific server after it no longer has
# any connections.
#director_user_expire = 15 min
# How the username is translated before being hashed. Useful values include
# %Ln if user can log in with or without @domain, %Ld if mailboxes are shared
# within domain.
#director_username_hash = %Lu
# To enable director service, uncomment the modes and assign a port.
service director {
unix_listener login/director {
#mode = 0666
}
fifo_listener login/proxy-notify {
#mode = 0666
}
unix_listener director-userdb {
#mode = 0600
}
inet_listener {
#port =
}
}
# Enable director for the wanted login services by telling them to
# connect to director socket instead of the default login socket:
service imap-login {
#executable = imap-login director
}
service pop3-login {
#executable = pop3-login director
}
service submission-login {
#executable = submission-login director
}
# Enable director for LMTP proxying:
protocol lmtp {
#auth_socket_path = director-userdb
}

View file

@ -27,7 +27,8 @@
#
# <doc/wiki/MailLocation.txt>
#
mail_location = mbox:~/mail:INBOX=/var/mail/%u
mail_home = /var/mail/%d/%n/home/
mail_location = maildir:/var/mail/%u/%n
# If you need to set multiple mailbox locations or want to change default
# namespace settings, you can do it by defining namespace sections.
@ -111,7 +112,7 @@ namespace inbox {
# Group to enable temporarily for privileged operations. Currently this is
# used only with INBOX when either its initial creation or dotlocking fails.
# Typically this is set to "mail" to give access to /var/mail.
mail_privileged_group = mail
mail_privileged_group = jupiter
# Grant access to these supplementary groups for mail processes. Typically
# these are used to set up access to shared mailboxes. Note that it may be
@ -215,7 +216,7 @@ mail_privileged_group = mail
# Space separated list of plugins to load for all services. Plugins specific to
# IMAP, LDA, etc. are added to this list in their own .conf files.
#mail_plugins =
mail_plugins = $mail_plugins quota
##
## Mailbox handling optimizations
@ -282,7 +283,7 @@ protocol !indexer-worker {
# This is done by stat()ing each entry, so it causes more disk I/O.
# (For systems setting struct dirent->d_type, this check is free and it's
# done always regardless of this setting)
#maildir_stat_dirs = no
maildir_stat_dirs = yes
# When copying a message, do it with hard links whenever possible. This makes
# the performance much better, and it's unlikely to have any side effects.

View file

@ -98,15 +98,17 @@ service auth {
# something else than 0666 and Dovecot lets the kernel enforce the
# permissions (e.g. 0777 allows everyone full permissions).
unix_listener auth-userdb {
#mode = 0666
#user =
#group =
mode = 0666
user = jupiter
group = jupiter
}
# Postfix smtp-auth
#unix_listener /var/spool/postfix/private/auth {
# mode = 0666
#}
unix_listener /dev/shm/sasl-auth.sock {
mode = 0666
user = postfix
group = postfix
}
# Auth process is run as this user.
#user = $default_internal_user

View file

@ -10,8 +10,8 @@ ssl = no #yes
# dropping root privileges, so keep the key file unreadable by anyone but
# root. Included doc/mkcert.sh can be used to easily generate self-signed
# certificate, just make sure to update the domains in dovecot-openssl.cnf
ssl_cert = /etc/dovecot/private/dovecot.pem
ssl_key = /etc/dovecot/private/dovecot.key
#ssl_cert = /etc/dovecot/private/dovecot.pem
#ssl_key = /etc/dovecot/private/dovecot.key
# If key file is password protected, give the password here. Alternatively
# give it when starting dovecot with -p parameter. Since this file is often
@ -53,7 +53,7 @@ ssl_client_ca_dir = /etc/ssl/certs
# Generate new params with `openssl dhparam -out /etc/dovecot/dh.pem 4096`
# Or migrate from old ssl-parameters.dat file with the command dovecot
# gives on startup when ssl_dh is unset.
ssl_dh = </usr/share/dovecot/dh.pem
#ssl_dh = </usr/share/dovecot/dh.pem
# Minimum SSL protocol version to use. Potentially recognized values are SSLv3,
# TLSv1, TLSv1.1, TLSv1.2 and TLSv1.3, depending on the OpenSSL version used.

View file

@ -1,14 +0,0 @@
# 10-tcpwrapper.conf
#
# service name for hosts.{allow|deny} are those defined as
# inet_listener in master.conf
#
#login_access_sockets = tcpwrap
#
#service tcpwrap {
# unix_listener login/tcpwrap {
# group = $default_login_user
# mode = 0600
# user = $default_login_user
# }
#}

View file

@ -1,48 +0,0 @@
##
## LDA specific settings (also used by LMTP)
##
# Address to use when sending rejection mails.
# Default is postmaster@%d. %d expands to recipient domain.
#postmaster_address =
# Hostname to use in various parts of sent mails (e.g. in Message-Id) and
# in LMTP replies. Default is the system's real hostname@domain.
#hostname =
# If user is over quota, return with temporary failure instead of
# bouncing the mail.
#quota_full_tempfail = no
# Binary to use for sending mails.
#sendmail_path = /usr/sbin/sendmail
# If non-empty, send mails via this SMTP host[:port] instead of sendmail.
#submission_host =
# Subject: header to use for rejection mails. You can use the same variables
# as for rejection_reason below.
#rejection_subject = Rejected: %s
# Human readable error message for rejection mails. You can use variables:
# %n = CRLF, %r = reason, %s = original subject, %t = recipient
#rejection_reason = Your message to <%t> was automatically rejected:%n%r
# Delimiter character between local-part and detail in email address.
#recipient_delimiter = +
# Header where the original recipient address (SMTP's RCPT TO: address) is taken
# from if not available elsewhere. With dovecot-lda -a parameter overrides this.
# A commonly used header for this is X-Original-To.
#lda_original_recipient_header =
# Should saving a mail to a nonexistent mailbox automatically create it?
#lda_mailbox_autocreate = no
# Should automatically created mailboxes be also automatically subscribed?
#lda_mailbox_autosubscribe = no
protocol lda {
# Space separated list of plugins to load (default is global mail_plugins).
#mail_plugins = $mail_plugins
}

View file

@ -1,19 +0,0 @@
##
## Mailbox access control lists.
##
# vfile backend reads ACLs from "dovecot-acl" file from mail directory.
# You can also optionally give a global ACL directory path where ACLs are
# applied to all users' mailboxes. The global ACL directory contains
# one file for each mailbox, eg. INBOX or sub.mailbox. cache_secs parameter
# specifies how many seconds to wait between stat()ing dovecot-acl file
# to see if it changed.
plugin {
#acl = vfile:/etc/dovecot/global-acls:cache_secs=300
}
# To let users LIST mailboxes shared by other users, Dovecot needs a
# shared mailbox dictionary. For example:
plugin {
#acl_shared_dict = file:/var/lib/dovecot/shared-mailboxes
}

View file

@ -1,21 +0,0 @@
# Authentication for checkpassword users. Included from 10-auth.conf.
#
# <doc/wiki/AuthDatabase.CheckPassword.txt>
passdb {
driver = checkpassword
args = /usr/bin/checkpassword
}
# passdb lookup should return also userdb info
userdb {
driver = prefetch
}
# Standard checkpassword doesn't support direct userdb lookups.
# If you need checkpassword userdb, the checkpassword must support
# Dovecot-specific extensions.
#userdb {
# driver = checkpassword
# args = /usr/bin/checkpassword
#}

View file

@ -1,15 +0,0 @@
# Deny access for users. Included from 10-auth.conf.
# Users can be (temporarily) disabled by adding a passdb with deny=yes.
# If the user is found from that database, authentication will fail.
# The deny passdb should always be specified before others, so it gets
# checked first.
# Example deny passdb using passwd-file. You can use any passdb though.
passdb {
driver = passwd-file
deny = yes
# File contains a list of usernames, one per line
args = /etc/dovecot/deny-users
}

View file

@ -1,16 +0,0 @@
# Authentication via dict backend. Included from 10-auth.conf.
#
# <doc/wiki/AuthDatabase.Dict.txt>
passdb {
driver = dict
# Path for dict configuration file, see
# example-config/dovecot-dict-auth.conf.ext
args = /etc/dovecot/dovecot-dict-auth.conf.ext
}
userdb {
driver = dict
args = /etc/dovecot/dovecot-dict-auth.conf.ext
}

View file

@ -6,12 +6,13 @@
passdb {
driver = passwd-file
mechanisms = plain login
args = scheme=SHA512-CRYPT username_format=%u /etc/dovecot/users
args = scheme=SHA512-CRYPT username_format=%u /etc/dovecot/userdb
}
userdb {
driver = passwd-file
args = username_format=%u /etc/dovecot/users
args = username_format=%u /etc/dovecot/userdb
default_fields = uid=jupiter gid=jupiter home=/var/mail/%d/%u/home/
# Default fields that can be overridden by passwd-file
#default_fields = quota_rule=*:storage=1G

View file

@ -1,30 +0,0 @@
# Authentication for SQL users. Included from 10-auth.conf.
#
# <doc/wiki/AuthDatabase.SQL.txt>
passdb {
driver = sql
# Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
args = /etc/dovecot/dovecot-sql.conf.ext
}
# "prefetch" user database means that the passdb already provided the
# needed information and there's no need to do a separate userdb lookup.
# <doc/wiki/UserDatabase.Prefetch.txt>
#userdb {
# driver = prefetch
#}
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
# If you don't have any user-specific settings, you can avoid the user_query
# by using userdb static instead of userdb sql, for example:
# <doc/wiki/UserDatabase.Static.txt>
#userdb {
#driver = static
#args = uid=vmail gid=vmail home=/var/vmail/%u
#}

View file

@ -1,24 +0,0 @@
# Static passdb. Included from 10-auth.conf.
# This can be used for situations where Dovecot doesn't need to verify the
# username or the password, or if there is a single password for all users:
#
# - proxy frontend, where the backend verifies the password
# - proxy backend, where the frontend already verified the password
# - authentication with SSL certificates
# - simple testing
#passdb {
# driver = static
# args = proxy=y host=%1Mu.example.com nopassword=y
#}
#passdb {
# driver = static
# args = password=test
#}
#userdb {
# driver = static
# args = uid=vmail gid=vmail home=/home/%u
#}

View file

@ -1,74 +0,0 @@
# Authentication for system users. Included from 10-auth.conf.
#
# <doc/wiki/PasswordDatabase.txt>
# <doc/wiki/UserDatabase.txt>
# PAM authentication. Preferred nowadays by most systems.
# PAM is typically used with either userdb passwd or userdb static.
# REMEMBER: You'll need /etc/pam.d/dovecot file created for PAM
# authentication to actually work. <doc/wiki/PasswordDatabase.PAM.txt>
passdb {
driver = pam
# [session=yes] [setcred=yes] [failure_show_msg=yes] [max_requests=<n>]
# [cache_key=<key>] [<service name>]
#args = dovecot
}
# System users (NSS, /etc/passwd, or similar).
# In many systems nowadays this uses Name Service Switch, which is
# configured in /etc/nsswitch.conf. <doc/wiki/AuthDatabase.Passwd.txt>
#passdb {
#driver = passwd
# [blocking=no]
#args =
#}
# Shadow passwords for system users (NSS, /etc/shadow or similar).
# Deprecated by PAM nowadays.
# <doc/wiki/PasswordDatabase.Shadow.txt>
#passdb {
#driver = shadow
# [blocking=no]
#args =
#}
# PAM-like authentication for OpenBSD.
# <doc/wiki/PasswordDatabase.BSDAuth.txt>
#passdb {
#driver = bsdauth
# [blocking=no] [cache_key=<key>]
#args =
#}
##
## User databases
##
# System users (NSS, /etc/passwd, or similar). In many systems nowadays this
# uses Name Service Switch, which is configured in /etc/nsswitch.conf.
userdb {
# <doc/wiki/AuthDatabase.Passwd.txt>
driver = passwd
# [blocking=no]
#args =
# Override fields from passwd
#override_fields = home=/home/virtual/%u
}
# Static settings generated from template <doc/wiki/UserDatabase.Static.txt>
#userdb {
#driver = static
# Can return anything a userdb could normally return. For example:
#
# args = uid=500 gid=500 home=/var/mail/%u
#
# LDA and LMTP needs to look up users only from the userdb. This of course
# doesn't work with static userdb because there is no list of users.
# Normally static userdb handles this by doing a passdb lookup. This works
# with most passdbs, with PAM being the most notable exception. If you do
# the user verification another way, you can add allow_all_users=yes to
# the args in which case the passdb lookup is skipped.
#
#args =
#}

4
target/pam/pam.d/smtp Normal file
View file

@ -0,0 +1,4 @@
# /etc/pam.d/smtp
auth required pam_pgsql.so
account required pam_pgsql.so
password required pam_pgsql.so

12
target/pam/pam_pgsql.conf Normal file
View file

@ -0,0 +1,12 @@
# /etc/pam_pgsql.conf
database = mails
host = localhost
user = mailreader
password = mailreader-secret
table = users
user_column = userid
pwd_column = password
#expired_column = acc_expired
#newtok_column = acc_new_pwreq
pw_type = crypt
#debug

16
target/postfix/main.cf Normal file
View file

@ -0,0 +1,16 @@
# https://doc.dovecot.org/2.4.2/howto/virtual/postgresql.html
transport_maps = pgsql:/etc/postfix/transport.cf
virtual_uid_maps = pgsql:/etc/postfix/uids.cf
virtual_gid_maps = pgsql:/etc/postfix/gids.cf
# Note that we've set virtual_mailbox_base to /home, which means that it's
# prefixed to all home directories in SQL database.
virtual_mailbox_base = /home
virtual_mailbox_maps = pgsql:/etc/postfix/mailboxes.cf
virtual_maps = pgsql:/etc/postfix/virtual.cf
mydestination = $mydomain, $myhostname
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = domain.org
smtp_sasl_auth_enable = no

View file

@ -0,0 +1,3 @@
pwcheck_method: saslauthd
saslauthd_path: /etc/mux
mech_list: login plain

View file

@ -0,0 +1,8 @@
user=mailreader
# TODO: remove default password
password=mailreader-secret
dbname=mails
table=transport
select_field=transport
where_field=domain
hosts=localhost

8
target/postfix/uids.cf Normal file
View file

@ -0,0 +1,8 @@
user=mailreader
# TODO: remove default password
password=mailreader-secret
dbname=mails
table=users
select_field=uid
where_field=userid
hosts=localhost

View file

@ -0,0 +1 @@
host all all 127.0.0.1 255.255.255.255 password

4
target/sasl/saslauthd Normal file
View file

@ -0,0 +1,4 @@
# /etc/default/saslauthd
START=yes
MECHANISMS=pam
PARAMS="-r -m /var/spool/postfix/etc"

View file

@ -0,0 +1,4 @@
#!/bin/bash
sudo -u postgres createdb mails
sudo -u postgres psql mails -f ./build/mail-create-tables.sql

View file

@ -0,0 +1,21 @@
#!/bin/bash
set -e
apt install -y build-essential pkg-config m4 libtool automake autoconf libgcrypt20-dev libtool libpam0g-dev libpq-dev
git clone https://github.com/pam-pgsql/pam-pgsql
cd pam-pgsql
autoreconf --install
chmod +x ./configure
./configure
make
# sleep .5
# printf "\n\n\n"
# sleep .5
#
# make install
#
# exit 1

View file

@ -0,0 +1,50 @@
CREATE TABLE transport (
domain VARCHAR(128) NOT NULL,
transport VARCHAR(128) NOT NULL,
PRIMARY KEY (domain)
);
CREATE TABLE users (
userid VARCHAR(128) NOT NULL,
password VARCHAR(128),
realname VARCHAR(128),
uid INTEGER NOT NULL,
gid INTEGER NOT NULL,
home VARCHAR(128),
mail VARCHAR(255),
PRIMARY KEY (userid)
);
CREATE TABLE virtual (
address VARCHAR(255) NOT NULL,
userid VARCHAR(255) NOT NULL,
PRIMARY KEY (address)
);
create view postfix_mailboxes as
select userid, home||'/' as mailbox from users
union all
select domain as userid, 'dummy' as mailbox from transport;
create view postfix_virtual as
select userid, userid as address from users
union all
select userid, address from virtual;
-- TODO: we MUST NOT include default passwords in this script.
-- in future, it would be nice to generate these accounts as part of setup,
-- using credentials provided to us by the user.
-- `mailreader` could be randomly-generated, though.
CREATE USER mailreader PASSWORD 'mailreader-secret';
grant select on transport, users, virtual, postfix_mailboxes, postfix_virtual to mailreader;
create user mailwriter password 'mailwriter-secret';
grant select, insert, update, delete on transport, users, virtual, postfix_mailboxes, postfix_virtual to mailwriter;
-- TODO: remove example users
insert into transport (domain, transport) values ('domain.org', 'virtual:');
insert into transport (domain, transport) values ('foo.org', 'virtual:');
insert into users (userid, uid, gid, home) values ('user@domain.org', 1001, 1001, 'domain.org/mails/user');
insert into users (userid, uid, gid, home) values ('user2@domain.org', 1001, 1001, 'domain.org/mails/user2');
insert into users (userid, uid, gid, home) values ('user@foo.org', 1002, 1002, 'foo.org/mails/user');
insert into virtual (address, userid) values ('foo@foo.org', 'user@foo.org');

View file

@ -1,8 +1,12 @@
#!/bin/bash
set -e
DEBUG_PACKAGES=(procps)
POSTFIX_PACKAGES=(postfix)
DOVECOT_PACKAGES=(dovecot-core dovecot-imapd dovecot-ldap)
# libpam-pgsql is only available in debian sid at the moment; building in compile step
DB_PACKAGES=(sasl2-bin libsasl2-modules postgresql)
POSTFIX_PACKAGES=(postfix postfix-pgsql)
DOVECOT_PACKAGES=(dovecot-core dovecot-imapd dovecot-pgsql)
RSPAMD_PACKAGES=(rspamd redis-server)
FAIL2BAN_PACKAGES=(fail2ban)
@ -10,6 +14,7 @@ PACKAGES=(
tini
supervisor
${DEBUG_PACKAGES[@]}
${DB_PACKAGES[@]}
${POSTFIX_PACKAGES[@]}
${DOVECOT_PACKAGES[@]}
# ${RSPAMD_PACKAGES[@]}

View file

@ -17,9 +17,11 @@ start_daemon () {
# start daemons
log "info" "Starting daemons..."
start_daemon fail2ban-server
start_daemon rspamd
start_daemon rspamd-redis
# start_daemon fail2ban
# start_daemon rspamd
# start_daemon rspamd-redis
start_daemon saslauthd
start_daemon postgresql
start_daemon dovecot
start_daemon postfix

View file

@ -34,10 +34,29 @@ autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
[program:dovecot]
command=/usr/sbin/dovecot
[program:saslauthd]
command=/usr/sbin/saslauthd
autostart=false
startsecs=3
startsecs=0
stopwaitsecs=55
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
[program:postgresql]
command=/usr/lib/postgresql/15/bin/postgres -D /etc/postgresql/15/main
user=postgres
autostart=false
startsecs=0
stopwaitsecs=55
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
[program:dovecot]
command=/usr/sbin/dovecot -F -c /etc/dovecot/dovecot.conf
autostart=false
startsecs=0
stopwaitsecs=55
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
@ -46,7 +65,7 @@ stderr_logfile=/var/log/supervisor/%(program_name)s.log
[program:postfix]
command=/usr/sbin/postfix
autostart=false
startsecs=3
startsecs=0
stopwaitsecs=55
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log