#TIL : Run container processes as non-root user

As default, docker runs your container as root user (uid=0). Although docker isolates your filesystem to protect docker host, but running processes as root is redundant and increasing attacking surface. Even it can make its written files having root owner, which can mess your docker-host filesystem permission.

This is example that show docker runs as root

1
$ docker run --rm alpine sleep 30

and open another terminal to check this

1
2
3
4
$ ps xau | grep 'sleep'
khanhic+ 15552 0.5 0.4 1264452 49280 pts/1 Sl+ 17:37 0:00 docker run --rm alpine:3.9 sleep 30
root 15610 0.6 0.0 1520 4 ? Ss 17:37 0:00 sleep 30
khanhic+ 15876 0.0 0.0 23076 1024 pts/2 S+ 17:37 0:00 grep --color=auto sleep

You can see that the process sleep 30 is running as root with pid = 15610


To control which user docker container runs as, you can use the --user [userid]:[groupid] argument

Example

1
$ docker run --rm --user 1000:1000 alpine sleep 30

Then you will get this result

1
2
3
4
$ ps xau | grep 'sleep'
khanhic+ 16275 2.0 0.4 1411916 50124 pts/1 Sl+ 17:41 0:00 docker run --rm --user 1000:1000 alpine:3.9 sleep 30
khanhic+ 16336 1.5 0.0 1520 4 ? Ss 17:41 0:00 sleep 30
khanhic+ 16403 0.0 0.0 23076 984 pts/2 S+ 17:41 0:00 grep --color=auto sleep

TIP : you can set a environment variable by add this line to ~/.bash_profile or ~/.bashrc

