#!/bin/bash # A non-login root shell (for example, `su` without `-`) may omit sbin. # Include administrative commands such as ldconfig and phpenmod for all steps. export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin${PATH:+:${PATH}}" echo echo echo echo "===================BY WWW.MAGNUSBILLING.ORG========================="; echo "_ _ ______ _ _ _ _ "; echo "|\ /| | ___ (_) | (_) "; echo "| \ / | ___ ____ _ __ _ _ _____ | |_/ /_| | |_ _ __ ____ "; echo "| \/ |/ \/ _ \| '_ \| | | \| ___| | ___ \ | | | | '_ \ / _ \ "; echo "| |\/| | | | (_| | | | | |_| ||____ | |_/ / | | | | | | | (_| | "; echo "|_| |_|\___|\___ |_| | |_____|_____| \___/|_|_|_|_|_| |_|\___ | "; echo " _/ | _/ | "; echo " |__/ |__/ "; echo " "; echo "======================= VOIP SYSTEM FOR LINUX ======================="; echo sleep 3 if [[ ${EUID} -ne 0 ]]; then echo "Run this installer as root." exit 1 fi if [[ -f /var/www/html/mbilling/index.php ]]; then echo "This server already has MagnusBilling installed"; exit; fi get_linux_distribution () { if [ ! -r /etc/os-release ]; then echo "Unable to identify the Linux distribution: /etc/os-release not found." exit 1 fi # shellcheck disable=SC1091 . /etc/os-release case "${ID:-}" in debian) DEBIAN_MAJOR="${VERSION_ID%%.*}" case "${DEBIAN_MAJOR}" in 11|12) ODBC_RUNTIME_PACKAGE="libodbc1" ;; 13) ODBC_RUNTIME_PACKAGE="libodbc2" ;; *) echo "Unsupported Debian version: ${VERSION_ID:-unknown}." echo "Supported Debian versions: 11, 12 and 13." exit 1 ;; esac DIST="DEBIAN" ;; ubuntu) DIST="UBUNTU" case "${VERSION_ID:-}" in 22.04) ODBC_RUNTIME_PACKAGE="libodbc1" ;; 24.04|26.04) ODBC_RUNTIME_PACKAGE="libodbc2" ;; *) echo "Unsupported Ubuntu version: ${VERSION_ID:-unknown}." echo "Supported Ubuntu LTS versions: 22.04, 24.04 and 26.04." exit 1 ;; esac ;; *) echo "Installation does not support distribution: ${ID:-unknown}." exit 1 ;; esac HTTP_DIR="/etc/apache2/" HTTP_CONFIG="${HTTP_DIR}apache2.conf" MYSQL_CONFIG="/etc/mysql/mariadb.conf.d/60-magnusbilling.cnf" APACHE_USER="www-data" echo "Detected ${PRETTY_NAME:-${ID}}." echo "Using ODBC runtime package: ${ODBC_RUNTIME_PACKAGE}." } get_linux_distribution startup_services() { # Startup Services if [ "${DIST}" = "DEBIAN" ] || [ "${DIST}" = "UBUNTU" ]; then for service in mariadb apache2 asterisk; do if ! systemctl restart "${service}"; then echo "Unable to restart required service: ${service}." exit 1 fi done fi } genpasswd() { length=$1 [ "$length" == "" ] && length=16 tr -dc A-Za-z0-9_ < /dev/urandom | head -c ${length} | xargs } apt_install() { if ! DEBIAN_FRONTEND=noninteractive apt-get install -y "$@"; then echo "Unable to install required packages: $*" exit 1 fi } configure_pjsip_dns() { local resolv_file="/etc/resolv.conf" local resolv_backup local nameserver local dns_failures=0 if [ ! -r "${resolv_file}" ]; then echo "WARNING: ${resolv_file} is not readable. PJSIP DNS checks were skipped." return fi echo "Checking configured DNS servers for PJSIP SRV response..." while read -r nameserver; do [ -n "${nameserver}" ] || continue # NXDOMAIN is a valid and fast DNS response. dig returns success when # the server answers, regardless of whether this test name exists. if dig +time=1 +tries=1 +short \ "@${nameserver}" SRV _sip._udp.magnusbilling-dns-check.invalid \ > /dev/null 2>&1; then echo "DNS server ${nameserver}: responding" else echo "WARNING: DNS server ${nameserver} did not answer within 1 second." dns_failures=$((dns_failures + 1)) fi done < <(awk '/^[[:space:]]*nameserver[[:space:]]+/ { print $2 }' "${resolv_file}") if [ "${dns_failures}" -gt 0 ]; then echo "WARNING: ${dns_failures} configured DNS server(s) may delay PJSIP reloads." echo "Remove or repair non-responsive resolvers before using DNS-based trunks." fi configure_pjsip_dns_cache } configure_pjsip_dns_cache() { local resolv_file="/etc/resolv.conf" local cache_dir="/etc/magnusbilling" local cache_config="${cache_dir}/dns-cache.conf" local cache_service="magnusbilling-dns-cache.service" local resolv_dir="/etc/resolvconf/resolv.conf.d" local backup_dir local nameserver local upstream_count=0 local use_resolvconf=0 # A resolver already listening on port 53 may have its own cache and # configuration owner. Never replace it or bind over it automatically. if ! systemctl is-active --quiet "${cache_service}" && [ -n "$(ss -H -lntu '( sport = :53 )')" ]; then echo "Existing DNS service detected. Keeping its resolver configuration." return fi if command -v resolvconf > /dev/null 2>&1 && [ -d "${resolv_dir}" ]; then use_resolvconf=1 elif [ -L "${resolv_file}" ] || systemctl is-active --quiet NetworkManager || grep -Eqi 'generated|do not edit' "${resolv_file}"; then echo "WARNING: DNS configuration is managed externally; local cache setup skipped." return fi # Preserve the site's recursive DNS servers, and exclude loopback to # prevent forwarding queries back into this cache. Keep the config on reruns. if [ ! -f "${cache_config}" ]; then mkdir -p "${cache_dir}" || return 1 { echo 'listen-address=127.0.0.1' echo 'bind-interfaces' echo 'no-resolv' echo 'cache-size=4096' } > "${cache_config}" || return 1 while read -r nameserver; do case "${nameserver}" in 127.*|::1|0.0.0.0|::|'') continue ;; esac if dig +time=2 +tries=1 +short "@${nameserver}" A debian.org | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then echo "server=${nameserver}" >> "${cache_config}" upstream_count=$((upstream_count + 1)) fi done < <(awk '/^[[:space:]]*nameserver[[:space:]]+/ { print $2 }' "${resolv_file}") if [ "${upstream_count}" -eq 0 ]; then rm -f "${cache_config}" echo "WARNING: No working external DNS server found; local cache setup skipped." return fi fi # dnsmasq-base supplies the binary without starting the distribution's # general-purpose DNS/DHCP service. Use an isolated, DNS-only unit instead. apt_install dnsmasq-base if ! /usr/sbin/dnsmasq --test --conf-file="${cache_config}"; then echo "WARNING: Local DNS cache configuration is invalid; resolver unchanged." return fi cat > "/etc/systemd/system/${cache_service}" <<'EOF' [Unit] Description=MagnusBilling local DNS cache Wants=network-online.target After=network-online.target Before=asterisk.service [Service] ExecStart=/usr/sbin/dnsmasq --keep-in-foreground --conf-file=/etc/magnusbilling/dns-cache.conf Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl daemon-reload if ! systemctl enable --now "${cache_service}"; then echo "WARNING: Local DNS cache failed to start; resolver unchanged." return fi if ! dig +time=2 +tries=1 +short @127.0.0.1 A debian.org | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then systemctl disable --now "${cache_service}" echo "WARNING: Local DNS cache did not resolve the test host; resolver unchanged." return fi backup_dir=$(mktemp -d /etc/magnusbilling/dns-backup.XXXXXX) || return 1 cp -L -- "${resolv_file}" "${backup_dir}/resolv.conf" || return 1 if [ "${use_resolvconf}" -eq 1 ]; then cp -a -- "${resolv_dir}" "${backup_dir}/resolv.conf.d" || return 1 touch "${resolv_dir}/head" "${resolv_dir}/tail" # head makes localhost the first DNS; tail overrides dynamic search # suffixes. Remove only our exact entries so repeated setup is idempotent. sed -i '/^[[:space:]]*nameserver[[:space:]]*127\.0\.0\.1[[:space:]]*$/d' "${resolv_dir}/head" { echo 'nameserver 127.0.0.1'; cat "${resolv_dir}/head"; } > "${backup_dir}/head.new" cat "${backup_dir}/head.new" > "${resolv_dir}/head" sed -i '/^[[:space:]]*search[[:space:]]*\.[[:space:]]*$/d' "${resolv_dir}/tail" echo 'search .' >> "${resolv_dir}/tail" resolvconf -u else sed -E -i '/^[[:space:]]*(nameserver|search|domain)[[:space:]]/d' "${resolv_file}" echo 'nameserver 127.0.0.1' >> "${resolv_file}" echo 'search .' >> "${resolv_file}" fi # Check the generated file as well: a broken resolvconf symlink must not # result in reporting success while Asterisk still uses external DNS. if [ "$(awk '/^[[:space:]]*nameserver[[:space:]]+/ { print $2; exit }' "${resolv_file}")" != '127.0.0.1' ] || [ "$(awk '/^[[:space:]]*(search|domain)[[:space:]]+/ { value=$0 } END { print value }' "${resolv_file}")" != 'search .' ] || ! dig +time=2 +tries=1 +short A debian.org | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then if [ "${use_resolvconf}" -eq 1 ]; then cp -a -- "${backup_dir}/resolv.conf.d/." "${resolv_dir}/" # These files may have been created by touch above. [ -f "${backup_dir}/resolv.conf.d/head" ] || rm -f "${resolv_dir}/head" [ -f "${backup_dir}/resolv.conf.d/tail" ] || rm -f "${resolv_dir}/tail" resolvconf -u fi cat "${backup_dir}/resolv.conf" > "${resolv_file}" echo "WARNING: Resolver validation failed; previous DNS configuration restored." return fi echo "Local DNS cache configured for PJSIP. Resolver backup: ${backup_dir}" } if ! apt-get update --allow-releaseinfo-change; then echo "Unable to update the APT package lists." exit 1 fi apt_install locales sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen || echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen locale-gen echo 'LANG=en_US.UTF-8' > /etc/default/locale echo 'LC_ALL=en_US.UTF-8' >> /etc/default/locale update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 if [ "${DIST}" = "UBUNTU" ]; then apt_install software-properties-common if ! add-apt-repository -y universe; then echo "Unable to enable the Ubuntu universe repository." exit 1 fi fi if ! apt-get -o Acquire::Check-Valid-Until=false update; then echo "Unable to refresh the APT package lists." exit 1 fi apt_install apache2 dnsutils apt_install autoconf automake devscripts gawk ntpsec g++ curl wget ca-certificates sudo xmlstarlet libjansson-dev git "${ODBC_RUNTIME_PACKAGE}" odbcinst unixodbc unixodbc-dev patchelf apt_install php-fpm php php-dev php-common php-cli php-gd php-pear php-sqlite3 php-curl php-mbstring php-xml php-mysql libapache2-mod-php apt_install unzip uuid-dev libxml2-dev openssl libcurl4-openssl-dev gettext gcc sqlite3 libsqlite3-dev subversion mpg123 apt_install libncurses-dev mariadb-server htop sngrep firewalld fail2ban cron rsyslog whiptail libblocksruntime-dev iproute2 iptables ngrep configure_pjsip_dns mkdir -p /var/www/html/mbilling cd /var/www/html/mbilling if ! wget -O MagnusBilling8-current.tar.gz https://magnusbilling.org/download/MagnusBilling8-current.tar.gz; then echo "Unable to download MagnusBilling." exit 1 fi if ! tar xzf MagnusBilling8-current.tar.gz; then echo "Unable to extract MagnusBilling." exit 1 fi echo echo '----------- Install Asterisk 20 ----------' echo sleep 1 cd /usr/src rm -rf asterisk* clear wget https://raw.githubusercontent.com/magnussolution/magnusbilling/source/script/asterisk-20.9.2.tar.gz tar xzvf asterisk-20.9.2.tar.gz rm -rf asterisk-20.9.2.tar.gz cd asterisk-* useradd -r -d /var/lib/asterisk -s /usr/sbin/nologin -c 'Asterisk PBX' asterisk mkdir /var/run/asterisk mkdir /var/log/asterisk chown -R asterisk:asterisk /var/run/asterisk chown -R asterisk:asterisk /var/log/asterisk make clean contrib/scripts/install_prereq install ./configure --with-jansson-bundled --with-pjproject-bundled make menuselect.makeopts menuselect/menuselect --enable res_config_mysql menuselect.makeopts menuselect/menuselect --enable codec_opus menuselect.makeopts make make install make samples make config ldconfig echo ' noload => chan_sip.so ' >> /etc/asterisk/modules.conf echo ' AddType application/octet-stream .csv AllowOverride All DirectoryIndex index.htm index.html index.php index.php3 default.html index.cgi deny from all deny from all deny from all deny from all deny from all deny from all ' >> "${HTTP_CONFIG}" PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION, ".", PHP_MINOR_VERSION;') PHP_TIMEZONE=$(timedatectl show --property=Timezone --value 2>/dev/null) [ -n "${PHP_TIMEZONE}" ] || PHP_TIMEZONE="UTC" PHP_MAGNUS_INI="/etc/php/${PHP_VERSION}/mods-available/magnusbilling.ini" cat > "${PHP_MAGNUS_INI}" <//' "${HTTP_CONFIG}" systemctl enable apache2 systemctl enable --now ntpsec echo echo "----------- Starting MariaDB with unix_socket root authentication ----------" echo systemctl start mariadb systemctl enable --now mariadb cat > "${MYSQL_CONFIG}" <<'EOF' [mysqld] max_connections = 500 key_buffer_size = 64M max_allowed_packet = 64M thread_stack = 1M thread_cache_size = 8 query_cache_limit = 8M query_cache_size = 64M expire_logs_days = 10 max_binlog_size = 1G secure_file_priv = /var/lib/mysql-files symbolic_links = 0 sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES tmp_table_size = 128M open_files_limit = 500000 EOF install -d -o root -g root -m 0755 /var/lib/mysql-files startup_services echo echo '----------- Installing the Web Interface ----------' echo sleep 2 rm -rf /var/www/html/index.html cd /var/www/html/mbilling/resources/images/ rm -rf lock-screen-background.jpg wget https://magnusbilling.org/download/lock-screen-background.jpg cd /var/www/html/mbilling/ rm -rf /var/www/html/mbilling/tmp && mkdir /var/www/html/mbilling/tmp mkdir -p /var/www/html/mbilling/assets mkdir -p /var/run/magnus mkdir -p /usr/local/src/magnus touch /etc/asterisk/extensions_magnus.conf touch /etc/asterisk/extensions_magnus_did.conf touch /etc/asterisk/pjsip_magnus.conf touch /etc/asterisk/pjsip_magnus_user.conf touch /etc/asterisk/musiconhold_magnus.conf touch /etc/asterisk/queues_magnus.conf touch /etc/asterisk/voicemail_magnus.conf touch /etc/asterisk/mbilling.conf selectLanguage() { echo "SELECT THE MAIN LANGUAGE" echo "------------------------------------------" echo "Options:" echo echo "1. Portuguese" echo "2. English" echo "3. Spanish" echo echo -n "Select one option: " read opcao case $opcao in 1) installBr;; 2) installEn;; 3) installEs;; esac } cp -rf /var/www/html/mbilling/resources/sounds/br /var/lib/asterisk/sounds cp -rf /var/www/html/mbilling/resources/sounds/es /var/lib/asterisk/sounds cp -rf /var/www/html/mbilling/resources/sounds/en /var/lib/asterisk/sounds installBr() { language='br' cp -rf /var/www/html/mbilling/script/br /var/lib/asterisk/ cd /var/lib/asterisk wget https://raw.githubusercontent.com/magnussolution/magnusbilling7/source/script/sounds/Sounds-br.tar.gz tar xzvf Sounds-br.tar.gz } installEn() { language='en' } installEs() { language='en' cp -n /var/www/html/mbilling/resources/sounds/en/* /var/lib/asterisk/sounds mkdir /var/lib/asterisk/es cd /var/lib/asterisk/es wget https://raw.githubusercontent.com/magnussolution/magnusbilling7/source/script/sounds/Sounds-es.tar.gz tar xzvf Sounds-es.tar.gz } if [[ $1 == '' ]]; then selectLanguage elif [[ $1 == 'en' ]]; then installEn elif [[ $1 == 'br' ]]; then installBr elif [[ $1 == 'es' ]]; then installEs else selectLanguage fi cd /var/www/html/mbilling echo $'[billing] exten => _[*0-9].,1,AGI("/var/www/html/mbilling/resources/asterisk/mbilling.php") same => n,Hangup() exten => _+X.,1,Goto(billing,${EXTEN:1},1) exten => h,1,hangup() exten => *111,1,VoiceMailMain(${CHANNEL(peername)}@billing) same => n,Hangup() [trunk_answer_handler] exten => s,1,Set(MASTER_CHANNEL(TRUNKANSWERTIME)=${EPOCH}) same => n,Return() ' > /etc/asterisk/extensions_magnus.conf echo " [general] enabled = yes port = 5038 bindaddr = 0.0.0.0 displayconnects = no [magnus] secret = magnussolution deny=0.0.0.0/0.0.0.0 permit=127.0.0.1/255.255.255.0 read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan write = system,call,agent,user,config,command,reporting,originate " > /etc/asterisk/manager.conf echo "#include extensions_magnus.conf" >> /etc/asterisk/extensions.conf echo '#include extensions_magnus_did.conf' >> /etc/asterisk/extensions.conf echo "#include musiconhold_magnus.conf" >> /etc/asterisk/musiconhold.conf echo "#include voicemail_magnus.conf" >> /etc/asterisk/voicemail.conf echo " noload => res_config_sqlite3.so noload => res_config_sqlite.so noload => chan_skinny.so noload => cdr_custom.so noload => cdr_odbc.so noload => cdr_sqlite3_custom.so noload => cdr_csv.so noload => cdr_manager.so noload => chan_iax2.so noload => cdr_mysql.so noload => app_celgenuserevent.so noload => cel_custom.so noload => cel_manager.so noload => cel_odbc.so noload => cel_sqlite3_custom.so noload => res_format_attr_celt.so noload => res_pjsip_endpoint_identifier_anonymous.so " >> /etc/asterisk/modules.conf echo " /var/log/asterisk/*log { missingok rotate 3 weekly postrotate /usr/sbin/asterisk -rx 'logger reload' > /dev/null 2> /dev/null endscript } /var/log/asterisk/messages { missingok rotate 3 weekly postrotate /usr/sbin/asterisk -rx 'logger reload' > /dev/null 2> /dev/null endscript } /var/log/asterisk/magnus { missingok rotate 3 daily postrotate /usr/sbin/asterisk -rx 'logger reload' > /dev/null 2> /dev/null endscript } /var/log/asterisk/fail2ban { missingok rotate 3 weekly postrotate /usr/sbin/asterisk -rx 'logger reload' > /dev/null 2> /dev/null endscript } " > /etc/logrotate.d/asterisk echo echo "----------- Installing the new Database ----------" echo sleep 2 MBillingMysqlPass=$(genpasswd) DATABASE_FILE=/var/www/html/mbilling/script/database.sql DB_CONFIG_FILE=/etc/asterisk/res_config_mysql.conf if [[ ! -f ${DATABASE_FILE} ]]; then echo "Database schema not found: ${DATABASE_FILE}" exit 1 fi # Debian and Ubuntu MariaDB packages authenticate the OS root user through the # local Unix socket. Keep that authentication method and create a separate # password-based account for MagnusBilling. if ! mariadb --protocol=socket < /dev/null; then echo "Unable to connect to MariaDB using the MagnusBilling application account." exit 1 fi if ! install -o root -g asterisk -m 0640 /dev/null "${DB_CONFIG_FILE}"; then echo "Unable to create the MagnusBilling database configuration." exit 1 fi if ! printf '[general]\ndbhost = 127.0.0.1\ndbname = mbilling\ndbuser = mbillingUser\ndbpass = %s\n' \ "${MBillingMysqlPass}" > "${DB_CONFIG_FILE}"; then echo "Unable to write the MagnusBilling database configuration." exit 1 fi rm -rf /var/www/html/mbilling/script echo '[directories](!) astetcdir => /etc/asterisk astmoddir => /usr/lib/asterisk/modules astvarlibdir => /var/lib/asterisk astdbdir => /var/lib/asterisk astkeydir => /var/lib/asterisk astdatadir => /var/lib/asterisk astagidir => /var/lib/asterisk/agi-bin astspooldir => /var/spool/asterisk astrundir => /var/run/asterisk astlogdir => /var/log/asterisk runuser = asterisk rungroup = asterisk ' > /etc/asterisk/asterisk.conf echo " [options] documentation_language = en_US verbose = 5 debug = 0 maxfiles = 500000 hideconnect = 1 [compat] pbx_realtime=1.6 res_agi=1.6 app_set=1.6" >> /etc/asterisk/asterisk.conf echo 500000 > /proc/sys/fs/file-max echo "fs.file-max=500000">>/etc/sysctl.conf ulimit -c unlimited # The maximum size of core files created. ulimit -d unlimited # The maximum size of a process's data segment. ulimit -f unlimited # The maximum size of files created by the shell (default option) ulimit -i unlimited # The maximum number of pending signals ulimit -n 99999 # The maximum number of open file descriptors. ulimit -q unlimited # The maximum POSIX message queue size ulimit -u unlimited # The maximum number of processes available to a single user. ulimit -v unlimited # The maximum amount of virtual memory available to the process. ulimit -x unlimited # ??? ulimit -s 240 # The maximum stack size ulimit -l unlimited # The maximum size that may be locked into memory. ulimit -a # All current limits are reported. echo ' * soft nofile 500000 * hard nofile 500000 * soft core unlimited * hard core unlimited * soft data unlimited * hard data unlimited * soft fsize unlimited * hard fsize unlimited * soft memlock unlimited * hard memlock unlimited * soft cpu unlimited * hard cpu unlimited * soft nproc unlimited * hard nproc unlimited * soft locks unlimited * hard locks unlimited * soft sigpending unlimited * hard sigpending unlimited' >> /etc/security/limits.conf CRONPATH='/var/spool/cron/crontabs/root' echo " * * * * * php /var/www/html/mbilling/cron.php massivecall 8 8 * * * php /var/www/html/mbilling/cron.php servicescheck * * * * * php /var/www/html/mbilling/cron.php callchart 1 * * * * php /var/www/html/mbilling/cron.php NotifyClient 1 22 * * * php /var/www/html/mbilling/cron.php DidCheck 1 23 * * * php /var/www/html/mbilling/cron.php PlanCheck 0 2 * * * php /var/www/html/mbilling/cron.php Backup 0 4 * * * /var/www/html/mbilling/protected/commands/clear_memory 30 1 * * * /var/www/html/mbilling/protected/commands/update.sh */2 * * * * flock -n /tmp/SummaryTablesCdr.lock php /var/www/html/mbilling/cron.php SummaryTablesCdr */3 * * * * php /var/www/html/mbilling/cron.php PhoneBooksReprocess * * * * * php /var/www/html/mbilling/cron.php statussystem * * * * * php /var/www/html/mbilling/cron.php didwww */5 * * * * php /var/www/html/mbilling/cron.php alarm * * * * * php /var/www/html/mbilling/cron.php TrunkSIPCodes 59 23 * * * php /var/www/html/mbilling/cron.php NotifyClientDaily " > $CRONPATH chmod 600 $CRONPATH echo " * * * * * root php /var/www/html/mbilling/cron.php cryptocurrency */2 * * * * root flock -n /tmp/failtwobanip.lock php /var/www/html/mbilling/cron.php failtwobanip ">> /etc/crontab echo " [general] bindaddr = 0.0.0.0 [transport-udp] type = transport protocol = udp bind = 0.0.0.0:5060 #include pjsip_magnus.conf #include pjsip_magnus_user.conf " > /etc/asterisk/pjsip.conf echo " #include queues_magnus.conf " >> /etc/asterisk/queues.conf echo " " > /var/www/html/index.php echo " User-agent: * Disallow: /mbilling/ " > /var/www/html/robots.txt systemctl daemon-reload systemctl enable --now cron install_fail2ban() { apt_install fail2ban } echo echo "Installing Fail2ban & Iptables" echo ssh_port=$( awk ' /^[[:space:]]*Port[[:space:]]+[0-9]+/ && $1 !~ /^#/ { port=$2 } END { print port ? port : "22" } ' /etc/ssh/sshd_config ) WAN_IF=$(ip -4 route show default | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1); exit}}') if [ -z "$WAN_IF" ] || ! ip link show "$WAN_IF" >/dev/null 2>&1; then echo echo "WARNING: Unable to detect the WAN interface from the IPv4 default route." echo "Firewalld installation will continue using the default zone: public." echo "No interface will be explicitly assigned to the public zone." echo echo "To fix this later, identify the public interface with:" echo " ip -br addr" echo echo "Then assign it manually, for example:" echo " firewall-cmd --permanent --zone=public --change-interface=eth0" echo " firewall-cmd --reload" echo WAN_IF="" else echo "Public interface detected: $WAN_IF" fi apt_install firewalld install_fail2ban systemctl enable fail2ban systemctl disable --now iptables 2>/dev/null || true systemctl disable --now netfilter-persistent 2>/dev/null || true if [ "${DIST}" = "UBUNTU" ]; then systemctl disable --now ufw 2>/dev/null || true fi systemctl enable --now firewalld firewall-cmd --set-default-zone=public firewall-cmd --zone=public --add-port=$ssh_port/tcp --permanent firewall-cmd --zone=public --add-port=22/tcp --permanent firewall-cmd --zone=public --add-port=80/tcp --permanent firewall-cmd --zone=public --add-port=443/tcp --permanent firewall-cmd --zone=public --add-port=5060/udp --permanent firewall-cmd --zone=public --add-port=10000-20000/udp --permanent if [ -n "$WAN_IF" ]; then firewall-cmd --permanent --zone=public --change-interface="$WAN_IF" fi firewall-cmd --reload firewall-cmd --state firewall-cmd --get-active-zones if [ -n "$WAN_IF" ]; then firewall-cmd --get-zone-of-interface="$WAN_IF" fi firewall-cmd --zone=public --list-all touch /var/www/html/mbilling/protected/runtime/application.log echo echo "Fail2ban configuration!" echo echo ' [INCLUDES] [Definition] failregex = NOTICE.* .*: Useragent: sipcli.*\[\] ignoreregex = ' > /etc/fail2ban/filter.d/asterisk_cli.conf echo ' [INCLUDES] [Definition] failregex = .*NOTICE.* tried to authenticate with nonexistent user.* ignoreregex = ' > /etc/fail2ban/filter.d/asterisk_manager.conf echo ' [INCLUDES] [Definition] failregex = NOTICE.* .*hangupcause to DB: 200, \[\] ignoreregex = ' > /etc/fail2ban/filter.d/asterisk_hgc_200.conf echo ' [INCLUDES] [Definition] failregex = .*client \].*request failed: URI too long.* .*client \].*request failed: error reading the headers ignoreregex = ' > /etc/fail2ban/filter.d/mbilling_ddos.conf echo ' [INCLUDES] [Definition] failregex = .*Username and password combination is invalid - User.*IP: ignoreregex = ' > /etc/fail2ban/filter.d/mbilling_login.conf echo " [DEFAULT] ignoreip = 127.0.0.1 bantime = 600 findtime = 600 maxretry = 3 bantime.increment = true bantime.factor = 2 bantime.maxtime = 30d backend = auto usedns = warn banaction = firewallcmd-allports banaction_allports = firewallcmd-allports [asterisk-iptables] enabled = true filter = asterisk logpath = /var/log/asterisk/messages maxretry = 5 bantime = 600 port = 5060,5061 protocol = udp [ast-cli-attck] enabled = true filter = asterisk_cli logpath = /var/log/asterisk/messages maxretry = 1 bantime = -1 [asterisk-manager] enabled = true filter = asterisk_manager logpath = /var/log/asterisk/messages maxretry = 1 bantime = -1 [ast-hgc-200] enabled = true filter = asterisk_hgc_200 logpath = /var/log/asterisk/messages maxretry = 20 bantime = -1 [mbilling_login] enabled = true filter = mbilling_login logpath = /var/www/html/mbilling/protected/runtime/application.log maxretry = 3 bantime = 300 [ip-blacklist] enabled = true maxretry = 0 findtime = 15552000 bantime = -1 [sshd] enabled=true [mbilling_ddos] enabled = true filter = mbilling_ddos logpath = /var/log/apache2/error.log maxretry = 20 bantime = 3600" > /etc/fail2ban/jail.local rm -rf /var/www/html/mbilling/resources/ip.blacklist touch /var/www/html/mbilling/resources/ip.blacklist echo " [Definition] failregex = ^ \[.*\]$ ignoreregex = " > /etc/fail2ban/filter.d/ip-blacklist.conf echo " [general] dateformat=%F %T [logfiles] console => error messages => notice,warning,error magnus => debug " > /etc/asterisk/logger.conf touch /var/log/auth.log install -d -m 0755 /var/run/fail2ban asterisk -rx "module reload logger" systemctl enable fail2ban.service systemctl restart fail2ban.service iptables -L -v php /var/www/html/mbilling/cron.php updatemysql for d in assets tmp protected/runtime resources/reports resources/images; do cat > "/var/www/html/mbilling/$d/.htaccess" <<'EOF' Require all denied # Se estiver usando mod_php, isto ajuda extra: php_flag engine off php_flag engine off EOF done chmod +x /var/www/html/mbilling/protected/commands/*.sh # SSH-managed panel IP allowlists live outside the document root and database. install -d -o root -g "$APACHE_USER" -m 0750 /etc/magnusbilling/panel-ip-access for command_name in addmyip delmyip releaseAll; do install -o root -g root -m 0755 \ /var/www/html/mbilling/protected/commands/panelIpAccessCommand.sh \ "/usr/local/sbin/$command_name" done mkdir -p /usr/local/src/magnus/monitor mkdir -p /usr/local/src/magnus/sounds mkdir -p /usr/local/src/magnus/backup mv /usr/local/src/backup* /usr/local/src/magnus/backup usermod -aG asterisk $APACHE_USER systemctl restart apache2 #permissions find /etc/asterisk -name "*magnus*" -exec chown asterisk:asterisk {} \; find /etc/asterisk -name "*magnus*" -exec chmod 660 {} \; find /etc/asterisk -name "*mbilling*" -exec chown asterisk:asterisk {} \; find /etc/asterisk -name "*mbilling*" -exec chmod 660 {} \; mkdir -p /var/spool/asterisk/outgoing/.magnusbilling-tmp chown root:asterisk /var/spool/asterisk/outgoing chmod 730 /var/spool/asterisk/outgoing chown $APACHE_USER:asterisk /var/spool/asterisk/outgoing/.magnusbilling-tmp chmod 770 /var/spool/asterisk/outgoing/.magnusbilling-tmp chown -R root:$APACHE_USER /usr/local/src/magnus chmod -R 730 /usr/local/src/magnus chown -R root:asterisk /var/lib/asterisk/moh chmod -R 730 /var/lib/asterisk/moh chown root:asterisk /etc/asterisk/res_config_mysql.conf chmod 0640 /etc/asterisk/res_config_mysql.conf chown -R root:root /var/www/html/mbilling find /var/www/html/mbilling -type d -exec chmod 755 {} \; find /var/www/html/mbilling -type f -exec chmod 644 {} \; for d in protected/runtime assets tmp resources/reports resources/images; do chown -R $APACHE_USER:$APACHE_USER "/var/www/html/mbilling/$d" find "/var/www/html/mbilling/$d" -type d -exec chmod 750 {} \; find "/var/www/html/mbilling/$d" -type f -exec chmod 640 {} \; done chown -R asterisk:asterisk /var/www/html/mbilling/resources/asterisk chmod +x /var/www/html/mbilling/resources/asterisk/mbilling.php chmod 500 /var/www/html/mbilling/resources/asterisk chmod 500 /var/www/html/mbilling/resources/asterisk/mbilling.php chown -R asterisk:asterisk /var/lib/asterisk chown -R asterisk:asterisk /var/log/asterisk chown -R asterisk:asterisk /var/spool/asterisk chown -R asterisk:asterisk /var/run/asterisk # end permissions echo ' [Unit] Description=Asterisk PBX (MagnusBilling) Documentation=man:asterisk(8) After=network.target [Service] Type=simple User=asterisk Group=asterisk Environment=AST_USER=asterisk Environment=AST_GROUP=asterisk Environment=HOME=/var/lib/asterisk WorkingDirectory=/var/lib/asterisk ExecStart=/usr/sbin/asterisk -f -U asterisk -G asterisk -C /etc/asterisk/asterisk.conf ExecStop=/usr/sbin/asterisk -rx "core stop now" ExecReload=/usr/sbin/asterisk -rx "core reload" Restart=always RestartSec=4 LimitNOFILE=500000 LimitNPROC=500000 LimitCORE=infinity NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=yes RuntimeDirectory=asterisk RuntimeDirectoryMode=0750 StandardOutput=null StandardError=journal ReadWritePaths=/var/lib/asterisk /var/spool/asterisk /var/log/asterisk SyslogIdentifier=asterisk LogLevelMax=notice SyslogLevel=err [Install] WantedBy=multi-user.target ' > /etc/systemd/system/asterisk.service systemctl disable asterisk 2>/dev/null systemctl daemon-reload systemctl enable asterisk systemctl restart asterisk if command -v journalctl >/dev/null 2>&1; then install -d -m 0755 /etc/systemd/journald.conf.d printf '%s\n' \ '[Journal]' \ 'SystemMaxUse=200M' \ 'RuntimeMaxUse=200M' \ 'SystemMaxFileSize=20M' \ > /etc/systemd/journald.conf.d/magnusbilling.conf systemctl restart systemd-journald journalctl --rotate journalctl --vacuum-size=200M fi echo echo echo =============================================================== echo chmod +x /var/www/html/mbilling/protected/commands/*.sh /var/www/html/mbilling/protected/commands/update.sh p4_proc() { set $(grep "model name" /proc/cpuinfo); if [ "$4" == "Celeron" ]; then wget https://www.magnusbilling.org/download/codecs/codec_g729-ast200-gcc4-glibc-pentium.so cp /usr/src/codec_g729-ast200-gcc4-glibc-pentium.so /usr/lib/asterisk/modules/codec_g729.so return 0; fi wget https://www.magnusbilling.org/download/codecs/codec_g729-ast200-gcc4-glibc-pentium4.so mv codec_g729-ast200-gcc4-glibc-pentium4.so /usr/lib/asterisk/modules/codec_g729.so } p4_x64_proc() { wget https://www.magnusbilling.org/download/codecs/codec_g729-ast200-gcc4-glibc-x86_64-pentium4.so mv /usr/src/codec_g729-ast200-gcc4-glibc-x86_64-pentium4.so /usr/lib/asterisk/modules/codec_g729.so } p3_proc() { set $(grep "model name" /proc/cpuinfo); if [ "$4" == "Intel(R)" && "$5" == "Pentium(R)" && "$6"== "III" ];then wget https://www.magnusbilling.org/download/codecs/codec_g729-ast200-gcc4-glibc-pentium.so mv /usr/src/codec_g729-ast200-gcc4-glibc-pentium.so /usr/lib/asterisk/modules/codec_g729.so return 0; fi wget https://www.magnusbilling.org/download/codecs/codec_g729-ast200-gcc4-glibc-pentium3.so mv /usr/src/codec_g729-ast200-gcc4-glibc-pentium3.so /usr/lib/asterisk/modules/codec_g729.so } AMD_proc() { wget https://www.magnusbilling.org/download/codecs/codec_g729-ast200-gcc4-glibc-athlon-sse.so mv /usr/src/codec_g729-ast200-gcc4-glibc-athlon-sse.so /usr/lib/asterisk/modules/codec_g729.so } processor_type() { _UNAME=`uname -a`; _IS_64_BIT=`echo "$_UNAME" | grep x86_64` if [ -n "$_IS_64_BIT" ]; then _64BIT=1; else _64BIT=0; fi; } echo "INSTALLING G729 CODECS......... FROM http://asterisk.hosting.lv"; cd /usr/src rm -rf codec_* processor_type; _IS_AMD=`cat /proc/cpuinfo | grep AMD`; _P3=`cat /proc/cpuinfo | grep "Pentium III"`; _P3_R=`cat /proc/cpuinfo | grep "Pentium(R) III"`; _INTEL=`cat /proc/cpuinfo | grep Intel`; if [ -n "$_IS_AMD" ]; then echo "Processor type detected: AMD"; if [ "$_64BIT" == 1 ]; then echo "It is a x64 proc"; p4_x64_proc; else echo "AMD processor detected"; AMD_proc; fi elif [ -n "$_P3_R" ]; then echo "Pentium(R) III processor detected"; p3_proc; elif [ "$_64BIT" == 1 ]; then echo "Processor type detected: INTEL x64"; p4_x64_proc; elif [ -n "$_INTEL" ]; then echo "Pentium IV processor detected"; p4_proc; elif [ -n "$_P3" ]; then echo "Pentium III processor detected"; p3_proc; else echo -e "Automatic detection of required codec installation script failed\nYou must manually select and install the required codec according to this output:"; cat /proc/cpuinfo uname -a echo "you can find codecs installation scripts in http://asterisk.hosting.lv"; fi; for codec in /usr/lib/asterisk/modules/codec_g729.so; do if [ -f "${codec}" ]; then if patchelf --help 2>&1 | grep -q -- '--clear-execstack'; then echo "Clearing executable-stack flag from ${codec}." patchelf --clear-execstack "${codec}" else echo "This patchelf version cannot clear the executable-stack flag from ${codec}." fi asterisk -rx "module load $(basename "${codec}")" fi done sleep 4 asterisk -rx 'core show translation' whiptail --title "MagnusBilling Instalation Result" --msgbox "Congratulations! You have installed MagnusBilling in your Server.\n\nAccess your MagnusBilling in http://your_ip/ \n Username = root \n Password = magnus \n\nMariaDB root access uses the local Unix socket (run: mariadb).\n\n\nPRESS ANY KEY TO REBOOT YOUR SERVER" --fb 20 70 reboot