From 4acb424c7feb03e55184c084f7699fd9be9e02b8 Mon Sep 17 00:00:00 2001 From: Stephen Sawchuk Date: Sat, 17 Oct 2015 11:06:30 -0400 Subject: [PATCH] compute: create default firewall rules --- lib/compute/zone.js | 65 +++++++++++++++++++++++ test/compute/zone.js | 122 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) diff --git a/lib/compute/zone.js b/lib/compute/zone.js index 76f60ad7d67..4d02e01a18f 100644 --- a/lib/compute/zone.js +++ b/lib/compute/zone.js @@ -20,6 +20,7 @@ 'use strict'; +var async = require('async'); var extend = require('extend'); var format = require('string-format-obj'); var gceImages = require('gce-images'); @@ -275,6 +276,11 @@ Zone.prototype.createVM = function(name, config, callback) { } if (body.http || body.https) { + // We will add tags to the created instance (http-server and/or + // https-server), and create the appropriate firewall rules to allow + // connections on the necessary ports to these tags. + var createFirewallMethods = []; + body.networkInterfaces[0].accessConfigs = [ { type: 'ONE_TO_ONE_NAT' @@ -286,6 +292,9 @@ Zone.prototype.createVM = function(name, config, callback) { if (body.http) { delete body.http; + + createFirewallMethods.push(this.createHttpServerFirewall_.bind(this)); + if (body.tags.items.indexOf('http-server') === -1) { body.tags.items.push('http-server'); } @@ -293,10 +302,25 @@ Zone.prototype.createVM = function(name, config, callback) { if (body.https) { delete body.https; + + createFirewallMethods.push(this.createHttpsServerFirewall_.bind(this)); + if (body.tags.items.indexOf('https-server') === -1) { body.tags.items.push('https-server'); } } + + // We have to make sure the firewall rules exist to allow HTTP/S traffic. + async.parallel(createFirewallMethods, function(err) { + if (err) { + callback(err); + return; + } + + self.createVM(name, body, callback); + }); + + return; } if (body.os) { @@ -317,6 +341,7 @@ Zone.prototype.createVM = function(name, config, callback) { self.createVM(name, body, callback); }); + return; } @@ -714,6 +739,46 @@ Zone.prototype.vm = function(name) { return new VM(this, name); }; +/** + * This method attempts to create a firewall rule to allow tcp:80 connections. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - If the firewall couldn't be created and it + * didn't already exist. + */ +Zone.prototype.createHttpServerFirewall_ = function(callback) { + this.compute.createFirewall('default-allow-http', { + protocols: { + tcp: [80] + }, + ranges: ['0.0.0.0/0'], + tags: ['http-server'] + }, function(err) { + // If it already exists, we're all good. + callback(err && err.code !== 409 ? err : null); + }); +}; + +/** + * This method attempts to create a firewall rule to allow tcp:443 connections. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - If the firewall couldn't be created and it + * didn't already exist. + */ +Zone.prototype.createHttpsServerFirewall_ = function(callback) { + this.compute.createFirewall('default-allow-https', { + protocols: { + tcp: [443] + }, + ranges: ['0.0.0.0/0'], + tags: ['https-server'] + }, function(err) { + // If it already exists, we're all good. + callback(err && err.code !== 409 ? err : null); + }); +}; + /** * Make a new request object from the provided arguments and wrap the callback * to intercept non-successful responses. diff --git a/test/compute/zone.js b/test/compute/zone.js index 466d9c2b802..e7fc250189d 100644 --- a/test/compute/zone.js +++ b/test/compute/zone.js @@ -341,6 +341,20 @@ describe('Zone', function() { http: true }; + beforeEach(function() { + zone.createHttpServerFirewall_ = function(callback) { + callback(); + }; + }); + + it('should create a firewall rule', function(done) { + zone.createHttpServerFirewall_ = function() { + done(); + }; + + zone.createVM(NAME, CONFIG, assert.ifError); + }); + it('should add a network interface accessConfig', function(done) { zone.makeReq_ = function(method, path, query, body) { assert.deepEqual(body.networkInterfaces[0].accessConfigs[0], { @@ -394,6 +408,20 @@ describe('Zone', function() { https: true }; + beforeEach(function() { + zone.createHttpsServerFirewall_ = function(callback) { + callback(); + }; + }); + + it('should create a firewall rule', function(done) { + zone.createHttpsServerFirewall_ = function() { + done(); + }; + + zone.createVM(NAME, CONFIG, assert.ifError); + }); + it('should add a network interface accessConfig', function(done) { zone.makeReq_ = function(method, path, query, body) { assert.deepEqual(body.networkInterfaces[0].accessConfigs[0], { @@ -981,6 +1009,100 @@ describe('Zone', function() { }); }); + describe('createHttpServerFirewall_', function() { + it('should create a firewall rule', function(done) { + zone.compute.createFirewall = function(name, config) { + assert.strictEqual(name, 'default-allow-http'); + assert.deepEqual(config, { + protocols: { + tcp: [80] + }, + ranges: ['0.0.0.0/0'], + tags: ['http-server'] + }); + + done(); + }; + + zone.createHttpServerFirewall_(assert.ifError); + }); + + it('should execute callback with error & API response', function(done) { + var error = new Error('Error.'); + + zone.compute.createFirewall = function(name, config, callback) { + callback(error); + }; + + zone.createHttpServerFirewall_(function(err) { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should not execute callback with error if 409', function(done) { + var error = new Error('Error.'); + error.code = 409; + + var apiResponse = {}; + + zone.compute.createFirewall = function(name, config, callback) { + callback(error, null, apiResponse); + }; + + zone.createHttpServerFirewall_(function(err) { + assert.strictEqual(err, null); + done(); + }); + }); + }); + + describe('createHttpsServerFirewall_', function() { + it('should create a firewall rule', function(done) { + zone.compute.createFirewall = function(name, config) { + assert.strictEqual(name, 'default-allow-https'); + assert.deepEqual(config, { + protocols: { + tcp: [443] + }, + ranges: ['0.0.0.0/0'], + tags: ['https-server'] + }); + + done(); + }; + + zone.createHttpsServerFirewall_(assert.ifError); + }); + + it('should execute callback with error & API response', function(done) { + var error = new Error('Error.'); + + zone.compute.createFirewall = function(name, config, callback) { + callback(error); + }; + + zone.createHttpsServerFirewall_(function(err) { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should not execute callback with error if 409', function(done) { + var error = new Error('Error.'); + error.code = 409; + + zone.compute.createFirewall = function(name, config, callback) { + callback(error); + }; + + zone.createHttpsServerFirewall_(function(err) { + assert.strictEqual(err, null); + done(); + }); + }); + }); + describe('makeReq_', function() { it('should make the correct request to Compute', function(done) { var expectedPathPrefix = '/zones/' + zone.name;