{"id":953,"date":"2016-10-11T09:20:51","date_gmt":"2016-10-11T07:20:51","guid":{"rendered":"http:\/\/www.netnea.com\/cms\/?page_id=953"},"modified":"2025-11-07T11:05:26","modified_gmt":"2025-11-07T10:05:26","slug":"apache-tutorial-10_efficient-configuration-and-debugging","status":"publish","type":"page","link":"https:\/\/www.netnea.com\/cms\/apache-tutorial-10_efficient-configuration-and-debugging\/","title":{"rendered":"Efficiently Configuring and Debugging Apache and ModSec in the Shell"},"content":{"rendered":"\n<h2 id=\"efficiently-configuring-and-debugging-apache-and-modsec-in-the-shell\">Efficiently configuring and debugging Apache and ModSec in the shell<\/h2>\n<h3 id=\"what-are-we-doing\">What are we doing?<\/h3>\n<p>We are setting up shell tools and a method enabling us to efficiently edit Apache configurations and test them in just a few seconds without using a browser or having to constantly search through files by hand.<\/p>\n<h3 id=\"why-are-we-doing-this\">Why are we doing this?<\/h3>\n<p>Successfully configuring the Apache web server requires a lot of know-how and experience. When ModSecurity is added and intervenes in processing this make configuration even more complicated. That\u2019s why it\u2019s necessary to set up the right tools and a systematic workflow. This is the objective of this tutorial.<\/p>\n<p>The tutorial is a bit pedantic, since it takes optimizing individual key presses in all seriousness. Since dozens, if not hundreds of actions have to be initiated in sequence in the configuration of a web server, there is plenty of room for optimizing the workflow, as ridiculous as it may seem. The advantage of this is that we can remove unnecessary ballast, clearing the view to the actual configuration problem. To this end this tutorial will be presenting some tricks and ideas that promise to be of benefit.<\/p>\n<h3 id=\"requirements\">Requirements<\/h3>\n<ul>\n<li>An Apache web server, ideally one created using the file structure shown in <a href=\"https:\/\/www.netnea.com\/cms\/apache_tutorial_1_apache_compilieren\/\">Tutorial 1 (Compiling an Apache web server)<\/a>.<\/li>\n<li>Understanding of the minimal configuration in <a href=\"https:\/\/www.netnea.com\/cms\/apache_tutorial_2_apache_minimal_konfigurieren\/\">Tutorial 2 (Configuring a minimal Apache server)<\/a>.<\/li>\n<li>An Apache web server with SSL\/TLS support as in <a href=\"https:\/\/www.netnea.com\/cms\/apache-tutorial-4-ssl-server-konfigurieren\">Tutorial 4 (Configuring an SSL server)<\/a><\/li>\n<li>An Apache web server with extended access log as in <a href=\"https:\/\/www.netnea.com\/cms\/apache-tutorial-5-zugriffslog-ausbauen\/\">Tutorial 5 (Extending and analyzing the access log)<\/a><\/li>\n<li>An Apache web server with ModSecurity as in <a href=\"https:\/\/www.netnea.com\/cms\/apache-tutorial-6-modsecurity-einbinden\/\">Tutorial 6 (Embedding ModSecurity)<\/a><\/li>\n<li>An Apache web server with a Core Rule Set installation as in <a href=\"http:\/\/www.netnea.com\/cms\/modsecurity-core-rules-einbinden\/\">Tutorial 7 (Embedding Core Rules)<\/a><\/li>\n<\/ul>\n<h3 id=\"step-1-curl\">Step 1: Curl<\/h3>\n<p>Curl is the right tool for making HTTP requests. HTTP must of course first work in the browser or the application. But when debugging or configuring new features the browser normally proves to be very cumbersome. Cooking handling, the size of the window, automatically following redirects, etc. all contribute towards making work in the browser very time consuming. That\u2019s why when you have identified a problem in the browser it\u2019s better to reproduce it using <code>curl<\/code>, find the bug, fix it in the server configuration and finally, verify it in the browser.<\/p>\n<p>We\u2019ve used <code>curl<\/code> a number of different ways in the preceding tutorials. This puts us in a strong position. But adding one or two features to your toolbox is still worthwhile.<\/p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb1-1\"><a href=\"#cb1-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"ex\">curl<\/span> --cookie-jar \/tmp\/cookies.txt --cookie \/tmp\/cookies.txt --data <span class=\"st\">&quot;username=test&quot;<\/span><span class=\"kw\">\\<\/span><\/span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\" aria-hidden=\"true\"><\/a>    <span class=\"ex\">--data<\/span> <span class=\"st\">&quot;password=xxxxxxxx&quot;<\/span> http:\/\/localhost\/login.action<\/span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\" aria-hidden=\"true\"><\/a><span class=\"ex\">...<\/span><\/span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"ex\">curl<\/span> http:\/\/localhost\/login.html --next --cookie-jar \/tmp\/cookies.txt --cookie \/tmp\/cookies.txt<span class=\"kw\">\\<\/span><\/span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\" aria-hidden=\"true\"><\/a>    <span class=\"ex\">--data<\/span> <span class=\"st\">&quot;username=test&quot;<\/span> --data <span class=\"st\">&quot;password=xxxxxxxx&quot;<\/span> http:\/\/localhost\/login.action<\/span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\" aria-hidden=\"true\"><\/a><span class=\"ex\">...<\/span><\/span><\/code><\/pre><\/div>\n<p>The first example works with a cookie file, writes the cookies received and reads new requests from the cookies. In the second example, which works only with a relatively new version of <code>curl<\/code>, multiple requests are put together on a single line. What\u2019s interesting is the <code>--next<\/code> option dividing the command line arguments. The parameters following <code>--next<\/code> apply only to the right. This means that the rules above initially made a GET request, and then followed with a POST request on the same TCP connection and afterwards stored the session cookie in the cookie jar.<\/p>\n<h3 id=\"step-2-single-apache-configuration-file\">Step 2: Single Apache configuration file<\/h3>\n<p>The Apache configuration used in this series of tutorials is intended for the lab. By this I mean a test environment in which you can quickly try out the configuration or perform debugging regardless of productive HTTP traffic. One essential feature of the Apache configuration being used was the method for accommodating the entire configuration (with the exception of the OWASP ModSecurity Core Rule Set) in one file. The advantage of this is that we are able to quickly find our way around in the file and get to specific passages via standard search commands. However, this principle is not only beneficial in a lab-like setting. Working this way is also useful in a production environment for eliminating most conflicts and redundancies. This however runs counter to the widespread trend towards modularization of the web server configuration into a variety of files and induces errors. Indeed, I have many times been confronted by setups that configured the VirtualHosts multiple times, set up conflicting access restrictions and whose administrators seemed surprised to learn that the directives configured were being overridden in other places. This is the reason why whenever possible I try to work on one configuration in a single file.<\/p>\n<p>In any case, it\u2019s important to have a clear structure and a stringent approach. It must be clear where each directive is included in the configuration. Marked out sections have to be sufficiently commented (without hiding the directive in a jungle of documentation as in the default Apache configuration). The example configuration in the preceding tutorials attempted to do just that.<\/p>\n<p>In a lab-like setting, it is probably best to set up a template. For my own work I have come up with a configuration named <code>httpd.conf_template<\/code>. It\u2019s a ready-to-go configuration including reverse proxy configuration, the core rules, the performance log supported by ModSecurity, etc. and is based on the configurations from the preceding tutorials. When I start working on a new problem, I duplicate this template file and then slightly adjust this configuration to bend it towards the right scenario.<\/p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb2-1\"><a href=\"#cb2-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"fu\">cp<\/span> httpd.conf_template httpd.conf_problem-of-the-day<\/span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"ex\">vim<\/span> httpd.conf_problem-of-the-day<\/span><\/code><\/pre><\/div>\n<p><code>Vim<\/code> is perfectly suited for editing Apache configurations, but the editor does not play such a key role so long as it supports syntax highlighting, which in my opinion is an invaluable feature.<\/p>\n<h3 id=\"step-3-apachex\">Step 3: Apachex<\/h3>\n<p><code>curl<\/code> and a single Apache file provide us a good foundation for quickly customizing configurations and testing them just as fast. This usually involves a somewhat annoying step between these often repeated steps: restarting the web server. In the first two tutorials we started the sever each time using the <code>-X<\/code> command line flag. I often work with <code>-X<\/code>, because it does not put the server into daemon mode and instead allows it to run as a single process in the foreground. Any server crash shows up immediately in the shell. If we start the web server as usual and run it as a daemon, we then have to watch the error log and make sure that we don\u2019t miss a crash, which can involve all kinds of strange effects. Although it\u2019s possible to monitor for crashes this way, in my experience it is a surprisingly serious drawback. So I work with <code>-X<\/code>.<\/p>\n<p>The normal procedure for working with a single process Apache is to alternate starting the binary with the configuration file configured above and stopping it via <code>CTRL-C<\/code>. This is accompanied by two more disadvantages: First, we have to enter the name of the configuration file, or access it from the shell history starting from the second iteration. What\u2019s even less pleasant is the loss of semaphores caused by using <code>CTRL-C<\/code> to quit the web server. Semaphores are a Linux operating system communication structure used by the web server. The number of semaphores is finite and when we quit the web server, it often fails to release a reserved semaphore. Eventually, the supply of semaphores is exhausted and the server can longer be started. It will instead issue the following error message, which can easily be attributed to the semaphore problem:<\/p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb3-1\"><a href=\"#cb3-1\" aria-hidden=\"true\"><\/a>[<span class=\"ex\">emerg<\/span>] (28)<span class=\"ex\">No<\/span> space left on device: Couldn<span class=\"st\">&#39;t create accept lock<\/span><\/span><\/code><\/pre><\/div>\n<p>The problem is not the lack of space, but the lack of free semaphores. They have to be recovered before a restart. The following construction has proven useful:<\/p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb4-1\"><a href=\"#cb4-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"fu\">sudo<\/span> ipcs -s <span class=\"kw\">|<\/span> <span class=\"fu\">grep<\/span> www-data <span class=\"kw\">|<\/span> <span class=\"fu\">awk<\/span> <span class=\"st\">&#39;{ print $2 }&#39;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">xargs<\/span> -n 1 sudo ipcrm sem<\/span><\/code><\/pre><\/div>\n<p>We use <code>ipcs -s<\/code> to read a list of semaphores, select the right lines via the web server\u2019s user name and then use <code>awk<\/code> to select the second column of the output. We extract the ID of the semaphore, which when then use xargs and <code>ipcrm sem<\/code> to delete.<\/p>\n<p>This is how to get a handle on this problem, the error message is still a nuisance, but repeatedly having to go back through the history to keep from having to re-type the name of the configuration file is no longer necessary. It\u2019s better to have both done by a script: <code>apachex<\/code>. This script is available online (<a href=\"https:\/\/github.com\/Apache-Labor\/labor\/blob\/master\/bin\/apachex\">apachex<\/a>).<\/p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb5-1\"><a href=\"#cb5-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <\/span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\" aria-hidden=\"true\"><\/a><span class=\"ex\">.\/bin\/apachex<\/span> -h<\/span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\" aria-hidden=\"true\"><\/a><span class=\"ex\">.\/bin\/apachex<\/span><\/span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\" aria-hidden=\"true\"><\/a><span class=\"ex\">Script<\/span> to launch apache repeatedly via httpd -X.<\/span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\" aria-hidden=\"true\"><\/a><span class=\"ex\">The<\/span> script guesses the desired apache config and handles<\/span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\" aria-hidden=\"true\"><\/a> <span class=\"ex\">-<\/span> sudo<\/span>\n<span id=\"cb5-10\"><a href=\"#cb5-10\" aria-hidden=\"true\"><\/a> <span class=\"ex\">-<\/span> pidfile<\/span>\n<span id=\"cb5-11\"><a href=\"#cb5-11\" aria-hidden=\"true\"><\/a> <span class=\"ex\">-<\/span> semaphore cleanup<\/span><\/code><\/pre><\/div>\n<p>The script first attempts to identify the most recently edited local Apache configuration file. This is very likely to be the configuration we want to test. If the assumption made is incorrect, it may be necessary to exit the script and <code>touch<\/code> the file you want. The script is aware of the different locations for configuration files and selects one file each time. It derives the httpd binary from this file. It then uses this information to start a loop. Then in the loop any web server process running is stopped, the semaphores are retrieved and the server restarted, with the help of <code>sudo<\/code> if necessary. The script uses the configuration file identified at the beginning. We send the single process Apache to the background, but keep it running in the current shell. It is thus not yet running as a separate daemon in the background, but will continue outputting it\u2019s information to our shell. We will then continue to be informed if the process crashes.<\/p>\n<p>Now, before we can start a new iteration of the loop, we pause for a bit and give the user the option of exiting the script either via the <code>q<\/code> key or initiating another iteration of the loop via the Enter key.<\/p>\n<p>The script thus provides threefold functionality: * It identifies the Apache configuration file we want to test. * It stops and starts a web server process at the press of a key * In ensures that the supply of semaphores is not exhausted<\/p>\n<p>In summary: One press of the Enter key and Apache will be restarted using the most recently edited configuration:<\/p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb6-1\"><a href=\"#cb6-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"ex\">apachex<\/span><\/span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\" aria-hidden=\"true\"><\/a><span class=\"ex\">Launching<\/span> apache on config file \/apache\/conf\/httpd.conf_problem-of-the-day ... ok<\/span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\" aria-hidden=\"true\"><\/a><span class=\"ex\">Press<\/span> [enter] to restart apache, enter [q] to stop apache and exit: <\/span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\" aria-hidden=\"true\"><\/a><span class=\"ex\">Stopping<\/span> active apache process ... ok<\/span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\" aria-hidden=\"true\"><\/a><span class=\"ex\">Launching<\/span> apache on config file \/apache\/conf\/httpd.conf_problem-of-the-day ... ok<\/span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb6-10\"><a href=\"#cb6-10\" aria-hidden=\"true\"><\/a><span class=\"ex\">Press<\/span> [enter] to restart apache, enter [q] to stop apache and exit: q<\/span>\n<span id=\"cb6-11\"><a href=\"#cb6-11\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb6-12\"><a href=\"#cb6-12\" aria-hidden=\"true\"><\/a><span class=\"ex\">Bailing<\/span> out ... ok<\/span><\/code><\/pre><\/div>\n<h3 id=\"step-4-lastrequestsummary\">Step 4: lastrequestsummary<\/h3>\n<p>We still lack intelligent access to the log files. <code>curl -v<\/code> does provide us feedback about the results of a request, but with ModSecurity rules it\u2019s important to be able to follow processing on the server side. And with its chatty and unclear log file entries, ModSecurity poses a challenge, especially since a single request can quickly generate more logging that can fit in a window and most of this information is of no interest. What we are missing is a summary of a request including information from both the <code>access log<\/code> and the <code>error log<\/code>. The <code>lastrequestsummary<\/code> adds just such an analysis (<a href=\"https:\/\/github.com\/Apache-Labor\/labor\/blob\/master\/bin\/lastrequestsummary\">lastrequestsummary<\/a>):<\/p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb7-1\"><a href=\"#cb7-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"fu\">cat<\/span> lastrequestsummary<\/span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\" aria-hidden=\"true\"><\/a><span class=\"co\">#!\/bin\/bash<\/span><\/span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\" aria-hidden=\"true\"><\/a><span class=\"co\">#<\/span><\/span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\" aria-hidden=\"true\"><\/a><span class=\"co\"># Script that extracts information from the latest request in the access<\/span><\/span>\n<span id=\"cb7-6\"><a href=\"#cb7-6\" aria-hidden=\"true\"><\/a><span class=\"co\"># log and enriches this with info from the error-log.<\/span><\/span>\n<span id=\"cb7-7\"><a href=\"#cb7-7\" aria-hidden=\"true\"><\/a><span class=\"co\">#<\/span><\/span>\n<span id=\"cb7-8\"><a href=\"#cb7-8\" aria-hidden=\"true\"><\/a><span class=\"co\"># Script is meant to be called regularly via watch.<\/span><\/span>\n<span id=\"cb7-9\"><a href=\"#cb7-9\" aria-hidden=\"true\"><\/a><span class=\"co\">#<\/span><\/span>\n<span id=\"cb7-10\"><a href=\"#cb7-10\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-11\"><a href=\"#cb7-11\" aria-hidden=\"true\"><\/a><span class=\"va\">ACCESSLOG=<\/span><span class=\"st\">&quot;<\/span><span class=\"va\">$1<\/span><span class=\"st\">&quot;<\/span><\/span>\n<span id=\"cb7-12\"><a href=\"#cb7-12\" aria-hidden=\"true\"><\/a><span class=\"va\">ERRORLOG=<\/span><span class=\"st\">&quot;<\/span><span class=\"va\">$2<\/span><span class=\"st\">&quot;<\/span><\/span>\n<span id=\"cb7-13\"><a href=\"#cb7-13\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-14\"><a href=\"#cb7-14\" aria-hidden=\"true\"><\/a><span class=\"va\">ACCESS_IGNORE_REGEX=<\/span><span class=\"st\">&quot;(heartbeat)&quot;<\/span><\/span>\n<span id=\"cb7-15\"><a href=\"#cb7-15\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-16\"><a href=\"#cb7-16\" aria-hidden=\"true\"><\/a><span class=\"kw\">if<\/span><span class=\"bu\"> [<\/span> <span class=\"ot\">-z<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLOG<\/span><span class=\"st\">&quot;<\/span><span class=\"bu\"> ]<\/span>; <span class=\"kw\">then<\/span> <\/span>\n<span id=\"cb7-17\"><a href=\"#cb7-17\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Accesslog not passed via commandline. Please pass path to accesslog as first parameter. \\<\/span><\/span>\n<span id=\"cb7-18\"><a href=\"#cb7-18\" aria-hidden=\"true\"><\/a><span class=\"st\">This is fatal. Aborting.&quot;<\/span><\/span>\n<span id=\"cb7-19\"><a href=\"#cb7-19\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">exit<\/span> 1<\/span>\n<span id=\"cb7-20\"><a href=\"#cb7-20\" aria-hidden=\"true\"><\/a><span class=\"kw\">fi<\/span><\/span>\n<span id=\"cb7-21\"><a href=\"#cb7-21\" aria-hidden=\"true\"><\/a><span class=\"kw\">if<\/span><span class=\"bu\"> [<\/span> <span class=\"ot\">!<\/span> <span class=\"ot\">-f<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLOG<\/span><span class=\"st\">&quot;<\/span><span class=\"bu\"> ]<\/span>; <span class=\"kw\">then<\/span> <\/span>\n<span id=\"cb7-22\"><a href=\"#cb7-22\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Accesslog <\/span><span class=\"va\">$ACCESSLOG<\/span><span class=\"st\"> not found. This is fatal. Aborting.&quot;<\/span><\/span>\n<span id=\"cb7-23\"><a href=\"#cb7-23\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">exit<\/span> 1<\/span>\n<span id=\"cb7-24\"><a href=\"#cb7-24\" aria-hidden=\"true\"><\/a><span class=\"kw\">fi<\/span><\/span>\n<span id=\"cb7-25\"><a href=\"#cb7-25\" aria-hidden=\"true\"><\/a><span class=\"kw\">if<\/span><span class=\"bu\"> [<\/span> <span class=\"ot\">-z<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ERRORLOG<\/span><span class=\"st\">&quot;<\/span><span class=\"bu\"> ]<\/span>; <span class=\"kw\">then<\/span> <\/span>\n<span id=\"cb7-26\"><a href=\"#cb7-26\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Errorlog not passed via commandline. Please pass path to errorlog as first parameter.\\<\/span><\/span>\n<span id=\"cb7-27\"><a href=\"#cb7-27\" aria-hidden=\"true\"><\/a><span class=\"st\">This is fatal. Aborting.&quot;<\/span><\/span>\n<span id=\"cb7-28\"><a href=\"#cb7-28\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">exit<\/span> 1<\/span>\n<span id=\"cb7-29\"><a href=\"#cb7-29\" aria-hidden=\"true\"><\/a><span class=\"kw\">fi<\/span><\/span>\n<span id=\"cb7-30\"><a href=\"#cb7-30\" aria-hidden=\"true\"><\/a><span class=\"kw\">if<\/span><span class=\"bu\"> [<\/span> <span class=\"ot\">!<\/span> <span class=\"ot\">-f<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ERRORLOG<\/span><span class=\"st\">&quot;<\/span><span class=\"bu\"> ]<\/span>; <span class=\"kw\">then<\/span> <\/span>\n<span id=\"cb7-31\"><a href=\"#cb7-31\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Errorlog <\/span><span class=\"va\">$ERRORLOG<\/span><span class=\"st\"> not found. This is fatal. Aborting.&quot;<\/span><\/span>\n<span id=\"cb7-32\"><a href=\"#cb7-32\" aria-hidden=\"true\"><\/a>        <span class=\"bu\">exit<\/span> 1<\/span>\n<span id=\"cb7-33\"><a href=\"#cb7-33\" aria-hidden=\"true\"><\/a><span class=\"kw\">fi<\/span><\/span>\n<span id=\"cb7-34\"><a href=\"#cb7-34\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-35\"><a href=\"#cb7-35\" aria-hidden=\"true\"><\/a><span class=\"va\">ACCESSLINE=$(<\/span><span class=\"fu\">tail<\/span> -200 <span class=\"va\">$ACCESSLOG<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">grep<\/span> -E -v <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESS_IGNORE_REGEX<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">tail<\/span> -1<span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-36\"><a href=\"#cb7-36\" aria-hidden=\"true\"><\/a><span class=\"va\">ID=$(<\/span><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLINE<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">egrep<\/span> -o <span class=\"st\">&quot; [a-zA-Z0-9@-]{24} &quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">tr<\/span> -d <span class=\"st\">&quot; &quot;<\/span><span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-37\"><a href=\"#cb7-37\" aria-hidden=\"true\"><\/a><span class=\"va\">METHOD_PATH=$(<\/span><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLINE<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">cut<\/span> -d<span class=\"dt\">\\ <\/span> -f6,7 <span class=\"kw\">|<\/span> <span class=\"fu\">cut<\/span> -b2-<span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-38\"><a href=\"#cb7-38\" aria-hidden=\"true\"><\/a><span class=\"va\">STATUS=$(<\/span><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLINE<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">cut<\/span> -d<span class=\"dt\">\\ <\/span> -f9<span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-39\"><a href=\"#cb7-39\" aria-hidden=\"true\"><\/a><span class=\"va\">SCORES=$(<\/span><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLINE<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">egrep<\/span> -o <span class=\"st\">&quot;[0-9-]+ [0-9-]+$&quot;<\/span><span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-40\"><a href=\"#cb7-40\" aria-hidden=\"true\"><\/a><span class=\"va\">TIME=$(<\/span><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLINE<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">cut<\/span> -d<span class=\"dt\">\\ <\/span> -f5 <span class=\"kw\">|<\/span> <span class=\"fu\">cut<\/span> -d. -f1<span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-41\"><a href=\"#cb7-41\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-42\"><a href=\"#cb7-42\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-43\"><a href=\"#cb7-43\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$(<\/span><span class=\"fu\">date<\/span> +<span class=\"st\">&quot;%H:%M:%S&quot;<\/span><span class=\"va\">)<\/span><span class=\"st\"> watching: <\/span><span class=\"va\">$ACCESSLOG<\/span><span class=\"st\">  <\/span><span class=\"va\">$ERRORLOG<\/span><span class=\"st\">&quot;<\/span><\/span>\n<span id=\"cb7-44\"><a href=\"#cb7-44\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span><\/span>\n<span id=\"cb7-45\"><a href=\"#cb7-45\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$TIME<\/span><span class=\"st\"> <\/span><span class=\"va\">$STATUS<\/span><span class=\"st\"> <\/span><span class=\"va\">$SCORES<\/span><span class=\"st\"> <\/span><span class=\"va\">$METHOD_PATH<\/span><span class=\"st\"> (<\/span><span class=\"va\">$ID<\/span><span class=\"st\">)&quot;<\/span><\/span>\n<span id=\"cb7-46\"><a href=\"#cb7-46\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span><\/span>\n<span id=\"cb7-47\"><a href=\"#cb7-47\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-48\"><a href=\"#cb7-48\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;ModSecurity Rules Triggered:&quot;<\/span><\/span>\n<span id=\"cb7-49\"><a href=\"#cb7-49\" aria-hidden=\"true\"><\/a><span class=\"va\">MODSEC=$(<\/span><span class=\"fu\">tail<\/span> -500 <span class=\"va\">$ERRORLOG<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">grep<\/span> <span class=\"va\">$ID<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">grep<\/span> -o -E <span class=\"st\">&quot; (at|against) .*\\[file.*\\[id <\/span><span class=\"dt\">\\&quot;<\/span><span class=\"st\">[0-9]+.*\\<\/span><\/span>\n<span id=\"cb7-50\"><a href=\"#cb7-50\" aria-hidden=\"true\"><\/a><span class=\"st\">\\[msg <\/span><span class=\"dt\">\\&quot;<\/span><span class=\"st\">[^<\/span><span class=\"dt\">\\&quot;<\/span><span class=\"st\">]+&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">tr<\/span> -d <span class=\"dt\">\\&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">sed<\/span> -e <span class=\"st\">&quot;s\/ at the end of input at\/ at\/&quot;<\/span> -e <span class=\"st\">&quot;s\/ required. \/. \/&quot;<\/span> <span class=\"kw\">\\<\/span><\/span>\n<span id=\"cb7-51\"><a href=\"#cb7-51\" aria-hidden=\"true\"><\/a><span class=\"ex\">-e<\/span> <span class=\"st\">&quot;s\/\\[rev .*\\[msg\/[msg\/&quot;<\/span> -e <span class=\"st\">&quot;s\/\\. \/ \/&quot;<\/span> -e <span class=\"st\">&quot;s\/(Total .*\/(Total ...) ...\/&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">tr<\/span> -d <span class=\"dt\">\\]<\/span> <span class=\"kw\">|<\/span> <span class=\"kw\">\\<\/span><\/span>\n<span id=\"cb7-52\"><a href=\"#cb7-52\" aria-hidden=\"true\"><\/a><span class=\"fu\">cut<\/span> -d<span class=\"dt\">\\ <\/span> -f3,9,11- <span class=\"kw\">|<\/span><\/span>\n<span id=\"cb7-53\"><a href=\"#cb7-53\" aria-hidden=\"true\"><\/a><span class=\"fu\">sed<\/span> -e <span class=\"st\">&quot;s\/^\\([^ ]*\\) \\([^ ]*\\)\/\\2 \\1\/&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">awk<\/span> <span class=\"st\">&quot;{ printf <\/span><span class=\"dt\">\\&quot;<\/span><span class=\"st\">%+6s %-35s %s %s %s %s %s %s %s %s %s \\<\/span><\/span>\n<span id=\"cb7-54\"><a href=\"#cb7-54\" aria-hidden=\"true\"><\/a><span class=\"st\">%s %s %s %s %s %s %s %s %s\\n<\/span><span class=\"dt\">\\&quot;<\/span><span class=\"st\">, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">1, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">2, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">3, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">4, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">5, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">6, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">7, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">8, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">9, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">10, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">11, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">12, \\<\/span><\/span>\n<span id=\"cb7-55\"><a href=\"#cb7-55\" aria-hidden=\"true\"><\/a><span class=\"dt\">\\$<\/span><span class=\"st\">13, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">14, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">15, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">16, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">17, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">18, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">19, <\/span><span class=\"dt\">\\$<\/span><span class=\"st\">20 }&quot;<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">sed<\/span> -e <span class=\"st\">&quot;s\/\\ *$\/\/&quot;<\/span><span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-56\"><a href=\"#cb7-56\" aria-hidden=\"true\"><\/a><span class=\"co\"># This is a crazy one-liner. A description starting with &quot;grep -o -E&quot;:<\/span><\/span>\n<span id=\"cb7-57\"><a href=\"#cb7-57\" aria-hidden=\"true\"><\/a><span class=\"co\"># We grep for the various ModSec alert messages and take the content from the<\/span><\/span>\n<span id=\"cb7-58\"><a href=\"#cb7-58\" aria-hidden=\"true\"><\/a><span class=\"co\"># at\/against via the parameter name, the id up and including the message. tr<\/span><\/span>\n<span id=\"cb7-59\"><a href=\"#cb7-59\" aria-hidden=\"true\"><\/a><span class=\"co\"># and sed and again tr are then used to strip this down. Now cut is used to<\/span><\/span>\n<span id=\"cb7-60\"><a href=\"#cb7-60\" aria-hidden=\"true\"><\/a><span class=\"co\"># extract (1) the parameter, (2) the id and (3) the message. Then we use sed<\/span><\/span>\n<span id=\"cb7-61\"><a href=\"#cb7-61\" aria-hidden=\"true\"><\/a><span class=\"co\"># to swap the position of the parameter and the id. Then we used awk to print<\/span><\/span>\n<span id=\"cb7-62\"><a href=\"#cb7-62\" aria-hidden=\"true\"><\/a><span class=\"co\"># the three fields in a clean table. This demands the used of a lot of %s<\/span><\/span>\n<span id=\"cb7-63\"><a href=\"#cb7-63\" aria-hidden=\"true\"><\/a><span class=\"co\"># fields, which results in a lot of empty spaces at the end of the line, which<\/span><\/span>\n<span id=\"cb7-64\"><a href=\"#cb7-64\" aria-hidden=\"true\"><\/a><span class=\"co\"># are finally removed.<\/span><\/span>\n<span id=\"cb7-65\"><a href=\"#cb7-65\" aria-hidden=\"true\"><\/a><span class=\"kw\">if<\/span><span class=\"bu\"> [<\/span> <span class=\"ot\">-z<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$MODSEC<\/span><span class=\"st\">&quot;<\/span><span class=\"bu\"> ]<\/span>; <span class=\"kw\">then<\/span><\/span>\n<span id=\"cb7-66\"><a href=\"#cb7-66\" aria-hidden=\"true\"><\/a>        <span class=\"va\">MODSEC=<\/span><span class=\"st\">&quot;***NONE***&quot;<\/span><\/span>\n<span id=\"cb7-67\"><a href=\"#cb7-67\" aria-hidden=\"true\"><\/a><span class=\"kw\">fi<\/span><\/span>\n<span id=\"cb7-68\"><a href=\"#cb7-68\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$MODSEC<\/span><span class=\"st\">&quot;<\/span><\/span>\n<span id=\"cb7-69\"><a href=\"#cb7-69\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span><\/span>\n<span id=\"cb7-70\"><a href=\"#cb7-70\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-71\"><a href=\"#cb7-71\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Apache Error Log:&quot;<\/span><\/span>\n<span id=\"cb7-72\"><a href=\"#cb7-72\" aria-hidden=\"true\"><\/a><span class=\"va\">ERRORLINES=$(<\/span><span class=\"fu\">tail<\/span> -500 <span class=\"va\">$ERRORLOG<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">grep<\/span> <span class=\"va\">$ID<\/span> <span class=\"kw\">|<\/span> <span class=\"fu\">grep<\/span> -v -E <span class=\"st\">&quot;ModSecurity.*\\b(at|against)\\b&quot;<\/span><span class=\"va\">)<\/span><\/span>\n<span id=\"cb7-73\"><a href=\"#cb7-73\" aria-hidden=\"true\"><\/a><span class=\"kw\">if<\/span><span class=\"bu\"> [<\/span> <span class=\"ot\">-z<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ERRORLINES<\/span><span class=\"st\">&quot;<\/span><span class=\"bu\"> ]<\/span>; <span class=\"kw\">then<\/span><\/span>\n<span id=\"cb7-74\"><a href=\"#cb7-74\" aria-hidden=\"true\"><\/a>        <span class=\"va\">ERRORLOG=<\/span><span class=\"st\">&quot;***NONE***&quot;<\/span><\/span>\n<span id=\"cb7-75\"><a href=\"#cb7-75\" aria-hidden=\"true\"><\/a><span class=\"kw\">fi<\/span><\/span>\n<span id=\"cb7-76\"><a href=\"#cb7-76\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ERRORLINES<\/span><span class=\"st\">&quot;<\/span><\/span>\n<span id=\"cb7-77\"><a href=\"#cb7-77\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span><\/span>\n<span id=\"cb7-78\"><a href=\"#cb7-78\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb7-79\"><a href=\"#cb7-79\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Full Apache Access Log:&quot;<\/span><\/span>\n<span id=\"cb7-80\"><a href=\"#cb7-80\" aria-hidden=\"true\"><\/a><span class=\"bu\">echo<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$ACCESSLINE<\/span><span class=\"st\">&quot;<\/span><\/span><\/code><\/pre><\/div>\n<p>The script is not a very elegant implementation of the idea. In fact, it has somewhat of a brute character, but it also has the advantage that output can be easily changed. Which is highly recommended here, because the script is only an example of what has worked for me. Let\u2019s take a quick look at the script. It expects a request using the two log files as parameters $1 and $2. The last request is determined on this basis, whereas heartbeat requests (uninteresting requests from a monitoring service, for example) are ignored. The unique request ID is extracted from the request. Then the method, path, status, ModSecurity scores are extracted. This information is then output.<\/p>\n<p>Next comes a summary of ModSecurity messages related to the request. The very non-transparent construction is oriented towards <code>melsummary<\/code> introduced earlier, that is used in all of its complexity here. Last, but not least, the script prints the messages from the error log that were not ModSecurity alerts.<\/p>\n<p>Typical output from the script looks like this:<\/p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb8-1\"><a href=\"#cb8-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span> <span class=\"ex\">lastrequestsummary<\/span> \/apache\/logs\/access.log \/apache\/logs\/error.log <\/span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\" aria-hidden=\"true\"><\/a><span class=\"ex\">07<\/span>:53:20 watching: \/apache\/logs\/access.log  \/apache\/logs\/error.log<\/span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\" aria-hidden=\"true\"><\/a><span class=\"ex\">07<\/span>:53:14 200 2 0 GET \/index.html?a=..... (VqkfSH8AAQEAAHjqe40AAAAC)<\/span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\" aria-hidden=\"true\"><\/a><span class=\"ex\">ModSecurity<\/span> Rules Triggered:<\/span>\n<span id=\"cb8-7\"><a href=\"#cb8-7\" aria-hidden=\"true\"><\/a><span class=\"ex\">981172<\/span> ARGS:a                                           Restricted SQL Character Anomaly Detection Alert - \u2026<\/span>\n<span id=\"cb8-8\"><a href=\"#cb8-8\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb8-9\"><a href=\"#cb8-9\" aria-hidden=\"true\"><\/a><span class=\"ex\">Apache<\/span> Error Log:<\/span>\n<span id=\"cb8-10\"><a href=\"#cb8-10\" aria-hidden=\"true\"><\/a>[<span class=\"ex\">2016-01-27<\/span> 07:53:14.334862] [authz_core:debug] 127.0.0.1:36837 VqkfSH8AAQEAAHjqe40AAAAC AH01626: \u2026<\/span>\n<span id=\"cb8-11\"><a href=\"#cb8-11\" aria-hidden=\"true\"><\/a><span class=\"ex\">authorization<\/span> result of Require all granted: granted<\/span>\n<span id=\"cb8-12\"><a href=\"#cb8-12\" aria-hidden=\"true\"><\/a>[<span class=\"ex\">2016-01-27<\/span> 07:53:14.334899] [authz_core:debug] 127.0.0.1:36837 VqkfSH8AAQEAAHjqe40AAAAC AH01626: \u2026<\/span>\n<span id=\"cb8-13\"><a href=\"#cb8-13\" aria-hidden=\"true\"><\/a><span class=\"ex\">authorization<\/span> result of <span class=\"op\">&lt;<\/span>RequireAll<span class=\"op\">&gt;<\/span>: granted<\/span>\n<span id=\"cb8-14\"><a href=\"#cb8-14\" aria-hidden=\"true\"><\/a>[<span class=\"ex\">2016-01-27<\/span> 07:53:14.334914] [authz_core:debug] 127.0.0.1:36837 VqkfSH8AAQEAAHjqe40AAAAC AH01626: \u2026<\/span>\n<span id=\"cb8-15\"><a href=\"#cb8-15\" aria-hidden=\"true\"><\/a><span class=\"ex\">authorization<\/span> result of <span class=\"op\">&lt;<\/span>RequireAny<span class=\"op\">&gt;<\/span>: granted<\/span>\n<span id=\"cb8-16\"><a href=\"#cb8-16\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb8-17\"><a href=\"#cb8-17\" aria-hidden=\"true\"><\/a><span class=\"ex\">Full<\/span> Apache Access Log:<\/span>\n<span id=\"cb8-18\"><a href=\"#cb8-18\" aria-hidden=\"true\"><\/a><span class=\"ex\">127.0.0.1<\/span> - - [2016-01-27 07:53:14.333396] <span class=\"st\">&quot;GET \/index.html?a=..... HTTP\/1.1&quot;<\/span> 200 45 <span class=\"st\">&quot;-&quot;<\/span> <span class=\"st\">&quot;curl\/7.35.0&quot;<\/span> \u2026<\/span>\n<span id=\"cb8-19\"><a href=\"#cb8-19\" aria-hidden=\"true\"><\/a><span class=\"ex\">localhost<\/span> 127.0.0.1 80 - - + <span class=\"st\">&quot;-&quot;<\/span> VqkfSH8AAQEAAHjqe40AAAAC - - 125 256 -% 4294 560 130 250 2 0<\/span><\/code><\/pre><\/div>\n<p>On the third line we see the timestamp of the request, the HTTP status, the ModSecurity core rules incoming anomaly score, the outgoing anomaly score, method, path and finally in brackets, the unique request ID. The other lines are self-explanatory and are simply an illustration of what can be done with such a script.<\/p>\n<p>The trick is using <code>watch<\/code> to regularly call this script at short intervals. A separate shortcut script named <code>watch-lastrequestsummary<\/code> can be used for this (<a href=\"https:\/\/github.com\/Apache-Labor\/labor\/blob\/master\/bin\/watch-lastrequestsummary\">watch-lastrequestsummary<\/a>):<\/p>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb9-1\"><a href=\"#cb9-1\" aria-hidden=\"true\"><\/a>$<span class=\"op\">&gt;<\/span><span class=\"fu\">cat<\/span> watch-lastrequestsummary <\/span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\" aria-hidden=\"true\"><\/a><span class=\"co\">#!\/bin\/bash<\/span><\/span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\" aria-hidden=\"true\"><\/a><span class=\"co\">#<\/span><\/span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\" aria-hidden=\"true\"><\/a><span class=\"co\"># Watch lastrequestsummary every second<\/span><\/span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\" aria-hidden=\"true\"><\/a><span class=\"co\">#<\/span><\/span>\n<span id=\"cb9-6\"><a href=\"#cb9-6\" aria-hidden=\"true\"><\/a><span class=\"co\"># Adopt filenames as see fit<\/span><\/span>\n<span id=\"cb9-7\"><a href=\"#cb9-7\" aria-hidden=\"true\"><\/a><\/span>\n<span id=\"cb9-8\"><a href=\"#cb9-8\" aria-hidden=\"true\"><\/a><span class=\"ex\">watch<\/span> --interval 1 --no-title <span class=\"st\">&quot;lastrequestsummary \/apache\/logs\/access.log \/apache\/logs\/error.log&quot;<\/span><\/span><\/code><\/pre><\/div>\n<p>Particularly for production use, it is also advisable to adjust the file names here, or to have an intelligent search process set them automatically.<\/p>\n<h3 id=\"step-5-dividing-the-screen-into-4-sections\">Step 5: Dividing the screen into 4 sections<\/h3>\n<p>In the preceding four steps we have seen how to configure Apache, easily start it, talk to it as efficiently as possible and finally, to check its behavior in the log files. It\u2019s time to assign each of these steps to its own shell window. This brings us to a classic four-window setup which has proven to be very efficient in practice. Provided enough screen space is available, there\u2019s nothing against trying a 6 or 9-window setup, unless you\u2019d lose track of things by doing so.<\/p>\n<p>For me a 4-window setup has proven useful and I recommend using one while working on the Apache web server. I use a tiling window manager, but this is of course not required to set up this layout. The only thing that is important is arranging the windows in the right sequence for the workflow. By doing so, my eyes follow a circle in counterclockwise direction:<\/p>\n<ul>\n<li>Apache configuration (top left, window extended vertically)<\/li>\n<li>apachex (bottom left, window shortened vertically)<\/li>\n<li>curl (bottom right)<\/li>\n<li>watch melsummary (top right)<\/li>\n<\/ul>\n<p>The procedure goes as follows: The configuration is modified at the top left and pressing the Enter key restarts it at the bottom left. The desired curl request talks to the web server at the bottom right and with no additional interaction via the keyboard the content of the log files are output automatically above it and visually inspected. Then comes the next step in the configuration, restart, curl, inspect the log files \u2026 then back to configuration, restart, curl, inspect the log files, etc.<\/p>\n<p>This cyclical workflow is kept very lean. It enables me to go through one to three customization cycles per minute. This is how I create a new configuration step-by-step. The lean process also enables the development of very complicated ModSecurity formulas without getting lost along the way, because you can get caught up in editing the configuration or reading the log files when using lots of different parameters to call curl.<\/p>\n<p>Here\u2019s a screenshot of my desktop:<\/p>\n<figure>\n<img decoding=\"async\" src=\"https:\/\/www.netnea.com\/files\/4-shells-screenshot.png\" alt=\"\" \/><figcaption>Screenshot: 4 Shells<\/figcaption>\n<\/figure>\n<h3 id=\"references\">References<\/h3>\n<ul>\n<li><a href=\"https:\/\/raw.githubusercontent.com\/Apache-Labor\/labor\/master\/bin\/apachex\">apachex<\/a><\/li>\n<li><a href=\"https:\/\/raw.githubusercontent.com\/Apache-Labor\/labor\/master\/bin\/lastrequestsummary\">lastrequestsummary<\/a><\/li>\n<li><a href=\"https:\/\/raw.githubusercontent.com\/Apache-Labor\/labor\/master\/bin\/watch-lastrequestsummary\">watch-lastrequestsummary<\/a><\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Semaphore_%28programming%29\">Semaphore<\/a><\/li>\n<\/ul>\n<h3 id=\"license-copying-further-use\">License \/ Copying \/ Further use<\/h3>\n<p><a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/4.0\/\"><img decoding=\"async\" alt=\"Creative Commons License\" style=\"border-width:0\" src=\"https:\/\/i.creativecommons.org\/l\/by-nc-sa\/4.0\/80x15.png\" \/><\/a><br \/>This work is licensed under a <a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/4.0\/\">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License<\/a>.<\/p>\n<h5 id=\"changelog\">Changelog<\/h5>\n<ul>\n<li>2018-04-13: Update title format (markdown); rewordings (Simon Studer)<\/li>\n<li>2017-12-17: Fixed several links<\/li>\n<li>2017-02-16: Reformatting<\/li>\n<li>2016-10-10: Fixing small issues<\/li>\n<li>2016-07-15: Apache 2.4.20 -&gt; 2.4.23<\/li>\n<li>2016-07-15: Apache 2.4.20 -&gt; 2.4.23<\/li>\n<li>2016-04-18: Fixing small issues<\/li>\n<li>2016-03-10: Translated to English<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Efficiently configuring and debugging Apache and ModSec in the shell What are we doing? We are setting up shell tools and a method enabling us to efficiently edit Apache configurations and test them in just a few seconds without using a browser or having to constantly search through files by hand. Why are we doing [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-953","page","type-page","status-publish","czr-hentry"],"_links":{"self":[{"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/pages\/953","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/comments?post=953"}],"version-history":[{"count":2,"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/pages\/953\/revisions"}],"predecessor-version":[{"id":2064,"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/pages\/953\/revisions\/2064"}],"wp:attachment":[{"href":"https:\/\/www.netnea.com\/cms\/wp-json\/wp\/v2\/media?parent=953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}