feat: configure mail fetcher with offlineimap

- Use tags to help on the Qrexec policy notation;
- Create AppVMs also to fetch and send emails, useful for OfflineIMAP
  that requires sync;
- OfflineIMAP is smart enough depending on the server, such as Gmail;
- Quote options managed by the user such as password fields as they
  could contain spaces; and
- Default fetching method to always keep files on the remote to avoid
  users being surprised about the fetcher behavior or losing data.
This commit is contained in:
Ben Grande 2025-01-08 16:55:37 +01:00
parent b03ceb500c
commit 3d4ab18d28
No known key found for this signature in database
GPG Key ID: 00C64E14F51F9E56
15 changed files with 325 additions and 94 deletions

View File

@ -22,6 +22,7 @@ host:
- rpm_spec/qusal-fedora-xfce.spec
- rpm_spec/qusal-fetcher.spec
- rpm_spec/qusal-kicksecure-minimal.spec
- rpm_spec/qusal-mail.spec
- rpm_spec/qusal-media.spec
- rpm_spec/qusal-mgmt.spec
- rpm_spec/qusal-mirage-builder.spec
@ -50,6 +51,7 @@ host:
- rpm_spec/qusal-sys-ssh.spec
- rpm_spec/qusal-sys-ssh-agent.spec
- rpm_spec/qusal-sys-syncthing.spec
- rpm_spec/qusal-sys-tailscale.spec
- rpm_spec/qusal-sys-usb.spec
- rpm_spec/qusal-sys-wireguard.spec
- rpm_spec/qusal-terraform.spec

View File

