Skip to main content

Working with VMware vShield REST API in perl

Here is an overview of how to use perl code to work with VMware's vShield API.

vShield App and Edge are two security products offered by VMware. vShield Edge has a broad range of functionality such as firewall, VPN, load balancing, NAT, and DHCP. vShield App is a NIC-level firewall for virtual machines.

We'll focus today on how to use the API to programatically make firewall rule changes. Here are some of the things you can do with the API:
  • List the current firewall ruleset
  • Add new rules
  • Get a list of past firewall revisions
  • Revert back to a previous ruleset revision
vShield API documentation is available here.

Before we get into the API itself, let's look at what the firewall ruleset looks like. It's formatted as XML:




1.1.1.1/32
10.1.1.1/32
datacenter-2
ANY





1023
High
1


ANY<
Application type="UNICAST">LDAP over SSL
636
TCP
ALLOW
deny




1020
Low
3


ANY
IMAP
143
TCP<
Action>ALLOW
false





Here are some notes about the XML configuration:
  • The API works mainly with container objects. A container can range from a datacenter or cluster all the way down to a port group or IP address.
  • Every container object must be listed in the <containerassociation> section.
  • Container objects have instance IDs. The instance ID is also referred to as the managed object ID (MOID)
  • Every firewall rule has its own ID as well as precedence and position fields.

If you want to edit a firewall ruleset, you must specify which ruleset you want. Every object has its own ruleset. So you could edit a ruleset at the datacenter level, cluster level, etc. all the way down to the port group level.

For simplicity let's work with the ruleset at the datacenter level because this will cover all VMs in that datacenter.

The first thing to do is get the object ID for the datacenter. If you don't already know this then you must look it up. There are two places you can find it:
  1. Use the Managed Object Browser in vCenter Server, located at https://<vcenter IP>/mob, e.g., https://10.1.1.1/mob
  2. Query your vCenter Server with the vSphere SDK

Either way you must have access to vCenter Server.

Here is some perl code to query vCenter. The code assumes that $dc_name is set to the name of your datacenter. You can find this name in the vSphere client.
use VMware::RunTime;

$ENV{'VI_SERVER'} = $vc_ip;
$ENV{'VI_USERNAME'} = $vc_user;
$ENV{'VI_PASSWORD'} = $vc_pass;

# read/validate options and connect to the server
Opts::add_options(%opts);
Opts::parse();
Opts::validate();
Util::connect();

$view = Vim::find_entity_views(view_type => 'Datacenter');
foreach $datacenter (@$view) {
 if (lc($datacenter->{name}) eq lc($dc_name)) {
  return $datacenter->{mo_ref}->{value};
 }
}

return "Not_found";

Note that this code requires you include the .pm files from the perl SDK. You can find these in lib/VMware/share/VMware/ in the perl SDK tarball.

Once you have the object ID for your datacenter, you can use it to create the vShield URL that you will need to access the datacenter's firewall ruleset:
$url = "https://" . $vsm_ip . "/api/1.0/zones/" . $moid . "/firewall/rules";
Note that this URL is to access the ruleset in vShield App. If you want to access the ruleset in vShield Edge instead, simply change the "zones" in the URL to "network". So the resulting vShield Edge URL looks like this:
$url = "https://" . $vsm_ip . "/api/1.0/network/" . $moid . "/firewall/rules";
Now that you have the URL, use it to get the ruleset with a simple HTTP GET using Basic Authentication:
$ua = LWP::UserAgent->new;
$request = HTTP::Request->new(GET =>$url);
$request->authorization_basic($vsm_user, $vsm_pass);
$response = $ua->request($request);

$response now contains the XML ruleset. Copy it to a variable such as $ruleset and use your favorite XML library to work directly with each rule. I found that using XML::LibXML provides the best routines for both parsing and editing the XML.

This code iterates through each rule, loading the source address and protocol into variables.
my $parser = new XML::LibXML;
my $tree = $parser->parse_string($ruleset);
my $root = $tree->documentElement();

foreach my $rule_ref ($root->findnodes('RuleSet/Rule')) {
 $rule_src = $rule_ref->findvalue('Source/@ref');
 $rule_prot = $rule_ref->findvalue('Protocol');
}

Note that the source address is accessible as an attribute named "ref" in the source tag. XPath syntax uses '@' to access XML attributes.

The vShield API has certain restrictions when it comes to adding firewall rules. You can't just add a rule to the existing ruleset. Every time you update the ruleset with new rules, you replace all of the old rules.

