// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.network.resource; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; // for prettyFormat() import javax.xml.transform.stream.StreamSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; // http client handling import org.apache.http.client.ResponseHandler; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.MaintainCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.ReadyAnswer; import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupExternalFirewallCommand; import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.PortForwardingRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.host.Host; import com.cloud.network.rules.FirewallRule; import com.cloud.network.utils.HttpClientWrapper; import com.cloud.resource.ServerResource; import com.cloud.utils.NumbersUtil; import com.cloud.utils.exception.ExecutionException; import com.cloud.utils.net.NetUtils; public class PaloAltoResource implements ServerResource { private String _name; private String _zoneId; private String _ip; private String _username; private String _password; private String _guid; private String _key; private Integer _numRetries; private Integer _timeoutInSeconds; private String _publicZone; private String _privateZone; private String _publicInterface; private String _privateInterface; private String _publicInterfaceType; private String _privateInterfaceType; private String _virtualRouter; private String _threatProfile; private String _logProfile; private String _pingManagementProfile; private static final Logger s_logger = Logger.getLogger(PaloAltoResource.class); private static String s_apiUri = "/api"; private static HttpClient s_httpclient; protected enum PaloAltoMethod { GET, POST; } private enum PaloAltoPrimative { CHECK_IF_EXISTS, ADD, DELETE; } private enum InterfaceType { AGGREGATE("aggregate-ethernet"), ETHERNET("ethernet"); private final String type; private InterfaceType(String type) { this.type = type; } @Override public String toString() { return type; } } private enum Protocol { TCP("tcp"), UDP("udp"), ICMP("icmp"), ALL("all"); private final String protocol; private Protocol(String protocol) { this.protocol = protocol; } @Override public String toString() { return protocol; } } private enum GuestNetworkType { SOURCE_NAT, INTERFACE_NAT; } @Override public Answer executeRequest(Command cmd) { if (cmd instanceof ReadyCommand) { return execute((ReadyCommand)cmd); } else if (cmd instanceof MaintainCommand) { return execute((MaintainCommand)cmd); } else if (cmd instanceof IpAssocCommand) { return execute((IpAssocCommand)cmd); } else if (cmd instanceof SetStaticNatRulesCommand) { return execute((SetStaticNatRulesCommand)cmd); } else if (cmd instanceof SetPortForwardingRulesCommand) { return execute((SetPortForwardingRulesCommand)cmd); } else if (cmd instanceof SetFirewallRulesCommand) { return execute((SetFirewallRulesCommand)cmd); } else if (cmd instanceof ExternalNetworkResourceUsageCommand) { return execute((ExternalNetworkResourceUsageCommand)cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { try { _name = (String)params.get("name"); if (_name == null) { throw new ConfigurationException("Unable to find name"); } _zoneId = (String)params.get("zoneId"); if (_zoneId == null) { throw new ConfigurationException("Unable to find zone"); } _ip = (String)params.get("ip"); if (_ip == null) { throw new ConfigurationException("Unable to find IP"); } _username = (String)params.get("username"); if (_username == null) { throw new ConfigurationException("Unable to find username"); } _password = (String)params.get("password"); if (_password == null) { throw new ConfigurationException("Unable to find password"); } _publicInterface = (String)params.get("publicinterface"); if (_publicInterface == null) { throw new ConfigurationException("Unable to find public interface."); } _privateInterface = (String)params.get("privateinterface"); if (_privateInterface == null) { throw new ConfigurationException("Unable to find private interface."); } _publicZone = (String)params.get("publicnetwork"); if (_publicZone == null) { throw new ConfigurationException("Unable to find public zone"); } _privateZone = (String)params.get("privatenetwork"); if (_privateZone == null) { throw new ConfigurationException("Unable to find private zone"); } _virtualRouter = (String)params.get("pavr"); if (_virtualRouter == null) { throw new ConfigurationException("Unable to find virtual router"); } _threatProfile = (String)params.get("patp"); _logProfile = (String)params.get("palp"); _guid = (String)params.get("guid"); if (_guid == null) { throw new ConfigurationException("Unable to find the guid"); } _numRetries = NumbersUtil.parseInt((String)params.get("numretries"), 1); _timeoutInSeconds = NumbersUtil.parseInt((String)params.get("timeout"), 300); // Open a socket and login if (!refreshPaloAltoConnection()) { throw new ConfigurationException("Unable to open a connection to the Palo Alto."); } // check that the threat profile exists if one was specified if (_threatProfile != null) { try { boolean has_profile = getThreatProfile(_threatProfile); if (!has_profile) { throw new ConfigurationException("The specified threat profile group does not exist."); } } catch (ExecutionException e) { throw new ConfigurationException(e.getMessage()); } } // check that the log profile exists if one was specified if (_logProfile != null) { try { boolean has_profile = getLogProfile(_logProfile); if (!has_profile) { throw new ConfigurationException("The specified log profile does not exist."); } } catch (ExecutionException e) { throw new ConfigurationException(e.getMessage()); } } // get public interface type try { _publicInterfaceType = getInterfaceType(_publicInterface); if (_publicInterfaceType.equals("")) { throw new ConfigurationException("The specified public interface is not configured on the Palo Alto."); } } catch (ExecutionException e) { throw new ConfigurationException(e.getMessage()); } // get private interface type try { _privateInterfaceType = getInterfaceType(_privateInterface); if (_privateInterfaceType.equals("")) { throw new ConfigurationException("The specified private interface is not configured on the Palo Alto."); } } catch (ExecutionException e) { throw new ConfigurationException(e.getMessage()); } _pingManagementProfile = "Ping"; try { ArrayList<IPaloAltoCommand> cmdList = new ArrayList<IPaloAltoCommand>(); managePingProfile(cmdList, PaloAltoPrimative.ADD); boolean status = requestWithCommit(cmdList); } catch (ExecutionException e) { throw new ConfigurationException(e.getMessage()); } return true; } catch (Exception e) { throw new ConfigurationException(e.getMessage()); } } @Override public StartupCommand[] initialize() { StartupExternalFirewallCommand cmd = new StartupExternalFirewallCommand(); cmd.setName(_name); cmd.setDataCenter(_zoneId); cmd.setPod(""); cmd.setPrivateIpAddress(_ip); cmd.setStorageIpAddress(""); cmd.setVersion(PaloAltoResource.class.getPackage().getImplementationVersion()); cmd.setGuid(_guid); return new StartupCommand[] {cmd}; } @Override public Host.Type getType() { return Host.Type.ExternalFirewall; } @Override public String getName() { return _name; } @Override public boolean start() { return true; } @Override public boolean stop() { return true; } @Override public PingCommand getCurrentStatus(final long id) { return new PingCommand(Host.Type.ExternalFirewall, id); } @Override public void disconnected() { // nothing for now... } @Override public IAgentControl getAgentControl() { return null; } @Override public void setAgentControl(IAgentControl agentControl) { return; } /* * Login */ private void openHttpConnection() { s_httpclient = new DefaultHttpClient(); // Allows you to connect via SSL using unverified certs s_httpclient = HttpClientWrapper.wrapClient(s_httpclient); } private boolean refreshPaloAltoConnection() { if (s_httpclient == null) { openHttpConnection(); } try { return login(_username, _password); } catch (ExecutionException e) { s_logger.error("Failed to login due to " + e.getMessage()); return false; } } private boolean login(String username, String password) throws ExecutionException { Map<String, String> params = new HashMap<String, String>(); params.put("type", "keygen"); params.put("user", username); params.put("password", password); String keygenBody; try { keygenBody = request(PaloAltoMethod.GET, params); } catch (ExecutionException e) { return false; } Document keygen_doc = getDocument(keygenBody); XPath xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = xpath.compile("/response[@status='success']/result/key/text()"); _key = (String)expr.evaluate(keygen_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (_key != null) { return true; } return false; } // ENTRY POINTS... private Answer execute(ReadyCommand cmd) { return new ReadyAnswer(cmd); } private Answer execute(MaintainCommand cmd) { return new MaintainAnswer(cmd); } private ExternalNetworkResourceUsageAnswer execute(ExternalNetworkResourceUsageCommand cmd) { return new ExternalNetworkResourceUsageAnswer(cmd); } /* * Guest networks */ private synchronized Answer execute(IpAssocCommand cmd) { refreshPaloAltoConnection(); return execute(cmd, _numRetries); } private Answer execute(IpAssocCommand cmd, int numRetries) { String[] results = new String[cmd.getIpAddresses().length]; int i = 0; try { IpAddressTO ip; if (cmd.getIpAddresses().length != 1) { throw new ExecutionException("Received an invalid number of guest IPs to associate."); } else { ip = cmd.getIpAddresses()[0]; } String sourceNatIpAddress = null; GuestNetworkType type = GuestNetworkType.INTERFACE_NAT; if (ip.isSourceNat()) { type = GuestNetworkType.SOURCE_NAT; if (ip.getPublicIp() == null) { throw new ExecutionException("Source NAT IP address must not be null."); } else { sourceNatIpAddress = ip.getPublicIp(); } } long guestVlanTag = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG)); String guestVlanGateway = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY); String cidr = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR); long cidrSize = NetUtils.cidrToLong(cidr)[1]; String guestVlanSubnet = NetUtils.getCidrSubNet(guestVlanGateway, cidrSize); Long publicVlanTag = null; if (ip.getBroadcastUri() != null) { String parsedVlanTag = parsePublicVlanTag(ip.getBroadcastUri()); if (!parsedVlanTag.equals("untagged")) { try { publicVlanTag = Long.parseLong(parsedVlanTag); } catch (Exception e) { throw new ExecutionException("Could not parse public VLAN tag: " + parsedVlanTag); } } } ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>(); if (ip.isAdd()) { // Implement the guest network for this VLAN implementGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); } else { // Remove the guest network: shutdownGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); } boolean status = requestWithCommit(commandList); results[i++] = ip.getPublicIp() + " - success"; } catch (ExecutionException e) { s_logger.error(e); if (numRetries > 0 && refreshPaloAltoConnection()) { int numRetriesRemaining = numRetries - 1; s_logger.debug("Retrying IPAssocCommand. Number of retries remaining: " + numRetriesRemaining); return execute(cmd, numRetriesRemaining); } else { results[i++] = IpAssocAnswer.errorResult; } } return new IpAssocAnswer(cmd, results); } private void implementGuestNetwork(ArrayList<IPaloAltoCommand> cmdList, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrNumber) throws ExecutionException { privateSubnet = privateSubnet + "/" + privateCidrNumber; managePrivateInterface(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateGateway + "/" + privateCidrNumber); if (type.equals(GuestNetworkType.SOURCE_NAT)) { managePublicInterface(cmdList, PaloAltoPrimative.ADD, publicVlanTag, publicIp + "/32", privateVlanTag); manageSrcNatRule(cmdList, PaloAltoPrimative.ADD, type, publicVlanTag, publicIp + "/32", privateVlanTag, privateGateway + "/" + privateCidrNumber); manageNetworkIsolation(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateSubnet, privateGateway); } String msg = "Implemented guest network with type " + type + ". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway + "/" + privateCidrNumber; msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + publicIp : ""; s_logger.debug(msg); } private void shutdownGuestNetwork(ArrayList<IPaloAltoCommand> cmdList, GuestNetworkType type, Long publicVlanTag, String sourceNatIpAddress, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrSize) throws ExecutionException { privateSubnet = privateSubnet + "/" + privateCidrSize; // remove any orphaned egress rules if they exist... removeOrphanedFirewallRules(cmdList, privateVlanTag); if (type.equals(GuestNetworkType.SOURCE_NAT)) { manageNetworkIsolation(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateSubnet, privateGateway); manageSrcNatRule(cmdList, PaloAltoPrimative.DELETE, type, publicVlanTag, sourceNatIpAddress + "/32", privateVlanTag, privateGateway + "/" + privateCidrSize); managePublicInterface(cmdList, PaloAltoPrimative.DELETE, publicVlanTag, sourceNatIpAddress + "/32", privateVlanTag); } managePrivateInterface(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateGateway + "/" + privateCidrSize); String msg = "Shut down guest network with type " + type + ". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway + "/" + privateCidrSize; msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + sourceNatIpAddress : ""; s_logger.debug(msg); } /* * Firewall rule entry point */ private synchronized Answer execute(SetFirewallRulesCommand cmd) { refreshPaloAltoConnection(); return execute(cmd, _numRetries); } private Answer execute(SetFirewallRulesCommand cmd, int numRetries) { FirewallRuleTO[] rules = cmd.getRules(); try { ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>(); for (FirewallRuleTO rule : rules) { if (!rule.revoked()) { manageFirewallRule(commandList, PaloAltoPrimative.ADD, rule); } else { manageFirewallRule(commandList, PaloAltoPrimative.DELETE, rule); } } boolean status = requestWithCommit(commandList); return new Answer(cmd); } catch (ExecutionException e) { s_logger.error(e); if (numRetries > 0 && refreshPaloAltoConnection()) { int numRetriesRemaining = numRetries - 1; s_logger.debug("Retrying SetFirewallRulesCommand. Number of retries remaining: " + numRetriesRemaining); return execute(cmd, numRetriesRemaining); } else { return new Answer(cmd, e); } } } /* * Static NAT rule entry point */ private synchronized Answer execute(SetStaticNatRulesCommand cmd) { refreshPaloAltoConnection(); return execute(cmd, _numRetries); } private Answer execute(SetStaticNatRulesCommand cmd, int numRetries) { StaticNatRuleTO[] rules = cmd.getRules(); try { ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>(); for (StaticNatRuleTO rule : rules) { if (!rule.revoked()) { manageStcNatRule(commandList, PaloAltoPrimative.ADD, rule); } else { manageStcNatRule(commandList, PaloAltoPrimative.DELETE, rule); } } boolean status = requestWithCommit(commandList); return new Answer(cmd); } catch (ExecutionException e) { s_logger.error(e); if (numRetries > 0 && refreshPaloAltoConnection()) { int numRetriesRemaining = numRetries - 1; s_logger.debug("Retrying SetStaticNatRulesCommand. Number of retries remaining: " + numRetriesRemaining); return execute(cmd, numRetriesRemaining); } else { return new Answer(cmd, e); } } } /* * Destination NAT (Port Forwarding) entry point */ private synchronized Answer execute(SetPortForwardingRulesCommand cmd) { refreshPaloAltoConnection(); return execute(cmd, _numRetries); } private Answer execute(SetPortForwardingRulesCommand cmd, int numRetries) { PortForwardingRuleTO[] rules = cmd.getRules(); try { ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>(); for (PortForwardingRuleTO rule : rules) { if (!rule.revoked()) { manageDstNatRule(commandList, PaloAltoPrimative.ADD, rule); } else { manageDstNatRule(commandList, PaloAltoPrimative.DELETE, rule); } } boolean status = requestWithCommit(commandList); return new Answer(cmd); } catch (ExecutionException e) { s_logger.error(e); if (numRetries > 0 && refreshPaloAltoConnection()) { int numRetriesRemaining = numRetries - 1; s_logger.debug("Retrying SetPortForwardingRulesCommand. Number of retries remaining: " + numRetriesRemaining); return execute(cmd, numRetriesRemaining); } else { return new Answer(cmd, e); } } } // IMPLEMENTATIONS... /* * Private interface implementation */ private String genPrivateInterfaceName(long vlanTag) { return _privateInterface + "." + Long.toString(vlanTag); } public boolean managePrivateInterface(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateGateway) throws ExecutionException { String interfaceName = genPrivateInterfaceName(privateVlanTag); switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/network/interface/" + _privateInterfaceType + "/entry[@name='" + _privateInterface + "']/layer3/units/entry[@name='" + interfaceName + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Private sub-interface exists: " + interfaceName + ", " + result); return result; case ADD: if (managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) { return true; } // add cmds // add sub-interface Map<String, String> a_sub_params = new HashMap<String, String>(); a_sub_params.put("type", "config"); a_sub_params.put("action", "set"); a_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _privateInterfaceType + "/entry[@name='" + _privateInterface + "']/layer3/units/entry[@name='" + interfaceName + "']"); a_sub_params.put("element", "<tag>" + privateVlanTag + "</tag><ip><entry name='" + privateGateway + "'/></ip><interface-management-profile>" + _pingManagementProfile + "</interface-management-profile>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); // add sub-interface to VR... Map<String, String> a_vr_params = new HashMap<String, String>(); a_vr_params.put("type", "config"); a_vr_params.put("action", "set"); a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='" + _virtualRouter + "']/interface"); a_vr_params.put("element", "<member>" + interfaceName + "</member>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params)); // add sub-interface to vsys... Map<String, String> a_vsys_params = new HashMap<String, String>(); a_vsys_params.put("type", "config"); a_vsys_params.put("action", "set"); a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface"); a_vsys_params.put("element", "<member>" + interfaceName + "</member>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params)); // add sub-interface to zone... Map<String, String> a_zone_params = new HashMap<String, String>(); a_zone_params.put("type", "config"); a_zone_params.put("action", "set"); a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='" + _privateZone + "']/network/layer3"); a_zone_params.put("element", "<member>" + interfaceName + "</member>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params)); return true; case DELETE: if (!managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) { return true; } // add cmds to the list // delete sub-interface from zone... Map<String, String> d_zone_params = new HashMap<String, String>(); d_zone_params.put("type", "config"); d_zone_params.put("action", "delete"); d_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='" + _privateZone + "']/network/layer3/member[text()='" + interfaceName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_zone_params)); // delete sub-interface from vsys... Map<String, String> d_vsys_params = new HashMap<String, String>(); d_vsys_params.put("type", "config"); d_vsys_params.put("action", "delete"); d_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface/member[text()='" + interfaceName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vsys_params)); // delete sub-interface from VR... Map<String, String> d_vr_params = new HashMap<String, String>(); d_vr_params.put("type", "config"); d_vr_params.put("action", "delete"); d_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='" + _virtualRouter + "']/interface/member[text()='" + interfaceName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vr_params)); // delete sub-interface... Map<String, String> d_sub_params = new HashMap<String, String>(); d_sub_params.put("type", "config"); d_sub_params.put("action", "delete"); d_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _privateInterfaceType + "/entry[@name='" + _privateInterface + "']/layer3/units/entry[@name='" + interfaceName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } /* * Public Interface implementation */ private String genPublicInterfaceName(Long id) { return _publicInterface + "." + Long.toString(id); } public boolean managePublicInterface(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, Long publicVlanTag, String publicIp, long privateVlanTag) throws ExecutionException { String interfaceName; if (publicVlanTag == null) { interfaceName = genPublicInterfaceName(new Long("9999")); } else { interfaceName = genPublicInterfaceName(publicVlanTag); } switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/network/interface/" + _publicInterfaceType + "/entry[@name='" + _publicInterface + "']/layer3/units/entry[@name='" + interfaceName + "']/ip/entry[@name='" + publicIp + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Public sub-interface & IP exists: " + interfaceName + " : " + publicIp + ", " + result); return result; case ADD: if (managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) { return true; } // add IP to the sub-interface Map<String, String> a_sub_params = new HashMap<String, String>(); a_sub_params.put("type", "config"); a_sub_params.put("action", "set"); a_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _publicInterfaceType + "/entry[@name='" + _publicInterface + "']/layer3/units/entry[@name='" + interfaceName + "']/ip"); a_sub_params.put("element", "<entry name='" + publicIp + "'/>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); // add sub-interface to VR (does nothing if already done)... Map<String, String> a_vr_params = new HashMap<String, String>(); a_vr_params.put("type", "config"); a_vr_params.put("action", "set"); a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='" + _virtualRouter + "']/interface"); a_vr_params.put("element", "<member>" + interfaceName + "</member>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params)); // add sub-interface to vsys (does nothing if already done)... Map<String, String> a_vsys_params = new HashMap<String, String>(); a_vsys_params.put("type", "config"); a_vsys_params.put("action", "set"); a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface"); a_vsys_params.put("element", "<member>" + interfaceName + "</member>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params)); // add sub-interface to zone (does nothing if already done)... Map<String, String> a_zone_params = new HashMap<String, String>(); a_zone_params.put("type", "config"); a_zone_params.put("action", "set"); a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='" + _publicZone + "']/network/layer3"); a_zone_params.put("element", "<member>" + interfaceName + "</member>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params)); return true; case DELETE: if (!managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) { return true; } // delete IP from sub-interface... Map<String, String> d_sub_params = new HashMap<String, String>(); d_sub_params.put("type", "config"); d_sub_params.put("action", "delete"); d_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _publicInterfaceType + "/entry[@name='" + _publicInterface + "']/layer3/units/entry[@name='" + interfaceName + "']/ip/entry[@name='" + publicIp + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } /* * Source NAT rule implementation */ private String genSrcNatRuleName(Long privateVlanTag) { return "src_nat." + Long.toString(privateVlanTag); } public boolean manageSrcNatRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway) throws ExecutionException { String publicInterfaceName; if (publicVlanTag == null) { publicInterfaceName = genPublicInterfaceName(new Long("9999")); } else { publicInterfaceName = genPublicInterfaceName(publicVlanTag); } String srcNatName = genSrcNatRuleName(privateVlanTag); switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + srcNatName + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Source NAT exists: " + srcNatName + ", " + result); return result; case ADD: if (manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) { return true; } String xml = ""; xml += "<from><member>" + _privateZone + "</member></from>"; xml += "<to><member>" + _publicZone + "</member></to>"; xml += "<source><member>" + privateGateway + "</member></source>"; xml += "<destination><member>any</member></destination>"; xml += "<service>any</service>"; xml += "<nat-type>ipv4</nat-type>"; xml += "<to-interface>" + publicInterfaceName + "</to-interface>"; xml += "<source-translation><dynamic-ip-and-port><interface-address>"; xml += "<ip>" + publicIp + "</ip>"; xml += "<interface>" + publicInterfaceName + "</interface>"; xml += "</interface-address></dynamic-ip-and-port></source-translation>"; Map<String, String> a_params = new HashMap<String, String>(); a_params.put("type", "config"); a_params.put("action", "set"); a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + srcNatName + "']"); a_params.put("element", xml); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); return true; case DELETE: if (!manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) { return true; } Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + srcNatName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } /* * Destination NAT rules (Port Forwarding) implementation */ private String genDstNatRuleName(String publicIp, long id) { return "dst_nat." + genIpIdentifier(publicIp) + "_" + Long.toString(id); } public boolean manageDstNatRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, PortForwardingRuleTO rule) throws ExecutionException { String publicIp = rule.getSrcIp(); String dstNatName = genDstNatRuleName(publicIp, rule.getId()); String publicInterfaceName; String publicVlanTag; if (rule.getSrcVlanTag() == null) { publicInterfaceName = genPublicInterfaceName(new Long("9999")); } else { publicVlanTag = parsePublicVlanTag(rule.getSrcVlanTag()); if (publicVlanTag.equals("untagged")) { publicInterfaceName = genPublicInterfaceName(new Long("9999")); } else { publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag)); } } switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + dstNatName + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Destination NAT exists: " + dstNatName + ", " + result); return result; case ADD: if (manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { return true; } // build source service xml String srcService; String protocol = rule.getProtocol(); int[] srcPortRange = rule.getSrcPortRange(); if (srcPortRange != null) { String portRange; if (srcPortRange.length == 1 || srcPortRange[0] == srcPortRange[1]) { portRange = String.valueOf(srcPortRange[0]); } else { portRange = String.valueOf(srcPortRange[0]) + "-" + String.valueOf(srcPortRange[1]); } manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null); srcService = genServiceName(protocol, portRange, null); } else { // no equivalent config in PA, so allow all traffic... srcService = "any"; } // build destination port xml (single port limit in PA) String dstPortXML = ""; int[] dstPortRange = rule.getDstPortRange(); if (dstPortRange != null) { dstPortXML = "<translated-port>" + dstPortRange[0] + "</translated-port>"; } // add public IP to the sub-interface Map<String, String> a_sub_params = new HashMap<String, String>(); a_sub_params.put("type", "config"); a_sub_params.put("action", "set"); a_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _publicInterfaceType + "/entry[@name='" + _publicInterface + "']/layer3/units/entry[@name='" + publicInterfaceName + "']/ip"); a_sub_params.put("element", "<entry name='" + publicIp + "/32'/>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); // add the destination nat rule for the public IP String xml = ""; xml += "<from><member>" + _publicZone + "</member></from>"; xml += "<to><member>" + _publicZone + "</member></to>"; xml += "<source><member>any</member></source>"; xml += "<destination><member>" + publicIp + "</member></destination>"; xml += "<service>" + srcService + "</service>"; xml += "<nat-type>ipv4</nat-type>"; xml += "<to-interface>" + publicInterfaceName + "</to-interface>"; xml += "<destination-translation><translated-address>" + rule.getDstIp() + "</translated-address>" + dstPortXML + "</destination-translation>"; Map<String, String> a_params = new HashMap<String, String>(); a_params.put("type", "config"); a_params.put("action", "set"); a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + dstNatName + "']"); a_params.put("element", xml); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); return true; case DELETE: if (!manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { return true; } // determine if we need to delete the ip from the interface as well... Map<String, String> c_params = new HashMap<String, String>(); c_params.put("type", "config"); c_params.put("action", "get"); c_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[destination/member[text()='" + publicIp + "']]"); String c_response = request(PaloAltoMethod.GET, c_params); String count = ""; NodeList response_body; Document doc = getDocument(c_response); XPath xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = xpath.compile("/response[@status='success']/result"); response_body = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (response_body.getLength() > 0 && response_body.item(0).getAttributes().getLength() > 0) { count = response_body.item(0).getAttributes().getNamedItem("count").getTextContent(); } // delete the dst nat rule Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + dstNatName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); if (!count.equals("") && Integer.parseInt(count) == 1) { // this dst nat rule is the last, so remove the ip... // delete IP from sub-interface... Map<String, String> d_sub_params = new HashMap<String, String>(); d_sub_params.put("type", "config"); d_sub_params.put("action", "delete"); d_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _publicInterfaceType + "/entry[@name='" + _publicInterface + "']/layer3/units/entry[@name='" + publicInterfaceName + "']/ip/entry[@name='" + publicIp + "/32']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); } return true; default: s_logger.debug("Unrecognized command."); return false; } } /* * Static NAT rule implementation */ private String genStcNatRuleName(String publicIp, long id) { return "stc_nat." + genIpIdentifier(publicIp) + "_" + Long.toString(id); } public boolean manageStcNatRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, StaticNatRuleTO rule) throws ExecutionException { String publicIp = rule.getSrcIp(); String stcNatName = genStcNatRuleName(publicIp, rule.getId()); String publicInterfaceName; String publicVlanTag; if (rule.getSrcVlanTag() == null) { publicInterfaceName = genPublicInterfaceName(new Long("9999")); } else { publicVlanTag = parsePublicVlanTag(rule.getSrcVlanTag()); if (publicVlanTag.equals("untagged")) { publicInterfaceName = genPublicInterfaceName(new Long("9999")); } else { publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag)); } } switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + stcNatName + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Static NAT exists: " + stcNatName + ", " + result); return result; case ADD: if (manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { return true; } // add public IP to the sub-interface Map<String, String> a_sub_params = new HashMap<String, String>(); a_sub_params.put("type", "config"); a_sub_params.put("action", "set"); a_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _publicInterfaceType + "/entry[@name='" + _publicInterface + "']/layer3/units/entry[@name='" + publicInterfaceName + "']/ip"); a_sub_params.put("element", "<entry name='" + publicIp + "/32'/>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); // add the static nat rule for the public IP String xml = ""; xml += "<from><member>" + _publicZone + "</member></from>"; xml += "<to><member>" + _publicZone + "</member></to>"; xml += "<source><member>any</member></source>"; xml += "<destination><member>" + publicIp + "</member></destination>"; xml += "<service>any</service>"; xml += "<nat-type>ipv4</nat-type>"; xml += "<to-interface>" + publicInterfaceName + "</to-interface>"; xml += "<destination-translation><translated-address>" + rule.getDstIp() + "</translated-address></destination-translation>"; Map<String, String> a_params = new HashMap<String, String>(); a_params.put("type", "config"); a_params.put("action", "set"); a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + stcNatName + "']"); a_params.put("element", xml); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); return true; case DELETE: if (!manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { return true; } // delete the static nat rule Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='" + stcNatName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); // delete IP from sub-interface... Map<String, String> d_sub_params = new HashMap<String, String>(); d_sub_params.put("type", "config"); d_sub_params.put("action", "delete"); d_sub_params.put("xpath", "/config/devices/entry/network/interface/" + _publicInterfaceType + "/entry[@name='" + _publicInterface + "']/layer3/units/entry[@name='" + publicInterfaceName + "']/ip/entry[@name='" + publicIp + "/32']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } /* * Firewall rule implementation */ private String genFirewallRuleName(long id) { // ingress return "policy_" + Long.toString(id); } private String genFirewallRuleName(long id, String vlan) { // egress return "policy_" + Long.toString(id) + "_" + vlan; } public boolean manageFirewallRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, FirewallRuleTO rule) throws ExecutionException { String ruleName; if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { ruleName = genFirewallRuleName(rule.getId(), rule.getSrcVlanTag()); } else { ruleName = genFirewallRuleName(rule.getId()); } switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='" + ruleName + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Firewall policy exists: " + ruleName + ", " + result); return result; case ADD: if (manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { return true; } String srcZone; String dstZone; String dstAddressXML; String appXML; String serviceXML; String protocol = rule.getProtocol(); String action = "allow"; // Only ICMP will use an Application, so others will be any. if (protocol.equals(Protocol.ICMP.toString())) { appXML = "<member>icmp</member><member>ping</member><member>traceroute</member>"; // use the default icmp applications... } else { appXML = "<member>any</member>"; } // Only TCP and UDP will use a Service, others will use any. if (protocol.equals(Protocol.TCP.toString()) || protocol.equals(Protocol.UDP.toString())) { String portRange; if (rule.getSrcPortRange() != null) { int startPort = rule.getSrcPortRange()[0]; int endPort = rule.getSrcPortRange()[1]; if (startPort == endPort) { portRange = String.valueOf(startPort); } else { portRange = String.valueOf(startPort) + "-" + String.valueOf(endPort); } manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null); serviceXML = "<member>" + genServiceName(protocol, portRange, null) + "</member>"; } else { // no equivalent config in PA, so allow all traffic... serviceXML = "<member>any</member>"; } } else { serviceXML = "<member>any</member>"; } // handle different types of fire wall rules (egress | ingress) if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { // Egress Rule srcZone = _privateZone; dstZone = _publicZone; dstAddressXML = "<member>any</member>"; // defaults to 'allow', the deny rules are as follows if (rule.getType() == FirewallRule.FirewallRuleType.System) { if (!rule.isDefaultEgressPolicy()) { // default of deny && system rule, so deny action = "deny"; } } else { if (rule.isDefaultEgressPolicy()) { // default is allow && user rule, so deny action = "deny"; } } } else { // Ingress Rule srcZone = _publicZone; dstZone = _privateZone; dstAddressXML = "<member>" + rule.getSrcIp() + "</member>"; } // build the source cidr xml String srcCidrXML = ""; List<String> ruleSrcCidrList = rule.getSourceCidrList(); if (ruleSrcCidrList.size() > 0) { // a cidr was entered, modify as needed... for (int i = 0; i < ruleSrcCidrList.size(); i++) { if (ruleSrcCidrList.get(i).trim().equals("0.0.0.0/0")) { // allow any if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { srcCidrXML += "<member>" + getPrivateSubnet(rule.getSrcVlanTag()) + "</member>"; } else { srcCidrXML += "<member>any</member>"; } } else { srcCidrXML += "<member>" + ruleSrcCidrList.get(i).trim() + "</member>"; } } } else { // no cidr was entered, so allow ALL according to firewall rule type if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { srcCidrXML = "<member>" + getPrivateSubnet(rule.getSrcVlanTag()) + "</member>"; } else { srcCidrXML = "<member>any</member>"; } } // build new rule xml String xml = ""; xml += "<from><member>" + srcZone + "</member></from>"; xml += "<to><member>" + dstZone + "</member></to>"; xml += "<source>" + srcCidrXML + "</source>"; xml += "<destination>" + dstAddressXML + "</destination>"; xml += "<application>" + appXML + "</application>"; xml += "<service>" + serviceXML + "</service>"; xml += "<action>" + action + "</action>"; xml += "<negate-source>no</negate-source>"; xml += "<negate-destination>no</negate-destination>"; if (_threatProfile != null && action.equals("allow")) { // add the threat profile if it exists xml += "<profile-setting><group><member>" + _threatProfile + "</member></group></profile-setting>"; } if (_logProfile != null && action.equals("allow")) { // add the log profile if it exists xml += "<log-setting>" + _logProfile + "</log-setting>"; } boolean has_default = false; String defaultEgressRule = ""; if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { // check if a default egress rule exists because it always has to be after the other rules. Map<String, String> e_params = new HashMap<String, String>(); e_params.put("type", "config"); e_params.put("action", "get"); e_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0_" + rule.getSrcVlanTag() + "']"); String e_response = request(PaloAltoMethod.GET, e_params); has_default = (validResponse(e_response) && responseNotEmpty(e_response)); // there is an existing default rule, so we need to remove it and add it back after the new rule is added. if (has_default) { s_logger.debug("Moving the default egress rule after the new rule: " + ruleName); NodeList response_body; Document doc = getDocument(e_response); XPath xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = xpath.compile("/response[@status='success']/result/entry/node()"); response_body = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } for (int i = 0; i < response_body.getLength(); i++) { Node n = response_body.item(i); defaultEgressRule += nodeToString(n); } Map<String, String> dd_params = new HashMap<String, String>(); dd_params.put("type", "config"); dd_params.put("action", "delete"); dd_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0_" + rule.getSrcVlanTag() + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, dd_params)); } } // add the new rule... Map<String, String> a_params = new HashMap<String, String>(); a_params.put("type", "config"); a_params.put("action", "set"); a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='" + ruleName + "']"); a_params.put("element", xml); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); // add back the default rule if (rule.getTrafficType() == FirewallRule.TrafficType.Egress && has_default) { Map<String, String> da_params = new HashMap<String, String>(); da_params.put("type", "config"); da_params.put("action", "set"); da_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0_" + rule.getSrcVlanTag() + "']"); da_params.put("element", defaultEgressRule); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, da_params)); s_logger.debug("Completed move of the default egress rule after rule: " + ruleName); } return true; case DELETE: if (!manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { return true; } Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='" + ruleName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } // remove orphaned rules if they exist... public void removeOrphanedFirewallRules(ArrayList<IPaloAltoCommand> cmdList, long vlan) throws ExecutionException { Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[contains(@name, 'policy') and contains(@name, '" + Long.toString(vlan) + "')]"); String response = request(PaloAltoMethod.GET, params); boolean has_orphans = (validResponse(response) && responseNotEmpty(response)); if (has_orphans) { Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[contains(@name, 'policy') and contains(@name, '" + Long.toString(vlan) + "')]"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); } } /* * Usage */ /* * Helper config functions */ // ensure guest network isolation private String genNetworkIsolationName(long privateVlanTag) { return "isolate_" + Long.toString(privateVlanTag); } public boolean manageNetworkIsolation(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateSubnet, String privateGateway) throws ExecutionException { String ruleName = genNetworkIsolationName(privateVlanTag); switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='" + ruleName + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Firewall policy exists: " + ruleName + ", " + result); return result; case ADD: if (manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) { return true; } String xml = ""; xml += "<from><member>" + _privateZone + "</member></from>"; xml += "<to><member>" + _privateZone + "</member></to>"; xml += "<source><member>" + privateSubnet + "</member></source>"; xml += "<destination><member>" + privateGateway + "</member></destination>"; xml += "<application><member>any</member></application>"; xml += "<service><member>any</member></service>"; xml += "<action>deny</action>"; xml += "<negate-source>no</negate-source>"; xml += "<negate-destination>yes</negate-destination>"; Map<String, String> a_params = new HashMap<String, String>(); a_params.put("type", "config"); a_params.put("action", "set"); a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='" + ruleName + "']"); a_params.put("element", xml); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); return true; case DELETE: if (!manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) { return true; } Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='" + ruleName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } // make the interfaces pingable for basic network troubleshooting public boolean managePingProfile(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim) throws ExecutionException { switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='" + _pingManagementProfile + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Management profile exists: " + _pingManagementProfile + ", " + result); return result; case ADD: if (managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) { return true; } // add ping profile... Map<String, String> a_params = new HashMap<String, String>(); a_params.put("type", "config"); a_params.put("action", "set"); a_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='" + _pingManagementProfile + "']"); a_params.put("element", "<ping>yes</ping>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params)); return true; case DELETE: if (!managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) { return true; } // delete ping profile... Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='" + _pingManagementProfile + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } private String genServiceName(String protocol, String dstPorts, String srcPorts) { String name; if (srcPorts == null) { name = "cs_" + protocol.toLowerCase() + "_" + dstPorts.replace(',', '.'); } else { name = "cs_" + protocol.toLowerCase() + "_" + dstPorts.replace(',', '.') + "_" + srcPorts.replace(',', '.'); } return name; } public boolean manageService(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, String protocol, String dstPorts, String srcPorts) throws ExecutionException { String serviceName = genServiceName(protocol, dstPorts, srcPorts); switch (prim) { case CHECK_IF_EXISTS: // check if one exists already Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='" + serviceName + "']"); String response = request(PaloAltoMethod.GET, params); boolean result = (validResponse(response) && responseNotEmpty(response)); s_logger.debug("Service exists: " + serviceName + ", " + result); return result; case ADD: if (manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) { return true; } String dstPortXML = "<port>" + dstPorts + "</port>"; String srcPortXML = ""; if (srcPorts != null) { srcPortXML = "<source-port>" + srcPorts + "</source-port>"; } // add ping profile... Map<String, String> a_params = new HashMap<String, String>(); a_params.put("type", "config"); a_params.put("action", "set"); a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='" + serviceName + "']"); a_params.put("element", "<protocol><" + protocol.toLowerCase() + ">" + dstPortXML + srcPortXML + "</" + protocol.toLowerCase() + "></protocol>"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params)); return true; case DELETE: if (!manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) { return true; } // delete ping profile... Map<String, String> d_params = new HashMap<String, String>(); d_params.put("type", "config"); d_params.put("action", "delete"); d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='" + serviceName + "']"); cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params)); return true; default: s_logger.debug("Unrecognized command."); return false; } } private String getPrivateSubnet(String vlan) throws ExecutionException { String _interfaceName = genPrivateInterfaceName(Long.parseLong(vlan)); Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/network/interface/" + _privateInterfaceType + "/entry[@name='" + _privateInterface + "']/layer3/units/entry[@name='" + _interfaceName + "']/ip/entry"); String response = request(PaloAltoMethod.GET, params); if (validResponse(response) && responseNotEmpty(response)) { NodeList response_body; Document doc = getDocument(response); XPath xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = xpath.compile("/response[@status='success']/result/entry"); response_body = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (response_body.getLength() > 0) { return response_body.item(0).getAttributes().getNamedItem("name").getTextContent(); } } return null; } /* * XML API commands */ /* Function to make calls to the Palo Alto API. */ /* All API calls will end up going through this function. */ protected String request(PaloAltoMethod method, Map<String, String> params) throws ExecutionException { if (method != PaloAltoMethod.GET && method != PaloAltoMethod.POST) { throw new ExecutionException("Invalid http method used to access the Palo Alto API."); } String responseBody = ""; String debug_msg = "Palo Alto Request\n"; // a GET method... if (method == PaloAltoMethod.GET) { String queryString = "?"; for (String key : params.keySet()) { if (!queryString.equals("?")) { queryString = queryString + "&"; } try { queryString = queryString + key + "=" + URLEncoder.encode(params.get(key), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new ExecutionException(e.getMessage()); } } if (_key != null) { queryString = queryString + "&key=" + _key; } try { debug_msg = debug_msg + "GET request: https://" + _ip + s_apiUri + URLDecoder.decode(queryString, "UTF-8") + "\n"; } catch (UnsupportedEncodingException e) { debug_msg = debug_msg + "GET request: https://" + _ip + s_apiUri + queryString + "\n"; } HttpGet get_request = new HttpGet("https://" + _ip + s_apiUri + queryString); ResponseHandler<String> responseHandler = new BasicResponseHandler(); try { responseBody = s_httpclient.execute(get_request, responseHandler); } catch (IOException e) { throw new ExecutionException(e.getMessage()); } } // a POST method... if (method == PaloAltoMethod.POST) { List<NameValuePair> nvps = new ArrayList<NameValuePair>(); for (String key : params.keySet()) { nvps.add(new BasicNameValuePair(key, params.get(key))); } if (_key != null) { nvps.add(new BasicNameValuePair("key", _key)); } debug_msg = debug_msg + "POST request: https://" + _ip + s_apiUri + "\n"; for (NameValuePair nvp : nvps) { debug_msg = debug_msg + "param: " + nvp.getName() + ", " + nvp.getValue() + "\n"; } HttpPost post_request = new HttpPost("https://" + _ip + s_apiUri); try { post_request.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); } catch (UnsupportedEncodingException e) { throw new ExecutionException(e.getMessage()); } ResponseHandler<String> responseHandler = new BasicResponseHandler(); try { responseBody = s_httpclient.execute(post_request, responseHandler); } catch (IOException e) { throw new ExecutionException(e.getMessage()); } } debug_msg = debug_msg + prettyFormat(responseBody); debug_msg = debug_msg + "\n" + responseBody.replace("\"", "\\\"") + "\n\n"; // test cases //s_logger.debug(debug_msg); // this can be commented if we don't want to show each request in the log. return responseBody; } /* Used for requests that require polling to get a result (eg: commit) */ private String requestWithPolling(PaloAltoMethod method, Map<String, String> params) throws ExecutionException { String job_id; String job_response = request(method, params); Document doc = getDocument(job_response); XPath xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = xpath.compile("/response[@status='success']/result/job/text()"); job_id = (String)expr.evaluate(doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (job_id.length() > 0) { boolean finished = false; Map<String, String> job_params = new HashMap<String, String>(); job_params.put("type", "op"); job_params.put("cmd", "<show><jobs><id>" + job_id + "</id></jobs></show>"); while (!finished) { String job_status; String response = request(PaloAltoMethod.GET, job_params); Document job_doc = getDocument(response); XPath job_xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/status/text()"); job_status = (String)expr.evaluate(job_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (job_status.equals("FIN")) { finished = true; String job_result; try { XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/result/text()"); job_result = (String)expr.evaluate(job_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (!job_result.equals("OK")) { NodeList job_details; try { XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/details/line"); job_details = (NodeList)expr.evaluate(job_doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } String error = ""; for (int i = 0; i < job_details.getLength(); i++) { error = error + job_details.item(i).getTextContent() + "\n"; } throw new ExecutionException(error); } return response; } else { try { Thread.sleep(2000); // poll periodically for the status of the async job... } catch (InterruptedException e) { /* do nothing */ } } } } else { return job_response; } return null; } /* Runs a sequence of commands and attempts to commit at the end. */ /* Uses the Command pattern to enable overriding of the response handling if needed. */ private synchronized boolean requestWithCommit(ArrayList<IPaloAltoCommand> commandList) throws ExecutionException { boolean result = true; if (commandList.size() > 0) { // CHECK IF THERE IS PENDING CHANGES THAT HAVE NOT BEEN COMMITTED... String pending_changes; Map<String, String> check_params = new HashMap<String, String>(); check_params.put("type", "op"); check_params.put("cmd", "<check><pending-changes></pending-changes></check>"); String check_response = request(PaloAltoMethod.GET, check_params); Document check_doc = getDocument(check_response); XPath check_xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = check_xpath.compile("/response[@status='success']/result/text()"); pending_changes = (String)expr.evaluate(check_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (pending_changes.equals("yes")) { throw new ExecutionException("The Palo Alto has uncommited changes, so no changes can be made. Try again later or contact your administrator."); } else { // ADD A CONFIG LOCK TO CAPTURE THE PALO ALTO RESOURCE String add_lock_status; Map<String, String> add_lock_params = new HashMap<String, String>(); add_lock_params.put("type", "op"); add_lock_params.put("cmd", "<request><config-lock><add></add></config-lock></request>"); String add_lock_response = request(PaloAltoMethod.GET, add_lock_params); Document add_lock_doc = getDocument(add_lock_response); XPath add_lock_xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = add_lock_xpath.compile("/response[@status='success']/result/text()"); add_lock_status = (String)expr.evaluate(add_lock_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (add_lock_status.length() == 0) { throw new ExecutionException("The Palo Alto is locked, no changes can be made at this time."); } try { // RUN THE SEQUENCE OF COMMANDS for (IPaloAltoCommand command : commandList) { result = (result && command.execute()); // run commands and modify result boolean } // COMMIT THE CHANGES (ALSO REMOVES CONFIG LOCK) String commit_job_id; Map<String, String> commit_params = new HashMap<String, String>(); commit_params.put("type", "commit"); commit_params.put("cmd", "<commit></commit>"); String commit_response = requestWithPolling(PaloAltoMethod.GET, commit_params); Document commit_doc = getDocument(commit_response); XPath commit_xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = commit_xpath.compile("/response[@status='success']/result/job/id/text()"); commit_job_id = (String)expr.evaluate(commit_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (commit_job_id.length() == 0) { // no commit was done, so release the lock... // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE String remove_lock_status; Map<String, String> remove_lock_params = new HashMap<String, String>(); remove_lock_params.put("type", "op"); remove_lock_params.put("cmd", "<request><config-lock><remove></remove></config-lock></request>"); String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params); Document remove_lock_doc = getDocument(remove_lock_response); XPath remove_lock_xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()"); remove_lock_status = (String)expr.evaluate(remove_lock_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (remove_lock_status.length() == 0) { throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!"); } } } catch (ExecutionException ex) { // REVERT TO RUNNING String revert_job_id; Map<String, String> revert_params = new HashMap<String, String>(); revert_params.put("type", "op"); revert_params.put("cmd", "<load><config><from>running-config.xml</from></config></load>"); requestWithPolling(PaloAltoMethod.GET, revert_params); // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE String remove_lock_status; Map<String, String> remove_lock_params = new HashMap<String, String>(); remove_lock_params.put("type", "op"); remove_lock_params.put("cmd", "<request><config-lock><remove></remove></config-lock></request>"); String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params); Document remove_lock_doc = getDocument(remove_lock_response); XPath remove_lock_xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()"); remove_lock_status = (String)expr.evaluate(remove_lock_doc, XPathConstants.STRING); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (remove_lock_status.length() == 0) { throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!"); } throw ex; // Bubble up the reason we reverted... } return result; } } else { return true; // nothing to do } } /* A default response handler to validate that the request was successful. */ public boolean validResponse(String response) throws ExecutionException { NodeList response_body; Document doc = getDocument(response); XPath xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = xpath.compile("/response[@status='success']"); response_body = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (response_body.getLength() > 0) { return true; } else { NodeList error_details; try { XPathExpression expr = xpath.compile("/response/msg/line/line"); error_details = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (error_details.getLength() == 0) { try { XPathExpression expr = xpath.compile("/response/msg/line"); error_details = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (error_details.getLength() == 0) { try { XPathExpression expr = xpath.compile("/response/result/msg"); error_details = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } } } String error = ""; for (int i = 0; i < error_details.getLength(); i++) { error = error + error_details.item(i).getTextContent() + "\n"; } throw new ExecutionException(error); } } /* Validate that the response is not empty. */ public boolean responseNotEmpty(String response) throws ExecutionException { NodeList response_body; Document doc = getDocument(response); XPath xpath = XPathFactory.newInstance().newXPath(); try { XPathExpression expr = xpath.compile("/response[@status='success']"); response_body = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new ExecutionException(e.getCause().getMessage()); } if (response_body.getLength() > 0 && (!response_body.item(0).getTextContent().equals("") || (response_body.item(0).hasChildNodes() && response_body.item(0).getFirstChild().hasChildNodes()))) { return true; } else { return false; } } /* Get the type of interface from the PA device. */ private String getInterfaceType(String interfaceName) throws ExecutionException { String[] types = {InterfaceType.ETHERNET.toString(), InterfaceType.AGGREGATE.toString()}; for (String type : types) { Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/network/interface/" + type + "/entry[@name='" + interfaceName + "']"); String ethernet_response = request(PaloAltoMethod.GET, params); if (validResponse(ethernet_response) && responseNotEmpty(ethernet_response)) { return type; } } return ""; } /* Get the threat profile from the server if it exists. */ private boolean getThreatProfile(String profile) throws ExecutionException { Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/profile-group/entry[@name='" + profile + "']"); String response = request(PaloAltoMethod.GET, params); return (validResponse(response) && responseNotEmpty(response)); } /* Get the log profile from the server if it exists. */ private boolean getLogProfile(String profile) throws ExecutionException { Map<String, String> params = new HashMap<String, String>(); params.put("type", "config"); params.put("action", "get"); params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/log-settings/profiles/entry[@name='" + profile + "']"); String response = request(PaloAltoMethod.GET, params); return (validResponse(response) && responseNotEmpty(response)); } /* Command Interface */ public interface IPaloAltoCommand { public boolean execute() throws ExecutionException; } /* Command Abstract */ private abstract class AbstractPaloAltoCommand implements IPaloAltoCommand { PaloAltoMethod method; Map<String, String> params; public AbstractPaloAltoCommand() { } public AbstractPaloAltoCommand(PaloAltoMethod method, Map<String, String> params) { this.method = method; this.params = params; } @Override public boolean execute() throws ExecutionException { String response = request(method, params); return validResponse(response); } } /* Implement the default functionality */ private class DefaultPaloAltoCommand extends AbstractPaloAltoCommand { public DefaultPaloAltoCommand(PaloAltoMethod method, Map<String, String> params) { super(method, params); } } /* * Misc */ private String genIpIdentifier(String ip) { return ip.replace('.', '-').replace('/', '-'); } private String parsePublicVlanTag(String uri) { return uri.replace("vlan://", ""); } private Protocol getProtocol(String protocolName) throws ExecutionException { protocolName = protocolName.toLowerCase(); try { return Protocol.valueOf(protocolName); } catch (Exception e) { throw new ExecutionException("Invalid protocol: " + protocolName); } } private Document getDocument(String xml) throws ExecutionException { StringReader xmlReader = new StringReader(xml); InputSource xmlSource = new InputSource(xmlReader); Document doc = null; try { doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlSource); } catch (Exception e) { s_logger.error(e); throw new ExecutionException(e.getMessage()); } if (doc == null) { throw new ExecutionException("Failed to parse xml " + xml); } else { return doc; } } // return an xml node as a string private String nodeToString(Node node) throws ExecutionException { StringWriter sw = new StringWriter(); try { Transformer t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); t.transform(new DOMSource(node), new StreamResult(sw)); } catch (Throwable t) { throw new ExecutionException("XML convert error when modifying PA config: " + t.getMessage()); } return sw.toString(); } // pretty printing of xml strings private String prettyFormat(String input) { int indent = 4; try { Source xmlInput = new StreamSource(new StringReader(input)); StringWriter stringWriter = new StringWriter(); StreamResult xmlOutput = new StreamResult(stringWriter); TransformerFactory transformerFactory = TransformerFactory.newInstance(); transformerFactory.setAttribute("indent-number", indent); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(xmlInput, xmlOutput); return xmlOutput.getWriter().toString(); } catch (Throwable e) { try { Source xmlInput = new StreamSource(new StringReader(input)); StringWriter stringWriter = new StringWriter(); StreamResult xmlOutput = new StreamResult(stringWriter); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent)); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(xmlInput, xmlOutput); return xmlOutput.getWriter().toString(); } catch (Throwable t) { return input; } } } //@Override @Override public void setName(String name) { // TODO Auto-generated method stub } //@Override @Override public void setConfigParams(Map<String, Object> params) { // TODO Auto-generated method stub } //@Override @Override public Map<String, Object> getConfigParams() { // TODO Auto-generated method stub return null; } //@Override @Override public int getRunLevel() { // TODO Auto-generated method stub return 0; } //@Override @Override public void setRunLevel(int level) { // TODO Auto-generated method stub } }