Ad blockling docker image

I’ve been wanting to block ads on a dns level on my home network for awhile lately.
Today wasn’t going to be a productive day, so I said I may as well make it productive.

Setting up a pi hole is the obvious choice, but I couldn’t find any SD cards after moving a few months ago. I did want to to setup on a dedicated pi to have some redundancy in case my home server was down, but I decided to be a grown up about and set up a docker image to do it.

Fortunately a docker image already exists for this project. It does want to take over ports 53, 80, but I’m already using them for a named daemon for openvpn and nginx.

Instead I decided to add a vip to the docker host and have it used 53 and 80 for the pi hole bound on that address instead.
It’s quite easy.

ip addr add 10.2.2.250/24

And then a systemd unit to to make it persistent across reboots.

view /etc/systemd/system/eno1-vip.service
[Unit]
Description=Add vip to eno1 after dhcp
Requires=dhcpcd@eno1.service
[Service]
ExecStart=/usr/bin/ip addr add 10.2.2.250/24 dev eno1
[Install]
WantedBy=default.target

This looks slightly at odds with itself, but I still prefer to use DHCP for the primary IP on the box.

The docker image is built with:

#!/bin/bash
IMAGE='diginc/pi-hole:alpine'
NIC='eno1'
IP="10.2.2.250"
#opennicproject.org public DNS
DNS1="5.9.49.12"
DNS2="5.135.183.146"
#Bind ports to the vip
docker run -p 10.2.2.250:53:53/tcp -p 10.2.2.250:53:53/udp -p 10.2.2.250:80:80 \
  --cap-add=NET_ADMIN \
  -e ServerIP="$IP" \
  -e DNS1=$DNS1 -e DNS2=$DNS2 \
  --name pihole \
  -d "$IMAGE"

This will result in the following iptables rules:

# iptables -t nat --list
....
Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere
DNAT       tcp  --  anywhere             Farnsworth           tcp dpt:http to:172.17.0.2:80
DNAT       tcp  --  anywhere             Farnsworth           tcp dpt:domain to:172.17.0.2:53
DNAT       udp  --  anywhere             Farnsworth           udp dpt:domain to:172.17.0.2:53

and the systemd unit for the pi hole docker image will look like:

view /etc/systemd/system/pihole.service
[Unit]
Description=pihole ad blocker
Requires=docker.service
After=eno1-vip.service

[Service]
Restart=always
ExecStart=/usr/bin/docker start -a pihole
ExecStop=/usr/bin/docker stop -t 2 pihole

[Install]
WantedBy=default.target

I had issue getting the http working on my openwrt setup. It did transpire to be a safety mechanism within dnsmasq, it won’t return a DNS record for an address that is the DNS server. This is due to rebind_protection.
Edit /etc/config/dhcp and set:

config dnsmasq
        option rebind_protection '0'

and restart dnsmasq. You’ll know this will have worked as you should be able to

dig +short pi.hole
10.2.2.250

In short it wasn’t too bad. I need to explore further how to incorporate a local DNS server, but I enjoyed putting this together and I may use docker more with this vip setup.

Scripting yakuake

I’m been a big fan of the workflow of using a drop down terminal emulator for over a decade now. Yakuake is a KDE version, taking it’s name from the terminal in Quake that used ~ to access it.

I’m not a fan of restarting my desktop environment for because it means I need to restart all my tabs. I’ve been using arch also for long time now, and, in my opinion there is less difficultly in updates if they are done often. I prefer to do a reboot after a kernel update. I could work around this, but I’ve sort of made it part of my week to do updates on a Friday evening and have a newly booted system when I come in on Monday.

For a long time I’ve wanted to be able to save my yakuake session, but I realised I could just script the initial state, and here is how I do it.

#!/bin/bash
# Starting yakuake based on user preferences. Information based on http://forums.gentoo.org/viewtopic-t-873915-start-0.html
# Adding sessions from previous website is broken, use this: http://pawelkoston.pl/blog/sublime-text-3-cheatsheet-modules-web-develpment/
# This line is needed in case yakuake does not accept fcitx inputs.
/usr/bin/yakuake --im /usr/bin/fcitx --inputstyle onthespot

SESSION_ID=0;

