标签 python 下的文章

批量设置域名 dkim 脚本 - iRedMail - dkim_setup.py - ptyhon - debian

环境:debian 12.10 ,iRedMail - 1.7.3
目标:批量配置域名 dkim

步骤:

  1. 将 dkim_setup.py 及 domains.txt 上传到邮局服务器文件系统里,如 /root
  2. python3 dkim_setup.py # 执行 dkim_setup.py 脚本。脚本会从 domains.txt (可自行创建,每行一个域名) 里逐行读取邮件域名,创建 dkim 文件、将 相应的域名和 key 追加到 /etc/amavis/conf.d/50-user 文件内容中。脚本每次执行前,会对当前的 /etc/amavis/conf.d/50-user 复制备份,预防出错后需要还原使用,最后脚本会将域名及对应的解析值追加到 dkim_dns_records.json ,方便您解析域名
  3. amavisd testkey # 如需验证 dkim 的查询结果,可执行此命令

如需手工操作:

  1. amavisd-new genrsa /var/lib/dkim/iredmail.demo.anqun.org.pem 2048 # 为邮件域名 iredmail.demo.anqun.org 生成数字签名文件
  2. chown amavis:amavis amavisd genrsa /var/lib/dkim/iredmail.demo.anqun.org.pem # 更改签名文件属主
  3. chmod 0400 /var/lib/dkim/iredmail.demo.anqun.org.pem # 更改签名权限
  4. 编辑 /etc/amavis/conf.d/50-user 文件内容,将签名的邮件域名和文件等追加进去

    # Add dkim_key here.
    dkim_key('iredmail.demo.anqun.org', 'dkim', '/var/lib/dkim/iredmail.demo.anqun.org.pem');
    
    @dkim_signature_options_bysender_maps = ({
     # 'd' defaults to a domain of an author/sender address,
     # 's' defaults to whatever selector is offered by a matching key
    
     # Per-domain dkim key
     #"domain.com"  => { d => "domain.com", a => 'rsa-sha256', ttl => 10*24*3600 },
    
     # catch-all (one dkim key for all domains)
     '.' => {d => 'iredmail.demo.anqun.org',
             a => 'rsa-sha256',
             c => 'relaxed/simple',
             ttl => 30*24*3600 },
    });

dkim_setup.py 的文件内容:

#!/usr/bin/env python3

import subprocess
import os
import datetime
import json

AMAVIS_CONFIG_FILE = "/etc/amavis/conf.d/50-user"
DKIM_BASE_PATH = "/var/lib/dkim"
DKIM_KEY_SIZE = 2048  # Or 2048, if desired
AMAVIS_COMMAND = "amavisd"  # Changed from amavisd-new
DNS_OUTPUT_FILE = "dkim_dns_records.json"

def generate_dkim_key(domain):
    """Generates a DKIM key for the given domain."""
    key_path = os.path.join(DKIM_BASE_PATH, f"{domain}.pem")
    command = [AMAVIS_COMMAND, "genrsa", key_path, str(DKIM_KEY_SIZE)]
    try:
        subprocess.run(command, check=True)
        subprocess.run(["chown", "amavis:amavis", key_path], check=True)
        subprocess.run(["chmod", "0400", key_path], check=True)
        print(f"DKIM key generated for {domain} at {key_path}")
        return key_path
    except subprocess.CalledProcessError as e:
        print(f"Error generating DKIM key for {domain}: {e}")
        return None

def get_dkim_public_key(key_path):
    """Extracts the DKIM public key from the .pem file using openssl."""
    try:
        command = ["openssl", "rsa", "-in", key_path, "-pubout", "-outform", "PEM"]
        process = subprocess.run(command, capture_output=True, text=True, check=True)
        public_key = process.stdout.strip()

        # Remove the BEGIN and END PUBLIC KEY lines and any newlines
        public_key = public_key.replace("-----BEGIN PUBLIC KEY-----", "")
        public_key = public_key.replace("-----END PUBLIC KEY-----", "")
        public_key = public_key.replace("\n", "")

        return public_key
    except subprocess.CalledProcessError as e:
        print(f"Error extracting DKIM public key using openssl: {e}")
        print(f"Stderr: {e.stderr}")  # Print stderr for more details
        return None
    except Exception as e:
        print(f"Error extracting DKIM public key: {e}")
        return None


