An Update: Seafile and Firejail

Here is an improved approach to running seafile within a firejail jail. Original Post can be found here.




_start() {
    /opt/seafile/seafile-server-latest/ start || exit $?
    /opt/seafile/seafile-server-latest/ start-fastcgi || exit $?

_stop() {
    /opt/seafile/seafile-server-latest/ stop
    /opt/seafile/seafile-server-latest/ stop

if [[ $1 == start ]]; then
    sleep inf &
    echo $child > /tmp/
    wait "$child"
elif [[ $1 == stop ]]; then
    kill -TERM $(cat /tmp/ 2>/dev/null
    echo "usage: $0 [start, stop]"
    exit 1

Systemd Unit File (/etc/systemd/system/seafile.service)


ExecStart=/usr/bin/firejail --profile=/etc/firejail/server.profile --name=seafile -- /opt/seafile/ start
ExecStop=/usr/bin/firejail --join=${MAINPID} -- /opt/seafile/ stop
ExecStop=/usr/bin/firejail --shutdown=${MAINPID}


Sandboxing Seafile with firejail

See Update: An Update: Seafile And Firejail


What is firejail?

Firejail is a security sandbox. It uses Linux namespaces, the same technology containers are based on. However, unlike containers, which use a separate root filesystem, firejail works on the system’s existing filesystem. For more info checkout the README and the FAQ.

What is Seafile?

Seafile is an open source cloud storage with file encryption and group sharing. Think Dropbox, Google Drive, etc…

Getting Started

I’ll assume already have Seafile and Firejail installed. My installation lives in /opt/seafile and therefore the following examples use this path, adjust as necessary.

If you are not running seafile as non-privileged user, you should be. Create a non-privileged user and update the permission in /opt/seafile accordingly.

You’ll also need to grab and place the python script in your system’s PATH. Personally, I like to use /opt/bin for scripts.

What is Waiter?

Waiter is a dead simple process manager. Firejail terminates when the called process exits. Since the seafile startup/shutdown script forks children and exits, filejail will exit as soon as  seafile is started. This is problematic, because all processes launched under firejail session are terminated. Basically $ firejail /opt/seafile/seafile-server/latest/ start does not work.

This was something I quickly hacked together, because I couldn’t find another tool that fit the bill. I tried Supervisord, but it lacked dependencies (seafile needs to start before seahub) and was unable to stop the launched processes due to forking and exiting.

Pull requests are welcomed and if there is a better tool, please let me know!


Waiter Configuration: /etc/waiter/seafile.conf

start = /opt/seafile/seafile-server-latest/ start
stop = /opt/seafile/seafile-server-latest/ stop

start = /opt/seafile/seafile-server-latest/ start-fastcgi
stop  = /opt/seafile/seafile-server-latest/ stop

Systemd Service Unit: /etc/systemd/system/seafile.service


ExecStart=/usr/bin/firejail --profile=/etc/firejail/server.profile --name=seafile -- /opt/bin/ -c ${CONFIG}
ExecStop=/usr/bin/firejail --shutdown=${MAINPID}



Starting Seafile

Reload Systemd:

$ systemctl daemon-reload

Start Seafile:

$ systemctl start seafile
$ systemctl status seafile


$ firejail --tree

Closing Thoughts

Seafile is just one example. I am hoping to use this approach to sandbox other application such as Asterisk and gogs.

EL7 – Loading a Network Module during Install


Last night I tried to perform a net-install on a system that uses the nvidia forcedeth network module. I quickly discovered that Redhat no longer includes the module in the EL7 kernel. No worries, I would simply compile the module, copy it to flash drive, load it into the kernel, and be on my way. Well, that didn’t go as planned.

After loading the module, I attempted to the install. After selecting my desired language, I was presented with an anaconda python traceback which prevented the installer from proceeding.

Traceback (most recent call first):
  File "/usr/lib64/python2.7/site-packages/pyanaconda/", line 593, in nm_device_setting_value
    raise SettingNotFoundError(name)

  File "/usr/lib64/python2.7/site-packages/pyanaconda/ui/gui/spokes/", line 236, in __init__
    uuid = nm.nm_device_setting_value(device.get_iface(), "connection", "uid")

The installer appeared to be crashing because there wasn’t a NetworkManager connection for the Ethernet device. A connection can easily be added using nmclient.

$ nmcli connection add type ethernet ifname enp0s10 


Objective: To perform a net-installation, with an unsupported network adapter.

Assumptions: These steps assumes that you have a compiled kernel module for your Ethernet adapter that is compatible with the EL7 kernel shipped with the installer.


  1. copy the network adapter kernel module to a usb stick
  2. boot the net install media
  3. switch from the GUI installer to TTY1 by pressing Ctrl+Alt+F1
  4. Enter the “shell” window in tmux (press ctrl+b 2)
  5. Mount the usb-stick and load the module
    $ mkdir /mnt/usb
    $ mount /dev/sdb1 /mnt/usb
    $ insmod /mnt/usb/module.ko
  6. verify the network Interface was created and note its name
    $ ip link
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    2: enp0s10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
        link/ether 00:19:66:bf:9a:d8 brd ff:ff:ff:ff:ff:ff
  7. Create a connection in Network Manager
    $ nmcli connection add type ethernet ifname enp0s10 

    Replace enp0s10 with the name of your network interface

  8. Switch back to the GUI Installer by pressing Ctrl+Alt+F6

At this point, you can continue with the installation. If necessary, you can use the installer’s network configuration tool to set a static ip or change other network related settings.

After installation, you should install the package that supports your network adapter.

The elrepo repository provides a number of kernel modules that do not ship with the EL kernel.

building tmux 1.9a statically

At work, I use tmux on daily basis. Unfortunately, the version shipped in epel (extra packages for enterprise linux) is slightly dated, v1.6. I didn’t want to update tmux system wide as this would impact other users. A single statically linked library that I could store in my home area would be perfect.

I discovered the build process was bit of a challenge. Although I found answers to my problems, they were not in a centrally located. Therefore, I am documenting my steps below.

These steps allowed me to build tmux 1.9a. These same steps should work for newer versions, however at some point, these steps may break. Additionally, these steps were performed on RHEL6 – they should apply to nearly any distro.

  1. Install glibc-static:
    $ yum install glibc-static
  2. Download libevent-2.0.21-stable
    $ ./configure --prefix=/tmp/local --disable-shared
    $ make
    $ make install
  3. Download ncurse 5.9
    $ ./configure --prefix=/tmp/local --with-default-terminfo-dir=/usr/share/terminfo  --with-terminfo-dirs="/etc/terminfo:/lib/terminfo:/usr/share/terminfo"
    $ make
    $ make install
  4. Download Tmux Source
    $ ./configure --enable-static CFLAGS="-I/tmp/local/include -I/tmp/local/include/ncurses" LDFLAGS="-L/tmp/local/lib -L/tmp/local/include -L/tmp/local/include/ncurses" LIBEVENT_CFLAGS="-I/tmp/local/include" LIBEVENT_LIBS="-L/tmp/local/lib -levent"
    $ make -j5
    $ cp tmux /home/$USER/bin


Troubleshooting / Build Issues:
Continue reading

Android: Email deletion does not propagate to IMAP server

Problem: When deleting a message in the Email client, the deletion does not propagate to the IMAP server.

Device: Motorola Moto X
OS: Android 4.4
App: Email 4.4-51
Server: CentOS 6.5 – dovecot 2.0.9 (version that ships with centos)

Solution: Upgrade dovecot from 2.0.9 to 2.2.10

There has been an open android bug (1029) regarding this issue since 2008.

Comment 86 on Android bug 1029 provides a possible explanation for what is happening:

“I believe the root cause is that the route Google and others have gone with IMAP is to try and make it perform like Exchange, where it stays connected and “pushes” emails immediately when they arrive. The big problem with this is that while IMAP was designed to work this way, it assumed
1. that there would be only one client connected to an account at a time, and
2. the internet connection and IP address of connected clients would be constant, not changing as you move between wifi hotspots and cell towers.
So, most mail servers queue up the “delete” commands until the user has disconnected. If we disable the ill-conceived “PUSH” crap, the mail program SHOULD operate like POP – connect, sync, disconnect. But either it is failing to disconnect, or a second connection (such as a home desktop you left logged in) is keeping your account open. K-9 seems to deal with this better by “remembering” what messages have been deleted and not restore them when it sees that they are still on the server.


Meanwhile, you can make the problem *somewhat* better if you set your IMAP mail to check less often than the timeout on the server (usually 30 minutes). That way, the hanging connection times out between mail checks, and the server will perform the deletions you’ve asked for.”

When reviewing the dovecot changelog, I did not see any changes that seemed related to this problem. I suspect (I have no real knowledge of IMAP and/or dovecot) that dovecot has changed the way it handles delete commands and thus the problem has been worked around. If anyone has information about what changed in dovecot please share!

Sadly, my solution only works if you can update or switch your mail server software.

ATrpms provides dovecot-2.2.10 for Centos6:

Configuring a Router with Comcast Business Class

For the last two months, I used NAT to make use of my 5 static ips. I wanted to do away with NAT and create another network for public ip space. My goal was simple; a router that was attached to two networks: a RFC1918 LAN and a LAN for my public ip block; however this would not be an easy task.

Desired Network Topology:

                       /-------------------\          ____Host A_____
                       +        LAN1       + ------> | |
                       +  +         ----------------
+-----------------+          +--------+          /--------------------\
|      modem      | --> eth0 | router | eth2 --> + LAN2 ( +
|    |          +--------+          \--------------------/
| |


Ideally, the modem would be configured as bridge, with a default route via eth0 to comcast’s gateway. The routing should be simple. Sadly, comcast made this way more difficult.

The modem, a SMCD3G (cable gateway), is a switch and router with modem functionality. In theory, it should be possible to disable the router/switch functionality and turn it into a bridge, however comcast refuses to provision the modems in this manner.

The modem has two interfaces, a coax interface and switched virtual interface (svi). The coax interface is provisioned automatically and is used to communicate with comcast’s gateway. The svi is configured with two network blocks: (RFC1918) and (static ip block). Unfortunately, it is not possible to see how the modem is configured, therefore the above is an educated guess, based on the network traffic I saw from the modem.

The modem’s configuration posed, a slight problem. The modem was configured with, but I wanted to assign to the router. After some trial and error, I figured out how to make this configuration work. The secret: proxy arp.

General Configuration / Theory

Disable the firewall/router functionality on the modem. The modem configuration can be accessed via; the username is ‘cusadmin’ and the password is ‘highspeed’. Comcast follows similar steps when you request them to bridge your modem.
Note, this configuration will not disable the routing and switching functionality of the modem.

Under …

Firewall -> Firewall Options
  * [checked] Disable Firewall for True Static IP Subnet Only
  * [checked] Disable Gateway Smart Packet Detection
  * [unchecked] Disable Ping on WAN Interface

Firewall -> Port Configuration
  * [checked]  Disable all Port Forwarding rules

Firewall -> 1-to-1 Network Address Translation
  * [checked] Disable all

LAN -> IP Setup
  * [unchecked] Enable LAN DHCP

Insure there are no static routes configured on the modem; filtering is disabled; a dmz has not been configured; and website blocking is disabled.

Assign to router:eth1 (lan1) and assign (lan2) to router:eth2

To get traffic between the modem and router, router:eth0 needs to be configured. Assign to router:eth0 and set the default gateway to (the modem’s lan ip).

At this point, it should be possible to access the internet from the router. The src ip of packets going out eth0, will be Since is not routeable on the internet, the modem will SNAT to To fix this we need set the src ip of the default route, to match the ip that is configured on router:eth1 (

Example: ip route add default via src

A tool such as will show the external src ip of the host.
From your host run: curl lives on the modem, outside of LAN1, therefore a static route to via (modem) should be configured.
ip route add via
Note: overlapping network space is a poor configuration practice and should be avoided. In this case, it is the only way to configure the network.

The problem

Host A ( tries to ping Echo requests go out, but Host A never receives echo replies. A tcpdump of router:eth0, verifies the icmp traffic is leaving the router with the correct src/dst ips, but does not show any return/inbound traffic, except for the modem. From the tcpdump, one will see the modem is sending the following arp requests:

ARP, Request who-has tell, length 28
ARP, Reply is-at c4:39:3a:02:02:02 (oui Unknown), length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46

The router can reply to the request for and The router can not reply for addresses not configured. No other devices will respond to these arp requests, because the router and modem are the only devices on the layer 2 segment. Why does this happen? The modem’s svi has an ip address of The network is directly attached and is the final destination. Luckily, we can make use of proxy arp. Proxy arp will make the router respond to all arp requests.

Enable proxy arp on router:eth0.

Now, when HOST A ( pings icmp echo requests are sent and echo replies are received.

SNAT needs to be configured so hosts in LAN2 ( can communicate on the internet. Keep in mind, the address used for SNAT can’t be the router gateway (, modem gateway (, or any host addresses in LAN1.

Applying the above configuration to a Linux router

Make sure the modem has been configured appropriately (see above).

Router Configuration

# set ip addresses
router$ ip addr add dev eth0
router$ ip addr add dev eth1
router$ ip addr add dev eth2

# bring up interfaces
router$ ip link set eth0 up
router$ ip link set eth1 up
router$ ip link set eth2 up

# add routes
ip route add default via src # change src to the ip on eth1
ip route add via # route to the modem's IP

# enable proxy arp on eth0
sysctl net.ipv4.conf.eth0.proxy_arp=1

iptables -A POSTROUTING -s -o eth0 -j SNAT --to-source

pidgin: disconnect specific accounts from cron

At the end of the day, I needed a way to automatically disconnect from my personal instant messaging accounts (jabber, aim), while staying connected to my work jabber server. This could be done manually ( Accounts -> Manage Accounts -> Uncheck $AccountName), however I tend to be extremely forgetful and often stay logged in.

With the aid of python, I wrote a script that will disconnect specific libpurple/pidgin accounts. Modify the accounts list in the script to reflect that accounts you want to disconnect.

This script was a bit tricky, because purple-remote uses dbus to communicate with pidgin/libpurple. Since cron sets up a bare minimum environment, we need to detect the active dbus session and set the following dbus environmental variables: DBUS_SESSION_BUS_ADDRESS, DBUS_SESSION_BUS_PID, DBUS_SESSION_BUS_WINDOWID. With these environment variable set, we identify the account id for each account you specified, and then tell libpurple to disable that account.


import subprocess
import sys
import os
import re

accounts=[{'name':'pyther24', 'protocol':'prpl-aim' },
          {'name':'', 'protocol':'prpl-jabber'},
          {'name':'', 'protocol':'prpl-jabber'}]

def check_pid(pid):
    """ Check For the existence of a unix pid. """
        os.kill(pid, 0)
    except OSError:
        return False
        return True

def main():
    if len(sys.argv) > 1 and sys.argv[1] == "login":

    # We need to get the dbus environment variable for purple-remote to work
    # These environment variables are stored in $HOME/.dbus/session-bus
    # there may be old sessions stored in .dbus/session-bus, we need to determine
    # which session to use

    env = os.environ.copy()
    # Fetch all sessions in $HOME/.dbus/session-bus
    for filename in os.listdir(dbus_dir):
        filepath = os.path.join(dbus_dir,filename)
        with open(filepath, 'r') as f:
            for line in f.readlines():
                if re.match('^#',line):

    # Determine which dbus session to use
    # Check pid to see if it is alive
    # If pid is alive see if the name contains dbus-daemon
    for s in dbus_sessions:

        if os.path.exists('/proc/{0}'.format(pid)):
            with open('/proc/{0}/cmdline'.format(pid)) as f:
                cmdline = f.readline().strip()
            # DBUS process must be dead


    # Get account ids for each account define above
    # Disable / Enable each account

    for a in accounts:
        p = subprocess.Popen(['purple-remote',


        # Store ID
            id = int(p.communicate()[0].strip())
        except ValueError:
            print('Could fetch id for account {0}'.format(a['name']))

        # Disable Account
        p =['purple-remote',

if __name__ == '__main__':

nxt2004 firmware

I needed to obtain the firmware for my KWorld ATSC 115 which uses the nxt2004 demodulator. The kernel module saa7134 requires the nxt2004 firmware to operate correctly. In the past the process to obtain the firmware was easy, but now that avermedia-usa now longer makes available the driver, it was much harder to track down the need firmware file.

The problem:

m2n:~ $ /tmp/get_dvb_firmware nxt2004
--2012-07-06 22:51:22--
Connecting to||:80... connected.
HTTP request sent, awaiting response... 404 Not Found
2012-07-06 22:51:22 ERROR 404: Not Found.

wget failed - unable to download firmware at /tmp/get_dvb_firmware line 662.

I figured I could just search for or dvb-fe-nxt2004.fw and be in business. Unfortunately, it was not that easy as it took me over an hour to find the file.

I came across linux-firmware-nonfree_1.11_all.deb which contained nxt2004.fw

Since it was so hard to find the firmware file I decided to mirror it along with the original deb package that I got it from.


To Install:

  1. wget
  2. cp dvb-fe-nxt2004.fw /lib/firmware
  3. reload appropriate module or reboot
  4. check out dmesg to confirm firmware was loaded

Hope this has helped someone…

Password Manager

Best practice states that passwords should contain letters (mixed case), numbers, and symbols, should be at least 8 characters in length, and should never be used twice. However, this isn’t very practical! How are you suppose to remember a different password for each site you have an account for?

I have been using 4 different password for my various accounts. This method has been working moderately well, but from a security standpoint, it’s suicide. I wanted to use a random password for each of my accounts. But, how would I ever remember all my passwords? A password manager, of course!

What I needed:

  • Passwords stored in an encrypted file
  • Master password to unlock the encrypted file
  • View passwords from the cli/ssh
  • Include additional information such as Security Questions and Answers
  • Integrated support for Firefox

What I used…

Vim Outliner

Vim Outliner is an outline processor. A screenshot is worth a thousand words.


By default when you save the file it will be a simple tab delimited text file. Vim, however, supports encryption. First, you need to set the encryption method by typing :setlocal cm=blowfish. If you want Blowfish to be the default encryption method for vim add the setlocal command to ~/.vimrc. Next, to encrypt the file, type :X. You will be prompted to set a password. Finally, save the file. When you open the file, you will be prompted for the password. If you fail to enter the right password you will see garbage characters.

Firefox Integration  (Mozilla-gnome-keyring)

Mozilla-gnome-keyring allows Firefox to store passwords and form logins in gnome-keyring. Gnome-keyring is much more secure than the default password manager in Firefox. The mozilla keyring must be unlocked to add / retrieve passwords. You can define how long the keyring should remain unlocked for (never, 15 minutes, 60 minutes, etc…). On my desktop I unlock the keyring for 60 minutes, but on my laptop I only unlock it for 10. When logging into a site, Firefox still prompts to  “Remember the Password”.  If you let Firefox remember the password, the password automatically gets recorded in the keyring.


I have been using this solution for about a month and it has fit my needs perfectly. I updated most of my accounts so they each have an unique password such as Dmqngi8ZoPyO or XGVoBOmd7Gar. Passwords are being stored twice: in the password file and in gnome-keyring. Since mozilla-gnome-keyring takes care of adding the passwords into gnome-keyring when I login to a site, I only have to record/update my passwords in the encrypted text file. In the rare case that I’m not at my computer, and I need a password, I simply ssh into my server and open the password file in vim.

Although the password file and keyring is encrypted it is still subject to a brute force attack. Make sure to use a strong master password, the longer the better. I would suggest at least 20 characters. In 2009, it would take a super computer 1.5hrs to crack an 8 character (alpha only; lower-case) password, but it would take 631 Billion years to crack a 20 character (alpha only; lower-case) password. Remember, as computers advance these times will decrease. And of course, a key logger could compromise the master password nearly instantly.