@ -23,15 +23,15 @@ Mail operations in Qubes OS.
## Description
Create a mail fetcher qube named "mail-fetcher", a mail reader qube names
"mail-reader" and a mail sender qube named "mail-sender".
Create a mail fetcher qube named "(disp-)mail-fetcher", a mail reader qube
names "mail-reader" and a mail sender qube named "(disp-)mail-sender".
The online "mail-fetcher" qube will fetch messages with POP3. After being
fetched, you can copy them to the offline "mail-reader" qube, where you will
be reading emails. After composing a message, the "mail-reader" qube will
The online "(disp-)mail-fetcher" qube will fetch messages with POP3. After
being fetched, you can copy them to the offline "mail-reader" qube, where you
will be reading emails. After composing a message, the "mail-reader" qube will
save the messages to a queue, which can be forwarded to the online
"mail-sender" qube. You can review messages to be sent from the "mail-sender"
qube and them send them via SMTP.
"(disp-)mail-sender" qube. You can review messages to be sent from the
"(disp-)mail-sender" qube and them send them via SMTP.
By default, the protocols used required SSL, POP3 on port 995, IMAP on port
995 and SMTP on port 587. You can always override any configuration via
@ -48,12 +48,13 @@ causes problems.
Mail is insecure per nature and users depend on archaic Unix tools that
[receive little to no maintenance](https://xkcd.com/2347/).
The qubes connected to the internet `mail-fetcher` and `mail-sender` hold the
account password to connect to the remote servers. If any of those are
compromised, your mail account can also be. Network firewall can help, to
some extent, if you consider the attacker doesn't have an account on the same
mail server you have, or sends a message from you mail account to an attacker
controlled mail and then delete from your sent messages.
The qubes connected to the internet `(disp-)mail-fetcher` and
`(disp-)mail-sender` hold the account password to connect to the remote
servers. If any of those are compromised, your mail account can also be.
Network firewall can help, to some extent, if you consider the attacker
doesn't have an account on the same mail server you have, or sends a message
from you mail account to an attacker controlled mail and then delete from your
sent messages.
The reader qube `mail-reader` also has a high attack surface. Although
offline, it can access PGP keys via split-gpg2 and also read all your mails,
@ -66,11 +67,11 @@ secure mail client, but there are none. `Mutt` will open `text/html` and
qube. See [reader](../reader/README.md) for offline disposables that can open
some kinds of files.
If you want to read the mail in the sender qube `mail-sender`, you may want to
do this before sending to the mail server, you should open the file in a
disposable to avoid a parsing bug in the editor to extract information such as
the password from the sender qube. This method doesn't prevent all kinds of
exploitation, as `msmtp` still needs to parse the mail to be sent.
You may want to read the mail in the sender qube `(disp-)mail-sender` before
sending to the mail server, you should open the file in a disposable to avoid
a parsing bug in the editor to extract information such as the password from
the sender qube. This method doesn't prevent all kinds of exploitation, as
`msmtp` still needs to parse the mail to be sent.
## Installation
@ -80,7 +81,7 @@ exploitation, as `msmtp` still needs to parse the mail to be sent.
sudo qubesctl top.enable mail reader
sudo qubesctl --targets=tpl-mail-fetcher,tpl-mail-reader,tpl-mail-sender,dvm-mail-fetcher,mail-reader,dvm-mail-sender,tpl-reader state.apply
sudo qubesctl top.disable mail reader
sudo qubesctl state.apply mail.appmenus,reader.appmenus
sudo qubesctl state.apply mail.appmenus
```
* State:
@ -96,7 +97,7 @@ sudo qubesctl --skip-dom0 --targets=tpl-mail-sender state.apply mail.install-sen
sudo qubesctl --skip-dom0 --targets=dvm-mail-fetcher state.apply mail.configure-fetcher
sudo qubesctl --skip-dom0 --targets=mail-reader state.apply mail.configure-reader
sudo qubesctl --skip-dom0 --targets=dvm-mail-sender state.apply mail.configure-sender
sudo qubesctl state.apply mail.appmenus,reader.appmenus
sudo qubesctl state.apply mail.appmenus
```
<!-- pkg:end:post-install -->
@ -107,23 +108,27 @@ You will use local files to override the ones provided by this package. Few
options must be set. Do not change the directories in the configuration
files, they need to stay the same.
You should firewall the `mail-fetcher` and `mail-sender` to the `POP3` server
or/and `IMAP` server and `SMTP` server, respectively.
You should firewall the `(disp-)mail-fetcher` and `(disp-)mail-sender` to the
`POP3` server or/and `IMAP` server and `SMTP` server, respectively.
Steps overview:
1. Receive mail via the `mail-fetcher` and transfer mail to `mail-reader`.
2. Read and compose mail from `mail-reader` and transfer to `mail-sender`.
3. Send queued mails from `mail-sender` to remote mail server.
1. Receive mail via the `(disp-)mail-fetcher` and transfer mail to
`mail-reader`.
2. Read and compose mail from `mail-reader` and transfer to
`(disp-)mail-sender`.
3. Send queued mails from `(disp-)mail-sender` to remote mail server.
### Fetcher
The fetcher fetches e-mails with `fdm` or `mpop` via the POP3 protocol or even
The fetcher fetches e-mails with `fdm` or `mpop` via the POP3 protocol or with
`offlineimap` via the IMAP protocol, you only need to choose one program for
this task, depending on your needs.
this task, depending on your needs. Please note that when using the POP3
protocol, only the INBOX will be fetched while when using IMAP, you can choose
which folders to fetch, defaults to fetch all folders.
The configuration must be done in `dvm-mail-fetcher`, while the fetching of
mails will be done in `disp-mail-fetcher`.
mails will be done in `(disp-)mail-fetcher`.
#### fdm Configuration
@ -139,7 +144,7 @@ Edit the configuration according to your needs:
editor ~/.fdm.conf
```
Check the connection is working:
Check if the connection is working:
```sh
fdm -kv poll
@ -160,9 +165,6 @@ systemctl --user start fdm.timer
#### mpop Configuration
Copy `~/.mpoprc.example` to `~/.mpoprc` and edit the configuration
according to your needs.
Copy example configuration file to where the program can read automatically:
```sh
@ -175,7 +177,7 @@ Edit the configuration according to your needs:
editor ~/.mpoprc
```
Check the connection is working:
Check if the connection is working:
```sh
mpop --debug --auth-only
@ -196,7 +198,43 @@ systemctl --user start mpop.timer
#### OfflineIMAP Configuration
TODO: difficult to exemplify as the folders are user and provider specific.
Copy example configuration file to where the program can read automatically:
```sh
cp -- ~/.netrc.example ~/.netrc
cp -- ~/.offlineimaprc.example ~/.offlineimaprc
```
Edit the configuration according to your needs:
```sh
editor ~/.netrc ~/.offlinemaprc
```
Check if the connection is working:
```sh
offlineimap --info
```
<!--
Ideally '--dry-run' would be used instead of `--info`, but it fails if
offlineimap has not been run yet to create the same directories available on
the remote.
-->
Fetch mail:
```sh
offlineimap
```
If the fetch was successful, enable the fetch scheduler:
```sh
systemctl --user enable offlineimap-oneshot.timer
systemctl --user start offlineimap-oneshot.timer
```
#### Send Inbox to Reader Qube
@ -242,7 +280,7 @@ qusal-send-mail
The sender sends e-mails with `msmtp` via the SMTP protocol.
The configuration must be done in `dvm-mail-sender`, while the sending of
mails are done in `disp-mail-sender`.
mails are done in `(disp-)mail-sender`.
#### msmtp Configuration

View File

@ -1,8 +1,13 @@
{#
SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-License-Identifier: AGPL-3.0-or-later
#}
include:
- reader.appmenus
{% from 'utils/macros/sync-appmenus.sls' import sync_appmenus -%}
{{ sync_appmenus('tpl-' ~ sls_path ~ '-sender') }}
{{ sync_appmenus('tpl-' ~ sls_path ~ '-reader') }}
{{ sync_appmenus('tpl-' ~ sls_path ~ '-fetcher') }}

View File

@ -1,5 +1,5 @@
{#
SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-License-Identifier: AGPL-3.0-or-later
#}
@ -28,4 +28,22 @@ include:
- group: user
- makedirs: True
"{{ slsdotpath }}-fetcher-offlineimaprc.example":
file.managed:
- name: /home/user/.offlineimaprc.example
- source: salt://{{ slsdotpath }}/files/fetcher/offlineimaprc.example
- mode: "0600"
- user: user
- group: user
- makedirs: True
"{{ slsdotpath }}-fetcher-netrc.example":
file.managed:
- name: /home/user/.netrc.example
- source: salt://{{ slsdotpath }}/files/fetcher/netrc.example
- mode: "0600"
- user: user
- group: user
- makedirs: True
{% endif -%}

View File

@ -1,5 +1,5 @@
{#
SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-License-Identifier: AGPL-3.0-or-later
#}
@ -21,4 +21,12 @@ include:
- group: user
- makedirs: True
"{{ slsdotpath }}-reader-mutt-offline":
file.symlink:
- require:
- pkg: dotfiles.copy-mutt
- name: /home/user/.config/mutt/90_offline.muttrc
- source: /home/user/.config/mutt/sample/offline.muttrc.example
- force: True
{% endif -%}

View File

@ -1,5 +1,5 @@
{#
SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-License-Identifier: AGPL-3.0-or-later
#}
@ -43,6 +43,100 @@ prefs:
{%- endload %}
{{ load(defaults) }}
{% load_yaml as defaults -%}
name: {{ slsdotpath }}-fetcher
force: True
require:
- qvm: tpl-{{ slsdotpath }}-fetcher
present:
- template: tpl-{{ slsdotpath }}-fetcher
- label: red
prefs:
- template: tpl-{{ slsdotpath }}-fetcher
- label: red
- audiovm: ""
- vcpus: 1
- memory: 200
- maxmem: 350
- include_in_backups: False
features:
- enable:
- servicevm
- disable:
- service.cups
- service.cups-browsed
- service.tinyproxy
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop"
tags:
- add:
- "mail-fetcher"
{%- endload %}
{{ load(defaults) }}
{% load_yaml as defaults -%}
name: {{ slsdotpath }}-reader
force: True
require:
- qvm: tpl-{{ slsdotpath }}-reader
present:
- template: tpl-{{ slsdotpath }}-reader
- label: red
prefs:
- template: tpl-{{ slsdotpath }}-reader
- label: red
- audiovm: ""
- vcpus: 1
- memory: 200
- maxmem: 350
- include_in_backups: False
features:
- enable:
- service.split-gpg2-client
- disable:
- service.cups
- service.cups-browsed
- service.tinyproxy
- set:
- menu-items: "mutt.desktop qubes-run-terminal.desktop qubes-start.desktop"
tags:
- add:
- "mail-reader"
{%- endload %}
{{ load(defaults) }}
{% load_yaml as defaults -%}
name: {{ slsdotpath }}-sender
force: True
require:
- qvm: tpl-{{ slsdotpath }}-sender
present:
- template: tpl-{{ slsdotpath }}-sender
- label: red
prefs:
- template: tpl-{{ slsdotpath }}-sender
- label: red
- audiovm: ""
- vcpus: 1
- memory: 200
- maxmem: 350
- include_in_backups: False
features:
- enable:
- servicevm
- disable:
- service.cups
- service.cups-browsed
- service.tinyproxy
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop"
tags:
- add:
- "mail-sender"
{%- endload %}
{{ load(defaults) }}
{% load_yaml as defaults -%}
name: dvm-{{ slsdotpath }}-fetcher
force: True
@ -67,30 +161,8 @@ features:
- service.cups
- service.cups-browsed
- service.tinyproxy
{%- endload %}
{{ load(defaults) }}
{% load_yaml as defaults -%}
name: {{ slsdotpath }}-reader
force: True
require:
- qvm: tpl-{{ slsdotpath }}-reader
present:
- template: tpl-{{ slsdotpath }}-reader
- label: red
prefs:
- template: tpl-{{ slsdotpath }}-fetcher
- label: red
- audiovm: ""
- vcpus: 1
- memory: 200
- maxmem: 350
- include_in_backups: False
features:
- disable:
- service.cups
- service.cups-browsed
- service.tinyproxy
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop"
{%- endload %}
{{ load(defaults) }}
@ -118,6 +190,8 @@ features:
- service.cups
- service.cups-browsed
- service.tinyproxy
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop"
{%- endload %}
{{ load(defaults) }}
@ -145,6 +219,11 @@ features:
- service.cups
- service.cups-browsed
- service.tinyproxy
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop"
tags:
- add:
- "mail-fetcher"
{%- endload %}
{{ load(defaults) }}
@ -172,6 +251,11 @@ features:
- service.cups
- service.cups-browsed
- service.tinyproxy
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop"
tags:
- add:
- "mail-sender"
{%- endload %}
{{ load(defaults) }}

View File

@ -1,11 +1,14 @@
# SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
# SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
## Do not modify this file, create a new policy with with a lower number in the
## file name instead. For example `30-user.policy`.
qusal.MailFetch * disp-mail-fetcher @default ask target=mail-reader default_target=mail-reader
qusal.MailFetch * @tag:mail-fetcher @tag:mail-reader ask
qusal.MailFetch * @tag:mail-fetcher @default ask default_target=mail-reader
qusal.MailFetch * @anyvm @anyvm deny
qusal.MailEnqueue * mail-reader @default ask target=disp-mail-sender default_target=disp-mail-sender
qusal.MailEnqueue * @tag:mail-reader @tag:mail-sender ask
qusal.MailEnqueue * @tag:mail-reader @default ask
qusal.MailEnqueue * @anyvm @anyvm deny
## vim:ft=qrexecpolicy

View File

@ -1,6 +1,6 @@
# ~/.fdm.conf
## SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
## SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
##
## SPDX-License-Identifier: AGPL-3.0-or-later
@ -8,10 +8,11 @@
action "inbox" maildir "%h/mail/INBOX"
##### EDIT THIS BLOCK #####
account "john-doe" pop3s
server "pop.mail.example"
user "john-doe"
pass "secret123"
account "john-doe@mail.example" pop3s
server "pop.mail.example"
user "john-doe@mail.example"
pass "secret123"
keep
##### DO NOT EDIT THIS BLOCK #####
match all action "inbox"

View File

@ -1,6 +1,6 @@
## ~/.mpoprc
## SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
## SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
##
## SPDX-License-Identifier: AGPL-3.0-or-later
@ -12,9 +12,12 @@ defaults
delivery maildir ~/mail/INBOX
uidls_file ~/.local/share/%U_at_%H
##### EDIT THIS BLOCK #####
account main
host pop.mail.example
user john-doe
password secret123
##### EDIT THIS BLOCK #####
host "pop.mail.example"
user "john-doe@mail.example"
password "secret123"
keep on
##### DO NOT EDIT THIS BLOCK #####
account default : main

View File

@ -0,0 +1,15 @@
# ~/.netrc
## SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
##
## SPDX-License-Identifier: AGPL-3.0-or-later
##### EDIT THIS BLOCK #####
machine "imap.gmail.com"
login "john-doe@mail.example"
password "secret123"
machine "pop.gmail.com"
login "john-doe@mail.example"
password "secret123"
# vim: ft=netrc

View File

@ -0,0 +1,29 @@
# ~/.offlineimaprc
## SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
##
## SPDX-License-Identifier: AGPL-3.0-or-later
##### DO NOT EDIT THIS BLOCK #####
[general]
accounts = main
[Account main]
localrepository = main-local
remoterepository = main-remote
[Repository main-local]
localfolders = ~/mail
##### EDIT THIS BLOCK #####
sync_deletes = no
type = Maildir
##### DO NOT EDIT THIS BLOCK #####
[Repository main-remote]
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
##### EDIT THIS BLOCK #####
synclabels = yes
readonly = yes
sync_deletes = no
type = IMAP
remotehost = imap.mail.example
remoteuser = john-doe@mail.example
# vim: ft=toml

View File

@ -1,5 +1,5 @@
{#
SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-License-Identifier: AGPL-3.0-or-later
#}
@ -33,24 +33,32 @@ include:
- libsasl2-modules
- libsasl2-modules-db
"{{ slsdotpath }}-fetcher-systemd-fdm.timer":
file.managed:
- name: /usr/lib/systemd/user/fdm.timer
- source: salt://{{ slsdotpath }}/files/fetcher/systemd/fdm.timer
- mode: "0644"
"{{ slsdotpath }}-fetcher-symlink-offlineimap-oneshort.service":
file.symlink:
- require:
- pkg: "{{ slsdotpath }}-fetcher-installed"
- name: /usr/lib/systemd/user/offlineimap-oneshot.service
- target: /usr/share/doc/offlineimap3/examples/systemd/offlineimap-oneshot.service
- force: True
"{{ slsdotpath }}-fetcher-symlink-offlineimap-oneshort.timer":
file.symlink:
- require:
- pkg: "{{ slsdotpath }}-fetcher-installed"
- name: /usr/lib/systemd/user/offlineimap-oneshot.timer
- target: /usr/share/doc/offlineimap3/examples/systemd/offlineimap-oneshot.timer
- force: True
"{{ slsdotpath }}-fetcher-systemd-user":
file.recurse:
- name: /usr/lib/systemd/user/
- source: salt://{{ slsdotpath }}/files/fetcher/systemd/
- dir_mode: "0755"
- file_mode: "0644"
- user: root
- group: root
- makedirs: True
"{{ slsdotpath }}-fetcher-systemd-fdm.service":
file.managed:
- name: /usr/lib/systemd/user/fdm.service
- source: salt://{{ slsdotpath }}/files/fetcher/systemd/fdm.service
- mode: "0644"
- user: root
- group: root
- makedirs: true
"{{ slsdotpath }}-fetcher-bin":
file.managed:
- name: /usr/bin/qusal-send-inbox

View File

@ -1,5 +1,5 @@
{#
SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-License-Identifier: AGPL-3.0-or-later
#}
@ -29,6 +29,7 @@ include:
- vim
- mutt
- notmuch-mutt
- msmtp
- w3m
- less
- urlview

View File

@ -1,5 +1,5 @@
{#
SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
SPDX-License-Identifier: AGPL-3.0-or-later
#}
@ -32,6 +32,22 @@ include:
- libsasl2-modules
- libsasl2-modules-db
"{{ slsdotpath }}-sender-symlink-msmtpq":
file.symlink:
- require:
- pkg: "{{ slsdotpath }}-sender-installed"
- name: /usr/bin/msmtpq
- target: /usr/libexec/msmtp/msmtpq/msmtpq
- force: True
"{{ slsdotpath }}-sender-symlink-msmtp-queue":
file.symlink:
- require:
- pkg: "{{ slsdotpath }}-sender-installed"
- name: /usr/bin/msmtp-queue
- target: /usr/libexec/msmtp/msmtpq/msmtp-queue
- force: True
"{{ slsdotpath }}-sender-rpc":
file.managed:
- name: /etc/qubes-rpc/qusal.MailEnqueue

View File

@ -28,7 +28,7 @@ fi
ignored="$(git ls-files --exclude-standard --others --ignored salt/)"
untracked="$(git ls-files --exclude-standard --others salt/)"
unwanted="$(printf '%s\n%s\n' "${ignored}" "${untracked}" |
grep -E "^salt/\S+/(README.md|.*\.sls|files/.*)$" | cut -d "/" -f2 |
grep -E "^salt/\S+/(README.md|version)$" | cut -d "/" -f2 |
sort -u)"
group="$(./scripts/spec-get.sh dom0 group)"
projects="$(find salt/ -mindepth 1 -maxdepth 1 -type d | sort -d |