Welcome to zewaren.net. This site presents myself and mostly archives the solutions to some problems I once had.

How to write a table into a PDF using Python and Reportlab

Not so frequently asked questions and stuff: 


This code will output a table into a lanscaped A4 PDF, with word wrap enabled.


from reportlab.lib import colors
from reportlab.lib.pagesizes import A4, inch, landscape
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet

doc = SimpleDocTemplate("test_report_lab.pdf", pagesize=A4, rightMargin=30,leftMargin=30, topMargin=30,bottomMargin=18)
doc.pagesize = landscape(A4)
elements = []

data = [
["Letter", "Number", "Stuff", "Long stuff that should be wrapped"],

#TODO: Get this line right instead of just copying it from the docs
style = TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'),
                       ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
                       ('BOX', (0,0), (-1,-1), 0.25, colors.black),

#Configure style and word wrap
s = getSampleStyleSheet()
s = s["BodyText"]
s.wordWrap = 'CJK'
data2 = [[Paragraph(cell, s) for cell in row] for row in data]

#Send the data and build the file

Here's the output.

If you use the default word wrap configuration, words that are too long won't be wrapped:

s.wordWrap = 'LTR'


How to use user/password authentication with OpenVPN on FreeBSD

Not so frequently asked questions and stuff: 

Image The FreeBSD logo


Configure the system

To use a textfile password database, install pam_pwdfile:

make -C /usr/ports/security/pam_pwdfile install clean

Generate your password using OpenSSL or anything else.

openssl passwd -crypt superpassword

Populate /usrl/local/etc/ovpn/passwd with your user/password database.


Configure pam in /etc/pam.d/openvpn:

auth            required        /usr/local/lib/pam_pwdfile.so pwdfile=/usr/local/etc/ovpn/passwd
account         required        pam_permit.so
session         required        pam_permit.so
password        required        pam_deny.so

To test your PAM setup, install pamtester and use it.

# make -C /usr/ports/security/pamtester install clean

# pamtester -v openvpn guest1 authenticate
pamtester: invoking pam_start(openvpn, guest1, ...)
pamtester: performing operation - authenticate
pamtester: successfully authenticated

On your server configuration file, add:

plugin /usr/local/lib/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
duplicate-cn   #add this if you want to have multiple connections using the same certificate. Otherwise, they'all get the same IP and you'll have problems.

On your clients, add:



Instal pam_ldap.

make -C /usr/ports/security/pam_ldap install clean

Create a configuration file, for example: /usr/local/etc/ovpn/ldap.conf, and populate it as usual.

host ldap-server.example.com
base dc=example,dc=com
ldap_version 3
binddn cn=pam,ou=services,dc=example,dc=com
bindpw DY5K82cG5avkCkz
port 389
scope sub
bind_timelimit 10
bind_policy soft
pam_filter objectclass=inetOrgPerson
pam_login_attribute uid
pam_password exop
nss_base_passwd         ou=people,dc=example,dc=com
nss_base_group          ou=group,dc=example,dc=com

To have your users be able to authenticate using both the text files and LDAP, use a configuration like this one:

auth            sufficient      /usr/local/lib/pam_ldap.so no_warn try_first_pass config=/usr/local/etc/ovpn/ldap.conf
auth            required        /usr/local/lib/pam_pwdfile.so pwdfile=/usr/local/etc/ovpn/passwd
account         required        pam_permit.so
session         required        pam_permit.so
password        required        pam_deny.so

Otherwise, create your PAM configuration as usual.

How to verify DKIM signatures manually

Posted 2014-10-31.
Image Image Image


You're setting up DKIM on your SMTP servers. You'd like to be able to check if your emails are signed correctly.

Using perl

Use Mail::DKIM::Verifier.

As per the documentation:

use Mail::DKIM::Verifier;

# create a verifier object
my $dkim = Mail::DKIM::Verifier->new();

# read an email from a file handle

# or read an email and pass it into the verifier, incrementally
while ()
    # remove local line terminators

    # use SMTP line terminators

# what is the result of the verify?
my $result = $dkim->result;

# there might be multiple signatures, what is the result per signature?
foreach my $signature ($dkim->signatures)
    print "signature identity: " . $signature->identity . "\n";
    print "verify result: " . $signature->result_detail . "\n";

# the alleged author of the email may specify how to handle email
foreach my $policy ($dkim->policies)
    die "fraudulent message" if ($policy->apply($dkim) eq "reject");


perl dkim_checker.pl 

Using PHP

Download php-dkim and phpseclib into the same folder. Write a sample code to use the classes:


php dkim_checker.php  Array
            [0] => Array
                    [status] => pass
                    [reason] => Success!



php dkim_checker.php  Array
            [0] => Array
                    [status] => permfail
                    [reason] => signature did not verify (example.com key #0)



Using python

Install pydkim.

cd dkimpy-0.5.4 && python setup.py install



Serve Clonezilla with PXE using TFTP on a UEFI computer

Package icon pxe-clonezilla-live-uefi.zip254.41 KB


Fetch the required files

What you need:

  • Clonezilla's zip file: clonezilla-live-20140915-trusty-amd64.zip
  • syslinux-6.03.zip Download it on kernel.org.
  • pxelinux's config file (pxelinux.cfg/default):
    DEFAULT Clonezilla-live
    LABEL Clonezilla-live
     MENU LABEL Clonezilla Live (Ramdisk)
     KERNEL vmlinuz
     APPEND initrd=initrd.img boot=live config noswap nolocales edd=on nomodeset ocs_live_run="ocs-live-general" ocs_live_extra_param="" ocs_live_keymap="" ocs_live_batch="no" ocs_lang="" vga=788 nosplash noprompt fetch=tftp://[INSERT YOUR IP HERE]/filesystem.squashfs

Extract PXELINUX's files

Extract the following files into the current folder.


Extract clonezilla's files

Extract the required files from the zipfile in the current folder.


Check that you have all the required files

Your folder should look like this:



Edit pxelinux.cfg and insert your IP. You can also preset parameters (see http://clonezilla.org/show-live-doc-content.php?topic=clonezilla-live/doc/99_Misc.


Set up your DHCP and TFTP servers and boot syslinux.efi.


How to allow specific IPs to connect to a lighttpd webserver, and have the others use mod_auth

Not so frequently asked questions and stuff: 



You protect your web applications using lighttpd's mod_auth.

You'd like to have some users (say, your office) access the server without having to log in.


Filter the configuration based on your users' IPs.

$HTTP["remoteip"] != "" {
    auth.backend = "ldap"
    auth.backend.ldap.hostname = ""
    auth.backend.ldap.base-dn  = "ou=people,dc=example,dc=com"
    auth.backend.ldap.filter   = "(&(uid=$)(memberOf=cn=some_group,ou=sgroups,dc=example,dc=com))"
    auth.backend.ldap.bind-dn  = "cn=our_lighttpd_user,ou=services_ro,dc=example,dc=com"
    auth.backend.ldap.bind-pw  = "qdkdfsgbxxcvbiuoisj"
    auth.backend.ldap.allow-empty-pw = "disable"
    auth.require = ( "" => (
            "method"  => "basic",
            "realm"   => "Our superb realm",
            "require" => "valid-user"

If you're using a reverse proxy, use mod_extforward to retrieve the correct IPs.

server.modules += ( "mod_extforward" )
extforward.forwarder = ("" => "trust")


How to apply FreeBSD10's autotools patches manually

Not so frequently asked questions and stuff: 

The FreeBSD logo Image


You're manually building something that require autotools on FreeBSD 10.

You're used to the port system patching your autotools files, and you want to apply theses patches to your current folder.


Choose your poison:


foreach f (`find . -type f \( -name config.libpath -o -name config.rpath -o -name configure -o -name libtool.m4 -o -name ltconfig -o -name libtool -o -name aclocal.m4 -o -name acinclude.m4 \)`)
    sed -i.fbsd10bak -e 's|freebsd1\*)|freebsd1.\*)|g' -e 's|freebsd\[12\]\*)|freebsd[12].*)|g' -e 's|freebsd\[123\]\*)|freebsd[123].*)|g' -e 's|freebsd\[\[12\]\]\*)|freebsd[[12]].*)|g' -e 's|freebsd\[\[123\]\]\*)|freebsd[[123]].*)|g' $f
    touch -f -mr $f.fbsd10bak $f
    rm $f.fbsd10bak


for f in `find . -type f \( -name config.libpath -o -name config.rpath -o -name configure -o -name libtool.m4 -o -name ltconfig -o -name libtool -o -name aclocal.m4 -o -name acinclude.m4 \)`; do
    sed -i.fbsd10bak -e 's|freebsd1\*)|freebsd1.\*)|g' -e 's|freebsd\[12\]\*)|freebsd[12].*)|g' -e 's|freebsd\[123\]\*)|freebsd[123].*)|g' -e 's|freebsd\[\[12\]\]\*)|freebsd[[12]].*)|g' -e 's|freebsd\[\[123\]\]\*)|freebsd[[123]].*)|g' $f
    touch -f -mr $f.fbsd10bak $f
    rm $f.fbsd10bak

How to extract the windows license key from a Lenovo T440p recovery CDs.

Not so frequently asked questions and stuff: 

Image Image


You own a Lenovo T440p with Windows 7 preinstalled, but you have Windows 8 recovery CDs. Unfortunately, your laptop doesn't include a CD drive.

You'd like to extract the Windows License key from the CD and install your computer yourself with a custom image (I mean, who would use a preinstalled Windows image with Norton Security for anything serious).

Chapter 1: extract the key from the CD

Five CDs were provided by Lenovo:

  • (1) Windows 8 Recovery Media for Windows 8 Products (Disk 1 of 2)
  • (1) Windows 8 Recovery Media for Windows 8 Products (Disk 2 of 2)
  • (2) Operating System Recovery Disk Windows 8 Pro (OEM Activation 3.0 Required) (Disk 1 of 2)
  • (2) Operating System Recovery Disk Windows 8 Pro (OEM Activation 3.0 Required) (Disk 2 of 2)
  • (1) Windows 8 Recovery Media for Windows 8 Products (Disk 1 of 1)
  • Office pro 2013

My guess is that we'll find what we need in the largest file contained in the CDs, which should be a Windows installation image.

This file is: M8S4AUS.swm. This looks like a split WIM file.

Let's merge it and open it.

E:\RECOVERY>imagex /export /ref M8S4AUS*.swm M8S4AUS.swm 1 exported.wim "exported"

ImageX Tool for Windows
Copyright (C) Microsoft Corp. All rights reserved.
Version: 6.3.9600.16384

Exporting: [E:\RECOVERY\M8S4AUS.swm, 1] ->
[ 100% ] Exporting progress

Successfully exported image #1.
Total elapsed time: 15 min 49 sec
imagex /MOUNT exported.wim 1 c:\truc

ImageX Tool for Windows
Copyright (C) Microsoft Corp. All rights reserved.
Version: 6.3.9600.16384
Mounting: [E:\RECOVERY\exported.wim, 1] -> [c:\truc]...
[ 100% ] Mounting progress
Successfully mounted image.
Total elapsed time: 42 sec

Let's see what's inside.

E:\RECOVERY>dir c:\truc

 Volume in drive C has no label.
 Volume Serial Number is 2E2F-1418

 Directory of c:\truc

2014-10-08  18:30              .
2014-10-08  18:30              ..
2012-07-26  09:33              PerfLogs
2013-03-25  23:07              Program Files
2013-03-25  23:07              Program Files (x86)
2013-03-26  15:02              SWWORK
2013-03-25  23:03              Users
2013-03-26  00:15              Windows
               0 File(s)              0 bytes
               8 Dir(s)  22,738,112,512 bytes free

Bingo! This definitely looks like a Windows image.

Download and start ProduKey. Open the Select Source window and Load the product keys from external Windows directory.


Voilà, the associated Microsoft keys are extracted and displayed.


Chapter 2: extract the key from the BIOS

Unfortunately, we did chapter 1 for nothing, since the key R3C2N-HT63Q-F4RKH-KPP3R-3667Q is a generic key called: Windows® 8 Default Product Keys to be used with OEM Activation 3.0.

This means that the real key is embedded inside the computer's BIOS.

Download RWEverything, click on ACPI, and go to tab MSDM.

Your key should be here.


Enjoy your license.

How to install pecl-apd with PHP 5.5 on FreeBSD

Not so frequently asked questions and stuff: 


This was written on 2014-10-05.

Fetch source (revision was 297236 at the time).

# svn co http://svn.php.net/repository/pecl/apd/trunk

Apply the following patch:

diff --git a/php_apd.c b/php_apd.c
index 9d7f238..3b1fe7e 100644
--- a/php_apd.c
+++ b/php_apd.c
@@ -56,11 +56,19 @@
 #undef TRACE_ZEND_COMPILE /* define to trace all calls to zend_compile_file */
 ZEND_DLEXPORT zend_op_array* apd_compile_file(zend_file_handle* TSRMLS_DC);
 ZEND_DLEXPORT zend_op_array* (*old_compile_file)(zend_file_handle* TSRMLS_DC);
+#if PHP_VERSION_ID = 20020731
 ZEND_DLEXPORT void apd_execute_internal(zend_execute_data *execute_data_ptr, int return_value_used TSRMLS_DC);
 ZEND_DLEXPORT void (*old_execute)(zend_op_array *op_array TSRMLS_DC);
+ZEND_DLEXPORT void apd_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
+#if  ZEND_EXTENSION_API_NO >= 20020731
+ZEND_DLEXPORT void apd_execute_internal(zend_execute_data *execute_data_ptr, struct _zend_fcall_info *fci, int return_value_used TSRMLS_DC);
+ZEND_DLEXPORT void (*old_execute_ex)(zend_execute_data *execute_data TSRMLS_DC);
 ZEND_DLEXPORT void onStatement(zend_op_array *op_array);
@@ -337,7 +345,7 @@ char *apd_get_active_function_name(zend_op_array *op_array TSRMLS_DC)
        else {
-           switch (execd->opline->op2.u.constant.value.lval) {
+           switch (execd->opline->extended_value) {
            case ZEND_EVAL:
                funcname = estrdup("eval");
@@ -441,8 +449,8 @@ static void log_time(TSRMLS_D)
        if(utime || stime || rtime) {
             int lineno = 0;
             if(EG(active_op_array)) {
-               if(EG(active_op_array)->start_op) {
-                   lineno = EG(active_op_array)->start_op->lineno;
+               if(EG(start_op)) {
+                   lineno = EG(start_op)->lineno;
                } else if(EG(active_op_array)->opcodes) {
                    lineno = EG(active_op_array)->opcodes->lineno;
@@ -510,28 +518,46 @@ PHP_MINIT_FUNCTION(apd)
    ZEND_INIT_MODULE_GLOBALS(apd, php_apd_init_globals, NULL)
+#if PHP_VERSION_ID op_array;
    char *fname = NULL;
    fname = apd_get_active_function_name(op_array TSRMLS_CC);
        trace_function_entry(EG(function_table), fname, ZEND_USER_FUNCTION,
-       old_execute(op_array TSRMLS_CC);

If you don't, you'll have the following error output:
 cc -I. -I/root/apdgit/apd2/trunk -DPHP_ATOM_INC -I/root/apdgit/apd2/trunk/include -I/root/apdgit/apd2/trunk/main -I/root/apdgit/apd2/trunk -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /root/apdgit/apd2/trunk/php_apd.c  -fPIC -DPIC -o .libs/php_apd.o
/root/apdgit/apd2/trunk/php_apd.c:206:36: warning: format string is not a string literal (potentially insecure)
                fprintf(APD_GLOBALS(pprof_file), newStr);
/root/apdgit/apd2/trunk/php_apd.c:318:12: warning: assigning to 'char *' from 'const char *' discards qualifiers
                tmpfname = execd->function_state.function->common.function_name;
                         ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:322:15: warning: assigning to 'char *' from 'const char *' discards qualifiers
                                classname = Z_OBJCE(*execd->object)->name;
                                          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:329:15: warning: assigning to 'char *' from 'const char *' discards qualifiers
                                classname = execd->function_state.function->common.scope->name;
                                          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:340:31: error: no member named 'u' in 'union _znode_op'
                        switch (execd->opline->op2.u.constant.value.lval) {
                                ~~~~~~~~~~~~~~~~~~ ^
/root/apdgit/apd2/trunk/php_apd.c:444:29: error: no member named 'start_op' in 'struct _zend_op_array'
                                if(EG(active_op_array)->start_op) {
                                   ~~~~~~~~~~~~~~~~~~~  ^
/root/apdgit/apd2/trunk/php_apd.c:445:36: error: no member named 'start_op' in 'struct _zend_op_array'
                                        lineno = EG(active_op_array)->start_op->lineno;
                                                 ~~~~~~~~~~~~~~~~~~~  ^
/root/apdgit/apd2/trunk/php_apd.c:506:35: warning: 'memset' call operates on objects of type 'zend_apd_globals'
      (aka 'struct _zend_apd_globals') while the size is based on a different type 'zend_apd_globals *'
      (aka 'struct _zend_apd_globals *') [-Wsizeof-pointer-memaccess]
    memset(apd_globals, 0, sizeof(apd_globals));
           ~~~~~~~~~~~            ^~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:506:35: note: did you mean to dereference the argument to 'sizeof' (and multiply it
      by the number of elements)?
    memset(apd_globals, 0, sizeof(apd_globals));
/root/apdgit/apd2/trunk/php_apd.c:514:15: error: non-object type 'void (zend_op_array *)' is not assignable
        zend_execute = apd_execute;
        ~~~~~~~~~~~~ ^
/root/apdgit/apd2/trunk/php_apd.c:515:24: warning: incompatible pointer types assigning to 'void
      (*)(zend_execute_data *, struct _zend_fcall_info *, int)' from 'void (zend_execute_data *, int)'
        zend_execute_internal = apd_execute_internal;
                              ^ ~~~~~~~~~~~~~~~~~~~~
/root/apdgit/apd2/trunk/php_apd.c:544:64: error: too few arguments to function call, expected 3, have 2
        execute_internal(execute_data_ptr, return_value_used TSRMLS_CC);
        ~~~~~~~~~~~~~~~~                                              ^
/usr/local/include/php/Zend/zend_execute.h:62:1: note: 'execute_internal' declared here
ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, struct _zend_fcall_info *fci, int return_...
/usr/local/include/php/main/../main/php_config.h:6:19: note: expanded from macro 'ZEND_API'
# define ZEND_API __attribute__ ((visibility("default")))
/root/apdgit/apd2/trunk/php_apd.c:724:11: warning: assigning to 'char *' from 'const char *' discards qualifiers
        filename = zend_get_executed_filename(TSRMLS_C);
                 ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 warnings and 5 errors generated.
*** Error code 1

Configure and build:

# phpize
# ./configure
# make


# make install


zend_extension = "/usr/local/lib/php/20121212/apd.so"


# php -v
PHP 5.5.13 (cli) (built: Jun 11 2014 11:54:41)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2014, by Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
    with Advanced PHP Debugger (APD) v1.0.2-dev, , by George Schlossnagle

You'll find patched version of the project here:
Github: ZeWaren/pecl-apd (https://github.com/ZeWaren/pecl-apd).

How to create a certificate to send APNS notifications, manually using OpenSSL

Not so frequently asked questions and stuff: 


Create your key.

openssl genrsa -out truc.example.org.key 2048

Create the certificate request:

openssl req -new -key truc.example.org.key -out truc.example.org.csr

Upload the CSR to Apple, and get the resultant certificate.

Optionally convert the certificate to PEM format.

openssl x509 -inform der -in truc.example.org.cer -out truc.example.org.crt.pem

Check if you can connect to APNS with your credentials:

openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert truc.example.org.crt.pem -key truc.example.org.key

Use the key and certificate files for profit.

How to add HTTP Authorization to Synology's web interface

Not so frequently asked questions and stuff: 



This article is relevant to a DS412+ appliance.

You're using a small Synology server. You use the web interface for whatever purpose. (the one at http://nas.example.com:5001). You'd like to access this interface remotely, but you are afraid to open the port on your firewall since you don't trust the associated CGI scripts.


Add HTTP auth to httpd, so that any request must go through a password.

Create file /usr/syno/etc/httpd/passwd (here or in whatever location you fancy) and create your credential database.


Create file /etc/httpd/conf/extra/httpd-something.conf and configure httpd to serve on port 5002 the same content as port 5001, but with basic HTTP authentication.

Listen 5002

LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authn_file_module modules/mod_authn_file.so

    ServerName *
    ServerAlias *

    SSLEngine on

        AuthType Basic
        AuthName "Secure Content"
        AuthBasicProvider file
        AuthUserFile /usr/syno/etc/httpd/passwd
        Require valid-user

Edit /etc/httpd/conf/httpd.conf-sys and include the preceding file at the end.

Include conf/extra/httpd-something.conf

Restart httpd.

/usr/syno/etc/rc.d/S97apache-sys.sh restart

Forward port 5002 in your firewall.


Enjoy your (a tiny bit more) secured NAS from anywhere in the world.


Subscribe to Front page feed