Building a RHEL 9 Web Server on AWS
As with other posts out here, I seldom build servers, and as such, two things happen. First, I forget many of the particulars that go into setting up the server. Second, many of the particulars change because software versions change, as do best-practices.
So, this post covers the most recent steps I took to a) spin up a RHEL 8 server and b) install Apache dto c) serve up a PHP application that d) connects to an Oracle database.
Let's get to it.
Launch RDS Oracle 19c Database Instance
First things first, we need an Oracle database server. For this, we are going to using AWS RDS to create an Oracle database instance.
I will ignore steps to set up an AWS account.
Launch EC2 Web Server Instance
Now the database is running, let's create a web server to run our PHP
application.
-
Go to EC2 Dashboard in AWS.
-
Click "Instances"
-
Click "Launch Instances"
-
Enter name: "RHEL8 Web Server"
-
For AMI, search for "RHEL8", select "Red Hat Enterprise Linux 8 (HVM)" from Red Hat. Pricing at time this was selected was $0.156/hr. Click "Subscribe on instance launch"
-
Instance type: t2.micro
-
Key pair (I created a new ED25519 key pair for this server)
-
Network settings: click "Edit"
-
Check/change VPC
-
Ensure the Subnet selected is one that is open to the Internet
-
Auto-assign public IP: Enable
-
Firewall (security groups): Create security group
- Security group name: RHEL8 Web Server
- Description: used default
- Inbound rules: ssh/Anywhere, HTTP/Anywhere, HTTPS/Anywhere
-
Configure storage: 1x 50 GiB gp3
-
-
Click "Launch instance"
-
After a few minutes, the server should be up and running, and we should be able to login via ssh and do a system update.
$ ssh-add -L
$ ssh-add --apple-use-keychain .ssh/KeyName.pem
$ ssh-add -L
$ ssh ec2-user@1.2.3.4 (server ip address)
To install packages on the shiny new RHEL8 web server, I will use the default package manager dnf
, which is new to me (since the last time I built a server). dnf
stands for "Dandified Yum", and has been adopted by Redhat to replace yum
, the previous package manager (which by the way, still exists on the server).
$ sudo dnf update
$ sudo dnf upgrade
Install Oracle Instant Client
To be able to connect to the Oracle RDS database from the EC2 server, we need to download and install Oracle Instant Client.
Go to the Oracle Download Site and download the following RPMs:
$ curl -O https://download.oracle.com/otn_software/linux/instantclient/2340000/oracle-instantclient-basic-23.4.0.24.05-1.el8.x86_64.rpm
$ curl -O https://download.oracle.com/otn_software/linux/instantclient/2340000/oracle-instantclient-sqlplus-23.4.0.24.05-1.el8.x86_64.rpm
$ curl -O https://download.oracle.com/otn_software/linux/instantclient/2340000/oracle-instantclient-tools-23.4.0.24.05-1.el8.x86_64.rpm
$ curl -O https://download.oracle.com/otn_software/linux/instantclient/2340000/oracle-instantclient-devel-23.4.0.24.05-1.el8.x86_64.rpm
Since we downloaded the rpm
files, we can install them using dnf
:
$ sudo dnf install oracle-instantclient-basic-23.4.0.24.05-1.el8.x86_64.rpm
$ sudo dnf install oracle-instantclient-sqlplus-23.4.0.24.05-1.el8.x86_64.rpm
$ sudo dnf install oracle-instantclient-tools-23.4.0.24.05-1.el8.x86_64.rpm
$ sudo dnf install oracle-instantclient-devel-23.4.0.24.05-1.el8.x86_64.rpm
To test the installation, check to ensure sqlplus is installed and working (don't you wish all these packages would standardize on a single way to check the version?):
$ sqlplus -V
You need five things to connect to the database: the user, the password, the RDS endpoint, the port, and the database name (SID).
Here is how you enter all those things into sqlplus
to connect to the database:
$ sqlplus 'my-user@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=my.db.endpoint.rds.amazonaws.com)(PORT=1521))(CONNECT_DATA=(SID=my-demo-db)))'
If everything installed correctly, you should see this response:
SQL*Plus: Release 23.0.0.0.0 - Production on Mon Jul 1 20:35:03 2024
Version 23.4.0.24.05
Copyright (c) 1982, 2024, Oracle. All rights reserved.
Enter password:
Enter the password to receive the SQL>
prompt:
Last Successful login time: Mon Jul 01 2024 17:08:39 +00:00
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.23.0.0.0
SQL>
Voila! You are able to connect to the RDS Oracle database from the EC2 server. Enter quit
to exit.
Install Apache
Now, let's get the web server running.
$ sudo dnf install httpd
$ sudo apachectl start
This is probably the easiest step. You should be able to open a browser at the server IP address:
$ open http://123.456.78.9
You should see the default 'Red Hat Enterprise Linux Test Page' which is configured to come up by default.
The web root of a default installation exists at /var/www/html
, but note that there is no index.html
page at that location. This is because the configuration file is set up to launch a default page from another location. You can override this behavior by putting adding index.html
in the web root:
$ cd /var/www/html
$ sudo echo "<html><body><h1>It works.</h1></body></html>" | sudo tee index.html > /dev/null
Now when you go to the server IP address, instead of the default Red Hat page, you will see your new index.html page.
To start, stop, restart, or check the status of the service:
$ sudo apachectl start
$ sudo apachectl stop
$ sudo apachectl restart
$ sudo apachectl status
PHP (with oci8)
Next up, we need to install all the php
components that will get PHP working on the command line and in the browser, and connecting to an Oracle database via the Oracle Instant Client.
To get a list of PHP packages:
$ sudo dnf search php
This doesn't show versions. To check available PHP versions:
$ sudo dnf module list php
The default displays as 7.2. We want to install PHP 8.2, which is available but not enabled, so we need to enable it.
$ sudo dnf module enable php:8.2
Now we can install the PHP packages we need:
$ sudo dnf install php php-devel php-pear
In addition to php
, we need php-devel
and php-pear
so that we can install the oci8
package via pecl
.
Check versions:
$ php --version
$ pecl help version
Now install oci8
.
$ sudo pecl install oci8
The install process requests the path to the Oracle Instant Client libraries that were already installed in the ORACLE_HOME
directory, but including the /lib
folder. We entered:
$ 'instantclient,/usr/lib/oracle/23/client64/lib'
Build process completed successfully
Installing '/usr/lib64/php/modules/oci8.so'
install ok: channel://pecl.php.net/oci8-3.3.0
configuration option "php_ini" is not set to php.ini location
You should add "extension=oci8.so" to php.ini
Looks like we can set a configuration option so pecl knows where the php.ini
file is. Since we didn't, we will have to edit it manually. In addition to adding the oci8
extension, we need to make a few other changes to the php.ini
file for testing purposes.
$ sudo vi /etc/php.ini
Scroll down to where the extensions are listed (almost all are commented out), and add "extension=oci8.so" at the end of the list and save the file.
The php.ini
file is well documented, letting you know most of the settings and their default, development and production values. To show errors in php, find the display_errors
setting and switch it to On
.
Once all the software is installed and the configurations set, it is time to restart Apache:
$ sudo systemctl restart php-fpm
$ sudo apachectl restart
First, run a simple script to make sure php
is working in both batch and in the web browser:
$ php --version
$ php -i
$ cd /var/www/html
$ sudo echo "<?php phpinfo(); ?>" > sudo tee test_php.php > /dev/null
Open a browser to http://123.456.78.9/test_php.php
and you should see the PHP configuration, similar to what was dumped to stdout in the terminal window.
Look for the location of php.ini
in both the browser and in batch. In our case, both environments are using the same php.ini
file.
Next, do the same test with th test_oci.php
script in both batch and in the web browser.
The batch test worked, but in the browser, we get a warning: "ORA-12546: TNS:permission denied Help: https://docs.oracle.com/error-help/db/ora-12546/"
ChatGPT had lots of stuff about config variables, but I am suspicious it is not accurate. The stuff about SELinux (Security Enhanced) looked more plausible, so I tried checking that:
$ sudo getentforce
Enforcing
Security contexts are above my pay grade, at least for now, but there was plenty to suggest that Apache did not have the proper context to leverage the oci8
libraries. So I (temporarily) disabled them:
$ sudo setenforce 0
$ sudo getenforce
Permissive
And the oci8 test script worked in the browser!
Next, I tried resetting the Enforcing
and setting context for the Oracle Instant Client libaries per ChatGPT instructions:
$ sudo chcon -Rv -t httpd_sys_content_t /usr/lib/oracle/23/client64
The command seemed to work (after additional googling, I added the v=verbose directive to see a log of changes, and there were no issues), but the oci8 test script did not work in the browser.
We will return to this issue later, but for now will leave the server in Permissive
mode.
Configure Users/Permissions
Now time to add the application user who is responsible for running the application.
$ sudo adduser avaion
$ sudo groupadd webapp
$ sudo usermod -a -G webapp avaion
$ sudo usermod -a -G webapp apache
Check group assignments
$ sudo lid avaion
$ sudo lid apache
Login as avaion and check group assignment
$ sudo su - avaion
$ groups
Create avaion directory
$ cd /opt
$ sudo mkdir avaion
$ sudo chown -R avaion:webapp /opt/avaion
$ sudo chown 2750 avaion
$ cd avaion
Change permissions to opt/avaion to give avaion access
$ sudo find /opt/avaion -type d -exec chmod 750 {} \;
$ sudo find /opt/avaion -type f -exec chmod 640 {} \;
For testing purposes, add a www
directory under /opt/avaion
(the real www
directory will exist under a project controlled by git
):
$ sudo su - avaion
$ cd /opt/avaion
$ mkdir www
$ exit
The last line exit
above exits from the avaion user back to ec2-user to complete the rest of the configurations below.
In the web root, add a symbolic link to point to the newly created www
directory:
$ cd /var/www/html
$ sudo ln -s /opt/avaion/www rpt
Move the previously created index.html
, test_php.php
and test_oci.php
scripts from the web root to the www
directory:
$ mv index.html rpt
$ mv test_php.php rpt
$ mv test_oci.php rpt
NOTE: Symbolic links have 777 permissions, as they adopt permissions from the directory or file they actually point to.
Set all directory permissions:
$ cd /opt
$ sudo find avaion -type d -exec chmod 750 {} \;
Set all file permissions:
$ sudo find avaion -type f -exec chmod 640 {} \;
If you can now run all of the test scripts in the browser in their new rpt
location, permissions are correct and the server is configured and ready for use.