Compare commits
2 Commits
d9b1b5f869
...
ae5a2c3acc
| Author | SHA1 | Date | |
|---|---|---|---|
| ae5a2c3acc | |||
| c5170e67b0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
**/.env
|
**/.env
|
||||||
**/__pycache__
|
**/__pycache__
|
||||||
**/*.pem
|
**/*.pem
|
||||||
|
**/.DS_Store
|
||||||
|
|||||||
29
dns-config/README.md
Normal file
29
dns-config/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Mail-in-a-Box DNS Configuration Module
|
||||||
|
|
||||||
|
The `dns-config` module provides a command-line interface and a module to configure Mail-in-a-Box's server to delegate a DNS zone to Azure DNS. It retrieves the nameservers for a given Azure DNS zone and sets up the necessary DNS records in Mail-in-a-Box to delegate the zone to Azure DNS.
|
||||||
|
|
||||||
|
The module also contains additional commands to list Azure or Mail-in-a-Box DNS records. It can also be used to set, update, add or delete DNS records in Mail-in-a-Box.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The following commands are available in the `dns-config` module:
|
||||||
|
|
||||||
|
- `list-azure`: List Azure DNS records for a given resource group and zone name.
|
||||||
|
- `list-miab`: List Mail-in-a-Box DNS records of a given type (A, CNAME, MX, etc.).
|
||||||
|
- `set`: Set a DNS record in Mail-in-a-Box.
|
||||||
|
- `add`: Add a DNS record in Mail-in-a-Box.
|
||||||
|
- `delete`: Delete a DNS record in Mail-in-a-Box.
|
||||||
|
|
||||||
|
Each command has its own `--help` option that provides more information on how to use it.
|
||||||
|
|
||||||
|
To automatically configure an Azure DNS zone in Mail-in-a-Box, you can use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m dns-config configure-miab --resource-group <RESOURCE_GROUP> --zone-name <ZONE_NAME>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then list the Mail-in-a-Box DNS records to verify that the delegation records have been added:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m dns-config list-miab --type NS
|
||||||
|
```
|
||||||
1
dns-config/__init__.py
Normal file
1
dns-config/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Module
|
||||||
92
dns-config/__main__.py
Normal file
92
dns-config/__main__.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import argparse
|
||||||
|
from .azure import get_dns_nameservers
|
||||||
|
from . import miab
|
||||||
|
|
||||||
|
def azure_list_command(args):
|
||||||
|
nameservers = get_dns_nameservers(args.resource_group, args.zone_name)
|
||||||
|
print(f"Nameservers for zone {args.zone_name}:")
|
||||||
|
for ns in nameservers:
|
||||||
|
print(f" - {ns}")
|
||||||
|
|
||||||
|
# Mail-in-a-Box API wrapper functions
|
||||||
|
def miab_set_command(args):
|
||||||
|
return miab.set_custom_dns(args.domain_name, args.record_type, args.record_value)
|
||||||
|
|
||||||
|
def miab_delete_command(args):
|
||||||
|
return miab.delete_custom_dns(args.domain_name, args.record_type)
|
||||||
|
|
||||||
|
def miab_add_command(args):
|
||||||
|
return miab.add_custom_dns(args.domain_name, args.record_type, args.record_value)
|
||||||
|
|
||||||
|
def miab_list_command(args):
|
||||||
|
records = miab.list_custom_dns(args.record_type)
|
||||||
|
if args.record_type:
|
||||||
|
print(f"Custom {args.record_type} records:")
|
||||||
|
else:
|
||||||
|
print("Custom DNS records:")
|
||||||
|
|
||||||
|
for record in records:
|
||||||
|
print(f" - {record['name']} ({record['type']}): {record['value']}")
|
||||||
|
|
||||||
|
def miab_configure_command(args):
|
||||||
|
# Get a list of nameservers for the specified Azure DNS zone
|
||||||
|
nameservers = get_dns_nameservers(args.resource_group, args.zone_name)
|
||||||
|
|
||||||
|
if not nameservers:
|
||||||
|
print("No nameservers found. Aborting configuration.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Add each nameserver as an NS record in the Mail-in-a-Box DNS server
|
||||||
|
print(f"Configuring Mail-in-a-Box with nameservers from Azure DNS zone {args.zone_name}...")
|
||||||
|
for ns in nameservers:
|
||||||
|
print(f"Adding NS record for {ns}...")
|
||||||
|
success = miab.add_custom_dns(args.zone_name, "NS", ns)
|
||||||
|
if success:
|
||||||
|
print(f"Successfully added NS record for {ns}")
|
||||||
|
else:
|
||||||
|
print(f"Failed to add NS record for {ns}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="DNS Configuration Tool")
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
|
||||||
|
# list command - lists nameservers for a given Azure DNS zone
|
||||||
|
list_azure_parser = subparsers.add_parser("list-azure", help="List nameservers for an Azure DNS zone")
|
||||||
|
list_azure_parser.add_argument("--resource-group", required=True, help="Resource group name")
|
||||||
|
list_azure_parser.add_argument("--zone-name", required=True, help="DNS zone name")
|
||||||
|
list_azure_parser.set_defaults(func=azure_list_command)
|
||||||
|
|
||||||
|
# list-miab command - lists custom DNS records in the Mail-in-a-Box DNS server
|
||||||
|
list_miab_parser = subparsers.add_parser("list-miab", help="List custom DNS records in the Mail-in-a-Box DNS server")
|
||||||
|
list_miab_parser.add_argument("--record-type", help="Filter by DNS record type (e.g., A, CNAME, MX)")
|
||||||
|
list_miab_parser.set_defaults(func=miab_list_command)
|
||||||
|
|
||||||
|
# set command - sets a record in the Mail-in-a-Box DNS server
|
||||||
|
set_parser = subparsers.add_parser("set", help="Set a DNS record in the Mail-in-a-Box DNS server")
|
||||||
|
set_parser.add_argument("--domain-name", required=True, help="Domain name to set the record for")
|
||||||
|
set_parser.add_argument("--record-type", required=True, help="DNS record type (e.g., A, CNAME, MX)")
|
||||||
|
set_parser.add_argument("--record-value", required=True, help="Value for the DNS record")
|
||||||
|
set_parser.set_defaults(func=miab_set_command)
|
||||||
|
|
||||||
|
# delete command - deletes a record from the Mail-in-a-Box DNS server
|
||||||
|
delete_parser = subparsers.add_parser("delete", help="Delete a DNS record from the Mail-in-a-Box DNS server")
|
||||||
|
delete_parser.add_argument("--domain-name", required=True, help="Domain name to delete the record for")
|
||||||
|
delete_parser.add_argument("--record-type", required=True, help="DNS record type (e.g., A, CNAME, MX)")
|
||||||
|
delete_parser.add_argument("--record-value", help="Value for the DNS record")
|
||||||
|
delete_parser.set_defaults(func=miab_delete_command)
|
||||||
|
|
||||||
|
# add command - adds a record to the Mail-in-a-Box DNS server
|
||||||
|
add_parser = subparsers.add_parser("add", help="Add a DNS record to the Mail-in-a-Box DNS server")
|
||||||
|
add_parser.add_argument("--domain-name", required=True, help="Domain name to add the record for")
|
||||||
|
add_parser.add_argument("--record-type", required=True, help="DNS record type (e.g., A, CNAME, MX)")
|
||||||
|
add_parser.add_argument("--record-value", required=True, help="Value for the DNS record")
|
||||||
|
add_parser.set_defaults(func=miab_add_command)
|
||||||
|
|
||||||
|
# configure-miab - retrieves nameservers for an Azure DNS zone and configures them in the Mail-in-a-Box DNS server
|
||||||
|
configure_miab_parser = subparsers.add_parser("configure-miab", help="Configure Mail-in-a-Box DNS server with nameservers from an Azure DNS zone")
|
||||||
|
configure_miab_parser.add_argument("--resource-group", required=True, help="Azure Resource group name containing the DNS zone")
|
||||||
|
configure_miab_parser.add_argument("--zone-name", required=True, help="DNS zone name")
|
||||||
|
configure_miab_parser.set_defaults(func=miab_configure_command)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
args.func(args)
|
||||||
15
dns-config/azure.py
Normal file
15
dns-config/azure.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from azure.identity import DefaultAzureCredential
|
||||||
|
from azure.mgmt.dns import DnsManagementClient
|
||||||
|
import os
|
||||||
|
|
||||||
|
AZURE_SUBSCRIPTION_ID = os.environ.get("AZURE_SUBSCRIPTION_ID", None)
|
||||||
|
|
||||||
|
def get_dns_nameservers(resource_group_name: str, zone_name: str, azure_subscription_id: str | None = None) -> list[str]:
|
||||||
|
try:
|
||||||
|
creds = DefaultAzureCredential()
|
||||||
|
dns_client = DnsManagementClient(creds, azure_subscription_id or AZURE_SUBSCRIPTION_ID)
|
||||||
|
zone = dns_client.zones.get(resource_group_name, zone_name)
|
||||||
|
return zone.name_servers
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error fetching DNS nameservers: {e}")
|
||||||
|
return []
|
||||||
67
dns-config/miab.py
Normal file
67
dns-config/miab.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from os import getenv
|
||||||
|
from base64 import b64encode
|
||||||
|
from urllib import parse
|
||||||
|
from jmespath import search
|
||||||
|
import requests
|
||||||
|
|
||||||
|
MIAB_USERNAME = getenv('MIAB_USERNAME', None) or getenv('MAILINABOX_EMAIL', None)
|
||||||
|
MIAB_PASSWORD = getenv('MIAB_PASSWORD', None) or getenv('MAILINABOX_PASSWORD', None)
|
||||||
|
MIAB_HOST = getenv('MIAB_HOST', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
MIAB_HOST = parse.urlparse(getenv('MAILINABOX_BASE_URL', None)).hostname
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Prepare the Basic Auth header for MIAB API requests
|
||||||
|
MIAB_AUTH_HEADER = "Basic " + b64encode(f"{MIAB_USERNAME}:{MIAB_PASSWORD}".encode()).decode()
|
||||||
|
|
||||||
|
def get_miab_url(domain_name: str, record_type: str = "A") -> str:
|
||||||
|
if record_type == "A":
|
||||||
|
return f"https://{MIAB_HOST}/admin/dns/custom/{domain_name}"
|
||||||
|
else:
|
||||||
|
return f"https://{MIAB_HOST}/admin/dns/custom/{domain_name}/{record_type.upper()}"
|
||||||
|
|
||||||
|
def set_custom_dns(domain_name: str, record_type: str = "A", record_value: str | None = None) -> bool:
|
||||||
|
if not MIAB_HOST or not MIAB_USERNAME or not MIAB_PASSWORD:
|
||||||
|
raise Exception("MIAB configuration is incomplete. Please set MIAB_HOST, MIAB_USERNAME, and MIAB_PASSWORD.")
|
||||||
|
|
||||||
|
url = get_miab_url(domain_name, record_type)
|
||||||
|
payload = record_value if record_value else ""
|
||||||
|
response = requests.put(url, data=payload, headers={"Authorization": MIAB_AUTH_HEADER, "Content-Type": "text/plain"})
|
||||||
|
return response.status_code == 200
|
||||||
|
|
||||||
|
def delete_custom_dns(domain_name: str, record_type: str = "A", record_value: str | None = None) -> bool:
|
||||||
|
if not MIAB_HOST or not MIAB_USERNAME or not MIAB_PASSWORD:
|
||||||
|
raise Exception("MIAB configuration is incomplete. Please set MIAB_HOST, MIAB_USERNAME, and MIAB_PASSWORD.")
|
||||||
|
|
||||||
|
url = get_miab_url(domain_name, record_type)
|
||||||
|
payload = record_value if record_value else ""
|
||||||
|
response = requests.delete(url, data=payload, headers={"Authorization": MIAB_AUTH_HEADER, "Content-Type": "text/plain"})
|
||||||
|
return response.status_code == 200
|
||||||
|
|
||||||
|
def add_custom_dns(domain_name: str, record_type: str = "A", record_value: str | None = None) -> bool:
|
||||||
|
if not MIAB_HOST or not MIAB_USERNAME or not MIAB_PASSWORD:
|
||||||
|
raise Exception("MIAB configuration is incomplete. Please set MIAB_HOST, MIAB_USERNAME, and MIAB_PASSWORD.")
|
||||||
|
|
||||||
|
url = get_miab_url(domain_name, record_type)
|
||||||
|
payload = record_value if record_value else ""
|
||||||
|
response = requests.post(url, data=payload, headers={"Authorization": MIAB_AUTH_HEADER, "Content-Type": "text/plain"})
|
||||||
|
return response.status_code == 200
|
||||||
|
|
||||||
|
def list_custom_dns(record_type: str = None):
|
||||||
|
response = requests.get(
|
||||||
|
f"https://{MIAB_HOST}/admin/dns/custom",
|
||||||
|
headers={"Authorization": MIAB_AUTH_HEADER}
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
records = response.json()
|
||||||
|
if record_type:
|
||||||
|
jmespath_expr = f"[?rtype=='{record_type.upper()}']"
|
||||||
|
else:
|
||||||
|
jmespath_expr = "[]"
|
||||||
|
|
||||||
|
return search(jmespath_expr + ".{name: qname, type: rtype, value: value}", records)
|
||||||
|
else:
|
||||||
|
raise Exception(f"Failed to retrieve DNS records: {response.status_code} {response.text}")
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
requests
|
||||||
|
jmespath
|
||||||
|
azure-identity
|
||||||
|
azure-mgmt-dns
|
||||||
Reference in New Issue
Block a user