Chris Gilligan » new media http://chrisgilligan.com portfolio of web work Fri, 24 May 2013 18:34:12 +0000 en-US hourly 1 http://wordpress.org/?v=3.5.1 WordPress Fail2Ban RegEx for RedHat, CentOS, Amazon Linux http://chrisgilligan.com/consulting/wordpress-wp-fail2ban-regex-redhat-centos-amazon-linux/ http://chrisgilligan.com/consulting/wordpress-wp-fail2ban-regex-redhat-centos-amazon-linux/#comments Sun, 28 Apr 2013 00:30:24 +0000 Chris Gilligan http://chrisgilligan.com/?p=1875 VacantServer WordPress sites are getting hammered with bad logins and probes. We’ve implemented a plugin to log failed login attempts to syslog, and a Fail2Ban filter for the same. If you run these on RedHat, you’ll need some additional configuration info… here it is: WordPress login failure regex (error_log): [crayon-519fc7112cd99/] Apache nohome regex (error_log): [crayon-519fc7112ce1a/] [...]

The post WordPress Fail2Ban RegEx for RedHat, CentOS, Amazon Linux appeared first on Chris Gilligan » new media.

]]>
VacantServer WordPress sites are getting hammered with bad logins and probes.

We’ve implemented a plugin to log failed login attempts to syslog, and a Fail2Ban filter for the same. If you run these on RedHat, you’ll need some additional configuration info… here it is:

WordPress login failure regex (error_log):
^%(__prefix_line)sAuthentication failure for .* from <HOST>$

Apache nohome regex (error_log):

[[]client <HOST>[]] File does not exist: .*/~.*

PHP noscript regex (/home/*/logs/error_log,/var/log/httpd/error_log):

[[]client <HOST>[]] (File does not exist|script not found or unable to stat): /\S*(\.php|\.asp|\.exe|\.pl)
[[]client <HOST>[]] script '/\S*(\.php|\.asp|\.exe|\.pl)\S*' not found or unable to stat *$

Please enable the generic apache-nohome, apache-noscript. Install wp fail2ban plugin (and configure it for your server) on your high traffic blogs. These all are helping during the current onslaught, which also includes probing for wp-admin directories, probing for /wp-admin/login.php, plus comment spam.

Here are some additional resources:

Incoming search terms for the article:

The post WordPress Fail2Ban RegEx for RedHat, CentOS, Amazon Linux appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/consulting/wordpress-wp-fail2ban-regex-redhat-centos-amazon-linux/feed/ 0
Fail2Ban Regex for RedHat, Fedora, CentOS and Amazon Linux 2013 http://chrisgilligan.com/portfolio/fail2ban-regex/ http://chrisgilligan.com/portfolio/fail2ban-regex/#comments Sat, 20 Apr 2013 01:07:24 +0000 Chris Gilligan http://chrisgilligan.com/?p=1831 Fail2Ban is a very efficient daemon that scans log files for malicious activity, and offers several options to ban offending IPs and hostnames. Although it is highly configurable, it requires a depth of knowledge beyond that required for GUI-accessible firewalls such as ConfigServer Security & Firewall. Harden the Kernel Before do anything further, it is [...]

The post Fail2Ban Regex for RedHat, Fedora, CentOS and Amazon Linux 2013 appeared first on Chris Gilligan » new media.

]]>

Fail2Ban is a very efficient daemon that scans log files for malicious activity, and offers several options to ban offending IPs and hostnames. Although it is highly configurable, it requires a depth of knowledge beyond that required for GUI-accessible firewalls such as ConfigServer Security & Firewall.

Harden the Kernel

Before do anything further, it is necessary to harden the server at the kernel level. Doing so will prevent the majority of attacks that Fail2Ban config will have to deal with. Do this: Linux kernel sysctl hardening. Prior to learning those trix, I had solely relied upon ConfigServer Security and Firewall (CSF) to block malicious attacks. Heading those off at the kernel level is much more efficient.

I’m a huge fan of CSF, having used it with cPanel, Webmin and Virtualmin for many years. Moving to Amazon Web Services presented a challenge, however. I’ve yet to figure out how to use CSF on AWS, so I’ve backtracked to my old standby, Fail2Ban.

“Out of the box,” Fail2Ban for CentOS & other RedHat downstream distros only provides protection against SASL login failures. But with a little work and research, one can successfully configure many more “jails” for the malicious traffic. To take full advantage of Fail2Ban, the admin will need to become familiar with RegEx: regular expressions.

Amazon Web Services Security Groups

AWS provides several levels of firewall protection for EC2 instances; I mainly utilize EC2 security groups and VPC security groups. These firewalls serve to allow traffic to an instance based on IP address, IP block, port, etc. VPC will gateway traffic to and within a virtual cloud; one can create a security group for a cohort of replicated database servers to only allow traffic from within the virtual cloud. Likewise, one could create a cloud group with public facing, load-balanced Varnish reverse proxies that interact with an Apache or NginX app server, and that app server connects to the database cohort. The only publicly-accessible servers would be the web proxies. This is similar to what one might expect in a traditional network operations center.

Learn some RegEx

Learning a bit about RegEx will benefit the admin in virtually every programming endeavor: PHP, Perl, C, SQL etc. all make use of RegEx to match strings of text. Matching a variable string of text in a log file, for example, allows Fail2Ban to act upon that string, sending an email, looking up a hostname, and/or invoking a firewall rule to block traffic from the offending origin for a designated amount of time.

As stated earlier, plain vanilla Fail2Ban regex from EPEL is functional only to block SASL authentication errors. Most of the filters in the package are, at best, examples of what can be done, and will not function as written. Why? Because Fail2Ban has to deal with the particular implementations of log files in various GNU/Linux distributions, and the peculiarities of the reporting structure in those logs, no single RegEx, nor log file location, is going to work for every distribution. Neither can Fail2Ban account for every change in log file location or structure from version to version. What worked for Centos 5.x may not work for CentOS 6.x; what worked in kernel 2.8 may be invalid in kernel 3.4.

Also stated earlier, it is well worth the time to learn a little about RegEx. One vehicle to propel the admin far along the path is RegExr. Author Greg Skinner provides an online version as well as Adobe AIR standalone app. RegExr is an invaluable tool for writing and proofing regular expressions.

Screen Shot 2013-04-19 at 6.32.51 PM

Write the RegEx

OK, here’s the payoff for reading so far: a regex filter that will squelch Dovecot brute force attacks on CentOS 6, Amazon Linux 2013 and other kernel 3.4 RedHat downstream distros such as Fedora and Arch. This would, of course go in /etc/fail2ban/filter.d/dovecot.conf:

Here’s the original, commented out via #:

#failregex = .*(?:pop3-login|dovecot):.*(?:Authentication failure|Aborted login \(auth failed|Aborted login \(tried to use disabled|Disconnected \(auth failed).*rip=(?P<host>\S*),.*

…and here’s a substitute, created via experimentation with RegExr:

failregex = .*auth.*pam.*dovecot.*(?:authentication failure).*rhost=<HOST>.*

How to create a valid RegEx to match the failures to ban? For that, one must monitor log files. In the case of Dovecot on Amazon Linux 2013, that would be /var/log/secure — same as it has ever been with RedHat downstreams. However, the logging is different than it may have been in CentOS 5.8.

Have a look at the log files: use grep to filter the secure log on “authentication failure”

tail -f -n 20000 /var/log/secure | grep "authentication failure"

There will likely be log entries similar to the following:

Apr 19 05:22:19 vm5 auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=oracle rhost=80.255.3.104 
Apr 19 05:22:20 vm5 auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=sybase rhost=80.255.3.104 
Apr 19 05:22:20 vm5 auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=informix rhost=80.255.3.104

RegEx mentioned previously will match those entries, and via Fail2Ban: BAN the offending IPs.

So, you think you’re ready with RegEx fu to tackle the Local Jail (jail.local)? Not so fast. Don’t use regex in jail.local. This file takes wild cards, not regex… so if you want to monitor all users’ /home/user/logs/access_log — you must wildcard /home/*/logs/access_log . NOT /home/.*/logs !

… more to come: follow this post for more details!

Incoming search terms for the article:

The post Fail2Ban Regex for RedHat, Fedora, CentOS and Amazon Linux 2013 appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/portfolio/fail2ban-regex/feed/ 0 Chris Gilligan
Auto Mailer Form with Validation and reCAPTCHA http://chrisgilligan.com/forms/auto-mailer-form-with-validation-and-recaptcha/ http://chrisgilligan.com/forms/auto-mailer-form-with-validation-and-recaptcha/#comments Wed, 16 Jan 2013 21:51:06 +0000 Chris Gilligan http://chrisgilligan.com/?p=1566 I was recently searching for a form template that uses Twitter’s Bootstrap framework. PFBC, the PHP Form Builder Class, was recently refactored with Bootstrap, and it also supports validation and reCAPTCHA. PFBC does not include a form handler, but it does take the difficult and redundant work out of creating self-validating forms. Adding an auto-mailer [...]

The post Auto Mailer Form with Validation and reCAPTCHA appeared first on Chris Gilligan » new media.

]]>

