You've already forked Mailu
							
							
				mirror of
				https://github.com/Mailu/Mailu.git
				synced 2025-10-30 23:37:43 +02:00 
			
		
		
		
	Handle DKIM key generation and storage
This commit is contained in:
		| @@ -20,7 +20,9 @@ default_config = { | ||||
|     'HOSTNAME': 'mail.freeposte.io', | ||||
|     'DOMAIN': 'freeposte.io', | ||||
|     'POSTMASTER': 'postmaster', | ||||
|     'DEBUG': False | ||||
|     'DEBUG': False, | ||||
|     'DKIM_PATH': '/dkim/{domain}.{selector}.key', | ||||
|     'DKIM_SELECTOR': 'dkim' | ||||
| } | ||||
|  | ||||
| # Load configuration from the environment if available | ||||
|   | ||||
							
								
								
									
										21
									
								
								admin/freeposte/admin/dkim.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								admin/freeposte/admin/dkim.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| """ No crypto operation is done on keys. | ||||
| They are thus represented as ASCII armored PEM. | ||||
| """ | ||||
|  | ||||
| from OpenSSL import crypto | ||||
|  | ||||
|  | ||||
| def gen_key(key_type=crypto.TYPE_RSA, bits=1024): | ||||
|     """ Generate and return a new RSA key. | ||||
|     """ | ||||
|     key = crypto.PKey() | ||||
|     key.generate_key(key_type, bits) | ||||
|     return crypto.dump_privatekey(crypto.FILETYPE_PEM, key) | ||||
|  | ||||
|  | ||||
| def strip_key(pem): | ||||
|     """ Return only the b64 part of the ASCII armored PEM. | ||||
|     """ | ||||
|     key = crypto.load_privatekey(crypto.FILETYPE_PEM, pem) | ||||
|     public_pem = crypto.dump_publickey(crypto.FILETYPE_PEM, key) | ||||
|     return public_pem.replace(b"\n", b"").split(b"-----")[2] | ||||
| @@ -1,10 +1,14 @@ | ||||
| from freeposte.admin import db | ||||
| from freeposte.admin import db, dkim | ||||
| from freeposte import app | ||||
|  | ||||
| from sqlalchemy.ext import declarative | ||||
| from passlib import context | ||||
| from datetime import datetime | ||||
|  | ||||
| import re | ||||
| import time | ||||
| import os | ||||
| import glob | ||||
|  | ||||
|  | ||||
| # Many-to-many association table for domain managers | ||||
| @@ -34,6 +38,28 @@ class Domain(Base): | ||||
|     max_users = db.Column(db.Integer, nullable=False, default=0) | ||||
|     max_aliases = db.Column(db.Integer, nullable=False, default=0) | ||||
|  | ||||
|     @property | ||||
|     def dkim_key(self): | ||||
|         file_path = app.config["DKIM_PATH"].format( | ||||
|             domain=self.name, selector=app.config["DKIM_SELECTOR"]) | ||||
|         if os.path.exists(file_path): | ||||
|             with open(file_path, "rb") as handle: | ||||
|                 return handle.read() | ||||
|  | ||||
|     @dkim_key.setter | ||||
|     def dkim_key(self, value): | ||||
|         file_path = app.config["DKIM_PATH"].format( | ||||
|             domain=self.name, selector=app.config["DKIM_SELECTOR"]) | ||||
|         with open(file_path, "wb") as handle: | ||||
|             handle.write(value) | ||||
|  | ||||
|     @property | ||||
|     def dkim_publickey(self): | ||||
|         return dkim.strip_key(self.dkim_key).decode("utf8") | ||||
|  | ||||
|     def generate_dkim_key(self): | ||||
|         self.dkim_key = dkim.gen_key() | ||||
|  | ||||
|     def has_email(self, localpart): | ||||
|         for email in self.users + self.aliases: | ||||
|             if email.localpart == localpart: | ||||
|   | ||||
| @@ -10,7 +10,7 @@ Domain details | ||||
|  | ||||
| {% block main_action %} | ||||
| {% if current_user.global_admin %} | ||||
| <a class="btn btn-primary" href="#">Regenerate keys</a> | ||||
| <a class="btn btn-primary" href="{{ url_for(".domain_genkeys", domain_name=domain.name) }}">Regenerate keys</a> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| @@ -26,8 +26,18 @@ Domain details | ||||
|       <td><pre>{{ domain.name }}. 600 IN MX 10 {{ config["HOSTNAME"] }}.</pre></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>DNS SPF entry</th> | ||||
|       <td><pre>{{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ config["HOSTNAME"] }} -all"</pre></td> | ||||
|       <th>DNS SPF entries</th> | ||||
|       <td><pre> | ||||
| {{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ config["HOSTNAME"] }} -all" | ||||
| {{ domain.name }}. 600 IN SPF "v=spf1 mx a:{{ config["HOSTNAME"] }} -all"</pre></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>DKIM public key</th> | ||||
|       <td><pre style="white-space: pre-wrap; word-wrap: break-word;">{{ domain.dkim_publickey }}</pre></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>DNS DKIM entry</th> | ||||
|       <td><pre style="white-space: pre-wrap; word-wrap: break-word;">{{ config["DKIM_SELECTOR"] }}._domainkey IN 600 TXT "v=DKIM1; k=rsa; p={{ domain.dkim_publickey }}"</pre></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>DNS DMARC entry</th> | ||||
|   | ||||
| @@ -63,3 +63,11 @@ def domain_details(domain_name): | ||||
|     domain = utils.get_domain_admin(domain_name) | ||||
|     return flask.render_template('domain/details.html', domain=domain, | ||||
|         config=flask_app.config) | ||||
|  | ||||
|  | ||||
| @app.route('/domain/genkeys/<domain_name>', methods=['GET']) | ||||
| def domain_genkeys(domain_name): | ||||
|     domain = utils.get_domain_admin(domain_name) | ||||
|     domain.generate_dkim_key() | ||||
|     return flask.redirect( | ||||
|         flask.url_for(".domain_details", domain_name=domain_name)) | ||||
|   | ||||
| @@ -6,6 +6,7 @@ Flask-migrate | ||||
| Flask-script | ||||
| flask_wtf | ||||
| WTForms-Components | ||||
| PyOpenSSL | ||||
| passlib | ||||
| gunicorn | ||||
| docker-py | ||||
|   | ||||
| @@ -55,6 +55,7 @@ services: | ||||
|     env_file: freeposte.env | ||||
|     volumes: | ||||
|       - /freeposte/filter:/data | ||||
|       - /freeposte/dkim:/dkim | ||||
|  | ||||
|   antispam: | ||||
|     build: rspamd | ||||
| @@ -79,6 +80,7 @@ services: | ||||
|     env_file: freeposte.env | ||||
|     volumes: | ||||
|       - /freeposte/freeposte:/data | ||||
|       - /freeposte/dkim:/dkim | ||||
|       - /var/run/docker.sock:/var/run/docker.sock:ro | ||||
|  | ||||
|   webmail: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user