#First Tab
# Start dmesg and radio
SESSION_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.addSession)
TERMINAL_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.terminalIdsForSessionId $SESSION_ID )
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "journalctl -f --dmesg" 
qdbus org.kde.yakuake /yakuake/tabs  setTabTitle $SESSION_ID "dmesg/radio"
TERMINAL_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.splitTerminalTopBottom $TERMINAL_ID)
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "cd ./clockradio/test_work/"
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "./ncurses.py"

#Second Tab
# Start reverse ssh
SESSION_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.addSession)
TERMINAL_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.terminalIdsForSessionId $SESSION_ID )
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "while true; do ssh -R 19999:localhost:22 myhomemachine.ccoffey.ie ; done"
qdbus org.kde.yakuake /yakuake/tabs  setTabTitle $SESSION_ID "reverse"

#Third Tab
# Start split tab for two hosts
SESSION_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.addSession)
TERMINAL_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.terminalIdsForSessionId $SESSION_ID )
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "ssh host1 -i ~/.ssh/somekey" 
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "df -m"
qdbus org.kde.yakuake /yakuake/tabs  setTabTitle $SESSION_ID "c01"
TERMINAL_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.splitTerminalTopBottom $TERMINAL_ID)
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "ssh host2 -i ~/.ssh/somekey""
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "df -m"
                                                                         


#Fourth Tab
# Start root shell 
SESSION_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.addSession)
TERMINAL_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.terminalIdsForSessionId $SESSION_ID )
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal $TERMINAL_ID "su -"
qdbus org.kde.yakuake /yakuake/tabs  setTabTitle $SESSION_ID "local root"

#Fifth Tab
#Start local empty shell 
SESSION_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.addSession)
TERMINAL_ID=$(qdbus org.kde.yakuake /yakuake/sessions org.kde.yakuake.terminalIdsForSessionId $SESSION_ID )
qdbus org.kde.yakuake /yakuake/tabs  setTabTitle $SESSION_ID "local"

And this is how it looks:
yakuake_example

Playing around with Python, Dbus and mpris

mpris is a standard method for communicating with media players through dbus.

I had some problems trying to find documentation for working with it through python.

The first thing I did was use dbus-monitor and qdbusviewer-qt4 to generate and watch some events:

dbus-monitor output to find state:

method call sender=:1.1584 -> dest=org.mpris.MediaPlayer2.nuvolaplayer serial=288 path=/org/mpris/MediaPlayer2; interface=org.freedesktop.DBus.Properties; member=Get
   string "org.mpris.MediaPlayer2.Player"
   string "PlaybackStatus"
method return sender=:1.1668 -> dest=:1.1584 reply_serial=288
   variant       string "Stopped"

The Python codes which does the same is

import dbus
bus = dbus.SessionBus()
proxy = bus.get_object("org.mpris.MediaPlayer2.nuvolaplayer", "/org/mpris/MediaPlayer2")
properties_manager = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
properties_manager.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')
->dbus.String(u'Playing', variant_level=1)

And the state is returned.

dbus-monitor output for PlayPause:

method call sender=:1.1584 -> dest=org.mpris.MediaPlayer2.nuvolaplayer serial=682 path=/org/mpris/MediaPlayer2; interface=org.mpris.MediaPlayer2.Player; member=PlayPause
method return sender=:1.1668 -> dest=:1.1584 reply_serial=682
signal sender=:1.1668 -> dest=(null destination) serial=383 path=/org/mpris/MediaPlayer2; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
   string "org.mpris.MediaPlayer2.Player"
   array [
      dict entry(
         string "CanPlay"
         variant             boolean false
      )
   ]
   array [
   ]
signal sender=:1.1668 -> dest=(null destination) serial=384 path=/org/mpris/MediaPlayer2; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
   string "org.mpris.MediaPlayer2.Player"
   array [
      dict entry(
         string "CanPause"
         variant             boolean true
      )
   ]
   array [
   ]
signal sender=:1.1668 -> dest=(null destination) serial=385 path=/org/mpris/MediaPlayer2; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
   string "org.mpris.MediaPlayer2.Player"
   array [
      dict entry(
         string "PlaybackStatus"
         variant             string "Playing"
      )
   ]
   array [
   ]

Extra code on the above to do this:

event_manager = dbus.Interface(proxy, 'org.mpris.MediaPlayer2.Player')
event_manager.PlayPause()
event_manager.Next()

