Latest Entries »

Scenario : A palo alto firewall has been successfully setup to use global protect, along with LDAP authentication. Also, USER-ID has been setup internally,with firewall policies written to include username / groups. This allows the firewall administrator to deploy consistent firewall policies to both internal and VPN users, based on active directory groups

Problem: After a relatively long period of time (months), global protect users complain that although they can connect successfully, no internal resources are reachable over VPN. Further investigation on the firewall shows that the firewall correctly authenticates the user:

show user ip-user-mapping all

In the output of the above command, look for “GP” in the “from” column. The firewall also correctly maps the user to the correct group:

show user group name 6pmmalta\abcxyz

However, the firewall traffic logs show the user generated traffic hitting the default deny rule that should be fired only when a user is not identified, or not placed in the correct group. This obviously seems to be a bug of some kind, one which I think is related to user caching. The following workaround got us back up and running:

 

1. Run the following commands to refresh the user-group mappings:

debug user-id clear group all

debug user-id refresh group-mapping all

2. Defined a bogus user id agent under “device > user identification > user-id agents”. Commited configuration

3. Deleted the above user id agent and re-committed

 

After the above three steps were taken, VPN connectivity was restored.

About these ads

Quick Tip…

You’re a Palo Alto firewall administrator, and you’ve setup USER ID to identify all your users and write user-based firewall policies.

Problem: some users have linux laptops and do not login to the domain, others are windows users that the USER ID system simply does not pick up.

Solution:

1. Create a read only (possibly empty) share on your fileserver

2. Have your linux users write a startup script that just mounts this fileshare, and similarly for windows users, to have every user access the fileshare at least once throughout their day.

3. Install the PaloAlto user agent, and make sure “enable server session read” is enabled

 

Selection_074

You’re good to go

Any problematic users simply need to visit your share to get identified and be granted/denied access by your user based policies

Problem : in some scenarios, for example running a periodic health check on an LDAP / AD server, you may want to set a low timeout so that if a server does not answer LDAP bind requests, a user is notified in a reasonable amount of time. Also, if your PHP code is blocking, a long timeout setting will make your code seem unresponsive. Changing the LDAP connection timeout is not well documented on php.net, so following is a quick example (based on 
http://grokbase.com/t/php/php-bugs/07a3b9qf7h/42837-new-timeout-parameter-sugest-for-ldap-bind-and-or-ldap-connect
):

 

$ldapconn = ldap_connect(“192.168.1.1″);
ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, 2); /* 2 second timeout */

This article explores the process of transitioning from a PHP environment to a python one.

In a recent project we started at 6PM Infrastructure, we have a very interesting and complex problem which requires an interesting mix of cross-platform standalone applications (programs which are run as standalone applications, called from the command line that run in both Linux and Windows environments, for example “python exampleProgram.py –username=dvassallo“), as well as web applications (that is, an application which is called via normal HTTP GET and POST requests, for example “
http://myserver.com/exampleProgram.py
“). This mix of requirements made us favor python, even though initial PoCs were all written in PHP. The reason being that, especially in standalone applications, python far outshone PHP, since it was extremely easy to quickly write multi-proccessing and mulit-threaded applications. At the same time, python has several frameworks (which we are just beginning to explore) that can quickly and easily build the web applications that we need.

Coming fresh out of a PHP development exersize, it was a bit of a culture shift to get my head around Python. Thankfully, with the help of a very talented and patient developer, Alfred, we quickly got up and running. This blog post records my process of getting the web application half of Python up and running, more for my future reference than anything, but hopefully will help another dev… The following was performed on a plain vanilla Ubuntu 12.10 LTS server installation. The below was actually well documented in their own respective sites, the below also serves as a single repository for that documentation

- Installing apache, and mod_wsgi

The first thing we need is an environment within which to run python scripts and return the results to a client web browsing. My server of choice is still apache at the moment, and to get this running we do a usual:

apt-get install apache2 libapache2-mod-wsgi python2.7

this sets up the apache webserver (
http://httpd.apache.org/
), the python environment, and mod_wsgi (
https://code.google.com/p/modwsgi/
), which bridges the apache server to the python environment.

- Configuring apache to server python pages

I favor writing separate config files for apache. So, make sure you have the following line in your /etc/apache2/apache2.conf file:

Include conf.d/*.conf

We then place the following into /etc/apache2/conf.d/mod_wsgi.conf:

LoadModule wsgi_module modules/mod_wsgi.so

<Directory /var/www/wsgi>
Options ExecCGI
AddHandler wsgi-script .py
</Directory>

The above simply loads the wsgi module, and defines a folder (/var/www/wsgi), within which any file ending in .py will be executed by the wsgi module.

- Appeasing the dev gods: our first wsgi ‘Hello World’

To get a taste of python environment, we build our first hello world program, and we place the following into /var/www/wsgi/hello.py:

#!/usr/bin/env python

def application(environ, start_response):

status = ’200 OK’

output = ‘<html><body>Hello World!</body></html>’

response_headers = [('Content-type', 'text/html'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)

return [output]

By default, as soon as you call a wsgi script, automatically the “application” function gets called, and gets passed two arguments, the more important of which is the environ argument, which contains the environment of the HTTP call (for example whether the call was a GET or POST, any cookies, and so on). I found the following blog post extremely useful in my first steps into the python web world:


http://webpython.codepoint.net/wsgi_tutorial

- Introducing Flask

Once you start developing in the above environment, you quickly start noticing a major deficiency between PHP any Python…. Python has no built in replacement for PHP’s sessions, which was a kind of deal breaker for us since we did not want to build an entire web enviornment from scratch. Enter Flask (
http://flask.pocoo.org/docs/
) a lightweight python framework targeted specifically to build web applications in python, including support for cookies, sessions, and so on…

So how do we get flask within the wsgi environment we built in our previous step? This is how I went about it…First we install flask on our ubuntu server, with a quick:

apt-get install python-flask

Next, we setup our python file that hosts our second “Hello World” code, run from within mod_wsgi on apache, built on the Flask framework…. we add the following code to myApp.py, the full path being: /var/www/wsgi/myApp.py:

from flask import Flask
app = Flask(__name__)

@app.route(‘/’)
def hello_world():
return ‘Hello World!’

@app.route(‘/dave’)
def myDave():
return ‘Hello World – From Dave’

if __name__ == ‘__main__’:
app.run()

In the above, we first import the flask module we’ve just installed, and define two “routers”. The first routes “/” to the hello_world function, which just returns the text “Hello World!”. The second route servers any requests to /dave to be served by the myDave function, which prints “Hello World – From Dave”. The last couple of lines run the main flask app. By default, Flask expects the main application to be called “app”. In fact the Flask documentation explicitly states that you shouldnt mess around with this and keep the main function called “app”

A keen reader will notice that mod_wsgi expects the main function called to be named application, while Flask expects it to be called app. How do we account for this difference? The Flask official documentation has a nice and easy way of doing this. We write a “wrapper file” which imports the above flask application into a mod_wsgi environment. We place the following into /var/www/wsgi/flask.py:

import sys
sys.path.append(‘/var/www/wsgi’)

from myApp import app as application

The first two lines import the sys module, and add the /var/www/wsgi directory to the python PATH environment. If this is omitted, upon attempting to run our script, apache will spit out error messages in the logs that basically tell you that the “myApp” module cannot be found.

The third line in our script tried to import out Flask application myApp with a special twist… we import the Flask “app” function but under a new alias called “application“. This is how we bridge Flask’s “app” to mod_wsgi’s “application”

Now, when we visit:


http://myserver.com/wsgi/flask.py
, will result in “Hello World!”, while visiting
http://myserver.com/wsgi/flask.py/dave
will result in “Hello World – From Dave”, as expected

- Some quick tips

Unlike PHP, mod_wsgi caches python files so that subsequent requests to these file are served faster…. so do yourself a favour, and when you change any code in your python applications, make sure to restart the apache server to force it to reload the code with a normal /etc/init.d/apache restart

Palo Alto networks have an interesting feature in their BGP module called “Conditional Adv” – this is found in the Network > Virtual Routers > default > BGP > Conditional Adv tab of the GUI. There are no concrete examples in their KB of how to implement this, so here is a rundown of why and how you could use this feature. It is analogous to the cisco conditional BGP advertising feature.

Scenario:

Imagine that, due to availability requirements, you have two ISP points of presence in your network. Either two separate ISPs,  or maybe two different physical connections from the same ISP (the latter is my case). Each of these links has BGP running over them, normally each link would have a /30 subnet which contains only 2 IP addresses – your firewall IP and the ISP’s gateway IP. The idea is that you would like to have not only outbound traffic redundancy (that is relatively easy since you just need to configure a policy based route with monitoring to your primary ISP – lots of documentation on this scenario exists), but to also have incoming traffic redundancy. To give a concrete example, let’s say you own the public IP range 1.1.1.0/30. You would need this range to be primarily advertised over the primary ISP link. Should this link for any reason go down, you would then need to advertise the same range to the secondary ISP link. You could play around with BGP path attributes, and use the usual tricks like pre-pending your AS number multiple times to increase the BGP “weight” over the secondary link. However, this is not entirely reliable because an upstream BGP process may overwrite your carefully planned BGP attributes.

Diagrammatically:

Selection_345

You can see that we only want to advertise our public IP range to the “GM” peer, unless that link fails and we then need to advertise to the “GG” peer.

Configuration

The BGP configuration starts off pretty standard. See my previous blog post for some more details, a quick run-down follows:

1. On the palo alto external interface, define your public IP range

interface

2. Under network > virtual routers > default, open the “redistribution profiles” tab and create a profile that will redistribute your IP range into the BGP routing process. Make sure the “redist” button is selected.

redist

3. Move on to the BGP tab, and enter your appropriate router ID and AS number

4. On the “peer groups” sub-tab, enter the appropriate peer configuration:

peer_groups

As you can see above, we insert both GM and GG

5. Under the “redist rules” tab, define a rule that allows the PA to redistribute our previously configured redistribution profile into BGP:

redistrules

6. In this case, I left the “import” tab empty since I’d like to accept routes the peers send

7. In the “export” tab we have an “allow” rule, advertising our public IP range, and a deny rule so we do not advertise anything else. 

export

Two points of note here. First, the deny rule at the bottom is quite important. You do not want to advertise more routes than you have to, failing to do so may lead to your WAN link becoming a “transit link” and passing traffic that isn’t generated by your organisation. Second, more importantly, note that in the export allow rule, I defined both GG and GM in the “used by” section

At this stage, if we now were to check the ISP routers, we’d see the 1.1.1.0/30 range advertised to both the routers… normal BGP operation up to this point. In fact, we can see the PA is advertising the route to both ISP routers:

pa_cmd1

8. Setting up conditional advertising, under the “Conditional Adv” tab. Create a new policy.

  • In the “used by” section, select only the secondary ISP connection (GG in my case), since you would like the conditional advertising to only ever be applied to the secondary link.
  • In the “non-exist filter” section, you need to specify which route from the local bgp rib you would like to monitor. (sidenote, to view the local bgp RIB you can do so from the GUI or by issuing show routing protocol bgp loc-rib). Unfortunately you are not allowed to monitor on the 0.0.0.0/0 route, so you have to arrange with your ISP to advertise at least one non-default route. In the case, the ISP is advertising an unused IP assigned to a loopback interface on their router, which is 2.2.2.1/32, as seen in my local RIB:

loc_rib

As long as the route 2.2.2.1/32 exists in the RIB, it means my primary ISP connection is up and I do not need to advertise to my secondary ISP. So we choose that route as the non-exist filter:

non-exist

  • Should the monitored route, fail, only then advertise my public range to GG, so we define our public IP range in the “advertise filters” tab

advertise_filt

 

 

Commit the changes, and once done issue the command “show routing protocol bgp policy cond-adv”, which should output something similar to:

 

VIRTUAL ROUTER: default (id 1)
==========
Peer/Group: secondary
Suppress condition met: yes
Suppress condition (Non-exist filter):
name: GM_Loopback
AS pattern: 65532
Destination: 2.2.2.1
hit count: 2
Route filter (Advertise filter):
name: AdvertiseToGG
Destination: 1.1.1.0/30
hit count: 1
———-

Should you see “Suppress condition met: no”, you may need to disable the primary ISP bgp peer, commit, and re-enable the bgp peer. If you now check the routes that the PA is advertising, you will see it only advertising to our primary peer GM:

rib_supp

If I now purposely shut down the primary peer, the suppress condition will not be met and the public IP range is now advertised to the secondary peer GG:

failed

And turning it back on reverses it, advertising only to GM, our primary peer.

 

 

 

 

Follow

Get every new post delivered to your Inbox.

Join 90 other followers

%d bloggers like this: