We will try to install Drupal 9 on a CentOS Stream 9 VPS/Server, and we will walk through all necessary steps to build and deploy our Drupal 9 application.
Web Technology To Use
As described in the next figure, we will use:
- Nginx as a reverse proxy (Port 443, 80)
- Apache as a web-server (Port 81)
- Drupal 9 CMS Our PHP Web Application
- MariaDB Database Server
We will consider an example domain name: drupalize.com, with server IP-Address: 69.164.208.30
But while you apply this tutorial for your project, do not forget to replace your domain name and IP-Address values with yours.
Configure The CentOS Stream Server
We will:
- Update our server.
- Install required packages ( i.e. MariaDB, Nginx, Apache, and PHP )
- Firewalls for all ports except HTTP(S) and SSH.
- Install fail2bab to harden SSH access.
- Installing Drupal
Step1: Update CentOS Stream Server
Update Our System
To update our Centos stream we just need to run the command.
# dnf update
Disable SELinux
We need to disable SELinux, by editing the file /etc/selinux/config and setting SELINUX=disabled
# vim /etc/selinux/config ~~~~~ ~~~~~ # To revert back to SELinux enabled: # # grubby --update-kernel ALL --remove-args selinux # SELINUX=disabled # SELINUXTYPE= can take one of these three values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted ~~~~ ~~~~
Then restart your device…
Step2: Install And Configure Nginx As A Reverse Proxy
Install And Configure Nginx
To Install And Enable Nginx, simply run the command
# dnf install nginx # systemctl enable nginx
Configure Nginx
And to configure Nginx As A Reverse Proxy for your web server, create a new config file for your website domain in the Nginx configuration directory /etc/nginx/conf.d
with the name of your domain as drupalize.com.conf
.
Edit your website domain config file /etc/nginx/conf.d/drupalize.com.conf
and append the following settings into it.
PS: don't forget to replace your server name with your domain name, also listen IP-Address to your server's public IP-Address. (colored with green bold).
# Define Your Server To Listen To Your Plubic IP ADDRESS && HTTP Port 80 # Then Redirect To HTTPS Port 443 server { listen 69.164.208.30:80; server_name drupalize.com; access_log off; return 301 https://drupalize.com$request_uri; } # If you want to redirect www to non-www domain, uncomment the next server block. #server { # listen 69.164.208.30:80; # server_name www.drupalize.com; # access_log off; # return 301 https://drupalize.com$request_uri; #} server { listen 69.164.208.30:443 ssl default; server_name drupalize.com; # We capture the logs access_log /var/log/nginx/drupalize.com.log; # Insert a full path of your Self-Signed certificate Or a Validation Signed one. # In the next step we will learn how to create our Self-Signed certificate or you can read this tutorial # To make your Self-Signd Certificate in 3 Steps, and read this for Validation Signed one. ssl_certificate /opt/cert/drupalize.com.cert.pem; ssl_certificate_key /opt/cert/drupalize.com.key.pem; ssl_dhparam /opt/cert/dhparam.pem; # Restrict to secure protocols, depending on whether you have visitors # from older browsers ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Restrict ciphers to known secure ones ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256; ssl_prefer_server_ciphers on; ssl_ecdh_curve secp384r1; # Disable Certificate Stapling and Verification if its a self-signed one. ssl_stapling off; ssl_stapling_verify off; # Caching SSL. ssl_session_cache shared:SSL:20m; ssl_session_timeout 180m; ssl_session_tickets on; # Adjustment Headers # Uncomment the next header if you are using Validation Certification To Force using it and setting up ssl cache age. #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; #Enable and configure GZIP gzip on; gzip_static on; gzip_comp_level 9; gzip_min_length 256; gzip_proxied any; gzip_vary on; gzip_buffers 16 8k; gzip_http_version 1.1; proxy_http_version 1.1; gzip_types application/atom+xml application/javascript application/x-javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/ttf application/x-ttf application/font-woff application/x-font-opentype application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype font/ttf font/x-woff image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/javascript text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; # text/html is always compressed by gzip module # To allow POST on static pages error_page 405 =200 $uri$is_args$args; location / { # Pass Every request to Apache at the Port 81 proxy_pass http://127.0.0.1:81; # Adjustement reverse proxy headers. proxy_read_timeout 640; proxy_connect_timeout 640; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Port 443; proxy_buffers 16 32k; proxy_buffer_size 64k; proxy_request_buffering off; proxy_buffering off; } # Enable Caching Static files... location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|ttf|woff|eot|bz2|gz|woff2|svg|bmp)$ { proxy_pass http://127.0.0.1:81; proxy_read_timeout 640; proxy_connect_timeout 640; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Port 443; proxy_buffers 32 64k; proxy_buffer_size 128k; proxy_request_buffering off; proxy_buffering off; access_log off; log_not_found on; # Extend Browsers Caching Time for static file expires 60d; } }
Of course, we can ignore using SSL, and using HTTP Port 80 Only, so you will use the configuration as is shown in the github config file link, its the same example configuration, and so Ignore the next step (Step3).
Step3: Create Your SSL Self-Signed Or Validation Certificate
To create your self-signed SSL, go to the SSL store directory ie /opt/cert and start running the command
# mkdir /opt/cert # cd /opt/cert # openssl req -newkey rsa:2048 -new -nodes -x509 -days 13650 -keyout drupalize.com.key.pem -out drupalize.com.cert.pem Generating a RSA private key ......+++++ .+++++ writing new private key to 'drupalize.com.key.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:Wyoming Locality Name (eg, city) []:Sheridan Organization Name (eg, company) [Internet Widgits Pty Ltd]:CompanyName Organizational Unit Name (eg, section) []:IT Common Name (e.g. server FQDN or YOUR name) []:yourdomainname.com Email Address []:info@yourdomainname.com
And for hardening this SSL Certificate we will generate Diffie-Hellman group dhparam.pem file
# openssl dhparam -out dhparam.pem 4096
PS: Creating dhparam.pem file may require several minutes to get finished.
Now we get the listed 3 certificate files in the certificate directory /opt/cert
- drupalize.com.cert.pem
- drupalize.com.key.pem
- dhparam.pem
Then make sure to enter the correct path of the certificate files, into the Nginx config file that we created in the previous step /etc/nginx/sites-available/drupalize.com.conf.
To have a validate signed certificate for your domain, you can follow the How to create your validated signed SSL by a trusted CA For Linux guide, and you can get your free one too from Let's Encrypt
Step4: Install Apache Web Server And Configure Our Virtual Host
Install And Configure Apache
To install the Apache web server, and then enable it for systemd, we will need to run the commands
# dnf install httpd httpd-tools # systemctl enable httpd
After installing Apache, go to the main Apache configuration file at /etc/httpd/conf/httpd.conf to set it to listen to port 81 at localhost 127.0.0.1 IP-Address.
So we edit the configuration file using vim editor and look for the Listen 80 line to change it to Listen 127.0.0.1:81 as the following
# vim /etc/httpd/conf/httpd.conf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Change this to Listen on a specific IP address, but note that if
# httpd.service is enabled to run at boot time, the address may not be
# available when the service starts. See the httpd.service(8) man
# page for more information.
#
#Listen 12.34.56.78:80
Listen 127.0.0.1:81
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create Our VirtualHost
To create our Virtual Host, we will go to the Apache config directory /etc/httpd/conf.d/, create a drupalize.com.conf file, then add the below virtual host settings then save the file.
<VirtualHost *:81> DocumentRoot /home/drupalize/public_html ServerAdmin info@drupalize.com ServerName drupalize.com ServerAlias *.drupalize.com <Directory "/home/drupalize/public_html"> Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted </Directory> </VirtualHost>
Step5: Install MariaDB The Database Server
Install MariaDB
To install, enable, and run the MariaDB we will run the commands
# dnf install mariadb mariadb-server # systemctl enable mariadb # systemctl start mariadb
MariaDB Basic Secure
After installation is complete, we need to apply the basic security configuration step.
We need to run the command mysql_secure_installation and Interact with outputs as the following:
- Enter current password for root (enter for none): [Press Enter With Empty Value]
- Switch to unix_socket authentication [Y/n] [Answer n]
- Change the root password? [Y/n] [Answer Y & Enter your database root password]
- Remove anonymous users? [Y/n] [Answer Y]
- Disallow root login remotely? [Y/n] [Answer Y]
- Remove test database and access to it? [Y/n] [Answer Y]
- Reload privilege tables now? [Y/n] [Answer Y]
# mysql_secure_installation NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY! In order to log into MariaDB to secure it, we'll need the current password for the root user. If you've just installed MariaDB, and haven't set the root password yet, you should just press enter here. Enter current password for root (enter for none): OK, successfully used password, moving on... Setting the root password or using the unix_socket ensures that nobody can log into the MariaDB root user without the proper authorisation. You already have your root account protected, so you can safely answer 'n'. Switch to unix_socket authentication [Y/n] n ... skipping. You already have your root account protected, so you can safely answer 'n'. Change the root password? [Y/n] y New password: Re-enter new password: Password updated successfully! Reloading privilege tables.. ... Success! By default, a MariaDB installation has an anonymous user, allowing anyone to log into MariaDB without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? [Y/n] y ... Success! Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? [Y/n] y ... Success! By default, MariaDB comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? [Y/n] y - Dropping test database... ... Success! - Removing privileges on test database... ... Success! Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? [Y/n] y ... Success! Cleaning up... All done! If you've completed all of the above steps, your MariaDB installation should now be secure. Thanks for using MariaDB!
Create A Database User for our Drupal Website
Log in to the database server as root (we set a database root password in the previous step), as below, and replace PassWord with yours.
We will create a new database named drupal, and a new database user named drupal_db_user with drupal_db_password.
You can replace the values with yours, and then flush MariaDB privileges as the following.
# mysql -uroot -pPassWord Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 11 Server version: 10.5.13-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> create database drupal; Query OK, 1 row affected (0.001 sec) MariaDB [(none)]> GRANT ALL PRIVILEGES ON drupal.* TO 'drupal_db_user'@'localhost' IDENTIFIED BY 'drupal_db_password'; Query OK, 0 rows affected (0.002 sec) MariaDB [(none)]> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.002 sec) MariaDB [(none)]> exit; Bye
Step6: Install PHP
Install PHP and Drupal required extensions using the command
# dnf install php php-mysqlnd php-json php-gd php-xml php-openssl php-curl php-mbstring php-zip
Step7: Add New User To The System
We will need to add a new user to the system which usually has a username as the domain name, then we will set a password for the domain user.
After that, we will create the public web directory public_html, which must be exactly as we add in the above apache virtual host configuration.
Then append the domain user to the Apache System User Group
# adduser drupalize # passwd drupalize Changing password for user drupalize. New password:[Enter your password here] Retype new password:[Re-Eneter your password] passwd: all authentication tokens updated successfully. # chmod 755 /home/drupalize/ # cd /home/drupalize # mkdir public_html # usermod -a -G apache domain
Step8: Firewall And Fail2ban
Firewall Setting
Firewall all ports and keep the HTTP 80, HTTPS 443, and SSH 22 Open, by running
# firewall-cmd --add-service=http --permanent # firewall-cmd --add-service=https --permanent # firewall-cmd --add-service=ssh --permanent # firewall-cmd --reload
Install And Run Fail2Ban
To Secure SSH from Brute Force attacks, we will install and run Fail2ban using EPEL repository as the following
# dnf install epel-release # dnf update # dnf install fail2ban # systemctl enable fail2ban # systemctl start fail2ban
Downloading And Installing Drupal 9
Step9: Download Drupal
Install Composer and tar packages using the command
# dnf install composer tar
Go to the public_html web directory /home/domain/public_html, and run the download and extract command to get the latest Drupal package as the following.
# curl -sSL https://www.drupal.org/download-latest/tar.gz | tar -xz --strip-components=1
Step10: Start The Installation Process
Now we can start our Web services as the following
# systemctl start nginx # systemctl start httpd
Set Drupal Files and Permissions
Go to the /home/drupalize domain directory, and apply the following permission to the public_html and contents
# cd /home/drupalize
# mkdir public_html/sites/default/files
# cp public_html/sites/default/default.settings.php public_html/sites/default/settings.php
# chown -R drupalize.apache public_html
# chmod -R 2750 public_html
# chmod -R 2770 public_html/sites/default/files
The following permission is temporary until you complete the installation process, and you must set the settings.php to 2640 after finishing.
# chmod -R 2770 public_html/sites/default/settings.php
Drupal Installation Wizard
We will use the classic Web GUI installation wizard method, by firing the domain URL into the web browser.
For the first run, and if you are using a self-signed certificate, you will get this SSL validation error message, and will need to choose to proceed to domain unsafe
Start Your Installation Wizard Steps, and Choose your language, then press Save and Continue
The Next Step will be to choose your installation profile, as we will select the standard profile
The next step will be a verification process, then set up the Drupal database using the database information which we created in the step5
- Database name: drupal
- Database username: drupal_db_user
- Database password: drupal_db_password
After entering the database information, the install site progress will start, and you will need to make the final step, with set up the site configuration
Entering your site configuration and finish the installation wizard.
Then return the settings.php file to set its permission to 2640, to keep drupal safe.
# chmod 2640 public_html/sites/default/settings.php
Now all done, and the installation mission is completed.