#!/usr/bin/python #coding: utf-8 # +------------------------------------------------------------------- # | aaPanel # +------------------------------------------------------------------- # | Copyright (c) 2015-2099 aaPanel(www.aapanel.com) All rights reserved. # +------------------------------------------------------------------- # | Author: wzjie # | Author: zhwen # +------------------------------------------------------------------- # +-------------------------------------------------------------------- # | 宝塔邮局 # +-------------------------------------------------------------------- import sys,re,os,json sys.path.append("class/") sys.path.append("/www/server/panel/class/") import public main_cf = "/etc/postfix/main.cf" download_url = public.get_url() def get_postconf(): if os.path.exists("/usr/sbin/postconf"): return "/usr/sbin/postconf" elif os.path.exists("/sbin/postconf"): return "/sbin/postconf" else: return "postconf" class change_to_rspamd: def change_dkim_server(self): # 更换dkim服务器,从opendkim切换到rspamd pass def change_postfix_conf(self): # postfix配置修改为支持rspamd shell = """ {postconf} -e "myhostname = $(hostname)" {postconf} -e "smtpd_milters = inet:127.0.0.1:11332" {postconf} -e "non_smtpd_milters = inet:127.0.0.1:11332" {postconf} -e "milter_mail_macros = i {{mail_addr}} {{client_addr}} {{client_name}} {{auth_authen}}" {postconf} -e "milter_protocol = 6" {postconf} -e "milter_default_action = accept" """.format(postconf=get_postconf()) public.ExecShell(shell) def comment_old_spam(self): conf = public.readFile(main_cf) if not conf: return rep = "##BT-ANTISPAM-BEGIN(.|\n)+##BT-ANTISPAM-END" conf = re.sub(rep, "", conf) public.writeFile(main_cf, conf) def change_dovecot_conf(self): # dovecot配置修改为支持rspamd shell = """ wget "{download_conf_url}/mail_sys/dovecot/90-sieve_rspamd.conf" -O /etc/dovecot/conf.d/90-sieve.conf -T 10 wget "{download_conf_url}/mail_sys/dovecot/20-lmtp.conf" -O /etc/dovecot/conf.d/20-lmtp.conf -T 10 wget "{download_conf_url}/mail_sys/dovecot/20-imap.conf" -O /etc/dovecot/conf.d/20-imap.conf -T 10 wget "{download_conf_url}/mail_sys/dovecot/15-mailboxes.conf" -O /etc/dovecot/conf.d/15-mailboxes.conf -T 10 """.format(download_conf_url=download_url) public.ExecShell(shell) def main(self): self.comment_old_spam() self.change_postfix_conf() self.change_dovecot_conf() mail_server_init().setup_rspamd() class mail_server_init: def __init__(self): self.sys_v = public.get_linux_distribution().lower() self.logfile = '/tmp/mail_init.log' self.db_files = { 'postfixadmin': '/www/vmail/postfixadmin.db', 'postfixmaillog': '/www/vmail/postfixmaillog.db', 'mail_unsubscribe': '/www/vmail/mail_unsubscribe.db', 'abnormal_recipient': '/www/vmail/abnormal_recipient.db' } def write_logs(self,content,emtpy=None): if emtpy: public.writeFile(self.logfile, '') if '\n' not in content: content += '\n' public.writeFile(self.logfile,content,'a+') def check_env(self): data = {} data['HostName'] = self.check_hostname() data['Postfix-install'] = {"status":True,"msg":"Postfix已经安装"} if os.path.exists('/usr/sbin/postfix') else {"status":False,"msg":"Postfix未安装,请点击修复按钮"} data['Dovecot-install'] = {"status":True,"msg":"Deovecot已经安装"} if os.path.exists('/usr/sbin/dovecot') else {"status":False,"msg":"Dovecot未安装,请点击修复按钮"} data['Postfix-Version'] = self.check_postfix_ver() data['Redis-install'] = {"status":True,"msg":"Redis已经安装"} if os.path.exists('/www/server/redis/src/redis-server') else {"status":False,"msg":"请到软件商店内安装Redis"} data['Redis-Passwd'] = self.check_redis_passwd(data['Redis-install']) data['Rspamd-install'] = {"status":True,"msg":"Rspamd已经安装"} if os.path.exists('/usr/bin/rspamd') else {"status":False,"msg":"Rspamd未安装,请点击修复按钮"} data['Sqlite-support'] = self.check_sqlite() data['SElinux'] ={"status":True,"msg":"SElinux已经禁用"} if not 'enforcing' in public.ExecShell('getenforce')[0].lower() else {"status":False,"msg":"请先禁用SElinux"} return data # 安装并配置postfix, dovecot def setup_mail_sys(self, args): ''' 安装邮局系统主函数 :param args: :return: ''' self.write_logs('|-Set up the postfix service to listen to all network cards...') public.ExecShell('{postconf} -e "inet_interfaces = all"'.format(postconf=get_postconf())) self.write_logs('|-Checking system key directory permissions...') if self._check_syssafe(): self.write_logs('|-Check system key directory permissions: failed') return public.returnMsg(False, '请先关闭【系统加固】') self.write_logs('|-Initializing...') if not self.prepare_work(): return public.returnMsg(False, '初始化失败') if not self.conf_postfix()['status']: return public.returnMsg(False, 'Postfix配置失败!') if not self.conf_dovecot(): return public.returnMsg(False, 'Dovecot配置失败!') if not self.setup_rspamd(): return public.returnMsg(False, 'Rspamd配置失败!') # if not self.setup_opendkim(): # return public.returnMsg(False, 'Failed to configure opendkim 0') # 初始化dns数据库 from sslModel import dataModel dataModel.main().check_table() self.write_logs('|{}'.format("-"*60)) self.write_logs('|-Initialized successfully!') return public.returnMsg(True, '初始化完成') # 检查系统加固是否开启 def _check_syssafe(self, args=None): if not os.path.exists('/www/server/panel/plugin/syssafe/'): return False data = json.loads(public.readFile('/www/server/panel/plugin/syssafe/config.json')) return data['open'] def check_hostname(self): import socket rep = '^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$' hostname = socket.gethostname() if re.search(rep,hostname): return public.returnMsg(True,'success') return public.returnMsg(False, '你的主机名 ({}) 不合规定, 需要是完整域名' '你可以通过以下命令修复你的主机名 ' '在ssh终端执行 \'hostnamectl set-hostname --static mail.example.com\''.format(hostname)) def M(self, table_name): import db sql = db.Sql() sql._Sql__DB_FILE = '/www/vmail/postfixadmin.db' return sql.table(table_name) def MD(self, table_name, db_key): if db_key not in self.db_files: raise ValueError(f"Unknown database key: {db_key}") import db sql = db.Sql() sql._Sql__DB_FILE = self.db_files[db_key] sql._Sql__encrypt_keys = [] return sql.table(table_name) # 放行端口 def _release_port(self, port): from collections import namedtuple try: import firewalls get = namedtuple("get", ["port", "ps", "type"]) get.port = port get.ps = 'Mail-Server' get.type = "port" firewalls.firewalls().AddAcceptPort(get) # return get.port return port except Exception as e: return "Release failed {}".format(e) def prepare_work(self): ''' 安装前的准备工作 :return: ''' shell_str = ''' useradd -r -u 150 -g mail -d /www/vmail -s /sbin/nologin -c "Virtual Mail User" vmail mkdir -p /www/vmail chmod -R 770 /www/vmail chown -R vmail:mail /www/vmail if [ ! -f "/www/vmail/postfixadmin.db" ]; then touch /www/vmail/postfixadmin.db chown vmail:mail /www/vmail/postfixadmin.db chmod 660 /www/vmail/postfixadmin.db fi''' self.write_logs('',emtpy=True) self.write_logs('|-Adding user: vmail') self.write_logs('|-Create mail storage directory: /www/vmail') self.write_logs('|-Set directory permissions: 770') self.write_logs('|-Set directory owner: vmail:mail') if "centos" in self.sys_v: public.ExecShell(shell_str) elif "ubuntu" in self.sys_v: public.ExecShell(shell_str) # copy证书 if not os.path.exists("/etc/pki/dovecot/certs/dovecot.pem"): self.write_logs('|-Generate dovecot certificate: /etc/pki/dovecot/certs/dovecot.pem') shell_str = """ sudo mkdir -p /etc/pki/dovecot/certs/ sudo mkdir -p /etc/pki/dovecot/private/ sudo cp /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/pki/dovecot/certs/dovecot.pem sudo mv /etc/ssl/private/ssl-cert-snakeoil.key /etc/pki/dovecot/private/dovecot.pem """ public.ExecShell(shell_str) else: return public.returnMsg(False, "目前仅支持Centos和Ubuntu") # 配置防火墙 for i in ["25", "110", "143", "465", "995", "993", "587"]: self.write_logs('|-Releasing port: {}'.format(i)) self._release_port(i) # 调用防火墙开放端口 from safeModel.firewallModel import main as firewall firewall_obj = firewall() # protocol port type address brief new_get = public.dict_obj() new_get.protocol = "tcp" new_get.ports = str(i) new_get.choose = "all" new_get.address = "" new_get.domain = "" new_get.types = "accept" new_get.brief = "Mail-Server" new_get.source = "" firewall_obj.create_rules(new_get) # 创建数据表 # 域名表 增字段 邮箱数量 mailboxes 邮箱默认大小 mailbox_quota 域名配额 quota 每秒几个邮件 rate_limit self.write_logs('|-Initializing database...') sql = '''CREATE TABLE IF NOT EXISTS `domain` ( `domain` varchar(255) NOT NULL, `a_record` TEXT DEFAULT "", `mailboxes` int DEFAULT 50, -- 创建邮箱数量 `mailbox_quota` bigint(20) NOT NULL DEFAULT 5368709120, -- 邮箱默认空间大小 `quota` bigint(20) NOT NULL DEFAULT 10737418240, -- 域名配额 `rate_limit` int DEFAULT 12, -- 每秒几个邮件 `created` datetime NOT NULL, `active` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`domain`));''' with self.M("") as obj: obj.execute(sql, ()) # self.M('').execute(sql, ()) # 邮箱账号 增加massage 记录发件数 sql = '''CREATE TABLE IF NOT EXISTS `mailbox` ( `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `password_encode` varchar(255) NOT NULL, `full_name` varchar(255) NOT NULL, `is_admin` tinyint(1) NOT NULL DEFAULT 0, `maildir` varchar(255) NOT NULL, `quota` bigint(20) NOT NULL DEFAULT 0, `local_part` varchar(255) NOT NULL, `domain` varchar(255) NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, `active` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`username`));''' with self.M("") as obj: obj.execute(sql, ()) # self.M('').execute(sql, ()) # 邮件转发 sql = '''CREATE TABLE IF NOT EXISTS `alias` ( `address` varchar(255) NOT NULL, `goto` text NOT NULL, `domain` varchar(255) NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, `active` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`address`));''' with self.M("") as obj: obj.execute(sql, ()) # self.M('').execute(sql, ()) # 系统内用表 没看到使用 sql = '''CREATE TABLE IF NOT EXISTS `alias_domain` ( `alias_domain` varchar(255) NOT NULL, `target_domain` varchar(255) NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, `active` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`alias_domain`));''' with self.M("") as obj: obj.execute(sql, ()) # self.M('').execute(sql, ()) # 新增3个表 批量发件用 # 邮件模版表 sql = '''CREATE TABLE IF NOT EXISTS `temp_email` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` varchar(255) NULL, -- 邮件名 有模版时为模版名 `type` tinyint(1) NOT NULL DEFAULT 0, --上传html 0 拖拽html 1 `content` text NOT NULL, -- 邮件正文 路径 `render` text NOT NULL, -- html渲染数据 `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `is_temp` tinyint(1) NOT NULL DEFAULT 0 -- 是否是模版 );''' with self.M("") as obj: obj.execute(sql, ()) # self.M('').execute(sql, ()) # 任务表 sql = '''CREATE TABLE IF NOT EXISTS `email_task` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `task_name` varchar(255) NOT NULL, -- 任务名 `addresser` varchar(320) NOT NULL, -- 发件人 `recipient_count` int NOT NULL, -- 收件人数量 `task_process` tinyint NOT NULL, -- 任务进程 0待执行 1执行中 2 已完成 `pause` tinyint NOT NULL, -- 暂停状态 1 暂停中 0 未暂停 执行中的任务才能暂停 `temp_id` INTEGER NOT NULL, -- 邮件对应id `is_record` INTEGER NOT NULL DEFAULT 0, -- 是否记录到发件箱 `unsubscribe` INTEGER NOT NULL DEFAULT 0, -- 是否增加退订按钮 0 没有 1 增加退订按钮 `threads` INTEGER NOT NULL DEFAULT 0, -- 线程数量 控制发送线程数 0时自动控制线程 0~10 `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `remark` text, -- 备注 `etypes` varchar(320) NOT NULL DEFAULT '1', -- 邮件类型id 默认为1 多类型 1,2,3 `start_time` INTEGER NOT NULL DEFAULT 0, -- 任务开始时间 `subject` text NULL, -- 邮件主题 改task存 可空 `full_name` varchar(255), -- 新发件人名 改task存 可空 `recipient` text NOT NULL, -- 收件人路径 改task存 `track_open` INTEGER NOT NULL DEFAULT 0, -- 追踪邮件打开 `track_click` INTEGER NOT NULL DEFAULT 0, -- 链接点击 `active` tinyint(1) NOT NULL DEFAULT 0 -- 预留字段 );''' with self.M("") as obj: obj.execute(sql, ()) # self.M('').execute(sql, ()) # 发送统计表 改 错误详情表 task_id与recipient 联合唯一 避免重试邮件重复记录 sql = '''CREATE TABLE IF NOT EXISTS `task_count` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `task_id` INTEGER NOT NULL, -- 所属任务编号 `recipient` varchar(320) NOT NULL, -- 收件人 `delay` varchar(320) NOT NULL, -- 延时 `delays` varchar(320) NOT NULL, -- 各阶段延时 `dsn` varchar(320) NOT NULL, -- dsn `relay` text NOT NULL, -- 中继服务器 `domain` varchar(320) NOT NULL, -- 域名 `status` varchar(255) NOT NULL, -- 错误状态 `err_info` text NOT NULL, -- 错误详情 `created` INTEGER NOT NULL DEFAULT 0, UNIQUE (`task_id`, `recipient`) -- 联合唯一约束 );''' with self.M("") as obj: obj.execute(sql, ()) # 邮件类型表 欢迎邮件 营销邮件 sql = '''CREATE TABLE IF NOT EXISTS `mail_type` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `mail_type` varchar(320) NOT NULL, -- 邮件类型 `created` INTEGER NOT NULL, `active` tinyint(1) NOT NULL DEFAULT 0 -- 预留字段 );''' with self.M("") as obj: obj.execute(sql, ()) # 插入一条类型 sql_insert = ''' INSERT INTO `mail_type`(`mail_type`, `created`) VALUES ('Default', strftime('%s', 'now'));''' with self.M("") as obj: obj.execute(sql_insert, ()) # 退订表 退订时间和退订邮箱联合唯一 /www/vmail/mail_unsubscribe.db sql = '''CREATE TABLE IF NOT EXISTS `mail_unsubscribe` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `created` INTEGER NOT NULL, `recipient` varchar(320) NOT NULL, -- 收件人 `etype` INTEGER NOT NULL DEFAULT 1, -- 邮件类型id `active` tinyint(1) NOT NULL DEFAULT 0, -- 0 取消订阅 1订阅 `task_id` INTEGER DEFAULT 0, -- 群发任务 id (退订有关联id 订阅没有) UNIQUE(etype, recipient) );''' with self.MD("", "mail_unsubscribe") as obj3: aa = obj3.execute(sql, ()) # public.print_log("更新退订表 mail_unsubscribe --{}".format(aa)) # 统计日志 收件人和日志时间联合唯一 sql = '''CREATE TABLE IF NOT EXISTS `mail_errlog` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `created` INTEGER NOT NULL, `recipient` varchar(320) NOT NULL, -- 收件人 `delay` varchar(320) NOT NULL, -- 延时 `delays` varchar(320) NOT NULL, -- 各阶段延时 `dsn` varchar(320) NOT NULL, -- dsn `relay` text NOT NULL, -- 中继服务器 `domain` varchar(320) NOT NULL, -- 域名 `status` varchar(255) NOT NULL, -- 错误状态 `err_info` text NOT NULL, -- 错误详情 UNIQUE(created, recipient) );''' with self.MD("", "postfixmaillog") as obj2: obj2.execute(sql, ()) # self.M2('').execute(sql, ()) # 邮件日志分析统计表 接收 received, 发送 delivered, 转发 forwarded, 延迟 deferred, 退回 bounced, 拒绝 rejected sql = '''CREATE TABLE IF NOT EXISTS `log_analysis` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `received` INTEGER NOT NULL DEFAULT 0, -- 接收 `delivered` INTEGER NOT NULL DEFAULT 0, -- 发送 `forwarded` INTEGER NOT NULL DEFAULT 0, -- 转发 `deferred` INTEGER NOT NULL DEFAULT 0, -- 延迟 `bounced` INTEGER NOT NULL DEFAULT 0, -- 退回 `rejected` INTEGER NOT NULL DEFAULT 0, -- 拒绝 `time` INTEGER NOT NULL, -- 时间 每小时时间戳 UNIQUE(`created`) );''' with self.M("") as obj: obj.execute(sql, ()) # 异常用户表 sql = '''CREATE TABLE IF NOT EXISTS `abnormal_recipient` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `created` INTEGER NOT NULL, -- 邮件时间 时间戳 `recipient` varchar(320) NOT NULL, -- 收件人 `count` INTEGER NOT NULL, -- 次数 `status` varchar(255) NOT NULL, -- 状态 `task_name` varchar(255) NOT NULL, -- 任务名 UNIQUE(recipient) );''' with self.MD("", "abnormal_recipient") as obj4: obj4.execute(sql, ()) # # 邮件日志分析统计表 # sql = '''CREATE TABLE IF NOT EXISTS `email_log` ( # --`id` INTEGER PRIMARY KEY AUTOINCREMENT, # `hostname` varchar(320) NOT NULL, -- 主机名 hello-mail # `process` varchar(320) NOT NULL, -- 进程 postfix/smtp[3033510] # `email_id` varchar(320) NOT NULL, -- 邮件id 17C2A38062F # `recipient` varchar(320) NOT NULL, -- 收件人 # `relay` text NOT NULL, -- 中继服务器 # `delay` varchar(320) NOT NULL, -- 延时 # `delays` varchar(320) NOT NULL, -- 各阶段延时 连接建立,发送数据,等待响应,传输完成 # `dsn` varchar(320) NOT NULL, -- dsn # `status` varchar(255) NOT NULL, -- 错误状态 # `domain` varchar(320) NOT NULL, -- 域名 # `err_info` text NOT NULL, -- 错误详情 # `created` INTEGER NOT NULL, -- 时间戳 # PRIMARY KEY (`email_id`)); # ''' # self.M('').execute(sql, ()) # 判断/www/vmail/postfixadmin.db文件是否存在 if os.path.exists('/www/vmail/postfixadmin.db'): self.write_logs('|-The database is initialized successfully...') return True else: self.write_logs('|-Database initialization failed...') return False def _check_sendmail(self): pid = '/run/sendmail/mta/sendmail.pid' if os.path.exists(pid): self.write_logs('|-Check that there is an additional mail service aaa, which is stopping') public.ExecShell('systemctl stop sendmail && systemctl disable sendmail') def conf_postfix(self): ''' 安装,配置postfix服务, postfix提供发信功能 :return: ''' # 检查sendmail服务,如果有则停止 self._check_sendmail() # 修改postfix配置 self.write_logs('|-Initializing postfix...') edit_postfix_conf_shell = ''' {postconf} -e "myhostname = $(hostname)" {postconf} -e "inet_interfaces = all" {postconf} -e "mydestination =" {postconf} -e "virtual_mailbox_domains = sqlite:/etc/postfix/sqlite_virtual_domains_maps.cf" {postconf} -e "virtual_alias_maps = sqlite:/etc/postfix/sqlite_virtual_alias_new_maps.cf, sqlite:/etc/postfix/sqlite_virtual_alias_domain_new_maps.cf, sqlite:/etc/postfix/sqlite_virtual_alias_domain_catchall_new_maps.cf, sqlite:/etc/postfix/bt_catchnone_maps.cf" {postconf} -e "virtual_mailbox_maps = sqlite:/etc/postfix/sqlite_virtual_mailbox_maps.cf, sqlite:/etc/postfix/sqlite_virtual_alias_domain_mailbox_maps.cf" {postconf} -e "smtpd_sasl_type = dovecot" {postconf} -e "smtpd_sasl_path = private/auth" {postconf} -e "smtpd_sasl_auth_enable = yes" {postconf} -e "smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination" {postconf} -e "smtpd_use_tls = yes" {postconf} -e "smtp_tls_security_level = may" {postconf} -e "smtpd_tls_security_level = may" {postconf} -e "virtual_transport = lmtp:unix:private/dovecot-lmtp" {postconf} -e "smtpd_milters = inet:127.0.0.1:11332" {postconf} -e "non_smtpd_milters = inet:127.0.0.1:11332" {postconf} -e "milter_mail_macros = i {{mail_addr}} {{client_addr}} {{client_name}} {{auth_authen}}" {postconf} -e "milter_protocol = 6" {postconf} -e "milter_default_action = accept" {postconf} -e "message_size_limit = 102400000" '''.format(postconf=get_postconf()) public.ExecShell(edit_postfix_conf_shell) self.write_logs('|-Downloading additional configuration files...') download_sql_conf_shell = ''' wget "{download_conf_url}/mail_sys/postfix/master.cf" -O /etc/postfix/master.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_alias_domain_catchall_new_maps.cf" -O /etc/postfix/sqlite_virtual_alias_domain_catchall_new_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/bt_catchnone_maps.cf" -O /etc/postfix/bt_catchnone_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_alias_domain_new_maps.cf" -O /etc/postfix/sqlite_virtual_alias_domain_new_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_alias_domain_mailbox_maps.cf" -O /etc/postfix/sqlite_virtual_alias_domain_mailbox_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_alias_domain_maps.cf" -O /etc/postfix/sqlite_virtual_alias_domain_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_domains_maps.cf" -O /etc/postfix/sqlite_virtual_domains_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_alias_new_maps.cf" -O /etc/postfix/sqlite_virtual_alias_new_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_domains_new_maps.cf" -O /etc/postfix/sqlite_virtual_domains_new_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/sqlite_virtual_mailbox_maps.cf" -O /etc/postfix/sqlite_virtual_mailbox_maps.cf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/postfix/btrule.cf" -O /etc/postfix/btrule.cf -T 10 >> {logfile} 2>&1 '''.format(download_conf_url=download_url,logfile=self.logfile) aaa = public.ExecShell(download_sql_conf_shell) # public.print_log("执行命令后返回 --{}".format(aaa)) result = public.readFile("/etc/postfix/sqlite_virtual_mailbox_maps.cf") if not result or not re.search(r"\n*query\s*=\s*", result): self.write_logs('|- Read file content {}: Failed'.format("/etc/postfix/sqlite_virtual_mailbox_maps.cf")) return public.returnMsg(False,"获取邮局配置失败!") restart_service_shell = 'systemctl enable postfix && systemctl restart postfix' self.write_logs('|-Restarting postfix service...') public.ExecShell(restart_service_shell) return public.returnMsg(True,"配置成功!") def conf_dovecot(self): ''' 安装,配置dovecot服务, dovecot提供收信功能 :return: ''' self.write_logs('|-Initializing dovecot...') self.write_logs('|-Downloading additional configuration files...') download_conf_shell = ''' wget "{download_conf_url}/mail_sys/dovecot/dovecot-sql.conf.ext" -O /etc/dovecot/dovecot-sql.conf.ext -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/dovecot.conf" -O /etc/dovecot/dovecot.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/10-mail.conf" -O /etc/dovecot/conf.d/10-mail.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/10-ssl.conf" -O /etc/dovecot/conf.d/10-ssl.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/10-master.conf" -O /etc/dovecot/conf.d/10-master.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/10-auth.conf" -O /etc/dovecot/conf.d/10-auth.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/90-sieve_rspamd.conf" -O /etc/dovecot/conf.d/90-sieve.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/20-lmtp.conf" -O /etc/dovecot/conf.d/20-lmtp.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/20-imap.conf" -O /etc/dovecot/conf.d/20-imap.conf -T 10 >> {logfile} 2>&1 wget "{download_conf_url}/mail_sys/dovecot/15-mailboxes.conf" -O /etc/dovecot/conf.d/15-mailboxes.conf -T 10 >> {logfile} 2>&1 '''.format(download_conf_url=download_url,logfile=self.logfile) public.ExecShell(download_conf_shell) result = public.readFile("/etc/dovecot/dovecot.conf") if not result or not re.search(r"\n*protocol\s+imap", result): self.write_logs('|-Read file content {}: Failed'.format("/etc/dovecot/dovecot.conf")) return False # 关闭protocols注释 dovecot_conf = public.readFile("/etc/dovecot/dovecot.conf") dovecot_conf = re.sub(r"#protocols\s*=\s*imap\s*pop3\s*lmtp", "protocols = imap pop3 lmtp", dovecot_conf) public.writeFile("/etc/dovecot/dovecot.conf", dovecot_conf) if 'centos8' in self.sys_v: if not self.create_ssl(): self.write_logs('|-Generate self-signed certificate: Failed') return False if not os.path.exists('/etc/pki/dovecot/private/dovecot.pem') or not os.path.exists( '/etc/pki/dovecot/certs/dovecot.pem'): self.create_ssl() restart_service_shell = ''' chown -R vmail:dovecot /etc/dovecot chmod -R o-rwx /etc/dovecot systemctl enable dovecot systemctl restart dovecot ''' self.write_logs('|-Restarting dovecot...') public.ExecShell(restart_service_shell) return True # 自签证书 def create_ssl(self, get=None): import OpenSSL self.write_logs('|-生成自签证书...') from mailModel import mainModel as mail_sys_main mail_sys_main.main().back_file('/etc/pki/dovecot/certs/dovecot.pem') mail_sys_main.main().back_file('/etc/pki/dovecot/private/dovecot.pem') key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) cert = OpenSSL.crypto.X509() cert.set_serial_number(0) cert.get_subject().CN = public.GetLocalIp() cert.set_issuer(cert.get_subject()) cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60) cert.set_pubkey(key) cert.sign(key, 'md5') cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) if not isinstance(cert_ca, str): cert_ca = cert_ca.decode() if not isinstance(private_key, str): private_key = private_key.decode() if len(cert_ca) > 100 and len(private_key) > 100: public.writeFile('/etc/pki/dovecot/certs/dovecot.pem', cert_ca) public.writeFile('/etc/pki/dovecot/private/dovecot.pem', private_key) return True else: mail_sys_main.main().restore_file('/etc/pki/dovecot/certs/dovecot.pem') mail_sys_main.main().restore_file('/etc/pki/dovecot/private/dovecot.pem') return False def check_postfix_ver(self): postfix_version = public.ExecShell(r"{postconf} mail_version|sed -r 's/.* ([0-9\.]+)$/\1/'".format( postconf=get_postconf()))[0].strip() if postfix_version.startswith('3'): return public.returnMsg(True,postfix_version) else: return public.returnMsg(False,"当前版本不支持或Postfix没有安装成功:{}".format(postfix_version)) def check_redis_passwd(self,redis_install): redis_conf = public.readFile('/www/server/redis/redis.conf') if redis_install['status']: if re.search('\n\s*requirepass',redis_conf): return public.returnMsg(True,"Redis已经设置密码") return public.returnMsg(False,"请到Redis管理器设置密码!") def check_sqlite(self): if not public.ExecShell('{postconf} -m | grep sqlite'.format(postconf=get_postconf()))[0].strip(): return public.returnMsg(False,"Postfix的Sqlite包异常,请检查") return public.returnMsg(True,"Postfix已支持Sqlite") def setup_rspamd(self): # public.print_log("进入修复文件1") # 修改postfix配置 self.write_logs('|-Initializing rspamd...') edit_postfix_conf_shell = ''' {postconf} -e "smtpd_milters = inet:127.0.0.1:11332" {postconf} -e "non_smtpd_milters = inet:127.0.0.1:11332" {postconf} -e "milter_mail_macros = i {{mail_addr}} {{client_addr}} {{client_name}} {{auth_authen}}" {postconf} -e "milter_protocol = 6" {postconf} -e "milter_default_action = accept" '''.format(postconf=get_postconf()) public.ExecShell(edit_postfix_conf_shell) self.write_logs('|-Downloading additional configuration files...') get_rspamd_conf_shell = """ mkdir -p /usr/lib/dovecot/sieve wget -O /etc/rspamd/worker-normal.inc {download_conf_url}/mail_sys/rspamd/worker-normal.inc -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /etc/rspamd/worker-fuzzy.inc {download_conf_url}/mail_sys/rspamd/worker-fuzzy.inc -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /etc/rspamd/statistic.conf {download_conf_url}/mail_sys/rspamd/statistic.conf -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /etc/rspamd/local.d/worker-controller.inc {download_conf_url}/mail_sys/rspamd/worker-controller.inc -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /etc/rspamd/worker-proxy.inc {download_conf_url}/mail_sys/rspamd/worker-proxy.inc -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /etc/rspamd/local.d/dkim_signing.conf {download_conf_url}/mail_sys/rspamd/modules.d/dkim_signing_bt.conf -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /etc/rspamd/local.d/milter_headers.conf {download_conf_url}/mail_sys/rspamd/modules.d/milter_headers_bt.conf -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /etc/rspamd/local.d/redis.conf {download_conf_url}/mail_sys/rspamd/modules.d/redis_bt.conf -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /usr/lib/dovecot/sieve/report-ham.sieve {download_conf_url}/mail_sys/dovecot/lib/report-ham.sieve -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /usr/lib/dovecot/sieve/report-spam.sieve {download_conf_url}/mail_sys/dovecot/lib/report-spam.sieve -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /usr/lib/dovecot/sieve/spam-to-folder.sieve {download_conf_url}/mail_sys/dovecot/lib/spam-to-folder.sieve -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /usr/lib/dovecot/sieve/sa-learn-spam.sh {download_conf_url}/mail_sys/dovecot/lib/sa-learn-spam.sh -T 20 --no-check-certificate >> {logfile} 2>&1 wget -O /usr/lib/dovecot/sieve/sa-learn-ham.sh {download_conf_url}/mail_sys/dovecot/lib/sa-learn-ham.sh -T 20 --no-check-certificate >> {logfile} 2>&1 sievec /usr/lib/dovecot/sieve/spam-to-folder.sieve sievec /usr/lib/dovecot/sieve/report-spam.sieve sievec /usr/lib/dovecot/sieve/report-ham.sieve chmod +x /usr/lib/dovecot/sieve/sa-learn-spam.sh chmod +x /usr/lib/dovecot/sieve/sa-learn-ham.sh """.format(download_conf_url=download_url,logfile=self.logfile) public.ExecShell(get_rspamd_conf_shell) # 生成web端管理密码 self.write_logs('|-Generating rspamd management password...') passwd = public.GetRandomString(8) passwd_en = public.ExecShell('rspamadm pw -p "{}"'.format(passwd))[0].strip('\n') public.writeFile('/etc/rspamd/passwd', passwd) worker_controller_path = '/etc/rspamd/local.d/worker-controller.inc' worker_controller = public.readFile(worker_controller_path) if worker_controller: if 'BT_PASSWORD' in worker_controller: worker_controller = worker_controller.replace('password = "BT_PASSWORD";', 'password = "{}";'.format(passwd_en)) public.writeFile(worker_controller_path, worker_controller) # 设置rspamd redis密码 rspamd_redis_path = '/etc/rspamd/local.d/redis.conf' rspamd_redis = public.readFile(rspamd_redis_path) if rspamd_redis: if 'BT_REDIS_PASSWD' in rspamd_redis: rspamd_redis = rspamd_redis.replace('password = "BT_REDIS_PASSWD";', 'password = "{}";'.format(self.get_redis_passwd())) public.writeFile(rspamd_redis_path, rspamd_redis) self.write_logs('|-Restarting rspamd...') public.ExecShell('systemctl restart rspamd postfix dovecot') return True def get_redis_passwd(self): redis_path = '/www/server/redis/redis.conf' redis_conf = public.readFile(redis_path) passwd = re.search('\n\s*requirepass\s+(.*)',redis_conf) if passwd: return passwd.groups(0)[0] return False def install_dovecot_on_ubuntu(self): install_shell = """ sudo apt remove dovecot-core dovecot-imapd dovecot-lmtpd dovecot-pop3d dovecot-sqlite dovecot-sieve -y dpkg -P dovecot-core dovecot-imapd dovecot-lmtpd dovecot-pop3d dovecot-sqlite dovecot-sieve sudo apt install dovecot-core dovecot-pop3d dovecot-imapd dovecot-lmtpd dovecot-sqlite dovecot-sieve -y """ public.ExecShell(install_shell) shell_str = """ sudo rm -f /etc/pki/dovecot/certs/dovecot.pem sudo rm -f /etc/pki/dovecot/private/dovecot.pem sudo mkdir -p /etc/pki/dovecot/certs/ sudo mkdir -p /etc/pki/dovecot/private/ sudo cp /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/pki/dovecot/certs/dovecot.pem sudo mv /etc/ssl/private/ssl-cert-snakeoil.key /etc/pki/dovecot/private/dovecot.pem """ public.ExecShell(shell_str) return self.conf_dovecot() def install_dovecot_on_centos7(self): install_shell = """ yum remove dovecot -y yum install dovecot-pigeonhole -y yum install dovecot -y """ public.ExecShell(install_shell) return self.conf_dovecot() def install_dovecot_on_centos8(self): install_shell = """ yum remove dovecot -y yum install dovecot-pigeonhole -y yum install dovecot -y """ public.ExecShell(install_shell) return self.conf_dovecot() def install_postfix_on_ubuntu(self): back_shell = "" restore_shell = "" if os.path.exists("/etc/postfix/main.cf"): back_shell = "mv /etc/postfix /etc/postfix_aap_bak" restore_shell = """ sudo rm -rf /etc/postfix mv /etc/postfix_aap_bak /etc/postfix """ install_shell = """ %s sudo apt remove postfix postfix-sqlite -y sudo dpkg -P postfix postfix-sqlite sudo debconf-set-selections <<< "postfix postfix/mailname string ${hostname}" sudo debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Internet Site'" sudo apt install postfix -y sudo apt install postfix-sqlite -y sudo apt install sqlite -y %s """ % (back_shell,restore_shell) public.ExecShell(install_shell) def install_postfix_on_centos7(self): back_shell = "" restore_shell = "" if os.path.exists("/etc/postfix/main.cf"): back_shell = "mv /etc/postfix /etc/postfix_aap_bak" restore_shell = """ sudo rm -rf /etc/postfix mv /etc/postfix_aap_bak /etc/postfix """ install_shell = """ {b} yum remove postfix -y wget -O /tmp/postfix3-3.4.7-1.gf.el7.x86_64.rpm {download_conf_url}/install/plugin/mail_sys/rpm/postfix3-3.4.7-1.gf.el7.x86_64.rpm yum localinstall /tmp/postfix3-3.4.7-1.gf.el7.x86_64.rpm -y rm -f /tmp/postfix3-3.4.7-1.gf.el7.x86_64.rpm wget -O /tmp/postfix3-sqlite-3.4.7-1.gf.el7.x86_64.rpm {download_conf_url}/install/plugin/mail_sys/rpm/postfix3-sqlite-3.4.7-1.gf.el7.x86_64.rpm yum localinstall /tmp/postfix3-sqlite-3.4.7-1.gf.el7.x86_64.rpm -y /tmp/postfix3-sqlite-3.4.7-1.gf.el7.x86_64.rpm {r} """.format(download_conf_url=download_url,b=back_shell,r=restore_shell) public.ExecShell(install_shell) def install_postfix_on_centos8(self): back_shell = "" restore_shell = "" if os.path.exists("/etc/postfix/main.cf"): back_shell = "mv /etc/postfix /etc/postfix_aap_bak" restore_shell = """ sudo rm -rf /etc/postfix mv /etc/postfix_aap_bak /etc/postfix """ install_shell = """ {b} yum remove postfix -y wget -O /tmp/postfix3-3.4.9-1.gf.el8.x86_64.rpm {download_conf_url}/install/plugin/mail_sys/rpm/postfix3-3.4.9-1.gf.el8.x86_64.rpm yum localinstall /tmp/postfix3-3.4.9-1.gf.el8.x86_64.rpm -y rm -f /tmp/postfix3-3.4.9-1.gf.el8.x86_64.rpm wget -O /tmp/postfix3-sqlite-3.4.9-1.gf.el8.x86_64.rpm {download_conf_url}/install/plugin/mail_sys/rpm/postfix3-sqlite-3.4.9-1.gf.el8.x86_64.rpm yum localinstall /tmp/postfix3-sqlite-3.4.9-1.gf.el8.x86_64.rpm -y rm -f /tmp/postfix3-sqlite-3.4.9-1.gf.el8.x86_64.rpm {r} """.format(download_conf_url=download_url,b=back_shell,r=restore_shell) public.ExecShell(install_shell) if __name__ == '__main__': args = sys.argv if len(args) > 1: if args[1] == 'change_to_rspamd': change_to_rspamd().main() elif args[1] == 'setup_mail_sys': print(mail_server_init().setup_mail_sys(args)) elif args[1] == 'check_env': print(mail_server_init().check_env()) elif args[1] == 'install_dovecot_on_ubuntu': print(mail_server_init().install_dovecot_on_ubuntu()) elif args[1] == 'install_dovecot_on_centos7': print(mail_server_init().install_dovecot_on_centos7()) elif args[1] == 'install_dovecot_on_centos8': print(mail_server_init().install_dovecot_on_centos8()) elif args[1] == 'install_postfix_on_ubuntu': print(mail_server_init().install_postfix_on_ubuntu()) elif args[1] == 'install_postfix_on_centos7': print(mail_server_init().install_postfix_on_centos7()) elif args[1] == 'install_postfix_on_centos8': print(mail_server_init().install_postfix_on_centos8()) else: print('args error') else: print('args error')