├── service-status-mail@.service ├── csf_pignore.sh ├── setssl ├── setelk ├── motd ├── load_alert.sh ├── varnish.service ├── permissions.sh ├── service-status-mail.sh ├── devicedetect-include.vcl ├── varnish.params ├── default_geoip2_dynamic.vcl ├── .gitignore ├── regex.custom.pm ├── filebeat.yml ├── env.php ├── README.md ├── default.vcl ├── all_3_default.vcl ├── composer_replace └── magenx.install.default.latest.sh /service-status-mail@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Service Status Monitor 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/local/bin/service-status-mail.sh %I 8 | ExecStop= 9 | KillMode=none 10 | -------------------------------------------------------------------------------- /csf_pignore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## disable noisy firewall alerts 3 | for program in $(grep -o 'EXE:.*' /var/log/lfd.log | awk -F' ' '{print tolower($1)}' | sort -u) 4 | do 5 | echo "${program}" >> /etc/csf/csf.pignore 6 | done 7 | 8 | csf -ra 9 | -------------------------------------------------------------------------------- /setssl: -------------------------------------------------------------------------------- 1 | bin/magento setup:store-config:set --base-url="https://${MAGENTO_DOMAIN}/" 2 | bin/magento setup:store-config:set --base-url-secure="https://${MAGENTO_DOMAIN}/" 3 | bin/magento setup:store-config:set --use-secure-admin=1 4 | bin/magento setup:store-config:set --use-secure=1 5 | -------------------------------------------------------------------------------- /setelk: -------------------------------------------------------------------------------- 1 | bin/magento config:set catalog/search/engine 'elasticsearch7' 2 | bin/magento config:set catalog/search/elasticsearch7_server_hostname "127.0.0.1" 3 | bin/magento config:set catalog/search/elasticsearch7_server_port "9200" 4 | bin/magento config:set catalog/search/elasticsearch7_index_prefix "magento2" 5 | bin/magento config:set catalog/search/elasticsearch7_enable_auth "1" 6 | bin/magento config:set catalog/search/elasticsearch7_username "elastic" 7 | bin/magento config:set catalog/search/elasticsearch7_password "${ELASTIC_PASSWORD}" 8 | -------------------------------------------------------------------------------- /motd: -------------------------------------------------------------------------------- 1 | 2 | 3 | * 4 | * 5 | * Welcome to the MagenX Magento MAGENTO_VERSION_INSTALLED-[MAGENX_VERSION] 6 | * 7 | * 8 | * _ __ ___ __ _ __ _ ___ _ __ __ __ 9 | * | '_ ` _ \ / _` |/ _` |/ _ \ '_ \\ \/ / 10 | * | | | | | | (_| | (_| | __/ | | |> < 11 | * |_| |_| |_|\__,_|\__, |\___|_| |_/_/\_\ 12 | * __/ | 13 | * |___/ 14 | * 15 | * 16 | * This server is configured by MagenX 17 | * If you have any questions or need help 18 | * please dont hesitate to contact us at: 19 | * 20 | * [support email]: support@magenx.com 21 | * [website url]: https://www.magenx.com 22 | * 23 | * 24 | 25 | -------------------------------------------------------------------------------- /load_alert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Configuration 4 | LOAD_THRESHOLD=3 5 | EMAIL_TO="ADMIN_EMAIL" 6 | EMAIL_SUBJECT="High Load Average Alert on $(hostname)" 7 | 8 | # Get the 1-minute load average 9 | LOAD_AVERAGE=$(uptime | awk -F'[a-z]:' '{ print $2 }' | awk -F', ' '{ print $1 }' | tr -d ' ') 10 | # Get conneted IPs qty 11 | ESTABLISHED=$(ss -ntp state established 'sport = :443' | awk '{print $4}' | cut -d: -f1 | sort -u | wc -l) 12 | 13 | # Compare the load average with the threshold 14 | if (( $(echo "${LOAD_AVERAGE} > ${LOAD_THRESHOLD}" | bc -l) )); then 15 | EMAIL_BODY=$(cat < " 6 | echo "Example: $0 /var/www/html magento 2" 7 | exit 1 8 | } 9 | 10 | # Check arguments 11 | [ $# -ne 3 ] && usage 12 | 13 | MAGENTO_ROOT="$1" 14 | PHP_USER="$2" 15 | MAX_DEPTH="$3" 16 | FULL_LOG="/var/tmp/$$.permissions.log" 17 | 18 | # Check if setfacl exists 19 | USE_ACL=true 20 | if ! command -v getfacl &>/dev/null; then 21 | echo "" 22 | echo "[!] Warning: getfacl not found. ACL details will not be logged." 23 | echo "[!] Permissions could be exploited if misconfigured." 24 | USE_ACL=false 25 | sleep 5 26 | else 27 | echo "" 28 | echo "[OK] Good: getfacl found. ACL details will be logged." 29 | fi 30 | 31 | echo "" 32 | echo "[?] Checking write permissions for ${PHP_USER} in: ${MAGENTO_ROOT} (Depth: ${MAX_DEPTH})" 33 | echo "--------------------------------------------------" 34 | 35 | find "${MAGENTO_ROOT}" -maxdepth "${MAX_DEPTH}" -type d -print0 | 36 | while IFS= read -r -d '' dir; do 37 | if sudo -u "${PHP_USER}" test -w "$dir" 2>/dev/null; then 38 | echo "[WRITABLE] ${dir}" 39 | ${USE_ACL} && getfacl -p "${dir}" >> "${FULL_LOG}" 40 | echo "----------------------------------------" 41 | fi 42 | done 43 | echo "" 44 | ${USE_ACL} && echo "See full log in ${FULL_LOG}" 45 | echo "" 46 | -------------------------------------------------------------------------------- /service-status-mail.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MAILTO="MAGENTO_ADMIN_EMAIL" 3 | MAILFROM="MAGENTO_DOMAIN" 4 | SERVER_IP_ADDR=$(ip route get 1 | awk '{print $NF;exit}') 5 | SERVICE=$1 6 | 7 | SERVICE_STATUS=$(systemctl status ${SERVICE}) 8 | 9 | ############################################################################################ 10 | ## PUSHOVER NOTIFICATIONS 11 | ## Before continuing, you’ll need a user key and API key from the official Pushover website. 12 | 13 | ## Sign up for an account using https://pushover.net/login 14 | ## Make note of the user key found in the top right after logging in 15 | ## Create an app using https://pushover.net/apps 16 | ## Make note of the API key shown after creating an app 17 | 18 | ## Pushover extra settings 19 | #PUSHOVER_URL="https://api.pushover.net/1/messages.json" 20 | #PUSHOVER_TOKEN="" 21 | #PUSHOVER_USER="" 22 | 23 | # curl -s -F "token=${PUSHOVER_TOKEN}" \ 24 | # -F "user=${PUSHOVER_USER}" \ 25 | # -F "title=[ALERT] on ${MAILFROM} ${SERVER_IP_ADDR}" \ 26 | # -F "message=${SERVICE_STATUS}" ${PUSHOVER_URL} \ 27 | # -F "priority=1" 28 | 29 | # Simple email function 30 | sendmail ${MAILTO} <load(); 4 | return [ 5 | 'backend' => [ 6 | 'frontName' => $_ENV['ADMIN_PATH'] 7 | ], 8 | 'remote_storage' => [ 9 | 'driver' => 'file' 10 | ], 11 | 'queue' => [ 12 | 'amqp' => [ 13 | 'host' => 'rabbitmq', 14 | 'port' => '5672', 15 | 'user' => $_ENV['OWNER'], 16 | 'password' => $_ENV['RABBITMQ_PASSWORD'], 17 | 'virtualhost' => '/'.$_ENV['OWNER'] 18 | ], 19 | 'consumers_wait_for_messages' => 0 20 | ], 21 | 'indexer' => [ 22 | 'batch_size' => [ 23 | 'cataloginventory_stock' => [ 24 | 'simple' => 650 25 | ], 26 | 'catalog_category_product' => 1000, 27 | 'catalogsearch_fulltext' => [ 28 | 'partial_reindex' => 650, 29 | 'mysql_get' => 850, 30 | 'elastic_save' => 1000 31 | ], 32 | 'catalog_product_price' => [ 33 | 'simple' => 650, 34 | 'default' => 850, 35 | 'configurable' => 1000 36 | ], 37 | 'catalogpermissions_category' => 1000, 38 | 'inventory' => [ 39 | 'simple' => 650, 40 | 'default' => 850, 41 | 'configurable' => 1000 42 | ] 43 | ] 44 | ], 45 | 'cron_consumers_runner' => [ 46 | 'cron_run' => true, 47 | 'max_messages' => 5, 48 | 'single_thread' => true, 49 | 'consumers_wait_for_messages' => 0, 50 | 'consumers' => [ 51 | 'product_action_attribute.update', 52 | 'product_action_attribute.website.update', 53 | 'exportProcessor', 54 | 'codegeneratorProcessor', 55 | 'sales.rule.update.coupon.usage', 56 | 'sales.rule.quote.trigger.recollect', 57 | 'product_alert' 58 | ] 59 | ], 60 | 'crypt' => [ 61 | 'key' => $_ENV['CRYPT_KEY'] 62 | ], 63 | 'db' => [ 64 | 'table_prefix' => '', 65 | 'connection' => [ 66 | 'default' => [ 67 | 'host' => 'mariadb', 68 | 'dbname' => $_ENV['DATABASE_NAME'], 69 | 'username' => $_ENV['DATABASE_USER'], 70 | 'password' => $_ENV['DATABASE_PASSWORD'], 71 | 'model' => 'mysql4', 72 | 'engine' => 'innodb', 73 | 'initStatements' => 'SET NAMES utf8;', 74 | 'active' => '1', 75 | 'profiler' => [ 76 | 'class' => '\Magento\Framework\DB\Profiler', 77 | 'enabled' => (bool)($_SERVER['MAGE_DB_PROFILER'] ?? false), 78 | ], 79 | 'driver_options' => [ 80 | 1014 => false 81 | ] 82 | ] 83 | ] 84 | ], 85 | 'resource' => [ 86 | 'default_setup' => [ 87 | 'connection' => 'default' 88 | ] 89 | ], 90 | 'MAGE_MODE' => $_ENV['MODE'], 91 | 'session' => [ 92 | 'save' => 'redis', 93 | 'redis' => [ 94 | 'host' => 'session', 95 | 'port' => '6379', 96 | 'password' => $_ENV['REDIS_PASSWORD'], 97 | 'timeout' => '2.5', 98 | 'persistent_identifier' => $_ENV['OWNER'].'_sess', 99 | 'database' => '0', 100 | 'compression_threshold' => '2048', 101 | 'compression_library' => 'lz4', 102 | 'log_level' => '3', 103 | 'max_concurrency' => '20', 104 | 'break_after_frontend' => '5', 105 | 'break_after_adminhtml' => '30', 106 | 'first_lifetime' => '600', 107 | 'bot_first_lifetime' => '60', 108 | 'bot_lifetime' => '7200', 109 | 'disable_locking' => '0', 110 | 'min_lifetime' => '60', 111 | 'max_lifetime' => '2592000', 112 | 'sentinel_master' => '', 113 | 'sentinel_servers' => '', 114 | 'sentinel_connect_retries' => '5', 115 | 'sentinel_verify_master' => '0' 116 | ] 117 | ], 118 | 'cache' => [ 119 | 'graphql' => [ 120 | 'id_salt' => $_ENV['GRAPHQL_ID_SALT'] 121 | ], 122 | 'frontend' => [ 123 | 'default' => [ 124 | 'id_prefix' => $_ENV['OWNER'].'_', 125 | 'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis', 126 | 'backend_options' => [ 127 | 'server' => 'cache', 128 | 'database' => '0', 129 | 'persistent' => $_ENV['OWNER'].'_cache', 130 | 'port' => '6380', 131 | 'password' => $_ENV['REDIS_PASSWORD'], 132 | 'compress_data' => '1', 133 | 'compression_lib' => 'l4z', 134 | '_useLua' => true, 135 | 'use_lua' => true, 136 | 'preload_keys' => [ 137 | $_ENV['OWNER'].'_EAV_ENTITY_TYPES', 138 | $_ENV['OWNER'].'_GLOBAL_PLUGIN_LIST', 139 | $_ENV['OWNER'].'_DB_IS_UP_TO_DATE', 140 | $_ENV['OWNER'].'_SYSTEM_DEFAULT', 141 | ] 142 | ] 143 | ] 144 | ], 145 | 'allow_parallel_generation' => false 146 | ], 147 | 'lock' => [ 148 | 'provider' => 'db' 149 | ], 150 | 'directories' => [ 151 | 'document_root_is_pub' => true 152 | ], 153 | 'http_cache_hosts' => [ 154 | [ 155 | 'host' => 'varnish', 156 | 'port' => '8081' 157 | ] 158 | ], 159 | 'cache_types' => [ 160 | 'config' => 1, 161 | 'layout' => 1, 162 | 'block_html' => 1, 163 | 'collections' => 1, 164 | 'reflection' => 1, 165 | 'db_ddl' => 1, 166 | 'compiled_config' => 1, 167 | 'eav' => 1, 168 | 'customer_notification' => 1, 169 | 'full_page' => 1, 170 | 'config_integration' => 1, 171 | 'config_integration_api' => 1, 172 | 'translate' => 1, 173 | 'config_webservice' => 1 174 | ], 175 | 'downloadable_domains' => [ 176 | $_ENV['DOMAIN'] 177 | ], 178 | 'system' => [ 179 | 'default' => [ 180 | 'catalog' => [ 181 | 'search' => [ 182 | 'engine' => 'opensearch', 183 | 'opensearch_server_hostname' => 'opensearch', 184 | 'opensearch_enable_auth' => '1', 185 | 'opensearch_server_port' => '9200', 186 | 'opensearch_index_prefix' => $_ENV['OWNER'], 187 | 'opensearch_username' => $_ENV['OWNER'], 188 | 'opensearch_password' => $_ENV['OPENSEARCH_PASSWORD'] 189 | ] 190 | ] 191 | ] 192 | ], 193 | 'install' => [ 194 | 'date' => 'Thu, 11 Aug 2022 10:47:18 +0000' 195 | ] 196 | ]; 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magento 2 installation - Magenx e-commerce webstack 2 | ## Debian 12 13 | Ubuntu 22.04 24.04 3 | 4 | ## Production ready + AWS Graviton2 ARM support 5 | 6 | > get your $100 credit and deploy on [DigitalOcean](https://m.do.co/c/ccc5d115377f) 7 | 8 | ## :rocket: Installation: 9 | 10 | ``` 11 | curl -Lo magenx.sh https://magenx.sh && bash magenx.sh 12 | ``` 13 | https://user-images.githubusercontent.com/1591200/128596448-2bf94578-8e80-47bf-b770-1799ae97df53.mp4 14 | 15 | you can run in `screen` to have indestructible session: 16 | 17 | ``` 18 | apt install -y screen 19 | screen 20 | bash magenx.sh 21 | ``` 22 | 23 | ## System requirements: 24 | *Dedicated server / Container* 25 | *8Gb RAM* 26 | *like [DigitalOcean cloud servers](https://m.do.co/c/ccc5d115377f) 27 | 28 | 29 | ## 💾 MagenX ecommerce webstack for Magento 2 Open Source 30 | Get a fully pre-configured server with Magento and LEMP stack in just 10 minutes! 🚀 31 | 32 | - [x] Linux system packages with automatic updates 33 | - [x] Initial system optimization and hardening 34 | - [x] Varnish HTTPS cache setup 35 | - [x] MariaDB my.cnf optimization 36 | - [x] Nginx optimized config with security 37 | - [x] OpenSearch 2.x latest 38 | - [x] PHP-FPM (apcu, opcache, lzf, snappy, redis) 39 | - [x] Redis Magento Cache and Sessions (2 instances) 40 | - [x] RabbitMQ message queue 41 | - [x] Letsencrypt/certbot configuration 42 | - [x] Separate Magento files owner and php-fpm user 43 | - [x] Advanced ACL linux permissions, read/write protection 44 | - [x] Chroot configuration: jailed ssh and php user (optional) 45 | 46 | Extra premium options available: 47 | 48 | - [x] Multiple environments 49 | - [x] Webmin control panel 50 | - [x] SFTP advanced configuration 51 | - [x] SSH private key access ready 52 | - [x] ConfigServer Security and Firewall advanced configuration 53 | - [x] Nginx and CSF Firewall DDOS mitigation 54 | - [x] Nginx and CSF Firewall Carding Attack mitigation 55 | - [x] MariaDB database optimization 56 | - [x] Mytop database monitoring 57 | - [x] Proxysql split database / custom port 58 | - [x] n98-magerun2 Magento 2 cli management 59 | - [x] PhpMyAdmin custom path with http auth 60 | - [x] Goaccess nginx log visualization 61 | - [x] Malware scanner (maldet) with email alerts 62 | - [x] Auditd Magento 2 files monitoring 63 | - [x] Automatic nginx images optimization 64 | - [x] Magento 2 logs rotation 65 | - [x] PWA Studio ready 66 | - [x] Hyva Theme ready 67 | - [x] Ready for production. 68 | 69 | Complete linux stack including: 70 | - linux and webstack settings optimization 71 | - letsencrypt 72 | - iotop 73 | - sysstat 74 | - git/svn 75 | - strace 76 | - python-pip 77 | - iptraf 78 | - nginx images optimization 79 | - geoip 80 | - logs rotation 81 | - separate permissions for nginx and php user 82 | - nodejs for development 83 | - and many more 84 | 85 | 86 | ## 🔡 Environment / Magento mode: 87 | The script configures webstack, users, folders, and all settings for a given environment. Productions mode. Read-only. Linux ACL 88 | 89 | 90 | ## 📄 Get config: 91 | All configuration parameters saved in sqlite database. 92 | ``` 93 | sqlite3 -line /opt/magenx/config/magenx.db "SELECT * FROM magento;" 94 | sqlite3 -line /opt/magenx/config/magenx.db "SELECT * FROM system;" 95 | ``` 96 | 97 | ## 🛡️ SSL / HTTPS: 98 | Once up and running, set up SSL with certbot (already installed): 99 | `certbot certonly --agree-tos --no-eff-email --email {EMAIL} --webroot -w /home/{USER}/public_html/pub` 100 | and uncomment the lines for SSL in: 101 | - /etc/nginx/nginx.conf 102 | - /etc/nginx/sites-available/{DOMAIN_NAME}.conf 103 | 104 | 105 | ## :hammer_and_wrench: DevOps idea: 106 | You have the opportunity to install a new Magento 2, and it is best to do this in a staging environment. Push the code to your Github repository and from there develop and deploy to production and staging environment using Github Actions. 107 | This is the safest and most productive approach. 108 | There are few configuration files available for Github Actions [paid extra] deployments: 109 | - `/opt/deploy/deploy.py` - basic script to catch Github Actions deployment input and run zero-downtime deployment 110 | - `~/.env` - magento 2 environment variables 111 | - `~/.ssh/authorized_keys` - pre-configured ssh keys 112 | 113 | 114 | ### Deployment Flow: 115 | - Create release time folder 202508211230 (date example) 116 | - Symlink shared files into new release 117 | - Extract new version to releases/202508211230/ 118 | - Atomically switch current symlink to new release 119 | - Remove old releases (keep last 2-3) 120 | 121 | ``` 122 | /home/USER/ 123 | ├── current -> releases/202508211230/ # Symlink to active version 124 | ├── releases/ # All deployed versions 125 | │ ├── 202508211030/ # Previous release 126 | │ └── 202508211230/ # Current release 127 | └── shared/ # Persistent data 128 | ├── var # Logs, tmp (symlink from active version) 129 | └── pub/media/ # Uploads, images (symlink from active version) 130 | ``` 131 | 132 | ### Minimal Explanation: 133 | - current → Symlink pointing to the active release 134 | - releases/ → Contains all versioned deployments 135 | - shared/ → Holds persistent files across deployments 136 | 137 | This gives you zero-downtime deployments and instant rollbacks. 138 | 139 | You can use tools that help you implement the folder-based atomic deployment structure 140 | (with current, releases, shared directories) on your own server. 141 | 142 | ## 🧰 Tools: 143 | [**you can use the following free included**]: 144 | - [Magento 2 deployment pipeline](https://github.com/magenx/Magento-2-deployment-pipeline) - Fully automated Magento 2 development and deployment pipeline with code review, composer check and pre-release => release workflow | MVP 145 | [ just create your own repository, add your magento files, copy only .github folder to your reposity, it will auto init ] 146 | - `sudo cacheflush` - to flush magento cache and restart php-fpm / nginx 147 | - `mysqltuner` - to see mysql metrics and parameters 148 | - `mytop` - database query monitoring / management 149 | - `n98-magerun2` - magento 2 extented cli 150 | - magento profiler built in nginx security header - conf_m2/maps.conf 151 | 152 | [**paid deployment tools**]: 153 | - deploy.py 154 | - deploy.sh 155 | - collection of scripts and server settings to enabple zero-downtime remote deployments 156 | 157 | 158 | ## 😻 Support the project 159 | You can use this for free. But its not free to create it. This takes time and research. 160 | If you are using this project, there are few ways you can support it: 161 | - [x] Star and sharing the project 162 | - [x] Open an issue to help make it better 163 | - [x] Donate 164 | - [x] Write a review https://trustpilot.com/review/www.magenx.com 165 | 166 | ![deniszokov_paypal_qrcode](https://github.com/magenx/Magento-2-aws-cluster-terraform/assets/1591200/3175c8a5-7786-4056-87c0-b4e0727f4ede) 167 | 168 | ❤️ Opensource 169 | -------------------------------------------------------------------------------- /default.vcl: -------------------------------------------------------------------------------- 1 | # Varnish version 7 2 | vcl 4.0; 3 | 4 | import std; 5 | 6 | backend default { 7 | .host = "nginx"; 8 | .port = "80"; 9 | .first_byte_timeout = 600s; 10 | .probe = { 11 | .request = "GET /health_check.php HTTP/1.1" 12 | "Host: DOMAIN_PLACEHOLDER" 13 | "User-Agent: Varnish backend health check" 14 | "Connection: close"; 15 | .timeout = 2s; 16 | .interval = 5s; 17 | .window = 10; 18 | .threshold = 5; 19 | } 20 | } 21 | 22 | acl purge { 23 | "localhost"; 24 | "nginx"; 25 | } 26 | 27 | sub vcl_recv { 28 | 29 | set req.http.X-Forwarded-Proto = "https"; 30 | 31 | if (req.restarts > 0) { 32 | set req.hash_always_miss = true; 33 | } 34 | 35 | if (req.method == "PURGE") { 36 | if (client.ip !~ purge) { 37 | return (synth(405, "Method not allowed")); 38 | } 39 | # To use the X-Pool header for purging varnish during automated deployments, make sure the X-Pool header 40 | # has been added to the response in your backend server config. This is used, for example, by the 41 | # capistrano-magento2 gem for purging old content from varnish during it's deploy routine. 42 | if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) { 43 | return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required")); 44 | } 45 | if (req.http.X-Magento-Tags-Pattern) { 46 | ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern); 47 | } 48 | if (req.http.X-Pool) { 49 | ban("obj.http.X-Pool ~ " + req.http.X-Pool); 50 | } 51 | return (synth(200, "Purged")); 52 | } 53 | 54 | if (req.method != "GET" && 55 | req.method != "HEAD" && 56 | req.method != "PUT" && 57 | req.method != "POST" && 58 | req.method != "TRACE" && 59 | req.method != "OPTIONS" && 60 | req.method != "DELETE") { 61 | /* Non-RFC2616 or CONNECT which is weird. */ 62 | return (pipe); 63 | } 64 | 65 | # We only deal with GET and HEAD by default 66 | if (req.method != "GET" && req.method != "HEAD") { 67 | return (pass); 68 | } 69 | 70 | # Bypass customer, shopping cart, checkout 71 | if (req.url ~ "/customer" || req.url ~ "/checkout") { 72 | return (pass); 73 | } 74 | 75 | # Bypass health check requests 76 | if (req.url ~ "^/health_check.php") { 77 | return (pass); 78 | } 79 | 80 | # Bypass cache if profiler headers are present 81 | if (req.http.X-Mage-Profiler == "PROFILER_PLACEHOLDER" || req.http.X-Mage-Db-Profiler == "PROFILER_PLACEHOLDER") { 82 | return (pass); 83 | } else { 84 | unset req.http.X-Mage-Profiler; 85 | unset req.http.X-Mage-Db-Profiler; 86 | } 87 | 88 | # Set initial grace period usage status 89 | set req.http.grace = "none"; 90 | 91 | # normalize url in case of leading HTTP scheme and domain 92 | set req.url = regsub(req.url, "^http[s]?://", ""); 93 | 94 | # collect all cookies 95 | std.collect(req.http.Cookie); 96 | 97 | # Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression 98 | if (req.http.Accept-Encoding) { 99 | if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") { 100 | # No point in compressing these 101 | unset req.http.Accept-Encoding; 102 | } elsif (req.http.Accept-Encoding ~ "gzip") { 103 | set req.http.Accept-Encoding = "gzip"; 104 | } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") { 105 | set req.http.Accept-Encoding = "deflate"; 106 | } else { 107 | # unknown algorithm 108 | unset req.http.Accept-Encoding; 109 | } 110 | } 111 | 112 | # Remove all marketing get parameters to minimize the cache objects 113 | if (req.url ~ "(\?|&)(gad_source|gbraid|wbraid|_gl|dclid|gclsrc|srsltid|msclkid|gclid|cx|_kx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") { 114 | set req.url = regsuball(req.url, "(gad_source|gbraid|wbraid|_gl|dclid|gclsrc|srsltid|msclkid|gclid|cx|_kx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); 115 | set req.url = regsub(req.url, "[?|&]+$", ""); 116 | } 117 | 118 | # Static files caching 119 | if (req.url ~ "^/(media|static)/") { 120 | # Static files should not be cached by default 121 | #return (pass); 122 | # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines 123 | unset req.http.Https; 124 | unset req.http.X-Forwarded-Proto; 125 | unset req.http.Cookie; 126 | } 127 | 128 | # Bypass authenticated GraphQL requests without a X-Magento-Cache-Id 129 | if (req.url ~ "/graphql" && !req.http.X-Magento-Cache-Id && req.http.Authorization ~ "^Bearer") { 130 | return (pass); 131 | } 132 | 133 | return (hash); 134 | } 135 | 136 | sub vcl_hash { 137 | if ((req.url !~ "/graphql" || !req.http.X-Magento-Cache-Id) && req.http.cookie ~ "X-Magento-Vary=") { 138 | hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1")); 139 | } 140 | 141 | # To make sure http users don't see ssl warning 142 | if (req.http.X-Forwarded-Proto) { 143 | hash_data(req.http.X-Forwarded-Proto); 144 | } 145 | 146 | 147 | 148 | if (req.url ~ "/graphql") { 149 | call process_graphql_headers; 150 | } 151 | } 152 | 153 | sub process_graphql_headers { 154 | if (req.http.X-Magento-Cache-Id) { 155 | hash_data(req.http.X-Magento-Cache-Id); 156 | 157 | # When the frontend stops sending the auth token, make sure users stop getting results cached for logged-in users 158 | if (req.http.Authorization ~ "^Bearer") { 159 | hash_data("Authorized"); 160 | } 161 | } 162 | 163 | if (req.http.Store) { 164 | hash_data(req.http.Store); 165 | } 166 | 167 | if (req.http.Content-Currency) { 168 | hash_data(req.http.Content-Currency); 169 | } 170 | } 171 | 172 | sub vcl_backend_response { 173 | 174 | set beresp.grace = 3d; 175 | 176 | if (beresp.http.content-type ~ "text") { 177 | set beresp.do_esi = true; 178 | } 179 | 180 | if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") { 181 | set beresp.do_gzip = true; 182 | } 183 | 184 | if (beresp.http.X-Magento-Debug) { 185 | set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control; 186 | } 187 | 188 | # cache only successfully responses and 404s that are not marked as private 189 | if ((beresp.status != 200 && beresp.status != 404) || beresp.http.Cache-Control ~ "private") { 190 | set beresp.uncacheable = true; 191 | set beresp.ttl = 86400s; 192 | return (deliver); 193 | } 194 | 195 | # validate if we need to cache it and prevent from setting cookie 196 | if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) { 197 | # Collapse beresp.http.set-cookie in order to merge multiple set-cookie headers 198 | # Although it is not recommended to collapse set-cookie header, 199 | # it is safe to do it here as the set-cookie header is removed below 200 | std.collect(beresp.http.set-cookie); 201 | # Do not cache the response under current cache key (hash), 202 | # if the response has X-Magento-Vary but the request does not. 203 | if ((bereq.url !~ "/graphql" || !bereq.http.X-Magento-Cache-Id) 204 | && bereq.http.cookie !~ "X-Magento-Vary=" 205 | && beresp.http.set-cookie ~ "X-Magento-Vary=") { 206 | set beresp.ttl = 0s; 207 | set beresp.uncacheable = true; 208 | } 209 | unset beresp.http.set-cookie; 210 | } 211 | 212 | # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass 213 | if (beresp.ttl <= 0s || 214 | beresp.http.Surrogate-control ~ "no-store" || 215 | (!beresp.http.Surrogate-Control && 216 | beresp.http.Cache-Control ~ "no-cache|no-store") || 217 | beresp.http.Vary == "*") { 218 | # Mark as Hit-For-Pass for the next 2 minutes 219 | set beresp.ttl = 120s; 220 | set beresp.uncacheable = true; 221 | } 222 | 223 | # If the cache key in the Magento response doesn't match the one that was sent in the request, don't cache under the request's key 224 | if (bereq.url ~ "/graphql" && bereq.http.X-Magento-Cache-Id && bereq.http.X-Magento-Cache-Id != beresp.http.X-Magento-Cache-Id) { 225 | set beresp.ttl = 0s; 226 | set beresp.uncacheable = true; 227 | } 228 | 229 | return (deliver); 230 | } 231 | 232 | sub vcl_deliver { 233 | 234 | if (obj.uncacheable) { 235 | set resp.http.X-Magento-Cache-Debug = "UNCACHEABLE"; 236 | } else if (obj.hits) { 237 | set resp.http.X-Magento-Cache-Debug = "HIT"; 238 | set resp.http.Grace = req.http.grace; 239 | } else { 240 | set resp.http.X-Magento-Cache-Debug = "MISS"; 241 | } 242 | 243 | # Not letting browser to cache non-static files. 244 | if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(media|static)/") { 245 | set resp.http.Pragma = "no-cache"; 246 | set resp.http.Expires = "-1"; 247 | set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; 248 | } 249 | 250 | if (!resp.http.X-Magento-Debug) { 251 | unset resp.http.Age; 252 | } 253 | unset resp.http.X-Magento-Debug; 254 | unset resp.http.X-Magento-Tags; 255 | unset resp.http.X-Powered-By; 256 | unset resp.http.Server; 257 | unset resp.http.X-Varnish; 258 | unset resp.http.Via; 259 | unset resp.http.Link; 260 | } 261 | 262 | sub vcl_hit { 263 | if (obj.ttl >= 0s) { 264 | # Hit within TTL period 265 | return (deliver); 266 | } 267 | if (std.healthy(req.backend_hint)) { 268 | if (obj.ttl + 300s > 0s) { 269 | # Hit after TTL expiration, but within grace period 270 | set req.http.grace = "normal (healthy server)"; 271 | return (deliver); 272 | } else { 273 | # Hit after TTL and grace expiration 274 | return (restart); 275 | } 276 | } else { 277 | # server is not healthy, retrieve from cache 278 | set req.http.grace = "unlimited (unhealthy server)"; 279 | return (deliver); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /all_3_default.vcl: -------------------------------------------------------------------------------- 1 | # VCL version 5.0 is not supported so it should be 4.0 even though actually used Varnish version is 6 2 | vcl 4.0; 3 | 4 | import std; 5 | # The minimal Varnish version is 6.0 6 | # For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https' 7 | 8 | 9 | backend production { 10 | .host = "127.0.0.1"; 11 | .port = "8080"; 12 | .first_byte_timeout = 600s; 13 | .probe = { 14 | .request = "GET /health_check.php HTTP/1.1" 15 | "Host: production.example.com" 16 | "X-Probe: Varnish backend health check" 17 | "Connection: close"; 18 | .timeout = 2s; 19 | .interval = 5s; 20 | .window = 10; 21 | .threshold = 5; 22 | } 23 | } 24 | 25 | backend staging { 26 | .host = "127.0.0.1"; 27 | .port = "8080"; 28 | .first_byte_timeout = 600s; 29 | .probe = { 30 | .request = "GET /health_check.php HTTP/1.1" 31 | "Host: staging.example.com" 32 | "X-Probe: Varnish backend health check" 33 | "Connection: close"; 34 | .timeout = 2s; 35 | .interval = 5s; 36 | .window = 10; 37 | .threshold = 5; 38 | } 39 | } 40 | 41 | backend developer { 42 | .host = "127.0.0.1"; 43 | .port = "8080"; 44 | .first_byte_timeout = 600s; 45 | .probe = { 46 | .request = "GET /health_check.php HTTP/1.1" 47 | "Host: developer.example.com" 48 | "X-Probe: Varnish backend health check" 49 | "Connection: close"; 50 | .timeout = 2s; 51 | .interval = 5s; 52 | .window = 10; 53 | .threshold = 5; 54 | } 55 | } 56 | 57 | acl purge { 58 | "localhost"; 59 | } 60 | 61 | sub vcl_recv { 62 | if (req.restarts > 0) { 63 | set req.hash_always_miss = true; 64 | } 65 | 66 | if (req.http.host == "production.example.com") { 67 | set req.backend_hint = production; 68 | } else if (req.http.host == "staging.example.com") { 69 | set req.backend_hint = staging; 70 | } else if (req.http.host == "developer.example.com") { 71 | set req.backend_hint = developer; 72 | } 73 | 74 | if (req.method == "PURGE") { 75 | if (client.ip !~ purge) { 76 | return (synth(405, "Method not allowed")); 77 | } 78 | # To use the X-Pool header for purging varnish during automated deployments, make sure the X-Pool header 79 | # has been added to the response in your backend server config. This is used, for example, by the 80 | # capistrano-magento2 gem for purging old content from varnish during it's deploy routine. 81 | if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) { 82 | return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required")); 83 | } 84 | if (req.http.X-Magento-Tags-Pattern) { 85 | ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern); 86 | } 87 | if (req.http.X-Pool) { 88 | ban("obj.http.X-Pool ~ " + req.http.X-Pool); 89 | } 90 | return (synth(200, "Purged")); 91 | } 92 | 93 | if (req.method != "GET" && 94 | req.method != "HEAD" && 95 | req.method != "PUT" && 96 | req.method != "POST" && 97 | req.method != "TRACE" && 98 | req.method != "OPTIONS" && 99 | req.method != "DELETE") { 100 | /* Non-RFC2616 or CONNECT which is weird. */ 101 | return (pipe); 102 | } 103 | 104 | # We only deal with GET and HEAD by default 105 | if (req.method != "GET" && req.method != "HEAD") { 106 | return (pass); 107 | } 108 | 109 | # Bypass customer, shopping cart, checkout 110 | if (req.url ~ "/customer" || req.url ~ "/checkout") { 111 | return (pass); 112 | } 113 | 114 | # Bypass health check requests 115 | if (req.url ~ "^/(pub/)?(health_check.php)$") { 116 | return (pass); 117 | } 118 | 119 | # Bypass profiler query 120 | if (req.url ~ "\?PROFILER_PLACEHOLDER") { 121 | return (pass); 122 | } 123 | 124 | # Set initial grace period usage status 125 | set req.http.grace = "none"; 126 | 127 | # normalize url in case of leading HTTP scheme and domain 128 | set req.url = regsub(req.url, "^http[s]?://", ""); 129 | 130 | # collect all cookies 131 | std.collect(req.http.Cookie); 132 | 133 | # Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression 134 | if (req.http.Accept-Encoding) { 135 | if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") { 136 | # No point in compressing these 137 | unset req.http.Accept-Encoding; 138 | } elsif (req.http.Accept-Encoding ~ "gzip") { 139 | set req.http.Accept-Encoding = "gzip"; 140 | } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") { 141 | set req.http.Accept-Encoding = "deflate"; 142 | } else { 143 | # unknown algorithm 144 | unset req.http.Accept-Encoding; 145 | } 146 | } 147 | 148 | # Remove all marketing get parameters to minimize the cache objects 149 | if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") { 150 | set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); 151 | set req.url = regsub(req.url, "[?|&]+$", ""); 152 | } 153 | 154 | # Static files caching 155 | if (req.url ~ "^/(pub/)?(media|static)/") { 156 | # Static files should not be cached by default 157 | #return (pass); 158 | 159 | # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines 160 | unset req.http.Https; 161 | unset req.http.X-Forwarded-Proto; 162 | unset req.http.Cookie; 163 | } 164 | 165 | # Bypass authenticated GraphQL requests without a X-Magento-Cache-Id 166 | if (req.url ~ "/graphql" && !req.http.X-Magento-Cache-Id && req.http.Authorization ~ "^Bearer") { 167 | return (pass); 168 | } 169 | 170 | return (hash); 171 | } 172 | 173 | sub vcl_hash { 174 | if ((req.url !~ "/graphql" || !req.http.X-Magento-Cache-Id) && req.http.cookie ~ "X-Magento-Vary=") { 175 | hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1")); 176 | } 177 | 178 | # To make sure http users don't see ssl warning 179 | if (req.http.X-Forwarded-Proto) { 180 | hash_data(req.http.X-Forwarded-Proto); 181 | } 182 | 183 | 184 | if (req.url ~ "/graphql") { 185 | call process_graphql_headers; 186 | } 187 | } 188 | 189 | sub process_graphql_headers { 190 | if (req.http.X-Magento-Cache-Id) { 191 | hash_data(req.http.X-Magento-Cache-Id); 192 | 193 | # When the frontend stops sending the auth token, make sure users stop getting results cached for logged-in users 194 | if (req.http.Authorization ~ "^Bearer") { 195 | hash_data("Authorized"); 196 | } 197 | } 198 | 199 | if (req.http.Store) { 200 | hash_data(req.http.Store); 201 | } 202 | 203 | if (req.http.Content-Currency) { 204 | hash_data(req.http.Content-Currency); 205 | } 206 | } 207 | 208 | sub vcl_backend_response { 209 | 210 | set beresp.grace = 3d; 211 | 212 | if (beresp.http.content-type ~ "text") { 213 | set beresp.do_esi = true; 214 | } 215 | 216 | if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") { 217 | set beresp.do_gzip = true; 218 | } 219 | 220 | if (beresp.http.X-Magento-Debug) { 221 | set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control; 222 | } 223 | 224 | # cache only successfully responses and 404s that are not marked as private 225 | if (beresp.status != 200 && 226 | beresp.status != 404 && 227 | beresp.http.Cache-Control ~ "private") { 228 | set beresp.uncacheable = true; 229 | set beresp.ttl = 86400s; 230 | return (deliver); 231 | } 232 | 233 | # validate if we need to cache it and prevent from setting cookie 234 | if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) { 235 | unset beresp.http.set-cookie; 236 | } 237 | 238 | # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass 239 | if (beresp.ttl <= 0s || 240 | beresp.http.Surrogate-control ~ "no-store" || 241 | (!beresp.http.Surrogate-Control && 242 | beresp.http.Cache-Control ~ "no-cache|no-store") || 243 | beresp.http.Vary == "*") { 244 | # Mark as Hit-For-Pass for the next 2 minutes 245 | set beresp.ttl = 120s; 246 | set beresp.uncacheable = true; 247 | } 248 | 249 | # If the cache key in the Magento response doesn't match the one that was sent in the request, don't cache under the request's key 250 | if (bereq.url ~ "/graphql" && bereq.http.X-Magento-Cache-Id && bereq.http.X-Magento-Cache-Id != beresp.http.X-Magento-Cache-Id) { 251 | set beresp.ttl = 0s; 252 | set beresp.uncacheable = true; 253 | } 254 | 255 | return (deliver); 256 | } 257 | 258 | sub vcl_deliver { 259 | if (resp.http.x-varnish ~ " ") { 260 | set resp.http.X-Magento-Cache-Debug = "HIT"; 261 | set resp.http.Grace = req.http.grace; 262 | } else { 263 | set resp.http.X-Magento-Cache-Debug = "MISS"; 264 | } 265 | 266 | # Not letting browser to cache non-static files. 267 | if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") { 268 | set resp.http.Pragma = "no-cache"; 269 | set resp.http.Expires = "-1"; 270 | set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; 271 | } 272 | 273 | if (!resp.http.X-Magento-Debug) { 274 | unset resp.http.Age; 275 | } 276 | unset resp.http.X-Magento-Debug; 277 | unset resp.http.X-Magento-Tags; 278 | unset resp.http.X-Powered-By; 279 | unset resp.http.Server; 280 | unset resp.http.X-Varnish; 281 | unset resp.http.Via; 282 | unset resp.http.Link; 283 | } 284 | 285 | sub vcl_hit { 286 | if (obj.ttl >= 0s) { 287 | # Hit within TTL period 288 | return (deliver); 289 | } 290 | if (std.healthy(req.backend_hint)) { 291 | if (obj.ttl + 300s > 0s) { 292 | # Hit after TTL expiration, but within grace period 293 | set req.http.grace = "normal (healthy server)"; 294 | return (deliver); 295 | } else { 296 | # Hit after TTL and grace expiration 297 | return (restart); 298 | } 299 | } else { 300 | # server is not healthy, retrieve from cache 301 | set req.http.grace = "unlimited (unhealthy server)"; 302 | return (deliver); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /composer_replace: -------------------------------------------------------------------------------- 1 | "replace": { 2 | "adobe-commerce/os-extensions-metapackage": "*", 3 | "amzn/amazon-pay-and-login-magento-2-module": "*", 4 | "amzn/amazon-pay-and-login-with-amazon-core-module": "*", 5 | "amzn/amazon-pay-module": "*", 6 | "amzn/amazon-pay-sdk-php": "*", 7 | "amzn/login-with-amazon-module": "*", 8 | "astock/stock-api-libphp": "*", 9 | "dotmailer/dotmailer-magento2-extension": "*", 10 | "dotmailer/dotmailer-magento2-extension-chat": "*", 11 | "dotmailer/dotmailer-magento2-extension-enterprise": "*", 12 | "dotmailer/dotmailer-magento2-extension-package": "*", 13 | "klarna/m2-payments": "*", 14 | "klarna/module-core": "*", 15 | "klarna/module-kp": "*", 16 | "klarna/module-ordermanagement": "*", 17 | "magento/adobe-stock-integration": "*", 18 | "magento/google-shopping-ads": "*", 19 | "magento/inventory-metapackage": "*", 20 | "magento/inventory-composer-installer": "*", 21 | "magento/inventory-composer-metapackage": "*", 22 | "magento/module-adobe-stock-admin-ui": "*", 23 | "magento/module-adobe-stock-asset": "*", 24 | "magento/module-adobe-stock-asset-api": "*", 25 | "magento/module-adobe-stock-client": "*", 26 | "magento/module-adobe-stock-client-api": "*", 27 | "magento/module-adobe-stock-image": "*", 28 | "magento/module-adobe-stock-image-admin-ui": "*", 29 | "magento/module-adobe-stock-image-api": "*", 30 | "magento/module-analytics": "*", 31 | "magento/module-authorizenet": "*", 32 | "magento/module-authorizenet-acceptjs": "*", 33 | "magento/module-authorizenet-cardinal": "*", 34 | "magento/module-authorizenet-graph-ql": "*", 35 | "magento/module-braintree-graph-ql": "*", 36 | "magento/module-bundle-graph-ql": "*", 37 | "magento/module-cardinal-commerce": "*", 38 | "magento/module-order-cancellation-graph-ql": "*", 39 | "magento/module-catalog-analytics": "*", 40 | "magento/module-catalog-cms-graph-ql": "*", 41 | "magento/module-catalog-customer-graph-ql": "*", 42 | "magento/module-catalog-customer-ql": "*", 43 | "magento/module-catalog-graph-ql": "*", 44 | "magento/module-catalog-rule-graph-ql": "*", 45 | "magento/module-catalog-inventory-graph-ql": "*", 46 | "magento/module-catalog-url-rewrite-graph-ql": "*", 47 | "magento/module-checkout-agreements-graph-ql": "*", 48 | "magento/module-cms-graph-ql": "*", 49 | "magento/module-cms-url-rewrite-graph-ql": "*", 50 | "magento/module-configurable-product-graph-ql": "*", 51 | "magento/module-customer-analytics": "*", 52 | "magento/module-customer-balance-graph-ql": "*", 53 | "magento/module-customer-downloadable-graph-ql": "*", 54 | "magento/module-customer-graph-ql": "*", 55 | "magento/module-cybersource": "*", 56 | "magento/module-dhl": "*", 57 | "magento/module-directory-graph-ql": "*", 58 | "magento/module-downloadable-graph-ql": "*", 59 | "magento/module-eav-graph-ql": "*", 60 | "magento/module-eway": "*", 61 | "magento/module-fedex": "*", 62 | "magento/module-gift-card-account-graph-ql": "*", 63 | "magento/module-gift-card-graph-ql": "*", 64 | "magento/module-google-adwords": "*", 65 | "magento/module-google-optimizer": "*", 66 | "magento/module-grouped-product-graph-ql": "*", 67 | "magento/module-inventory": "*", 68 | "magento/module-inventory-admin-ui": "*", 69 | "magento/module-inventory-advanced-checkout": "*", 70 | "magento/module-inventory-api": "*", 71 | "magento/module-inventory-bundle-product": "*", 72 | "magento/module-inventory-bundle-product-admin-ui": "*", 73 | "magento/module-inventory-cache": "*", 74 | "magento/module-inventory-catalog": "*", 75 | "magento/module-inventory-catalog-admin-ui": "*", 76 | "magento/module-inventory-catalog-api": "*", 77 | "magento/module-inventory-catalog-search": "*", 78 | "magento/module-inventory-configurable-product": "*", 79 | "magento/module-inventory-configurable-product-admin-ui": "*", 80 | "magento/module-inventory-configurable-product-indexer": "*", 81 | "magento/module-inventory-configuration": "*", 82 | "magento/module-inventory-configuration-api": "*", 83 | "magento/module-inventory-distance-based-source-selection": "*", 84 | "magento/module-inventory-distance-based-source-selection-admin-ui": "*", 85 | "magento/module-inventory-distance-based-source-selection-api": "*", 86 | "magento/module-inventory-elasticsearch": "*", 87 | "magento/module-inventory-export-stock": "*", 88 | "magento/module-inventory-export-stock-api": "*", 89 | "magento/module-inventory-graph-ql": "*", 90 | "magento/module-inventory-grouped-product": "*", 91 | "magento/module-inventory-grouped-product-admin-ui": "*", 92 | "magento/module-inventory-grouped-product-indexer": "*", 93 | "magento/module-inventory-import-export": "*", 94 | "magento/module-inventory-indexer": "*", 95 | "magento/module-inventory-low-quantity-notification": "*", 96 | "magento/module-inventory-low-quantity-notification-admin-ui": "*", 97 | "magento/module-inventory-low-quantity-notification-api": "*", 98 | "magento/module-inventory-multi-dimensional-indexer-api": "*", 99 | "magento/module-inventory-product-alert": "*", 100 | "magento/module-inventory-requisition-list": "*", 101 | "magento/module-inventory-reservation-cli": "*", 102 | "magento/module-inventory-reservations": "*", 103 | "magento/module-inventory-reservations-api": "*", 104 | "magento/module-inventory-sales": "*", 105 | "magento/module-inventory-sales-admin-ui": "*", 106 | "magento/module-inventory-sales-api": "*", 107 | "magento/module-inventory-sales-frontend-ui": "*", 108 | "magento/module-inventory-setup-fixture-generator": "*", 109 | "magento/module-inventory-shipping": "*", 110 | "magento/module-inventory-shipping-admin-ui": "*", 111 | "magento/module-inventory-source-deduction-api": "*", 112 | "magento/module-inventory-source-selection": "*", 113 | "magento/module-inventory-source-selection-api": "*", 114 | "magento/module-inventory-in-store-pickup-webapi-extension": "*", 115 | "magento/module-inventory-in-store-pickup-shipping-api": "*", 116 | "magento/module-inventory-in-store-pickup-shipping-admin-ui": "*", 117 | "magento/module-inventory-in-store-pickup-api": "*", 118 | "magento/module-inventory-in-store-pickup-shipping": "*", 119 | "magento/module-inventory-in-store-pickup-sales-api": "*", 120 | "magento/module-inventory-in-store-pickup": "*", 121 | "magento/module-inventory-in-store-pickup-quote": "*", 122 | "magento/module-inventory-in-store-pickup-sales": "*", 123 | "magento/module-inventory-in-store-pickup-sales-admin-ui": "*", 124 | "magento/module-inventory-in-store-pickup-quote-graph-ql": "*", 125 | "magento/module-inventory-in-store-pickup-multishipping": "*", 126 | "magento/module-inventory-in-store-pickup-graph-ql": "*", 127 | "magento/module-inventory-in-store-pickup-frontend": "*", 128 | "magento/module-inventory-in-store-pickup-admin-ui": "*", 129 | "magento/module-inventory-bundle-import-export": "*", 130 | "magento/module-inventory-bundle-product-indexer": "*", 131 | "magento/module-marketplace": "*", 132 | "magento/module-paypal-graph-ql": "*", 133 | "magento/module-quote-analytics": "*", 134 | "magento/module-quote-graph-ql": "*", 135 | "magento/module-related-product-graph-ql": "*", 136 | "magento/module-review-analytics": "*", 137 | "magento/module-reward-graph-ql": "*", 138 | "magento/module-rma-graph-ql": "*", 139 | "magento/module-sales-analytics": "*", 140 | "magento/module-sales-graph-ql": "*", 141 | "magento/module-sample-data": "*", 142 | "magento/module-send-friend-graph-ql": "*", 143 | "magento/module-signifyd": "*", 144 | "magento/module-store-graph-ql": "*", 145 | "magento/module-swagger": "*", 146 | "magento/module-swagger-webapi": "*", 147 | "magento/module-swagger-webapi-async": "*", 148 | "magento/module-swatches-graph-ql": "*", 149 | "magento/module-tax-graph-ql": "*", 150 | "magento/module-theme-graph-ql": "*", 151 | "magento/module-ups": "*", 152 | "magento/module-url-rewrite-graph-ql": "*", 153 | "magento/module-usps": "*", 154 | "magento/module-vault-graph-ql": "*", 155 | "magento/module-securitytxt": "*", 156 | "magento/module-version": "*", 157 | "magento/module-weee-graph-ql": "*", 158 | "magento/module-wishlist-analytics": "*", 159 | "magento/module-wishlist-graph-ql": "*", 160 | "magento/module-worldpay": "*", 161 | "magento/module-elasticsearch-6": "*", 162 | "magento/module-elasticsearch-7": "*", 163 | "magento/module-elasticsearch-8": "*", 164 | "magento/module-admin-analytics": "*", 165 | "magento/module-paypal-captcha": "*", 166 | "magento/module-release-notification": "*", 167 | "magento/module-media-content": "*", 168 | "magento/module-media-content-api": "*", 169 | "magento/module-media-content-catalog": "*", 170 | "magento/module-media-content-cms": "*", 171 | "magento/module-media-gallery-ui": "*", 172 | "magento/module-media-gallery-ui-api": "*", 173 | "magento/module-media-gallery-integration": "*", 174 | "magento/module-media-gallery-synchronization": "*", 175 | "magento/module-media-gallery-synchronization-api": "*", 176 | "magento/module-media-content-synchronization": "*", 177 | "magento/module-media-content-synchronization-api": "*", 178 | "magento/module-media-content-synchronization-catalog": "*", 179 | "magento/module-media-content-synchronization-cms": "*", 180 | "magento/module-media-gallery-synchronization-metadata": "*", 181 | "magento/module-media-gallery-metadata": "*", 182 | "magento/module-media-gallery-metadata-api": "*", 183 | "magento/module-media-gallery-catalog-ui": "*", 184 | "magento/module-media-gallery-cms-ui": "*", 185 | "magento/module-media-gallery-catalog-integration": "*", 186 | "magento/module-media-gallery-catalog": "*", 187 | "magento/module-media-gallery-renditions": "*", 188 | "magento/module-media-gallery-renditions-api": "*", 189 | "magento/module-review-graph-ql": "*", 190 | "magento/module-newsletter-graph-ql": "*", 191 | "magento/module-gift-message-graph-ql": "*", 192 | "magento/module-page-builder": "*", 193 | "magento/module-page-builder-admin-analytics": "*", 194 | "magento/module-page-builder-analytics": "*", 195 | "magento/module-aws-s3-page-builder": "*", 196 | "magento/module-catalog-page-builder-analytics": "*", 197 | "magento/module-cms-page-builder-analytics": "*", 198 | "magento/module-paypal": "*", 199 | "magento/module-adobe-ims": "*", 200 | "magento/module-adobe-ims-api": "*", 201 | "magento/module-admin-adobe-ims": "*", 202 | "magento/module-admin-adobe-ims-two-factor-auth": "*", 203 | "magento/module-application-performance-monitor": "*", 204 | "magento/module-application-performance-monitor-new-relic": "*", 205 | "magento/module-new-relic-reporting": "*", 206 | "magento/module-graph-ql-new-relic": "*", 207 | "magento/module-login-as-customer": "*", 208 | "magento/module-login-as-customer-admin-ui": "*", 209 | "magento/module-login-as-customer-api": "*", 210 | "magento/module-login-as-customer-assistance": "*", 211 | "magento/module-login-as-customer-frontend-ui": "*", 212 | "magento/module-login-as-customer-graph-ql": "*", 213 | "magento/module-login-as-customer-log": "*", 214 | "magento/module-login-as-customer-page-cache": "*", 215 | "magento/module-login-as-customer-quote": "*", 216 | "magento/module-login-as-customer-sales": "*", 217 | "magento/module-service-proxy": "*", 218 | "magento/module-payment-services-dashboard": "*", 219 | "magento/module-payment-services-paypal-graph-ql": "*", 220 | "magento/services-connector": "*", 221 | "magento/module-services-id": "*", 222 | "magento/module-services-id-graph-ql-server": "*", 223 | "magento/module-services-id-layout": "*", 224 | "magento/module-saas-common": "*", 225 | "magento/module-payment-services-base": "*", 226 | "magento/module-payment-services-saas-export": "*", 227 | "magento/module-payment-services-paypal": "*", 228 | "paypal/module-braintree-graph-ql": "*", 229 | "paypal/module-braintree-core": "*", 230 | "paypal/module-braintree-customer-balance": "*", 231 | "paypal/module-braintree-gift-card-account": "*", 232 | "paypal/module-braintree-gift-wrapping": "*", 233 | "paypal/module-braintree-gift-card": "*", 234 | "paypal/module-braintree-reward": "*", 235 | "temando/module-shipping": "*", 236 | "temando/module-shipping-m2": "*", 237 | "temando/module-shipping-remover": "*", 238 | "vertexinc/product-magento-module": "*", 239 | "vertex/module-address-validation": "*", 240 | "vertex/module-tax": "*", 241 | "vertex/product-magento-module": "*", 242 | "vertex/sdk": "*", 243 | "yotpo/magento2-module-yotpo-reviews": "*", 244 | "yotpo/magento2-module-yotpo-reviews-bundle": "*" 245 | }, 246 | -------------------------------------------------------------------------------- /magenx.install.default.latest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #=================================================================================# 3 | # MagenX e-commerce stack for Magento 2 # 4 | # Copyright (C) 2013-present admin@magenx.com # 5 | # All rights reserved. # 6 | #=================================================================================# 7 | SELF=$(basename $0) 8 | MAGENX_VERSION=$(curl -s https://api.github.com/repos/magenx/Magento-2-server-installation/tags 2>&1 | head -3 | grep -oP '(?<=")\d.*(?=")') 9 | MAGENX_BASE="https://magenx.sh" 10 | ################################################################################### 11 | ### REPOSITORY AND PACKAGES ### 12 | ################################################################################### 13 | 14 | # Github installation repository raw url 15 | MAGENX_INSTALL_GITHUB_REPO="https://raw.githubusercontent.com/magenx/Magento-2-server-installation/master" 16 | 17 | # Magento 18 | VERSION_LIST=$(curl -s https://api.github.com/repos/magento/magento2/tags 2>&1 | grep -oP '(?<=name": ").*(?=")' | sort -r) 19 | PROJECT="composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition" 20 | 21 | COMPOSER_NAME="8c681734f22763b50ea0c29dff9e7af2" 22 | COMPOSER_PASSWORD="02dfee497e669b5db1fe1c8d481d6974" 23 | 24 | ## Version lock 25 | COMPOSER_VERSION="2.7" 26 | RABBITMQ_VERSION="4*" 27 | ERLANG_VERSION="1:27*" 28 | MARIADB_VERSION="11.4" 29 | PHP_VERSION="8.4" 30 | OPENSEARCH_VERSION="2.x" 31 | VARNISH_VERSION="76" 32 | REDIS_VERSION="8" 33 | NODE_VERSION="20" 34 | NVM_VERSION="0.40.3" 35 | 36 | # Repositories 37 | MARIADB_REPO_CONFIG="https://r.mariadb.com/downloads/mariadb_repo_setup" 38 | 39 | # Nginx configuration 40 | NGINX_VERSION=$(curl -s http://nginx.org/en/download.html | grep -oP '(?<=gz">nginx-).*?(?=)' | head -1) 41 | MAGENX_NGINX_GITHUB_REPO="https://raw.githubusercontent.com/magenx/Magento-nginx-config/master/" 42 | MAGENX_NGINX_GITHUB_REPO_API="https://api.github.com/repos/magenx/Magento-nginx-config/contents/magento2" 43 | 44 | # Debug Tools 45 | MYSQL_TUNER="https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl" 46 | 47 | # Malware detector 48 | MALDET="https://www.rfxn.com/downloads/maldetect-current.tar.gz" 49 | 50 | # WebStack Packages .deb 51 | WEB_STACK_CHECK="mysql* rabbitmq* elasticsearch opensearch percona-server* maria* php* nginx* varnish* certbot* redis* webmin" 52 | 53 | EXTRA_PACKAGES="curl jq gnupg2 auditd apt-transport-https apt-show-versions ca-certificates lsb-release make autoconf snapd automake libtool uuid-runtime \ 54 | perl openssl unzip screen nfs-common inotify-tools iptables smartmontools mlocate vim wget sudo apache2-utils \ 55 | logrotate git netcat-openbsd patch ipset postfix strace rsyslog moreutils lsof sysstat acl attr iotop expect imagemagick snmp ssl-cert-check" 56 | 57 | PERL_MODULES="liblwp-protocol-https-perl libdbi-perl libconfig-inifiles-perl libdbd-mysql-perl libterm-readkey-perl" 58 | 59 | PHP_PACKAGES=(cli fpm common mysql zip lz4 gd mbstring curl xml bcmath intl ldap soap oauth apcu) 60 | ################################################################################### 61 | ### COLORS ### 62 | ################################################################################### 63 | RED="\e[31;40m" 64 | GREEN="\e[32;40m" 65 | YELLOW="\e[33;40m" 66 | WHITE="\e[37;40m" 67 | BLUE="\e[0;34m" 68 | ### Background 69 | DGREYBG=" \e[100m" 70 | BLUEBG=" \e[1;44m" 71 | REDBG=" \e[41m" 72 | ### Styles 73 | BOLD="\e[1m" 74 | ### Reset 75 | RESET="\e[0m" 76 | ################################################################################### 77 | ### ECHO MESSAGES DESIGN ### 78 | ################################################################################### 79 | WHITETXT () { 80 | MESSAGE=${@:-"${RESET}Error: No message passed"} 81 | echo -e " ${WHITE}${MESSAGE}${RESET}" 82 | } 83 | BLUETXT () { 84 | MESSAGE=${@:-"${RESET}Error: No message passed"} 85 | echo -e " ${BLUE}${MESSAGE}${RESET}" 86 | } 87 | REDTXT () { 88 | MESSAGE=${@:-"${RESET}Error: No message passed"} 89 | echo -e " ${RED}${MESSAGE}${RESET}" 90 | } 91 | GREENTXT () { 92 | MESSAGE=${@:-"${RESET}Error: No message passed"} 93 | echo -e " ${GREEN}${MESSAGE}${RESET}" 94 | } 95 | YELLOWTXT () { 96 | MESSAGE=${@:-"${RESET}Error: No message passed"} 97 | echo -e " ${YELLOW}${MESSAGE}${RESET}" 98 | } 99 | BLUEBG () { 100 | MESSAGE=${@:-"${RESET}Error: No message passed"} 101 | echo -e "${BLUEBG}${MESSAGE}${RESET}" 102 | } 103 | pause () { 104 | read -p " $*" 105 | } 106 | _echo () { 107 | echo -en " $@" 108 | } 109 | 110 | PACKAGES_INSTALLED () { 111 | GREENTXT "Installed: " 112 | apt -qq list --installed $@ 2>/dev/null | awk '{print " " $0}' 113 | } 114 | ################################################################################### 115 | ### ARROW KEYS UP/DOWN MENU ### 116 | ################################################################################### 117 | updown_menu () { 118 | i=1;for items in $(echo $1); do item[$i]="${items}"; let i=$i+1; done 119 | i=1 120 | echo -e "\n Use up/down arrow keys then press [ Enter ] to select $2" 121 | while [ 0 ]; do 122 | if [ "$i" -eq 0 ]; then i=1; fi 123 | if [ ! "${item[$i]}" ]; then let i=i-1; fi 124 | echo -en "\r " 125 | echo -en "\r${item[$i]}" 126 | read -sn 1 selector 127 | case "${selector}" in 128 | "B") let i=i+1;; 129 | "A") let i=i-1;; 130 | "") echo; read -sn 1 -p " [?] To confirm [ "$(echo -e $BOLD${item[$i]}$RESET)" ] press "$(echo -e $BOLD$GREEN"y"$RESET)" or "$(echo -e $BOLD$RED"n"$RESET)" for new selection" confirm 131 | if [[ "${confirm}" =~ ^[Yy]$ ]]; then 132 | printf -v "$2" '%s' "${item[$i]}" 133 | break 134 | else 135 | echo 136 | echo -e "\n Use up/down arrow keys then press [ Enter ] to select $2" 137 | fi 138 | ;; 139 | esac 140 | done } 141 | ################################################################################### 142 | ### CHECK IF ROOT AND CREATE DATABASE TO SAVE ALL SETTINGS ### 143 | ################################################################################### 144 | echo "" 145 | echo "" 146 | # root? 147 | if [[ ${EUID} -ne 0 ]]; then 148 | echo 149 | REDTXT "[!] This script must be run as root user!" 150 | YELLOWTXT "[!] Login as root and run this script again." 151 | exit 1 152 | else 153 | GREENTXT "PASS: ROOT!" 154 | fi 155 | 156 | # Config path 157 | MAGENX_CONFIG_PATH="/opt/magenx/config" 158 | if [ ! -d "${MAGENX_CONFIG_PATH}" ]; then 159 | mkdir -p ${MAGENX_CONFIG_PATH} 160 | fi 161 | 162 | # SQLite check, create database path and command 163 | if ! which sqlite3 >/dev/null; then 164 | echo "" 165 | YELLOWTXT "[!] SQLite is not installed on this system!" 166 | YELLOWTXT "[!] Installing..." 167 | echo "" 168 | echo "" 169 | apt update 170 | apt -y install sqlite3 171 | fi 172 | 173 | SQLITE3_DB="magenx.db" 174 | SQLITE3_DB_PATH="${MAGENX_CONFIG_PATH}/${SQLITE3_DB}" 175 | SQLITE3="sqlite3 ${SQLITE3_DB_PATH}" 176 | if [ ! -f "${SQLITE3_DB_PATH}" ]; then 177 | ${SQLITE3} "" "" 178 | 179 | # Create base tables to save configuration 180 | ${SQLITE3} "CREATE TABLE IF NOT EXISTS system( 181 | machine_id text, 182 | distro_name text, 183 | distro_version text, 184 | web_stack text, 185 | timezone text, 186 | system_test text, 187 | ssh_port text, 188 | terms text, 189 | system_update text, 190 | php_version text, 191 | nginx_version text, 192 | mariadb_version text, 193 | webmin_password text, 194 | mysql_root_password text, 195 | opensearch_admin_password text 196 | );" 197 | 198 | ${SQLITE3} "CREATE TABLE IF NOT EXISTS magento( 199 | redis_password text, 200 | rabbitmq_password text, 201 | indexer_password text, 202 | version_installed text, 203 | domain text, 204 | owner text, 205 | php_user text, 206 | root_path text, 207 | database_host text, 208 | database_name text, 209 | database_user text, 210 | database_password text, 211 | admin_login text, 212 | admin_password text, 213 | admin_email text, 214 | locale text, 215 | admin_path text, 216 | crypt_key text, 217 | tfa_key text, 218 | private_ssh_key text, 219 | public_ssh_key text, 220 | github_actions_private_ssh_key text, 221 | github_actions_public_ssh_key text 222 | );" 223 | 224 | ${SQLITE3} "CREATE TABLE IF NOT EXISTS menu( 225 | lemp text, 226 | magento text, 227 | database text, 228 | install text, 229 | config text, 230 | webmin text 231 | );" 232 | 233 | ${SQLITE3} "INSERT INTO menu (lemp, magento, database, install, config, webmin) 234 | VALUES('-', '-', '-', '-', '-', '-', '-');" 235 | fi 236 | ################################################################################### 237 | ### CHECK IF WE CAN RUN IT ### 238 | ################################################################################### 239 | ## Ubuntu Debian 240 | ## Distro detectction 241 | distro_error() { 242 | echo "" 243 | REDTXT "[!] ${OS_NAME} ${OS_VERSION} detected" 244 | echo "" 245 | echo " Unfortunately, your operating system distribution and version are not supported by this script" 246 | echo " Supported: Ubuntu 22|24; Debian 12|13" 247 | echo " Please email admin@magenx.com and let us know if you run into any issues" 248 | echo "" 249 | exit 1 250 | } 251 | 252 | # Check if distribution name and version are already in the database 253 | DISTRO_INFO=($(${SQLITE3} -list -separator ' ' "SELECT distro_name, distro_version FROM system;")) 254 | if [ -n "${DISTRO_INFO[0]}" ]; then 255 | DISTRO_NAME="${DISTRO_INFO[0]}" 256 | DISTRO_VERSION="${DISTRO_INFO[1]}" 257 | GREENTXT "PASS: [ ${DISTRO_NAME} ${DISTRO_VERSION} ]" 258 | else 259 | # Detect distribution name and version 260 | if [ -f "/etc/os-release" ]; then 261 | . /etc/os-release 262 | DISTRO_NAME="${NAME}" 263 | DISTRO_VERSION="${VERSION_ID}" 264 | 265 | # Check if distribution is supported 266 | if [ "${DISTRO_NAME%% *}" == "Ubuntu" ] && [[ "${DISTRO_VERSION}" =~ ^(22.04|24.04) ]]; then 267 | DISTRO_NAME="Ubuntu" 268 | elif [ "${DISTRO_NAME%% *}" == "Debian" ] && [[ "${DISTRO_VERSION}" =~ ^(12|13) ]]; then 269 | DISTRO_NAME="Debian" 270 | else 271 | distro_error 272 | fi 273 | 274 | # Confirm distribution detection with user input 275 | echo "" 276 | _echo "${YELLOW}[?]${REDBG}${BOLD}[ ${DISTRO_NAME} ${DISTRO_VERSION} ]${RESET} ${YELLOW}detected correctly ? [y/n][n]: ${RESET}" 277 | read distro_detect 278 | if [ "${distro_detect}" = "y" ]; then 279 | echo "" 280 | GREENTXT "PASS: [ ${DISTRO_NAME} ${DISTRO_VERSION} ]" 281 | # Get machine id 282 | MACHINE_ID="$(cat /etc/machine-id)" 283 | ${SQLITE3} "INSERT INTO system (machine_id, distro_name, distro_version) VALUES ('${MACHINE_ID}', '${DISTRO_NAME}', '${DISTRO_VERSION}');" 284 | else 285 | distro_error 286 | fi 287 | else 288 | distro_error 289 | fi 290 | fi 291 | 292 | # network is up? 293 | host1=${MAGENX_BASE} 294 | host2=github.com 295 | 296 | RESULT=$(((ping -w3 -c2 ${host1} || ping -w3 -c2 ${host2}) > /dev/null 2>&1) && echo "up" || (echo "down" && exit 1)) 297 | if [[ ${RESULT} == up ]]; then 298 | GREENTXT "PASS: NETWORK IS UP. GREAT, LETS START!" 299 | else 300 | echo "" 301 | REDTXT "[!] Network is down ?" 302 | YELLOWTXT "[!] Please check your network settings." 303 | echo "" 304 | echo "" 305 | exit 1 306 | fi 307 | 308 | # install packages to run CPU and HDD test 309 | dpkg-query -l curl time bc bzip2 tar >/dev/null || { echo; echo; apt update -o Acquire::ForceIPv4=true; apt -y install curl time bc bzip2 tar; } 310 | 311 | # check if you need self update 312 | MD5_NEW=$(curl -sL ${MAGENX_BASE} > ${SELF}.new && md5sum ${SELF}.new | awk '{print $1}') 313 | MD5=$(md5sum ${SELF} | awk '{print $1}') 314 | if [[ "${MD5_NEW}" == "${MD5}" ]]; then 315 | GREENTXT "PASS: INTEGRITY CHECK FOR '${SELF}' OK" 316 | rm ${SELF}.new 317 | elif [[ "${MD5_NEW}" != "${MD5}" ]]; then 318 | echo "" 319 | YELLOWTXT "Integrity check for '${SELF}'" 320 | YELLOWTXT "detected different md5 checksum" 321 | YELLOWTXT "remote repository file has some changes" 322 | echo "" 323 | REDTXT "IF YOU HAVE LOCAL CHANGES REMOVE INTEGRITY CHECK OR SKIP UPDATES" 324 | echo "" 325 | _echo "[?] Would you like to update the file now? [y/n][y]: " 326 | read update_agree 327 | if [ "${update_agree}" == "y" ];then 328 | mv ${SELF}.new ${SELF} 329 | echo "" 330 | GREENTXT "The file has been upgraded, please run it again" 331 | echo "" 332 | exit 1 333 | else 334 | echo "" 335 | YELLOWTXT "New file saved to ${SELF}.new" 336 | echo 337 | fi 338 | fi 339 | 340 | # check if memory is enough 341 | TOTALMEM=$(awk '/MemTotal/{print $2}' /proc/meminfo | xargs -I {} echo "scale=4; {}/1024^2" | bc | xargs printf "%1.0f") 342 | if [ "${TOTALMEM}" -ge "4" ]; then 343 | GREENTXT "PASS: TOTAL RAM [${TOTALMEM}Gb]" 344 | else 345 | echo 346 | REDTXT "[!] Total RAM less than ${BOLD}4Gb" 347 | YELLOWTXT "[!] To run complete stack you need more RAM" 348 | echo 349 | fi 350 | 351 | # check if web stack is clean 352 | WEB_STACK=$(${SQLITE3} "SELECT web_stack FROM system;") 353 | if [ "${WEB_STACK}" != "magenx" ]; then 354 | installed_packages="$(apt -qq list --installed ${WEB_STACK_CHECK} 2> /dev/null | cut -d'/' -f1 | tr '\n' ' ')" 355 | if [ ! -z "$installed_packages" ]; then 356 | REDTXT "[!] Some webstack packages already installed" 357 | YELLOWTXT "[!] You need to remove them or reinstall minimal OS version" 358 | echo 359 | echo -e "\t\t apt -y remove ${installed_packages}" 360 | echo 361 | echo 362 | exit 1 363 | else 364 | # set web_stack clean 365 | ${SQLITE3} "UPDATE system SET web_stack = 'magenx';" 366 | fi 367 | fi 368 | 369 | # print path 370 | GREENTXT "PATH: ${PATH}" 371 | 372 | # configure system/magento timezone 373 | TIMEZONE="$(${SQLITE3} "SELECT timezone FROM system;")" 374 | if [ -z "${TIMEZONE}" ]; then 375 | echo "" 376 | echo "" 377 | YELLOWTXT "[!] Server and Magento timezone configuration:" 378 | echo "" 379 | pause "[] Press [Enter] key to proceed" 380 | echo "" 381 | dpkg-reconfigure tzdata 382 | TIMEZONE=$(timedatectl | awk '/Time zone:/ {print $3}') 383 | ${SQLITE3} "UPDATE system SET timezone = '${TIMEZONE}';" 384 | fi 385 | GREENTXT "TIMEZONE: ${TIMEZONE}" 386 | 387 | echo 388 | echo 389 | SYSTEM_TEST=$(${SQLITE3} "SELECT system_test FROM system;") 390 | if [ -z "${SYSTEM_TEST}" ]; then 391 | echo 392 | BLUEBG "~ QUICK SYSTEM TEST ~" 393 | WHITETXT "-------------------------------------------------------------------------------------" 394 | echo 395 | # run I/O and CPU tests 396 | TEST_FILE="TEST_FILE__$$" 397 | TAR_FILE="TAR_FILE" 398 | _echo "${YELLOW}[?] I/O PERFORMANCE${RESET}:" 399 | IO=$( ( dd if=/dev/zero of=${TEST_FILE} bs=64k count=16k conv=fdatasync && rm -f ${TEST_FILE} ) 2>&1 | awk -F, '{IO=$NF} END { print IO}' ) 400 | _echo ${IO} 401 | echo 402 | _echo "${YELLOW}[?] CPU PERFORMANCE${RESET}:" 403 | dd if=/dev/urandom of=${TAR_FILE} bs=1024 count=25000 >>/dev/null 2>&1 404 | CPU_TIME=$( (/usr/bin/time -f "%es" tar cfj ${TAR_FILE}.bz2 ${TAR_FILE}) 2>&1 ) 405 | rm -f ${TAR_FILE}* 406 | _echo ${CPU_TIME} 407 | echo 408 | echo 409 | echo 410 | # set system_test tested 411 | ${SQLITE3} "UPDATE system SET system_test = 'I/O:${IO} CPU:${CPU_TIME}';" 412 | echo 413 | pause "[] Press [Enter] key to proceed" 414 | echo 415 | fi 416 | echo 417 | 418 | # ssh port test 419 | SSH_PORT=$(${SQLITE3} "SELECT ssh_port FROM system;") 420 | if [ -z "${SSH_PORT}" ]; then 421 | echo "" 422 | OVERRIDE_DIR="/etc/ssh/sshd_config.d" 423 | echo "" 424 | YELLOWTXT "SSH config optimization:" 425 | tee ${OVERRIDE_DIR}/10-magenx-security.conf << 'EOF' 426 | LoginGraceTime 30 427 | MaxAuthTries 6 428 | X11Forwarding no 429 | PrintLastLog yes 430 | TCPKeepAlive yes 431 | ClientAliveInterval 600 432 | ClientAliveCountMax 3 433 | UseDNS no 434 | PrintMotd no 435 | #Subsystem sftp /usr/lib/openssh/sftp-server -l INFO 436 | EOF 437 | 438 | echo "" 439 | echo "" 440 | CURRENT_PORT=$(sshd -T | grep '^port ' | awk '{print $2}') 441 | if [ "${CURRENT_PORT}" = "22" ]; then 442 | SSH_PORT=$(shuf -i 9537-9554 -n 1) 443 | tee ${OVERRIDE_DIR}/20-magenx-custom-port.conf << EOF 444 | Port ${SSH_PORT} 445 | EOF 446 | echo "Changed SSH port from 22 to ${SSH_PORT}" 447 | fi 448 | 449 | chmod 600 ${OVERRIDE_DIR}/*magenx*.conf 450 | 451 | systemctl restart sshd.service 452 | echo 453 | GREENTXT "SSH configurations were updated - OK" 454 | echo 455 | GREENTXT "[!] SSH Port: ${SSH_PORT}" 456 | echo 457 | systemctl restart sshd.service 458 | ss -tlp | grep sshd 459 | echo 460 | echo 461 | REDTXT "[!] IMPORTANT: Now open new SSH session with the new port!" 462 | REDTXT "[!] IMPORTANT: Do not close your current session!" 463 | echo 464 | _echo "[?] Have you logged in another session? [y/n][n]: " 465 | read ssh_test 466 | if [ "${ssh_test}" == "y" ]; then 467 | echo 468 | GREENTXT "[!] SSH Port: ${SSH_PORT}" 469 | echo 470 | ${SQLITE3} "UPDATE system SET ssh_port = '${SSH_PORT}';" 471 | echo 472 | echo 473 | pause "[] Press [Enter] key to proceed" 474 | else 475 | echo 476 | rm ${OVERRIDE_DIR}/*magenx*.conf 477 | REDTXT "Restoring sshd_config file back to defaults ${GREEN} [ok]" 478 | systemctl restart sshd.service 479 | echo 480 | GREENTXT "SSH configuration has been restored - OK" 481 | ss -tlp | grep sshd 482 | fi 483 | fi 484 | 485 | # Enter domain name and ssh user per environment 486 | DOMAIN=($(${SQLITE3} "SELECT domain FROM magento;")) 487 | if [ "${DOMAIN}" = "" ]; then 488 | echo "" 489 | echo "" 490 | echo "" 491 | read -e -p "$(echo -e ${YELLOW}" [?] Store domain name: "${RESET})" -i "yourdomain.tld" DOMAIN 492 | read -e -p "$(echo -e ${YELLOW}" [?] Files owner/SSH user: "${RESET})" -i "${DOMAIN//[-.]/}" OWNER 493 | 494 | ${SQLITE3} "INSERT INTO magento (domain, owner, php_user, root_path) VALUES ( '${DOMAIN}', '${OWNER}', 'php-${OWNER}', '/home/${OWNER}/public_html' );" 495 | else 496 | GREENTXT "DOMAIN: ${DOMAIN}" 497 | fi 498 | echo "" 499 | echo "" 500 | ################################################################################### 501 | ### AGREEMENT ### 502 | ################################################################################### 503 | echo "" 504 | TERMS=$(${SQLITE3} "SELECT terms FROM system;") 505 | if [ "${TERMS}" != "agreed" ]; then 506 | echo 507 | YELLOWTXT "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" 508 | echo 509 | YELLOWTXT "BY INSTALLING THIS SOFTWARE AND BY USING ANY AND ALL SOFTWARE" 510 | YELLOWTXT "YOU ACKNOWLEDGE AND AGREE:" 511 | echo 512 | YELLOWTXT "THIS SOFTWARE AND ALL SOFTWARE PROVIDED IS PROVIDED AS IS" 513 | YELLOWTXT "UNSUPPORTED AND WE ARE NOT RESPONSIBLE FOR ANY DAMAGE" 514 | echo 515 | YELLOWTXT "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" 516 | echo 517 | echo 518 | _echo "[?] Do you agree to these terms ? [y/n][y]: " 519 | read terms_agree 520 | if [ "${terms_agree}" == "y" ]; then 521 | # set terms agreed 522 | ${SQLITE3} "UPDATE system SET terms = 'agreed';" 523 | else 524 | REDTXT "Going out." 525 | echo 526 | exit 1 527 | fi 528 | fi 529 | ################################################################################### 530 | ### MAIN MENU ### 531 | ################################################################################### 532 | showMenu () { 533 | MENU_CHECK=($(${SQLITE3} -list -separator ' ' "SELECT lemp, magento, database, install, config, webmin FROM menu;")) 534 | printf "\033c" 535 | echo "" 536 | echo "" 537 | echo -e "${DGREYBG}${BOLD} MAGENTO SERVER CONFIGURATION v.${MAGENX_VERSION} ${RESET}" 538 | BLUETXT ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" 539 | echo "" 540 | WHITETXT "[${MENU_CHECK[0]}] Install repository and LEMP packages : ${YELLOW}\tlemp" 541 | WHITETXT "[${MENU_CHECK[1]}] Download Magento latest version : ${YELLOW}\tmagento" 542 | WHITETXT "[${MENU_CHECK[2]}] Setup Magento database : ${YELLOW}\tdatabase" 543 | WHITETXT "[${MENU_CHECK[3]}] Install Magento no sample data : ${YELLOW}\tinstall" 544 | WHITETXT "[${MENU_CHECK[4]}] Post-Installation config : ${YELLOW}\tconfig" 545 | echo "" 546 | BLUETXT ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" 547 | echo "" 548 | WHITETXT "[${MENU_CHECK[6]}] Install Webmin control panel : ${YELLOW}\twebmin" 549 | echo "" 550 | BLUETXT ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" 551 | echo "" 552 | WHITETXT "[-] To quit and exit : ${RED}\texit" 553 | echo "" 554 | echo "" 555 | } 556 | while [ 1 ] 557 | do 558 | showMenu 559 | read CHOICE 560 | case "${CHOICE}" in 561 | "lemp") 562 | echo "" 563 | echo "" 564 | ################################################################################### 565 | ### SYSTEM UPGRADE ### 566 | ################################################################################### 567 | # Get distro_name to make sure its set 568 | DISTRO_NAME=$(${SQLITE3} "SELECT distro_name FROM system;") 569 | 570 | # check if system update still required 571 | SYSTEM_UPDATE=$(${SQLITE3} "SELECT system_update FROM system;") 572 | if [ -z "${SYSTEM_UPDATE}" ]; then 573 | ## install all extra packages 574 | echo "" 575 | BLUEBG "[~] SYSTEM UPDATE AND PACKAGES INSTALLATION [~]" 576 | WHITETXT "-------------------------------------------------------------------------------------" 577 | echo "" 578 | debconf-set-selections <<< "postfix postfix/mailname string localhost" 579 | debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Local only'" 580 | apt update && apt upgrade -y 581 | apt -y install software-properties-common 582 | apt-add-repository -y contrib 583 | apt update 584 | DEBIAN_FRONTEND=noninteractive apt -y install ${EXTRA_PACKAGES} ${PERL_MODULES} 585 | echo "" 586 | if [ "$?" != 0 ]; then 587 | echo "" 588 | REDTXT "[!] Installation error." 589 | REDTXT "[!] Please correct errors and run it again." 590 | exit 1 591 | echo "" 592 | fi 593 | # Set system_update to full release version 594 | [ "${DISTRO_NAME}" == "Debian" ] && FULL_VERSION="$(cat /etc/debian_version )" || FULL_VERSION="$(lsb_release -d | awk '/(20|22)\.04.+/{print $3}')" 595 | ${SQLITE3} "UPDATE system SET system_update = 'installed @ ${FULL_VERSION}';" 596 | echo "" 597 | fi 598 | echo "" 599 | echo "" 600 | BLUEBG "[~] LEMP WEB STACK INSTALLATION [~]" 601 | WHITETXT "-------------------------------------------------------------------------------------" 602 | echo "" 603 | echo "" 604 | _echo "${YELLOW}[?] Install MariaDB ${MARIADB_VERSION} database ? [y/n][n]:${RESET} " 605 | read mariadb_install 606 | if [ "${mariadb_install}" == "y" ]; then 607 | echo "" 608 | read -e -p "$(echo -e ${YELLOW}" [?] Enter required MARIADB version: "${RESET})" -i "${MARIADB_VERSION}" MARIADB_VERSION 609 | # Set mariadb-server-version 610 | ${SQLITE3} "UPDATE system SET mariadb_version = '${MARIADB_VERSION}';" 611 | curl -LsS "${MARIADB_REPO_CONFIG}" | bash -s -- --mariadb-server-version="mariadb-${MARIADB_VERSION}" --skip-maxscale --skip-verify --skip-eol-check 612 | echo "" 613 | if [ "$?" = 0 ] # if repository installed then install package 614 | then 615 | echo "" 616 | GREENTXT "MariaDB repository installed - OK" 617 | echo "" 618 | YELLOWTXT "MariaDB ${MARIADB_VERSION} database installation:" 619 | echo "" 620 | apt update 621 | systemctl stop mariadb 622 | apt install -y mariadb-server 623 | if [ "$?" = 0 ] # if package installed then configure 624 | then 625 | echo "" 626 | GREENTXT "MariaDB installed - OK" 627 | echo "" 628 | systemctl enable mariadb 629 | echo "" 630 | PACKAGES_INSTALLED mariadb* 631 | echo "127.0.0.1 mariadb" >> /etc/hosts 632 | echo "" 633 | WHITETXT "Downloading my.cnf file from MagenX Github repository" 634 | curl -sSo /etc/my.cnf https://raw.githubusercontent.com/magenx/magento-mysql/master/my.cnf/my.cnf 635 | echo "" 636 | WHITETXT "[?] Calculating [innodb_buffer_pool_size]:" 637 | INNODB_BUFFER_POOL_SIZE=$(echo "0.5*$(awk '/MemTotal/ { print $2 / (1024*1024)}' /proc/meminfo | cut -d'.' -f1)" | bc | xargs printf "%1.0f") 638 | if [ "${INNODB_BUFFER_POOL_SIZE}" == "0" ]; then IBPS=1; fi 639 | sed -i "s/innodb_buffer_pool_size = 4G/innodb_buffer_pool_size = ${INNODB_BUFFER_POOL_SIZE}G/" /etc/my.cnf 640 | ##sed -i "s/innodb_buffer_pool_instances = 4/innodb_buffer_pool_instances = ${INNODB_BUFFER_POOL_SIZE}/" /etc/my.cnf 641 | echo "" 642 | WHITETXT "innodb_buffer_pool_size = ${INNODB_BUFFER_POOL_SIZE}G" 643 | WHITETXT "innodb_buffer_pool_instances = ${INNODB_BUFFER_POOL_SIZE}" 644 | echo "" 645 | else 646 | echo "" 647 | REDTXT "MariaDB installation error" 648 | exit # if package is not installed then exit 649 | fi 650 | else 651 | echo "" 652 | REDTXT "MariaDB repository installation error" 653 | exit # if repository is not installed then exit 654 | fi 655 | else 656 | echo 657 | YELLOWTXT "MariaDB installation was skipped by user input. Proceeding to next step." 658 | fi 659 | echo 660 | WHITETXT "=============================================================================" 661 | echo 662 | _echo "${YELLOW}[?] Install Nginx ${NGINX_VERSION} ? [y/n][n]:${RESET} " 663 | read nginx_install 664 | if [ "${nginx_install}" == "y" ]; then 665 | echo "" 666 | read -e -p "$(echo -e ${YELLOW}" [?] Enter required NGINX version: "${RESET})" -i "${NGINX_VERSION}" NGINX_VERSION 667 | # Set nginx version 668 | ${SQLITE3} "UPDATE system SET nginx_version = '${NGINX_VERSION}';" 669 | echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/mainline/${DISTRO_NAME,,} `lsb_release -cs` nginx" | tee /etc/apt/sources.list.d/nginx.list 670 | curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null 671 | echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" | tee /etc/apt/preferences.d/99nginx 672 | if [ "$?" = 0 ]; then # if repository installed then install package 673 | echo 674 | GREENTXT "Nginx repository installed - OK" 675 | echo 676 | YELLOWTXT "Nginx ${NGINX_VERSION} installation:" 677 | echo 678 | apt update 679 | apt -y install nginx nginx-module-perl nginx-module-image-filter 680 | if [ "$?" = 0 ]; then 681 | echo 682 | GREENTXT "Nginx ${NGINX_VERSION} installed - OK" 683 | echo 684 | systemctl enable nginx >/dev/null 2>&1 685 | PACKAGES_INSTALLED nginx* 686 | else 687 | echo 688 | REDTXT "Nginx ${NGINX_VERSION} installation error" 689 | exit # if package is not installed then exit 690 | fi 691 | else 692 | echo 693 | REDTXT "Nginx repository installation error" 694 | exit 695 | fi 696 | else 697 | echo 698 | YELLOWTXT "Nginx installation was skipped by user input. Proceeding to next step." 699 | fi 700 | echo 701 | WHITETXT "=============================================================================" 702 | echo 703 | _echo "${YELLOW}[?] Install PHP ? [y/n][n]:${RESET} " 704 | read php_install 705 | if [ "${php_install}" == "y" ]; then 706 | echo "" 707 | read -e -p "$(echo -e ${YELLOW}" [?] Enter required PHP version: "${RESET})" -i "${PHP_VERSION}" PHP_VERSION 708 | # Set php_version 709 | ${SQLITE3} "UPDATE system SET php_version = '${PHP_VERSION}';" 710 | echo "" 711 | if [ "${DISTRO_NAME}" == "Debian" ]; then 712 | curl -o /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg 713 | echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list 714 | else 715 | add-apt-repository ppa:ondrej/php -y 716 | fi 717 | if [ "$?" = 0 ]; then 718 | echo "" 719 | GREENTXT "PHP repository installed - OK" 720 | echo "" 721 | echo "" 722 | YELLOWTXT "PHP ${PHP_VERSION} installation:" 723 | echo 724 | apt update 725 | apt -y install php${PHP_VERSION} ${PHP_PACKAGES[@]/#/php${PHP_VERSION}-} php-pear 726 | if [ "$?" = 0 ]; then 727 | echo "" 728 | GREENTXT "PHP ${PHP_VERSION} installed - OK" 729 | echo "" 730 | PACKAGES_INSTALLED php${PHP_VERSION}* 731 | echo "" 732 | # composer download 733 | echo "" 734 | YELLOWTXT "Composer ${COMPOSER_VERSION} installation:" 735 | echo "" 736 | php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 737 | php composer-setup.php --${COMPOSER_VERSION} --install-dir=/usr/bin --filename=composer 738 | php -r "unlink('composer-setup.php');" 739 | else 740 | echo "" 741 | REDTXT "PHP installation error" 742 | exit 1 # if package is not installed then exit 743 | fi 744 | else 745 | echo 746 | REDTXT "PHP repository installation error" 747 | exit 1 # if repository is not installed then exit 748 | fi 749 | else 750 | echo 751 | YELLOWTXT "PHP installation was skipped by user input. Proceeding to next step." 752 | fi 753 | echo 754 | echo 755 | WHITETXT "=============================================================================" 756 | echo 757 | _echo "${YELLOW}[?] Install Redis ${REDIS_VERSION} ? [y/n][n]:${RESET} " 758 | read redis_install 759 | if [ "${redis_install}" == "y" ]; then 760 | curl -fL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg 761 | echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/redis.list 762 | if [ "$?" = 0 ]; then # 763 | echo 764 | GREENTXT "Redis repository installed - OK" 765 | echo 766 | YELLOWTXT "Redis installation:" 767 | echo 768 | apt update 769 | apt -y install redis 770 | echo "" 771 | if [ "$?" = 0 ]; then 772 | echo "" 773 | GREENTXT "Redis installed - OK" 774 | echo "" 775 | PACKAGES_INSTALLED redis-server* 776 | echo "" 777 | echo "" 778 | YELLOWTXT "Redis configuration per environment:" 779 | echo "" 780 | 781 | systemctl stop redis-server 782 | systemctl disable redis-server 783 | 784 | # Create Redis config 785 | cat > /etc/systemd/system/redis@.service < /etc/redis/${SERVICE}.conf<> /etc/hosts 898 | 899 | systemctl daemon-reload 900 | systemctl enable redis@${SERVICE} 901 | systemctl restart redis@${SERVICE} 902 | done 903 | else 904 | echo 905 | REDTXT "Redis installation error" 906 | exit 1 # if package is not installed then exit 907 | fi 908 | else 909 | echo 910 | REDTXT "Redis repository installation error" 911 | exit 1 912 | fi 913 | else 914 | echo 915 | YELLOWTXT "Redis installation was skipped by user input. Proceeding to next step." 916 | fi 917 | echo 918 | WHITETXT "=============================================================================" 919 | echo 920 | echo 921 | _echo "${YELLOW}[?] Install RabbitMQ ${RABBITMQ_VERSION} ? [y/n][n]:${RESET} " 922 | read rabbitmq_install 923 | if [ "${rabbitmq_install}" == "y" ];then 924 | curl -1sLf 'https://dl.cloudsmith.io/public/rabbitmq/rabbitmq-server/setup.deb.sh' | bash 925 | curl -1sLf 'https://dl.cloudsmith.io/public/rabbitmq/rabbitmq-erlang/setup.deb.sh' | bash 926 | cat > /etc/apt/preferences.d/erlang <> /etc/hosts 945 | echo "" 946 | echo "" 947 | YELLOWTXT "RabbitMQ ${RABBITMQ_VERSION} configuration per environment:" 948 | echo "" 949 | systemctl stop rabbitmq-server 950 | systemctl stop epmd* 951 | epmd -kill 952 | 953 | cat > /etc/rabbitmq/rabbitmq.conf < /etc/rabbitmq/rabbitmq-env.conf < /etc/rabbitmq/advanced.config <<'END' 969 | [{kernel, [ 970 | % Bind Erlang distribution to localhost only 971 | {inet_dist_use_interface, {127,0,0,1}}, 972 | % Fix Erlang distribution port range 973 | {inet_dist_listen_min, 25672}, 974 | {inet_dist_listen_max, 25672} 975 | ]} 976 | ]. 977 | END 978 | 979 | cat >> /etc/sysctl.conf < /etc/systemd/system/epmd.service < /etc/systemd/system/epmd.socket < /etc/varnish/secret 1069 | systemctl daemon-reload 1070 | PACKAGES_INSTALLED varnish* 1071 | echo "127.0.0.1 varnish" >> /etc/hosts 1072 | else 1073 | echo "" 1074 | REDTXT "Varnish Cache installation error" 1075 | exit 1 1076 | fi 1077 | else 1078 | echo "" 1079 | REDTXT "Varnish Cache repository installation error" 1080 | exit 1 1081 | fi 1082 | else 1083 | echo "" 1084 | YELLOWTXT "Varnish installation was skipped by user input. Proceeding to next step." 1085 | fi 1086 | echo 1087 | WHITETXT "=============================================================================" 1088 | echo 1089 | _echo "${YELLOW}[?] Install OpenSearch ${OPENSEARCH_VERSION} ? [y/n][n]:${RESET} " 1090 | read opensearch_install 1091 | if [ "${opensearch_install}" == "y" ];then 1092 | curl -o- https://artifacts.opensearch.org/publickeys/opensearch.pgp | gpg --dearmor --batch --yes -o /usr/share/keyrings/opensearch-keyring 1093 | echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring] https://artifacts.opensearch.org/releases/bundle/opensearch/${OPENSEARCH_VERSION}/apt stable main" > /etc/apt/sources.list.d/opensearch-${OPENSEARCH_VERSION}.list 1094 | if [ "$?" = 0 ]; then 1095 | echo "" 1096 | GREENTXT "OpenSearch ${OPENSEARCH_VERSION} repository installed - OK" 1097 | echo "" 1098 | YELLOWTXT "OpenSearch ${OPENSEARCH_VERSION} installation:" 1099 | echo "" 1100 | YELLOWTXT "Re-generating random password for admin user" 1101 | if [[ -z "$(${SQLITE3} "SELECT opensearch_admin_password FROM system;")" ]]; then 1102 | OPENSEARCH_ADMIN_PASSWORD="$(head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9-#?$&' | fold -w 20 | head -n 1)" 1103 | ${SQLITE3} "UPDATE system SET opensearch_admin_password = '${OPENSEARCH_ADMIN_PASSWORD}';" 1104 | fi 1105 | 1106 | apt update 1107 | OPENSEARCH_ADMIN_PASSWORD="$(${SQLITE3} "SELECT opensearch_admin_password FROM system;")" 1108 | env OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} apt -y install opensearch 1109 | 1110 | YELLOWTXT "OpenSearch pre-configuration:" 1111 | echo "" 1112 | ## opensearch settings 1113 | OWNER=$(${SQLITE3} "SELECT owner FROM magento LIMIT 1;") 1114 | if ! grep -q "${OWNER}" /etc/opensearch/opensearch.yml >/dev/null 2>&1 ; then 1115 | cp /etc/opensearch/opensearch.yml /etc/opensearch/opensearch.yml_default 1116 | cat > /etc/opensearch/opensearch.yml < /etc/opensearch/jvm.options.d/${parameter["BRAND"]}.options 1162 | -Xms${HEAP_SIZE}g 1163 | -Xmx${HEAP_SIZE}g 1164 | END 1165 | 1166 | fi 1167 | 1168 | if [ "$?" = 0 ]; then 1169 | echo "" 1170 | GREENTXT "OpenSearch ${OPENSEARCH_VERSION} installed - OK" 1171 | echo "" 1172 | 1173 | chown -R :opensearch /etc/opensearch/* 1174 | systemctl daemon-reload 1175 | systemctl enable opensearch.service 1176 | systemctl restart opensearch.service 1177 | 1178 | if [ "$?" != 0 ]; then 1179 | echo "" 1180 | REDTXT "[!] OpenSearch startup error" 1181 | REDTXT "[!] Please correct error above and try again" 1182 | echo "" 1183 | exit 1 1184 | fi 1185 | 1186 | # generate opensearch password 1187 | INDEXER_PASSWORD="$(head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1)" 1188 | ${SQLITE3} "UPDATE magento SET indexer_password = '${INDEXER_PASSWORD}';" 1189 | OWNER=$(${SQLITE3} "SELECT owner FROM magento;") 1190 | echo "" 1191 | YELLOWTXT "Waiting for OpenSearch initialization ..." 1192 | timeout 10 sh -c 'until nc -z $0 $1; do sleep 1; done' 127.0.0.1 9200 1193 | curl -XGET -u admin:${OPENSEARCH_ADMIN_PASSWORD} "http://127.0.0.1:9200/_cluster/health?wait_for_status=green&timeout=60s" 1194 | sleep 5 1195 | 1196 | # Create role 1197 | curl -u admin:${OPENSEARCH_ADMIN_PASSWORD} -XPUT "http://127.0.0.1:9200/_plugins/_security/api/roles/${OWNER}" \ 1198 | -H "Content-Type: application/json" \ 1199 | -d "$(cat <> /etc/hosts 1252 | else 1253 | echo "" 1254 | REDTXT "OpenSearch ${OPENSEARCH_VERSION} installation error" 1255 | exit 1 1256 | fi 1257 | else 1258 | echo "" 1259 | REDTXT "OpenSearch ${OPENSEARCH_VERSION} repository installation error" 1260 | exit 1 1261 | fi 1262 | else 1263 | echo "" 1264 | YELLOWTXT "OpenSearch ${OPENSEARCH_VERSION} installation was skipped by user input. Proceeding to next step." 1265 | fi 1266 | echo "" 1267 | echo "" 1268 | ${SQLITE3} "UPDATE menu SET lemp = 'x';" 1269 | ## keep versions for critical services to avoid issues 1270 | apt-mark hold opensearch erlang rabbitmq-server 1271 | echo "" 1272 | echo "" 1273 | GREENTXT "~ REPOSITORIES AND PACKAGES INSTALLATION IS COMPLETED ~" 1274 | WHITETXT "-------------------------------------------------------------------------------------" 1275 | echo "" 1276 | echo "" 1277 | pause '[] Press [Enter] key to show the menu' 1278 | printf "\033c" 1279 | ;; 1280 | ################################################################################### 1281 | ### MAGENTO DOWNLOAD ### 1282 | ################################################################################### 1283 | "magento") 1284 | printf "\033c" 1285 | echo 1286 | BLUEBG "[~] MAGENTO 2 CONFIGURATION [~]" 1287 | WHITETXT "-------------------------------------------------------------------------------------" 1288 | echo "" 1289 | # configure 1290 | DOMAIN="$(${SQLITE3} "SELECT domain FROM magento;")" 1291 | OWNER="$(${SQLITE3} "SELECT owner FROM magento;")" 1292 | PHP_USER="$(${SQLITE3} "SELECT php_user FROM magento;")" 1293 | ROOT_PATH="$(${SQLITE3} "SELECT root_path FROM magento;")" 1294 | ## create magento/ssh user 1295 | useradd -d ${ROOT_PATH%/*} -s /bin/bash ${OWNER} 1296 | mkdir -p ${ROOT_PATH} 1297 | ## create magento php user 1298 | useradd -M -s /sbin/nologin -d ${ROOT_PATH%/*} ${PHP_USER} 1299 | usermod -g ${PHP_USER} ${OWNER} 1300 | chmod 711 ${ROOT_PATH%/*} 1301 | chown -R ${OWNER}:${PHP_USER} ${ROOT_PATH} 1302 | # magento root folder permissions 1303 | chmod 2750 ${ROOT_PATH} 1304 | setfacl -R -m m:r-X,u:${OWNER}:rwX,g:${PHP_USER}:r-X,o::-,d:u:${OWNER}:rwX,d:g:${PHP_USER}:r-X,d:o::- ${ROOT_PATH} 1305 | setfacl -R -m u:nginx:r-X,d:u:nginx:r-X ${ROOT_PATH} 1306 | 1307 | cd ${ROOT_PATH} 1308 | echo "" 1309 | _echo "[?] Download Magento 2 ? [y/n][n]: " 1310 | read download_magento 1311 | if [ "${download_magento}" == "y" ];then 1312 | echo "" 1313 | echo "" 1314 | YELLOWTXT "[?] Select Magento full version: " 1315 | updown_menu "${VERSION_LIST}" VERSION_INSTALLED 1316 | echo "" 1317 | echo "" 1318 | echo " [!] Magento [ ${VERSION_INSTALLED} ]" 1319 | echo " [!] Downloading to [ ${ROOT_PATH} ]" 1320 | echo "" 1321 | echo "" 1322 | pause '[] Press [Enter] key to start downloading' 1323 | echo "" 1324 | ## create some dirs and files 1325 | touch ${ROOT_PATH%/*}/{.bashrc,.bash_profile,.bash_history} 1326 | mkdir -p ${ROOT_PATH%/*}/{.config,.cache,.local,.composer,.nvm} 1327 | chmod 2700 ${ROOT_PATH%/*}/{.config,.cache,.local,.composer,.nvm} 1328 | chmod 600 ${ROOT_PATH%/*}/{.bashrc,.bash_profile,.bash_history} 1329 | chown -R ${OWNER}:${OWNER} ${ROOT_PATH%/*}/{.config,.cache,.local,.composer,.nvm,.bashrc,.bash_profile,.bash_history} 1330 | ## 1331 | 1332 | su ${OWNER} -s /bin/bash -c "composer -n -q config -g http-basic.repo.magento.com ${COMPOSER_NAME} ${COMPOSER_PASSWORD}" 1333 | su ${OWNER} -s /bin/bash -c "${PROJECT}=${VERSION_INSTALLED} . --no-install" 1334 | 1335 | # composer replace bloatware 1336 | curl -sO ${MAGENX_INSTALL_GITHUB_REPO}/composer_replace 1337 | sed -i '/"conflict":/ { 1338 | r composer_replace 1339 | N 1340 | }' composer.json 1341 | 1342 | rm composer_replace 1343 | 1344 | ### install magento from here ### 1345 | su ${OWNER} -s /bin/bash -c "composer install" 1346 | 1347 | if [ "$?" != 0 ]; then 1348 | echo "" 1349 | REDTXT "[!] Magento composer installation error" 1350 | REDTXT "[!] Please correct error above and try again" 1351 | echo "" 1352 | exit 1 1353 | fi 1354 | 1355 | # make magento great again 1356 | sed -i "s/\[2-6\]/(1\[0-3\]\|\[2-9\])/" app/etc/di.xml 1357 | fi 1358 | 1359 | # reset permissions 1360 | su ${OWNER} -s /bin/bash -c "echo 007 > magento_umask" 1361 | su ${OWNER} -s /bin/bash -c "mkdir -p generated pub/static var pub/media" 1362 | su ${OWNER} -s /bin/bash -c "mkdir -p var/tmp" 1363 | setfacl -R -m u:${OWNER}:rwX,g:${PHP_USER}:rwX,o::-,d:u:${OWNER}:rwX,d:g:${PHP_USER}:rwX,d:o::- var pub/media 1364 | 1365 | # save all the variables 1366 | ${SQLITE3} "UPDATE menu SET magento = 'x';" 1367 | ${SQLITE3} "UPDATE magento SET version_installed = '${VERSION_INSTALLED}';" 1368 | 1369 | echo "" 1370 | echo "" 1371 | echo "" 1372 | GREENTXT "[~] MAGENTO ${VERSION_INSTALLED} DOWNLOADED AND READY FOR SETUP [~]" 1373 | WHITETXT "--------------------------------------------------------------------" 1374 | echo 1375 | echo 1376 | pause '[] Press [Enter] key to show menu' 1377 | printf "\033c" 1378 | ;; 1379 | ################################################################################### 1380 | ### DATABASE SETUP ### 1381 | ################################################################################### 1382 | "database") 1383 | printf "\033c" 1384 | echo 1385 | BLUEBG "[~] CREATE MYSQL USER AND DATABASE [~]" 1386 | WHITETXT "-------------------------------------------------------------------------------------" 1387 | if [ ! -f /root/.my.cnf ]; then 1388 | MYSQL_ROOT_PASSWORD="$(head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9@%^&?=+_[]{}()<>-' | fold -w 15 | head -n 1)${RANDOM}" 1389 | systemctl restart mariadb 1390 | mariadb-admin status --wait=2 &>/dev/null || { REDTXT "\n [!] MYSQL SERVER DOWN \n"; exit 1; } 1391 | mariadb --connect-expired-password < /root/.my.cnf < /root/.mytop < /root/.my.cnf <-' | fold -w 15 | head -n 1)${RANDOM}" DATABASE_PASSWORD 1431 | echo "" 1432 | for USER_HOST in ${DATABASE_HOST} localhost 127.0.0.1 1433 | do 1434 | mariadb < 3 {print $NF}' | sort )" LOCALE 1502 | echo "" 1503 | updown_menu "$(bin/magento info:currency:list | sed "s/[|+-]//g" | awk 'NR > 3 {print $NF}' | sort )" CURRENCY 1504 | echo "" 1505 | echo "" 1506 | YELLOWTXT "[-] Magento ${GET_[version_installed]} ready to be installed" 1507 | echo "" 1508 | pause '[!] Press [Enter] key to run setup:install' 1509 | echo 1510 | su ${GET_[owner]} -s /bin/bash -c "bin/magento setup:install --base-url=https://${GET_[domain]}/ \ 1511 | --db-host=${GET_[database_host]} \ 1512 | --db-name=${GET_[database_name]} \ 1513 | --db-user=${GET_[database_user]} \ 1514 | --db-password='${GET_[database_password]}' \ 1515 | --admin-firstname=${ADMIN_FIRSTNAME} \ 1516 | --admin-lastname=${ADMIN_LASTNAME} \ 1517 | --admin-email=${ADMIN_EMAIL} \ 1518 | --admin-user=${ADMIN_LOGIN} \ 1519 | --admin-password='${ADMIN_PASSWORD}' \ 1520 | --language=${LOCALE} \ 1521 | --currency=${CURRENCY} \ 1522 | --timezone=${TIMEZONE} \ 1523 | --cleanup-database \ 1524 | --use-rewrites=1 \ 1525 | --session-save=redis \ 1526 | --session-save-redis-host=session \ 1527 | --session-save-redis-port=$(awk '/port /{print $2}' /etc/redis/session.conf) \ 1528 | --session-save-redis-log-level=3 \ 1529 | --session-save-redis-db=0 \ 1530 | --session-save-redis-password='${GET_[redis_password]}' \ 1531 | --session-save-redis-compression-lib=lz4 \ 1532 | --cache-backend=redis \ 1533 | --cache-backend-redis-server=cache \ 1534 | --cache-backend-redis-port=$(awk '/port /{print $2}' /etc/redis/cache.conf) \ 1535 | --cache-backend-redis-db=0 \ 1536 | --cache-backend-redis-password='${GET_[redis_password]}' \ 1537 | --cache-backend-redis-compress-data=1 \ 1538 | --cache-backend-redis-compression-lib=l4z \ 1539 | --amqp-host=rabbitmq \ 1540 | --amqp-port=5672 \ 1541 | --amqp-user=${GET_[owner]} \ 1542 | --amqp-password='${GET_[rabbitmq_password]}' \ 1543 | --amqp-virtualhost='/${GET_[owner]}' \ 1544 | --consumers-wait-for-messages=0 \ 1545 | --search-engine=opensearch \ 1546 | --opensearch-host=opensearch \ 1547 | --opensearch-port=9200 \ 1548 | --opensearch-index-prefix=${GET_[owner]} \ 1549 | --opensearch-enable-auth=1 \ 1550 | --opensearch-username=${GET_[owner]} \ 1551 | --opensearch-password='${GET_[indexer_password]}'" 1552 | 1553 | if [ "$?" != 0 ]; then 1554 | echo "" 1555 | REDTXT "[!] Magento setup:install error" 1556 | REDTXT "[!] Please correct error above and try again" 1557 | echo "" 1558 | exit 1 1559 | fi 1560 | 1561 | # save config variables 1562 | ${SQLITE3} "UPDATE magento SET 1563 | admin_login = '${ADMIN_LOGIN}', 1564 | admin_password = '${ADMIN_PASSWORD}', 1565 | admin_email = '${ADMIN_EMAIL}', 1566 | locale = '${LOCALE}', 1567 | admin_path = '$(grep -Po "(?<='frontName' => ')\w*(?=')" ${GET_[root_path]}/app/etc/env.php)', 1568 | crypt_key = '$(grep -Po "(?<='key' => ')\w*(?=')" ${GET_[root_path]}/app/etc/env.php)';" 1569 | 1570 | ${SQLITE3} "UPDATE menu SET install = 'x';" 1571 | fi 1572 | 1573 | echo "" 1574 | echo "" 1575 | echo "" 1576 | WHITETXT "=============================================================================" 1577 | echo 1578 | GREENTXT "Magento ${GET_[version_installed]} installed" 1579 | echo 1580 | WHITETXT "=============================================================================" 1581 | echo 1582 | 1583 | pause '[] Press [Enter] key to show menu' 1584 | printf "\033c" 1585 | ;; 1586 | ################################################################################### 1587 | ### FINAL CONFIGURATION ### 1588 | ################################################################################### 1589 | "config") 1590 | printf "\033c" 1591 | echo "" 1592 | BLUEBG "[~] POST-INSTALLATION CONFIGURATION [~]" 1593 | WHITETXT "-------------------------------------------------------------------------------------" 1594 | echo "" 1595 | # network is up? 1596 | host1=google.com 1597 | host2=github.com 1598 | 1599 | RESULT=$(((ping -w3 -c2 ${host1} || ping -w3 -c2 ${host2}) > /dev/null 2>&1) && echo "up" || (echo "down" && exit 1)) 1600 | if [[ ${RESULT} == up ]]; then 1601 | GREENTXT "PASS: NETWORK IS UP. GREAT, LETS START!" 1602 | else 1603 | echo 1604 | REDTXT "[!] NETWORK IS DOWN" 1605 | YELLOWTXT "[!] PLEASE CHECK YOUR NETWORK SETTINGS" 1606 | echo 1607 | echo 1608 | exit 1 1609 | fi 1610 | 1611 | # Get variables for configuration 1612 | SSH_PORT="$(${SQLITE3} "SELECT ssh_port FROM system;")" 1613 | PHP_VERSION="$(${SQLITE3} "SELECT php_version FROM system;")" 1614 | TIMEZONE="$(${SQLITE3} "SELECT timezone FROM system;")" 1615 | 1616 | echo "" 1617 | YELLOWTXT "[-] Server hostname settings" 1618 | DOMAIN="$(${SQLITE3} "SELECT domain FROM magento LIMIT 1;")" 1619 | hostnamectl set-hostname "${DOMAIN}" --static 1620 | hostname 1621 | 1622 | echo "" 1623 | YELLOWTXT "[-] Create motd banner" 1624 | curl -o /etc/motd "${MAGENX_INSTALL_GITHUB_REPO}/motd" 1625 | sed -i "s/MAGENX_VERSION/${MAGENX_VERSION}/" /etc/motd 1626 | 1627 | echo "" 1628 | YELLOWTXT "[-] Sysctl parameters" 1629 | tee /etc/sysctl.conf <&1 | awk -F'"' '/download_url/ {print $4 ; system("curl -O "$4)}' >/dev/null 1752 | ln -s /etc/nginx/sites-available/default.conf /etc/nginx/sites-enabled/default.conf 1753 | mkdir -p /etc/nginx/conf_m2 && cd /etc/nginx/conf_m2/ 1754 | curl ${MAGENX_NGINX_GITHUB_REPO_API}/conf_m2 2>&1 | awk -F'"' '/download_url/ {print $4 ; system("curl -O "$4)}' >/dev/null 1755 | mkdir -p /etc/nginx/ipset && cd /etc/nginx/ipset/ 1756 | curl ${MAGENX_NGINX_GITHUB_REPO_API}/ipset 2>&1 | awk -F'"' '/download_url/ {print $4 ; system("curl -O "$4)}' >/dev/null 1757 | 1758 | echo "" 1759 | YELLOWTXT "[-] Magento profiler configuration in nginx" 1760 | PROFILER_PLACEHOLDER="$(head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1)" 1761 | sed -i "s/PROFILER_PLACEHOLDER/${PROFILER_PLACEHOLDER}/g" /etc/nginx/conf_m2/maps.conf 1762 | 1763 | echo "" 1764 | YELLOWTXT "[-] phpMyAdmin installation and configuration" 1765 | PHPMYADMIN_LOCATION=$(head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 6 | head -n 1) 1766 | BLOWFISH_SECRET=$(head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) 1767 | echo " phpMyAdmin location => ${PHPMYADMIN_LOCATION}" 1768 | echo "" 1769 | mkdir -p /usr/share/phpMyAdmin && cd $_ 1770 | composer -n create-project phpmyadmin/phpmyadmin . 1771 | 1772 | tee config.inc.php <<'END' 1773 | /dev/null 1834 | echo "deb [signed-by=/usr/share/keyrings/goaccess.gpg arch=$(dpkg --print-architecture)] https://deb.goaccess.io/ $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/goaccess.list 1835 | apt update 1836 | apt -y install goaccess 1837 | 1838 | ## 1839 | # Configuration 1840 | # Create an associative array 1841 | declare -A GET_ 1842 | # Get the data for the Magento mode from the magento table | sqlite .mode line key=value 1843 | QUERY=$(${SQLITE3} -line "SELECT * FROM magento;") 1844 | # Loop through the lines of the query output and add the key=value pairs to the associative array 1845 | while IFS='=' read -r KEY VALUE; do 1846 | # Extract the key and value from the line separated by ' = ' 1847 | KEY=$(echo "${KEY}" | tr -d '[:space:]') 1848 | VALUE=$(echo "${VALUE}" | tr -d '[:space:]') 1849 | # Skip adding key=value pair if value is empty 1850 | if [[ -n "${VALUE}" ]]; then 1851 | # Add the key=value pair to the associative array 1852 | GET_["${KEY}"]="${VALUE}" 1853 | fi 1854 | done <<< "${QUERY}" 1855 | echo "" 1856 | # Use associative array here 1857 | _echo "${YELLOW}[?]${REDBG}${BOLD}[ Configuration ]${RESET} ${YELLOW}${RESET}" 1858 | echo "" 1859 | echo "" 1860 | 1861 | YELLOWTXT "[-] Php-fpm pool configuration" 1862 | tee /etc/php/${PHP_VERSION}/fpm/pool.d/${GET_[owner]}.conf < /tmp/${GET_[php_user]}_crontab 2046 | cat << END | tee -a /tmp/${GET_[php_user]}_crontab 2047 | #~ MAGENTO START ${BP_HASH} 2048 | * * * * * /usr/bin/php${PHP_VERSION} ${GET_[root_path]}/bin/magento cron:run 2>&1 | grep -v "Ran jobs by schedule" >> ${GET_[root_path]}/var/log/magento.cron.log 2049 | #~ MAGENTO END ${BP_HASH} 2050 | END 2051 | crontab -u ${GET_[php_user]} /tmp/${GET_[php_user]}_crontab 2052 | rm /tmp/${GET_[php_user]}_crontab 2053 | 2054 | echo "" 2055 | YELLOWTXT "[-] Creating Magento environment variables to /home/${GET_[owner]}/.env" 2056 | tee /home/${GET_[owner]}/.env <> .ssh/authorized_keys 2104 | 2105 | echo "" 2106 | YELLOWTXT "[-] Creating bash_profile" 2107 | tee .bash_profile <> /etc/bashrc 2173 | echo "" 2174 | ## simple installation stats 2175 | DOMAIN=$(${SQLITE3} "SELECT domain FROM magento LIMIT 1;") 2176 | DISTRO_NAME=$(${SQLITE3} "SELECT distro_name FROM system;") 2177 | curl --silent -X POST https://www.magenx.com/ping_back_os_${DISTRO_NAME}_domain_${DOMAIN}_geo_${TIMEZONE}_keep_30d >/dev/null 2>&1 2178 | echo "" 2179 | echo "#===================================================================================================================#" 2180 | GREENTXT "${BOLD}~~ SERVER IS READY. THANK YOU ~~" 2181 | echo "#===================================================================================================================#" 2182 | echo "" 2183 | ${SQLITE3} "UPDATE menu SET config = 'x';" 2184 | echo "" 2185 | pause '[] Press [Enter] key to show menu' 2186 | ;; 2187 | ################################################################################### 2188 | ### WEBMIN INSTALLATION ### 2189 | ################################################################################### 2190 | "webmin") 2191 | echo "" 2192 | echo "" 2193 | _echo "[?] Install Webmin Control Panel ? [y/n][n]: " 2194 | DOMAIN=$(${SQLITE3} "SELECT domain FROM magento LIMIT 1;") 2195 | OWNER=$(${SQLITE3} "SELECT owner FROM magento LIMIT 1;") 2196 | ADMIN_EMAIL=$(${SQLITE3} "SELECT admin_email FROM magento LIMIT 1;") 2197 | read webmin_install 2198 | if [ "${webmin_install}" == "y" ];then 2199 | echo "" 2200 | YELLOWTXT "Webmin installation:" 2201 | echo "" 2202 | curl -s -O https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh 2203 | bash setup-repos.sh 2204 | apt update 2205 | apt -y install webmin 2206 | if [ "$?" = 0 ]; then 2207 | WEBMIN_PORT=$(shuf -i 17556-17728 -n 1) 2208 | sed -i 's/theme=gray-theme/theme=authentic-theme/' /etc/webmin/config 2209 | sed -i 's/preroot=gray-theme/preroot=authentic-theme/' /etc/webmin/miniserv.conf 2210 | sed -i "s/port=10000/port=${WEBMIN_PORT}/" /etc/webmin/miniserv.conf 2211 | sed -i "s/listen=10000/listen=${WEBMIN_PORT}/" /etc/webmin/miniserv.conf 2212 | sed -i '/keyfile=\|certfile=/d' /etc/webmin/miniserv.conf 2213 | echo "keyfile=/etc/letsencrypt/live/${DOMAIN}/privkey.pem" >> /etc/webmin/miniserv.conf 2214 | echo "certfile=/etc/letsencrypt/live/${DOMAIN}/cert.pem" >> /etc/webmin/miniserv.conf 2215 | 2216 | echo "webmin_${OWNER}:\$1\$84720675\$F08uAAcIMcN8lZNg9D74p1:::::$(date +%s):::0::::" > /etc/webmin/miniserv.users 2217 | sed -i "s/root:/webmin_${OWNER}:/" /etc/webmin/webmin.acl 2218 | WEBMIN_PASSWORD=$(head -c 500 /dev/urandom | tr -dc 'a-zA-Z0-9@#%^?=+_[]{}()' | fold -w 15 | head -n 1) 2219 | /usr/share/webmin/changepass.pl /etc/webmin/ webmin_${OWNER} "${WEBMIN_PASSWORD}" 2220 | 2221 | systemctl enable webmin 2222 | /etc/webmin/restart 2223 | 2224 | echo 2225 | GREENTXT "Webmin installed - OK" 2226 | echo 2227 | YELLOWTXT "[!] Webmin Port: ${WEBMIN_PORT}" 2228 | YELLOWTXT "[!] User: webmin_${OWNER}" 2229 | YELLOWTXT "[!] Password: ${WEBMIN_PASSWORD}" 2230 | echo "" 2231 | REDTXT "[!] PLEASE ENABLE TWO-FACTOR AUTHENTICATION!" 2232 | 2233 | ${SQLITE3} "UPDATE system SET webmin_password = '${WEBMIN_PASSWORD}';" 2234 | else 2235 | echo 2236 | REDTXT "Webmin installation error" 2237 | fi 2238 | else 2239 | echo 2240 | YELLOWTXT "Webmin installation was skipped by user input." 2241 | fi 2242 | echo 2243 | echo 2244 | pause '[] Press [Enter] key to show menu' 2245 | echo 2246 | ;; 2247 | "exit") 2248 | REDTXT "[!] Exit" 2249 | exit 2250 | ;; 2251 | 2252 | ################################################################################### 2253 | ### CATCH ALL MENU - THE END ### 2254 | ################################################################################### 2255 | 2256 | *) 2257 | printf "\033c" 2258 | ;; 2259 | esac 2260 | done 2261 | --------------------------------------------------------------------------------