summaryrefslogtreecommitdiffstats
path: root/scripts/python
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/python')
-rwxr-xr-xscripts/python/mxme.py220
1 files changed, 177 insertions, 43 deletions
diff --git a/scripts/python/mxme.py b/scripts/python/mxme.py
index 6bdbf69..73758c1 100755
--- a/scripts/python/mxme.py
+++ b/scripts/python/mxme.py
@@ -1,5 +1,20 @@
#!/usr/bin/python3
"""
+The flagship email-domain enabler and repair tool for Mailcow/PowerDNS infrastructures.
+
+The following checks will be performed:
+ DNS:
+ - DMARC record
+ - DKIM record
+ - MX1 + MX2 records
+ Mail:
+ - MX1 domain entry
+ - MX2 domain entry
+ - MX1 DKIM keypair
+
+If any of the checks fail, the failed operation will be written to the respective system, in addition to overwriting possibly existing DNS records with fresh DMARC/DKIM/SPF/MX records.
+
+Created and Last modified: 15/09/2021 by Georg Pfuetzenreuter <georg@lysergic.dev>
"""
import requests
import sys
@@ -21,8 +36,10 @@ APIKEY_PDNS = os.environ.get('APIKEY_PDNS')
# MAILCOW SETTINGS
ENDPOINT_MAILCOW = os.environ.get('ENDPOINT_MAILCOW')
APIKEY_MAILCOW = os.environ.get('APIKEY_MAILCOW')
+ENDPOINT_MAILCOW_2 = os.environ.get('ENDPOINT_MAILCOW_2')
+APIKEY_MAILCOW_2 = os.environ.get('APIKEY_MAILCOW_2')
-if None in (ENDPOINT_PDNS, APIKEY_PDNS, ENDPOINT_MAILCOW, APIKEY_MAILCOW):
+if None in (ENDPOINT_PDNS, APIKEY_PDNS, ENDPOINT_MAILCOW, APIKEY_MAILCOW, ENDPOINT_MAILCOW_2, APIKEY_MAILCOW_2):
print("Could not load environment variables. Please check your .env file.")
sys.exit(0)
@@ -58,7 +75,7 @@ if dnsok == False:
sys.exit(1)
# MAILCOW (can I put cow emoji in comment?)
-print("Mail: Querying domain ...")
+print("Mail [MX1]: Querying domain ...")
server = ENDPOINT_MAILCOW
api_key = APIKEY_MAILCOW
api = '/api/v1'
@@ -91,36 +108,67 @@ except requests.exceptions.HTTPError as err:
print(err)
sys.exit(1)
-if mailok == True and initprimary == False:
- print("Mail: Querying DKIM ...")
- URL = server + get + '/dkim/' + domain
- try:
- response = requests.get(
- URL,
- headers = {'accept': 'application/json', 'X-API-Key': api_key},
- )
- data = response.json()
- #print(data)
- if 'dkim_selector' in data:
- selector = data['dkim_selector']
- txtshould = data['dkim_txt']
- length = data['length']
- pubkey = data['pubkey']
- #print(f"Domain: {domain}\nSelector: {selector}\nTXT: {txtshould}\nPublic Key: {pubkey}")
- #print(txtshould)
- print("Mail: DKIM keypair found.")
- dkimok = True
- else:
- dkimok = False
- print("Mail: No DKIM keypair found.")
- except KeyError:
- print("Mail: No or faulty DKIM lookup.")
- except requests.exceptions.ConnectionError as err:
- print("Connection failed.")
- sys.exit(1)
- except requests.exceptions.HTTPError as err:
- print(err)
- sys.exit(1)
+print("Mail [MX2]: Querying domain ...")
+server2 = ENDPOINT_MAILCOW_2
+api_key2 = APIKEY_MAILCOW_2
+api2 = '/api/v1'
+URL = server2 + get + '/domain/' + domain
+try:
+ response = requests.get(
+ URL,
+ headers = {'accept': 'application/json', 'X-API-Key': api_key2},
+ )
+ data = response.json()
+ status = response.status_code
+ #print(data)
+ if 'max_new_mailbox_quota' in data:
+ print("Mail: Domain found.")
+ mail2ok = True
+ initsecondary = False
+ domain_name = data['domain_name']
+ relayhost = data['relayhost']
+ else:
+ mail2ok = False
+ initsecondary = True
+ print("Mail: Domain NOT found.")
+ dkim2ok = False
+except requests.exceptions.ConnectionError as err:
+ print("Connection failed.")
+ sys.exit(1)
+except requests.exceptions.HTTPError as err:
+ print(err)
+ sys.exit(1)
+
+#if mailok == True and initprimary == False:
+print("Mail: Querying DKIM ...")
+URL = server + get + '/dkim/' + domain
+try:
+ response = requests.get(
+ URL,
+ headers = {'accept': 'application/json', 'X-API-Key': api_key},
+ )
+ data = response.json()
+ #print(data)
+ if 'dkim_selector' in data:
+ selector = data['dkim_selector']
+ txtshould = data['dkim_txt']
+ length = data['length']
+ pubkey = data['pubkey']
+ #print(f"Domain: {domain}\nSelector: {selector}\nTXT: {txtshould}\nPublic Key: {pubkey}")
+ #print(txtshould)
+ print("Mail: DKIM keypair found.")
+ dkimok = True
+ else:
+ dkimok = False
+ print("Mail: No DKIM keypair found.")
+except KeyError:
+ print("Mail: No or faulty DKIM lookup.")
+except requests.exceptions.ConnectionError as err:
+ print("Connection failed.")
+ sys.exit(1)
+except requests.exceptions.HTTPError as err:
+ print(err)
+ sys.exit(1)
print("DNS: Querying records ...")
URL = ENDPOINT_PDNS + '/api/v1/servers/localhost/zones/' + domain + './export'
@@ -155,10 +203,32 @@ try:
except NameError:
print("DNS: No DMARC TXT record found.")
dnsdmarcok = False
+ for record in data.split('\n'):
+ if '10 3gy.de.' in record:
+ mxrec1 = record
+ try:
+ mxrec1
+ print("DNS: Found primary MX record.")
+ dnsmx1ok = True
+ except NameError:
+ print("DNS: Did NOT find primary MX record.")
+ dnsmx1ok = False
+ for record in data.split('\n'):
+ if '20 3gy.pl.' in record:
+ mxrec2 = record
+ try:
+ mxrec2
+ print("DNS: Found secondary MX record.")
+ dnsmx2ok = True
+ except NameError:
+ print("DNS: Did NOT find secondary MX record.")
+ dnsmx2ok = False
except NameError:
- print("DNS: Missing or faulty DKIM/DMARC records.")
+ print("DNS: Missing or faulty records.")
dnsdmarcok = False
dnsdkimok = False
+ dnsmx1ok = False
+ dnsmx2ok = False
except requests.exceptions.ConnectionError as err:
print("Connection failed.")
sys.exit(1)
@@ -166,17 +236,21 @@ except requests.exceptions.HTTPError as err:
print(err)
sys.exit(1)
-if dnsok == True and mailok == True and dkimok == True and dnsdkimok == True and dnsdmarcok == True:
+if dnsok == True and mailok == True and dkimok == True and dnsdkimok == True and dnsdmarcok == True and dnsmx1ok == True and dnsmx2ok == True:
print("All good. No changes seem to be needed. Aborting.")
sys.exit(0)
else:
print("Found inconsistencies:")
- print(f"DNS OK: {dnsok} - Mail OK: {mailok} - Mail DKIM OK: {dkimok} - DNS DKIM OK: {dnsdkimok} - DNS DMARC OK: {dnsdmarcok}")
- print("Will attempt a full repair if not cancelled within 5 seconds ...")
- time.sleep(5)
+ print(f"DNS OK: {dnsok} - Mail OK: {mailok} - Mail DKIM OK: {dkimok} - DNS DKIM OK: {dnsdkimok} - DNS DMARC OK: {dnsdmarcok} - DNS MX1 OK: {dnsmx1ok} - DNS MX2 OK: {dnsmx2ok}")
+ try:
+ print("Will attempt a full repair if not cancelled within 5 seconds ...")
+ time.sleep(5)
+ except KeyboardInterrupt:
+ print(" Cancelled!")
+ sys.exit(1)
if initprimary == True:
- print("Mail: Initializing domain ...")
+ print("Mail [MX1]: Initializing domain ...")
URL = server + add + '/domain'
payload = {
"active": "1",
@@ -199,21 +273,60 @@ if initprimary == True:
json = payload,
)
data = response.json()
- status = data[0]['type']
try:
+ status = data[0]['type']
status
except:
- print("Mail Error:")
+ print("Mail [MX1] Error:")
print(data)
sys.exit(1)
if status == 'success':
- print("Mail: Created domain.")
+ print("Mail [MX1]: Created domain.")
if status == 'danger':
- print("Mail: Failed to create domain.")
+ print("Mail [MX1]: Failed to create domain.")
print(data)
#print(f"CREATION: {status}")
-if initprimary == True or dkimok == False:
+if initsecondary == True:
+ print("Mail [MX2]: Initializing domain ...")
+ URL = server2 + add + '/domain'
+ payload = {
+ "active": "1",
+ "aliases": "20",
+ "backupmx": "1",
+ "defquota": "1024",
+ "description": domain + ' - Failover',
+ "domain": domain,
+ "mailboxes": "10",
+ "maxquota": "2048",
+ "quota": "5120",
+ "relay_all_recipients": "1",
+ "rl_frame": "s",
+ "rl_value": "10",
+ "restart_sogo": "0"
+ }
+ response = requests.post(
+ URL,
+ headers = {'accept': 'application/json', 'X-API-Key': api_key2, 'Content-Type': 'application/json'},
+ json = payload,
+ )
+ data = response.json()
+ try:
+ status = data[0]['type']
+ status
+ except:
+ print("Mail [MX2] Error:")
+ print(data)
+ sys.exit(1)
+ if status == 'success':
+ print("Mail [MX2]: Created domain.")
+ if status == 'danger':
+ print("Mail [MX2]: Failed to create domain.")
+ print(data)
+ #print(f"CREATION: {status}")
+
+#if initprimary == True or dkimok == False:
+if dkimok == False:
print("Mail: Initializing DKIM ...")
URL = server + add + '/dkim'
payload = {
@@ -340,6 +453,27 @@ else:
print(status)
print(response.json())
sys.exit(1)
+print("DNS: Patching MX ...")
+payload = {
+"rrsets": [{"name": domain + ".", "type": "MX", "ttl": "3600", "changetype": "REPLACE", "records": [{"content": "10 3gy.de.", "disabled": False, "name": domain + "."}, {"content": "20 3gy.pl.", "disabled": False, "name": domain + "."}]}]
+}
+response = requests.patch(
+URL,
+headers = {'accept': 'application/json', 'X-API-Key': APIKEY_PDNS, 'Content-Type': 'application/json'},
+json = payload,
+)
+status = response.status_code
+if status == 204:
+ print("MX: OK!")
+elif status == 422:
+ print("MX: Failed:")
+ print(response.json())
+ sys.exit(1)
+else:
+ print("Unhandled error.")
+ print(status)
+ print(response.json())
+ sys.exit(1)
print("Done.")
sys.exit(0)