You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
360 lines
63 KiB
360 lines
63 KiB
import { addBanner, addArticle, addTitle, addHeader, addParagraph, addClassParagraph, addSubHeader, addOrderedList, addUnorderedList, addBlockquote, addInset, addInsetList, addInsetCodeListing, addInsetBulletList, addImageWithCaption, addButtonGroup, addSidebar, addSyntax, menu, global_menu } from '/scripts/import.js'; |
|
import { local_menu } from '/scripts/webdev.js'; |
|
|
|
const heading = document.querySelector(".heading"); |
|
const global = document.querySelector(".global_menu"); |
|
const local = document.querySelector(".local_menu"); |
|
const sidebar = document.querySelector(".sidebar"); |
|
const main = document.querySelector(".main_content"); |
|
|
|
heading.append(addTitle("Linux Web Services")); |
|
heading.append(addParagraph("Scott Simpson - LinkedIn Learning - December 2016")); |
|
heading.append(addParagraph("Chapter 1 - Serve Web Content with Apache")); |
|
|
|
main.append(addSubHeader("What are HTTP Headers?")); |
|
main.append(addSubHeader("Preparation - port forwarding from the virtual machine to the host")); |
|
main.append(addParagraph("Note that this course is not specific to any Linux distro, so any code is going to be tested on both an Ubuntu and a CentOS system. To make it easier to identify the code,")); |
|
main.append(addClassParagraph("CentOS Code will look like this", "centos")); |
|
main.append(addParagraph("and")); |
|
main.append(addClassParagraph("Ubuntu code will look like this", "ubuntu")); |
|
main.append(addParagraph("In preparation for the course, as well as creating a new Centos 7 VM, I’ve set up port forwarding which will allow me to ssh from the host machine (Windows 10) to the VM.")); |
|
main.append(addParagraph("This is a really straightforward procedure and is detailed <a href=\"https://www.techrepublic.com/article/how-to-use-port-forwarding-in-virtualbox/\" class=\"linuxlink\">here</a>. Steps are as follows:")); |
|
main.append(addParagraph("In Virtual Box, select the machine and then click Settings. Click on the Network tab and then Port Forwarding (this is under the Advanced options so it may be necessary to expand these first). Click the plus sign to add a Port Forwarding rule and enter the settings shown in figure 1.")); |
|
main.append(addImageWithCaption("./images/figure1.png", "Figure 1 - the settings for setting up port forwarding between the VM and the host machine")); |
|
main.append(addParagraph("To ssh from the host to the VM, open a command window (I’m not sure if you have to run this as administrator but probably best to do that) and type the command:")); |
|
main.append(addSyntax("ssh -p2222 USERNAME@127.0.0.1")); |
|
main.append(addParagraph("Obviously, you will need to replace USERNAME with a valid username for the VM.")); |
|
main.append(addParagraph("To go the other way, that is to ssh to the host machine from the VM, you use a similar command:")); |
|
main.append(addSyntax("ssh -p22 USERNAME@x.x.x.x")); |
|
main.append(addParagraph("This time, you use a user account from the host machine and x.x.x.x represents the host machine’s IP address. I created a user on the Windows 10 machine (username LinuxUser, password $alvation) specifically to allow me to ssh without using my own account and everything seems to be okay until I get to the point of entering the password which seems to be getting rejected. This may be because the host machine needs to be restarted before I can use the account in this way, but I will investigate it further if that becomes necessary.")); |
|
main.append(addParagraph("Additional note to this, in order for the Windows 10 host machine to be able to accept ssh connections, it needs to have OpenSSH server installed. In order to allow it to connect to other machines (including the VM), OpenSSH client installed. See <a href=\"https://winaero.com/blog/enable-openssh-server-windows-10/\" class=\"linuxlink\">this page</a> for more info. It also provides some additional interesting information relating to OpenSSH server.")); |
|
main.append(addSyntax("How a Web Server Works")); |
|
main.append(addParagraph("Quite simply, web servers deliver web resources such as web pages, videos and so on to a client. Usually, this will be a browser, but you can also use tools such as CURL to make a request directly and applications can also make requests.")); |
|
main.append(addParagraph("Apache is probably the most popular web server at the moment but nginx is becoming more popular and if you must set up a Windows web server, you will probably use Microsoft’s Internet Information Services (IIS) application.")); |
|
main.append(addParagraph("Apache is probably the most popular web server at the moment but nginx is becoming more popular and if you must set up a Windows web server, you will probably use Microsoft’s Internet Information Services (IIS) application.")); |
|
main.append(addParagraph("There are other types of app servers and special purpose web servers such as Tomcat for Java, Node for JavaScript and Sinatra for Ruby.")); |
|
main.append(addParagraph("Note that web servers traditionally listen for requests on port 80 or port 443.")); |
|
main.append(addParagraph("In the case of a web server, most of the work of managing it is a matter of working out what resources the server has access to and how to handle or route the requests it receives. In large scale deployments, you may also have to consider how to spready demand across multiple servers, but that is beyond the scope of this course.")); |
|
main.append(addSyntax("Ensure Network Connectivity")); |
|
main.append(addParagraph("A server may need to be accessed from an internal network or from an external network (via the Internet). Either way, we must ensure that it is accessible to the machines we want to allow access to. The following are helpful")); |
|
main.append(addParagraph("a reliable network connection (via ethernet cable)")); |
|
main.append(addParagraph("a static IP address")); |
|
main.append(addParagraph("Port 80 (and/or 443) should be open - some ISPs will block these ports for inbound traffic in order to encourage customers to take a business account")); |
|
main.append(addParagraph("On this last point, it may be necessary to configure the firewall to allow traffic on these ports.")); |
|
main.append(addParagraph("Web servers on a network are often placed in something called the DMZ (De-militarized zone). This is a part of the network that is open to the outside world so it can allow access to the organizations website without also providing access to the rest of the network.")); |
|
main.append(addParagraph("These generally only allow inbound traffic on ports 80 and 443. Any other traffic such as ssh would be denied so any management connection would have to be made from inside the network.")); |
|
main.append(addParagraph("Another final point is that with a web server, it has to be accessible all the time so you will want to ensure that any power saving options which might, for instance, hibernate the pc after a period of inactivity are switched off.")); |
|
main.append(addSyntax("Install Apache")); |
|
main.append(addParagraph("It is worth noting that RedHat based distros such as CentOS, refer to apache as httpd (Hypertext Transfer Protocol Daemon) so you would install it with a command such as")); |
|
main.append(addClassParagraph("sudo yum install httpd", "centos")); |
|
main.append(addParagraph("Debian based distros such as Ubuntu refer to it simply as apache so you would install it with")); |
|
main.append(addClassParagraph("sudo apt install apache", "ubuntu")); |
|
main.append(addParagraph("Even in a distro such as Centos, you will still see references to apache in logs or in settings and will also sometimes see references which including the major version number. At time of writing, this is 2.4.6 so you may see references to apache2.")); |
|
main.append(addParagraph("In general, these can be considered synonyms although in most cases, such as when installing the software, you will need to specify the precise term so they can be considered semantically synonymous but not necessarily interchangeable.")); |
|
main.append(addParagraph("We can check that it is installed by checking the version number with a command such as")); |
|
main.append(addClassParagraph("httpd -v", "ubuntu")); |
|
main.append(addParagraph("or we can check its status with something like")); |
|
main.append(addClassParagraph("systemctl status httpd", "centos")); |
|
main.append(addParagraph("Note in the latter case, we can see that httpd is loaded but it is not active yet. We will start up the web server once it has been configured to host our website.")); |
|
main.append(addParagraph("I mentioned that in Ubuntu, the apache server is referred to as apache (or apache 2) so to check the version, we would use something like")); |
|
main.append(addClassParagraph("apache2 -v", "ubuntu")); |
|
main.append(addParagraph("and we would check the status of the server with")); |
|
main.append(addClassParagraph("service apache2 status", "ubuntu")); |
|
main.append(addParagraph("which simply tells us that apache2 is not running.")); |
|
main.append(addParagraph("Another thing to mention here is that a basic installation of apache (or httpd) is only really suitable for serving up basic web pages, it doesn’t include support for things like php. However, we will add additional modules later to provide extra functionality and this will include php.")); |
|
main.append(addSubHeader("Explore the Configuration File")); |
|
main.append(addParagraph("The main configuration file is called httpd on a RedHat based system and apache2.conf on a Debian system. Respectively, you will find these in the /etc/httpd/conf and /etc/apache folders. The file, httpd.conf tends to be larger than apache2.conf because the Debian systems tend to split this and put some of the settings in additional files.")); |
|
main.append(addParagraph("We’ll take a look through the settings on a CentOS system, so we are looking at httpd.conf. We want to be able to scroll through it so we will view it with less")); |
|
main.append(addClassParagraph("less /etc/httpd/conf/httpd.conf", "centos")); |
|
main.append(addParagraph("There are a lot of comments in the file, particularly at the start and there is a reference to the online documentation for Apache which can be found <a href=\"http://httpd.apache.org/docs/2.4/\" class=\"linuxlink\">here</a>.")); |
|
main.append(addParagraph("In most cases, we won’t need to change the config file but knowing what is there can be useful and can help to understand how the server works.")); |
|
main.append(addParagraph("If we scroll past the initial comments, the first settings we find are in a section called ServerRoot. This is where we set the location for the servers log and config files and it is not a good idea to change this unless you have a specific reason to do so but it’s important to know that you can if you need to. If you do change it, you need to be careful that the permissions are in order. If you change this so that, as an example, the log and config files are saved inside your home directory, the server may not be able to read or write files there (the latter being more likely). For obvious reasons, this would be a particular problem with the logs.")); |
|
main.append(addParagraph("A quick aside here, the settings are often referred to as directives, especially in the Apache documentation so you may see things like the ServerRoot directive. This refers to the ServerRoot setting mentioned in the previous paragraph and I will try to use this terminology as consistently as possible. It is also important to remember that if we use a relative path name in the config file, the ServerRoot is prepended to the path.")); |
|
main.append(addParagraph("Next, we have the Listen directive which allows you to set the IP address and the port number that the server will listen on. The default setting is 80 which means that we are listening for traffic on port 80 with any available IP address.")); |
|
main.append(addParagraph("A little further down is the Dynamic Shared Object (DSO) support allows us to add extra functionality to the server and we will see this later when we add support for php. The include line tells the server to load any .conf files found in the conf.modules.d folder. This is a relative path so if you want to go and view or list these files, you will need to remember to prepend the ServerRoot path so in this case, we might list the files with")); |
|
main.append(addClassParagraph("ll /etc/httpd/conf.modules.d", "centos")); |
|
main.append(addParagraph("The user and group that the server can be run as is next and this is set as user apache and group apache and anything the server needs to access must be accessible by this user or group. If not, you may see errors which can be hard to diagnose.")); |
|
main.append(addParagraph("The next section is easy to miss if you scroll past it, that’s the Main Server Configuration and it acts as a kind of fallback if a site you are hosting doesn’t have its own config file. If you only have one site, you can configure it here.")); |
|
main.append(addParagraph("The ServerAdmin directive provides an email address so that you have a point of contact if there is a problem with the site. We can also add this email address to error messages.")); |
|
main.append(addParagraph("ServerName is the name of your server, assuming you have a domain name for it. It is commented out by default and we won’t change this at this stage.")); |
|
main.append(addParagraph("After ServerName, we can see some definition blocks which govern access to some directories - note that in this context, block should be interpreted as section rather than something that blocks access. This is important because the point of these blocks is to control access, so it can block or allow access.")); |
|
main.append(addParagraph("Figure 2 shows the first of these blocks although it might be worth noting that this is only the first block. There are others but they don’t all appear together.")); |
|
main.append(addImageWithCaption("./images/figure2.png", "Figure 2 - the Directory / permission block (highlighted).")); |
|
main.append(addParagraph("The override block does look a bit like an HTML tag and this can look a little strange, particularly the first line which is")); |
|
main.append(addParagraph("<Directory>")); |
|
main.append(addParagraph("This starts the block and we close it with")); |
|
main.append(addParagraph("</Directory />")); |
|
main.append(addParagraph("The closing “tag” certainly looks a closing tag from HTML, but the opening “tag” looks like it has an error which would be the case if it were HTML. What we are actually seeing here, however, is the / character after directory is actually the directory we are controlling in this block.")); |
|
main.append(addParagraph("Another source of confusion is that it does look as though it may refer to the root folder and this is kind of correct except that this is the root folder in context. Since the context is a web server, the root directory is actually referring to the ServerRoot directory defined earlier in the file.")); |
|
main.append(addParagraph("In terms of the actual settings, we have two directives here, an AllowOverride directive and a Require directive.")); |
|
main.append(addParagraph("The AllowOverride directive determines whether or not this rule can be overwritten by a subsequent rule and as it is set to none, in this case it can’t be.")); |
|
main.append(addParagraph("The require directive determines whether anyone accessing the site through the web service can access the directory and because it is set to all denied, that cannot.")); |
|
main.append(addParagraph("The DocumentRoot directive tells the server where the documents for the site are located. Note that the directives set on the ServerRoot directory have effectively denied access to anyone and this also applies to sub-folders so this folder is not actually accessible over the web, but we will fix this in the next section. Note that the DocumentRoot is /var/www/html.")); |
|
main.append(addParagraph("After the DocumentRoot section, we have another block, this time controlling access on the /var/www directory and this is shown in figure 3.")); |
|
main.append(addImageWithCaption("./images/figure3.png", "Figure 3 - the Directory /var/www permission block (highlighted)")); |
|
main.append(addParagraph("So, this is not the DocumentRoot directory, but it is the directory that contains the DocumentRoot directory. As before, the AllowOverride directive is set to none but the Require directive is set to all granted.")); |
|
main.append(addParagraph("In the next Directory block, we are looking at access for the html folder inside /var/www (and this IS the DocumentRoot folder. This has three directives. Again, we have the AllowOverride directive set to none and the Require directive set to all granted so this is the same as its parent folder (/var/www).")); |
|
main.append(addParagraph("However, we have an additional directive and this is the Options directive. In this directive, two options are specified and these are Indexes and FollowSymLinks. Indexes means that the directory is browsable. This means that if a browser tries to open a page in the folder and fails to find it, a list of the contents of the directory will be displayed instead. This will look similar to the kind of listable files you see on an ftp site.")); |
|
main.append(addParagraph("This is potentially a security risk and you certainly won’t want anyone to browse a folder that contains vulnerable code.")); |
|
main.append(addParagraph("FollowSymLinks allows the server to navigate to another file or folder via a symbolic link so you can access locations outside the normal directory structure thereby, effectively extending the directory structure. However, you should bear in mind that the server can only do that if it has the appropriate permissions for the file or folder being linked to.")); |
|
main.append(addParagraph("Next, we have the DirectoryIndex which specifies what file will be returned to a browser when no specific file is requested. For instance, you might type www.amazon.com into your browser and the page that you will see returned is the default page which is usually called index.html, index.php or something similar.")); |
|
main.append(addParagraph("As far as I am aware, the browser will always display a page called index.html or index.php in these circumstances but I’m not sure if that means that this directive is not required or if it is because of this directive that we see this type of behaviour, I suspect the latter!")); |
|
main.append(addParagraph("The directive is shown in figure 4.")); |
|
main.append(addImageWithCaption("./images/figure4.png", "Figure 4 - the DirectoryIndex directive")); |
|
main.append(addParagraph("This looks a little different from previous directives in that it is inside a conditional block and it can be executed if the specified module is either present or not, depending on how the module is written. In this case, the directive will apply if the module, dir_module, is present.")); |
|
main.append(addParagraph("Another interesting directive, shown in figure 5 is the requires directive in the Files block.")); |
|
main.append(addImageWithCaption("./images/figure5.png", "Figure 5 - the Files block")); |
|
main.append(addParagraph("The block indicates that the enclosed directive is to be applied to the files supplied as an argument in the opening “tag”. Note that this specifies .ht* and the requires directive specifies all denied. This means that no one will be able to access any file that starts with .ht so it would cover files such as .htaccess or .httpd. These tend to be files where configuration and/or security data is stored so it is a good idea not to grant access to them.")); |
|
main.append(addParagraph("You might expect that this would block access to files such as index.html, but this is not the case. The pattern doesn’t have anything in front of the dot, so any file that has a name like index.html or anything.html will not match because these filenames do have something before the dot.")); |
|
main.append(addParagraph("The next section deals with logging which we won’t cover in detail but briefly, the location of the logs is specified as well as the level of detail (in this case, warning) that is being logged and some other details of how the logging will work.")); |
|
main.append(addParagraph("Below that we have another conditional block that applies if the module, alias_module, is present and this allows to create aliases to some directories. In our file, we have one alias")); |
|
main.append(addSyntax("ScriptAlias /cgi-bin “/var/www/cgi-bin/”")); |
|
main.append(addParagraph("This allows us to reference the cgi-bin folder as if it were in the same folder as our website and it also has the advantage that we could move the files in this folder to a new location and only have to change this location in this section of the config file.")); |
|
main.append(addParagraph("We also have another block below this which controls access to the folder, /var/www/cgi-bin/.")); |
|
main.append(addParagraph("The next block is another conditional block which applies if mime_module is found. Mime is an older name for a standard describing different types of data. It is an important module because it helps to define how the server deals with different types of data it is sent. For example, if the data is an image file, if the server doesn’t know that, it will interpret the data as just a stream of bytes and will attempt to render it, usually resulting in the web browser displaying gibberish.")); |
|
main.append(addParagraph("In most cases, you won’t need to do anything with this section, but it will allow you to have the server deal with certain types of file in a way you specify, for instance if you have files with a custom extension. You would do that with an AddType declaration. There are some examples of this already in the config file such as")); |
|
main.append(addSyntax("AddType application/x-gzip .tqz")); |
|
main.append(addParagraph("This tells the server that .tgz files are of type gzip.")); |
|
main.append(addParagraph("Handlers and filters tell the server how to process data before it is sent to the client and that includes cgi.")); |
|
main.append(addParagraph("Next, we have a default character set which is normally UTF-8 but it is a default, so it can be changed in an html file.")); |
|
main.append(addParagraph("The next section is another conditional block that checks to see if there is a mime_magic and provides the path to that module if it does exist. This module stores magic numbers that are embedded in files and help the server to determine the file type if it does not have an extension.")); |
|
main.append(addParagraph("Again, you would only be changing this if you had some custom types and you wanted to make sure they are handled correctly.")); |
|
main.append(addParagraph("Below that, we have some customizable error responses which allow you to provide a message or redirect to specific html file for various error responses. For example, if the server is sending a 404 response, we could add the location of a html file showing a customized page not found message so that if the error is encountered, the client will see our customized page rather than a standard page 404 error.")); |
|
main.append(addParagraph("For a bit of fun, you can specify an html file as something like")); |
|
main.append(addSyntax("http.cat/413")); |
|
main.append(addParagraph("which will display an image of a cat with the error message. An example of this is shown in figure 6.")); |
|
main.append(addImageWithCaption("./images/figure6.jpg", "Figure 6 - a sample cat error message from https://http.cat/413 (with thanks to http.cat)")); |
|
main.append(addParagraph("EnableMMAP and EnableSendFile tweak how the server serves up files. We won’t make any changes to them here.")); |
|
main.append(addParagraph("The final directive is the IncludeOptional directive in the Supplemental configuration section as shown in figure 7.")); |
|
main.append(addImageWithCaption("./images/figure7.png", "Figure 7 - the Supplemental configuration section showing the IncludeOptional directive")); |
|
main.append(addParagraph("This loads up all of the config files in the conf.d folder and this is inside the ServerRoot folder (usually /etc/httpd) we saw earlier in the config file.")); |
|
main.append(addParagraph("If you have to make changes to the config file, you will want to ensure that you can go back to the previous configuration, for example, if you have made a change and it breaks something. There are two approaches to this. You can either copy the config file to a backup before you make any changes, or you can comment out the default lines as you replace them - or a combination of the two.")); |
|
main.append(addParagraph("Bear in mind that if the config file does become unstable, you can always restore the original from the package (if you have it).")); |
|
main.append(addParagraph("It’s worth remembered that the config file described here is on a CentOS machine, so this is how it is organized in a RedHat environment. A Debian configuration isn’t described here because the options are essentially the same.")); |
|
main.append(addParagraph("The main difference you will see on a Debian-based system is that parts of the configuration are placed in separate files in /etc/apache2/conf-available and these are enabled by creating a symbolic link in the /etc/apache-2/conf-enabled folder.")); |
|
main.append(addParagraph("You could, if you wanted, mimic this kind of set-up on a CentOS (or other RedHat based environment) but it doesn’t work like that by default so you would need to make the necessary changes to the config file and create the available and enable folders.")); |
|
main.append(addParagraph("Although it is not an official documentation site, the University of Cambridge has some useful information on Web Server Management which you can find <a href=\" https://www.cl.cam.ac.uk/~jw35/courses/apache/html/book1.htm\" class=\"linuxlink\">here</a>. This takes you through the creation of a configuration file.")); |
|
main.append(addSubHeader("Start Up and Shut Down the Service")); |
|
main.append(addParagraph("We will be accessing the server with its IP address which we can get with")); |
|
main.append(addSyntax("ip -4 a")); |
|
main.append(addParagraph("We can access our site from another computer with the IP address. If we want to view it on the local machine, we can do so with a browser or with the curl command at the command line. Before we make the service live, however, we should check that the firewall is running with")); |
|
main.append(addClassParagraph("sudo systemctl status firewalld", "centos")); |
|
main.append(addParagraph("This should show as active and running which it does. Now, if we want to allow access to the site from outside the network, we need to make sure that the firewall will permit this traffic.")); |
|
main.append(addParagraph("To allow access on port 80, we use the command")); |
|
main.append(addClassParagraph("sudo firewall-cmd --zone=public --add-service=http --permanent", "centos")); |
|
main.append(addParagraph("and similarly, we can allow access on port 443 with the command")); |
|
main.append(addClassParagraph("sudo firewall-cmd --zone=public --add-service=https --permanent", "centos")); |
|
main.append(addParagraph("The change does not take effect immediately, but we can reload the firewalld and that will force the")); |
|
main.append(addClassParagraph("sudo firewall cmd reload", "centos")); |
|
main.append(addParagraph("So now we have the IP address and we have allowed http and https traffic, we can go to a browser and type in the IP address. The result is an “Unable to connect message”. This is because although http traffic is allowed, there is nothing listening for it. This is the job of the web server so we have to make sure it is started before we can connect.")); |
|
main.append(addParagraph("We can check the status of the web server with")); |
|
main.append(addClassParagraph("sudo systemctl status httpd", "centos")); |
|
main.append(addParagraph("This shows that the service is loaded but is not running so we can start it up with")); |
|
main.append(addClassParagraph("sudo systemctl start httpd", "centos")); |
|
main.append(addParagraph("On older systems, we can start he server with")); |
|
main.append(addClassParagraph("service httpd start", "centos")); |
|
main.append(addParagraph("and we can use a similar command on a Debian system")); |
|
main.append(addSyntax("service apache2 start")); |
|
main.append(addParagraph("Now, if we go back to the browser and try again, we should now see the placeholder page, this is the page Apache displays when there is no index file.")); |
|
main.append(addImageWithCaption("./images/figure8.png", "Figure 8 - the output from the sudo systemctl status httpd command which shows that apache is running")); |
|
main.append(addParagraph("As we can see in figure 8, Apache HTTP Server is active (running) but it is also disabled. This doesn’t affect the running of the service, but it does mean that if we reboot the machine, we will also need to start httpd again. To ensure that the server is started and ready to go as soon as the machine boots up and without us having to remember to start it, the service has to be enabled which we can do with")); |
|
main.append(addClassParagraph("sudo systemctl enable httpd", "centos")); |
|
main.append(addParagraph("We can re-check the status and we will now see that the service is not only loaded but also enabled.")); |
|
main.append(addParagraph("If we make some changes, let’s say by introducing an index.html file and by making some changes in the configuration file, the web service has to be reloaded and we can do that with")); |
|
main.append(addClassParagraph("sudo systemctl restart httpd", "centos")); |
|
main.append(addParagraph("NOTE that this will interrupt anyone browsing the site or downloading from the site, so if it is live, you want to be very careful about using it.")); |
|
main.append(addParagraph("We have seen how to start and enable the web service, but there may be scenarios where we want to reverse one or both of these and we can do that, as you might imagine with stop and disable. So, to stop the service, we use")); |
|
main.append(addClassParagraph("sudo systemctl stop httpd", "centos")); |
|
main.append(addParagraph("will stop the service and")); |
|
main.append(addClassParagraph("sudo systemctl stop httpd", "centos")); |
|
main.append(addParagraph("will disable it.")); |
|
main.append(addSubHeader("Deploy a Site")); |
|
main.append(addParagraph("Now that we have our server up and running, there are three steps to get a custom page to display on the server.")); |
|
main.append(addInsetBulletList(["Make sure the server has a name.", "Copy the files from the development space to where the server can find them.", "Check that everything is correct and then take a look at our site."])); |
|
main.append(addParagraph("To give the server a name, we have to first decide whether it will be accessible from the internet. If it is, we will probably want to register a domain name and then point the address for the A record to our IP address. This allows the name to be resolved to an IP address using DNS.")); |
|
main.append(addParagraph("My domain names are registered at <a href=\"https://my.ionos.co.uk/product-overview\" class=\"linuxlink\">ionos.co.uk</a> and these are <a href=\"osztromok.com\" class=\"linuxlink\">osztromok.com</a> and osztromok.co.uk. I also have osztromok.site registered with <a href=\"www.wedos.com\" class=\"linuxlink\">Wedos</a> (in the Czech Republic).")); |
|
main.append(addParagraph("When setting the A record, it is also a good idea to set a CNAME. This is kind of like an alias for your domain name to cover a scenario where someone types the url into a browser without the www. As an example, wedos.com used to hosting the website www.osztromok.eu for me and they had set a CNAME for my site of osztromok.eu. If you type osztromok.eu into a browser, this would have the same effect as typing www.osztromok.eu. Note that this site is no longer accessible because I had to give up the domain post-brexit.")); |
|
main.append(addParagraph("It may also be worth noting here that you can use redirects. For example, if I want to host my own website with a name of www.osztromok.com, I can redirect any traffic for www.osztromok.co.uk to this address. This is actually quite common with British companies. As an example, if you type www.tesco.co.uk into a browser, the Tesco website will appear but you may notice that the URL changes to www.tesco.com.")); |
|
main.append(addParagraph("Obviously, these steps are only required if the web site is exposed to the Internet. Another scenario maybe deployment of a website with an organisation, you will need to register the name in the internal DNS system.")); |
|
main.append(addParagraph("Optionally, you can use the hostnamectl command to set the hostname without having to edit the /etc/hostname file. This option is suitable if you are hosting a single site, but not for multiple sites so we will ignore this option. More information on the command can be found <a href=\"https://www.geeksforgeeks.org/hostnamectl-command-in-linux-with-examples/\" class=\"linuxlink\">here</a>.")); |
|
main.append(addParagraph("One of the consequences of setting the hostname in this way is that it is automatically detectable by the Apache server and if it is not set, you will see notices in the error log.")); |
|
main.append(addParagraph("Another option is to add the hostnames to the hosts file with")); |
|
main.append(addSyntax("sudo vim /etc/hosts")); |
|
main.append(addParagraph("Figure shows the how edited hosts file would look if we took this option. Note that we need to edit this file with elevated permissions.")); |
|
main.append(addImageWithCaption("./images/figure9.png", "Figure 9 - the hosts file edited to include the ip address and hostname")); |
|
main.append(addParagraph("third option, and the one we will take, is to edit the config file with")); |
|
main.append(addClassParagraph("sudo vim /etc/httpd/conf/httpd.conf", "centos")); |
|
main.append(addParagraph("Figure 10 shows the ServerName directive in the config file before any changes are made.")); |
|
main.append(addImageWithCaption("./images/figure10.png", "Figure 10 - the httpd config file showing the ServerName without any changes")); |
|
main.append(addParagraph("The example directive is commented out but if not, we will need to do that. We can then add our own name in the same format as shown in figure 11.")); |
|
main.append(addImageWithCaption("./images/figure11.png", "Figure 11 - the httpd config file showing the ServerName with our own server name added")); |
|
main.append(addParagraph("Again, we would replace httpd with apache2 on a Debian system.")); |
|
main.append(addClassParagraph("sudo vim /etc/apache2/apache2.conf", "ubuntu")); |
|
main.append(addParagraph("However, the config file in Debian doesn’t have a ServerName directive. Recall that in Debian, there are additional config options in the files found in the /etc/apache2/conf-available directory so the directive may simply be somewhere else.")); |
|
main.append(addParagraph("It seems that the available options here are to edit the hosts file or to put the server name (assuming there is an option to do this) in the config file specific to each host. More information on this option is available <a href=\"https://unix.stackexchange.com/questions/155150/where-in-apache-2-do-you-set-the-servername-directive-globally\" class=\"linuxlink\">here</a>.")); |
|
main.append(addParagraph("As with any change in the config file, we will have to restart apache for the new server name to take effect with")); |
|
main.append(addClassParagraph("systemctl restart httpd", "centos")); |
|
main.append(addParagraph("The next thing we want to do is to introduce some content and rather than use the exercise files for this, I’m going to use my own website. I have a copy of the files for this site on OneDrive, so I have downloaded these into a folder called Website which is inside my home folder on my Centos 7 VM. These have then been copied into the html folder for the web server with the command")); |
|
main.append(addClassParagraph("sudo cp -R * /var/www/html", "centos")); |
|
main.append(addParagraph("Note that sudo is required because the owner of the html folder is root and I have used the -R option to copy all the files including all subdirectories and their contents.")); |
|
main.append(addParagraph("We can take a look at the contents of the html folder with a long listing so we can see permissions and the results are shown in figure 12.")); |
|
main.append(addImageWithCaption("./images/figure12.png", "Figure 12 - the contents of the /var/www/html folder which now holds the files for my website")); |
|
main.append(addParagraph("There are a couple of things worth noting here. Firstly, the owner (and group) for these files is root. Recall that these files need to be accessed by the user apache and since no ACLs have been set, the permissions for apache fall under other. This means that apache has read-only access.")); |
|
main.append(addParagraph("This is fine because it means that if an attacker gets access to our system, they will be unable to write to these documents using the credentials of the apache user.")); |
|
main.append(addParagraph("It’s worth mentioning something about security here, specifically SELinux security contexts. The web server runs in its own security context and the files it serves need to be labelled with that context. If you copy files, the files take on the security context of the destination and we will see how to check that in a moment. Essentially, this means that the files we copied into the html folder should have the correct context.")); |
|
main.append(addParagraph("If you move a file into the html directory, it will keep its original context and this can lead to problems if the context is not updated.")); |
|
main.append(addParagraph("We can use the restorecon command to set the context for the location we are working in. To check the context, we can do that with a simple directory listing using the -Z option (which gives the security contexts). This would typically be something like")); |
|
main.append(addClassParagraph("./ll -hZ", "centos")); |
|
main.append(addParagraph("The output from this is shown in figure 13.")); |
|
main.append(addImageWithCaption("./images/figure13.png", "Figure 13 - a listing of the /var/www/html folder using the -Z option to display the security contexts")); |
|
main.append(addParagraph("The security context ends with a _t and here we can see that it is httpd_sys_content_t and this is the context that relates to the httpd service so we are good to go.")); |
|
main.append(addSubHeader("Configure Logging")); |
|
main.append(addParagraph("Two important logs you will want to look at are the access and error logs. By default, these are normally stored in the /etc/httpd/logs/ directory. However, if we get a long listing of this directory, we will see that there is a symbolic link to /var/log/httpd/. If we get a listing of this directory, we can see the log files are there.")); |
|
main.append(addParagraph("Note that these directories are owned by root so any command will have to be executed with elevated privileges. It’s also worth pointing out that because the /etc/httpd/logs directory contains only a symbolic link to another directory, we cannot list the log files by listing that directory. We can, however, use the path to access the logs.")); |
|
main.append(addParagraph("For instance, let’s say we want to read the error log with vim, we can do that with")); |
|
main.append(addClassParagraph("sudo vim /etc/httpd/logs/error_log", "centos")); |
|
main.append(addParagraph("and this is exactly the same as")); |
|
main.append(addClassParagraph("sudo vim /var/log/httpd/error_log", "centos")); |
|
main.append(addParagraph("The access log records any attempt to access a file such as an html file, a css file and so on.")); |
|
main.append(addParagraph("Typically, we might want to look at it with the tail command since we would most likely want to look at the most recent entries.")); |
|
main.append(addClassParagraph("sudo tail /etc/httpd/logs/access_log", "centos")); |
|
main.append(addParagraph("Figure 14 shows the output of this command.")); |
|
main.append(addImageWithCaption("./images/figure14.png", "Figure 14 - the last few entries from the access_log file viewed with tail")); |
|
main.append(addParagraph("Something worth noting in this context is that you may well find that you are typing the command twice if you forget to use sudo. There is a neat shortcut, however, whereby you can use !! to execute the previous command.")); |
|
main.append(addParagraph("Figure 15 shows the results of trying to execute the command without sudo (in effect, without the appropriate permissions) and the use of !! to rerun the command with sudo.")); |
|
main.append(addImageWithCaption("./images/figure15.png", "Figure 15 - using !! to execute the previous command with sudo")); |
|
main.append(addParagraph("I have highlighted the line where the !! argument is being used. Obviously, this is not restricted to use with sudo, but it is a useful trick to know.")); |
|
main.append(addParagraph("As an example, I placed a text file called daddy.txt in my Document folder on the Centos VM and displayed the contents with a simple cat command (note that I have navigated to the Documents folder first so I am not specifying a path).")); |
|
main.append(addSyntax("cat daddy.txt")); |
|
main.append(addParagraph("Now, if I want to pipe this command to grep, I can do that with something like")); |
|
main.append(addSyntax("!! | grep Achoo")); |
|
main.append(addParagraph("This command is automatically converted to")); |
|
main.append(addSyntax("cat daddy.txt | grep Achoo")); |
|
main.append(addParagraph("and executed.")); |
|
main.append(addParagraph("If you type !! on it’s own, it simply repeats the last command or command sequence. What I mean by that is that if after executing the second of the above three commands, we execute")); |
|
main.append(addSyntax("!!")); |
|
main.append(addParagraph("this is converted to")); |
|
main.append(addSyntax("cat daddy.txt | grep Achoo")); |
|
main.append(addParagraph("Another useful shortcut is to get a list of previous commands with history. Note that these commands are numbered and if you want to execute one of them again, you can do that with ")); |
|
main.append(addSyntax("!x")); |
|
main.append(addParagraph("where x is the number in history of the command you want to repeat. This particular trick as well as a number of others are described on the webpage, <a href=\"https://itsfoss.com/linux-command-tricks/\" class=\"linuxlink\">20 Linux Command Tips and Tricks That Will Save You A Lot of Time</a>.")); |
|
main.append(addParagraph("Going back to the access_log file in figure 15, we can see that for each access there is")); |
|
main.append(addInsetBulletList("a source address for each request", "a time stamp for each event", "some information about the request including type (GET), the name of the resource requested and a response status code (200 for OK)", "a browser description tag")); |
|
main.append(addParagraph("It is important to remember that the access_log does not record only successful access requests, but also records failures as can be seen in figure 16.")); |
|
main.append(addImageWithCaption("./images/figure16.png", "Figure 16 - the last few entries from the access_log file viewed with tail as seen previously in figure 14, but this time showing a failed request")); |
|
main.append(addParagraph("This looks similar to the output we saw in figure 14. Prior to generating the output, I tried to access a directory in my site that doesn’t exist and so this led to a 404 error. In the last entry in figure 16, you can see that the status code is 404.")); |
|
main.append(addParagraph("The log entry also records the name of the non-existent directory.")); |
|
main.append(addParagraph("Whilst this can be thought of as being an error, it is not an error on the part of the web server. The user (myself) has requested a non-existent resource and the server has correctly returned a 404 response.")); |
|
main.append(addParagraph("The significance of this is that whilst this is logged in access_log, it is not logged in error_log. The error_log records errors the web server itself encounters and this might be, for example, problems parsing PHP. We can use tail to look at this one as well.")); |
|
main.append(addSyntax("sudo tail /etc/logs/httpd/error_log")); |
|
main.append(addParagraph("It’s not likely that we will see any real errors at this point since our web server hasn’t been running long enough to encounter any. The output is shown in figure 17.")); |
|
main.append(addImageWithCaption("./images/figure17.png", "Figure 8 - the output from the sudo systemctl status httpd command which shows that apache is running")); |
|
main.append(addParagraph("There are some general housekeeping notifications and we can see the error, “Could not reliably determine the server’s fully qualified domain name”. This is the notification referred to earlier that we would expect to see if not using hostnamectl to set the name of the host (see under <a href=\"#deploy\" class=\"linuxlink\"></a>). It is an expected notification so we can disregard it.")); |
|
main.append(addParagraph("If you are hosting a number of sites, or one site with a lot of traffic, it is a good idea to check these logs periodically and you might also consider creating some scripts to look for particular errors or addresses or you may want to set up some log monitoring software like Splunk. More information on Splunk can be found from <a href=\"https://www.splunk.com/en_us/central-log-management.html\" class=\"linuxlink\">their website</a> or in one of the LinkedIn Learning courses such as <a href=\"https://www.linkedin.com/learning/learning-splunk\" class=\"linuxlink\">Learning Splunk</a> or <a href=\"https://www.linkedin.com/learning/network-forensics\" class=\"linuxlink\">Network Forensics</a>.")); |
|
main.append(addSubHeader("Add Modules to Extend Apache")); |
|
main.append(addParagraph("As mentioned previously, we can add modules to apache to increase its functionality. These modules, also known as Dynamic Shared Objects, are essentially libraries not too different to DLLs in Windows.")); |
|
main.append(addParagraph("Apache has several core module built in and we can list these with")); |
|
main.append(addClassParagraph("httpd -l", "centos")); |
|
main.append(addParagraph("as shown in figure 18.")); |
|
main.append(addImageWithCaption("./images/figure18.png", "Figure 8 - the output from the sudo systemctl status httpd command which shows that apache is running")); |
|
main.append(addParagraph("There are also additional loaded static and shared modules that Apache makes use of and these can be similarly listed with")); |
|
main.append(addClassParagraph("httpd -M", "centos")); |
|
main.append(addParagraph("This is a longer list and if we pipe the output to wc -l we can see that there are 82. A more sensible option if you want to inspect this list is")); |
|
main.append(addClassParagraph("httpd -M | less", "centos")); |
|
main.append(addParagraph("We can also see from this that static modules are listed first and then shared modules.")); |
|
main.append(addParagraph("Let’s install php with")); |
|
main.append(addClassParagraph("sudo yum install -y php", "centos")); |
|
main.append(addParagraph("Earlier, we saw the Dynamic Shared Object in the config file which caused additional config files to be loaded (from the conf.modules.d folder which is inside the ServerRoot folder which is /etc/httpd). If we list the contents of this directory with")); |
|
main.append(addClassParagraph("ll /etc/httpd/conf.modules.d", "centos")); |
|
main.append(addParagraph("we can see that when php was installed, it placed a config file here as shown in figure 19.")); |
|
main.append(addImageWithCaption("./images/figure19.png", "Figure 19 - the contents of the conf.modules.d folder showing the file placed there when we installed phpg")); |
|
main.append(addParagraph("The contents of the file, 10-php.conf are shown in figure 20.")); |
|
main.append(addImageWithCaption("./images/figure20.png", "Figure 20 - the 10-php.conf file from the /etc/httpd/conf.modules.d folder")); |
|
main.append(addParagraph("In addition, a config file, php.conf has been placed in the /etc/httpd/conf.d folder and this is shown in figure 21.")); |
|
main.append(addImageWithCaption("./images/figure21.png", "Figure 21 - the php.conf file from the /etc/httpd folder")); |
|
main.append(addParagraph("In addition, a config file, php.conf has been placed in the /etc/httpd/conf.d folder and this is shown in figure 21.")); |
|
main.append(addParagraph("This is a longer file and we can see that it includes a DirectoryIndex directive, index.php so we can create a PHP application with a file called index.php and we don’t need to add this to the URL. There are some configuration options at the bottom that dictate how PHP handles sessions, but this is not covered in the current course.")); |
|
main.append(addParagraph("We can create a test file for our php to ensure that it is working. This can go into any folder that the apache server has access to but, for the sake of simplicity, I will put it in the /var/www/html folder. The file is called php.test and the contents are shown in figure 22.")); |
|
main.append(addImageWithCaption("./images/figure22.png", "Figure 22 - the test.php file")); |
|
main.append(addParagraph("We can execute it by typing the URL, localhost/test.php in a browser window. Note that the URL is, of course, dictated by the location of the test.php file so for instance, if we placed it in a folder called /var/www/html/php, the URL would be localhost/php/test.php.")); |
|
main.append(addParagraph("Now, if we type this into a browser straight away, we may find that when we try to view the file test.php in a browser, we are likely to see just a blank page. This is because we need to restart the web service in order for it to pick up the new shared object. So we will restart it with")); |
|
main.append(addSyntax("sudo systemctl restart httpd")); |
|
main.append(addParagraph("and reload the browser and we should see the php has been properly executed.")); |
|
main.append(addImageWithCaption("./images/figure23.png", "Figure 23 - the output for our test.php file which confirms php is working")); |
|
main.append(addParagraph("There are, as we saw, a lot of modules and the documentation for them can be found at <a href=\"http://httpd.apache.org/docs/2.4/mod/\" class=\"linuxlink\">http://httpd.apache.org/docs/2.4/mod/</a>.")); |
|
main.append(addSubHeader("Use Virtual Hosts to Serve Many Sites")); |
|
main.append(addParagraph("There may be many reasons for wanting to deliver a number of websites from a single server. For example, we may have sites that are subdomains such as api.example.com or docs.example.com or we may have completely separate domain names. We can also have a scenario where data is provided from a different location depending on which port the request comes to.")); |
|
main.append(addParagraph("In each case, we will use virtual hosts to deliver the correct website. Essentially, a virtual host entry is a block in the configuration that specifies a domain name or pattern and a webpage or resource. If a request comes in that matches the pattern in the entry, the specified resource is served up.")); |
|
main.append(addParagraph("At this point, we don’t have any of these entries in our config file, so the server is configured simply to serve up data from the /var/www/html/directory in response to requests arriving on port 80.")); |
|
main.append(addParagraph("Let’s say we want to set up a subdomain, say members.osztromok.com. In a real-world scenario, you may have a team of developers working on the main site with an admin team responsible for the content of the members subdomain with content for each being in separate directories.")); |
|
main.append(addParagraph("The steps to set this up are")); |
|
main.append(addInsetList(["Make sure that we have a DNS entry for the domain or subdomain pointed at the server’s IP address.", "Make sure that the information we want to deliver is in the correct folder.", "Add an appropriate virtual host entry to the config file.", "Restart the web server.", "Test."])); |
|
main.append(addParagraph("To create the DNS entry, we need to go to <a href=\"www.ionos.co.uk\" class=\"linuxlink\">www.ionos.co.uk</a> again and add the appropriate DNS entry for the subdomain. The Ionos website makes it easy to create and configure subdomains. We will set the IP address to the same as that for the main site. This means that if everything is working correctly, both osztromok.com and members.osztromok.com will server up the same site.")); |
|
main.append(addParagraph("Next, I will create a copy of the main site by navigating to the /var/www directory and executing the command")); |
|
main.append(addSyntax("sudo cp -Rv html members")); |
|
main.append(addParagraph("Of course, I don’t need the whole site for the purpose of demonstrating a subdomain but this gives me the main page without having to worry about which other files I need to copy along with it. I will also make a minor change to the index.html file so that when it is loaded, I can be sure that I am looking at the members page and not the main page.")); |
|
main.append(addParagraph("For the next step, we will navigate to the /etc/httpd/conf.d folder and create a config file for the members site with")); |
|
main.append(addSyntax("touch members-site.conf")); |
|
main.append(addParagraph("Figure 24 shows the contents of this file")); |
|
main.append(addImageWithCaption("./images/figure24.png", "Figure 24 - the member-site.conf file we have created for our members site")); |
|
main.append(addParagraph("We will need to restart the web server again for these changes to take effect. Once we have done that, if we go to a web browser, we will see that members.osztromok.com calls up the index.html file from the /var/www/members folder.")); |
|
main.append(addParagraph("If we now try to load the page at osztromok.com, we also see the index.html file from the members folder. This is because the settings for osztromok.com are in the httpd.conf file, that is the main config file for apache. This file is read before the files in the /etc/httpd/conf.d folder. As a result, when we read in the member-site.conf folder, its settings override those in the httpd.conf file and so our main site, osztromok.com, is now pointing to the members site.")); |
|
main.append(addParagraph("To fix this, we can create a config file for the main site in the /etc/httpd/conf.d folder, let’s say we call it default-site.conf. The contents of this file are shown in figure 25.")); |
|
main.append(addImageWithCaption("./images/figure25.png", "Figure 25 - the default-site.conf file")); |
|
main.append(addParagraph("Note that we have added a new directive here, ServerAlias, which allows us to catch all requests where the page name matches the pattern something.osztromok.com. That is, of course, assuming we have a DNS entry pointing to it.")); |
|
main.append(addParagraph("Now, if we restart the web server, we find that osztromok.com works as expected. But it is not entirely clear what will happen with the members.osztromok.com page because we gave two config files which specify the site either explicitly (members.osztromok.com) in member-site.conf and implicitly (*.osztromok.com) in default-site.conf.")); |
|
main.append(addParagraph("If we look at the members site again, we actually see the main site. Recall that the config files in the /etc/httpd/conf.d folder are processed in alphabetical order. Since this means that the default-site.conf file is processed first, it is picking up the subdomain before the members site has a chance to grab it.")); |
|
main.append(addParagraph("We can fix this simply by changing the processing order and we can do that by renaming the default-site.conf file as welcome-site.conf. Of course, we can call both sites anything we like but we want to make sure the file for the members site comes first alphabetically.")); |
|
main.append(addParagraph("We saw another approach earlier where the config file for php was preceded by the number 10 (so 10-php.conf) and this system of numbering the conf files is another way to ensure that the correct processing order is followed.")); |
|
main.append(addParagraph("Now, when we restart the browser, we see that both sites work as expected.")); |
|
main.append(addSubHeader("Common Troubleshooting Topics")); |
|
main.append(addParagraph("If you are having problems getting your web site up and running, here are some things you can check out.")); |
|
main.append(addInset("Is the Webserver Running")); |
|
main.append(addParagraph("We can check that with the")); |
|
main.append(addSyntax("sudo systemctl status httpd")); |
|
main.append(addParagraph("command. If it’s not running, the reason for this may be shown somewhere in the output of this command but if not, you may need to check the error logs to see what is happening, especially if it happens repeatedly. Also, bear in mind that the service must be enabled for it to run automatically.")); |
|
main.append(addInset("Are We Listening on Port 80")); |
|
main.append(addParagraph("You can check the config file to make sure that you are set up to listen for http traffic on port 80. We can check the config file for this or we can type the command")); |
|
main.append(addSyntax("ss -an")); |
|
main.append(addParagraph("and look for a listener on port 80. Figure 26 shows the output of this command.")); |
|
main.append(addImageWithCaption("./images/figure26.png", "Figure 26 - the output from the command ss -an | grep 80")); |
|
main.append(addParagraph("As you can see in figure 26, the listener on port 80 is near the bottom. You can also see that 80 is highlighted in the output and this is because I piped the output of the ss command to grep 80.")); |
|
main.append(addInset("Is the Server Returning Data Locally")); |
|
main.append(addParagraph("We can easily check this with a curl command so")); |
|
main.append(addSyntax("curl localhost")); |
|
main.append(addParagraph("or we could use the IP address. Figure 27 shows the type of output we expect to see if this is successful.")); |
|
main.append(addImageWithCaption("./images/figure27.png", "Figure 27 - the output (or the end of it) from the curl localhost command")); |
|
main.append(addParagraph("So, the curl command would be expected to return the html file as a document which is just another way of displaying it.")); |
|
main.append(addImageWithCaption("./images/figure28.png", "Figure 28 - the output we see with curl and the IP address")); |
|
main.append(addParagraph("Figure 28 shows the output from using the curl command with the IP address rather than localhost. In this case, curl is returning valid data with the host address but not with the IP address. In the config file, this was set to")); |
|
main.append(addSyntax("Listen :80")); |
|
main.append(addParagraph("so it looks like an obvious problem and I have changed it to")); |
|
main.append(addSyntax("Listen 82.39.17.42:80")); |
|
main.append(addParagraph("This is my router IP address but when I then try to restart the web server, it refuses to start so I suspect it is having problems with this IP address. As a check, I have commented out the line and tried to restart the web service, but this doesn’t seem to have any effect. Similarly, reinstating the original Listen directive doesn’t seem to help.")); |
|
main.append(addParagraph("I have also tried port forwarding to port 8080 from the Virginmedia router and setting the server to listen on that port with no effect.")); |
|
main.append(addParagraph("I have now gone back to listening on port 80 with the Directive")); |
|
main.append(addSyntax("Listen 192.168.0.11:80")); |
|
main.append(addParagraph("which is the internal IP address for the server. This has allowed me to start the web server but I still can’t access the site so I have re-added the port forwarding and I can now see that the web server is delivering the apache default website.")); |
|
main.append(addParagraph("So still not working correctly, but it is at least giving me a web page. The fact that it is returning the Apache default web page is strange, though, because this is not in the default website folder anymore. Actually, I was unable to find it which suggests it may have been deleted. This, in turn, suggests that the web server returning the data is not the webserver, but this seems unlikely given that I believe the course videos are setting up a VM, so I don’t think that using a VM is the problem. I will leave this for now.")); |
|
main.append(addParagraph("The next thing to check is whether the firewall is allowing HTTP traffic through and we can check that with")); |
|
main.append(addSyntax("sudo firewall-cmd --list-all")); |
|
main.append(addParagraph("The output of this is shown in figure 29.")); |
|
main.append(addImageWithCaption("./images/figure29.png", "Figure 29 - the output of the sudo firewall-cmd --list-all command")); |
|
main.append(addParagraph("What we are looking for here is http listed under services and we can see in figure 29 that it is, so that isn’t a problem.")); |
|
main.append(addParagraph("Sometimes, you will see that a website kind-of works. For instance, you might see the correct webpage loading, but it might not look right. If the CSS has not been applied, that should be fairly obvious because the site will look fairly plain (assuming all of the styling on the page is done with CSS).")); |
|
main.append(addParagraph("The likeliest reason is that one or more files have either the wrong permissions or the wrong security context. Recall that files in the site folder should be owned by user root and group root and should have 644 permissions for files (it is 755 for directories, the reason for this should be obvious). We can examine these, along with the security context, with a simple list command using the -Z option. This is shown in figure 30.")); |
|
main.append(addImageWithCaption("./images/figure30.png", "Figure 30 - a directory listing from the site folder showing permissions and the security context")); |
|
main.append(addParagraph("We can see, here, that the permissions are correct and the security context is httpd_sys_content_t so there are no problems here although I have not checked inside the directories, which I would do if there was a concern or apparent problem that might be caused by incorrect permissions, ownership or security context.")); |
|
main.append(addParagraph("If we want to change the security context in order to simulate an error of this type, we can use the chcon command. Let’s say we want to see what happens if the CSS file cannot be accessed because of this type of error, we can do that with")); |
|
main.append(addSyntax("sudo chcon -t user_home_t style.css.")); |
|
main.append(addParagraph("Note that we are setting the security context to that of the user’s home folder which is an error which might occur if the file is edited there and then moved to the site folder. Recall that in this scenario, the file retains the original context and actually, should be copied across in order to pick up the correct context (that of the destination folder).")); |
|
main.append(addParagraph("I should also point out that in my site, the CSS file used by the main page (index.html) is in the styles folder so you would need to either navigate there or specify a relative path including the styles folder.")); |
|
main.append(addParagraph("Don’t forget to change it back with restorecon.")); |
|
main.append(addParagraph("Another possible error is php files being displayed as plain text or a blank screen rather than displaying the results of executing the php code. If this happens, we will need to check whether the php module is installed and active.")); |
|
main.append(addParagraph("We can check this with")); |
|
main.append(addSyntax("httpd -M")); |
|
main.append(addParagraph("You will probably find that it is at or near the bottom of the output but if you need to search for a particular module, you can pipe the output to grep or less.")); |
|
main.append(addParagraph("For any other errors, you will likely want to look in the error log for more information and it is worth remembering that since this is a large file, you might want to look at the end of it with a command such as")); |
|
main.append(addSyntax("tail /var/log/httpd/error_log")); |
|
|
|
addSidebar("webdev"); |