refactor: Remove mailinabox packages, replaced by secubox-app-mailserver
Deleted: - secubox-app-mailinabox (Docker-based) - luci-app-mailinabox Replaced by custom native implementation: - secubox-app-mailserver (LXC Postfix+Dovecot) - luci-app-mailserver Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
04d61baa6f
commit
58fe0909ac
@ -1,32 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Copyright (C) 2025 CyberMind.fr
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI support for Mail-in-a-Box
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_NAME:=luci-app-mailinabox
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
define Package/luci-app-mailinabox/install
|
||||
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
|
||||
$(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.mailinabox $(1)/usr/libexec/rpcd/luci.mailinabox
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-mailinabox.json $(1)/usr/share/luci/menu.d/luci-app-mailinabox.json
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
|
||||
$(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-mailinabox.json $(1)/usr/share/rpcd/acl.d/luci-app-mailinabox.json
|
||||
|
||||
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/mailinabox
|
||||
$(INSTALL_DATA) ./htdocs/luci-static/resources/view/mailinabox/*.js $(1)/www/luci-static/resources/view/mailinabox/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
@ -1,41 +0,0 @@
|
||||
# LuCI Mail-in-a-Box
|
||||
|
||||
Self-hosted mail server management dashboard (SMTP, IMAP, webmail).
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
opkg install luci-app-mailinabox
|
||||
```
|
||||
|
||||
## Access
|
||||
|
||||
LuCI menu: **Services -> Mail Server**
|
||||
|
||||
## Tabs
|
||||
|
||||
- **Overview** -- Service status, container health, domain configuration
|
||||
- **Settings** -- Hostname, domain, service control
|
||||
|
||||
## RPCD Methods
|
||||
|
||||
Backend: `luci.mailinabox`
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `status` | Service and container status |
|
||||
| `get_config` | Get mail server configuration |
|
||||
| `save_config` | Save hostname and domain settings |
|
||||
| `install` | Install Mail-in-a-Box container |
|
||||
| `start` | Start mail services |
|
||||
| `stop` | Stop mail services |
|
||||
| `restart` | Restart mail services |
|
||||
| `logs` | Fetch service logs |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `luci-base`
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@ -1,103 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require ui';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
|
||||
var callStatus = rpc.declare({ object: 'luci.mailinabox', method: 'status', expect: {} });
|
||||
var callInstall = rpc.declare({ object: 'luci.mailinabox', method: 'install', expect: {} });
|
||||
var callStart = rpc.declare({ object: 'luci.mailinabox', method: 'start', expect: {} });
|
||||
var callStop = rpc.declare({ object: 'luci.mailinabox', method: 'stop', expect: {} });
|
||||
var callRestart = rpc.declare({ object: 'luci.mailinabox', method: 'restart', expect: {} });
|
||||
|
||||
var css = '.mb-container{max-width:900px;margin:0 auto}.mb-header{display:flex;justify-content:space-between;align-items:center;padding:1.5rem;background:linear-gradient(135deg,#3b82f6 0%,#1d4ed8 100%);border-radius:16px;color:#fff;margin-bottom:1.5rem}.mb-header h2{margin:0;font-size:1.5rem;display:flex;align-items:center;gap:.5rem}.mb-status{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border-radius:20px;font-size:.9rem}.mb-status.running{background:rgba(16,185,129,.2)}.mb-status.stopped{background:rgba(239,68,68,.2)}.mb-dot{width:10px;height:10px;border-radius:50%;animation:pulse 2s infinite}.mb-status.running .mb-dot{background:#10b981}.mb-status.stopped .mb-dot{background:#ef4444}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}.mb-card{background:#fff;border-radius:12px;padding:1.5rem;box-shadow:0 2px 8px rgba(0,0,0,.08);margin-bottom:1rem}.mb-card-title{font-size:1.1rem;font-weight:600;margin-bottom:1rem;display:flex;align-items:center;gap:.5rem}.mb-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem}.mb-info-item{padding:1rem;background:#f8f9fa;border-radius:8px}.mb-info-label{font-size:.8rem;color:#666;margin-bottom:.25rem}.mb-info-value{font-size:1rem;font-weight:500}.mb-actions{display:flex;gap:.75rem;flex-wrap:wrap}.mb-btn{padding:.6rem 1.2rem;border-radius:8px;border:none;cursor:pointer;font-weight:500;transition:all .2s}.mb-btn-primary{background:linear-gradient(135deg,#3b82f6,#1d4ed8);color:#fff}.mb-btn-success{background:#10b981;color:#fff}.mb-btn-danger{background:#ef4444;color:#fff}.mb-btn-secondary{background:#6b7280;color:#fff}.mb-btn:disabled{opacity:.5;cursor:not-allowed}.mb-ports{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1rem}.mb-port{padding:.5rem 1rem;background:#e0e7ff;border-radius:8px;font-size:.85rem}.mb-port-name{font-weight:600;color:#3b82f6}.mb-not-installed{text-align:center;padding:3rem}.mb-features{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:1rem;margin:1.5rem 0;text-align:left}.mb-feature{padding:.75rem;background:#eff6ff;border-radius:8px;font-size:.9rem}';
|
||||
|
||||
return view.extend({
|
||||
load: function() { return callStatus(); },
|
||||
|
||||
handleInstall: function() {
|
||||
ui.showModal(_('Installing Mail Server'), [E('p', { 'class': 'spinning' }, _('Installing...'))]);
|
||||
callInstall().then(function(r) {
|
||||
ui.hideModal();
|
||||
if (r.success) ui.addNotification(null, E('p', r.message || _('Started')));
|
||||
else ui.addNotification(null, E('p', _('Failed: ') + r.error), 'error');
|
||||
}).catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', e.message), 'error'); });
|
||||
},
|
||||
|
||||
handleStart: function() {
|
||||
ui.showModal(_('Starting...'), [E('p', { 'class': 'spinning' }, _('Starting...'))]);
|
||||
callStart().then(function(r) { ui.hideModal(); ui.addNotification(null, E('p', _('Started'))); })
|
||||
.catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', e.message), 'error'); });
|
||||
},
|
||||
|
||||
handleStop: function() {
|
||||
ui.showModal(_('Stopping...'), [E('p', { 'class': 'spinning' }, _('Stopping...'))]);
|
||||
callStop().then(function(r) { ui.hideModal(); ui.addNotification(null, E('p', _('Stopped'))); })
|
||||
.catch(function(e) { ui.hideModal(); ui.addNotification(null, E('p', e.message), 'error'); });
|
||||
},
|
||||
|
||||
render: function(status) {
|
||||
if (!document.getElementById('mb-mail-styles')) {
|
||||
var s = document.createElement('style'); s.id = 'mb-mail-styles'; s.textContent = css; document.head.appendChild(s);
|
||||
}
|
||||
|
||||
if (!status.installed || !status.docker_available) {
|
||||
return E('div', { 'class': 'mb-container' }, [
|
||||
E('div', { 'class': 'mb-header' }, [
|
||||
E('h2', {}, ['\ud83d\udce7 ', _('Mail Server')]),
|
||||
E('div', { 'class': 'mb-status stopped' }, [E('span', { 'class': 'mb-dot' }), E('span', {}, _('Not Installed'))])
|
||||
]),
|
||||
E('div', { 'class': 'mb-card' }, [
|
||||
E('div', { 'class': 'mb-not-installed' }, [
|
||||
E('div', { 'style': 'font-size:4rem;margin-bottom:1rem' }, '\ud83d\udce7'),
|
||||
E('h3', {}, _('Mail-in-a-Box')),
|
||||
E('p', {}, _('Self-hosted email server with SMTP, IMAP, spam filtering, and webmail.')),
|
||||
E('div', { 'class': 'mb-features' }, [
|
||||
E('div', { 'class': 'mb-feature' }, '\ud83d\udce4 SMTP'),
|
||||
E('div', { 'class': 'mb-feature' }, '\ud83d\udce5 IMAP'),
|
||||
E('div', { 'class': 'mb-feature' }, '\ud83d\udee1 SpamAssassin'),
|
||||
E('div', { 'class': 'mb-feature' }, '\ud83d\udd12 SSL/TLS'),
|
||||
E('div', { 'class': 'mb-feature' }, '\ud83c\udf10 Webmail'),
|
||||
E('div', { 'class': 'mb-feature' }, '\ud83d\udc80 Fail2ban')
|
||||
]),
|
||||
!status.docker_available ? E('div', { 'style': 'color:#ef4444;margin-bottom:1rem' }, _('Docker required')) : '',
|
||||
E('button', { 'class': 'mb-btn mb-btn-primary', 'click': ui.createHandlerFn(this, 'handleInstall'), 'disabled': !status.docker_available }, _('Install Mail Server'))
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
return E('div', { 'class': 'mb-container' }, [
|
||||
E('div', { 'class': 'mb-header' }, [
|
||||
E('h2', {}, ['\ud83d\udce7 ', _('Mail Server')]),
|
||||
E('div', { 'class': 'mb-status ' + (status.running ? 'running' : 'stopped') }, [
|
||||
E('span', { 'class': 'mb-dot' }),
|
||||
E('span', {}, status.running ? _('Running') : _('Stopped'))
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'mb-card' }, [
|
||||
E('div', { 'class': 'mb-card-title' }, ['\u2139\ufe0f ', _('Configuration')]),
|
||||
E('div', { 'class': 'mb-info-grid' }, [
|
||||
E('div', { 'class': 'mb-info-item' }, [E('div', { 'class': 'mb-info-label' }, _('Hostname')), E('div', { 'class': 'mb-info-value' }, status.hostname)]),
|
||||
E('div', { 'class': 'mb-info-item' }, [E('div', { 'class': 'mb-info-label' }, _('Domain')), E('div', { 'class': 'mb-info-value' }, status.domain)]),
|
||||
E('div', { 'class': 'mb-info-item' }, [E('div', { 'class': 'mb-info-label' }, _('Data Path')), E('div', { 'class': 'mb-info-value' }, status.data_path)])
|
||||
]),
|
||||
E('div', { 'class': 'mb-ports' }, [
|
||||
E('div', { 'class': 'mb-port' }, [E('span', { 'class': 'mb-port-name' }, 'SMTP'), ' :' + status.smtp_port]),
|
||||
E('div', { 'class': 'mb-port' }, [E('span', { 'class': 'mb-port-name' }, 'IMAP'), ' :' + status.imap_port]),
|
||||
E('div', { 'class': 'mb-port' }, [E('span', { 'class': 'mb-port-name' }, 'IMAPS'), ' :' + status.imaps_port])
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'mb-card' }, [
|
||||
E('div', { 'class': 'mb-card-title' }, ['\u26a1 ', _('Actions')]),
|
||||
E('div', { 'class': 'mb-actions' }, [
|
||||
E('button', { 'class': 'mb-btn mb-btn-success', 'click': ui.createHandlerFn(this, 'handleStart'), 'disabled': status.running }, _('Start')),
|
||||
E('button', { 'class': 'mb-btn mb-btn-danger', 'click': ui.createHandlerFn(this, 'handleStop'), 'disabled': !status.running }, _('Stop')),
|
||||
E('a', { 'href': L.url('admin', 'secubox', 'services', 'mailinabox', 'settings'), 'class': 'mb-btn mb-btn-secondary' }, _('Settings'))
|
||||
])
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
handleSaveApply: null, handleSave: null, handleReset: null
|
||||
});
|
||||
@ -1,75 +0,0 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require uci';
|
||||
|
||||
return view.extend({
|
||||
load: function() { return uci.load('mailinabox'); },
|
||||
|
||||
render: function() {
|
||||
var m, s, o;
|
||||
|
||||
m = new form.Map('mailinabox', _('Mail Server Settings'),
|
||||
_('Configure your mail server. IMPORTANT: Set hostname and domain before installing.'));
|
||||
|
||||
s = m.section(form.TypedSection, 'mailinabox', _('General Settings'));
|
||||
s.anonymous = true;
|
||||
s.addremove = false;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enabled'));
|
||||
o.default = '0';
|
||||
|
||||
o = s.option(form.Value, 'hostname', _('Mail Hostname'),
|
||||
_('Full hostname for mail server (e.g., mail.example.com)'));
|
||||
o.default = 'mail.example.com';
|
||||
|
||||
o = s.option(form.Value, 'domain', _('Domain'),
|
||||
_('Primary email domain (e.g., example.com)'));
|
||||
o.default = 'example.com';
|
||||
|
||||
o = s.option(form.Value, 'data_path', _('Data Path'));
|
||||
o.default = '/srv/mailserver';
|
||||
|
||||
o = s.option(form.Value, 'timezone', _('Timezone'));
|
||||
o.default = 'UTC';
|
||||
|
||||
s = m.section(form.TypedSection, 'mailinabox', _('Ports'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Value, 'smtp_port', _('SMTP Port'));
|
||||
o.datatype = 'port';
|
||||
o.default = '25';
|
||||
|
||||
o = s.option(form.Value, 'submission_port', _('Submission Port'));
|
||||
o.datatype = 'port';
|
||||
o.default = '587';
|
||||
|
||||
o = s.option(form.Value, 'imap_port', _('IMAP Port'));
|
||||
o.datatype = 'port';
|
||||
o.default = '143';
|
||||
|
||||
o = s.option(form.Value, 'imaps_port', _('IMAPS Port'));
|
||||
o.datatype = 'port';
|
||||
o.default = '993';
|
||||
|
||||
s = m.section(form.TypedSection, 'mailinabox', _('Features'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'enable_spamassassin', _('SpamAssassin'));
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.Flag, 'enable_clamav', _('ClamAV Antivirus'));
|
||||
o.default = '0';
|
||||
|
||||
o = s.option(form.Flag, 'enable_fail2ban', _('Fail2ban'));
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.ListValue, 'ssl_type', _('SSL Type'));
|
||||
o.value('letsencrypt', _("Let's Encrypt"));
|
||||
o.value('manual', _('Manual'));
|
||||
o.value('self-signed', _('Self-signed'));
|
||||
o.default = 'letsencrypt';
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@ -1,139 +0,0 @@
|
||||
#!/bin/sh
|
||||
# RPCD backend for Mail-in-a-Box LuCI app
|
||||
|
||||
CONFIG="mailinabox"
|
||||
CONTAINER="secbx-mailserver"
|
||||
|
||||
uci_get() { uci -q get ${CONFIG}.main.$1; }
|
||||
uci_set() { uci set ${CONFIG}.main.$1="$2" && uci commit ${CONFIG}; }
|
||||
|
||||
get_status() {
|
||||
local enabled=$(uci_get enabled)
|
||||
local hostname=$(uci_get hostname)
|
||||
local domain=$(uci_get domain)
|
||||
local data_path=$(uci_get data_path)
|
||||
|
||||
local docker_available=0
|
||||
command -v docker >/dev/null 2>&1 && docker_available=1
|
||||
|
||||
local running=0
|
||||
local container_status="stopped"
|
||||
if [ "$docker_available" = "1" ]; then
|
||||
if docker ps --filter "name=$CONTAINER" --format "{{.Names}}" 2>/dev/null | grep -q "$CONTAINER"; then
|
||||
running=1
|
||||
container_status="running"
|
||||
fi
|
||||
fi
|
||||
|
||||
local installed=0
|
||||
if [ "$docker_available" = "1" ]; then
|
||||
docker images --format "{{.Repository}}" 2>/dev/null | grep -q "docker-mailserver" && installed=1
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
|
||||
"running": $([ "$running" = "1" ] && echo "true" || echo "false"),
|
||||
"installed": $([ "$installed" = "1" ] && echo "true" || echo "false"),
|
||||
"docker_available": $([ "$docker_available" = "1" ] && echo "true" || echo "false"),
|
||||
"container_status": "$container_status",
|
||||
"hostname": "${hostname:-mail.example.com}",
|
||||
"domain": "${domain:-example.com}",
|
||||
"data_path": "${data_path:-/srv/mailserver}",
|
||||
"smtp_port": $(uci_get smtp_port || echo 25),
|
||||
"imap_port": $(uci_get imap_port || echo 143),
|
||||
"imaps_port": $(uci_get imaps_port || echo 993)
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
get_config() {
|
||||
cat <<EOF
|
||||
{
|
||||
"enabled": "$(uci_get enabled || echo 0)",
|
||||
"hostname": "$(uci_get hostname || echo mail.example.com)",
|
||||
"domain": "$(uci_get domain || echo example.com)",
|
||||
"data_path": "$(uci_get data_path || echo /srv/mailserver)",
|
||||
"timezone": "$(uci_get timezone || echo UTC)",
|
||||
"smtp_port": "$(uci_get smtp_port || echo 25)",
|
||||
"submission_port": "$(uci_get submission_port || echo 587)",
|
||||
"imap_port": "$(uci_get imap_port || echo 143)",
|
||||
"imaps_port": "$(uci_get imaps_port || echo 993)",
|
||||
"enable_spamassassin": "$(uci_get enable_spamassassin || echo 1)",
|
||||
"enable_clamav": "$(uci_get enable_clamav || echo 0)",
|
||||
"ssl_type": "$(uci_get ssl_type || echo letsencrypt)"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
save_config() {
|
||||
local input; read -r input
|
||||
local hostname=$(echo "$input" | jsonfilter -e '@.hostname' 2>/dev/null)
|
||||
local domain=$(echo "$input" | jsonfilter -e '@.domain' 2>/dev/null)
|
||||
[ -n "$hostname" ] && uci_set hostname "$hostname"
|
||||
[ -n "$domain" ] && uci_set domain "$domain"
|
||||
echo '{"success": true}'
|
||||
}
|
||||
|
||||
do_install() {
|
||||
if command -v mailinaboxctl >/dev/null 2>&1; then
|
||||
mailinaboxctl install >/tmp/mailinabox-install.log 2>&1 &
|
||||
echo '{"success": true, "message": "Installation started"}'
|
||||
else
|
||||
echo '{"success": false, "error": "mailinaboxctl not found"}'
|
||||
fi
|
||||
}
|
||||
|
||||
do_start() {
|
||||
[ -x /etc/init.d/mailinabox ] && /etc/init.d/mailinabox start >/dev/null 2>&1 && uci_set enabled '1'
|
||||
echo '{"success": true}'
|
||||
}
|
||||
|
||||
do_stop() {
|
||||
[ -x /etc/init.d/mailinabox ] && /etc/init.d/mailinabox stop >/dev/null 2>&1
|
||||
echo '{"success": true}'
|
||||
}
|
||||
|
||||
do_restart() {
|
||||
[ -x /etc/init.d/mailinabox ] && /etc/init.d/mailinabox restart >/dev/null 2>&1
|
||||
echo '{"success": true}'
|
||||
}
|
||||
|
||||
get_logs() {
|
||||
local log_content=""
|
||||
[ -f /tmp/mailinabox-install.log ] && log_content=$(tail -n 50 /tmp/mailinabox-install.log 2>/dev/null | sed 's/"/\\"/g' | tr '\n' '|')
|
||||
echo "{\"logs\": \"$log_content\"}"
|
||||
}
|
||||
|
||||
list_methods() {
|
||||
cat <<'EOF'
|
||||
{
|
||||
"status": {},
|
||||
"get_config": {},
|
||||
"save_config": {"hostname": "string", "domain": "string"},
|
||||
"install": {},
|
||||
"start": {},
|
||||
"stop": {},
|
||||
"restart": {},
|
||||
"logs": {}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
list) list_methods ;;
|
||||
call)
|
||||
case "$2" in
|
||||
status) get_status ;;
|
||||
get_config) get_config ;;
|
||||
save_config) save_config ;;
|
||||
install) do_install ;;
|
||||
start) do_start ;;
|
||||
stop) do_stop ;;
|
||||
restart) do_restart ;;
|
||||
logs) get_logs ;;
|
||||
*) echo '{"error": "Unknown method"}' ;;
|
||||
esac
|
||||
;;
|
||||
*) echo '{"error": "Unknown command"}' ;;
|
||||
esac
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"admin/services/mailinabox": {
|
||||
"title": "Mail Server",
|
||||
"order": 60,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": ["luci-app-mailinabox"]
|
||||
}
|
||||
},
|
||||
"admin/services/mailinabox/overview": {
|
||||
"title": "Overview",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mailinabox/overview"
|
||||
}
|
||||
},
|
||||
"admin/services/mailinabox/settings": {
|
||||
"title": "Settings",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mailinabox/settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
{
|
||||
"luci-app-mailinabox": {
|
||||
"description": "Grant access to Mail-in-a-Box",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.mailinabox": ["status", "get_config", "logs"]
|
||||
},
|
||||
"uci": ["mailinabox"]
|
||||
},
|
||||
"write": {
|
||||
"ubus": {
|
||||
"luci.mailinabox": ["install", "start", "stop", "restart", "update", "save_config", "add_account", "list_accounts"]
|
||||
},
|
||||
"uci": ["mailinabox"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-app-mailinabox
|
||||
PKG_RELEASE:=1
|
||||
PKG_VERSION:=2.0.0
|
||||
PKG_ARCH:=all
|
||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||
PKG_LICENSE:=CC0-1.0
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/secubox-app-mailinabox
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
PKGARCH:=all
|
||||
SUBMENU:=SecuBox Apps
|
||||
TITLE:=SecuBox Mail Server (docker-mailserver)
|
||||
DEPENDS:=
|
||||
endef
|
||||
|
||||
define Package/secubox-app-mailinabox/description
|
||||
Complete email server solution using docker-mailserver for SecuBox.
|
||||
|
||||
Features:
|
||||
- Full email server (SMTP, IMAP, POP3)
|
||||
- User account management (add/remove/list)
|
||||
- Email aliases support
|
||||
- SpamAssassin spam filtering
|
||||
- ClamAV antivirus (optional)
|
||||
- Fail2ban intrusion prevention
|
||||
- Let's Encrypt SSL certificates
|
||||
- Backup and restore functionality
|
||||
- DNS configuration verification
|
||||
- Health monitoring and diagnostics
|
||||
|
||||
Commands: mailinaboxctl --help
|
||||
endef
|
||||
|
||||
define Package/secubox-app-mailinabox/conffiles
|
||||
/etc/config/mailinabox
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/secubox-app-mailinabox/install
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/etc/config/mailinabox $(1)/etc/config/mailinabox
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/etc/init.d/mailinabox $(1)/etc/init.d/mailinabox
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/mailinaboxctl $(1)/usr/sbin/mailinaboxctl
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,secubox-app-mailinabox))
|
||||
@ -1,52 +0,0 @@
|
||||
# SecuBox Mail Server (docker-mailserver)
|
||||
|
||||
Full-featured mail server with SMTP, IMAP, POP3, spam filtering, antivirus, and automatic Let's Encrypt certificates. Runs docker-mailserver in a managed Docker container on OpenWrt.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
opkg install secubox-app-mailinabox
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
UCI config file: `/etc/config/mailinabox`
|
||||
|
||||
```bash
|
||||
uci set mailinabox.main.enabled='1'
|
||||
uci set mailinabox.main.hostname='mail.example.com'
|
||||
uci set mailinabox.main.domain='example.com'
|
||||
uci set mailinabox.main.ssl='letsencrypt'
|
||||
uci commit mailinabox
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
mailinaboxctl start # Start mail server
|
||||
mailinaboxctl stop # Stop mail server
|
||||
mailinaboxctl status # Show service status
|
||||
mailinaboxctl user add <email> # Add mail user
|
||||
mailinaboxctl user list # List mail users
|
||||
mailinaboxctl user del <email> # Remove mail user
|
||||
mailinaboxctl logs # View mail logs
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- SMTP (25/587), IMAP (993), POP3 (995)
|
||||
- SpamAssassin spam filtering
|
||||
- ClamAV antivirus scanning
|
||||
- DKIM/SPF/DMARC support
|
||||
- Automatic Let's Encrypt TLS certificates
|
||||
- User and alias management via CLI
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `dockerd`
|
||||
- `docker`
|
||||
- `containerd`
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@ -1,30 +0,0 @@
|
||||
config mailinabox 'main'
|
||||
option enabled '0'
|
||||
option image 'ghcr.io/docker-mailserver/docker-mailserver:latest'
|
||||
option data_path '/srv/mailserver'
|
||||
|
||||
# Domain configuration (MUST be configured before use)
|
||||
option hostname 'mail.example.com'
|
||||
option domain 'example.com'
|
||||
option timezone 'UTC'
|
||||
|
||||
# Port mappings
|
||||
option smtp_port '25'
|
||||
option submission_port '587'
|
||||
option submissions_port '465'
|
||||
option imap_port '143'
|
||||
option imaps_port '993'
|
||||
option pop3_port '110'
|
||||
option pop3s_port '995'
|
||||
|
||||
# Feature flags
|
||||
option enable_pop3 '0'
|
||||
option enable_clamav '0'
|
||||
option enable_spamassassin '1'
|
||||
option enable_fail2ban '1'
|
||||
|
||||
# SSL configuration
|
||||
# Options: letsencrypt, manual, self-signed
|
||||
option ssl_type 'letsencrypt'
|
||||
# Email for Let's Encrypt notifications (optional but recommended)
|
||||
#option letsencrypt_email 'admin@example.com'
|
||||
@ -1,42 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=95
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
EXTRA_COMMANDS="status"
|
||||
EXTRA_HELP=" status Show mail server status"
|
||||
|
||||
SERVICE_BIN="/usr/sbin/mailinaboxctl"
|
||||
|
||||
start_service() {
|
||||
local enabled
|
||||
config_load mailinabox
|
||||
config_get enabled main enabled 0
|
||||
|
||||
[ "$enabled" != "1" ] && {
|
||||
echo "Mail server is disabled. Enable with: uci set mailinabox.main.enabled=1"
|
||||
return 0
|
||||
}
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command "$SERVICE_BIN" service-run
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
"$SERVICE_BIN" service-stop >/dev/null 2>&1
|
||||
}
|
||||
|
||||
restart_service() {
|
||||
stop_service
|
||||
sleep 2
|
||||
start_service
|
||||
}
|
||||
|
||||
status() {
|
||||
"$SERVICE_BIN" status
|
||||
}
|
||||
@ -1,725 +0,0 @@
|
||||
#!/bin/sh
|
||||
# SecuBox Mail-in-a-Box manager - Docker Mailserver Edition
|
||||
# Copyright (C) 2024 CyberMind.fr
|
||||
#
|
||||
# Based on docker-mailserver for lightweight email hosting
|
||||
|
||||
CONFIG="mailinabox"
|
||||
CONTAINER_NAME="secbx-mailserver"
|
||||
OPKG_UPDATED=0
|
||||
|
||||
# Paths
|
||||
DATA_BASE="/srv/mailserver"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: mailinaboxctl <command>
|
||||
|
||||
Container Management:
|
||||
install Install prerequisites, prepare directories, pull image
|
||||
check Run prerequisite checks (ports, DNS, storage)
|
||||
update Pull new image and restart
|
||||
status Show container and service status
|
||||
logs Show container logs (use -f to follow)
|
||||
shell Open shell in container
|
||||
service-run Internal: run container via procd
|
||||
service-stop Stop container
|
||||
|
||||
Email Account Management:
|
||||
user-add <email> [password] Add email account
|
||||
user-del <email> Remove email account
|
||||
user-list List all email accounts
|
||||
user-passwd <email> Change user password
|
||||
alias-add <alias> <target> Add email alias
|
||||
alias-del <alias> Remove email alias
|
||||
alias-list List all aliases
|
||||
|
||||
Domain & SSL:
|
||||
domain-add <domain> Add email domain
|
||||
domain-list List configured domains
|
||||
ssl-status Show SSL certificate status
|
||||
ssl-renew Force SSL certificate renewal
|
||||
|
||||
Backup & Restore:
|
||||
backup [path] Backup mail data and config
|
||||
restore <backup-file> Restore from backup
|
||||
|
||||
Diagnostics:
|
||||
health Run health checks
|
||||
dns-check [domain] Verify DNS records for domain
|
||||
ports Check required ports
|
||||
config Show current configuration
|
||||
test-email <to> Send test email
|
||||
|
||||
Post-Installation:
|
||||
1. Configure hostname and domain in /etc/config/mailinabox
|
||||
2. Set proper DNS records (A, MX, SPF, DKIM, DMARC)
|
||||
3. Start with: /etc/init.d/mailinabox start
|
||||
4. Add users with: mailinaboxctl user-add admin@yourdomain.com
|
||||
|
||||
Required DNS Records:
|
||||
A mail.domain.com -> your-public-ip
|
||||
MX domain.com -> mail.domain.com (priority 10)
|
||||
TXT domain.com -> "v=spf1 mx -all"
|
||||
TXT _dmarc.domain.com -> "v=DMARC1; p=quarantine"
|
||||
TXT mail._domainkey.domain.com -> (DKIM key from container)
|
||||
EOF
|
||||
}
|
||||
|
||||
require_root() { [ "$(id -u)" -eq 0 ] || { echo "Root required" >&2; exit 1; }; }
|
||||
|
||||
log_info() { echo "[INFO] $*"; }
|
||||
log_warn() { echo "[WARN] $*" >&2; }
|
||||
log_error() { echo "[ERROR] $*" >&2; }
|
||||
|
||||
uci_get() { uci -q get ${CONFIG}.main.$1; }
|
||||
uci_set() { uci set ${CONFIG}.main.$1="$2" && uci commit ${CONFIG}; }
|
||||
|
||||
# Load configuration with defaults
|
||||
load_config() {
|
||||
enabled="$(uci_get enabled || echo 0)"
|
||||
image="$(uci_get image || echo ghcr.io/docker-mailserver/docker-mailserver:latest)"
|
||||
data_path="$(uci_get data_path || echo /srv/mailserver)"
|
||||
hostname="$(uci_get hostname || echo mail.example.com)"
|
||||
domain="$(uci_get domain || echo example.com)"
|
||||
timezone="$(uci_get timezone || cat /etc/TZ 2>/dev/null || echo UTC)"
|
||||
|
||||
# Ports
|
||||
smtp_port="$(uci_get smtp_port || echo 25)"
|
||||
submission_port="$(uci_get submission_port || echo 587)"
|
||||
submissions_port="$(uci_get submissions_port || echo 465)"
|
||||
imap_port="$(uci_get imap_port || echo 143)"
|
||||
imaps_port="$(uci_get imaps_port || echo 993)"
|
||||
pop3_port="$(uci_get pop3_port || echo 110)"
|
||||
pop3s_port="$(uci_get pop3s_port || echo 995)"
|
||||
|
||||
# Features
|
||||
enable_pop3="$(uci_get enable_pop3 || echo 0)"
|
||||
enable_clamav="$(uci_get enable_clamav || echo 0)"
|
||||
enable_spamassassin="$(uci_get enable_spamassassin || echo 1)"
|
||||
enable_fail2ban="$(uci_get enable_fail2ban || echo 1)"
|
||||
ssl_type="$(uci_get ssl_type || echo letsencrypt)"
|
||||
letsencrypt_email="$(uci_get letsencrypt_email)"
|
||||
}
|
||||
|
||||
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
|
||||
|
||||
# =============================================================================
|
||||
# Docker Functions
|
||||
# =============================================================================
|
||||
|
||||
ensure_packages() {
|
||||
for pkg in "$@"; do
|
||||
if ! opkg list-installed 2>/dev/null | grep -q "^$pkg "; then
|
||||
if [ "$OPKG_UPDATED" -eq 0 ]; then
|
||||
opkg update || return 1
|
||||
OPKG_UPDATED=1
|
||||
fi
|
||||
opkg install "$pkg" || return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
docker_ready() {
|
||||
command -v docker >/dev/null 2>&1 && [ -S /var/run/docker.sock ]
|
||||
}
|
||||
|
||||
check_prereqs() {
|
||||
load_config
|
||||
log_info "Checking prerequisites..."
|
||||
|
||||
# Check hostname configuration
|
||||
if [ "$hostname" = "mail.example.com" ] || [ "$domain" = "example.com" ]; then
|
||||
log_warn "Please configure hostname and domain in /etc/config/mailinabox"
|
||||
log_warn "docker-mailserver requires a valid domain name"
|
||||
fi
|
||||
|
||||
# Check cgroups
|
||||
[ -d /sys/fs/cgroup ] || { log_error "/sys/fs/cgroup missing"; return 1; }
|
||||
|
||||
# Install Docker
|
||||
ensure_packages dockerd docker containerd || return 1
|
||||
|
||||
# Enable and start Docker
|
||||
/etc/init.d/dockerd enable >/dev/null 2>&1
|
||||
if ! /etc/init.d/dockerd status >/dev/null 2>&1; then
|
||||
/etc/init.d/dockerd start || return 1
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Wait for Docker socket
|
||||
local retry=0
|
||||
while [ ! -S /var/run/docker.sock ] && [ $retry -lt 30 ]; do
|
||||
sleep 1
|
||||
retry=$((retry + 1))
|
||||
done
|
||||
|
||||
[ -S /var/run/docker.sock ] || { log_error "Docker socket not available"; return 1; }
|
||||
|
||||
# Create data directories
|
||||
ensure_dir "$data_path"
|
||||
ensure_dir "$data_path/mail-data"
|
||||
ensure_dir "$data_path/mail-state"
|
||||
ensure_dir "$data_path/mail-logs"
|
||||
ensure_dir "$data_path/config"
|
||||
|
||||
log_info "Docker ready, directories created"
|
||||
return 0
|
||||
}
|
||||
|
||||
pull_image() {
|
||||
load_config
|
||||
log_info "Pulling Docker image: $image"
|
||||
docker pull "$image"
|
||||
}
|
||||
|
||||
stop_container() {
|
||||
docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
docker rm "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
container_running() {
|
||||
docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER_NAME}$"
|
||||
}
|
||||
|
||||
# Execute setup.sh in container
|
||||
docker_setup() {
|
||||
if ! container_running; then
|
||||
log_error "Container not running. Start with: /etc/init.d/mailinabox start"
|
||||
return 1
|
||||
fi
|
||||
docker exec -it "$CONTAINER_NAME" setup "$@"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# User Management Commands
|
||||
# =============================================================================
|
||||
|
||||
cmd_user_add() {
|
||||
local email="$1"
|
||||
local password="$2"
|
||||
|
||||
[ -z "$email" ] && { log_error "Usage: mailinaboxctl user-add <email> [password]"; return 1; }
|
||||
|
||||
if [ -n "$password" ]; then
|
||||
docker_setup email add "$email" "$password"
|
||||
else
|
||||
docker_setup email add "$email"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_user_del() {
|
||||
local email="$1"
|
||||
[ -z "$email" ] && { log_error "Usage: mailinaboxctl user-del <email>"; return 1; }
|
||||
docker_setup email del "$email"
|
||||
}
|
||||
|
||||
cmd_user_list() {
|
||||
docker_setup email list
|
||||
}
|
||||
|
||||
cmd_user_passwd() {
|
||||
local email="$1"
|
||||
[ -z "$email" ] && { log_error "Usage: mailinaboxctl user-passwd <email>"; return 1; }
|
||||
docker_setup email update "$email"
|
||||
}
|
||||
|
||||
cmd_alias_add() {
|
||||
local alias="$1"
|
||||
local target="$2"
|
||||
[ -z "$alias" ] || [ -z "$target" ] && { log_error "Usage: mailinaboxctl alias-add <alias> <target>"; return 1; }
|
||||
docker_setup alias add "$alias" "$target"
|
||||
}
|
||||
|
||||
cmd_alias_del() {
|
||||
local alias="$1"
|
||||
[ -z "$alias" ] && { log_error "Usage: mailinaboxctl alias-del <alias>"; return 1; }
|
||||
docker_setup alias del "$alias"
|
||||
}
|
||||
|
||||
cmd_alias_list() {
|
||||
docker_setup alias list
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Domain & SSL Commands
|
||||
# =============================================================================
|
||||
|
||||
cmd_domain_add() {
|
||||
local domain="$1"
|
||||
[ -z "$domain" ] && { log_error "Usage: mailinaboxctl domain-add <domain>"; return 1; }
|
||||
|
||||
# Create virtual domain entry
|
||||
load_config
|
||||
local vhost_file="$data_path/config/postfix-virtual.cf"
|
||||
if ! grep -q "^$domain" "$vhost_file" 2>/dev/null; then
|
||||
echo "$domain" >> "$vhost_file"
|
||||
log_info "Domain $domain added. Restart service to apply."
|
||||
else
|
||||
log_warn "Domain $domain already exists"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_domain_list() {
|
||||
load_config
|
||||
log_info "Configured domains:"
|
||||
if [ -f "$data_path/config/postfix-virtual.cf" ]; then
|
||||
cat "$data_path/config/postfix-virtual.cf" | grep -v "^#" | grep -v "^$"
|
||||
fi
|
||||
echo ""
|
||||
echo "Primary domain: $domain"
|
||||
echo "Mail hostname: $hostname"
|
||||
}
|
||||
|
||||
cmd_ssl_status() {
|
||||
load_config
|
||||
log_info "SSL Configuration:"
|
||||
echo " Type: $ssl_type"
|
||||
|
||||
if container_running; then
|
||||
docker_setup debug show-mail-logs | grep -i "ssl\|cert\|tls" | tail -20
|
||||
fi
|
||||
|
||||
# Check certificate files
|
||||
if [ -d "$data_path/config/ssl" ]; then
|
||||
echo ""
|
||||
echo "Certificate files:"
|
||||
ls -la "$data_path/config/ssl/" 2>/dev/null || echo " No certificates found"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_ssl_renew() {
|
||||
load_config
|
||||
if [ "$ssl_type" = "letsencrypt" ]; then
|
||||
log_info "Triggering Let's Encrypt renewal..."
|
||||
if container_running; then
|
||||
docker exec "$CONTAINER_NAME" certbot renew
|
||||
else
|
||||
log_error "Container not running"
|
||||
fi
|
||||
else
|
||||
log_warn "SSL type is not letsencrypt"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Backup & Restore Commands
|
||||
# =============================================================================
|
||||
|
||||
cmd_backup() {
|
||||
require_root
|
||||
load_config
|
||||
|
||||
local backup_path="${1:-/tmp}"
|
||||
local timestamp=$(date +%Y%m%d_%H%M%S)
|
||||
local backup_file="$backup_path/mailserver_backup_$timestamp.tar.gz"
|
||||
|
||||
log_info "Creating backup..."
|
||||
|
||||
# Stop container for consistent backup
|
||||
local was_running=0
|
||||
if container_running; then
|
||||
was_running=1
|
||||
log_info "Stopping container for backup..."
|
||||
stop_container
|
||||
fi
|
||||
|
||||
# Create backup
|
||||
tar czf "$backup_file" -C "$(dirname $data_path)" "$(basename $data_path)" 2>/dev/null || {
|
||||
log_error "Backup failed"
|
||||
[ $was_running -eq 1 ] && /etc/init.d/mailinabox start
|
||||
return 1
|
||||
}
|
||||
|
||||
# Restart if was running
|
||||
[ $was_running -eq 1 ] && /etc/init.d/mailinabox start
|
||||
|
||||
log_info "Backup created: $backup_file"
|
||||
ls -lh "$backup_file"
|
||||
}
|
||||
|
||||
cmd_restore() {
|
||||
require_root
|
||||
load_config
|
||||
|
||||
local backup_file="$1"
|
||||
[ -z "$backup_file" ] || [ ! -f "$backup_file" ] && {
|
||||
log_error "Usage: mailinaboxctl restore <backup-file>"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_warn "This will OVERWRITE existing mail data!"
|
||||
echo -n "Continue? [y/N] "
|
||||
read answer
|
||||
[ "$answer" != "y" ] && [ "$answer" != "Y" ] && { echo "Aborted"; return 1; }
|
||||
|
||||
# Stop container
|
||||
if container_running; then
|
||||
log_info "Stopping container..."
|
||||
stop_container
|
||||
fi
|
||||
|
||||
# Remove existing data
|
||||
log_info "Removing existing data..."
|
||||
rm -rf "$data_path"
|
||||
|
||||
# Restore
|
||||
log_info "Restoring from backup..."
|
||||
tar xzf "$backup_file" -C "$(dirname $data_path)" || {
|
||||
log_error "Restore failed"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_info "Restore complete. Start service with: /etc/init.d/mailinabox start"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Diagnostic Commands
|
||||
# =============================================================================
|
||||
|
||||
cmd_health() {
|
||||
load_config
|
||||
|
||||
echo "=== Mail Server Health Check ==="
|
||||
echo ""
|
||||
|
||||
# Container status
|
||||
echo "Container Status:"
|
||||
if container_running; then
|
||||
echo " [OK] Container is running"
|
||||
local uptime=$(docker inspect --format='{{.State.StartedAt}}' "$CONTAINER_NAME" 2>/dev/null)
|
||||
echo " Started: $uptime"
|
||||
else
|
||||
echo " [FAIL] Container is not running"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Port checks
|
||||
echo "Port Status:"
|
||||
for port in $smtp_port $submission_port $imaps_port; do
|
||||
if netstat -tln 2>/dev/null | grep -q ":$port "; then
|
||||
echo " [OK] Port $port is listening"
|
||||
else
|
||||
echo " [WARN] Port $port is not listening"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Service checks inside container
|
||||
if container_running; then
|
||||
echo "Services Status:"
|
||||
docker exec "$CONTAINER_NAME" supervisorctl status 2>/dev/null || echo " Unable to check services"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Disk usage
|
||||
echo "Disk Usage:"
|
||||
if [ -d "$data_path" ]; then
|
||||
du -sh "$data_path" 2>/dev/null
|
||||
du -sh "$data_path"/* 2>/dev/null | head -10
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_dns_check() {
|
||||
load_config
|
||||
local check_domain="${1:-$domain}"
|
||||
|
||||
echo "=== DNS Check for $check_domain ==="
|
||||
echo ""
|
||||
|
||||
# A record
|
||||
echo "A Record (mail.$check_domain):"
|
||||
local a_record=$(nslookup "mail.$check_domain" 2>/dev/null | grep -A1 "Name:" | tail -1)
|
||||
if [ -n "$a_record" ]; then
|
||||
echo " [OK] $a_record"
|
||||
else
|
||||
echo " [FAIL] No A record found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# MX record
|
||||
echo "MX Record ($check_domain):"
|
||||
local mx_record=$(nslookup -type=mx "$check_domain" 2>/dev/null | grep "mail exchanger")
|
||||
if [ -n "$mx_record" ]; then
|
||||
echo " [OK] $mx_record"
|
||||
else
|
||||
echo " [FAIL] No MX record found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# SPF record
|
||||
echo "SPF Record ($check_domain):"
|
||||
local spf=$(nslookup -type=txt "$check_domain" 2>/dev/null | grep "v=spf1")
|
||||
if [ -n "$spf" ]; then
|
||||
echo " [OK] $spf"
|
||||
else
|
||||
echo " [WARN] No SPF record found"
|
||||
echo " Recommended: \"v=spf1 mx -all\""
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# DMARC record
|
||||
echo "DMARC Record (_dmarc.$check_domain):"
|
||||
local dmarc=$(nslookup -type=txt "_dmarc.$check_domain" 2>/dev/null | grep "v=DMARC1")
|
||||
if [ -n "$dmarc" ]; then
|
||||
echo " [OK] $dmarc"
|
||||
else
|
||||
echo " [WARN] No DMARC record found"
|
||||
echo " Recommended: \"v=DMARC1; p=quarantine; rua=mailto:postmaster@$check_domain\""
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_ports() {
|
||||
load_config
|
||||
|
||||
echo "=== Port Status ==="
|
||||
echo ""
|
||||
echo "Required ports for mail server:"
|
||||
echo ""
|
||||
|
||||
local ports="25:SMTP 587:Submission 465:SMTPS 143:IMAP 993:IMAPS"
|
||||
[ "$enable_pop3" = "1" ] && ports="$ports 110:POP3 995:POP3S"
|
||||
|
||||
for entry in $ports; do
|
||||
local port=$(echo "$entry" | cut -d: -f1)
|
||||
local name=$(echo "$entry" | cut -d: -f2)
|
||||
|
||||
printf " %-6s %-12s " "$port" "$name"
|
||||
|
||||
if netstat -tln 2>/dev/null | grep -q ":$port "; then
|
||||
echo "[LISTENING]"
|
||||
else
|
||||
echo "[NOT LISTENING]"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Note: Port 25 may be blocked by some ISPs"
|
||||
}
|
||||
|
||||
cmd_config() {
|
||||
load_config
|
||||
|
||||
echo "=== Mail Server Configuration ==="
|
||||
echo ""
|
||||
echo "General:"
|
||||
echo " Enabled: $enabled"
|
||||
echo " Image: $image"
|
||||
echo " Hostname: $hostname"
|
||||
echo " Domain: $domain"
|
||||
echo " Data path: $data_path"
|
||||
echo " Timezone: $timezone"
|
||||
echo ""
|
||||
echo "Features:"
|
||||
echo " SpamAssassin: $enable_spamassassin"
|
||||
echo " ClamAV: $enable_clamav"
|
||||
echo " Fail2ban: $enable_fail2ban"
|
||||
echo " POP3: $enable_pop3"
|
||||
echo " SSL Type: $ssl_type"
|
||||
echo ""
|
||||
echo "Ports:"
|
||||
echo " SMTP: $smtp_port"
|
||||
echo " Submission: $submission_port"
|
||||
echo " IMAPS: $imaps_port"
|
||||
}
|
||||
|
||||
cmd_test_email() {
|
||||
local to="$1"
|
||||
[ -z "$to" ] && { log_error "Usage: mailinaboxctl test-email <to-address>"; return 1; }
|
||||
|
||||
load_config
|
||||
|
||||
if ! container_running; then
|
||||
log_error "Container not running"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Sending test email to $to..."
|
||||
docker exec "$CONTAINER_NAME" sh -c "echo 'Test email from SecuBox Mail Server' | mail -s 'Test from $hostname' $to"
|
||||
|
||||
log_info "Test email sent (check spam folder if not received)"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main Container Commands
|
||||
# =============================================================================
|
||||
|
||||
cmd_install() {
|
||||
require_root
|
||||
check_prereqs || exit 1
|
||||
pull_image || exit 1
|
||||
|
||||
uci_set enabled '1'
|
||||
uci commit ${CONFIG}
|
||||
/etc/init.d/mailinabox enable
|
||||
|
||||
load_config
|
||||
echo ""
|
||||
log_info "Mail server installed successfully!"
|
||||
echo ""
|
||||
echo "NEXT STEPS:"
|
||||
echo " 1. Edit /etc/config/mailinabox and configure:"
|
||||
echo " - hostname (e.g., mail.yourdomain.com)"
|
||||
echo " - domain (e.g., yourdomain.com)"
|
||||
echo " 2. Set up DNS records (see 'mailinaboxctl dns-check')"
|
||||
echo " 3. Start: /etc/init.d/mailinabox start"
|
||||
echo " 4. Add first user: mailinaboxctl user-add admin@$domain"
|
||||
echo ""
|
||||
}
|
||||
|
||||
cmd_check() {
|
||||
check_prereqs
|
||||
echo ""
|
||||
cmd_config
|
||||
echo ""
|
||||
cmd_ports
|
||||
}
|
||||
|
||||
cmd_update() {
|
||||
require_root
|
||||
pull_image || exit 1
|
||||
|
||||
if container_running; then
|
||||
/etc/init.d/mailinabox restart
|
||||
else
|
||||
log_info "Image updated. Start manually when ready."
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
echo "=== Container Status ==="
|
||||
docker ps -a --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
echo ""
|
||||
|
||||
if container_running; then
|
||||
echo "=== Service Status ==="
|
||||
docker exec "$CONTAINER_NAME" supervisorctl status 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_logs() {
|
||||
docker logs "$@" "$CONTAINER_NAME"
|
||||
}
|
||||
|
||||
cmd_shell() {
|
||||
if container_running; then
|
||||
docker exec -it "$CONTAINER_NAME" /bin/bash
|
||||
else
|
||||
log_error "Container not running"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_service_run() {
|
||||
require_root
|
||||
check_prereqs || exit 1
|
||||
load_config
|
||||
stop_container
|
||||
|
||||
log_info "Starting mail server container..."
|
||||
|
||||
# Build docker run command
|
||||
local docker_args="--name $CONTAINER_NAME"
|
||||
|
||||
# Hostname
|
||||
docker_args="$docker_args --hostname $hostname"
|
||||
docker_args="$docker_args --domainname $domain"
|
||||
|
||||
# Ports
|
||||
docker_args="$docker_args -p $smtp_port:25"
|
||||
docker_args="$docker_args -p $submission_port:587"
|
||||
docker_args="$docker_args -p $submissions_port:465"
|
||||
docker_args="$docker_args -p $imap_port:143"
|
||||
docker_args="$docker_args -p $imaps_port:993"
|
||||
|
||||
if [ "$enable_pop3" = "1" ]; then
|
||||
docker_args="$docker_args -p $pop3_port:110"
|
||||
docker_args="$docker_args -p $pop3s_port:995"
|
||||
fi
|
||||
|
||||
# Volumes
|
||||
docker_args="$docker_args -v $data_path/mail-data:/var/mail"
|
||||
docker_args="$docker_args -v $data_path/mail-state:/var/mail-state"
|
||||
docker_args="$docker_args -v $data_path/mail-logs:/var/log/mail"
|
||||
docker_args="$docker_args -v $data_path/config:/tmp/docker-mailserver"
|
||||
|
||||
# Let's Encrypt volume if using certbot
|
||||
if [ "$ssl_type" = "letsencrypt" ]; then
|
||||
ensure_dir "$data_path/letsencrypt"
|
||||
docker_args="$docker_args -v $data_path/letsencrypt:/etc/letsencrypt"
|
||||
fi
|
||||
|
||||
# Environment variables
|
||||
docker_args="$docker_args -e TZ=$timezone"
|
||||
docker_args="$docker_args -e OVERRIDE_HOSTNAME=$hostname"
|
||||
docker_args="$docker_args -e ENABLE_SPAMASSASSIN=$enable_spamassassin"
|
||||
docker_args="$docker_args -e ENABLE_CLAMAV=$enable_clamav"
|
||||
docker_args="$docker_args -e ENABLE_FAIL2BAN=$enable_fail2ban"
|
||||
docker_args="$docker_args -e ENABLE_POP3=$enable_pop3"
|
||||
docker_args="$docker_args -e SSL_TYPE=$ssl_type"
|
||||
docker_args="$docker_args -e PERMIT_DOCKER=network"
|
||||
docker_args="$docker_args -e ONE_DIR=1"
|
||||
docker_args="$docker_args -e POSTMASTER_ADDRESS=postmaster@$domain"
|
||||
|
||||
if [ -n "$letsencrypt_email" ]; then
|
||||
docker_args="$docker_args -e LETSENCRYPT_EMAIL=$letsencrypt_email"
|
||||
fi
|
||||
|
||||
# Capabilities
|
||||
docker_args="$docker_args --cap-add=NET_ADMIN"
|
||||
docker_args="$docker_args --cap-add=SYS_PTRACE"
|
||||
|
||||
# Restart policy
|
||||
docker_args="$docker_args --restart=unless-stopped"
|
||||
|
||||
exec docker run --rm $docker_args "$image"
|
||||
}
|
||||
|
||||
cmd_service_stop() {
|
||||
require_root
|
||||
stop_container
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main Entry Point
|
||||
# =============================================================================
|
||||
|
||||
case "${1:-}" in
|
||||
# Container management
|
||||
install) shift; cmd_install "$@" ;;
|
||||
check) shift; cmd_check "$@" ;;
|
||||
update) shift; cmd_update "$@" ;;
|
||||
status) shift; cmd_status "$@" ;;
|
||||
logs) shift; cmd_logs "$@" ;;
|
||||
shell) shift; cmd_shell "$@" ;;
|
||||
service-run) shift; cmd_service_run "$@" ;;
|
||||
service-stop) shift; cmd_service_stop "$@" ;;
|
||||
|
||||
# User management
|
||||
user-add) shift; cmd_user_add "$@" ;;
|
||||
user-del) shift; cmd_user_del "$@" ;;
|
||||
user-list) shift; cmd_user_list "$@" ;;
|
||||
user-passwd) shift; cmd_user_passwd "$@" ;;
|
||||
alias-add) shift; cmd_alias_add "$@" ;;
|
||||
alias-del) shift; cmd_alias_del "$@" ;;
|
||||
alias-list) shift; cmd_alias_list "$@" ;;
|
||||
|
||||
# Domain & SSL
|
||||
domain-add) shift; cmd_domain_add "$@" ;;
|
||||
domain-list) shift; cmd_domain_list "$@" ;;
|
||||
ssl-status) shift; cmd_ssl_status "$@" ;;
|
||||
ssl-renew) shift; cmd_ssl_renew "$@" ;;
|
||||
|
||||
# Backup & restore
|
||||
backup) shift; cmd_backup "$@" ;;
|
||||
restore) shift; cmd_restore "$@" ;;
|
||||
|
||||
# Diagnostics
|
||||
health) shift; cmd_health "$@" ;;
|
||||
dns-check) shift; cmd_dns_check "$@" ;;
|
||||
ports) shift; cmd_ports "$@" ;;
|
||||
config) shift; cmd_config "$@" ;;
|
||||
test-email) shift; cmd_test_email "$@" ;;
|
||||
|
||||
help|--help|-h|'') usage ;;
|
||||
*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;;
|
||||
esac
|
||||
Loading…
Reference in New Issue
Block a user