mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2025-10-03 00:02:53 -04:00
Compare commits
4 Commits
336c872f03
...
b66ac1fecf
Author | SHA1 | Date | |
---|---|---|---|
|
b66ac1fecf | ||
|
796e131c3a | ||
|
f52f125097 | ||
|
6d3d7a0294 |
@ -1,12 +1,13 @@
|
|||||||
# Whitelist generated by Postwhite v3.4 on Mon Sep 1 00:23:07 UTC 2025
|
# Whitelist generated by Postwhite v3.4 on Wed Oct 1 00:21:33 UTC 2025
|
||||||
# https://github.com/stevejenkins/postwhite/
|
# https://github.com/stevejenkins/postwhite/
|
||||||
# 2165 total rules
|
# 2216 total rules
|
||||||
2a00:1450:4000::/36 permit
|
2a00:1450:4000::/36 permit
|
||||||
2a01:111:f400::/48 permit
|
2a01:111:f400::/48 permit
|
||||||
2a01:111:f403::/49 permit
|
2a01:111:f403:2800::/53 permit
|
||||||
2a01:111:f403:8000::/50 permit
|
|
||||||
2a01:111:f403:8000::/51 permit
|
2a01:111:f403:8000::/51 permit
|
||||||
|
2a01:111:f403::/49 permit
|
||||||
2a01:111:f403:c000::/51 permit
|
2a01:111:f403:c000::/51 permit
|
||||||
|
2a01:111:f403:d000::/53 permit
|
||||||
2a01:111:f403:f000::/52 permit
|
2a01:111:f403:f000::/52 permit
|
||||||
2a01:238:20a:202:5370::1 permit
|
2a01:238:20a:202:5370::1 permit
|
||||||
2a01:238:20a:202:5372::1 permit
|
2a01:238:20a:202:5372::1 permit
|
||||||
@ -55,7 +56,8 @@
|
|||||||
8.40.222.0/23 permit
|
8.40.222.0/23 permit
|
||||||
8.40.222.250/31 permit
|
8.40.222.250/31 permit
|
||||||
12.130.86.238 permit
|
12.130.86.238 permit
|
||||||
13.107.246.40 permit
|
13.107.213.41 permit
|
||||||
|
13.107.246.41 permit
|
||||||
13.110.208.0/21 permit
|
13.110.208.0/21 permit
|
||||||
13.110.209.0/24 permit
|
13.110.209.0/24 permit
|
||||||
13.110.216.0/22 permit
|
13.110.216.0/22 permit
|
||||||
@ -174,6 +176,7 @@
|
|||||||
35.161.32.253 permit
|
35.161.32.253 permit
|
||||||
35.162.73.231 permit
|
35.162.73.231 permit
|
||||||
35.167.93.243 permit
|
35.167.93.243 permit
|
||||||
|
35.174.145.124 permit
|
||||||
35.176.132.251 permit
|
35.176.132.251 permit
|
||||||
35.205.92.9 permit
|
35.205.92.9 permit
|
||||||
35.228.216.85 permit
|
35.228.216.85 permit
|
||||||
@ -183,7 +186,6 @@
|
|||||||
37.218.249.47 permit
|
37.218.249.47 permit
|
||||||
37.218.251.62 permit
|
37.218.251.62 permit
|
||||||
39.156.163.64/29 permit
|
39.156.163.64/29 permit
|
||||||
40.90.65.81 permit
|
|
||||||
40.92.0.0/15 permit
|
40.92.0.0/15 permit
|
||||||
40.92.0.0/16 permit
|
40.92.0.0/16 permit
|
||||||
40.107.0.0/16 permit
|
40.107.0.0/16 permit
|
||||||
@ -271,9 +273,6 @@
|
|||||||
50.56.130.221 permit
|
50.56.130.221 permit
|
||||||
50.56.130.222 permit
|
50.56.130.222 permit
|
||||||
50.112.246.219 permit
|
50.112.246.219 permit
|
||||||
51.77.79.158 permit
|
|
||||||
51.83.17.38 permit
|
|
||||||
51.89.119.103 permit
|
|
||||||
52.1.14.157 permit
|
52.1.14.157 permit
|
||||||
52.5.230.59 permit
|
52.5.230.59 permit
|
||||||
52.6.74.205 permit
|
52.6.74.205 permit
|
||||||
@ -324,8 +323,6 @@
|
|||||||
52.234.172.96/28 permit
|
52.234.172.96/28 permit
|
||||||
52.235.253.128 permit
|
52.235.253.128 permit
|
||||||
52.236.28.240/28 permit
|
52.236.28.240/28 permit
|
||||||
54.36.149.183 permit
|
|
||||||
54.38.221.122 permit
|
|
||||||
54.90.148.255 permit
|
54.90.148.255 permit
|
||||||
54.165.19.38 permit
|
54.165.19.38 permit
|
||||||
54.174.52.0/24 permit
|
54.174.52.0/24 permit
|
||||||
@ -686,6 +683,8 @@
|
|||||||
82.165.159.45 permit
|
82.165.159.45 permit
|
||||||
82.165.159.130 permit
|
82.165.159.130 permit
|
||||||
82.165.159.131 permit
|
82.165.159.131 permit
|
||||||
|
85.9.206.169 permit
|
||||||
|
85.9.210.45 permit
|
||||||
85.158.136.0/21 permit
|
85.158.136.0/21 permit
|
||||||
85.215.255.39 permit
|
85.215.255.39 permit
|
||||||
85.215.255.40 permit
|
85.215.255.40 permit
|
||||||
@ -1234,16 +1233,14 @@
|
|||||||
99.83.190.102 permit
|
99.83.190.102 permit
|
||||||
103.9.96.0/22 permit
|
103.9.96.0/22 permit
|
||||||
103.28.42.0/24 permit
|
103.28.42.0/24 permit
|
||||||
103.122.78.238 permit
|
103.84.217.238 permit
|
||||||
|
103.89.75.238 permit
|
||||||
103.151.192.0/23 permit
|
103.151.192.0/23 permit
|
||||||
103.168.172.128/27 permit
|
103.168.172.128/27 permit
|
||||||
103.237.104.0/22 permit
|
103.237.104.0/22 permit
|
||||||
104.43.243.237 permit
|
104.43.243.237 permit
|
||||||
104.44.112.128/25 permit
|
104.44.112.128/25 permit
|
||||||
104.47.0.0/17 permit
|
104.47.0.0/17 permit
|
||||||
104.47.20.0/23 permit
|
|
||||||
104.47.75.0/24 permit
|
|
||||||
104.47.108.0/23 permit
|
|
||||||
104.130.96.0/28 permit
|
104.130.96.0/28 permit
|
||||||
104.130.122.0/23 permit
|
104.130.122.0/23 permit
|
||||||
106.10.144.64/27 permit
|
106.10.144.64/27 permit
|
||||||
@ -1378,7 +1375,6 @@
|
|||||||
108.174.6.215 permit
|
108.174.6.215 permit
|
||||||
108.175.18.45 permit
|
108.175.18.45 permit
|
||||||
108.175.30.45 permit
|
108.175.30.45 permit
|
||||||
108.177.96.0/20 permit
|
|
||||||
108.179.144.0/20 permit
|
108.179.144.0/20 permit
|
||||||
109.224.244.0/24 permit
|
109.224.244.0/24 permit
|
||||||
109.237.142.0/24 permit
|
109.237.142.0/24 permit
|
||||||
@ -1544,6 +1540,7 @@
|
|||||||
148.105.0.0/16 permit
|
148.105.0.0/16 permit
|
||||||
148.105.8.0/21 permit
|
148.105.8.0/21 permit
|
||||||
149.72.0.0/16 permit
|
149.72.0.0/16 permit
|
||||||
|
149.72.234.184 permit
|
||||||
149.72.248.236 permit
|
149.72.248.236 permit
|
||||||
149.97.173.180 permit
|
149.97.173.180 permit
|
||||||
150.230.98.160 permit
|
150.230.98.160 permit
|
||||||
@ -1599,6 +1596,7 @@
|
|||||||
159.183.0.0/16 permit
|
159.183.0.0/16 permit
|
||||||
159.183.68.71 permit
|
159.183.68.71 permit
|
||||||
159.183.79.38 permit
|
159.183.79.38 permit
|
||||||
|
159.183.129.172 permit
|
||||||
160.1.62.192 permit
|
160.1.62.192 permit
|
||||||
161.38.192.0/20 permit
|
161.38.192.0/20 permit
|
||||||
161.38.204.0/22 permit
|
161.38.204.0/22 permit
|
||||||
@ -1616,6 +1614,7 @@
|
|||||||
163.114.134.16 permit
|
163.114.134.16 permit
|
||||||
163.114.135.16 permit
|
163.114.135.16 permit
|
||||||
163.116.128.0/17 permit
|
163.116.128.0/17 permit
|
||||||
|
163.192.116.87 permit
|
||||||
164.152.23.32 permit
|
164.152.23.32 permit
|
||||||
164.152.25.241 permit
|
164.152.25.241 permit
|
||||||
164.177.132.168/30 permit
|
164.177.132.168/30 permit
|
||||||
@ -1655,6 +1654,7 @@
|
|||||||
169.148.131.0/24 permit
|
169.148.131.0/24 permit
|
||||||
169.148.138.0/24 permit
|
169.148.138.0/24 permit
|
||||||
169.148.142.10 permit
|
169.148.142.10 permit
|
||||||
|
169.148.142.33 permit
|
||||||
169.148.144.0/25 permit
|
169.148.144.0/25 permit
|
||||||
169.148.144.10 permit
|
169.148.144.10 permit
|
||||||
169.148.146.0/23 permit
|
169.148.146.0/23 permit
|
||||||
@ -1666,11 +1666,7 @@
|
|||||||
170.10.132.56/29 permit
|
170.10.132.56/29 permit
|
||||||
170.10.132.64/29 permit
|
170.10.132.64/29 permit
|
||||||
170.10.133.0/24 permit
|
170.10.133.0/24 permit
|
||||||
172.217.0.0/20 permit
|
|
||||||
172.217.32.0/20 permit
|
172.217.32.0/20 permit
|
||||||
172.217.128.0/19 permit
|
|
||||||
172.217.160.0/20 permit
|
|
||||||
172.217.192.0/19 permit
|
|
||||||
172.253.56.0/21 permit
|
172.253.56.0/21 permit
|
||||||
172.253.112.0/20 permit
|
172.253.112.0/20 permit
|
||||||
173.0.84.0/29 permit
|
173.0.84.0/29 permit
|
||||||
@ -2209,17 +2205,17 @@
|
|||||||
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
|
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
|
||||||
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
|
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
|
||||||
2607:f8b0:4000::/36 permit
|
2607:f8b0:4000::/36 permit
|
||||||
2620:109:c003:104::215 permit
|
|
||||||
2620:109:c003:104::/64 permit
|
2620:109:c003:104::/64 permit
|
||||||
2620:109:c006:104::215 permit
|
2620:109:c003:104::215 permit
|
||||||
2620:109:c006:104::/64 permit
|
2620:109:c006:104::/64 permit
|
||||||
|
2620:109:c006:104::215 permit
|
||||||
2620:109:c00d:104::/64 permit
|
2620:109:c00d:104::/64 permit
|
||||||
2620:10d:c090:400::8:1 permit
|
2620:10d:c090:400::8:1 permit
|
||||||
2620:10d:c091:400::8:1 permit
|
2620:10d:c091:400::8:1 permit
|
||||||
2620:10d:c09b:400::8:1 permit
|
2620:10d:c09b:400::8:1 permit
|
||||||
2620:10d:c09c:400::8:1 permit
|
2620:10d:c09c:400::8:1 permit
|
||||||
2620:119:50c0:207::215 permit
|
|
||||||
2620:119:50c0:207::/64 permit
|
2620:119:50c0:207::/64 permit
|
||||||
|
2620:119:50c0:207::215 permit
|
||||||
2800:3f0:4000::/36 permit
|
2800:3f0:4000::/36 permit
|
||||||
49.12.4.251 permit # checks.mailcow.email
|
49.12.4.251 permit # checks.mailcow.email
|
||||||
2a01:4f8:c17:7906::10 permit # checks.mailcow.email
|
2a01:4f8:c17:7906::10 permit # checks.mailcow.email
|
||||||
|
140
helper-scripts/mailbox_cleaner.py
Executable file
140
helper-scripts/mailbox_cleaner.py
Executable file
@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This script cleans up old messages from specified mailboxes (e.g., Trash, Junk)
|
||||||
|
in a mailcow environment. It can process a single user or all users, and it
|
||||||
|
supports dry-run mode.
|
||||||
|
|
||||||
|
Ideally, this script should be run daily via cron.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
DEFAULT_DAYS_BACK: int = 30
|
||||||
|
DEFAULT_MAILCOW_DIR: str = "/opt/mailcow-dockerized"
|
||||||
|
DEFAULT_MAILBOXES: list[str] = ["Trash", "Junk"]
|
||||||
|
|
||||||
|
|
||||||
|
def _run_doveadm_command(mailcow_dir: str, user: str | None, command: list[str]) -> str:
|
||||||
|
"""
|
||||||
|
Runs a doveadm command within the dovecot-mailcow container.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mailcow_dir: The path to the mailcow-dockerized directory.
|
||||||
|
user: The email address of the user to run the command for, or None for all users.
|
||||||
|
command: The doveadm command to run as a list of strings.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The standard output of the command.
|
||||||
|
"""
|
||||||
|
command = ["docker", "compose", "--project-directory", mailcow_dir, "exec", "-T", "dovecot-mailcow",
|
||||||
|
"doveadm"] + command
|
||||||
|
if user:
|
||||||
|
command.extend(["-u", user])
|
||||||
|
logging.debug(f"Executing command: {' '.join(command)}")
|
||||||
|
try:
|
||||||
|
result = subprocess.run(command, capture_output=True, text=True, check=True)
|
||||||
|
return result.stdout.strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
logging.error(f"Command execution failed: {' '.join(command)} (return code: {e.returncode})")
|
||||||
|
logging.error(f"Stderr: {e.stderr}")
|
||||||
|
logging.error(f"Stdout: {e.stdout}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""
|
||||||
|
Main function to parse arguments and execute the cleanup process.
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Clean up old messages from specified mailboxes in a mailcow environment.",
|
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||||
|
)
|
||||||
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
group.add_argument("--user", help="Email address of the single user to process.")
|
||||||
|
group.add_argument("--all", action="store_true", help="Process all users found via doveadm.")
|
||||||
|
parser.add_argument("--days-back", type=int, default=DEFAULT_DAYS_BACK,
|
||||||
|
help="Number of days back to consider for message deletion.")
|
||||||
|
parser.add_argument("--mailcow-directory", default=DEFAULT_MAILCOW_DIR,
|
||||||
|
help="Path to the mailcow-dockerized directory.")
|
||||||
|
parser.add_argument("--mailboxes", nargs='+', default=DEFAULT_MAILBOXES,
|
||||||
|
help="List of top-level mailboxes (and their subfolders) to process (e.g., Trash Junk).")
|
||||||
|
parser.add_argument("--debug", action="store_true", help="Enable debug logging.")
|
||||||
|
parser.add_argument("--dry-run", action="store_true", help="Perform a dry run without deleting anything.")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
if not os.path.isdir(args.mailcow_directory):
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"mailcow directory '{args.mailcow_directory}' does not exist or is not a directory.")
|
||||||
|
# If --all is specified, get all users
|
||||||
|
if args.all:
|
||||||
|
doveadm_output = _run_doveadm_command(args.mailcow_directory, None, ["user", "*"])
|
||||||
|
users = [line.strip() for line in doveadm_output.splitlines() if line.strip()]
|
||||||
|
# Otherwise, use the specified user
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
_run_doveadm_command(args.mailcow_directory, None, ["user", args.user])
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
logging.error(f"User '{args.user}' not found.")
|
||||||
|
sys.exit(1)
|
||||||
|
users = [args.user]
|
||||||
|
logging.info(f"Starting processing for {len(users)} users.")
|
||||||
|
logging.debug(f"Users to process: {', '.join(users)}")
|
||||||
|
# Iterate over each user
|
||||||
|
for user in users:
|
||||||
|
# Get all mailboxes for the current user
|
||||||
|
logging.info(f"Processing user: '{user}'.")
|
||||||
|
doveadm_output = _run_doveadm_command(args.mailcow_directory, user, ["mailbox", "list"])
|
||||||
|
# get all user mailboxes, sorted in reverse order
|
||||||
|
mailboxes = sorted([line.strip() for line in doveadm_output.splitlines() if line.strip()], reverse=True)
|
||||||
|
logging.info(f"User '{user}' has {len(mailboxes)} mailboxes.")
|
||||||
|
logging.debug(f"Mailboxes for user '{user}': {', '.join(mailboxes)}")
|
||||||
|
for mailbox in mailboxes:
|
||||||
|
# Iterate over each mailbox
|
||||||
|
logging.debug(f"Processing mailbox '{mailbox}' for user '{user}'.")
|
||||||
|
# Check if the mailbox is a target mailbox
|
||||||
|
if not any(re.match(rf"{re.escape(tmb)}(/|$)", mailbox, re.IGNORECASE) for tmb in args.mailboxes):
|
||||||
|
logging.debug(f"Skipping mailbox '{mailbox}' for user '{user}' as it is not a target mailbox.")
|
||||||
|
continue
|
||||||
|
# Expunge old messages from the mailbox
|
||||||
|
logging.info(
|
||||||
|
f"Expunging messages older than {args.days_back} days from mailbox '{mailbox}' for user '{user}'.")
|
||||||
|
if args.dry_run:
|
||||||
|
logging.info(f"[DRY-RUN] Skipping expunge command for mailbox '{mailbox}' of user '{user}'.")
|
||||||
|
else:
|
||||||
|
# Run the expunge command
|
||||||
|
_run_doveadm_command(args.mailcow_directory, user,
|
||||||
|
["expunge", "mailbox", mailbox, "savedbefore", f"{args.days_back}d"])
|
||||||
|
# Check if the mailbox is a sub-mailbox
|
||||||
|
if "/" not in mailbox:
|
||||||
|
logging.debug(
|
||||||
|
f"Skipping deletion check for top-level mailbox '{mailbox}' to preserve standard folders.")
|
||||||
|
continue
|
||||||
|
# Check if the mailbox is empty
|
||||||
|
doveadm_output = _run_doveadm_command(args.mailcow_directory, user, ["mailbox", "status", "messages", mailbox])
|
||||||
|
messages_count = int(doveadm_output.split("=")[1])
|
||||||
|
logging.debug(f"Mailbox '{mailbox}' for user '{user}' contains {messages_count} messages.")
|
||||||
|
if messages_count > 0:
|
||||||
|
logging.info(f"Skipping deletion of mailbox '{mailbox}' for user '{user}' as it is not empty.")
|
||||||
|
continue
|
||||||
|
# Delete the mailbox if it's empty
|
||||||
|
logging.info(f"Deleting mailbox '{mailbox}' for user '{user}' (only if empty).")
|
||||||
|
if args.dry_run:
|
||||||
|
logging.info(f"[DRY-RUN] Skipping delete command for mailbox '{mailbox}' of user '{user}'.")
|
||||||
|
else:
|
||||||
|
# As a safeguard, -e flag prevents mailbox deletion in case it's not empty
|
||||||
|
_run_doveadm_command(args.mailcow_directory, user, ["mailbox", "delete", "-e", "-s", mailbox])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
x
Reference in New Issue
Block a user