Updated 28-Jun-2024
Note: Amazon Linux AMI is essentially CentOS 6.x. Everything below works on CentOS as well.
Preparation for Amazon LAMP
The OpenVPN on Amazon Linux AMI is a good place to start in preping the L for the AMP part of the lamp stack. Lots of good stuff there. A followup is WordPress Multisite on Amazon Linux AMI.
The Order of Things with Amazon Linux AMI
Amazon Linux AMI is a bit picky about the order of things. Otherwise there will be dependency conflicts, which is a rife problem. Also, make sure to disable (at least temporarily) other repositories, otherwise there can be even more dependency conflicts. Here is a handy script to ensure that an upgrade (or fresh install) will go well, starting with Apache httpd, php, and ssl:
service httpd stop
yum -y erase httpd httpd-tools apr apr-util
yum -y remove php*
yum -y install php56
yum -y install php56-xml php56-xmlrpc php56-soap php56-gd php56-mbstring
yum -y install php56-cli php56-common php56-pdo
yum -y install php56-mysqlnd
yum -y install php56-opcache
yum -y install mod24_ssl
sed -i -e 's/SSLMutex/Mutex/g' /etc/httpd/conf.d/ssl.conf
service httpd start
service httpd restart
Source: Gist Note that this is PHP 5.6, not the vaunted 7.x version. Version 5.6 is (as of July, 2017) the most popular PHP version, and (in a recent study) is installed on 22.4% of all servers whose server-side programming languages are known, compared with 5.4% of all 7.x versions.
Enable httpd Service and Check if Enabled
Enable httpd as a service to startup
chkconfig httpd on
Check the configuration status of httpd
chkconfig --list httpd
Set User and Group Rights
This is especially useful when using sftp
.
usermod -a -G apache USERNAME
chown -R USERNAME:apache /var/www
chmod 2775 /var/www
find /var/www -type d -exec chmod 2775 {} \;
find /var/www -type f -exec chmod 0664 {} \;
Create and Test PHP Info Page
echo "" > /var/www/html/phpinfo.php
Note that there are at least three command-line ways of figuring out things such as which php.ini file am I using:
- php --ini
,
- php -i | grep "Configuration File"
, and
- apachectl -S
Copy opcache.php OpCache Status Page
cd /root/temp
wget https://raw.githubusercontent.com/rlerdorf/opcache-status/master/opcache.php
mv opcache.php /var/www/html/opcache.php
Test at:
https://host.server.tld/opcache.php
Apache Configuration
Spend time on the following files (shortcut scripts are shown after filename):
- nano /etc/httpd/conf/httpd.conf
- eap
- nano /etc/httpd/conf.d/ssl.conf
- essl
- nano /etc/httpd/conf.modules.d/00-mpm.conf
- empm
- sudo nano /etc/httpd/conf.modules.d/00-base.conf
- ebase
/etc/httpd/conf/httpd.conf Modifications
## Modifications at Top of File ##
RewriteEngine On
ServerTokens Prod
ServerSignature Off
TraceEnable off
Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure
Header always append X-Frame-Options DENY
Header set X-XSS-Protection "1; mode=block"
Header unset ETag
Header set Cache-Control "max-age=604800, must-revalidate"
FileETag None
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
Header append Vary User-Agent
RewriteCond %{REQUEST_METHOD} ^(TRACE|DELETE|TRACK) [NC]
RewriteRule ^.* - [F]
HostnameLookups Off
RequestHeader unset Proxy early
ServerTokens ProductOnly
## End Modifications ##
...
## Modifications inside directory ##
# Strip all subdomains not including status.
# NOTE: Disable this while subdomains help get to sites
#RewriteCond %{HTTP_HOST} !^status\.(.*)\.(.*) [NC]
#RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*) [NC]
#RewriteRule ^(.*) https://%2.%3/$1 [R=301,L]
# Require SSL (site-wide)
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R=301,L]
# Redirect Alias Domains (after SSL rewrite)
RewriteCond %{HTTP_HOST} ^domain.tld$ [NC]
RewriteRule ^(.*)$ https://domain2.tld2/$1 [R=301,L]
# WordPress
RewriteBase /
RewriteRule ^index\.php$ - [L]
# Obfuscate the /admin/ and wp-login.php stuff
RewriteRule ^(/)?SoMeWeIrDsHiT/?$ /wp-login.php [QSA,L]
RewriteRule ^(/)?wp-register-php/?$ /wplogin?action=register [QSA,L]
# Add a trailing slash to /wp-admin
RewriteRule ^wp-admin$ wp-admin/ [R=301,L]
# More WordPress Multisite
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
RewriteRule ^(.*\.php)$ $1 [L]
RewriteRule . index.php [L]
## End Modifications ##
...
## Modification after files section ##
# Disable XML-RPC
Require all denied
Order allow,deny
Deny from all
## End Modifications ##
...
## Modification inside mime_module ##
AddType text/plain .txt
AddType text/html .html
AddType text/xml .xml
AddType text/css .css
AddType application/x-javascript .js
AddType image/x-icon .ico
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE image/x-icon
## End Modification ##
...
## Modification at End of File ##
#ErrorDocument 500 "The server made a boo boo."
#ErrorDocument 404 /missing.html
StartServers 2
MinSpareServers 4
MaxSpareServers 8
MaxClients 64
MaxRequestsPerChild 2560
## End Modification ##
MPM Prefork rule of thumb is ram/15 for max clients, but test that out. The above is for a server with 1gb ram, though with a database on the same server, may need to throttle this back. From another server (with Apache installed), test with Apache Bench (ab
), for example:
ab -n 2000 -c 200 https://host.domain.tld:443/info.php
/etc/httpd/conf.d/ssl.conf Modifications
## Modifications at Top ##
SSLStrictSNIVHostCheck off
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/run/ocsp(128000)
## End of Modifications ##
...
## VirtualHost Modifications ##
SSLCipherSuite ALL:+HIGH:+TLSv1:!ADH:!EXP:!SSLv2:!MEDIUM:!LOW:!NULL:!aNULL
SSLCompression off
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
SSLVerifyClient none
## End of Modifications ##
Install apachetop for Amazon Linux
Because Amazon Linux AMI is still basically a CentOS 6.x base, and most repositories have a CentOS 7.x version of apachetop, it is important to download and install locally as follows:
mkdir /root/temp
cd /root/temp
wget http://dl.fedoraproject.org/pub/epel/6/i386//apachetop-0.15.6-1.el6.i686.rpm
yum -y localinstall apachetop-0.15.6-1.el6.i686.rpm
OpCache Configuration
First, comment out the opcache from /etc/php.ini
nano /etc/php.ini
As follows:
;declared in opcache.ini
;zend_extension = opcache.so
Next, configure the opcache.ini file:
nano /etc/php-5.6.d/10-opcache.ini
Configure as follows:
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
;128 is for 1gb ram
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=6400
;opcache.max_wasted_percentage=5
opcache.use_cwd=1
opcache.validate_timestamps=0
opcache.revalidate_freq=60
;opcache.revalidate_path=0
;opcache.save_comments=1
;opcache.load_comments=1
opcache.fast_shutdown=1
;opcache.enable_file_override=0
;opcache.optimization_level=0xffffffff
;opcache.inherited_hack=1
;opcache.dups_fix=0
opcache.blacklist_filename=/etc/php-5.6.d/opcache*.blacklist
;opcache.max_file_size=0
;opcache.consistency_checks=0
;opcache.force_restart_timeout=180
;opcache.error_log=
;opcache.log_verbosity_level=1
;opcache.preferred_memory_model=
;opcache.protect_memory=0
PHP.INI Configuration
[PHP]
;already declared in opcache.ini
;zend_extension = opcache.so
engine = On
;for piwik
always_populate_raw_post_data=-1
short_open_tag = Off
asp_tags = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
;unserialize_callback_func =
serialize_precision = 17
;open_basedir =
;disable_functions =
;disable_classes =
zend.enable_gc = On
;zend.multibyte = Off
;zend.script_encoding =
expose_php = Off
max_execution_time = 30
max_input_time = 60
memory_limit = 256M
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
html_errors = On
variables_order = "GPCS"
request_order = "GP"
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 8M
;auto_prepend_file =
;auto_append_file =
default_mimetype = "text/html"
;default_charset = "UTF-8"
;doc_root =
;user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 20M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 60
;;;;;;;;;;;;;;;;;;;
; Module Settings ;
;;;;;;;;;;;;;;;;;;;
[CLI Server]
cli_server.color = On
[Date]
date.timezone = Asia/Tokyo
[Pdo_mysql]
pdo_mysql.cache_size = 2000
pdo_mysql.default_socket=
[mail function]
smtp_port = 25
; For Unix only. You may supply arguments as well (default: "sendmail -t -i").
; http://php.net/sendmail-path
; sendmail_path = "/usr/sbin/sendmail -t -i"
; Force the addition of the specified parameters to be passed as extra parameters
; to the sendmail binary. These parameters will always replace the value of
; the 5th parameter to mail(), even in safe mode.
;mail.force_extra_parameters =
; Add X-PHP-Originating-Script: that will include uid of the script followed by the filname
mail.add_x_header = On
; The path to a log file that will log all mail() calls. Log entries include
; the full path of the script, line number, To address and headers.
;mail.log =
[SQL]
sql.safe_mode = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[Interbase]
ibase.allow_persistent = 1
ibase.max_persistent = -1
ibase.max_links = -1
ibase.timestampformat = "%Y-%m-%d %H:%M:%S"
ibase.dateformat = "%Y-%m-%d"
ibase.timeformat = "%H:%M:%S"
[MySQL]
mysql.allow_local_infile = On
mysql.allow_persistent = On
mysql.cache_size = 2000
mysql.max_persistent = -1
mysql.max_links = -1
mysql.default_port =
mysql.default_socket =
mysql.default_host =
mysql.default_user =
mysql.default_password =
mysql.connect_timeout = 60
mysql.trace_mode = Off
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[PostgreSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice = 0
[Sybase-CT]
sybct.allow_persistent = On
sybct.max_persistent = -1
sybct.max_links = -1
sybct.min_server_severity = 10
sybct.min_client_severity = 10
[bcmath]
bcmath.scale = 0
[Session]
session.save_handler = files
session.save_path = "/var/lib/php/session"
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.bug_compat_42 = Off
session.bug_compat_warn = Off
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.hash_function = 0
session.hash_bits_per_character = 5
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
[MSSQL]
mssql.allow_persistent = On
mssql.max_persistent = -1
mssql.max_links = -1
mssql.min_error_severity = 10
mssql.min_message_severity = 10
mssql.compatability_mode = Off
mssql.secure_connection = Off
tidy.clean_output = Off
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit = 5
ldap.max_links = -1
Install LetsEncrypt SSL Certs
If OpenSSL is installed and the default settings are in httpd.conf
and ssl.conf
, then LetsEncrypt should work in a straightforward way. Do add the various virtual server and virtual aliases in both files, and for the ./letsencrypt-auto
command, include all hosts adding on -d host.domain.tld
one after the other. Also, for Amazon Linux AMI have to include the --debug
flag.
yum -y install git
git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
cd /opt/letsencrypt
./letsencrypt-auto --debug --apache -d host.domain.com
Note: Use the staging environment for testing before doing a live run. It can save you an hour's wait, if there are things to work out.
Get the Certs into the HSTS Preload List
- Visit HSTSpreload.org
PHP Configuration
php.ini - stuff - more stuff ... MORE ...
MySQL 5.6 vs. MySQL 5.7
As of mid-July, 2017 there are two main options for MySQL: version 5.6 or version 5.7. MySQL 5.6 is available from the AMI repository, but 5.7 only from Oracle's community repository. Many people have complained about this, as they well should. MariaDB 10.x is a competitor, but without much adoption. The speed and functionality improvements of 5.7 make it the desired distribution release. The issue with the community repository is that it is unclear which distribution to use on Amazon Linux AMI. The standard redhat and fedora distributions don't work because they assume systemd (e.g., RHEL/CentOS 7.x). Some sources suggest using the generic linux distribution. Another option is the RHEL/CentOS 6.x build. Both seem viable, as long as 7.x repositories and distributions are disabled.
RDS with MySQL and Aurora
Besides installing and configuring a database on an EC2 or Lightsail instance (same thing), there are other Amazon database server options. The Amazon Relaltional Database Service (RDS), supports multiple engines including: - RDS for MySQL - RDS for Aurora Note that along with these there are other options such as nosql, redis, etc., and also various caching options.
For a basic MySQL installation (not on RDS, a separate database server), installing via a yum repository makes a shortcut.
MySQL 5.7 on Amazon Linux AMI standard installation
Install some new dependencies, if not present:
yum -y install libaio
yum -y install numactl
Download the repository (see latest version numbers here):
wget http://dev.mysql.com/get/mysql57-community-release-el6-11.noarch.rpm
yum -y localinstall mysql57-community-release-el6-11.noarch.rpm
Check to see if repo is installed, which ones are installed, and which available:
yum repolist | grep mysql
Read more about installing and enabling these MySQL repositories. Run the installation command, which will also install various common, libs, client, and server packages.
yum -y install mysql-community-server
Start the service
service mysqld start
Grep the error log which will have the generated password
grep 'temporary password' /var/log/mysqld.log
Login with that password
mysql –u root –p
Type in the password Then, change password
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewPassword';
Check to see if the password is hashed in the database, and also the version of MySQL installed.
select user,host,authentication_string from mysql.user;
select @@version,@@basedir,@@datadir;
Exit out of mysqld
exit
Set mysqld to start on boot, and see if it is set correctly:
chkconfig mysqld on
chkconfig --list mysqld
PhpMyAdmin on Amazon Linux
Install PhpMyAdmin from the Amazon EPEL Repository:
yum -y install phpmyadmin
Edit the file /etc/httpd/conf.d/phpMyAdmin.conf
and replace 127.0.0.1
with the ip address of the client.
nano /etc/httpd/conf.d/phpMyAdmin.conf
Restart the web server and database server (to be safe)
service httpd restart
service mysqld restart
Test to see if it works
https://host.domain.tld/phpmyadmin/