def domain_exists(domain):
    """Checks if the domain is already configured in Amavis."""
    try:
        with open(AMAVIS_CONFIG_FILE, "r") as f:
            config_content = f.read()
        return domain in config_content
    except FileNotFoundError:
        print(f"Error: Amavis config file not found at {AMAVIS_CONFIG_FILE}")
        return False

def backup_amavis_config():
    """Backs up the Amavis configuration file with a timestamp."""
    timestamp = datetime.datetime.now().strftime("%Y.%m.%d.%H.%M.%S")
    backup_file = f"{AMAVIS_CONFIG_FILE}.{timestamp}"
    try:
        subprocess.run(["cp", AMAVIS_CONFIG_FILE, backup_file], check=True)
        print(f"Amavis config backed up to {backup_file}")
        return True
    except subprocess.CalledProcessError as e:
        print(f"Error backing up Amavis config: {e}")
        return False

def update_amavis_config(domain, key_path):
    """Updates the Amavis configuration file with the DKIM settings."""
    try:
        with open(AMAVIS_CONFIG_FILE, "r") as f:
            config_lines = f.readlines()

        dkim_key_insert_point = None
        start_index = None
        end_index = None
        last_entry_index = None

        for i, line in enumerate(config_lines):
            if "dkim_key(" in line:
                dkim_key_insert_point = i + 1

            if "@dkim_signature_options_bysender_maps = (" in line:
                start_index = i

            if start_index is not None and "=>" in line:
                # 检查是否是一个 domain entry 的开始
                if line.lstrip().startswith('"') or line.lstrip().startswith("'"):
                    last_entry_index = i

            if "});" in line and start_index is not None:
                end_index = i
                break

        if dkim_key_insert_point is None:
            print("Could not find insertion point for dkim_key.")
            return False

        if start_index is None or end_index is None:
            print("Could not find signature options structure.")
            return False

        # 如果没有找到任何已有的 domain entry,则默认插入在 start_index + 1
        insert_sig_index = last_entry_index + 1 if last_entry_index is not None else start_index + 1

        # 构造新行
        new_dkim_key_line = f"dkim_key('{domain}', 'dkim', '{key_path}');\n"
        new_dkim_sig_line = f'    "{domain}" => {{ d => "{domain}", a => \'rsa-sha256\', ttl => 10*24*3600 }},\n'

        # 插入 dkim_key
        config_lines.insert(dkim_key_insert_point, new_dkim_key_line)

        # 如果不是第一个条目,检查前一行是否有逗号
        if last_entry_index is not None:
            prev_line = config_lines[insert_sig_index - 1]
            if not prev_line.strip().endswith(','):
                config_lines[insert_sig_index - 1] = prev_line.rstrip('\n') + ',\n'

        # 插入新的签名选项
        config_lines.insert(insert_sig_index, new_dkim_sig_line)

        # 写回文件
        with open(AMAVIS_CONFIG_FILE, "w") as f:
            f.writelines(config_lines)

        print(f"Amavis config updated for {domain}")
        return True

    except FileNotFoundError:
        print(f"Error: Amavis config file not found at {AMAVIS_CONFIG_FILE}")
        return False
    except Exception as e:
        print(f"Error updating Amavis config: {e}")
        return False

def restart_amavis():
    """Restarts the Amavis service."""
    try:
        subprocess.run(["systemctl", "restart", "amavis"], check=True)  # Corrected command
        print("Amavis service restarted.")
        return True
    except subprocess.CalledProcessError as e:
        print(f"Error restarting Amavis: {e}")
        return False

