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