The proper way to add a rule is to load the existing rules into memory as an XML tree, add the new rules to the tree, then post the updated tree back as the new ruleset.

This sample code illustrates how to add a new rule. Note that only a few of the fields are included here but every field in the rule is required, with the exception of Notes. You will get an error if you leave out a required field.
my $rule_ref = XML::LibXML::Element->new("Rule");

my $id_el = XML::LibXML::Element->new("ID");
$id_el->appendText("0");

my $src_el = XML::LibXML::Element->new("Source");
$src_el->setAttribute("ref", $src_ip);
$src_el->setAttribute("exclude", "false");

$rule_ref->addChild($id_el);
$rule_ref->addChild($src_el);

my $rule_root = $root->findnodes('RuleSet')->get_node(1);
$rule_root->addChild($rule_ref);

Here are some notes:
  • To add a new rule, specify an ID of 0. When vShield adds the new rule to the ruleset, it will automatically generate a new ID.
  • The Position field is required but you can set it to any value. I set it to a default of 50. vShield Manager rewrites this field every time you move rules around in the vShield GUI.
After you add the rule to the XML tree, you must also add a new container object for the IP addresses referenced by the rule:
my $contain_root = $root->findnodes('ContainerAssociation')->get_node(1);

my $contain_el = XML::LibXML::Element->new("Container");
$contain_el->setAttribute("id", $ip_addr);
my $ip_addr_el = XML::LibXML::Element->new("IPAddress");
$ip_addr_el->appendText($ip_addr);
$contain_el->addChild($ip_addr_el);
$contain_root->addChild($contain_el);

When you're done updating the XML tree, post the complete ruleset:
$ua = LWP::UserAgent->new;
$request = HTTP::Request->new(POST=>$self->{url});
$request->authorization_basic($self->{vsm_user}, $self->{vsm_pass});
$request->content_type('application/xml');
$request->content($root->toString());
$response = $ua->request($request);

Comments

Popular posts from this blog

Building a Hadoop cluster

I've recently had to build a Hadoop cluster for a class in information retrieval . My final project involved building a Hadoop cluster. Here are some of my notes on configuring the nodes in the cluster. These links on configuring a single node cluster and multi node cluster were the most helpful. I downloaded the latest Hadoop distribution then moved it into /hadoop. I had problems with this latest distribution (v.21) so I used v.20 instead. Here are the configuration files I changed: core-site.xml: fs.default.name hdfs://master:9000 hadoop.tmp.dir /hadoop/tmp A base for other temporary directories. hadoop-env.sh: # Variables required by Mahout export HADOOP_HOME=/hadoop export HADOOP_CONF_DIR=/hadoop/conf export MAHOUT_HOME=/Users/rpark/mahout PATH=/hadoop/bin:/Users/rpark/mahout/bin:$PATH # The java implementation to use. Required. export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home hdfs-site...

The #1 Mistake Made by Product People at All Levels

In my 20+ year career in product management for B2B enterprise companies, I have seen product managers at every level make a certain kind of mistake. It is so easy to make that I occasionally make it myself when I'm not careful. What is this mistake? It is to spend too much time on tasks and deliverables that are not core to the product function, which is to to determine and define products to be built. If you keep falling into this trap then ultimately you can't be effective at your job and your company won't sell compelling products. Your primary job as a product manager is to figure out what your market and customers need and make sure it gets built. If you aren't careful, you can spend all of your time performing tasks in support of products such as sales enablement, customer success, product marketing, and pre-sales. How Do You Know This Is Happening? It is easy to fall into this trap for many reasons. Here are a few scenarios that come to mind: Product Marketing ...

Connecting to SQL Server from OS X perl

I've been spending my coding time in the offhours working on Perl instead of Ruby. My coding time in general has been very limited, which is part of the reason for the length of time between updates. :) My latest project is to pull data out of a Microsoft SQL Server database for analysis. I'm using perl for various reasons: I need a crossplatform environment, and I need certain libraries that only work on perl. Some of the target users for my code run on Windows. I know that Ruby runs on Windows but it's not the platform of choice for Ruby developers. The vast majority seem to develop either on OS X or Linux. So Ruby on Windows isn't at the maturity that ActiveState perl is on Windows. In fact, I don't even run native perl anymore on my MacBook Pro. I've switched over to ActiveState perl because I don't need to compile anything every time I want to install new CPAN libraries. And because it's ActiveState, I'm that much more confident it will w...