Jul 27

Mikrotik as a PPTP server for Android

Reading time: 4 – 6 minutes

Two years ago I installed a Mikrotik Cloud Switch and lately I stoped my pfSense and I started using that switch as my network router, firewall and also as a switch. RouterOS is really powerful and allows to do a lot of things with that hardware. One of those things is set-up a VPN server based on PPTP. This is not the most secure way to create VPNs but usually the only requirement is a little bit of security on top of an IP over IP service that allows us to use local service when we’re in remote. In my case I have a lot of services in my LAN and I need some of them when I’m out of home specially I need to use them from my mobile phone.

Next steps describe how I set-up a PPTP server on my Mikrotik server allowing my Android 6 device (Huawei Mate 8) to connect to my home services through the VPN. Bellow you have a simple schema representing the schenario of the solution:


I’m only going to describe steps from the web console, of course, all those steps can be done using Winbox or the command line. To be honest I’m not used to RouterOS CLI but I think it’s not difficult to figure out the CLI commands to get the same result.

First step is set-up a pool of IP addresses to be assigned to the tunnel endpoints.



Thanks to an ARP proxy those IP addresses will be available like local IPs, this is transparent for the VPN configuration.


configuration of connection profile is done inside “Profiles” tab:


first of all create a new profile called “default-encription”:


and another profile called “pptp-profile”:


“Secrets” tab is where you have to manage users, in my case only two users are created:


configuration details about my user are:


Inside PPP menu there is a button with a label “PPTP server” click there…


… and copy next settings:


Don’t forget to check that your PPTP port is accessible from your public IP address. Remember it’s 1723/TCP.

Android configuration is simple, first of all go to “settings” icon. Look for a “More” section bellow network options, and you’ll find VPN managment. Add a new connection, define a name, the type and the IP address and leave the rest by default. After that when you come back to VPN list you’ll find your new VPN in the list, click there and just set-up your PPTP credentials.


If you have some trouble the only thing that you can do is go to Mikrotik logs or start sniffing to figure out where is the problem. I had to do some tests before it was working but in the end everything was so simple like I explained here.

Good luck and enjoy it.

Thanks to next blog entries to inspire me:



Jun 06

Remove old kernels when there is no space in /boot

Reading time: 1 – 2 minutes

The first step is get some space in the partition “/boot” because without that it’s impossible to do anything.

So go to /boot and remove some “initrd” files as they are the biggest ones. A few of them will be enough.

After that a good point is to ensure there is no partial installation pending to finish:

apt-get -f install

Now it’s a good idea to purge all kernels except the running one:

dpkg -l 'linux-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d' | xargs sudo apt-get -y purge

To avoid that in future before filling the partition, it’s a good idea to install and run periodically: purge-old-kernels. Installation and example of use are:

# installation
apt-get install bikeshed
# keep three old kernels:
purge-old-kernels --keep 3
# if you want to put that in the crontab use that command
purge-old-kernels --keep 3 -qy

If you’re a Grub user don’t forget to run:


Personally I have a nightmare with that problem and Ubuntu, especially with version 12.04 which is installed in a lot of servers that I manage. I repeated the previous process a lot of times and in the end, I decided to document it because I always have to go to Google and find the proper steps to solve that problem.

Jun 05

Protecting your email with MXGuarddog

Reading time: 2 – 2 minutes

mxguarddogAfter using VMWare ESXi for a long time as a Hypervisor for my virtual servers I decided to stop paying OVH for that service and I migrated my virtual machines to VPS servers on OVH. At the end of the day only two VPS with a cost of 3€/month are enough and I can stop a 50€/month dedicated server.

The biggest challenge that I had to solve was migrate mail server to a new server. So far today I was using pfSense a firewall for my virtual servers. They were in a virtual network; pfSense anti-spam services and mail forwarding were enough to receive “cleaned” mail in my private mail server with Postfix and Dovecot.

New configuration is just a cheap VPS (1xCPU+2GB RAM+10GB SSD) with Ubuntu 16.04 and also with Postfix and Dovecot. But I decided to rent the anti-spam, anti-malware and anti-virus service to MX Guarddog. I discovered that service just surfing on the big G. Only 0.25 cents per account per month it’s a very good price and it does all the things that I need and much more. Configuration is really simple if you know what you are doing. They have a very good and simple control panel to manage the service. This is the perfect service to get what I need.

In the control panel you can do all that you need, manage mail accounts and domains. View quarantined mails and all required configurations and tests to validate everything is ready and also maintain white and black lists. We’ll see during next days if the service gets the quality that I expect, I hope I have found a very good and cheap resource.

Apr 12

Sniffing traffic in a Linux box and streaming in real-time to Wireshark on Windows

Reading time: 1 – 2 minutes

Sniffing and inspect complex protocols on “tcpdump” is usually painful. Of course, “tcpflow” is a very useful tool but is not always enough to sniff in a console. Wireshark is always a better option when it’s time to debug and troubleshooting communication problems.

But it’s not always easy to plug a Network TAP where you want to sniff. If at that point we have a Linux box with “ssh” and “tcpdump”. An interesting option is stream sniffed traffic to another box with Wireshark and dissect packet octets in their layers, fields, etc.

When Wireshark box is based on Windows you need “plink.exe“, and you can do thinks like that:

plink.exe -ssh -pw LINUX_BOX_PASSWORD root@LINUX_BOX_IP "tcpdump -n -i INTERFACE_TO_SNIFF -s 0 -w - not port 22" | "%PATH_TO_WIRESHARK\Wireshark.exe" -k -i -

Next you have a screenshot with a real life example of that:


Apr 09

MacBook Air battery explossion

Reading time: 2 – 2 minutes

Two months ago I went to get my “Mac Book Air mid2011 version” and found that:

The batteries had exploded! It is curious bacause I have laptops saved for many years, one would say it has almost 20 years. Obviously the battery lasts very little but has never exploited. It is incredible that a brand that cares the quality of its product as Apple and a product that was the best in its class 5 years ago; today without more than being on a shelf it has exploded from one day to the other.

In Apple store didn’t want to know about the problem because it is out of guarantee; luckly it wasn’t my daily laptop and after buying a new battery in ebay I have changed the battery for les than 50€ and the laptop keeps running.


I don’t know if anybody else suffered that experience but IMHO Apple has failed and I’m very disappointed with their reaction with my issue with the product. I know it’s not on guarantee but I paid close to 1.700€ on a Laptop less than 5 years ago and I don’t expect that. Clearly this is a manufacturing problem with the battery. I have to recognize once again that Apple has very good quality products, or not, but day after day their customer support is being worst.

Just a final note my actual laptop is a Toshiba, I’m not proud of it but it works quite good so far today is for far more powerful than current MacBook Air with the same weight amd I don’t have to carry a lot of connectors and cables because everything is embedded, included the 4G modem.

Feb 12

X files: mouse pointer starts moving by itself

Reading time: 2 – 3 minutes

It seems a jog but it’s true, after buying my Toshiba Portégé Z30-A-180 PT243 I was so proud about the performance and laptop features. By default it was running a Windows 7 and after some months of using mouse pointer started moving drawing a diagonal in the screen there wasn’t a stela just a diagonal movement and during that automatic movement there were no way to get mouse control. New Toshiba laptops has a touchpad and a trackpoint a none of them was responding while that happened. Because that only happens time to time I didn’t pay attention to the problem.

Toshiba Portégé - Trackpoint and Touchpad

Last Christmas holidays I updated the laptop to Windows 10, and I was very happy to see how 99.9% of applications and configurations was maintained and running perfectly. But after some weeks mouse pointer movements return to my life, some times very often and some times less usual. But one afternoon I was totally desperate with that issue and I decided to look it up on Google. I found a thread on Toshiba support forum where more people was talking about the same issue. Proposed solutions are not perfect but helpful for me, they talk about a static electricity problem that affects trackpoint and the best option is disable it to forget the problem. Luckly I don’t use trackpoint because for me touchpad is more confortable and disable trackpoint is good enough solution in my case.

So if you have automatic mouse movements in Toshiba Portégé Z30 disable the trackpoint, don’t forget that Toshiba refers to that device as a Accupoint. Below you have a capture of the instructions to do that:

Disable Accupoint II

Disable Accupoint

I hope this blog entry has been so helpful as it has been for me.

Jan 10

El meu 2015

Reading time: 8 – 12 minutes

Re-editant l’article que vaig escriure per tancar l’any passat aquest any també he volgut fer un resum del que ha donat de si el 2015. Per desgràcia els compromisos personals i familiars no m’han permès publicar-lo fins a principis del 2016, tot i que jo sóc dels que pensa que més val tard que mai. He tancat un nou any on he fet més coses de les que hem pensava i quan m’he posat a rellegir les meves notes diaries, setmanals, mensuals, semestrals i anuals he al·lucinat.

A nivell personal i familiar, de nou he tingut moltíssimes oportunitats pel creixement personal. Moltíssimes ocasions on m’he hagut de sobreposar per aixecar-me i tornar a creixer un cop rera d’un altre. La millor notícia de l’any és que estem esperant el Roc. El que ha de ser el germà del Pol i que ha de neixer durant el mes de Febrer. Aquesta boníssima notícia eclipsa qualsevol altre tema i ha fet de pal de paller al voltant del qual s’han desenvolupat moltíssimes decisions durant l’any.

L’any 2015 va començar amb una bona notícia el mes de febrer perquè tornavem a estar embarassats. Però a finals de març un avortament ens va fer tornar a canviar els plans i la il·lusió es va tornar a esvaïr. Per tal de trencar una mica amb aquest amarg event ens varem escapar durant una setmaneta cap a terres del sur. Varem visitar “Puerto Banús” (Marbella), Puerto de la Duquesa, Tarifa i Gibraltar. Després d’haver passat per la història del Pol això no era res, però de nou apel·lava a la nostre fortalesa emocional. En moments com aquest és quan hem sento infinitament orgullós d’estar amb una persona tan excepcional com l’Estefania. No només ens varem aixecar sinó que ara com ús dic estem apunt de donar la benvinguda al germanet del Pol, en Roc.

Per aquestes mateixes dates la meva avia de Sant Sadurní, la Carmeta, també ens va dir adéu. Era l’últim avi que hem quedava viu. Per desgràcia ja feia una colla de mesos que anava perdent la serenitat. Per si no fos poc la meva mare i el meu germà també durant els primers 5 mesos de l’any van haver de superar temes de salut rellevants. Per sort, en ambdós casos només van ser ensurts. Curiosament per aquestes dates és sempre quan es concentren esdeveniments emocionalment més importants per la família. Coincidint a més amb l’aniversari de la mort del meu pare, la història del Pol, els 12 anys del meu accident de cotxe, el tercer aniversari del nostre casament i la cel·lebració dels meus 38 anys.

També per aquestes dates varem fer una nova escapada amb els “cunyis” (Sarai i Àlex); aquest cop a la costa brava. Concretament a Calella de Palafrugell. Un d’aquests racons que per molts cops que visitis mai et canses de tornar a visitar. No ens oblidem tampoc la caminada que varem fer l’Estefania i jo per celebrar el seu aniversari, junt amb la romeria de Torrelavit, l’Anna i el Carles ens varem arribar fins a Montserrat a peu. Tot un desafiament per nosaltres.

Per desgràcia aquest any no he pogut disfrutar tan del tennis com és habitual en mi els últims anys. Una epicondilitis m’ha tingut uns quatre mesos patint sense poder disfrutar d’aquest esport que tan m’agrada. Per sort he pogut seguir amb l’spinning i també he començat a anar a la pisicina. A finals d’any degut a compromisos professionals i també els personals que ja coneixeu he hagut de deixar l’spinning aprofitant que ja podia tornar a jugar a tennis. Així doncs, s’ha acabat l’any amb alguns quilets extres que cauran ben aviat a la que reprengui la meva activitat esportiva i professional.

De nou hem continuat invertint en la casa, no tan fort com l’any passat perquè ens haviem de recuperar una miqueta però si que hem instal·lat un descalcificador a la casa, un grup d’osmosis a la cuina, hem fet una nova habitació a l’estudi, hem renovat l’WC del pis de d’alt, hem posat una nova pergola pel cotxe i nova teulada a la caseta de fusta del jardí. El millor de tot plegat és que personalment he participat força en l’execució d’aquestes d’algunes d’aquestes tasques.

Degut a l’embaraç del Roc aquest any les vacances han estat força diferents de l’habitual. Primer de tot varem fer una petita escapadeta a Puigcerdà durant un cap de setmana i després una setmaneta a Roses, bàsicament per descansar i disfrutar de la platja a més de poder estar junts com a parella. Ja que degut a la feina he passat un estiu força ocupat. Aprofitant les vacances a Roses també ens varem acostar fins a Empuria Brava per provar el Windoor que m’havia regalat l’Estefania per l’aniversari.

A nivell de creixement personal, aquest any hem tingut un creixement important a M2M Cloud Factory i ens hem començat a consolidar com a empresa, producte i amb grans i bons clients; tot això m’ha fet evolucionar moltíssim perquè l’excés de feina i l’altíssima demanda que ha tingut la feina sobre la meva energia ha estat esgotador. A més grans persones de l’empresa ens han deixat; com ara el Marc i el Pau i això encara ha augmentat més l’exigència. Però al final tot passa per algo i sempre hi ha lectures positives a tots els esdeveniments.

Tot i amb això he pogut incorporar tres noves formacions al meu currículum, un parell de cursos de comptabilitat. Un d’introducció i l’altre d’anàlisis de balanços. Però el que realment m’ha fet creixer personalment ha estat el de Management 3.0. Un curs increible, on no només se m’han obert les portes a una nova forma de fer les coses sinó també uns contactes boníssims i la possibilitat de col·laborar dins del meu departament amb un advisor excepcional, el Gabri.

Les meves rutines matutines, fent meditació a primera hora del matí; les preguntes per enfocar el dia i després fer-ne balanç i molts d’altres exercicis que heredo de la PNL, el mindfulness i d’altres disciplines m’han anat acompanyant durant tot l’any. Estic molt content del nivell de fidelitat que tinc amb totes aquestes pràctiques i sovint n’agraeixo els beneficis.

Gràcies a l’Horizon 2020 que varem guanyar l’any passat a M2MCF ens hem pogut centrar en crear un nou producte el MIIMETIQ LITE que veurà la llum ben aviat, a més d’haver creat una nova solució basada en les Smart Glasses. Mentre treiem noves versions del “framework”. Tot plegat ha tingut força repercusió a la prensa. Personalment hem van fer una entrevista al diari Ara referent a aquests temes: L’Internet de les coses: un futur a la punta dels dits.

Professionalment també he pogut tancar finalment el projecte Empowering. Més de dos anys de projecte al costat del Xavi i la gent del BEE group (CIMNE). Finalment el meu contracte va espirar el mes de setembre i vaig poder posar punt i final a aquest projecte de Big Data tan interessant i al que li desitjo el millor. A més com a consultor de l’empresa IUL, conjuntament amb l’Adrià com a part de l’equip de Nakima també hem pogut crear un parell de productes en un temps rècord i amb una orientació tecnològica molt ben enfocada cap al IoT.

Tot plegat m’ha permès tocar força tecnologies tot i que moltes d’elles ja les coneixia. Voldria destacar-ne algunes: OpenWRT, Raspbery PI, Rancher, Ambari, NodeJS, NodeRED, ESP8266, IrDA, RF, etc. Finalment també he aprofitat per canviar de portàtil i he jubilat el Mac Book Air que tenia. Tot i ser un i7 amb 4G de RAM i 256GB de SSD, el seu rendiment és ridicul al costat del Toshiba Portégé que amb el i7 de dos cossos, 16GB de RAM i 500GB d’SSD vola. Comentar que li vaig canviar el disc mSATA que portava de serie perquè no podia ser de més de 256GB. A més aquest nou portàtil té tots els ports que hem calen integrats, fins hi tot un mòdem 4G. Cosa que hem permet no haver d’anar pel món amb la maleta plena de cables.

Abans de tancar aquest resum anual afegir que aquest any he donat tres conferències. L’Àlex i la gent de la UPC van tornar a confiar amb mi perquè a inicis d’any fes de nou una conferència per la gent del FIB Alumni, aquest cop sobre la meva SmartHome. Arran d’aquesta conferència en Marc organitzador del IoT meetup de Barcelona em va demanar que la repetís en anglès per la gent del meetup. Finalment a la Garrotxa Camp també van voler que la tornés a fer.

També gràcies al Josep Maria la gent de l’Ara van voler fer-me una entrevista a tota pàgina que va sortir el dia de la diada. Sincerament hem va agradar força com van resumir la meva vida professional sense entrar en detalls. Obviament es van deixar mil coses però fer un article sobre la meva vida professional no és senzill i crec que ho van aconseguir prou bé. A més arran d’aquest article al butlletí de la gent gran de Torrelavit també hem van voler entrevistar, cosa que hem va fer molta il·lusió.

Un altre any plè de grans events, oportunitats i emocions profundes viscudes a flor de pell. Un any que varem acomiadar amb l’Àlex i Sarai (els “cunyis”) a Benifaió amb un sopar excel·lent i de forma relaxada. Abraçant aquest 2016 plè d’esperances, il·lusió i amb moltíssimes ganes de viure i estimar. Des del cor i els braços oberts de nou: GRÀCIES!!!

Dec 23

Raspberry PI and OpenWRT flash partition proposal and rescue boot support for embedded systems

Reading time: 4 – 7 minutes


Main target of this post is describe how to organize flash partitions and how to modify default OpenWRT boot sequence to support a flexible and powerful rescue mode for Raspberry PI based projects. Just to clarify the explaination. When OpenWRT is build on a flash card for Raspberry, there are only two partitions.

The first one is vFat partition with kernel, firmware and other configuration files; the second one is a ext4 partition with root filesystem. Boot sequence loads the kernel and then mount root partition and run the init script. If ext4 filesystem is corrupted or could not be mounted boot sequence is stoped and there is no solution without extracting the flash card.


In this blog entry I’m going to describe a partition table and boot sequence strategy to avoid this kind of problems. Of course, there are other solutions to get similar results but I think this one is simple and powerful at the same time.

Summarizing features of this solution:

  • reduce risk when using intensive writing app
  • reduce damage risk on flash memories
  • fail-safe mode pressing a button
  • support application upgrades using opkg packages
  • support operative system upgrades using opkg packages

This solution proposal assume:

  • wear leveling protection solved by flash card
  • button connected to GPIO pins

The idea

Raspberry PI requires a vfat partition as its first flash partition where there are several required files for booting process, this is a bootloader substitution. For example, in that partition there are files like: start*.elf and bootcode.bin which are the GPU firmware and bootloaders. Another key file is kernel.img; this is the kernel used for booting. Bootloader parameters for kernel booting are in a file called cmdline.txt and firmware parameters are set in config.txt.

At this point the most important think to take into account is kernel.img file and cmdline parameters. Because kernel is loaded and executed by default with cmdline parameters set. When kernel boot process finishes root filesystem and init process sequence will be figured out from cmdline parameters.

At this point take a look on proposed partition table could be useful: (spaces are just as a reference, use what you need)

p1 - vfat (~50MB)
p2 - ext4 - operative system base (read-only) (~150MB)
p3 - ext4 - operative system (read-write) (~250MB)
p4 - logical partition
  p4.1 - ext4 - your_application files (usually read-only)
  p4.2 - ext4 - your_application data (usually read-write)

Fail-safe boot process key is partition p2 where a minimal OpenWRT installation with a modified init sequence is found. Main idea here is detect if a GPIO shortcut is done, usually this is done just pressing a physical button and you can interact with the user emitting some beep, for example, you can tell the user when you are waiting for button press using a beep and then emit two beeps when button press is detected or nothing if no button is pressed in 3 seconds. Finally the idea is detect if you need a regular boot or a fail-safe boot.

My suggestion for minimal OpenWRT is a small footprint installation of OpenWRT without kernel modules, just the monolitic kernel loaded. Then reduce init sequence to the minimum and add fail-safe logic (GPIO button capture); if button is pressed stop boot sequence and give a shell to the user. Regular way will be invoke init file of the rootfs (p3 in the partition table).

I think the idea is simple and the complexity is reduced in two parts both of them are the init file. To be more precise the p2 partition table has its own init file and p3 the other one. p2 init file load the minimum hardware to control button and give rescue environment when it’s needed. And p3 init file mounts read-write partition and the regular filesystem with regular boot processes and all kind of stuff that you need.

Final notes

I know this is not a very practical post, but my intention is only share some ideas that I have in mind. I spend most of my time designing architectures and I think this is a very powerful architecture of a boot sequence for some professional projects based on Raspberry PI and OpenWRT.

The best way to do what I describe in this post is putting p2 in a initrd file which is referenced in kernel parameters. Because then all read-only system is a RAM partition and rootfs init file has the PID 1 dropping dual-init file complexity. But I decided to modify this part because in the past I had some problems creating initrd files specially when required space for that partition is bigger than RAM. Anyway it’s important to take in account that initrd files has the same purpose as the proposed p2 partition.

Useful links

Oct 13

Small recap of web shell applications

Reading time: 2 – 2 minutes

Lately I found some useful web applications that publish a terminal application. This is very useful when you are traveling or you have a remote server which you want to maintain or access from anywhere. Also another interesting use of this kind of applications is as a terminal for embedded devices.

I tried to use them as my default applications but all of them have the same problem: keyboard shortcuts conflict with the browser. I’m very used to use a lot of shortcuts to manage my terminal application and remote shell and this is a problem because most of the shortcuts are redefined by your browser. May be it’s possible to disable browser shortcuts when you are using this kind of web applications but I didn’t find how.

I hope this small list is as much useful for you as it is for me:

  • Wetty = Web + tty (the best one IMHO)





Aug 12

Secure download URLs with expiration time

Reading time: 4 – 6 minutes


Imagine a HTTP server with those restrictions:

  • only specific files can be downloaded
  • with a limited time (expiration date)
  • an ID allows to trace who download files
  • with minimal maintenance and dependencies (no databases, or things like that)

the base of the solution that I designed is the URL format:

  • signature: is calculated with the next formula, given a “seed”
    • seed = “This is just a random text.”
    • str = customer_id + expire_date + path_n_file
    • signature = encode_base64( hmac_sha1( seed, str))
  • customer_id: just an arbitrary identifier when you want to distinguish who use the URL
  • expire_date: when the generated URL stops working
  • path_n_file: relative path in your private repository and the file to share

Understanding the ideas explained before I think it’s enough to understand what is the goal of the solution. I developed the solution using NGINX and LUA. But the NGINX version used is not the default version is a very patched version called Openresty. This version is specially famous because some important Chinese webs works with that, for instance, Taobao.com

Expiration URL solution Architecture schema

In the above schema there is a master who wants to share a file which is in the internal private repository, but the file has a time restriction and the URL is only for that customer. Then using the command line admin creates a unique URL with desired constrains (expiration date, customer to share and file to share). Next step is send the URL to the customer’s user. When the URL is requested NGINX server evaluates the URL and returns desired file only if the user has a valid URL. It means the URL is not expired, the file already exists, the customer identification is valid and the signature is not modified.

NGINX Configuration

server {
 server_name downloads.local;

 location ~ ^/(?<signature>[^/]+)/(?<customer_id>[^/]+)/(?<expire_date>[^/]+)/(?<path_n_file>.*)$ {
 content_by_lua_file "lua/get_file.lua";

 location / {
 return 403;

This is the server part of the NGINX configuration file, the rest of the file can as you want. Understanding this file is really simple, because the “server_name” works as always. Then only locations command are relevant. First “location” is just a regular expression which identifies the relevant variables of the URL and passes them to the LUA script. All other URLs that doesn’t match with the URI pattern fall in path “/” and the response is always “Forbiden” (HTTP 403 code). Then magics happen all in LUA code.

LUA scripts

There are some LUA files required:

  • create_secure_link.lua: creates secure URLs
  • get_file.lua: evaluates URLs and serves content of the required file
  • lib.lua: module developed to reuse code between other lua files
  • sha1.lua: SHA-1 secure hash computation, and HMAC-SHA1 signature computation in Lua (get from https://github.com/kikito/sha.lua)

It’s required to configure “lib.lua” file, at the beginning of the file are three variables to set up:

lib.secret = "This is just a long string to set a seed"
lib.base_url = "http://downloads.local/"
lib.base_dir = "/tmp/downloads/"

Create secure URLs is really simple, take look of the command parameters:

$ ./create_secure_link.lua 

 ./create_secure_link.lua <customer_id> <expiration_date> <relative_path/filename>

Create URLs with expiration date.

 customer_id: any string identifying the customer who wants the URL
 expiration_date: when URL has to expire, format: YYYY-MM-DDTHH:MM
 relative_path/filename: relative path to file to transfer, base path is: /tmp/downloads/

Run example:

$ mkdir -p /tmp/downloads/dir1
$ echo hello > /tmp/downloads/dir1/example1.txt
$ ./create_secure_link.lua acme 2015-08-15T20:30 dir1/example1.txt
$ date
Wed Aug 12 20:27:14 CEST 2015
$ curl http://downloads.local:55080/YjZhNDAzZDY0/acme/2015-08-15T20:30/dir1/example1.txt
$ date
Wed Aug 12 20:31:40 CEST 2015
$ curl http://downloads.local:55080/YjZhNDAzZDY0/acme/2015-08-15T20:30/dir1/example1.txt
Link expired

Little video demostration


Disclaimer and gratefulness