
{"id":3180,"date":"2021-05-19T01:14:38","date_gmt":"2021-05-19T01:14:38","guid":{"rendered":"https:\/\/blog.gordonbuchan.com\/blog\/?p=3180"},"modified":"2026-03-11T00:40:53","modified_gmt":"2026-03-11T00:40:53","slug":"web-presence-step-by-step-chapter-16-using-a-script-to-automate-the-creation-of-a-virtual-host-on-an-apache-web-server","status":"publish","type":"post","link":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/2021\/05\/19\/web-presence-step-by-step-chapter-16-using-a-script-to-automate-the-creation-of-a-virtual-host-on-an-apache-web-server\/","title":{"rendered":"Web presence step by step Chapter 16: Using a script to automate the creation of a virtual host on an Apache web server"},"content":{"rendered":"\n<p>Previous step: Chapter 15: <a href=\"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/2021\/05\/01\/web-presence-step-by-step-chapter-15-using-dwservice-net-to-provide-remote-technical-support-as-an-alternative-to-teamviewer\/\">Using dwservice.net to provide remote technical support as an alternative to TeamViewer<\/a><br>Next step: <a href=\"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/2021\/05\/29\/web-presence-step-by-step-chapter-17-using-subdomains-to-host-multiple-websites-under-a-single-domain-name\/\">Chapter 17: Using subdomains to host multiple websites under a single domain name<\/a><\/p>\n\n\n\n<p>Web presence step by step is a series of posts that show you to how to build a web presence.<\/p>\n\n\n\n<p>In this chapter we install and use a script to automate the creation of a virtual host on an Apache web server.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">A PHP script that automates the creation of a virtual host under Apache<\/h1>\n\n\n\n<p>This script collects and validates inputs, then executes the commands to create a virtual host under Apache.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A note about the source code view below<\/h2>\n\n\n\n<p>For formatting reasons, the text is limited to a fixed width. To fully view the text, you can scroll to the right to see the ends of lines, or use the print view for this blog post.<\/p>\n\n\n\n<p>To view the source code in an another text editor, download and uncompress the zip file described below, or select and copy the text from the source code example below, and paste the text into a file on your computer called \u201caddvhost.php\u201d<\/p>\n\n\n\n<p>Consider copying the file to your Apache web server&#8217;s \/usr\/bin directory with a chmod of 755 so that it can be executed from the system path. Steps to do so are included in the procedure below.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Saving the PHP script to a file called addvhost.php<\/h2>\n\n\n\n<p>Download this zip file:<\/p>\n\n\n\n<p><a href=\"https:\/\/blog.gordonbuchan.com\/files\/addvhost.zip\">https:\/\/blog.gordonbuchan.com\/files\/addvhost.zip<\/a><\/p>\n\n\n\n<p>Uncompress the zip file to extract the file \u201caddvhost.php\u201d then copy the file to your Apache web server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Source code of the script<\/h2>\n\n\n\n<p>Scroll right to see the ends of lines.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n#!\/usr\/bin\/php\n&amp;lt;?PHP\n\/\/ addvhost.php\n\/\/ v0103\n\/\/ updated to variable-ize vhostip as a base setting\n\/\/ creates a virtual host under Apache\n\/\/ Gordon Buchan 20210512 https:\/\/gordonbuchan.com\n\/\/ MIT License https:\/\/mit-license.org\n\/\/ tested on Ubuntu 20.04, may work on Debian\n\/\/ directory structure allows for chroot jails for SFTP:\n\/\/ in a jail you do not own your home directory, only your webdir\n\/\/ tip: apt install finger whois\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ start summary\n\/\/ initialize base settings in variables ie bvhwb\n\/\/ ask for vhostsubdomain, vhostusername, vhostpassword\n\/\/ infer vhosthomedir, vhostwebdir by convention\n\/\/ create user, home directory, password\n\/\/ create directory\n\/\/ create index.php document\n\/\/ chown vhosthomedir as root:root\n\/\/ chown vhostwebdir as vhostusername:vhostusername\n\/\/ chmod vhosthomedir\n\/\/ chmod vhostwebdir\n\/\/ create virtual host file\n\/\/ enable virtual host\n\/\/ echo suggestion that client restart apache, run certbot --apache, restart apache\n\/\/ end summary\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ start base settings\n\n$bvhostconfdir = &quot;\/etc\/apache2\/sites-available&quot;;\n$bvhwb = &quot;\/usr\/web&quot;;\n$restartcommandstr = &quot;systemctl apache2 restart&quot;;\n$vhostenablecommandstr = &quot;a2enmod&quot;;\n$echoplaintextpasswords = TRUE;\n$logplaintextpassword = TRUE;\n$vhostserveradmin = &quot;info@yourdomain.com&quot;;\n\/\/ tip: could be &quot;xxx.xxx.xxx.xxx&quot;\n$vhostip = &quot;*&quot;;\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ end base settings\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ start function sink\n \n\/\/ start polyfill\n\/\/ str_contains() polyfill for pre PHP8: code for this function taken from php.net\nif (!function_exists(&#039;str_contains&#039;)) {\n\tfunction str_contains(string $haystack, string $needle): bool \n\t{\n\treturn &#039;&#039; === $needle || false !== strpos($haystack, $needle);\n\t} \/\/ end function str_contains()\n}\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ end polyfill\n\n\/\/ validate functions\n\/\/ We will be using the readline() function to ask questions on the command line.\n\/\/ These functions allow us to do rich validation within a while statement to trap\n\/\/ the readline in a loop until our conditions are satisfied.\n\/\/ We will also echo text to the console with reasons for rejection to assist the client.\n\/\/ For example: bad string format, vhost appears to exist already, etc.\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nfunction vhostsubdomainverify($vhostsubdomainstr) {\nglobal $bvhwb;\nglobal $bvhostconfdir;\n\n\/\/assume true until proven false\n$returnval = TRUE;\n\n\/\/ is the string clean?\n\/\/ note that &quot;-&quot; hyphen character is permitted, not part of symbol sieve\nif ( preg_match(&#039;\/&#x5B;\\&#039;^\u00a3$%&amp;amp;*()}{@#~?&gt;&amp;lt;&gt;,|=_+\u00ac]\/&#039;, $vhostsubdomainstr) ) {\n\t$returnval = FALSE;\n\techo &quot;string has special character that is not permitted\\n&quot;;\n}\n\n\/\/ string does not contain a period symbol\nif (!str_contains($vhostsubdomainstr,&quot;.&quot;) ) {\n\t$returnval = FALSE;\n\techo &quot;string does not contain a \\&quot;.\\&quot; period symbol.\\n&quot;;\n}\n\n\/\/ string contains two period symbols in a row\nif (str_contains($vhostsubdomainstr,&quot;..&quot;) ) {\n\t$returnval = FALSE;\n\techo &quot;string contain two \\&quot;..\\&quot; period symbols in a row.\\n&quot;;\n}\n\n\/\/ string contains leading period symbol\n$strlen = strlen($vhostsubdomainstr);\n$begsample = substr($vhostsubdomainstr,0,1);\nif ($begsample == &quot;.&quot;) {\n\t$returnval = FALSE;\n\techo &quot;string begins with a \\&quot;.\\&quot; period symbol.\\n&quot;;\n}\n\n\/\/ string contains trailing period symbol\n$endlen = strlen($vhostsubdomainstr);\n$endsample = substr($vhostsubdomainstr,($endlen - 1),1);\nif ($endsample == &quot;.&quot;) {\n\t$returnval = FALSE;\n\techo &quot;string ends with a \\&quot;.\\&quot; period symbol.\\n&quot;;\n}\n\n\/\/ does the vhostsubdomain already exist?\n$vhostsubdomainstrund = str_replace(&quot;.&quot;,&quot;_&quot;,$vhostsubdomainstr);\nclearstatcache();\nif (is_dir(&quot;$bvhwb\/$vhostsubdomainstrund&quot;) ) {\n\t$returnval = FALSE;\n\techo &quot;webdir for proposed vhost already exists.\\n&quot;;\n} else {\n} \/\/ end if (is_dir()\n\n$grepforvhost1str = &quot;grep -i &#039;ServerName $vhostsubdomainstr&#039; $bvhostconfdir\/*&quot;;\n$grepforvhost2str = &quot;grep -i &#039;ServerAlias $vhostsubdomainstr&#039; $bvhostconfdir\/*&quot;;\n$grepforvhost1res = shell_exec($grepforvhost1str);\n$grepforvhost2res = shell_exec($grepforvhost2str);\n\n\/\/ if the string has contents something was there for the grep to find\nif ($grepforvhost1res || $grepforvhost2res) {\n\techo &quot;subdomain appears to be part of an existing virtual host\\n&quot;;\n\t$returnval = FALSE;\n}\n\nreturn $returnval;\n} \/\/ end function vhostsubdomainverify()\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nfunction prependverify($prependverify) {\n\n\/\/ let us make our tests and comparisons case-insensitive\n$lowerpv = strtolower($prependverify);\n\nif ( ( $lowerpv == &quot;n&quot;) || ($lowerpv == &quot;no&quot;) || ($lowerpv == &quot;y&quot;) || ($lowerpv == &quot;yes&quot;) ) {\n\t$returnval = TRUE;\n} else {\n\techo &quot;please indicate n or no, y or yes\\n&quot;;\n\t$returnval = FALSE;\n}\n\nreturn $returnval;\n} \/\/ end function prependverify()\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nfunction usernameverify($vhostusername) {\n\n\/\/ force to lower-case\n$vhostusername = strtolower($vhostusername);\n\n\/\/ assume TRUE until proven false\n$returnval = TRUE;\n\n\/\/ is the string clean?\n\/\/ note that &quot;-&quot; hyphen character is permitted, as is the &quot;_&quot; underscore character, not part of symbol sieve\nif ( preg_match(&#039;\/&#x5B;\\&#039;^\u00a3$%&amp;amp;*()}{@#~?&gt;&amp;lt;&gt;,|=+\u00ac]\/&#039;, $vhostusername) ) {\n\t$returnval = FALSE;\n\techo &quot;string has special character that is not permitted\\n&quot;;\n}\n\n$vhunstrlen = strlen($vhostusername);\n\nif ($vhunstrlen &amp;lt; 2) {\n\techo &quot;username should be minimum 2 characters\\n&quot;;\n\t$returnval = FALSE;\n}\nif ($vhunstrlen &gt; 32) {\n\techo &quot;username should be maximum 32 characters\\n&quot;;\n\t$returnval = FALSE;\n}\n\n\/\/ what does finger return?\n$fingerstr = shell_exec(&quot;finger $vhostusername 2&gt;&amp;amp;1&quot;);\n\nif (!str_contains(&quot;$fingerstr&quot;,&quot;no such user&quot;) ) {\n\techo &quot;finger found this username to already be in use\\n&quot;;\n\t$returnval = FALSE;\n}\n\nreturn $returnval;\n} \/\/ end function usernameverify()\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nfunction passwordplainverify($passwordplain) {\n\n\/\/ assume TRUE until proven false\n$returnval = TRUE;\n\n\/\/ we should do some tests here\n\/\/ but mostly just for length, not all that fancy stuff.\n\/\/ but: we will want to offer to auto-generate a plaintext password\n\n$ppstrlen = strlen($passwordplain);\n\nif ($ppstrlen &amp;lt; 8) {\n\techo &quot;password should be at least 8 characters\\n&quot;;\n\t$returnval = FALSE;\n}\n\nreturn $returnval;\n} \/\/ end function passwordplainverify()\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nfunction genpassverify($genpassverify) {\n\n\/\/ let us make our tests and comparisons case-insensitive\n$lowergpv = strtolower($genpassverify);\n\nif ( ( $lowergpv == &quot;n&quot;) || ($lowergpv == &quot;no&quot;) || ($lowergpv == &quot;y&quot;) || ($lowergpv == &quot;yes&quot;) ) {\n\t$returnval = TRUE;\n} else {\n\techo &quot;please indicate n or no, y or yes\\n&quot;;\n\t$returnval = FALSE;\n}\n\nreturn $returnval;\n} \/\/ end function genpassverify()\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nfunction genuserverify($genuserverify) {\n\n\/\/ let us make our tests and comparisons case-insensitive\n$lowerguv = strtolower($genuserverify);\n\nif ( ( $lowerguv == &quot;n&quot;) || ($lowerguv == &quot;no&quot;) || ($lowerguv == &quot;y&quot;) || ($lowerguv == &quot;yes&quot;) ) {\n\t$returnval = TRUE;\n} else {\n\techo &quot;please indicate n or no, y or yes\\n&quot;;\n\t$returnval = FALSE;\n}\n\nreturn $returnval;\n} \/\/ end function genuserverify()\n\n\/\/ end function sink\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ start get information at command line: vhostsubdomain, vhostusername, vhostpassword\n\/\/ also, generate and derive values\necho &quot;\\naddvhost.php\\n&quot;;\necho &quot;Add a virtual host to Apache\\n\\n&quot;;\n\n\/\/ ask and validate inputs\n\/\/ the readline is trapped in a loop until vhostsubdomainverify() is satisfied\n\/\/ function will also echo text to the console with reasons for rejection to assist the client\n\/\/ bad string format or vhost appears to exist already, etc.\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ vhostsubdomain\n\n$vhostsubdomain = &quot;&quot;;\nwhile (!$vhostsubdomain || !vhostsubdomainverify($vhostsubdomain) ) {\n    $vhostsubdomain = readline(&quot;Enter domain xxxxxxxx.xxx or subdomain xxxxxxxx.xxxxxxxx.xxx: &quot;);\n}\n\n\/\/ putting this here because it is right after we have the $vhostsubdomain string, and just before we need it for $genuseranswer\n\/\/ will also need this later for derived values like the $vhostwebdir\n\n$vhostsubdomainund = str_replace(&quot;.&quot;,&quot;_&quot;,$vhostsubdomain);\n\n\/\/ should we prepend with www. as well?\n\n$prependanswer = &quot;&quot;;\nwhile (!$prependanswer || !prependverify($prependanswer) ) {\n    $prependanswer = readline(&quot;Do you wish to prepend the subdomain www.$vhostsubdomain as well (n\/y)? &quot;);\n}\n\n$prependanswer = strtolower($prependanswer);\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ vhostusername\n\n\/\/ default username\n\/\/ should we offer to automatically generate a username based on the subdomain host name?\n\n$genuseranswer = &quot;&quot;;\nwhile (!$genuseranswer || !genuserverify($genuseranswer) ) {\n    $genuseranswer = readline(&quot;Generate a username? &quot;);\n}\n\n$genuseranswer = strtolower($genuseranswer);\n\nif ( ($genuseranswer==&quot;yes&quot;) || ($genuseranswer==&quot;y&quot;) ) {\n\t\/\/ generate a username\n\t\/\/ we are counting on the novel construction of this name with _ modeled on subdomain\n\n\t$vhostusername = $vhostsubdomainund;\n\t$vhostusernamestrlen = strlen($vhostusername);\n\t\/\/ the unique stuff is closer to the front\n\t\/\/ so we will truncate to first 32 characters\n\tif ($vhostusernamestrlen &gt; 32) {\n\t\t$vhostusername = substr($vhostusername,0,32);\n\t}\n\n\t\/\/ what does finger return?\n\t$fingerstr2 = shell_exec(&quot;finger $vhostusername 2&gt;&amp;amp;1&quot;);\n\tif (!str_contains(&quot;$fingerstr2&quot;,&quot;no such user&quot;) ) {\n\t\techo &quot;finger found this username to already be in use\\n&quot;;\n\t\texit();\n\t}\n\n} else {\n\t\/\/ the client said no to automatic generation of username so we will ask for one\n\t$vhostusername = &quot;&quot;;\n\twhile (!$vhostusername || !usernameverify($vhostusername) ) {\n\t    $vhostusername = readline(&quot;Enter username: &quot;);\n\t}\n} \/\/ end if ($genuseranswer==&quot;yes&quot;)\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ vhostpasswordplain\n\n\/\/ should we offer to automatically generate a plaintext password?\n\n$genpassanswer = &quot;&quot;;\nwhile (!$genpassanswer || !genpassverify($genpassanswer) ) {\n    $genpassanswer = readline(&quot;Generate a plaintext password? &quot;);\n}\n\n$genpassanswer = strtolower($genpassanswer);\n\nif ( ($genpassanswer==&quot;yes&quot;) || ($genpassanswer==&quot;y&quot;) ) {\n\t\/\/ generate a random plaintext password\n\t$vhostpasswordplain = &quot;&quot;;\n\t$passwordlength = &quot;8&quot;;\n\t$posscharsplain = &#039;0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&#039;;\n\t$posscharssymbols = &#039;!@#%*?&#039;;\n\t$posscharspstrlen = strlen($posscharsplain);\n\t$posscharssstrlen = strlen($posscharssymbols);\n\t\/\/ first the plain characters\n\tfor ($i=0;$i&amp;lt;($passwordlength-1);$i++) {\n\t\t$randomint = random_int(0,$posscharspstrlen-1);\n\t\t$randomchar = substr($posscharsplain,$randomint,1);\n\t\t$vhostpasswordplain .= $randomchar;\n\t} \/\/end for $i\n\t\/\/ now the symbol character\n\t\t$randomint = random_int(0,$posscharssstrlen-1);\n\t\t$randomchar = substr($posscharssymbols,$randomint,1);\n\t\t$vhostpasswordplain .= $randomchar;\n\t\/\/ now shuffle the string so the symbol position moves and as bonus the string is different\n\t$vhostpasswordplain = str_shuffle($vhostpasswordplain);\n} else {\n\t\/\/ the client said no to automatic generation of plaintext, so we will ask for one\n\t$vhostpasswordplain = &quot;&quot;;\n\twhile (!$vhostpasswordplain || !passwordplainverify($vhostpasswordplain) ) {\n\t\t$vhostpasswordplain = readline(&quot;Enter plaintext password: &quot;);\n\t}\n} \/\/ end if ($genpassanswer==&quot;yes&quot;)\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ vhostpasswordhashed (transformation)\n\n\/\/ yes, i tried password_hash() -- it did not work for SHA512, this does.\n\/\/ tip: apt install whois to get mkpasswd command\n$vhostpasswordhashed = shell_exec(&quot;mkpasswd -m sha-512 $vhostpasswordplain&quot;);\n\/\/ remove linefeed from the string\n$vhostpasswordhashed = str_replace(&quot;\\n&quot;,&quot;&quot;,$vhostpasswordhashed);\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ end get information at command line: vhostsubdomain, vhostusername, vhostpassword\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ start print collected values\n\n$vhosthomedir = &quot;$bvhwb\/$vhostusername&quot;;\n$vhostwebdir = &quot;$bvhwb\/$vhostusername\/$vhostsubdomain&quot;;\n\necho &quot;\\nvalues collected, generated, and derived\\n\\n&quot;;\n\necho &quot;vhostsubdomain: $vhostsubdomain\\n&quot;;\necho &quot;prependanswer: $prependanswer\\n&quot;;\necho &quot;vhostusername: $vhostusername\\n&quot;;\necho &quot;genpassanswer: $genpassanswer\\n&quot;;\nif ($echoplaintextpasswords) {\n\techo &quot;vhostpasswordplain: $vhostpasswordplain\\n&quot;;\n}\necho &quot;vhostpasswordhashed: $vhostpasswordhashed\\n&quot;;\necho &quot;vhosthomedir: $vhosthomedir\\n&quot;;\necho &quot;vhostwebdir: $vhostwebdir\\n&quot;;\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ end print collected values\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ start engine section\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ create the $vhostusername with $vhosthomedir and $vhostpasswordhashed\n\n\/\/ build the string, look at the string, then maybe do a shell_exec of the string\n$shelluseraddstr = &quot;useradd -m -d &#039;$vhosthomedir&#039; &#039;$vhostusername&#039; -s &#039;\/usr\/bin\/bash&#039; -p &#039;$vhostpasswordhashed&#039;&quot;;\n\n\/\/ disable for production\n\/\/ echo &quot;shelluseraddstr: $shelluseraddstr\\n&quot;;\n\n\/\/ so it will always be declared\n$shelluseraddret = &quot;&quot;;\n\/\/ disable for testing other conditions without committing to this\n$shelluseraddret = shell_exec($shelluseraddstr);\n\n\/\/echo &quot;shelluseraddret: $shelluseraddret\\n&quot;;\n\/\/ non-null (non-0) exit value from shell indicates an error\nif ($shelluseraddret) {\n\techo &quot;ERROR: there was a problem executing the shell command to create the vhostusername $vhostusername. Stopping.\\n&quot;;\n\texit();\n} else {\n\t\/\/echo &quot;SUCCESS: the vhostusername $vhostusername was created\\n&quot;;\n}\n\necho &quot;\\n&quot;;\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ mkdir $vhostwebdir\n\n$mkdirvhostwebdirret = mkdir($vhostwebdir,0775,TRUE);\n\nif (!$mkdirvhostwebdirret) {\n\techo &quot;ERROR: there was a problem creating the vhostwebdir $vhostwebdir. Stopping\\n&quot;;\n\texit();\n} else {\n\t\/\/echo &quot;SUCCESS: the vhostwebdir $vhostwebdir was created.\\n&quot;;\n}\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ fwrite $vhostwebdir\/index.php\n\n$indexfilecontents = &quot;&amp;lt;?PHP\\n\\necho \\&quot;&amp;lt;p&gt;$vhostsubdomain&amp;lt;\/p&gt;\\\\n\\&quot;;\\n&quot;;\n\n$fh1 = fopen(&quot;$vhostwebdir\/index.php&quot;,&quot;w&quot;);\n$filesuccess1 = fwrite($fh1,$indexfilecontents);\nfclose($fh1);\n \nif ($filesuccess1) {\n\n\t\/\/chown root $vhosthomedir\n\n\t$vhosthomedirownretu1 = chown(&quot;$vhosthomedir&quot;,&quot;root&quot;);\n\tif ($vhosthomedirownretu1) {\n\t\t\/\/echo &quot;SUCCESS chown root $vhosthomedir\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chown root $vhosthomedirdir not successful\\n&quot;;\n\t\texit();\n\t}\n\n\t\/\/chgrp root $vhosthomedir\n\n\t$vhosthomedirownretg1 = chgrp(&quot;$vhosthomedir&quot;,&quot;root&quot;);\n\tif ($vhosthomedirownretg1) {\n\t\t\/\/echo &quot;SUCCESS chgrp root $vhosthomedir\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chgrp root $vhosthomedirdir not successful\\n&quot;;\n\t\texit();\n\t}\n\n\t\/\/echo &quot;SUCCESS indexfile written to file: $vhostwebdir\/index.php\\n&quot;;\n    \/\/ chmod the $vhosthomedir\n    chmod(&quot;$vhosthomedir&quot;, 0755);\n\t\/\/ chmod the $vhostwebdir\n\tchmod(&quot;$vhostwebdir&quot;, 0755);\n\t$vhostwebdirperms = substr(sprintf(&#039;%o&#039;, fileperms(&quot;$vhostwebdir&quot;)), -4);\n\t\/\/echo &quot;vhostwebdirperms: $vhostwebdirperms\\n&quot;;\n\tif ($vhostwebdirperms == &quot;0755&quot;) {\n\t\t\/\/echo &quot;SUCCESS chmod 755 $vhostwebdir\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chmod 755 $vhostwebdir not successful. Stopping.\\n&quot;;\n\texit();\n\t}\n\n\t\/\/ chown $vhostusername $vhostwebdir\n\t$vhostwebdirownretu1 = chown(&quot;$vhostwebdir&quot;,$vhostusername);\n\tif ($vhostwebdirownretu1) {\n\t\t\/\/echo &quot;SUCCESS chown $vhostusername $vhostwebdir\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chown $vhostusername $vhostwebdir not successful\\n&quot;;\n\t\texit();\n\t}\n\n\t\/\/ chgrp $vhostusername $vhostwebdir\n\t$vhostwebdirownretg1 = chgrp(&quot;$vhostwebdir&quot;,$vhostusername);\n\tif ($vhostwebdirownretu1) {\n\t\t\/\/echo &quot;SUCCESS chgrp $vhostusername $vhostwebdir\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chgrp $vhostusername $vhostwebdir not successful\\n&quot;;\n\t\texit();\n\t}\n\n\t\/\/ chmod the $vhostwebdir\/index.php\n\tchmod(&quot;$vhostwebdir\/index.php&quot;, 0755);\n\t$vhostindexperms = substr(sprintf(&#039;%o&#039;, fileperms(&quot;$vhostwebdir\/index.php&quot;)), -4);\n\t\/\/echo &quot;vhostindexperms: $vhostindexperms\\n&quot;;\n\tif ($vhostindexperms == &quot;0755&quot;) {\n\t\t\/\/echo &quot;SUCCESS chmod 755 $vhostwebdir\/index.php\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chmod 755 $vhostwebdir\/index.php not successful. Stopping.\\n&quot;;\n\texit();\n\t}\n\n\t\/\/ chown $vhostusername $vhostwebdir\/index.php\n\t$vhostindexownretu1 = chown(&quot;$vhostwebdir\/index.php&quot;,$vhostusername);\n\tif ($vhostindexownretu1) {\n\t\t\/\/echo &quot;SUCCESS chown $vhostusername $vhostwebdir\/index.php\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chown $vhostusername $vhostwebdir\/index.php not successful\\n&quot;;\n\t\texit();\n\t}\n\n\t\/\/ chgrp $vhostusername $vhostwebdir\/index.php\n\t$vhostindexownretg1 = chgrp(&quot;$vhostwebdir\/index.php&quot;,$vhostusername);\n\tif ($vhostindexownretu1) {\n\t\t\/\/echo &quot;SUCCESS chgrp $vhostusername $vhostwebdir\/index.php\\n&quot;;\n\t} else {\n\t\techo &quot;ERROR chgrp $vhostusername $vhostwebdir\/index.php not successful\\n&quot;;\n\t\texit();\n\t}\n} else {\n\techo &quot;ERROR indexfile not written to file: $vhostwebdir\/index.php\\n&quot;;\n\texit();\n}\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ fwrite $bvhostconfdir\/$vhostsubdomain.conf\n\n$timestring = date(&quot;Y\/m\/d H:i:s T&quot;);\n\n$vhostconffilecontents  = &quot;# generated $timestring by addvhost.php\\n&quot;;\n$vhostconffilecontents .= &quot;&amp;lt;VirtualHost $vhostip:80&gt;\\n&quot;;\n$vhostconffilecontents .= &quot;&amp;lt;IfModule mpm_itk_module&gt;\\n&quot;;\n$vhostconffilecontents .= &quot;\\tAssignUserID $vhostusername $vhostusername\\n&quot;;\n$vhostconffilecontents .= &quot;&amp;lt;\/IfModule&gt;\\n&quot;;\n$vhostconffilecontents .= &quot;ServerName $vhostsubdomain\\n&quot;;\nif ( ($prependanswer == &quot;y&quot;) || ($prependanswer == &quot;yes&quot;) ) {\n\t$vhostconffilecontents .= &quot;ServerAlias www.$vhostsubdomain\\n&quot;;\n}\n$vhostconffilecontents .= &quot;DocumentRoot $vhostwebdir\\n&quot;;\n$vhostconffilecontents .= &quot;ServerAdmin $vhostserveradmin\\n&quot;;\n$vhostconffilecontents .= &quot;CustomLog \/var\/log\/apache2\/$vhostsubdomain-access_log combined\\n&quot;;\n$vhostconffilecontents .= &quot;ErrorLog \/var\/log\/apache2\/$vhostsubdomain-error_log\\n&quot;;\n$vhostconffilecontents .= &quot;&amp;lt;\/VirtualHost&gt;\\n&quot;;\n\n\/\/ disable in production\n\/\/ echo &quot;vhostconffilecontents = \\n$vhostconffilecontents\\n&quot;;\n\n\/\/ write the text file\n$fh2 = fopen(&quot;$bvhostconfdir\/$vhostsubdomain.conf&quot;,&quot;w&quot;);\n$filesuccess2 = fwrite($fh2,$vhostconffilecontents);\nfclose($fh2);\n \nif ($filesuccess2) {\n\t\/\/echo &quot;SUCCESS virtual host config written to file: $bvhostconfdir\/$vhostsubdomain.conf\\n&quot;;\n} else {\n \techo &quot;ERROR virtual host config not written to file: $bvhostconfdir\/$vhostsubdomain.conf\\n&quot;;\n \texit();\n}\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ shell_exec a2ensite $bvhostconfdir\/$vhostsubdomain.conf\n\n\/\/ so it will always be declared\n$shella2enret = &quot;&quot;;\n\/\/ disable for testing other conditions without committing to this\n$shella2enret = shell_exec(&quot;a2ensite $vhostsubdomain.conf&quot;);\n\n\/\/echo &quot;shella2enret: $shella2enret\\n&quot;;\n\/\/ non-null (non-0) exit value from shell indicates an error\nif ( str_contains($shella2enret,&quot;ERROR&quot;) ) {\n\t\/\/echo &quot;ERROR: there was a problem executing the shell command to enable the vhostsubdomain $vhostsubdomain. Stopping.\\n&quot;;\n\texit();\n} else {\n\techo &quot;SUCCESS: enabled vhostsubdomain $vhostsubdomain\\n&quot;;\n}\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ echo to console suggestion that systemctl restart apache2 be executed\n\necho &quot;\\n&quot;;\necho &quot;Next steps:\\n&quot;;\necho &quot;This script did not restart apache2. That is up to you.\\n&quot;;\necho &quot;systemctl restart apache2\\n&quot;;\necho &quot;validate site on port 80\\n&quot;;\necho &quot;run certbot --apache to expand ssl cert\\n&quot;;\necho &quot;systemctl restart apache2\\n&quot;;\necho &quot;validate site on port 443\\n&quot;;\necho &quot;\\n&quot;;\n\n\/\/ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ end engine section\n\n<\/pre><\/div>\n\n\n<h1 class=\"wp-block-heading\">Declaring the host name in DNS<\/h1>\n\n\n\n<p>Declare the host name in the DNS zone file for the domain:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"1024\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-13-920x1024.png\" alt=\"\" class=\"wp-image-3270\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-13-920x1024.png 920w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-13-269x300.png 269w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-13-768x855.png 768w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-13.png 1113w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"1024\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-14-920x1024.png\" alt=\"\" class=\"wp-image-3271\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-14-920x1024.png 920w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-14-269x300.png 269w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-14-768x855.png 768w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-14.png 1113w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Testing that the host name loads a &#8220;park page&#8221;<\/h2>\n\n\n\n<p>Use a web browser to visit the host name:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"936\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-27-1024x936.png\" alt=\"\" class=\"wp-image-3186\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-27-1024x936.png 1024w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-27-300x274.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-27-768x702.png 768w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-27.png 1113w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"936\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-35-1024x936.png\" alt=\"\" class=\"wp-image-3187\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-35-1024x936.png 1024w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-35-300x274.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-35-768x702.png 768w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-18-57-35.png 1113w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Executing commands as root<\/h1>\n\n\n\n<p>This chapter assumes that you are logged in as the root user. If you are not already root, escalate using this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsudo su\n<\/pre><\/div>\n\n\n<h1 class=\"wp-block-heading\">Installing the finger, whois, and unzip utilities<\/h1>\n\n\n\n<p>This script depends on the finger, whois, and unzip utilities.<\/p>\n\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\napt install finger whois unzip\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-3.png\" alt=\"\" class=\"wp-image-3237\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-3.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-3-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-3-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-4.png\" alt=\"\" class=\"wp-image-3238\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-4.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-4-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-4-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Downloading and uncompressing the addvhost.zip file<\/h1>\n\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nwget https:\/\/blog.gordonbuchan.com\/files\/addvhost.zip\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-5.png\" alt=\"\" class=\"wp-image-3241\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-5.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-5-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-5-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-6.png\" alt=\"\" class=\"wp-image-3242\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-6.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-6-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-6-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nunzip addvhost.zip\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-7.png\" alt=\"\" class=\"wp-image-3244\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-7.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-7-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-7-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-8.png\" alt=\"\" class=\"wp-image-3246\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-8.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-8-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-8-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>Enter these commands:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmv addvhost.php \/usr\/bin\nchmod 755 \/usr\/bin\/addvhost.php\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-9.png\" alt=\"\" class=\"wp-image-3248\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-9.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-9-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-9-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\naddvhost.php\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"533\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-10.png\" alt=\"\" class=\"wp-image-3250\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-10.png 786w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-10-300x203.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/image-10-768x521.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Completing the addvhost.php questionnaire, entering a username and a plaintext password<\/h1>\n\n\n\n<p>In this example, we decline the option to generate a username, and enter a value for the username. We also decline the option to generate a plaintext password, and enter a value for the plaintext password.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nroot@server01:~# addvhost.php\n\naddvhost.php\nAdd a virtual host to Apache\n\nEnter domain xxxxxxxx.xxx or subdomain xxxxxxxx.xxxxxxxx.xxx: webpresencestepbystep.ca\nDo you wish to prepend the subdomain www.webpresencestepbystep.ca as well (n\/y)? y\nGenerate a username? n\nEnter username: webuserca\nGenerate a plaintext password? n\nEnter plaintext password: password\n\nvalues collected, generated, and derived\n\nvhostsubdomain: webpresencestepbystep.ca\nprependanswer: y\nvhostusername: webuserca\ngenpassanswer: n\nvhostpasswordplain: password\nvhostpasswordhashed: $6$IdLp5YrW.Z3Tvnm$hlRvIBour47UcZrVm0QA2YgLp2z3C3e5W7PwiS3o.KbZz.mtFeCvWdew\/eemdec3Wz9t.WEIuIm3Q2EKTuXYd1\nvhosthomedir: \/usr\/web\/webuserca\nvhostwebdir: \/usr\/web\/webuserca\/webpresencestepbystep.ca\n\nSUCCESS: enabled vhostsubdomain webpresencestepbystep.ca\n\nNext steps:\nThis script did not restart apache2. That is up to you.\nsystemctl restart apache2\nvalidate site on port 80\nrun certbot --apache to expand ssl cert\nsystemctl restart apache2\nvalidate site on port 443\n<\/pre><\/div>\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsystemctl restart apache2\n<\/pre><\/div>\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncertbot --apache\n<\/pre><\/div>\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsystemctl restart apache2\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Visiting the virtual host<\/h2>\n\n\n\n<p>Use a web browser to visit the host name:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"893\" height=\"851\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-08-57.png\" alt=\"\" class=\"wp-image-3205\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-08-57.png 893w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-08-57-300x286.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-08-57-768x732.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Completing the addvhost.php questionnaire, accepting a generated username and plaintext password<\/h1>\n\n\n\n<p>In this example, we accept the option to generate a username. We also accept the option to generate a plaintext password.<\/p>\n\n\n\n<p>Take careful note of the plaintext password value, as shown in the &#8220;vhostpasswordplain&#8221; field.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nroot@server01:~# addvhost.php\n\naddvhost.php\nAdd a virtual host to Apache\n\nEnter domain xxxxxxxx.xxx or subdomain xxxxxxxx.xxxxxxxx.xxx: webpresencestepbystep.com\nDo you wish to prepend the subdomain www.webpresencestepbystep.com as well (n\/y)? y\nGenerate a username? y\nGenerate a plaintext password? y\n\nvalues collected, generated, and derived\n\nvhostsubdomain: webpresencestepbystep.com\nprependanswer: y\nvhostusername: webpresencestepbystep_com\ngenpassanswer: y\nvhostpasswordplain: NQeQ2%VT\nvhostpasswordhashed: $6$Woe9pPUwnXqUP$9RW60p6SSNfqLJSi4BeAyhe89mBpyTELk2\/at7eJcKqou5Q9Y6Nti4P7EoyTV0CBfin6SxlvNHvkZjrpEGxxX0\nvhosthomedir: \/usr\/web\/webpresencestepbystep_com\nvhostwebdir: \/usr\/web\/webpresencestepbystep_com\/webpresencestepbystep.com\n\nSUCCESS: enabled vhostsubdomain webpresencestepbystep.com\n\nNext steps:\nThis script did not restart apache2. That is up to you.\nsystemctl restart apache2\nvalidate site on port 80\nrun certbot --apache to expand ssl cert\nsystemctl restart apache2\nvalidate site on port 443\n<\/pre><\/div>\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsystemctl restart apache2\n<\/pre><\/div>\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncertbot --apache\n<\/pre><\/div>\n\n\n<p>Enter this command:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsystemctl restart apache2\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Visiting the virtual host<\/h2>\n\n\n\n<p>Use a web browser to visit the host name:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"893\" height=\"851\" src=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-09-52.png\" alt=\"\" class=\"wp-image-3201\" srcset=\"https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-09-52.png 893w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-09-52-300x286.png 300w, https:\/\/blog.gordonbuchan.com\/blog\/wp-content\/uploads\/2021\/05\/Screenshot-from-2021-05-18-20-09-52-768x732.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p>Previous step: Chapter 15: <a href=\"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/2021\/05\/01\/web-presence-step-by-step-chapter-15-using-dwservice-net-to-provide-remote-technical-support-as-an-alternative-to-teamviewer\/\">Using dwservice.net to provide remote technical support as an alternative to TeamViewer<\/a><br>Next step: Chapter 17: <a href=\"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/2021\/05\/29\/web-presence-step-by-step-chapter-17-using-subdomains-to-host-multiple-websites-under-a-single-domain-name\/\">Using subdomains to host multiple websites under a single domain name<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Previous step: Chapter 15: Using dwservice.net to provide remote technical support as an alternative to TeamViewerNext step: Chapter 17: Using subdomains to host multiple websites under a single domain name Web presence step by step is a series of posts that show you to how to build a web presence. In this chapter we install &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/2021\/05\/19\/web-presence-step-by-step-chapter-16-using-a-script-to-automate-the-creation-of-a-virtual-host-on-an-apache-web-server\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Web presence step by step Chapter 16: Using a script to automate the creation of a virtual host on an Apache web server&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-3180","post","type-post","status-publish","format-standard","hentry","category-linux"],"_links":{"self":[{"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3180","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=3180"}],"version-history":[{"count":82,"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3180\/revisions"}],"predecessor-version":[{"id":5588,"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3180\/revisions\/5588"}],"wp:attachment":[{"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=3180"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=3180"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.gordonbuchan.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=3180"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}