Screen Shot 2013-01-16 at 4.19.03 PMI was recently searching for a form template that uses Twitter’s Bootstrap framework. PFBC, the PHP Form Builder Class, was recently refactored with Bootstrap, and it also supports validation and reCAPTCHA. PFBC does not include a form handler, but it does take the difficult and redundant work out of creating self-validating forms.

Adding an auto-mailer to PFBC is very simple, however. You just need a simple PHP if/else and all is well.

If you have read this far, you should go ahead and download PFBC version 3 (with Bootstrap). You’ll want to install it on a dev server and read the documentation, which is on the example pages. Installation: put the downloaded folder in a public_html space that’s running PHP 5+.

Note that there are different packages for PHP 5.3, so get the correct flavor of version 3. This example works with PHP 5.3.

I have created an example form that includes most of the different types of inputs, all you need do is create a file in the same folder as the package’s main index.php. I’ve heavily commented the code, so you should be able to easily understand it.

<?php
// php-form-builder-class PFBC 
// set up name spaces for form, elements, and validation
use PFBC\Form;
use PFBC\Element;
use PFBC\Validation;

session_start();
error_reporting(E_ALL);
include("PFBC/Form.php");

include("header.php");
$version = file_get_contents("version");

// has the form been submitted and passed validation? 
// If so, send email and display a confirmation message.
if(Form::isValid($_POST["form"])) {
    /*The form's submitted data has been validated.  Your script can now proceed with any 
    further processing required.*/

  //get the POST data from the form
  $Course 				= $_POST['Course'];
  $Semester 			= $_POST['Semester'];
  $WhyUnavailable 		= $_POST['WhyUnavailable'];
  $DetailUnavailable 	= $_POST['DetailUnavailable'];
  $Classification 		= $_POST['Classification'];
  $Major 				= $_POST['Major'];
  $Email 				= $_POST['Email'];
  $Comments 			= $_POST['Comments'];
  // loop through the When field's checkbox group and output them all with a return between values
  foreach ($_POST['When'] as $val) { 
  $When .= $val."\n";
	}
  $DetailWhen 			= $_POST['DetailWhen'];

 	// create and send the email
	$to 			= "you@yourdomain.com";

    $email_from 	= $Email;

    $email_subject 	= "Course Request: $Course, $Semester, $When";

    $email_body 	= 	"Course: $Course\n\n".
						"Semester: $Semester\n\n".
						"Why: $WhyUnavailable\n$DetailUnavailable\n\n".
						"Classification: $Classification\n".
						"Major: $Major\n\n".
						"Email: $Email\n\n".
						"Comments: $Comments\n\n".
						"When offered:\n$When".
						"$DetailWhen\n\n";

	$headers		= "From: $email_from \r\n".
					  "Reply-To: $email_from \r\n";

	mail($to,$email_subject,$email_body,$headers);

		echo "			<div class=\"alert alert-success\">
				<a class=\"close\" data-dismiss=\"alert\" href=\"#\">×</a>
				<strong class=\"alert-heading\"> Thank you for submitting a course Course Request. By providing this feedback you will help us to make adjustments to the schedule both now and in the future.</strong>
			</div>";

 }//end if(Form::isValid

// is the form loaded for the first time, or does input need to be entered or corrected?
// if so, display the form. Validation prompts are displayed by the form itself.
else {
    header("Location: " . $_SERVER["PHP_SELF"]);
    /*Validation errors have been found.  We now need to redirect back to the 
    script where the form exists so the errors can be corrected and the form
    re-submitted.*/

echo "<h1>What course do you need?</h1>
<p>We are committed to doing all we can to help you graduate in as timely a fashion as possible. We know that sometimes, as you arrange your schedule for the upcoming semester, you are unable to find an available seat in a course that you need. Whenever possible we aim to be responsive to students' needs. By providing this feedback you will help us to make adjustments to the schedule both now and in the future.</p>
<div class=\"alert alert-warning\">
				<a class=\"close\" data-dismiss=\"alert\" href=\"#\">×</a>
				<strong class=\"alert-heading\">* denotes required fields.</strong>
			</div>";

// The form identifier
$form = new Form("CourseNeed");
// Prevent bootstrap and jquery from re-loading, already loaded in doc head
$form->configure(array(
	"prevent" => array("bootstrap", "jQuery")
));
// Validation field for feedback messages to user
$form->addElement(new Element\Hidden("form", "CourseNeed"));
// Form fields
// simple textbox, required
$form->addElement(new Element\Textbox("Course:", "Course", array(
	"required" => 1,
	"shortDesc" => "Department and course number of class you need",
	//"placeholder" => "MATH 101"
)));
// simple select, required
$form->addElement(new Element\Select("Semester:", "Semester", array( // first array is the options
	"",// blank value to force a choice
	"Fall",
	"Spring",
	"Summer I",
	"Summer II",
	),
	array( // second array is the parameters
	"Required" => 1, // required to force a choice
	"shortDesc" => "Choose One&hellip;"
)));
// simple select, required
$form->addElement(new Element\Select("Why Unavailable:", "WhyUnavailable", array( // first array is the options
	"",// blank value to force a choice
	"All the scheduled spots are full",
	"All available classes conflict with my schedule",
	"The course is not being offered in that semester",
	"Other",
	),
	array( // second array is the parameters
	"Required" => 1, // required to force a choice
	"shortDesc" => "Choose One&hellip; Why is the course unavailable to you?"
)));
$form->addElement(new Element\Textbox("Details:", "DetailUnavailable", array(
	"shortDesc" => "If 'Other': Why is the course unavailable to you?"
	)));
// simple select, required
$form->addElement(new Element\Select("Your Classification:", "Classification", array( // first array is the options
	"",// blank value to force a choice
	"Freshman",
	"Sophomore",
	"Junior",
	"Senior",
	"Graduate",
	),
	array( // second array is the parameters
	"Required" => 1, // required to force a choice
	"shortDesc" => "Choose Your Classsification&hellip;"
)));
$form->addElement(new Element\Textbox("Major:", "Major", array(
	"required" => 1,
	"shortDesc" => "Your Major"
)));
// email, self-validating, required
$form->addElement(new Element\Email("Email:", "Email", array(
	"Required" => 1,
	"shortDesc" => "Preferably, a university email address",
	"placeholder" => "you@domain.com"
)));
// simple textarea
$form->addElement(new Element\Textarea("Comments:", "Comments", array(
	"placeholder" => "Optional: additional information that may explain your situation or your needs."
)));
// simple checkbox set
$form->addElement(new Element\Checkbox("When:", "When", array( // first array is the options
	"Morning",
	"Afternoon",
	"Evening",
	"Online",
	"Other",
	),
	array( // second array is the parameters
	"Required" => 1, // required to force a choice
	"shortDesc" => "Please choose time(s) when would you like the course offered&hellip;"
)));
$form->addElement(new Element\Textbox("Details:", "DetailWhen", array(
	"shortDesc" => "If 'Other': When would you like the course offered?"
	)));
// reCAPTCHA with validation, automaticallly required
$form->addElement(new Element\Captcha("Validation:", array(
	"longDesc" => "This is a <a href=\"http://en.wikipedia.org/wiki/ReCAPTCHA\">
	reCaptcha test</a> to reduce spam submissions."
)));
// submit button and cancel button
$form->addElement(new Element\Button);
$form->addElement(new Element\Button("Cancel", "button", array(
	"onclick" => "history.go(-1);"
)));
// draw the form
$form->render();
}//end else

include("footer.php");

If you want to implement a form on a site that does not include Twitter Bootstrap, you may find the bootstrap.css conflicts with many of your site’s styling.

In that case, you can go to Bootstrap’s Customize and Download page, and choose only the css, js and components you need:

Scaffolding
none

Base CSS
Forms, Buttons

Components
Button groups, Alerts

JS Components
Collapse

Miscellaneous
Close icon

Utilities
Component animations

Responsive
none

Incoming search terms for the article:

The post Auto Mailer Form with Validation and reCAPTCHA appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/forms/auto-mailer-form-with-validation-and-recaptcha/feed/ 2 Chris Gilligan
Install phpMyAdmin with SSL on CentOS, Amazon Linux, RedHat (Apache or NginX) http://chrisgilligan.com/consulting/install-phpmyadmin-with-ssl-on-centos-amazon-linux-redhat/ http://chrisgilligan.com/consulting/install-phpmyadmin-with-ssl-on-centos-amazon-linux-redhat/#comments Mon, 24 Sep 2012 05:53:52 +0000 Chris Gilligan http://chrisgilligan.com/?p=1132 How to install phpMyAdmin on CentOS, Amazon Linux, or Redhat. Configuration instructions are provided for Apache and NginX web servers.

The post Install phpMyAdmin with SSL on CentOS, Amazon Linux, RedHat (Apache or NginX) appeared first on Chris Gilligan » new media.

]]>

phpMyAdmin database display

I recently ran into a problem with the upgraded 3.5.2 phpMyAdmin package provided via the rpmforge.repo. Search no longer works, nor does pagination, etc. Plus, it’s out of date and vulnerable to an XSS exploit.

I have solved this by changing to the EPEL repo, which maintains the latest version of phpMyAdmin.

This post will teach you how to install phpMyAdmin on CentOS, Amazon Linux, or Redhat. Configuration instructions are provided for Apache and NginX web servers.

For this to work properly and safely, you should be running SSL on your host. Otherwise, change the ForceSSL line in the config file provided below…

Install phpMyAdmin from EPEL repository

Uninstall current PMA:
yum erase phpMyAdmin

Set up EPEL repo:

Find the latest epel-release at http://download.fedoraproject.org/pub/epel/6/x86_64/
rpm -Uvh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

On Amazon Linux, epel-release is already loaded. Edit /etc/yum.repos.d/epel.repo to enabled=1

Edit /etc/yum.repos.d/epel.repo to only include necessary software packages:
includepkgs=phpMyAdmin php-php-gettext

I have removed RPMforge repo due to some recent problems, but if you still need rpm forge repo:
exclude=phpMyAdmin php-php-gettext

Install PMA:
yum install phpMyAdmin

Apache

Edit /etc/httpd/conf.d/phpMyAdmin.conf
  • Allow all incoming hosts for a web hosting server, or only allow hosts you require: localhost, your local workstation, etc.)
  • For a hosting server with open access, you should have a login failure daemon to block offending IP addresses with multiple failed HTTP logins. ConfigServer Firewall works well, and has modules for cPanel and Webmin. OSSEC is another reactive IPTables firewall with Apache login failure IP blacklisting.
  • For Amazon EC2, a good practice is to create a discrete database server, and only allow access to it inside a VPC security group, or from specific IP addresses enabled in that security group. That way, you can safely Allow from ALL hosts, because the VPC firewall will prevent other access. Install phpMyAdmin on a separate web server, and restrict access to PMA’s directory (see below).
# phpMyAdmin - Web based MySQL browser written in php
# 
# Allows only localhost by default
#
# But allowing phpMyAdmin to anyone other than localhost should be considered
# dangerous unless properly secured by SSL

Alias /phpMyAdmin /usr/share/phpMyAdmin
Alias /phpmyadmin /usr/share/phpMyAdmin

<Directory /usr/share/phpMyAdmin/>
   <IfModule mod_authz_core.c>
     # Apache 2.4
     <RequireAny>
       Require ip 127.0.0.1
       Require ip ::1
     </RequireAny>
   </IfModule>
   <IfModule !mod_authz_core.c>
     # Apache 2.2
     Order Deny,Allow
     # comment out Allow from All and add your own static IPs here for security 
     # Allow from All
     Allow from 123.456.7.89
     Allow from 12.345.67.89
     Allow from 127.0.0.1
     Allow from ::1
   </IfModule>
</Directory>

<Directory /usr/share/phpMyAdmin/setup/>
   <IfModule mod_authz_core.c>
     # Apache 2.4
     <RequireAny>
       Require ip 127.0.0.1
       Require ip ::1
     </RequireAny>
   </IfModule>
   <IfModule !mod_authz_core.c>
     # Apache 2.2
     Order Deny,Allow
     Deny from All
     Allow from 127.0.0.1
     Allow from ::1
   </IfModule>
</Directory>

# These directories do not require access over HTTP - taken from the original
# phpMyAdmin upstream tarball
#
<Directory /usr/share/phpMyAdmin/libraries/>
    Order Deny,Allow
    Deny from All
    Allow from None
</Directory>

<Directory /usr/share/phpMyAdmin/setup/lib/>
    Order Deny,Allow
    Deny from All
    Allow from None
</Directory>

<Directory /usr/share/phpMyAdmin/setup/frames/>
    Order Deny,Allow
    Deny from All
    Allow from None
</Directory>

# This configuration prevents mod_security at phpMyAdmin directories from
# filtering SQL etc.  This may break your mod_security implementation.
#
#<IfModule mod_security.c>
#    <Directory /usr/share/phpMyAdmin/>
#        SecRuleInheritance Off
#    </Directory>
#</IfModule>

NginX

Edit nginx.conf for the hostname’s server_name website

location /phpMyAdmin {
               root /usr/share/;
               index index.php;
               location ~ ^/phpMyAdmin/(.+\.php)$ {
                       try_files $uri =404;
                       root /usr/share/;
                       fastcgi_pass localhost:9002;
                       fastcgi_param HTTPS on;
                       fastcgi_index index.php;
                       fastcgi_param SCRIPT_FILENAME /usr/share$fastcgi_script_name;
                       include /etc/nginx/fastcgi_params;
                       fastcgi_buffer_size 128k;
                       fastcgi_buffers 256 4k;
                       fastcgi_busy_buffers_size 256k;
                       fastcgi_temp_file_write_size 256k;
                       fastcgi_intercept_errors on;
               }
               location ~* ^/phpMyAdmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt)) {
                       root /usr/share/;
               }
        }

        location /phpmyadmin {
               rewrite ^/* /phpMyAdmin last;
        }

Edit /etc/phpMyAdmin/config.inc.php

The config below shows some common config options. Important ones are ForceSSL and auth_type. For a production server, SSL should be ON and auth_type http is better; http auth uses MySQL user/pass combinations to restrict access to user-specific databases.

<?php
/* Servers configuration */
$i = 0;

/* Server: MySQL Server [1] */
$i++;
$cfg['Servers'][$i]['verbose'] = 'MySQL Server';
$cfg['Servers'][$i]['host'] = '122.34.567.89';
$cfg['Servers'][$i]['port'] = '3306';
$cfg['Servers'][$i]['socket'] = '';
$cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['extension'] = 'mysqli';
$cfg['Servers'][$i]['auth_type'] = 'http';
$cfg['Servers'][$i]['user'] = 'pma';
$cfg['Servers'][$i]['password'] = '';
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['controluser'] = 'pma';
$cfg['Servers'][$i]['controlpass'] = 'pmapassword';
$cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
$cfg['Servers'][$i]['relation'] = 'pma_relation';
$cfg['Servers'][$i]['userconfig'] = 'pma_userconfig';
$cfg['Servers'][$i]['table_info'] = 'pma_table_info';
$cfg['Servers'][$i]['column_info'] = 'pma_column_info';
$cfg['Servers'][$i]['history'] = 'pma_history';
$cfg['Servers'][$i]['recent'] = 'pma_recent';
$cfg['Servers'][$i]['table_uiprefs'] = 'pma_table_uiprefs';
$cfg['Servers'][$i]['tracking'] = 'pma_tracking';
$cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
$cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
$cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';

/* End of servers configuration */

$cfg['UploadDir'] = '/tmp';
$cfg['SaveDir'] = '/tmp';
/* only if your host supports SSL */
$cfg['ForceSSL'] = true;
$cfg['DefaultLang'] = 'en';
$cfg['ServerDefault'] = 1;
?>

Create the phpmyadmin database for advanced functionality

Look in phpMyAdmin folder /usr/share/phpMyAdmin/examples for create_tables.sql

ssh to server as root user
mysql
-- or if it asks for password --
mysql -u your-mysql-superuser -pyour-superuser-password
mysql > source /usr/share/phpMyAdmin/examples/create_tables.sql

Log in to PMA

Now you can log into PMA with your mysql root user credentials. https://yourhost.tld/phpmyadmin

  • Create a mysql user, pma, with the password you added to the config file, with no default permissions, on localhost
  • Give pma user all permissions on phpmyadmin database, on localhost

Now you have a secure PMA which will work for all mysql users on your host. Version 3.5+ now has Status Monitoring and Advisor. Used in conjunction with a performance tuning script like MySQL Tuning Primer, it will help you fine-tune your MySQL server to your requirements and your environment.

phpMyAdmin MySQL server dashboard

Incoming search terms for the article:

The post Install phpMyAdmin with SSL on CentOS, Amazon Linux, RedHat (Apache or NginX) appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/consulting/install-phpmyadmin-with-ssl-on-centos-amazon-linux-redhat/feed/ 0 Chris Gilligan Chris Gilligan
SSL and CloudFront CDN Support for WebFonts via .htaccess http://chrisgilligan.com/wordpress/add-cloudfront-cdn-support-for-webfonts-via-htaccess/ http://chrisgilligan.com/wordpress/add-cloudfront-cdn-support-for-webfonts-via-htaccess/#comments Wed, 05 Sep 2012 21:33:27 +0000 Chris Gilligan http://chrisgilligan.com/?p=1005 I recently upgraded my WordPress theme to WooThemes Canvas 5.x, and I found that some of the icons were not rendering, but were showing a letter or integer instead. I dug into the code and found that these icons are now delivered via @font-face webfonts. Meanwhile, I’m working on a client’s e-commerce site with Google [...]

The post SSL and CloudFront CDN Support for WebFonts via .htaccess appeared first on Chris Gilligan » new media.

]]>

I recently upgraded my WordPress theme to WooThemes Canvas 5.x, and I found that some of the icons were not rendering, but were showing a letter or integer instead. I dug into the code and found that these icons are now delivered via @font-face webfonts.

Meanwhile, I’m working on a client’s e-commerce site with Google WebFonts and a custom webfont to display the Rupee symbol (Indian currency).

Though the fonts were uploading properly to the CloudFront CDN, and were properly referenced in the minified CSS on the CDN, they were not rendering in Firefox or IE, and the SSL pages on the client’s site were throwing security warnings.

At first I thought this might be a W3 Total Cache issue, because upgrading to the latest development release had solved some other CSS issues. However, it turned out to be a browser security issue.

Evidently this is a security measure to prevent cross-site attacks, but you can allow this via Apache mod_headers, to allow your specific CloudFront (or other) domain, specified in the .htaccess file.

Some sites have suggested using “*” wildcards to allow all domains… but obviously this is a security issue: with this Header, you are granting JavaScript clients basic access to your resources. I recommend you only allow access to the specific CDN domains you require. Do this with a comma separated list, in double quotes.

# BEGIN CDN Cross-Site for Webfonts
<IfModule mod_mime.c>
        AddType font/ttf .ttf
        AddType font/eot .eot
        AddType font/opentype .otf
        AddType font/x-woff .woff
</IfModule>
<FilesMatch "\.(svg|ttf|otf|eot|woff)$">
    <IfModule mod_headers.c>
        Header set Access-Control-Allow-Origin "fonts.googleapis.com,yourdistro69.cloudfront.net"
    </IfModule>
</FilesMatch>
# END CDN Cross-Site for Webfonts

Yay! Delicious webfonts, even on SSL pages via CDN!

Incoming search terms for the article:

The post SSL and CloudFront CDN Support for WebFonts via .htaccess appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/wordpress/add-cloudfront-cdn-support-for-webfonts-via-htaccess/feed/ 0 Chris Gilligan
Varnish VCL and Config for WordPress with W3 Total Cache http://chrisgilligan.com/consulting/varnish-vcl-and-config-for-wordpress-with-w3-total-cache/ http://chrisgilligan.com/consulting/varnish-vcl-and-config-for-wordpress-with-w3-total-cache/#comments Wed, 15 Aug 2012 00:23:11 +0000 Chris Gilligan http://chrisgilligan.com/?p=867 I have been working on a Varnish front-end for Apache, to be used with WordPress sites. I described the architecture in Load Balancing Virtualmin WordPress Hosting Server with Varnish on AWS. I now have a configuration that seems to work for all WordPress features, including logged-out commenting. This configuration also works well with W3 Total [...]

The post Varnish VCL and Config for WordPress with W3 Total Cache appeared first on Chris Gilligan » new media.

]]>

I have been working on a Varnish front-end for Apache, to be used with WordPress sites. I described the architecture in Load Balancing Virtualmin WordPress Hosting Server with Varnish on AWS. I now have a configuration that seems to work for all WordPress features, including logged-out commenting. This configuration also works well with W3 Total Cache.

This configuration is for Varnish on a separate server, but should also work on a single server with appropriate changes to the port and backend IP settings.

Varnish Config (/etc/sysconfig/varnish)

# Configuration file for varnish
#
# /etc/init.d/varnish expects the variable $DAEMON_OPTS to be set from this
# shell script fragment.
#
#
# Maximum number of open files (for ulimit -n)
NFILES=131072
#
# Locked shared memory (for ulimit -l)
# Default log size is 82MB + header
MEMLOCK=82000
#
# Maximum size of corefile (for ulimit -c). Default in Fedora is 0
# DAEMON_COREFILE_LIMIT="unlimited"
#
# Set this to 1 to make init script reload try to switch vcl without restart.
# To make this work, you need to set the following variables
# explicit: VARNISH_VCL_CONF, VARNISH_ADMIN_LISTEN_ADDRESS,
# VARNISH_ADMIN_LISTEN_PORT, VARNISH_SECRET_FILE
RELOAD_VCL=1
#
## Advanced configuration
#
# # Main configuration file.
VARNISH_VCL_CONF=/etc/varnish/wordpress-varnish3.vcl
#
# # Default address and port to bind to
# # Blank address means all IPv4 and IPv6 interfaces, otherwise specify
# # a host name, an IPv4 dotted quad, or an IPv6 address in brackets.
# VARNISH_LISTEN_ADDRESS=
VARNISH_LISTEN_PORT=80
#
# # Telnet admin interface listen address and port
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
#
# # Shared secret file for admin interface
VARNISH_SECRET_FILE=/etc/varnish/secret
#
# # The minimum number of worker threads to start
VARNISH_MIN_THREADS=1
#
# # The Maximum number of worker threads to start
VARNISH_MAX_THREADS=1000
#
# # Idle timeout for worker threads
VARNISH_THREAD_TIMEOUT=120
#
# # Cache file location if using file cache
#VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
#
# # Cache size: in bytes, optionally using k / M / G / T suffix,
# # or in percentage of available disk space using the % suffix.
VARNISH_STORAGE_SIZE=3G
#
# # Backend storage specification
# malloc runs from RAM, file from file
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}"
#VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"
#
# # Default TTL used when the backend does not specify one
VARNISH_TTL=120
#
# # DAEMON_OPTS is used by the init script. If you add or remove options,
# # be sure you update this section, too.
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
-f ${VARNISH_VCL_CONF} \
-T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
-t ${VARNISH_TTL} \
-w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
-u varnish -g varnish \
-S ${VARNISH_SECRET_FILE} \
-s ${VARNISH_STORAGE}"
#

Varnish VCL (/etc/varnish/wordpress-varnish3.vcl)

backend origin {
.host = "10.11.12.13";
.port = "80";
.connect_timeout = 60s;
.first_byte_timeout = 60s;
.between_bytes_timeout = 60s;
}
#
sub vcl_recv {
# only using one backend
set req.backend = origin;
#
# set standard proxied ip header for getting original remote address
set req.http.X-Forwarded-For = client.ip;
#
# logged in users must always pass
if( req.url ~ "^/wp-(login|admin)" || req.http.Cookie ~ "wordpress_logged_in_" ){
return (pass);
}
# accept purges from w3tc and varnish http purge
if (req.request == "PURGE") {
return (lookup);
}
#
# don't cache search results
if( req.url ~ "\?s=" ){
return (pass);
}
#
# always pass through posted requests and those with basic auth
if ( req.request == "POST" || req.http.Authorization ) {
return (pass);
}
#
# else ok to fetch a cached page
unset req.http.Cookie;
return (lookup);
}
#
# accept purges from w3tc and varnish http purge
sub vcl_hit {
if (req.request == "PURGE") { purge; }
return (deliver);
}
#
# accept purges from w3tc and varnish http purge
sub vcl_miss {
if (req.request == "PURGE") { purge; }
return (fetch);
}
#
sub vcl_fetch {
#
# remove some headers we never want to see
unset beresp.http.Server;
unset beresp.http.X-Powered-By;
#
# only allow cookies to be set if we're in admin area - i.e. commenters stay logged out
if( beresp.http.Set-Cookie && req.url !~ "^/wp-(login|admin)" ){
unset beresp.http.Set-Cookie;
}
#
# don't cache response to posted requests or those with basic auth
if ( req.request == "POST" || req.http.Authorization ) {
return (hit_for_pass);
}
#
# only cache status ok
if ( beresp.status != 200 ) {
return (hit_for_pass);
}
#
# don't cache search results
if( req.url ~ "\?s=" ){
return (hit_for_pass);
}
#
# else ok to cache the response
set beresp.ttl = 24h;
return (deliver);
}
#
sub vcl_deliver {
# add debugging headers, so we can see what's cached
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
}
else {
set resp.http.X-Cache = "MISS";
}
# remove some headers added by varnish
unset resp.http.Via;
unset resp.http.X-Varnish;
}
#
sub vcl_hash {
hash_data( req.url );
# altering hash so subdomains are ignored.
# don't do this if you actually run different sites on different subdomains
if ( req.http.host ) {
hash_data( regsub( req.http.host, "^([^\.]+\.)+([a-z]+)$", "\1\2" ) );
} else {
hash_data( server.ip );
}
# ensure separate cache for mobile clients (WPTouch workaround)
if( req.http.User-Agent ~ "(iPod|iPhone|incognito|webmate|dream|CUPCAKE|WebOS|blackberry9\d\d\d)" ){
hash_data("touch");
}
return (hash);
}

Incoming search terms for the article:

The post Varnish VCL and Config for WordPress with W3 Total Cache appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/consulting/varnish-vcl-and-config-for-wordpress-with-w3-total-cache/feed/ 17 Chris Gilligan
Automated WordPress Installation Via Custom Virtualmin Script Installer http://chrisgilligan.com/wordpress/automated-wordpress-installation-via-custom-virtualmin-script-installer/ http://chrisgilligan.com/wordpress/automated-wordpress-installation-via-custom-virtualmin-script-installer/#comments Thu, 17 May 2012 20:42:10 +0000 Chris Gilligan http://chrisgilligan.com/?p=832 WordPress (or any other script) can automatically install during Virtualmin > Create Virtual Server. Virtualmin automatically creates users, MySQL database, and installs WordPress in the public_html root, then gives a link to the WordPress Installer. You can even create a custom installer with a set of plugins and themes. To create a custom script installer [...]

The post Automated WordPress Installation Via Custom Virtualmin Script Installer appeared first on Chris Gilligan » new media.

]]>

WordPress (or any other script) can automatically install during Virtualmin > Create Virtual Server.

Virtualmin automatically creates users, MySQL database, and installs WordPress in the public_html root, then gives a link to the WordPress Installer. You can even create a custom installer with a set of plugins and themes.

To create a custom script installer with plugins and themes:

  • Download http://wordpress.org/latest.zip & unzip
    •   add some themes and plugins folders in /wp-content/
    •   zip as copy-of-latest.zip
  • Create a copy of /usr/libexec/webmin/virtual-server/scripts/wordpress.pl
    •   edit copy-of-wordpress.pl to give a new name: script_wordpress_desc “Custom WordPress”
    •   link to your zip: ‘url’ => “file://localhost/path/to/copy-of-latest.zip”,
  • Put copy-of-wordpress.pl in /usr/libexec/webmin/virtual-server/scripts/
  • Virtualmin > System Settings > Server Templates > Default Settings > Default Script Installers
    • Custom WordPress

If you want to have the script archive download from a protected web directory, that is also possible:

'url' => "http://repo.server.com/file-ver.zip", 'nocache' => 1, 'user' => "username", 'pass' => "password"

Incoming search terms for the article:

The post Automated WordPress Installation Via Custom Virtualmin Script Installer appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/wordpress/automated-wordpress-installation-via-custom-virtualmin-script-installer/feed/ 22 Chris Gilligan
Punk Rock Music Community http://chrisgilligan.com/portfolio/punk-rock-music-community/ http://chrisgilligan.com/portfolio/punk-rock-music-community/#comments Tue, 10 Apr 2012 14:20:41 +0000 Chris Gilligan http://chrisgilligan.com/?p=771 Punktastic.com is a community powered site that covers punk music in Britain, but is poised to go worldwide. They cover punk and hardcore shows and festivals, and provide album reviews, video interviews and more. With a growing audience and user base, Punktastic needed a more reliable and robust web server, so they made the move [...]

The post Punk Rock Music Community appeared first on Chris Gilligan » new media.

]]>

Punktastic.com is a community powered site that covers punk music in Britain, but is poised to go worldwide. They cover punk and hardcore shows and festivals, and provide album reviews, video interviews and more.

With a growing audience and user base, Punktastic needed a more reliable and robust web server, so they made the move to a dedicated CentOS 5 series web server with 3GB RAM. While this is a relatively low-end box, it has plenty of horsepower for a single WordPress site and phpBB3 forum.

Punktastic.com web site

For this GIG, I soloed on…

  • transferring the site from a WAMP development server to the live LAMP server
  • configuring an active firewall to block the baddies
  • installing monitoring and administration tools
  • tuning Apache and MySQL for high traffic
  • integrating APC PHP Cache to speed up web pages and provide better concurrency

Loud Fast Rules.

Incoming search terms for the article:

The post Punk Rock Music Community appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/portfolio/punk-rock-music-community/feed/ 0 Chris Gilligan
WordPress with W3 Total Cache on Nginx with APC (Virtualmin) http://chrisgilligan.com/wordpress/wordpress-with-w3-total-cache-on-nginx-with-apc-virtualmin/ http://chrisgilligan.com/wordpress/wordpress-with-w3-total-cache-on-nginx-with-apc-virtualmin/#comments Mon, 12 Mar 2012 15:25:34 +0000 Chris Gilligan http://chrisgilligan.com/?p=672 Virtualmin now includes support for Nginx web server. I deploy many WordPress sites with W3 Total Cache and APC Alternative PHP Cache, so I was very interested to see how Nginx performance compares to Apache for WordPress, and whether APC and W3TC would play nice with Nginx. I prefer to install WordPress in public_html/wordpress for [...]

The post WordPress with W3 Total Cache on Nginx with APC (Virtualmin) appeared first on Chris Gilligan » new media.

]]>

Virtualmin now includes support for Nginx web server. I deploy many WordPress sites with W3 Total Cache and APC Alternative PHP Cache, so I was very interested to see how Nginx performance compares to Apache for WordPress, and whether APC and W3TC would play nice with Nginx.

I prefer to install WordPress in public_html/wordpress for ease of development, compatibility with other scripts, etc. Accordingly, the suggested configuration is for WordPress installed in a subfolder. This info also assumes you have successfully installed APC Cache on your server, and set up a clean install of Virtualmin with Nginx as the Alternative Web Server. Switching from Apache to Nginx later is difficult, as is transferring apache virtual server backups. Best to start fresh and build from scratch.

Testing the Virtualmin Nginx Modules

Virtualmin does not support Nginx and Apache together, and there is no easy migration from Apache to Nginx, so it is suggested to begin with a fresh install of Virtualmin with no existing accounts.

If you would like to experiment with Nginx, and you already have a Virtualmin Pro license, you are permitted to set up Virtualmin on a second server for testing and migration. I run the extra Virtualmin Pro system on a VPS that I use for backups, slave DNS, and testing.

The Nginx modules are also available for the free Virtualmin GPL version. To install the modules, go to Virtualmin Package Updates (wbm-virtualmin-nginx and wbm-virtualmin-nginx-ssl).

WordPress Plugins for this Setup

  • W3 Total Cache — adds opcode cache, minification, browser cache and page cache capabilities to WordPress… and it works with Nginx. Works best with Alternative PHP Cache to provide opcode and database cache. Setting APC for all caches works easiest with Nginx… setting to file caching introduces URL rewrite errors… See W3TC Settings information below for instructions on how to implement File caching.
  • nginx Compatibility — makes WordPress more compatible with Nginx, allows use of permalinks without /index.php/

Configuring Nginx for WordPress pretty URL Permalinks

The nginx Compatibility plugin has support for pretty URLs, but you must configure Nginx to use them. You should add or edit the following URL path locations to nginx.conf for your virtual domain… either by direct edit, or via URL Path Locations in Virtualmin > Services > Configure Nginx Website (see attached image…). This config is for WP installed in /public_html/wordpress, so you will need to omit or change /wordpress if WP is installed in a different directory.

location ^~ /files/ {
rewrite /files/(.+) /wordpress/wp-includes/ms-files.php?file=$1 last;
}
location @wordpress {
fastcgi_pass localhost:9000;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_NAME /index.php;
}
location ~ \.php$ {
try_files $uri @wordpress;
fastcgi_index index.php;
fastcgi_pass localhost:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
location ^~ /blogs.dir/ {
internal;
root /home/username/public_html/wordpress/wp-content;
}

Adding APC Support for a Virtual Domain

Because Virtualmin’s implementation of Nginx uses php-fastcgi, each virtual domain will load its own php.ini file, so you can add the APC directives to that file (/home/domainname/etc/php5/php.ini)

Add the following to php.ini (adjust to your requirements)

[APC]
extension = apc.so
apc.enabled = 1
apc.shm_segments = 1
apc.shm_size = 12M
apc.optimization = 0
apc.num_files_hint = 256
apc.user_entries_hint = 1024
apc.ttl = 0
apc.user_ttl = 0
apc.gc_ttl = 600
apc.cache_by_default = 0
apc.filters = "-/home/username/public_html/scripts-not-to-cache/.*,-/home/username/public_html/apc/.*"
apc.slam_defense = 0
apc.use_request_time = 1
apc.mmap_file_mask = /tmp/apc.XXXXXX
apc.file_update_protection = 2
apc.enable_cli = 0
apc.max_file_size = 2M
apc.stat = 1
apc.write_lock = 1
apc.report_autofilter = 0
apc.include_once_override = 0
apc.rfc1867 = 0
apc.rfc1867_prefix = "upload_"
apc.rfc1867_name = "APC_UPLOAD_PROGRESS"
apc.rfc1867_freq = 0
apc.localcache = 1
apc.localcache.size = 256
apc.coredump_unmap = 0
apc.stat_ctime = 0

Reloading PHP-fastcgi processes after changes to php.ini

You will need to reload the php-fastcgi processes if you change php.ini. Luckily, Virtualmin creates service scripts for each virtual domain, which you can use to reload PHP.

The service scripts are located in /etc/rc.d/init.d.
php-fcgi-domain1-com
php-fcgi-domain2-com

Script allows stop, start, restart
e.g.
service php-fcgi-domain2-com restart

The service may also be restarted via Webmin > Services > Bootup and Shutdown

Memory Considerations

PHP FCGId will launch the number of sub-processes you specify in Virtualmin > Server Configuration > Website Options, so beware of the memory overhead if you specify a large number, because each will consume the amount of memory specified in  apc.shm_size. You may need to experiment with this value, depending on your server’s memory and potential traffic.

Virtualmin explains: “When PHP scripts for this domain as run via FCGId, the number of PHP processes set in this field will be kept running at all times to serve requests. You can increase this from the default of 4 to improve PHP script latency, or decrease it to reduce memory use. Setting it to None will cause PHP processes to be launched only as needed on demand, and to be cleaned up after some period of inactivity.”

Nginx support on Virtualmin is working well, and it is reported that the lead developer, Jamie Cameron, is working on a php-fpm implementation, which should increase performance and lessen memory requirements. Let’s hope this rumor is true!

W3 Total Cache Settings

Provided you use the nginx.conf settings described above, using APC cache for all W3TC categories of cache/minify/etc. will work with no additional changes to the conf file. However, if you wish to choose Disk cache for page and/or minify cache, URL rewriting will not work properly on Virtualmin. You must include the location {…} directives in the nginx.conf created in your public_html root by W3TC, and add them after the location {…} directives described above.

You can do this either by copying the directives and pasting into /etc/nginx/nginx.conf, or (better) by using an include statement:

[...]
location ^~ /blogs.dir/ {
internal;
root /home/username/public_html/wordpress/wp-content;
}
include /home/username/public_html/nginx.conf;
[...]

However, remember you will need to restart NginX web server after making changes to the W3TC configuration, because these will over-write the local nginx.conf file.

Conclusion

Sorry, no benchmarks. See my example/test site here.

Nginx support on Virtualmin is working well, though I don’t see much improvement over Apache in terms of initial index page load, or time to first byte on the WordPress test site I created.  Cached and subsequent pages load very quickly, so it looks like there is an overall improvement. All WordPress functions seem to work well: comments, image uploads, etc. are no problem. I am interested to see if there is improvement with other scripts, especially phpbb3.

Incoming search terms for the article:

The post WordPress with W3 Total Cache on Nginx with APC (Virtualmin) appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/wordpress/wordpress-with-w3-total-cache-on-nginx-with-apc-virtualmin/feed/ 18 Chris Gilligan
Farm to Table Restaurant Website http://chrisgilligan.com/portfolio/farm-to-table-restaurant-website/ http://chrisgilligan.com/portfolio/farm-to-table-restaurant-website/#comments Sat, 24 Dec 2011 21:08:25 +0000 Chris Gilligan http://chrisgilligan.com/?p=566 Joe Naturals Farm Store Cafe is a “farm to table” cafe and natural foods store located in historic Leiper’s Fork, near Franklin, TN. The owners contacted me through a fellow farmer with whom I have worked on several web projects. The website project was languishing, after being abandoned by a designer who lacked knowledge of [...]

The post Farm to Table Restaurant Website appeared first on Chris Gilligan » new media.

]]>

Joe Naturals Farm Store Cafe is a “farm to table” cafe and natural foods store located in historic Leiper’s Fork, near Franklin, TN. The owners contacted me through a fellow farmer with whom I have worked on several web projects.

Joe Natural's website

The website project was languishing, after being abandoned by a designer who lacked knowledge of WordPress template coding. The site needed better integration with WordPress, including text-based navigation and replacement of scanned layout comps, which were being used for page display & navigation.

I reworked the stylesheet to replace image-based text with CSS styled text, integrated WordPress navigation, and then added a few WordPress plugins.

I also worked with the farmers’ social media person to add social media links and integrate Facebook “Likes” and cross publishing from the WordPress blog to the Facebook Fan Page.

Incoming search terms for the article:

The post Farm to Table Restaurant Website appeared first on Chris Gilligan » new media.

]]>
http://chrisgilligan.com/portfolio/farm-to-table-restaurant-website/feed/ 0 Chris Gilligan