{"id":436,"date":"2018-10-17T11:11:34","date_gmt":"2018-10-17T11:11:34","guid":{"rendered":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/configure-apache-server-and-deploy-wordpress-with-puppet-lisenet-com-linux-security\/"},"modified":"2018-10-17T11:11:34","modified_gmt":"2018-10-17T11:11:34","slug":"configure-apache-server-and-deploy-wordpress-with-puppet-lisenet-com-linux-security","status":"publish","type":"post","link":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/configure-apache-server-and-deploy-wordpress-with-puppet-lisenet-com-linux-security\/","title":{"rendered":"Configure Apache Server and Deploy WordPress with Puppet | Lisenet.com :: Linux | Security"},"content":{"rendered":"<p>We\u2019re going to use Puppet to install Apache and WordPress. <\/p>\n<p>This article is part of the <a href=\"https:\/\/www.lisenet.com\/2018\/homelab-project-with-kvm-katello-and-puppet\/\" target=\"_blank\">Homelab Project with KVM, Katello and Puppet<\/a> series.<\/p>\n<h2>Homelab<\/h2>\n<p>We have two CentOS 7 servers installed which we want to configure as follows:<\/p>\n<p>web1.hl.local (10.11.1.21) \u2013 Apache server with WordPress and NFS mount<br \/>web2.hl.local (10.11.1.22) \u2013 Apache server with WordPress and NFS mount<\/p>\n<p>SELinux set to enforcing mode.<\/p>\n<p>See the image below to identify the homelab part this article applies to. <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"793\" src=\"https:\/\/www.lisenet.com\/wp-content\/uploads\/2018\/04\/lisenet-homelab-diagram_apache.png\" width=\"1200\" \/><\/p>\n<h2>WordPress from a Custom Tarball<\/h2>\n<p>We won\u2019t be downloading WordPress from the Internet, but will be using our own custom build (tarball) instead. Each build is source controlled and tested locally prior releasing to the environment. <\/p>\n<p>The name of the tarball is wordpress-latest.tar.gz, and it\u2019s currently stored on the Katello server under \/var\/www\/html\/pub\/. This allows us to pull the archive from https:\/\/katello.hl.local\/pub\/wordpress-latest.tar.gz.<\/p>\n<p>Note that the file wp-config.php is never stored inside the tarball, but gets generated by Puppet.<\/p>\n<h2>Redundand Apache\/MySQL Architecture<\/h2>\n<p>To increase redundancy, each Apache server will be configured to use a different MySQL database server.<\/p>\n<p>Since our MySQL nodes are configured to use a Master\/Master replication, there should, in theory, be no difference in terms of data that\u2019s stored on each VM.<\/p>\n<p>We\u2019ll plug web1.hl.local in to db1.hl.local, and web2.hl.local in to db2.hl.local.<\/p>\n<h2>NFS Mount for Uploads<\/h2>\n<p>Both Apache servers will need an NFS client configured to mount shared storage.<\/p>\n<p>While users can use either of the Apache servers to upload files, we need to ensure that regardless of the VM they end up on, files across WordPress instances are the same. We could use rsync to solve the problem, but I\u2019m not sure on how well that would scale. Perhaps something to look at in the future.<\/p>\n<h2>Configuration with Puppet<\/h2>\n<p>Puppet master runs on the <a href=\"https:\/\/www.lisenet.com\/2016\/install-katello-on-centos-7\/\" target=\"_blank\">Katello<\/a> server.<\/p>\n<h3>Puppet Modules<\/h3>\n<p>We use the following Puppet modules:<\/p>\n<ol>\n<li><a href=\"https:\/\/forge.puppet.com\/derdanne\/nfs\" target=\"_blank\">derdanne-nfs<\/a> \u2013 to mount an NFS share for WordPress \/uploads folder <\/li>\n<li><a href=\"https:\/\/forge.puppet.com\/puppetlabs\/apache\" target=\"_blank\">puppetlabs-apache<\/a> \u2013 to install and configure Apache<\/li>\n<li><a href=\"https:\/\/forge.puppet.com\/puppet\/selinux\" target=\"_blank\">puppet-selinux<\/a> \u2013 to configure SELinux booleans (e.g. httpd_use_nfs)<\/li>\n<li><a href=\"https:\/\/forge.puppet.com\/hunner\/wordpress\" target=\"_blank\">hunner-wordpress<\/a> \u2013 to install WordPress<\/li>\n<\/ol>\n<p>Please see each module\u2019s documentation for features supported and configuration options available.<\/p>\n<h3>Firewall Configuration<\/h3>\n<p>Configure both Apache servers to allow HTTPS traffic:<\/p>\n<p>firewall { &#8216;007 allow HTTPS&#8217;:&#xD;<br \/>\n dport =&gt; &#8216;443&#8217;,&#xD;<br \/>\n source =&gt; &#8216;10.11.1.0\/24&#8217;,&#xD;<br \/>\n proto =&gt; tcp,&#xD;<br \/>\n action =&gt; accept,&#xD;<br \/>\n}<\/p>\n<p>There may be an insignificant performance penalty incurred while using encryption between HAProxy and Apache compared to plaintext HTTP, but we want to ensure that traffic is secured. HAProxy can be re-configured to offload TLS if this becomes a problem.<\/p>\n<h3>SELinux Booleans<\/h3>\n<p>Configure SELinux on both Apache servers. These are required in order to allow Apache to use NFS and connect to a remote MySQL instance. We also want to allow Apache (WordPress) to send email notifications.<\/p>\n<p>selinux::boolean { &#8216;httpd_use_nfs&#8217;: &#xD;<br \/>\n persistent =&gt; true, ensure =&gt; &#8216;on&#8217; &#xD;<br \/>\n}-&gt;&#xD;<br \/>\nselinux::boolean { &#8216;httpd_can_network_connect_db&#8217;: &#xD;<br \/>\n persistent =&gt; true, ensure =&gt; &#8216;on&#8217; &#xD;<br \/>\n}-&gt;&#xD;<br \/>\nselinux::boolean { &#8216;httpd_can_sendmail&#8217;:&#xD;<br \/>\n persistent =&gt; true, ensure =&gt; &#8216;on&#8217; &#xD;<br \/>\n}<\/p>\n<h3>Configure NFS Client<\/h3>\n<p>This needs to be applied for both Apache servers.<\/p>\n<p>class { &#8216;::nfs&#8217;:&#xD;<br \/>\n server_enabled =&gt; false,&#xD;<br \/>\n client_enabled =&gt; true,&#xD;<br \/>\n}-&gt;&#xD;<br \/>\nnfs::client::mount { &#8216;\/var\/www\/html\/wp-content\/uploads&#8217;:&#xD;<br \/>\n server =&gt; &#8216;nfsvip.hl.local&#8217;,&#xD;<br \/>\n share =&gt; &#8216;\/nfsshare\/uploads&#8217;,&#xD;<br \/>\n}<\/p>\n<p>The virtual IP (which NFS cluster runs on) is 10.11.1.31, and the DNS name is nfsvip.hl.local. The cluster is configured to export \/nfsshare. See <a href=\"https:\/\/www.lisenet.com\/2018\/configure-active-passive-nfs-server-on-a-pacemaker-cluster-with-puppet\/\" target=\"_blank\">here<\/a> for more info.<\/p>\n<h3>Install Apache<\/h3>\n<p>This needs to be applied for both Apache servers.<\/p>\n<p>We deploy one Apache virtualhost, and configure log forwarding to Graylog (see the custom_fragment section). I wrote a separate post for how to send Apache logs to Graylog, take a look <a href=\"https:\/\/www.lisenet.com\/2016\/send-apache-logs-to-graylog\/\" target=\"_blank\">here<\/a> if you need more info.<\/p>\n<p>TLS certificates are taken care of by the main Puppet manifest for the environment. See <a href=\"https:\/\/www.lisenet.com\/2018\/katello-working-with-puppet-modules-and-creating-the-main-manifest\/\" target=\"_blank\">here<\/a> for more info.<\/p>\n<p>package { &#8216;php-mysql&#8217;: &#xD;<br \/>\n ensure =&gt; &#8216;installed&#8217; &#xD;<br \/>\n}-&gt;&#xD;<br \/>\nclass { &#8216;apache&#8217;:&#xD;<br \/>\n default_vhost =&gt; false,&#xD;<br \/>\n default_ssl_vhost =&gt; false,&#xD;<br \/>\n default_mods =&gt; false,&#xD;<br \/>\n mpm_module =&gt; &#8216;prefork&#8217;,&#xD;<br \/>\n server_signature =&gt; &#8216;Off&#8217;,&#xD;<br \/>\n server_tokens =&gt; &#8216;Prod&#8217;,&#xD;<br \/>\n trace_enable =&gt; &#8216;Off&#8217;,&#xD;<br \/>\n log_formats =&gt; { graylog_access =&gt; &#8216;{ &#8220;version&#8221;: &#8220;1.1&#8221;, &#8220;host&#8221;: &#8220;%V&#8221;, &#8220;short_message&#8221;: &#8220;%r&#8221;, &#8220;timestamp&#8221;: %{%s}t, &#8220;level&#8221;: 6, &#8220;_user_agent&#8221;: &#8220;%i&#8221;, &#8220;_source_ip&#8221;: &#8220;%h&#8221;, &#8220;_duration_usec&#8221;: %D, &#8220;_duration_sec&#8221;: %T, &#8220;_request_size_byte&#8221;: %O, &#8220;_http_status_orig&#8221;: %s, &#8220;_http_status&#8221;: %&gt;s, &#8220;_http_request_path&#8221;: &#8220;%U&#8221;, &#8220;_http_request&#8221;: &#8220;%U%q&#8221;, &#8220;_http_method&#8221;: &#8220;%m&#8221;, &#8220;_http_referer&#8221;: &#8220;%i&#8221;, &#8220;_from_apache&#8221;: &#8220;true&#8221; }&#8217; },&#xD;<br \/>\n}&#xD;<br \/>\ninclude apache::mod::alias&#xD;<br \/>\ninclude apache::mod::headers&#xD;<br \/>\ninclude apache::mod::php&#xD;<br \/>\ninclude apache::mod::rewrite&#xD;<br \/>\ninclude apache::mod::ssl&#xD;<br \/>\n::apache::mod { &#8216;logio&#8217;: }&#xD;<br \/>\n&#xD;<br \/>\n## Configure VirtualHosts&#xD;<br \/>\napache::vhost { &#8216;blog_https&#8217;:&#xD;<br \/>\n port =&gt; 443,&#xD;<br \/>\n servername =&gt; &#8216;blog.hl.local&#8217;,&#xD;<br \/>\n docroot =&gt; &#8216;\/var\/www\/html&#8217;,&#xD;<br \/>\n manage_docroot =&gt; false,&#xD;<br \/>\n options =&gt; [&#8216;FollowSymLinks&#8217;,&#8217;MultiViews&#8217;],&#xD;<br \/>\n override =&gt; &#8216;All&#8217;,&#xD;<br \/>\n suphp_engine =&gt; &#8216;off&#8217;,&#xD;<br \/>\n ssl =&gt; true,&#xD;<br \/>\n ssl_cert =&gt; &#8216;\/etc\/pki\/tls\/certs\/hl.crt&#8217;,&#xD;<br \/>\n ssl_key =&gt; &#8216;\/etc\/pki\/tls\/private\/hl.key&#8217;,&#xD;<br \/>\n ssl_protocol =&gt; [&#8216;all&#8217;, &#8216;-SSLv2&#8217;, &#8216;-SSLv3&#8217;],&#xD;<br \/>\n ssl_cipher =&gt; &#8216;HIGH:!aNULL!MD5:!RC4&#8217;,&#xD;<br \/>\n ssl_honorcipherorder =&gt; &#8216;On&#8217;,&#xD;<br \/>\n redirectmatch_status =&gt; [&#8216;301&#8217;],&#xD;<br \/>\n redirectmatch_regexp =&gt; [&#8216;(.*).gz&#8217;],&#xD;<br \/>\n redirectmatch_dest =&gt; [&#8216;\/&#8217;],&#xD;<br \/>\n custom_fragment =&gt; &#8216;CustomLog &#8220;|\/usr\/bin\/nc -u syslog.hl.local 12201&#8221; graylog_access&#8217;,&#xD;<br \/>\n}<\/p>\n<h3>Install WordPress from Our Custom Tarball<\/h3>\n<p>We want to use our <a href=\"https:\/\/www.lisenet.com\/2018\/configure-mysql-replication-with-puppet\/\" target=\"_blank\">existing<\/a> MySQL configuration, meaning that we don\u2019t want WordPress creating any databases nor users. The details below are the ones we used when setting up MySQL servers.<\/p>\n<p>The only thing that\u2019s going to different here is the db_host parameter \u2013 one Apache server uses db1.hl.local, another one uses db2.hl.local.<\/p>\n<p>class { &#8216;wordpress&#8217;:&#xD;<br \/>\n db_user =&gt; &#8216;dbuser1&#8217;,&#xD;<br \/>\n db_password =&gt; &#8216;PleaseChangeMe&#8217;,&#xD;<br \/>\n db_name =&gt; &#8216;blog&#8217;,&#xD;<br \/>\n db_host =&gt; &#8216;db1.hl.local&#8217;,&#xD;<br \/>\n create_db =&gt; false,&#xD;<br \/>\n create_db_user =&gt; false,&#xD;<br \/>\n install_dir =&gt; &#8216;\/var\/www\/html&#8217;,&#xD;<br \/>\n install_url =&gt; &#8216;http:\/\/katello.hl.local\/pub&#8217;,&#xD;<br \/>\n version =&gt; &#8216;latest&#8217;,&#xD;<br \/>\n wp_owner =&gt; &#8216;apache&#8217;,&#xD;<br \/>\n wp_group =&gt; &#8216;root&#8217;,&#xD;<br \/>\n}<\/p>\n<p>Note how we set the owner to apache. This is something we may want to harden further depending on security requirements.<\/p>\n<p>If all goes well, we should end up with both servers using the NFS share:<\/p>\n<p>[<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a> ~]# df -h|egrep &#8220;File|uploads&#8221;&#xD;<br \/>\nFilesystem Size Used Avail Use% Mounted on&#xD;<br \/>\nnfsvip.hl.local:\/nfsshare\/uploads 2.0G 53M 1.9G 3% \/var\/www\/html\/wp-content\/uploads<br \/>\n[<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a> ~]# df -h|egrep &#8220;File|uploads&#8221;&#xD;<br \/>\nFilesystem Size Used Avail Use% Mounted on&#xD;<br \/>\nnfsvip.hl.local:\/nfsshare\/uploads 2.0G 53M 1.9G 3% \/var\/www\/html\/wp-content\/uploads<\/p>\n<p>We should also be able to create Graylog dashboard widgets by using Apache data, e.g.:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"205\" src=\"https:\/\/www.lisenet.com\/wp-content\/uploads\/2018\/05\/lisenet-homelab-graylog-widget.png\" width=\"446\" \/><\/p>\n<h2>What\u2019s Next?<\/h2>\n<p>We\u2019ll look into putting a pair of HAProxy servers in front of Apache to perform load balancing.<\/p>\n<p> <a href=\"https:\/\/www.lisenet.com\/2018\/configure-apache-server-and-deploy-wordpress-with-puppet\/\" target=\"_blank\">Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We\u2019re going to use Puppet to install Apache and WordPress. This article is part of the Homelab Project with KVM, Katello and Puppet series. Homelab We have two CentOS 7 servers installed which we want to configure as follows: web1.hl.local (10.11.1.21) \u2013 Apache server with WordPress and NFS mountweb2.hl.local (10.11.1.22) \u2013 Apache server with WordPress &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/configure-apache-server-and-deploy-wordpress-with-puppet-lisenet-com-linux-security\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Configure Apache Server and Deploy WordPress with Puppet | Lisenet.com :: Linux | Security&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-436","post","type-post","status-publish","format-standard","hentry","category-linux"],"_links":{"self":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/comments?post=436"}],"version-history":[{"count":0,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/436\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/media?parent=436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/categories?post=436"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/tags?post=436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}