Traffic from GWClient's LAN (192.168.2.0/24) to GWServer's LAN (192.168.1.0/24) works perfectly
Traffic from GWServer's LAN (192.168.1.0/24) to GWClient's LAN (192.168.2.0/24) does NOT work
Devices on GWServer's network cannot reach anything on GWClient's network, even though the reverse works fine.
Diagnosis
Verifying the Server Side (GWServer)
First, I confirmed GWServer was configured correctly:
# Check routes
ip route show
# Output: 192.168.2.0/24 via 192.168.9.2 dev wgsrv1 ✓
# Check WireGuard peer config
wg show
# Output: allowed ips: 192.168.9.2/32, 192.168.2.0/24 ✓
The server was sending packets correctly. I confirmed this by pinging from the WireGuard interface:
ping -I wgsrv1 192.168.2.1
Packets were being sent through the tunnel.
Finding the Problem (GWClient)
On GWClient, I ran a packet capture on the WireGuard interface:
tcpdump -i wgclt1 -n icmp
Key finding: Packets from GWServer were arriving at the interface, but no replies were being sent back.
The packets were reaching GWClient but being silently dropped. This pointed to a firewall issue.
Root Cause: UniFi's Zone Classification
After digging into the iptables rules, I discovered the issue:
UniFi classifies WireGuard VPN Client interfaces as "External" (WAN) zone.
The iptables chains UBIOS_WAN_LOCAL_USER and UBIOS_WAN_IN_USER have these rules:
RETURN if connection state is RELATED,ESTABLISHED
DROP if INVALID
Specific port forwards (if configured)
DROP everything else
When GWServer initiates a connection to GWClient, it's a new connection from GWClient's perspective. Since it's not RELATED or ESTABLISHED, it gets dropped by the default WAN firewall rules.
The Solution
Quick Test (Temporary)
To verify this was the issue, I added temporary iptables rules via SSH on GWClient:
After adding these rules, bidirectional traffic started working immediately. However, these rules don't survive a reboot.
Permanent Fix (UniFi Zone-Based Firewall)
The proper solution is to create Zone-Based Firewall policies in the UniFi UI on GWClient:
Rule 1: Allow VPN to Gateway
Name: WireGuard VPN to Gateway
Action: Allow
Source Zone: External
Source: IP Address — 192.168.9.0/24, 192.168.1.0/24
Destination Zone: Gateway
Protocol: All
Rule 2: Allow VPN to Internal
Name: WireGuard VPN to Internal
Action: Allow
Source Zone: External
Source: IP Address — 192.168.9.0/24, 192.168.1.0/24
Destination Zone: Internal
Protocol: All
To create these in the UniFi Network UI:
Go to Settings → Firewall & Security → Zones
Click Create Policy
Fill in the settings as shown above
Repeat for the second rule
Verification
After applying the firewall rules, test from GWServer:
ping 192.168.9.2 # GWClient's tunnel IP
ping 192.168.2.1 # GWClient's LAN gateway
Both should now work.
Why Does This Happen?
UniFi's WireGuard VPN Client feature is probably designed for "road warrior" scenarios—where a single device connects out to a remote network. It doesn't seem intended for true site-to-site configurations where traffic flows bidirectionally.
The Policy-Based Routing feature handles outbound routing decisions (which traffic goes through the tunnel), but it doesn't configure inbound traffic acceptance. The WireGuard Client interface is treated as an external/WAN interface, so all the default WAN firewall rules apply.
Summary
If your UniFi WireGuard site-to-site VPN only works in one direction:
The VPN Client side treats the WireGuard interface as External/WAN
Challenges when you copy server from a backup image
Some cloud server providers, like Rackspace, allow you to take full server images as a backup, so you can restore them in case anything happens.
Now, if you want to use these images for something like creating new servers with the same config, there is at least one challenge you need to be aware of.
When you create a new server from such an image, it will be an exact clone of the one it was created from. That means that it will match all the files, including the hostname but more importantly the host keys.
For other services that would try to connect to this server, that also connected to the source server, it will raise errors or warnings. For example, for ssh:
$ ssh [email protected]
The authenticity of host 'XX.XX.XX.XX (XX.XX.XX.XX)' can't be established. ED25519 key fingerprint is SHA256:randomstringhere. This host key is known by the following other names/addresses: \~/.ssh/known\_hosts:YY: source.bocribbz.com Are you sure you want to continue connecting (yes/no/[fingerprint])?
What you need to do to address this issue, is to regenerate the host key of the newly created server:
We have a Windows PC that we mainly use for gaming on Steam. We are not everytime in the same location as the PC, so we want to be able to turn it on and off remotely.
As we already have Homebridge running in the location we wanted to make use of it. Here’s what we did.
Before getting started with the Homebrdige configuration, we made sure the PC gets assigned a static IP from the DHCIP Server. Also recorded the iP and MAC, we will need them later. There are also some Windows 10 specific configs that have to be done to enable WoL, but this is more about Homebridge.
Install the Homebridge WOL plugin. Just searched for WOL from the homebridge web UI and installed it form there. There were 5 configs shown, and we took them one by one:
Pinging configuration: this is where we needed the IP of the PC, and besides that we bumped the ping interval, as we didnt care for instant results. 200 was a good interval for us.
Wake configuration: this is where we needed the MAC and the broadcast IP. Besides that we bumped the wake grace time to 90 seconds.
Shutdown configuration: this is what gave us the most headaches. We used this command: “net rpc shutdown --ipaddress 192.168.1.42 --user username%password”
Logging and Miscellaneous we didnt change anything.
Now we tested, and wake up worked... surprisingly.
The part that was more tricky was adding the ability to shut down the PC as well. First problem was our Homebrew server didnt had the “net” command... you know, from “net rpc shutdown ...”.
That was fixed by installing `samba-common-bin`
$ sudo apt-get install samba-common-bin
Now, this is where errors started getting strange. Next one was:
Could not initialise pipe winreg. Error was NT_STATUS_OBJECT_NAME_NOT_FOUND
Googled that and found our it had to do with some serive not being started. On the PC, opened Terminal as Administrator and typed:
> sc config RemoteRegistry start= auto
> sc start RemoteRegistry
Tried again to shutdown, another weird error
result was: WERR_CALL_NOT_IMPLEMENTED
We fixed this with Registry Editor
Added 32 bit DWORD "LocalAccountTokenFilterPolicy" with a value of "1" to HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Policies/System in the registry.
Now shutdown works. We can turn on and off the Windows 10 PC from Apple’s Home from both iOS and MacOS.
There are many tutorials online on how to setup DS18B20 or it’s waterproof version.
But few of them talk about troubleshooting. Here’s what we learned the hard way when the sensor is not recognized.
We installed the sensor using the recommended schema, but still when we were trying to check the sensor, we were getting dummy devices named `00-300000`
pi@raspberrypi:~ $ ls -la /sys/bus/w1/devices/ total 0 drwxr-xr-x 2 root root 0 Nov 2 11:33 . drwxr-xr-x 4 root root 0 Nov 2 11:22 .. lrwxrwxrwx 1 root root 0 Nov 2 11:33 00-300000000000 -> ../../../devices/w1_bus_master1/00-300000000000 lrwxrwxrwx 1 root root 0 Nov 2 11:33 00-700000000000 -> ../../../devices/w1_bus_master1/00-700000000000 lrwxrwxrwx 1 root root 0 Nov 2 11:33 00-b00000000000 -> ../../../devices/w1_bus_master1/00-b00000000000 lrwxrwxrwx 1 root root 0 Nov 2 11:22 w1_bus_master1 -> ../../../devices/w1_bus_master1
The only differences we had versus the schema, was that we were using a GPIO adapter (T shape). So the first step, was to remove that adapter.
The sensor was detected correctly and we were able to read temperature when we connected directly to the raspberry!
But then again, adafruit does show a tutorial where they are using a pi cobbler.
So we ordered a pi cobbler and that worked for us when we connected the sensor as close to the pins on the cobbler as possible.
So if you are using a GPIO adapter and temperature sensor is not working, try to remove it, or get the pi cobbler.
The Google Analytics default installation suggestion is to use Global Site Tag (gtag.js) tracking code.
On the Universal Analytics version, there were plugins that could be added for auto-tracking, and googleanalytics’ github account even had an autotrack repository. There is an issue asking for gtag support, but the answer is more towards using Google Tag Manager to get that functionality.
So, here we go, trying to setup some of the common user interactions through Google Tag Manager.
Universal Analytics in GTM
First step, is replacing the gtag.js snippet with the gtm.js snippet, from the quick start guide.
To setup analytics in GTM, add a new tag select the type as Universal Analytics.
In the Tag Configuration page, check the `Enable overriding settings in this tag` and input the GA-SITEID.
That is the minimal thing to get you started with GA in GTM.
Some advanced stuff you could do, is pass in the GA-SITEID using a settings variable, read here about using a data layer.
Remember to add triggering to your tag as all pages, and you should be done with the simple setup.
Recently we moved to a new place, and we noticed something interesting with the wireless signal from the router. 5GHz had farther coverage than 2.4GHz did. Which is weird, right?
Besides that a lot of interruption when using 2.4GHz network.
Then by accident, we noticed an interesting setting in the router’s config: “Reducing USB 3.0 interference”. It was disabled.
Switching it on, made the 2.4GHz signal stronger than the 5GHz one and back to being stable again.
Setting was found on MerlinWRT under Wireless > Professional > 2.4GHz Band.
For a few months, joining a Hangout from Chrome, could end up not picking any sound from our side. The only fix found on the internet at that point was just to restart `coreaudiod` and then things worked.
But more recently, as it started to happen more often, we looked for more stable solution. This is what we’re trying now.
In Audio MIDI Setup, we’ve changed the `Format` for Microphone, from 44 KHz to 48 KHz.
So far things have been working better. If things brake again, we’ll come back to update this.
Later Edit: Unfortunately the problem appeared again.
After the massive DDOS attack from October 2016, we decided to do some cleanup on how the smart devices in our home connect to the internet.
We’re talking here about the vacuum robot, the printer, IP monitoring camera and other smart sensors & devices.
Our old setup, was to use the same network for our phones, tablets and Macs as the one used by smart devices, most of them would use the 2.4Ghz and some are on 5Ghz wifi. The
With the new setup, we’re making use of the router’s Guest network feature, and isolate that from the network that we use for computers and disallow access to the shared drive that has a lot of personal information.
The only thing we are not so certain about is the printer. It’s not clear if data is encrypted and how well is encrypted, since now everything we print goes through the internet.
The environments have been easily replicated using vagrant cookbooks that create a Virtual Box machine with the same setup on both macs.
We decided to compare the run times for two actions, one for backend, which is running the tests suite; and one for front-end, which is building all the web assets using browserify.
MBPr vs MBAir - python tests
There are that many, but running the python unit and integration tests, its a close win for the MBPr witch is more than 2 seconds faster..
MBPr vs MBAir - collect static & browserify
This task obviously takes a little longer, thats why we increased the speed while browserify is running to x4. MBPr takes the edge again, almost 13 seconds faster.
These time differences dont seem much, but how many times do you run them a day? That difference can be more or less significant.
After these two benchmarks, its a narrow win for MBPr, but that doesnt entirely justify the price difference.
There is one thing that will make MBPr the clear winner. Its something that bothered us and created a lot of frustration during development. That is because we are not working from the exact same place, exact same position all the time; we are changing desks, sofas or even the position on the same chair.
And every time you do that, you also have to adjust the screen angle.
Check out this third video where you can see how the cursor is invisible on the MBAir and how it fades in as the angle changes.
This can be a temporary problem, until MBAir gets a better/retina display.
If MBAir does get a display upgrade, we would switch to it, as the MBPr felt like a heavy brick when switching back to it.
So (temporary) winner for this round of python web development tests: MacBook Pro Retina.
If you want to share your setup and why you chose it, we'd love to read about it in the comments bellow.
On a recent MySQL project, we had to implement geospatial functionality, so we decided to migrate it to Postgres, and use its mature PostGIS system.
This was a live product, with tons of sensitive data in a lot of tables; a migration script was needed.
Before we go into details, I want to emphasis that this is not the recommended way to do it.
mysql2postgres gem is what was used. It took a decent amount of time.
Duplicate key value violates unique constraint table_name_pk
This was the first error that struck us when saving existing objects; the reason being that the primary key sequence is out of sync.
The fix, for each table:
SELECT setval('table_name_id_seq', (SELECT MAX(id) FROM table_name)+1);
Null value in column "data_id" violates not-null constraint
The second error, but unlike the previous one, this only happened for new entries. This was more tricky to debug, because an object was created in the DB, but the variable didn't got it's id/pk value.
The reason for this is a missing owner for the sequence on every table.
The fix, for each table:
ALTER SEQUENCE table_name_id_seq OWNED BY table_name.id;
Hopefully, things will be smooth from now on. How about you, any problems migrating from MySQL to PostgreSQL?
If you are new to TDD, writing the tests might be a real pain, not to mention about worrying how fast they run.
Factory Boy
If you already are familiar or just getting started with OOP and feel comfortable with actually checking the attribute in order to make it obvious that the value was changed, then Factory Boy is really a tool for you.
It is a handy tool to help you get started with writing tests and checking how objects relate to each other.
The down-side is that you will have to always create a "context" to make sure that you have all the entries you need. Eg if you are adding an article, you first need to manually create (hardcode) the creation of a category.
Mock on the other hand, is a more advanced tool, and requires a totally different approach and even a different mentality about how the tests must be written and what should be tested.
Isolation is its main advantages and you dont have to create a "context" so that your tests can run.
The test's goal is to make sure that if some POST data is sent, then a .create() call is made with the correct arguments. The action done by. create() is checked in another test.
Not having to deal with DB or other hooks makes this approach the faster one.
As a python developer, you form habits of writing and organizing your code very well, and when the time comes to learn a new language, you try to stick to same patterns.
What is really great about nodeenv, is that it helps you avoid typing long source commands, and if you are used to virtualenvwrapper, you dont have to learn a new set of commands.
To install it you can follow these quick steps, after you have installed virtualenv and virtualenvwrapper.
$ mkvirtualenv node_proj
(node_proj)$ pip install nodeenv # install nodeenv in the py virtualenv
(node_proj)$ nodeenv -p # install nodejs and add new function to virtualenvwrapper
* Install node.js (0.10.26) ... done.
* Appending nodeenv settings to /opt/node_proj/shared/env/node_proj/bin/activate
(node_proj)$ deactivate; workon node_proj # reactivate to use the new functions
You now have a nodejs and a python isolated environment, and when you run npm install -g commands, it installs the packages in the node virtual env.
Check out the tree structure to ensure this is correct:
Recently I was in a dilemma if its safe to charge the iPhone from an iPad adapter. Apple's official response to this question: its safe, the battery takes as much power as it needs, even if the adapter can provide more.
But some answers suggested that its better for battery life to charge it from lower frequencies, like the USB from the MacBook.
First test: USB
Charging the iPhone from the Macbook's USB port in the evening, the battery only laster until next day evening, something like 24 hours.
In order to avoid doubt, this test was repeated, the same evening when it discharged, charged it again to 100%. Unfortunately, same result, no longer than 24 hours of battery life.
Both days, the route was similar, with wireless and location activated.
Second test: wall plug
This was terrible, got our hands on an adapter from a friend that day and charged it from the wall plug.
Same route, wireless and location activated, it passed 24 hours and still going.
Some interesting tests that could follow up: car plug and motorcycle plug, but based on previous results, chances are it wont last as long as the wall plug.