How do we build the above code? i.e. the next example line is variable:

proxy = bus.get_object("org.mpris.MediaPlayer2.nuvolaplayer", "/org/mpris/MediaPlayer2")

We can get a list of object in dbus which have mpris in the name:

for names in bus.list_names():
   ....:     if "mpris" in names:
   ....:         print names
->org.mpris.MediaPlayer2.nuvolaplayer

Lets’s start a clean slate:

for names in bus.list_names():
    if "mpris" in names:
        print names

->org.mpris.MediaPlayer2.spotify
->org.mpris.MediaPlayer2.nuvolaplayer

Create a new proxy for spotify:

proxy_new = bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
#The string /org/mpris/MediaPlayer2 is a constant.
#And let's give spotify an action
event_manager = dbus.Interface(proxy_new, 'org.mpris.MediaPlayer2.Player')
event_manager.PlayPause()
#And then query the actions:
properties_manager = dbus.Interface(proxy_new, 'org.freedesktop.DBus.Properties')

properties_manager.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')
->dbus.String(u'Paused', variant_level=1)
event_manager.PlayPause()
properties_manager.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')
->dbus.String(u'Playing', variant_level=1)

properties_manager.Get('org.mpris.MediaPlayer2.Player', 'Metadata')
->dbus.Dictionary({dbus.String(u'xesam:artist'): dbus.Array([dbus.String(u'Fear Factory')], signature=dbus.Signature('s'), variant_level=1), dbus.String(u'xesam:album'): dbus.String(u'Demanufacture', variant_level=1), dbus.String(u'mpris:artUrl'): dbus.String(u'file:///home/ccoffey/.cache/nuvolaplayer/album_art.6', variant_level=1), dbus.String(u'mpris:trackid'): dbus.String(u'1', variant_level=1), dbus.String(u'xesam:title'): dbus.String(u'H-K (Hunter-Killer)', variant_level=1)}, signature=dbus.Signature('sv'), variant_level=1)

track_info = properties_manager.Get('org.mpris.MediaPlayer2.Player', 'Metadata')
print track_info["xesam:artist"][0] +  " - " + track_info["xesam:title"] + " (" + track_info["xesam:album"] + ")"
->Slayer - Read Between the Lies (South of Heaven)

systemd unit for Xvfb

I decided to clean up some of the methods for running services. I beleive anyting on a host needs should run itself after a reboot.

Instead of launching manually, I decided it’s time to write a systemd unit.

Files available here

This will launch Xvfb on :1 and then run dbus and pulseaudio.
We need these for working with sound and mpris controls later on.

AVRCP with bluez5 on ArchLinux Arm (rpi)

The only references to AVRCP with bluez 5 was on the Tizen documentation
They use a tool called bluetooth-player. This is not included in any of the standard packages.
To get AVRCP tests out of the way, we need these tools. Fortunately the upstream package contains these tools.

Get latest bluez code from the bluez site. In this instance 5.24.

Confire th build with some sane options:
./configure --enable-experimental --disable-cups
make
make install

./tools/bluetooth-player now works:
[root@alarmpi ~]# ./Downloads/bluez-5.24/tools/bluetooth-player
[NEW] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 [default]
[bluetooth]# list
Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 [default]
[bluetooth]# show
Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0
Repeat: off
Shuffle: off
Status: playing
Position: 0x01ff37
Title: I Cover the Waterfront
Duration: 0x03c69f
Album: At Mister Kelly's
TrackNumber: 0x000000
Artist: Sarah Vaughan
NumberOfTracks: 0x000000
Genre:

[bluetooth]# next
Attempting to jump to next
Next successful
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 Title: Double-O
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 Duration: 0x0288f9
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 Album: The Complete Atomic Basie
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 TrackNumber: 0x000000
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 Artist: Count Basie
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 NumberOfTracks: 0x000000
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 Genre:
[CHG] Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 Position: 0x00004f
[bluetooth]# show
Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0
Repeat: off
Shuffle: off
Status: playing
Position: 0x00004f
Title: Double-O
Duration: 0x0288f9
Album: The Complete Atomic Basie
TrackNumber: 0x000000
Artist: Count Basie
NumberO

Let’s go further and try and control in a non interactive way, using mpris-proxy
[root@alarmpi ~]# mpris-proxy
org.bluez appeared
Bluetooth Adapter /org/bluez/hci0 found
Bluetooth Player /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/player0 found
Player org.mpris.MediaPlayer2.Samsung_Galaxy_Tab_2 created
Bluetooth Transport /org/bluez/hci0/dev_6C_F3_73_22_8E_1F/fd0 found

In another shell:
[root@alarmpi ~]# export DISPLAY=:1
[root@alarmpi ~]# dbus-send --print-reply --session --dest=org.mpris.MediaPlayer2.Samsung_Galaxy_Tab_2 /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' string:'Metadata'
method return sender=:1.1 -> dest=:1.2 reply_serial=2
variant array [
dict entry(
string "xesam:title"
variant string "Everybody's Jumpin'"
)
dict entry(
string "mpris:length"
variant int64 276085000
)
dict entry(
string "xesam:album"
variant string "Time Out"
)
dict entry(
string "xesam:trackNumber"
variant int32 0
)
dict entry(
string "xesam:artist"
variant array [
string "The Dave Brubeck Quartet"
]
)
dict entry(
string "xesam:genre"
variant array [
string ""
]
)
]

Skip a track:
[root@alarmpi ~]# dbus-send --print-reply --dest=org.mpris.MediaPlayer2.Samsung_Galaxy_Tab_2 /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Next
method return sender=:1.1 -> dest=:1.4 reply_serial=2

Next tasks?
Investigate dbus and mdbus2

Useful reading on D-Bus and mpris commands

A2DP reception on Archlinux ARM

The first step in new bedside clock is getting a Raspberry Pi to receive audio streams.
The platform for this will be bluez5 and pulseaudio.

Install Arch as per the guide, armv6 is the target.
Install some extra packages:
pacman -S pulseaudio-alsa bluez bluez-libs bluez-utils alsa-tools alsa-utils xvfb xorg-server-xvfb xf86-video-fbdev xf86-video-vesa xorg-xinit

Set device class in /etc/Bluetooth/main.conf
Class = 0x200420

Get a very basic X env
We’ll need this as it’s a lot better for pulseaudio to have dbus.
Xvfb :1 -screen 0 1x1x8 &
export Display=:1
dbus-launch
pulsesudio --start

Start bluetooth, systsemctl start bluetooth
And then interact with the dongle:
bluetoothctl
>power on
>agent KeyboardOnly
>default-agent
>discoverable on
>pairable on

Then you need to pair to the phone. Currently this is a manual process, but we’ll automate that later.

And that’s most of what it takes:
image

Pretty quickly I realised the onboard sound was terrible, but I had expected this.
Time to switch over to the X-Fi usb soundcard. We’ve no need for the onboard sound, so for simplicity let’s disable it:
/etc/modules-load.d/raspberrypi.conf:
bcm2708-rng
#snd-bcm2835

Quick reboot and we see:
[root@alarmpi ~]# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Pro [Sound Blaster X-Fi Go! Pro], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0

I tested this playing music overnight and it still worked the next day so I am going to count this as stable.

Next tasks, get working on AVRCP. This might take a bit longer.

IMG_20141028_234205

Beside Alarm Clock 2

A title with 2 at the end suggests there was something before now.
This is true.
Whilst on my Honeymoon is Copenhagen I saw this item in a department store, Illums Bolighus;

image

http://www.areaware.com/collections/50-and-under/products/iphone5-alarm-dock

I quite liked the idea but thought “this would be great with a qi charger and Bluetooth speakers”
As it is, it’s only function is to hide charger cable and “naturally” amplify the sound output.

After a bit of thought and some rushed work I ended up with this:

image

Which did everything I wanted, but was ugly as sin,

image

Contents were a cheap set of 5v speakers, cheap 5v Bluetooth receiver and cheap 5v qi charger on the front. So you see a pattern here?

It did and does continue to work, but there was one flaw. My phone, nexus 5, has an LCD screen and it doesn’t get very dark when displaying black.
For a bedside clock, I can’t use it to display the time as it’s too bright.

Step in the second model!

I have some ideas about what I want to achieve, but I am going to take it one step at a time.

The current build of materials so are:
Raspberry Pi B+
Edimax EW-7811UN WiFi stick
Asus Bt400 Bluetooth 4.0 stick.
Creative X-Fi Go! Pro
Spare micro SD card.