Category → Uncategorized
VPN (IPsec) tunnel between a pfSense 2.0 router and a FRITZ!Box
We have a pfSense 2.0 router at our coworking space which is hooked up to a pretty fast VDSL line so I thought it would be a fun idea to connect my home network (where I’m using a FRITZ!Box 7390) to the work LAN using a secure and permenent VPN tunnel.
Doing a quick Google search yields results for the 1.2 version of pfSense which is outdated and does not use DynDNS hostnames for both ends, so I did a quick writeup of my own.
Prerequisites
First things first, create permanent hostnames for your pfSense and your FRITZ!Box. If your DSL provider has assigned permanent IP addresses, that’s fine. If they didn’t you’ll probably need something like DynDNS. Last time I checked, you could still get free accounts, otherwise it’s just a few bucks a year – probably a good investment. You’ll need to configure both the pfSense and the FRITZ!Box to update your DynDNS hosts whenever their IP address changes, but that’s pretty straight forward so I won’t cover it here. Fun fact: you can add CNAME records to your company domain pointing to your DynDNS host, so it looks even more professional. We use vpn.launchco.com for instance – how cool is that?
You’ll also need two different primary subnets for your networks, i.e. if your home network lives in 192.168.178.0/24, which is the standard network a FRITZ!Box uses, your work network has to use something else, like 192.168.1.0/24, which is by the way the standard that pfSense uses – so you should be safe if you’re like me a big fan of sticking with sensible vendor defaults.
Now, with the permanent hostnames and subnets in place, let’s get down to business.
Setting up pfSense
We’re using IPsec, so let’s head to VPN -> IPsec first and click the [+] icon on the bottom right to add a new phase 1 entry.
Fill the form in accordance to what you see on the following screenshot:
Obviously, replace your-fritz.dyndns.org with the permanent hostname assigned to your FRITZ!Box as well as your-pfsense.dyndns.org with the one on your pfSense box. The Pre-Shared Key should be a long random string. Don’t worry, you won’t have to remember it. You’ll just save that in the FRITZ!Box later and then you can forget about it.
Next up, we need a phase 2 entry. For that, click the [+] icon next to a label that says Show 0 Phase-2 entries and fill the form like below:
Here, you just need to make sure that you replace 192.168.178.0 with the actual subnet your FRITZ!Box uses. Again, if you’ve sticked with the default when setting up the box, this setting should be right for you.
That should be it for the pfSense. After saving it’ll probably ask you to apply or reload the configuration. This is safe to do now.
Setting up the FRITZ!Box
Now, let’s finish this by configuring a VPN entry in your FRITZ!Box. From my perspective, this part is much easier, because I’m just pasting code instead of fiddling with screenshots – yay!
Fire up your favorite text editor and paste the following code:
Make the necessary modifications according to the comments in the file. Then, open the FRITZ!Box configuration interface in your browser and head to Internet -> Freigaben -> VPN, use the browse button to select the file you just created and click on VPN-Einstellungen importieren.
That’s it – you’re done. In my first trials I had to go back to the pfSense interface and navigate to Status -> IPsec to click on a small [>] (“play”) button to get things rolling. Maybe you need this, maybe it just works without it.
Getting the connection up after a restart of either of the two routers sometimes fails which is most probably due to the fact that DynDNS updates have not yet propagated when the VPN tries to connect. In this case, just be patient; both boxes will keep retrying to open VPN connections and you can always stop/start on both ends yourself. Once a connection is made, the tunnels are usually stable and rock-solid. Enjoy!
iPad Safari Bug: Touching iFrames
I didn’t want to buy an iPad. But in a recent project for one of our clients we had to optimize a page for the iPad. And that is why I just bought one to fix some bugs we had with that page. After some minutes of searching I came up with a nasty browser bug.
The page we developed contained a slider and an image that you can drag around. Both were implemented using the typical touch events that you know from mobile Safari: touchstart, touchmove, touchend, touchcancel. The page worked on my iPhone and on my iPad as well. But our client put the page into an iFrame in order to include it into his website. And that didn’t work.
After some investigation I discovered that the touch events worked in some situations, but not everywhere on the dom element. It turned out that the position of the iFrame has an impact on the area where the touch events work. Let’s say the iFrame is located 100 pixel below the documents top, then the touch events work anywhere on the image except for the lower 100 pixel. From what I know about browsers my guess is that the ‘cursor’ position is not calculated correctly when passing a touch event into an iFrame.
To get a better understanding, please try this little bug demo on your iPad or iPhone. The sources of the iFrame content look like this:
<html>
<body>
<div ontouchstart="alert('touched');"
style="position:absolute;top:0px;width:200px;height:200px;background:yellow;"></div>
<div ontouchstart="alert('won't be touched');"
style="position:absolute;top:200px;width:200px;height:200px;background:red;"></div>
<div ontouchstart="alert('touched only in upper half');"
style="position:absolute;top:0px;left:200px;width:200px;height:400px;background:orange;"></div>
</body>
</html>
And the iFrame sources:
<html>
<body>
<iframe style="position:absolute;top:200px;"
border="0" src="iframecontent.html" width="400" frameborder="0"
height="400" scrolling="auto"></iframe>
</body>
</html>
I reported the bug to Apple, but since their bug tracking is not very motivating, I might add the bug to webkit as well. (I didn’t even get an email telling me that the bug was reported.)
JavaScript Testing and Continuous Integration Part II
In my previous post I described how to use the YUI Test Framework to write JavaScript tests for your web application. This part continues with how to run the tests in different browsers and how this can be used with a continuous integration server.
Executing your tests in different browsers
A framework that is very famous when it comes to browser testing is Selenium. It automates the execution of click streams in different browsers using two major components: the selenium IDE and the selenium remote control. The IDE is a Firefox plugin for recording the click streams. Originally, the click streams are written as plain HTML tables, but over the time selenium added drivers for the most popular programming languages. This way, you may record a click stream and have it transformed into e.g. Java / JUnit, where you then can add several assertions or high level programming constructs. A simple selenium JUnit test may then look like this:
public class MyTest extends SeleneseTestCase {
protected Selenium selenium;
public void setUp() {
selenium = new DefaultSelenium("192.168.1.111", 4444,
"*firefox", "http://you.web.server/");
selenium.start();
}
public void testSomething() {
selenium.open("http://you.web.server/path/to/your/tests");
selenium.windowMaximize();
selenium.windowFocus();
// wait for...
selenium.isVisible("unittestsFinished");
}
}
Having the test, the selenium remote control come into play. It is used to open a browser, inject some JavaScript code that triggers all the desired events, and click through your app. In our case we use selenium to open a firefox, load the HTML page containing our YUI tests, and wait for them to be finished. (The wait is not shown in here, you have to write it yourself.) When our tests finish, we write an invisible <div> containing the string ‘unittestsFinished’ which indicates selenium that the browser may be closed.
Iterate over your browsers
Now that we have selenium to trigger our JavaScript tests we still need to iterate over all browsers. We also need to trigger selenium somehow. Since the continuous integration server at pidoco is a hudson server, and hudson can execute an ant build script, we use ant to do the job:
<property name="browsers" value="firefox,safari,iexplore"/>
<for list="${browsers}" param="browser" delimiter=",">
<sequential>
<junit haltonfailure="false" dir="antbuild" fork="yes"
showoutput="yes">
<sysproperty key="browser" value="@{browser}"/>
<formatter type="xml" />
<test name="tests.selenium.SeleniumYUITestsLauncher"
todir="tests"
outfile="@{browser}-selenium-result.xml"/>
</junit>
<get src="http://your.test.server/path/to/junitxml"
dest="tests/TEST-@{browser}-yui-result.xml"/>
<replace file="tests/TEST-@{browser}-yui-result.xml"
token="testsuite name=""
value="testsuite name="@{browser}."/>
</sequential>
</for>
Ant iterates over a list of browsers that we want the tests to be executed in. It then triggers the selenium test via JUnit. The test result in the *selenium-result.xml is not very interesting since it is only one test which opens the yui tests. Using a system property ant tells selenium which browser it should open.
Next, we download the reported YUI test result from our test server to the integration server. You may also use selenium to read the result from the HTML page directly and save it to a file within the JUnit test. However, we had a test report server from the beginning, so we used that to get the results from the browser to our integration server. Once we have the xml file we use a regular expression to add a fake package to the test suite such that the integration server can tell us in which browser a test may be failing.
Continuous Integration
The only thing that is left for the continuous integration server is to call the ant target and read all the xml files in the ‘tests’ folder as JUnit test results. This is pretty easy but allows you to analyse your JavaScript tests, which were executed automatically in different browsers, with all the power of the continuous integration server of your choice.
JavaScript Testing and Continuous Integration Part I
Adding your Tests into a continuous integration system for test automation is quite common among professional software development. However, when it comes to testing JavaScript code, many people lack the experience and best practices to set up a productive infrastructure. We at pidoco have invested quite some time to solve this problem and came up with the setup I describe in this blog post.
The JavaScript Testing Framework
There are countless frameworks out there to use for testing JavaScript code. And I have to admit it is quite confusing to find the best for one’s project. From what I saw up to now, many frameworks look very similar to each other. They do differ in some details, which make people like one or the other. To us, it was important to have a comprehensive documentation of the frameworks capabilities. Therefore, we chose the Yahoo UI test framework. It comes with an extensive documentation, just as you know from the Yahoo UI framework. You can transform a JavaScript Object into a test case, which makes it looking quite similar to JUnit tests:
var testCase = new Y.Test.Case({
name: "TestCase Name",
//traditional test names
testSomething : function () {
//...
},
testSomethingElse : function () {
//...
}
});
Within your test functions you have a broad variety of assertions that you can use. In addition, you can use the wait() and resume() functions to interrupt a test and wait for asynchronous events to happen. This is very useful if you need to wait for some UI components to be rendered or some server calls to return. Next, you can also trigger dom events using the YUI Event object:
Y.one("body").simulate("click");
Once you have written your first test case you want to execute the test and review the results. YUI Test comes with a nice test result viewer that you may include into your test site. On the other hand, the tests should be executed in several browsers and you may want to review the results on just one page. Therefore, the framework offers a function to send the result to a URL where a server may collect and show the results. In our first attempt we sent the result as a JSON string to the server, who transformed the collected results in a simple table with the tests in rows and the browsers in columns.
var reporter = new Y.Test.Reporter("http://www.yourserver.com/path/to/target",
Y.Test.Format.JSON);
reporter.report(results);
Since many continuous integration servers like to read test results in an xml file, YUI allows for several formats to get your results:
- Y.Test.Format.XML (default)
- Y.Test.Format.JSON
- Y.Test.Format.JUnitXML
- Y.Test.Format.TAP
Oh, and if you asked yourself ‘where the heck is this Y coming from?”, here is the solution:
var Y = YUI();
Y.use('test');
Being able to write a JavaScript unit or integration test the next question is coming up:
How to test your web app
Following a test driven development a lot of JavaScript has to be written just for the tests. When deploying the web app into the productive environment, we don’t want to include the testing code. But how to separate testing code from productive code? We found two solutions to be possible. First, we use a plugin that loads, triggers, and reports the tests, but is only included into the system during development and test. This way, we simply disable the plugin and have a productive system without any testing code. However, not every web app has a plugin system that supports this solution. Therefore, we have thought of a second way to test your app: deploy a second app that contains a simple HTML page which loads the testing code plus an iFrame containing the web app to be tested.
Please have in mind the same origin policy for the HTML page containing the iFrame and the content of the iFrame. If both are delivered from the same domain, the outer page may access the JavaScript code of the iFrame content. This enables us to call the productive code from the testing code. In case your code either of the application or the test checks for e.g. instances of Array, it fails if the array was created inside the iFrame and checked outside or vice versa. You may create an array using new window.frames[0].Array() and test for an array with a instanceof window.frames[0].Array.
Using the iFrame approach you may enhance the HTML page such that it loads you app and the test code, automatically runs the tests, and listening to the Y.Test.Runner.COMPLETE_EVENT to upload the test result to your reporting server.
How the tests are triggered in different browsers and how this goes with continuous integration is subject of the second part of this blog post.
SEO-friendly Affiliate Cookies powered by mod_rewrite
So you want to run an affiliate or partner program, like for example the Planio Partner Program. Good idea. Happy customers who recommend your service to their friends are the best marketing you can get. Why not reward your customers and make them even happier?
From a technical point of view, an affiliate program is nothing fancy at first glance:
- give your customer a link with a unique token
- once a visitor signs up, check if a token is present and look up the respective customer
- reward them!
However! There’s some technical pitfalls.
Keep track of the token
This is a rather easy one: of course, you have to remember the token throughout entire visits. You can’t expect a visitor to turn into a paying customer right on the first page. They will check out your site, visit a couple of pages, and maybe even come back another day to buy your product. You still want to reward your affiliate, so cookies will be your single option.
Don’t mess with Google
We’ve learned this the hard way with Magpie and it took us quite some time to recover our page rank, so be sure to read this! Google does not like duplicate content. If you’re copying what others write on the Web or if you have a lot of pages with similar or even identical content, Google’s algorithms will classify your site as spam. What does this have to do with your affiliate program? Well, all those referral links are different because of the token, yet they will most certainly render the same content.
So what can you do? Redirect. Don’t let your app render a page if the request URI contains an affiliate token. Redirect to the actual page using status code 301 (moved permanently). This way, Google will know that the link is still valid (and thus you will get most of the link juice from referring sites), but that its location has changed.
How to implement?
For a long time, we did this within in our application. Rails makes it really easy using before_filter, so it’s no big deal. However, your setup may be more complex. Maybe you have multiple apps on subdomains or sub-URIs and maybe they run on different frameworks. Just think of your corporate blog, most of the time it’s a WordPress. But you’d still want to reward your affiliates if they send you traffic via a link to a great blog post you’ve written, right?
For Planio, we moved the redirection and cookie part to the Web server. Below is a short and sweet Apache config snippet which works really well for us:
# affiliate cookie
RewriteCond %{QUERY_STRING} (.*)ref=([a-zA-Z0-9]{6})(&(.*))?
RewriteRule ^(.*)$ $1?%1%4 [CO=affiliate:%2:.plan.io:43200,R=301,L]
It does everything for us, so our apps don’t have to worry:
- detect a token in a request URI (we use a ref= query param with a 6 character token)
- set a cookie named affiliate using the token value which is valid for all our subdomains and for 30 days
- redirect to the same page using 301, removing the ref parameter and keeping all other query parameters intact (this is great for other tracking stuff, like the params you can generate for Google Analytics)
In the end, we just need a one-liner in our signup code that reads the cookie, finds the affiliate and associates the affiliate with the newly created account.
Update: Thomas points out that you could tell Google to ignore certain query params and avoid 301 redirects using canonicals. He also claims that Google would be my friend. Not so sure about the last one, though
I hope this was useful to you. Do you run affiliate programs for your products? What are your experiences? How did you implement them? I look forward to your thoughts in the comments!