def write_dns_record(domain, public_key):
    """Writes the DNS record to a JSON file."""
    dns_record_name = f"dkim._domainkey.{domain}"
    # Construct the TXT record value
    txt_record_value = f"v=DKIM1; p={public_key}"
    data = {dns_record_name: txt_record_value}

    try:
        # Check if the file exists and load existing data
        if os.path.exists(DNS_OUTPUT_FILE):
            with open(DNS_OUTPUT_FILE, "r") as f:
                try:
                    existing_data = json.load(f)
                except json.JSONDecodeError:
                    existing_data = {}  # Handle empty or corrupted JSON file
        else:
            existing_data = {}

        # Update with the new record
        existing_data.update(data)

        # Write back to the file
        with open(DNS_OUTPUT_FILE, "w") as f:
            json.dump(existing_data, f, indent=4, ensure_ascii=False)  # indent for readability, disable ASCII escaping

        print(f"DNS record written to {DNS_OUTPUT_FILE}")

    except Exception as e:
        print(f"Error writing DNS record to file: {e}")

if __name__ == "__main__":
    import sys

    # Backup Amavis config *before* processing any domains
    if not backup_amavis_config():
        print("Failed to backup Amavis config.  Aborting.")
        sys.exit(1)

    try:
        with open("domains.txt", "r") as f:
            domains = [line.strip() for line in f.readlines()]
    except FileNotFoundError:
        print("Error: domains.txt not found")
        sys.exit(1)


    for domain in domains:
        print(f"Processing domain: {domain}")

        if domain_exists(domain):
            print(f"Domain {domain} already exists in Amavis config. Skipping.")
            continue



        key_path = generate_dkim_key(domain)
        if key_path:
            if update_amavis_config(domain, key_path):
                public_key = get_dkim_public_key(key_path)
                if public_key:
                    write_dns_record(domain, public_key)
                else:
                    print(f"Failed to get public key for {domain}")
            else:
                print(f"Failed to update amavis config for {domain}")

    if domains: #Only restart if there was at least one domain to process
        restart_amavis()

演示视频:https://www.bilibili.com/video/BV1Jj7fztEN3/

参考:

在Ubuntu 16里安装 python 3.6 + uWSGI + Nginx

环境:Ubuntu 16 64位

1.apt update # 更新软件源

2.apt install software-properties-common # 为安装python3.6做准备,ubuntu16自带的是python3.5

3.add-apt-repository ppa:deadsnakes/ppa # 安装ppa

4.apt updat # 更新软件源

5.apt install python3.6 python3.6-dev # 安装python3.6及开发包

6.rm /usr/bin/python3 # 移除原有的python3.5链接

7.ln -s /usr/bin/python3.6 /usr/bin/python3 # 创建python3链接到python3.6

8.apt install mysql-server # 安装mysql数据库,设置密码

9.apt install mongodb # 安装 mongodb

10.apt install python3-pip # 安装 python3-pip

11.apt python3-mysql.connector # 安装 mysql.connector

12.cd /mys/street_app_server/ # 切换到 app_server 文件所在的根目录

13.pip3 install -r requirements.txt # 安装依赖的python3包

14.create database CWS_APP; # 在mysql的命令行中,或在phpmyadmin中,创建 CWS_APP 数据库

15.python3 manage.py init # 初始化,会自动创建openluat_user和street_machine数据库及相应的数据表

16.python3 manage.py runserver # 如需测试,可运行

17.pip3 install uWSGI # 安装 uWSGI

18.apt install supervisor # 安装 supervisor,用来管理uWSGI

19.vi /etc/supervisor/conf.d/mys.conf # 创建新的 supervisor 配置文件,配置uWSGI运行,内容如下:

[program:uwsgi]
command=uwsgi --ini /mys/street_app_server/config/uwsgi_config.ini --listen 128
environment=production="1",FLASK_CONFIG="production"
startretries=8                ; max # of serial start failures (default 3)
stdout_logfile=/mys/log/uwsgi.log        ; stdout log path, NONE for none; default AUTO
stdout_logfile_maxbytes=10MB   ; max # logfile bytes b4 rotation (default 50MB)
stdout_logfile_backups=8     ; # of stdout logfile backups (default 10)
stderr_logfile=/mys/log/uwsgi.log        ; stderr log path, NONE for none; default AUTO
stderr_logfile_maxbytes=10MB   ; max # logfile bytes b4 rotation (default 50MB)
stderr_logfile_backups=8     ; # of stderr logfile backups (default 10)

20.service supervisor start # 启动 supervisor 服务

21.apt install nginx # 安装 nginx

22.vi /etc/nginx/sites-enabled/mys # 创建新站配置文件,内容如下:

server {
        listen 80;

        server_name szt.anqun.org;
        root /mys/;
        error_log /mys/log/mafunginx.error;

        location / {
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:33410; # 这里与uwsgi中的端口号相同
        }

        location ~* .txt {
            root /mys/streetweb;
            index index.html;
        }

        location /assets {
            root    /mys/streetweb;
            index index.html;
            autoindex       on;
        }

        location /adminpage/assets {
            root    /mys/streetweb;
            index index.html;
            autoindex       on;
        }

        location /adminpage/ {
            root /mys/streetweb;
            index index.html;
        }
    }

23.nginx -s reload # 重载nginx配置文件

24.在浏览器里访问 http://域名/adminpage/ 应该会显示登录界面,默认用户名是 15300002713 ,密码是 888888

25.如需配置数据库密码等,配置文件在 /mys/street_app_server/config 目录里

26.cd /mys # 转到包含有 emqttd-ubuntu16-64.zip 文件的目录,解压

27../emqttd/bin/emqttd start # 启动emqttd

参考:https://www.jianshu.com/p/3fb071d55d4d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

在CentOS6里安装使用python2.7

CentOS6带的python版本默认是2.6,如需2.7的,可通过 softwarecollections.org 安装。

过程:

1.yum -y install centos-release-scl # 增加scl安装源

2.yum -y install python27 # 安装 python27

3.scl enable python27 bash # 使用 python27

4.30 * * * * scl enable python27 <path to script>/bash_script.sh # 如需定时任务里使用python27,可以这样写

参考:

在 web.py 中用阿里云的免费证书设置https访问

网友发帖问,所以有此实践。

环境:Debian9,python2.7

过程:

1.pip install web.py # 安装 web.py,本例版本是 web.py-0.39
webpy-1.png

2.pip install pyOpenSSL # 还需安装这个包
webpy-2.png

3.vi test.py # 创建测试文件,内容如下:(请替换相应的ssl证书存储路径)

import web
from web.wsgiserver import CherryPyWSGIServer

CherryPyWSGIServer.ssl_certificate = "/root/swas.anqun.org.pem"
CherryPyWSGIServer.ssl_private_key = "/root/swas.anqun.org.key"

urls = ("/.*", "hello")
app = web.application(urls, globals())

class hello:
    def GET(self):
        return 'Hello, world!'

if __name__ == "__main__":
    app.run()

webpy-3.png

4.python test.py # 运行测试,默认在8080端口上
webpy-4.png

5.在浏览器里访问,如本例,https://swas.anqun.org:8080,正常
webpy-5.png

参考:http://webpy.org/cookbook/ssl

在 CentOS 7 通过 Software Collections 源安装 python3.5

CentOS 7里默认的python版本是2.7.5,如需使用python3.5,可以通过 Software Collections 源安装。

过程:

  1. yum install centos-release-scl # 安装 Software Collections 源
  2. yum install rh-python35 # 安装 python3.5
  3. scl enable rh-python35 bash # 启用 python3.5
  4. python -m http.server 8082 # http访问测试

参考: