Thursday, October 30, 2014

Node.js "Error: too many parameters at queryparse"

If you've been using Express' body-parser and attempting to process large data submissions (aka forms with more than 1000 elements or 1000 sub-elements, you may have run into the following error:

Error: too many parameters
    at queryparse (/project/node_modules/body-parser/lib/types/urlencoded.js:120:17)
    at parse (/project/node_modules/body-parser/lib/types/urlencoded.js:64:9)

This is due to the fact the urlencode defaults to 1000 parameters by default. If you have a large form or just an abnormally large JSON submission, you'll need to increase this limit by doing the following:

var bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({
        extended: false,
    parameterLimit: 10000,
    limit: 1024 * 1024 * 10
}));
app.use(bodyParser.json({
        extended: false,
    parameterLimit: 10000,
    limit: 1024 * 1024 * 10
}));


This will allow you to provide up to 10,000 parameters (increase as needed) and 10 MB of data (also adjustable).

Tuesday, October 14, 2014

How to Disable SSLv3 on AWS Elastic Load Balancers

In a blog post today, Google announced that a vulnerability in SSLv3 had been found that could allow attackers to intercept data that had previously been assumed to be secured. Luckily, a very small portion of the web (IE6 users on Windows XP) still use SSLv3, so it can safely, for the most part, be disabled to mitigate the risk from this issue.

http://googleonlinesecurity.blogspot.com/2014/10/this-poodle-bites-exploiting-ssl-30.html

UPDATE 10/15: As Andrew and Julio point out in the comments below, AWS has since updated their default cipher security policies. Replace steps 5 and 6.

To modify the ciphers on AWS ELBs, follow the following steps:

1) Log into the AWS console and click on "Load Balancers."
2) Find the load balancer that handles your site's traffic (you shouldn't need to worry about internal VPC LBs, etc.)
3) Click the "Listeners" tab
4) Find the HTTPS/443 listener and click "Edit" under the cipher column
5) Change the option to "Custom"
6) Uncheck the SSLv3 option
5) Change the policy to "ELBSecurityPolicy-2014-10" which disables SSLv3 for you.
6) Save.

This should be sufficient to mitigate this risk with the information that is currently known.

Redirect HTTP to HTTPS Behind AWS Elastic Load Balancer - Node.js and Apache

Apache

When you enable HTTPS for your website, you should enforce that HTTPS is being used by automatically redirecting users who access your site over HTTP to the HTTPS version. When using Apache, this is done via a host redirect entry with mod_rewrite:

<VirtualHost *:80>

# Beginning declarations here

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI}

# Rest of entry here...
</VirtualHost>

Apache Behind an ELB

However, in the Amazon Web Services world, most applications utilize the load balancer as an SSL-termination point. This means that the traffic is decrypted at the load balancer and all traffic being sent to the final destination instances is actually sent over HTTP (from a security standpoint, this is generally an accepted practice as the load balancer and instance are in the same network, and thus secure from eavesdropping). If you used the rule above, your users would wind up in an endless redirect loop.

To fix this, the "RewriteCond" needs to be changed to:

RewriteCond %{HTTP:X-Forwarded-Proto} !https

This rule will check to see which protocol the user was using before he or she hit the load balancer - which is what HTTPS redirection should use.

Node.js

When using Node.js, the same thing can be accomplished by inserting some middleware that listens for all traffic and redirects the non-HTTPS requests to their HTTPS equivalents.

var myApp = express () ,
myServer = http.createServer(myApp);

myApp.use(function redirectHTTP(req, res, next) {
  if (!req.secure) {
    return res.redirect('https://' + req.headers.host + req.url);
  }
  next();
});

Node.js Behind an ELB

Just as in our first Apache example, this works great until it is placed behind a load balancer. If you're using a load balancer, you need to make some modifications. Instead of "req.secure", do the following:

if (req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'].toLowerCase() === 'http') {
return res.redirect('https://' + req.headers.host + req.url);
}

Using these techniques, you should be able to enforce the new HTTPS version of your website. Remember - if you serve both an HTTPS and an HTTP version of your site, the HTTPS is rendered pointless unless you also redirect the HTTP requests.

Sunday, September 28, 2014

Bringing Mint's Interface into the 21st Century

If you use the online financial planning app, Mint, you've probably wondered what decade it was designed in. Just browsing through the site presents a clutter of different styles, an incredible use of box-shadow, and more border-radius than should be permissible by law. It's time for an upgrade (Intuit, if you're reading this, please hire some designers!).

Now I am by no means a graphic designer. In fact, I'd argue that my understanding of colors, patterns, and other design principles is on par with most grade schoolers. However, in about three hours of work on a lazy Sunday afternoon, I was able to turn this:


Into this:



As you can see, not a ton has changed; my goal was not to completely design a new site. However, as I'll show in the next screenshots, I've tried to unify the interface a bit, get rid of the overzealous use of shadows, gradients, and rounded borders, as well as add a bit of white space to spread things out a bit. I also tried to flatten some of the progress bars to get rid of the outdated "Web 2.0" gloss effect.

For the transactions page, I've simply added white space to make things seem less cramped (pardon the gray box, I had to box out some of my own personal info):


Within the budgets page, I've gotten rid of all sorts of rounded glossy boxes and replaced them with whitespace and flat progress bars.


Not much has changed in the "Trends" page, but I did get rid of a lot of the cramped feeling and spread the content out a bit.


Finally, the "Investments" page was given more whitespace as well and a wider width for content:


Here is a list of other little changes I've made:


  • Increased the overall width to 1200px from the original 900. This gives us more room to work and added whitespace.
  • Changed all the popups to have sharp edges rather than rounded corners
  • Replaced the shadows with clean lines
  • Made all the menus square with straight hover edges rather than rounded corners
  • All the progress bars are flat now
  • Removed the bulky hover effects on the budgets page
  • Removed ads on the homepage and investments page
  • Removed the annoying Norton Certified seal in the footer
  • Widened the transactions and their details
  • A bunch of other small touches
I did not:
  • Get rid of the "Ways to Save" page. This is how Mint makes money and I don't want to destroy their revenue stream.
  • Modify the "Goals" page. It is so poorly designed right now with background images and borders and popups I wasn't brave enough to touch it.
  • Change the chart or graph styles for trends
Now, for a disclaimer: I do not guarantee that this will work for everyone's account. I tried to test it as much as possible, but there are a lot of hidden pieces of functionality within Mint that I may have missed. Finally, this code is all written out below. It's entirely CSS, so you can decide which parts you want to use. This code will not steal your account info or do anything else malicious.

I also don't make any guarantees about supporting this in the future. Mint could change their site tomorrow and break all of this. Fortunately, it's very easy to undo (just erase the user styles).

So how can you get this?


It's all CSS! I use an extension called "StyleBot" for Chrome (download link), but Firefox has similar extensions as well. Simply copy and paste the following styles into the extension by opening the extension while on mint.com, then clicking "Edit CSS" at the bottom, and then pasting it in.


#brokerage-link {
    display: none;
}

#filters-wrapper {
    border-radius: 0px;
    box-shadow: none;
    margin-left: 40px;
}

#filters-wrapper.closed {
    border: 0px;
    border-bottom: 0px solid lightgray;
    margin-bottom: 25px;
}

#filters-wrapper.open {
    border: 0px;
    border-bottom: 1px solid lightgray;
}

#graphSelectionNav {
    width: 100%;
}

#graphSelectionNav ul li {
}

#ira-link {
    display: none;
}

#menu-category li.active, #menu-categoryTypeFilter li.active {
    border-radius: 0px;
}

#menu-category ul {
    border-radius: 0px;
}

#menu-compare ul {
    border-radius: 0px;
}

#menu-compare ul li.active {
    border-radius: 0px;
}

#module-accounts ul li h3 {
    background: none;
    border: none;
    border-bottom: 1px solid #DFDFDF;
    box-shadow: none;
}

#module-advice {
    display: none;
}

#module-budget table div.bar {
    background: none;
    background-color: #ededed;
    border-radius: 0px;
    height: 20px;
}

#module-budget table div.bar span {
    background: none;
    background-color: #3FAF3B;
    border: none;
    border-radius: 0px;
    font-weight: normal;
    padding-bottom: 4px;
    padding-top: 3px;
    text-shadow: none;
}

#module-budget table tr {
    height: 40px;
}

#module-budget table tr.overbudget div.bar span {
    background: none;
    background-color: #ED5D51;
}

#module-budget table tr.warning div.bar span {
    background: none;
    background-color: #EFBE2E;
}

#module-budget thead th.bar {
    width: 70%;
}

#module-budget.module table thead th.budget {
    padding: 0 11px;
}

#module-goals table th.name {
    width: 40%;
}

#module-goals table th.next-step {
    width: 40%;
}

#module-investments table tr {
    height: 40px;
}

#module-offers {
    display: none;
}

#month-line {
    display: none;
    height: 500px;
}

#overview-left-column {
    margin: 0px;
    width: 302px;
}

#planning_group {
    padding: 5px 0 10px 250px;
}

#pop-categories, #pop-rules, #pop-tags, #pop-hotspot-overlay {
    border-radius: 0px;
}

#rollover-link {
    display: none;
}

#search-filters small {
    border-radius: 0px;
}

#transaction-ad {
    display: none;
}

#trust {
    display: none;
}

#txn-column-accounts {
    min-width: 240px;
    width: 20%;
}

#txn-detail {
    background: none;
}

#txnEdit #actions {
    left: 698px;
}

#txnEdit-basic tbody td.date, #txnEdit-basic-multi tbody td.date {
    max-width: 12%;
}

#txnEdit-form fieldset, #txnEdit-form-multi fieldset {
    width: 726px;
}

#txnEdit-form, #txnEdit-form-multi {
    top: 48px;
    width: 765px;
}

#txnEdit-mt-account label.txn-edit-labels.checknumber {
    width: 90px;
}

#txnEdit-note, #txnEdit-note-multi {
    width: 588px;
}

#txnEdit-split-icon {
    margin-left: -9px;
}

#txnEdit-toggle.noattachcol, #txnEdit-toggleP {
    left: 350px;
    width: 765px;
}

#ways_to_invest_control {
    display: none;
}

#wrapper {
    width: 1200px;
}

.custom-filters-action-wrapper {
    left: -130px;
    top: 10px;
}

.filters-top-line {
    display: none;
}

.module .module-menu .menu-wrapper {
    border-radius: 0px;
}

.module-reminders-content .timeline .chart .date {
    width: 25px;
}

.module-reminders-content .timeline .chart .dates {
    width: 810px;
}

.module-reminders-content .timeline .chart .divider-line {
    width: 830px;
}

.module-reminders-content .timeline .chart .graph {
    background: none;
    background-color: #3FAF3B;
    border-radius: 0px;
}

.nav #activeMenuItemGreenBar {
    width: 238px;
}

.overview {
    width: 100%;
}

.overviewPage .column-left {
    box-shadow: none;
}

.overviewPage .column-main {
    width: 95%;
}

.overviewPage .module .module-menu {
    left: 823px;
}

.pageContents > div {
    width: 100%;
}

.planningPage #incomeEE-list ul.ee_header {
    padding-bottom: 0px;
}

.planningPage #timeline {
    padding: 15px 0 5px 260px;
}

.planningPage .allocate_to_goals a.button {
    background: none;
    border-radius: 0px;
}

.planningPage .budget-summary {
    border-radius: 0px;
    box-shadow: none;
}

.planningPage .budget_group {
    padding: 5px 0 10px 260px;
}

.planningPage .right_col {
    width: 310px;
}

.planningPage div.ee_list ul.ee_header {
    padding-bottom: 0px;
}

.planningPage div.leftcolumn {
    width: 240px;
}

.planningPage ul.planning_items .edit-details {
    padding: 8px 0 0 1px;
}

.planningPage ul.planning_items .over-under-budget-text {
    margin: 6px 4px 0 0;
}

.planningPage ul.planning_items div.status span.progress_bar {
    background: none ;
    background-color: #3FAF3B ;
    border: none ;
    border-radius: 0px ;
    font-weight: normal ;
    padding-bottom: 4px ;
    padding-top: 3px ;
    text-shadow: none ;
}

.planningPage ul.planning_items div.status span.total_bar {
    background: none ;
    background-color: #ededed ;
    border-radius: 0px ;
    height: 20px ;
}

.planningPage ul.planning_items li {
    height: 70px;
}

.planningPage ul.planning_items li div.status span.progress_bar.full, .planningPage #incomeBudget-list-body li.overbudget div.status span.progress_bar.full {
    background-color: #ED5D51 ;
}

.planningPage ul.planning_items li.hover {
    background: none;
    padding: 7px 35px 0px 12px;
}

.planningPage ul.planning_items li.warning div.status span.progress_bar {
    background-color: #EFBE2E ;
}

.productPageContent {
    width: 100%;
}

.txnEdit-btn {
    border-radius: 2px;
}

a#txnEdit-category_picker.noattachcol, a#txnEdit-category_picker-multi.noattachcol, a#txnEdit-category_picker, a#txnEdit-category_picker-multi {
    left: 600px;
    top: 15px;
}

a.find_all span.find_all_wrapper {
    background: none;
}

a.find_all, a.split {
    background: none;
    border: 1px solid #D8D8D8;
    margin-bottom: 5px;
    margin-top: 10px;
    padding-bottom: 10px;
    padding-left: 10px;
}

a.find_all:hover, a.split:hover {
    background-color: #D8D8D8;
}

body div#graph-container {
    padding-left: 80px;
}

div#column-accounts.column {
    width: 100%;
}

div#column-accounts.column ul li div.result-number {
    border-radius: 0px;
    left: 227px;
}

div#column-accounts.column ul li li {
    padding: 10px 10px;
}

div#column-transactions div.controls#controls-top a.button {
    border-radius: 0px;
}

div#flash-container {
    margin-left: 10px;
    padding-left: 90px;
}

div#main {
    width: 1200px;
}

div#main.trends-main {
    width: 100%;
}

div.column#column-accounts ul li, div.column#column-accounts ul li li {
}

div.column#column-content {
    width: 900px;
}

div.column#column-transactions {
    width: 98%;
}

div.inspector {
    border-radius: 0px;
}

div.left-nav {
    width: 240px;
}

div.pop.newpop {
    border-radius: 0px;
}

div.premium-filters {
    width: 850px;
}

div.right-column {
    box-shadow: none;
    width: 945px;
}

div.txn-edit-group {
    width: 598px;
}

table.account {
    width: 100%;
}

table.transactions {
    width: 766px
;
}

table.transactions tbody td {
    padding: 12px 4px 12px 3px;
}

td.column.accounts {
    border-right: 1px solid #E1E6DD;
    box-shadow: none;
    width: 240px;
}

td.column.details {
    background: none;
}

td.column.details-budgets {
    background: none;
}

td.column.transactions {
    width: 70%;
}

ul.horizontal-bar li {
    background: none;
    background-color: #3FAF3B;
    border-radius: 0px;
}

ul.horizontal-bar li.debt {
    background: none;
    background-color: #ED5D51;
    border-radius: 0px;
}

ul.vertical-bar li a {
    background: none;
    background-color: #3FAF3B;
    border-radius: 0px;
}

ul.vertical-bar li a.debt {
    background: none;
    background-color: #ED5D51;
    border-radius: 0px;
}

Tuesday, September 9, 2014

OS X Fuse Input/output error Fix

If you're using OS X Fuse to mount a remote disk on a Mac device, you will occasioanlly receive the following error:

ssh: Input/output error

This has happened to me several times when allowing my Mac to sleep with the disk still mounted. When this happens, any operation on the disk seems to fail. I've managed to fix it with the following steps:

1) Shut down the remote server / disconnect it to prevent corruption (optional)
2) Attempt to unmount the disk cleanly from your Mac: umount /tmp/ssh (where /tmp/ssh is the location of the mounted disk)
3) If that fails, find the process ID of the SSHFS process: ps aux | grep sshfs
4) Kill that process: sudo kill <PID>
5) Remove the old folder: rmdir /tmp/ssh  (rm -rf /tmp/ssh if it fails)
6) Remount everything again:

 mkdir /tmp/ssh
 sshfs user@host:/ /tmp/ssh -ocache=no -onolocalcaches -ovolname=ssh

 This should fix the Input/Output errors and allow you to cleanly remount.

Sunday, September 7, 2014

Monitoring a Scaling Infrastructure at Aviary

I recently wrote a blog post for Aviary, where I am employed as a Server Engineer. It details how we setup monitoring to automatically scale with our infrastructure and covers tools like Nagios, StatsD, CollectD, and Graphite:

Monday, September 1, 2014

Why Security is Important for Developer Operations

After working at my current gig for about a year now (internship plus full-time), I wanted to take some time to outline my experiences transitioning to more of a developer operations role from my existing security background. The transition has been fairly straight-forward, as much of the work I do currently touches security in some aspect. However, the biggest point I've noticed is how much security considerations are involved when developing tools for a traditional developer operations requirement.

While I am a big proponent of at least some security training for everyone in a technical role, it remains to be seen just how much is "enough." Obviously, as the realm of technical fields continues to expand at its current pace, having every member of a technical team fully trained in the security of the applications they are developing is impossible. Yet it is also imperative that they at least understand the risks associated with these applications; doing so should be a requirement of a good developer.

As a "Developer Operations Engineer" (a role whose title is still rather undefined and unstandardized), I generally focus on several categories of projects: infrastructure development, monitoring and incident response, and deployments and the application lifecycle. While this is a highly compressed view of my role, each of these has a dizzying array of security concerns. While some companies offload much of these concerns to a security team, smaller companies need to remain vigilant of their impact.

In most modern startup environments, infrastructure development typically refers to everyone's favorite buzzword: the cloud. Amazon Web Services, Microsoft Azure, Google's App Engine, the list goes on. The security concerns associated with the cloud are not the focus of this post, but I do want to highlight places where security is especially important. Almost every one of these services has security enhancements that are not used as often as they should be. For example, AWS's Virtual Private Cloud is not only free, but can also greatly improve security when used properly. Yet quickly starting instances from EC2 still requires less hassle and so remains the more common choice. Another example is the use of security groups. AWS's security groups are infinitely customizable, yet simply opening a port to the world (0.0.0.0) is a tempting simpler option. While hosted infrastructure providers like Amazon and Google abstract a lot of security work away from the customer, good security practices still require active participation.

Monitoring and incident response is perhaps the area in which a lack of security can have the biggest impact. While many "DevOps" engineers view monitoring in terms of system performance, monitoring must also cover system security. Disk space, CPU utilization, and memory usage are all important indicators of a healthy system, but so too are login attempts, changes to file permissions, and unauthorized outgoing network connections. A good monitoring platform must also monitor for security events that could signal an intrusion or potential breach of security. In the same light, the response to a security event should also contain a viable plan for mitigating the same risk in the future.

Finally, the deployment of applications is a critical component of the security of an infrastructure. Because the main goal of deployments from a developer operation's standpoint is automation, any security bugs introduced once tend to be replicated. For this reason, it is imperative that the deployment process and any actors in it (Jenkins, AWS S3, etc.) are fully secured and audited often. Vulnerabilities that are present here can expand exponentially when deployments are pushed.

The role of developer operations or server engineering is rapidly changing and expanding. While it does, it is important, if not necessary, to include security in the expansion and ensure that those building a company's most critical technological parts are also trained in protecting them.

How to Enable Two-Factor Authentication for iCloud (And Why You Should)

This past weekend was not a good one for at least twenty or so celebrities, as well as Apple's iCloud service. According to a number of reports, a large number of female celebrities had personal photos released from their private iCloud accounts after a hacker was able to gain access to them. While we don't know all the details yet, it is likely that a combination of social engineering, weak passwords, and publicly available security question answers merged to allow the attacker access. In light of these events, it's a great idea to review the security of your account to avoid any accidental exposure of your private information.

Two-Factor Authentication

One of the easiest ways to secure your iCloud account is to use two-factor authentication. With this feature enabled, an attacker will not be able to log into your account unless he or she also has access to your phone. To set it up, first head over to your Apple ID security page here: https://appleid.apple.com/account/manage/security

Make sure that two-factor authentication is setup. If it is not, begin the process by clicking the link. You will be asked for several pieces of information, including a phone number and answers to your security questions. You will also be asked to provide the code texted to you as well as given a very long reset code which should be treated as if it is your password (in other words, don't write it down some place that will be lost, and don't save it on your desktop).

Once you enter all this information, two-factor authentication will be setup. Congratulations, you've just made your iCloud account ten thousand times more secure!

iCloud Two-Factor Authentication


Security Questions

For most accounts, the only thing that stands between a hacker resetting your password and maintaining your security is a strong set of password security questions. What good are these questions if an attacker can find the answers on Facebook? This is a good time to check your questions and make sure they are hacker-proof. Here are some tips:

  1. Security questions should be simple for you to remember but complex for anyone else
  2. The answers to your questions should not be something an attacker can find on Facebook, Twitter, your blog, or on your abandoned MySpace page.
  3. Be very specific in your answers. If the question asks where you met your significant other (a common question), don't use "Los Angeles" as the answer. Instead, provide a specific street name, a friend's name who introduced you, etc.).
  4. Don't choose simple questions. "What is your favorite color?" and "Where were you born?" are ridiculously simple questions for attackers. Choose more complex questions that ask for information not found in your online profiles.
Securing iCloud is relatively simple and doing so can go a long way in securing your personal photos and documents. Unfortunately, leaks like the one that happened this weekend are going to occur. But by following these guidelines, at least you can make the hacker's efforts less rewarding, and in most cases, stop them entirely.

Friday, July 11, 2014

Changing Releases and Tags on GitHub to a Different Commit

I recently came across a situation where I accidentally created a release on GitHub, pointed at a specific tag which was tagged to a commit. I fixed the changes, committed, but wanted the release to point to the new commit instead of the old one with the error. Unfortunately, GitHub doesn't have a way to do this online, but it can be done through a combination of command line git and the GitHub website.

First, make sure your repo is up-to-date with "git fetch." This will pull down the tag you made, including the wrong one.

git fetch

Next, delete the old tag by running:

git tag -d [tag-name-here]

So, for example, I did:

git tag -d v1.1.0

Next, commit and push your latest changes including the change you want the release to point to:

git add folder/file
git commit -m "message here"
git push -u origin master

Finally, push your tag changes as well:

git push origin :[tag-name-here]

If your tag has the same name as a branch, run:

git push origin :refs/tags/[tag-name-here]

Now, open GitHub in your browser and navigate to the "releases" page. Your release should now be listed as a "Draft" because its tag has been deleted.

You can now discard this release and create a new one with the same name pointed at the most recent commit.

Wednesday, July 2, 2014

Use google-passport Authentication in Node.js Projects without Google+

NPM passport-google and 400 Error "OpenID auth request contains an unregistered domain"

If you've been using the Node.js module "passport-google" for authentication in your projects you will now notice that new projects are receiving an error stating:

"400 That's an error. OpenID auth request contains an unregistered domain."

This issue is due to the fact that Google has deprecated their OpenID API for new domains beginning in May 2014. Old projects which had previously been used should not have an issue (although you should upgrade at some point), but new projects will not be allowed to authenticate.

There are two fixes for this:

1) Convert your application to using the new, Google+ sign-in. This will require users to have a Google+ profile and approve your application to access it. The passport-google-plus module located on GitHub can do just that: https://github.com/sqrrrl/passport-google-plus

2) Convert your application to use OAuth2.0 signin. Your users will not need to have a Google+ profile and this new method is the closest match to the old. The passport-google-oauth module can help with this: https://github.com/jaredhanson/passport-google-oauth

If you choose the second option, be sure to only provide the userinfo scope (and not the google.plus scope):

passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.email'] })

One additional note is that you will now be required to register your application at https://console.developers.google.com and create a client ID and secret (which are used in the passport module).

Friday, June 27, 2014

JavaScript Print Date in YYYY-MM-DD HH:mm:ss Format With Leading Zeros

Printing out a JavaScript date with leading zeros is a bit more complex than it should be.

var now = new Date();
var dateToPrint = now.getFullYear() + '-' + ('0' + (now.getMonth() + 1)).slice(-2) + '-' + ('0' + now.getDate()).slice(-2) + ' ' + ('0' + (now.getHours() + 1)).slice(-2) + ':' + ('0' + now.getMinutes()).slice(-2) + ':' + ('0' + now.getSeconds()).slice(-2);

The .slice(-2) method allows us to add a leading "0" to every date (including those already containing two characters) and then slice out the last two.

For example, if it is 1PM, the time is 13 (getHours() + 1), but then written as 013, then sliced to 13.

Tuesday, June 24, 2014

Sending CollectD Metrics to Graphite

There are no shortage of tutorials on setting up collectd as an agent on a machine. However, I have found little help in the way of describing how to setup a centralized collectd collection server that aggregates statistics from multiple clients and sends them to Graphite. This post will help you do just that. It focuses on Ubuntu, but the instructions are universally applicable.

First, let's setup the central collectd server. This could be on the same machine as your Graphite server, but on large production environments, it is not recommended.

The Ubuntu collectd repositories do not contain the necessary write_graphite plugin, so you must download and install collectd manually. Download the source from the website at: http://collectd.org/download.shtml

Next, run:

tar jxf collectd-version.tar.bz2
cd collectd-version
./configure
make all install

Once collectd is installed, modify the /opt/etc/collectd.conf file to contain the following:

Hostname "hostname"
FQDNLookup true
BaseDir "/opt/collectd/var/lib/collectd"
PIDFile "/opt/collectd/var/run/collectd.pid"
PluginDir "/opt/collectd/lib/collectd"
TypesDB "/opt/collectd/share/collectd/types.db"
Interval 10


LoadPlugin network
<Plugin network>
Listen "*" "12345"
</Plugin>

LoadPlugin interface
<Plugin interface>
Interface "eth0"
</Plugin>

LoadPlugin write_graphite
<Plugin write_graphite>
<Node "graphing">
Host "localhost"
Port "2003"
Protocol "tcp"
LogSendErrors true
Prefix "collectd."
StoreRates true
AlwaysAppendDS false
EscapeCharacter "_"
</Node>
</Plugin>

Make adjustments for your network as needed.

Now, we are going install the collectd agent on the client and then tell it to send the metrics to the collectd server (not Graphite).

The clients do not need the write_graphite plugin and can use the older version of Collectd that ships with the repositories. On each client, run:

sudo apt-get install collectd collectd-utils

Then, cd into /etc/collectd. Backup the collectd.conf file as collectd.conf.bkp or similar and then create a new collectd.conf file. In it, enable the plugins you want and also add the following:

Hostname "hostname"
FQDNLookup true
BaseDir "/var/lib/collectd"
PIDFile "/var/run/collectd.pid"
PluginDir "/usr/lib/collectd"
TypesDB "/usr/share/collectd/types.db"
Interval 10
#Timeout 5
ReadThreads 5

LoadPlugin network
<Plugin network>
Server "collectd.domain.com" "12345"
</Plugin>

LoadPlugin cpu
LoadPlugin load
LoadPlugin disk
LoadPlugin memory
LoadPlugin processes

Include "/etc/collectd/filters.conf"
Include "/etc/collectd/thresholds.conf"

Be sure to configure the network plugin with your collectd server information.

Now, if you log into Graphite, you should see that your clients are sending all of their statistics to the collectd server which is then sending them to Graphite.