1
export DOCKER_UID="$(id -u ${USER}):$(id -g ${USER})"`

then use docker command like docker run --user $DOCKER_UID ....

Boost Docker CI Build Speed to ~10X times

As an software engineering developer, you know that automated CI testing is one of keys to improve software release life-cycle.

But sometimes reality is not as good as you think, CI testing speed is slow (3-10 minutes / build) and it slows the release cycle speed down. And you try to look into your build logs to find out what causes the problem. Then you got it, it’s mostly the DATABASE service (MySQL, Postgres, MongoDB, …)

I will summarize some stages of your database in a testing build:

  • First, it initializes the data, loads config and listens to the connections (takes around 10-45 seconds)
  • Second, that you import your testing database into the server (including schemas and initialized data) takes around 20-60 seconds
  • Then, on each test case, it needs to clear all data then re-imports fixture data (takes around 30-120 seconds)

So how to make these servers run as fast as possible like some Key-Value databases do? (Redis, Memcached). The main different point is the MEMORY! What if we put all data inside memory??

All of we know that RAM speed with 150 times lower latency is technically better than SSD and HDD speed. And as a matter of fact, Linux is a good OS that supports a lot of filesystems, specially tmpfs, which you can mount files into your RAM memory.

However, nothing is perfect and this is not an exception. Actually, it is not a good option for persistent data which is not necessary for testing database. What it really needs is speed only, so it fits in.

That’s my idea, now I will try to test it on my CI environment (I use DroneCI using Docker). In new version 0.8+ of DroneCI, they support us to run docker containers within tmpfs mount.

So I just add this line into my drone config

1
2
3
4
5
6
7
8
9
services:
testdatabase:
thumbnail: mysql:5.7
# Add this 2 lines below to boost your database container
tmpfs:
- /var/lib/mysql
environment:
- MYSQL_DATABASE=testdb
- MYSQL_ROOT_PASSWORD=passwd

Result:

  • MySQL service initializes in 3 seconds instead of 25 seconds
  • Import testing database using mysql client takes below 1 second instead of 17 seconds
  • My test cases run 20-30% faster (I have few testcases using database)

So, worth a shot !!


Ref:

#TIL : Make cron job runs every seconds

Cron is a UNIX service that helps user run command periodically. And crontab is simple tool to setup cron job as user, just type in the command crontab -e then setup your cron schedule.

Btw, sometimes you want to run the cronjob every seconds (5, 10 or 20 seconds), but crontab only support every minute. How to achieve your goal without using another tool ?

I got an idea that we can use the sleep command to make it done. So this is solution.

This below is crontab rule that run command every 10 seconds.

* * * * * [command]
* * * * * sleep 10 && [command]
* * * * * sleep 20 && [command]
* * * * * sleep 30 && [command]
* * * * * sleep 40 && [command]
* * * * * sleep 50 && [command]

It’s simple, right ? ;)

#TIL : Disable IPv6 to stop getting stuck in network

I know IPv6 will be future for networking, but at this moment “It’s suck !” :(

Some service will be failed when trying to connect IPv6 destination :

  • apt package manager
  • smtp
  • curl

So I decided to disable IPv6 on every production server.

1
2
3
4
5
$ echo "net.ipv6.conf.all.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf
$ echo "net.ipv6.conf.default.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf
$ echo "net.ipv6.conf.lo.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf
$
$ sudo sysctl -p

I will re-enable it when everything works perfectly !

#TIL : Using watch command to tracking changes in period time

watch to a good command to run a command every N seconds.

And like its name, means you can watch something, its output changes with flag -d

It’s a great tool to help you learn a new language without hitting compile and run everytime you save a file.

1
$ watch -n 1 -d go run learn.go

This command will compile and run learn.go every 1 second

More flags :

  • -t : no title
  • -b : beep on non-zero exit code
  • -e : stop loop on error and exit on a keypress
  • -g : exit on change
  • -c : support colors
  • -h : you know ! ;)

Reading NFC Card ID on Ubuntu

Prerequisites

Install driver

My device is ACS122U : http://www.acs.com.hk/en/products/3/acr122u-usb-nfc-reader/#tab_downloads

Kernel modules

1
2
3
# echo "uinput" >> /etc/modules
# echo "install nfc /bin/false" >> /etc/modprobe.d/blacklist.conf
# echo "install pn533 /bin/false" >> /etc/modprobe.d/blacklist.conf

Install packages

1
sudo apt-get install swig libccid pcscd libpcsclite-dev libpcsclite1 python-dev python-pip gcc linux-headers-$(uname -r)

Run service

1
sudo service pcscd restart

Install python packages

1
sudo pip install pyscard python-uinput evdev

Source code

This is source code that read the card ID and simulate keystrokes to type card ID by text then pressing ENTER key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#! /usr/bin/env python

from __future__ import print_function
from time import sleep

from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver
from smartcard.CardMonitoring import CardMonitor, CardObserver
from smartcard.util import toHexString
from evdev import UInput, ecodes
import uinput


device = uinput.Device([uinput.KEY_0, uinput.KEY_1, uinput.KEY_2, uinput.KEY_3, uinput.KEY_4, uinput.KEY_5, uinput.KEY_6, uinput.KEY_7, uinput.KEY_8, uinput.KEY_9, uinput.KEY_A, uinput.KEY_B, uinput.KEY_C, uinput.KEY_D, uinput.KEY_E, uinput.KEY_F, uinput.KEY_G, uinput.KEY_H, uinput.KEY_I, uinput.KEY_J, uinput.KEY_K, uinput.KEY_L, uinput.KEY_M, uinput.KEY_N, uinput.KEY_O, uinput.KEY_P, uinput.KEY_Q, uinput.KEY_R, uinput.KEY_S, uinput.KEY_T, uinput.KEY_U, uinput.KEY_V, uinput.KEY_W, uinput.KEY_X, uinput.KEY_Y, uinput.KEY_Z, uinput.KEY_TAB])


def card_id_to_keyboards(card_id):
id = card_id.replace(' ', '')

return [getattr(uinput, 'KEY_'+x.upper()) for x in id]


# a simple card observer that tries to select DF_TELECOM on an inserted card
class selectDFTELECOMObserver(CardObserver):
"""A simple card observer that is notified
when cards are inserted/removed from the system and
prints the list of cards
"""

def __init__(self):
self.observer = ConsoleCardConnectionObserver()

def update(self, observable, actions):
try:
(addedcards, removedcards) = actions
for card in addedcards:
print("+Inserted: ", toHexString(card.atr))
card.connection = card.createConnection()
card.connection.connect()
card.connection.addObserver(self.observer)
apdu = [0xFF, 0xCA, 0x00, 0x00, 0x00]
response, sw1, sw2 = card.connection.transmit(apdu)
keys = card_id_to_keyboards(toHexString(response))
for key in keys:
device.emit_click(key)
ui = UInput()
ui.write(ecodes.EV_KEY, ecodes.KEY_ENTER, 0)
ui.write(ecodes.EV_KEY, ecodes.KEY_ENTER, 1)
ui.syn()
ui.close()
for card in removedcards:
print("-Removed: ", toHexString(card.atr))
except:
pass

if __name__ == '__main__':
print("Insert or remove a SIM card in the system.")
print("This program will exit in 60 seconds")
print("")
cardmonitor = CardMonitor()
selectobserver = selectDFTELECOMObserver()
cardmonitor.addObserver(selectobserver)

sleep(86400)

# don't forget to remove observer, or the
# monitor will poll forever...
cardmonitor.deleteObserver(selectobserver)

import sys
if 'win32' == sys.platform:
print('press Enter to continue')
sys.stdin.read(1)

Run

1
$ sudo python main.py

Demo

This is my demo application that using card ID like a user password, so I can unlock my computer or enter sudo mode :D

It Just Works

If you want a “just-work” solution, checkout this repo. It is built on Java smartcardio.

1
$ java -jar rfid-reader2keyboard.jar

Good luck ! ;)

#TIL : Send ENTER key to kernel

When you try to send an Enter keyboard to linux kernel, it looks like nothing happens.

This is because you only send a key press (KEY DOWN) but don’t send an key release (KEY UP) event after that.