From Octopress to Pelican

Finally I had some time to move away from Octopress... After trying a couple of Python static web generators I decided to give Pelican a try.

The "migration" was pretty easy. Install it via pip, configure your list of plugins and pages, update your posts (you have to do slightly modifications to your Octopress header posts) and you are good to go... I even managed to keep the same design I already had (thanks to this octopress theme)

So far, it feels good to have an easy bloggin platform in Python, so I totally recommend the change!

Later

Bye 2014 && Hello 2015

It is time to say goodbye to 2014... This is how I saw it from my old Samsung Galaxy S3:

{% img center /img/2014byphone.JPG '2014' %}

(50% business/50% leisure)

Happy New Year 2015!!! I'll see you around!

Easy blogging: Octopress, Git and Docker

Docker has been probably 2014 revelation (at least in IT world), so I have decided to get my hands on it, and at the same time, try to solve my "problem" with Ruby.

My idea is to "Dockerize" Octopress environment, and run it from a container every time I need to recreate the blog. And also, instead of keeping the blog posts (markdown files) locally, I will store them in a Git repository, and configure Git-hooks so the blog is recreated automatically.

First of all, make sure you have Docker and Git installed in your server.

In order to be able to build the blog, we will need Octopress source code. You can use your current Octopress installation or download it from Github:

$ git clone http://github.com/imathis/octopress.git octopress
$ cd octopress

Once Octopress is downloaded, let's create our Docker container using this Dockerfile:

FROM ubuntu:14.10

RUN apt-get update
RUN apt-get install git ruby1.9.3 ruby-dev build-essential language-pack-en nodejs python -y

ENV LC_ALL en_US.utf8

ADD . /blogSRC
WORKDIR /blogSRC

RUN adduser --uid 1000 --gid 50 deploy
RUN chown -R deploy:staff /blogSRC
USER deploy

RUN gem install bundler
RUN bundle install

ENTRYPOINT ["rake"]

This container will download and install Ruby environment and its dependences (There are some compatibility issues with Python3, so 2.7 has to be installed. See this), so we are able to run rake commands.

Build the container:

$ sudo docker build -t bynario/octopress .
Sending build context to Docker daemon 3.829 MB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.10
 ---> accae238329d
Step 1 : RUN apt-get update
 ---> Running in 5a456e103939
...
...
...
Step 11 : ENTRYPOINT ["rake"]
 ---> Running in c74572f9fb1a
 ---> 94d1b90e8e03
Removing intermediate container c74572f9fb1a
Successfully built 94d1b90e8e03

If it goes fine, you will see your new container by listing the images:

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
bynario/octopress   latest              94d1b90e8e03        About an hour ago   531.8 MB
ubuntu              14.10               accae238329d        4 days ago          221.1 MB
$

If you downloaded Octopress from Github in Step 1, then we need to install it. Since we already have our container ready, let's use it:

$ sudo docker run --rm -v /home/docker/octopress/:/blogSRC bynario/octopress install
mkdir -p source
cp -r .themes/classic/source/. source
mkdir -p sass
cp -r .themes/classic/sass/. sass
mkdir -p source/_posts
mkdir -p public
## Copying classic theme into ./source and ./sass
$

(/home/docker/octopress/ is my path to octopress installation folder, change it to fit your installation)

At this point our Octopress is propely installed, and Ruby is running from a Docker container, so there shouldn't be more issues with Ruby and system upgrades.

Now, let's get the posts from our git repository and configure the git-hook. For this I will assume that the posts are already commited and pushed to your git server (Github, BitBicker, etc), and your repositories are working just fine (ssh keys, users, etc..).

cd /home/docker/octopress/source/_posts
git init
git remote add origin git@server.domain:path/repository.git
git pull origin master

(Again, use the path that fits your installation, and update git@server.domain:path/repository.git with your own repository)

Now all the posts are in the _posts folder:

~/octopress/source/_posts$ ls -1
2011-01-01-tips-instalar-vmware-en-opensuse-11.markdown
...
...
2014-10-18-new-look-same-engine.markdown
2014-10-18-python-port-locker-slash-unlocker.markdown
~/octopress/source/_posts$

So we could generate the blog and check that it works. Let's run our docker container:

$ sudo docker run --rm -v /home/docker/octopress/:/blogSRC bynario/octopress generate
  • docker run --rm: run Docker and automatically clean up the container and remove the file system when the container exits.
  • -v /home/docker/octopress/:/blogSRC: Volume sharing. Mounting /home/docker/octopress/ in the local machine as /blogSRC in the container.
  • bynario/octopress: Image name and tag.
  • generate: Rake command to be executed by the container.

And finally let's create our git-hook. There are (at least) two different ways of working:

  • Webhooks: The repository will communicate with a web server whenever the repository is pushed to.
  • Client-Side Hooks: Local scripts that are called when certain events occur.

I chose the latter. So I configured a post-merge hook, that will be executed every time the content of my _post directory is updated after running a git pull:

sudo docker run --rm -v /home/docker/octopress/:/blogSRC bynario/octopress generate

Don't forget the execution rights: chmod +x /home/docker/octopress/source/_posts/.git/hooks/post-merge

Now, it is up to you how to call the git-hook. For example, you could run it manually every time you post something new or schedule it every night in crontab:

* 00 * * *  cd /home/docker/octopress/source/_posts && /usr/bin/git pull origin master

Happy blogging!

Python port locker/unlocker

If you run you own server (VPS, cloud instances, bare metal, no matter what), I guess you check your log files almost on daily basis, and probably you already run any the multiple available tools (the list is pretty long, but in my case I use logwatch) that helps you out to keep an eye in what is going on in your machine.

There is one thing in particular that drives me crazy. As soon as you expose a machine to the internet, people will be constantly trying to get SSH access:

{% img center /img/logwatch_print.JPG 'logwatch' %}

Obviously, there are tools that will make your life easier (like fail2ban, totally recommended) and some security tweaks (just Google for "linux server hardening") that you should be doing if you manage your own server, but even with all those things in place, I really don't like people trying to access to my server 24/7... Call me crazy, if you like.

A simple solution would have been just to change the SSH port to a something different, like 22222, and this might work for you. Unfortunately, I spend most of the time behind a proxy server, and I can only access the most common ports (the same very ones that are scanned on regular basis), so I needed to keep ssh service running in port 22.

Last year I coded a small bash script (just for myself) that allowed me to open and close SSH port on demand. It is been working fine since then, and I don't get annoyed by the ssh auth errors in logwath anymore. Now I migrated it to Python, and I thought it could be useful for anyone else, so here it is...

The script is not big deal, it will open/close certain ports that I preconfigured by reading a text file. Nothing new under the sun.

The only "particularity" of this script is that, instead of creating a service (webservice??) that allowed me to send the desired ssh state to the server (this would add another "attack vector"), I decided the script would pull the data from another location. I decided to use a (public) file in my dropbox folder.

If you want it to give it a try, these are the steps:

1 - Create a file in your dropbox folder and get the public link. Let's say that you get something like this:

https://www.dropbox.com/s/XXXXXXXXXXXXXXXX/file

2 - Add to this file any of the options you defined in the script. ie:

open ssh
close vpn
open  httpd

3 - Upload this script to your server (github link available in the code snippet header):

 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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python
#===============================================================================
#
#          FILE:  portLocker.py
#         USAGE:  python portLocker.py
#   DESCRIPTION:  port blocker/unblocker via dropbox file
#        AUTHOR:  http://bynario.com
#       CREATED:  05/08/2014 17:37:23 CEST
#===============================================================================
import time,sys,os,subprocess

dbFile="https://www.dropbox.com/s/XXXXXXXXXXXXXXXX/file"
logFile="/var/log/locker_log.log"
htmlFile="/usr/share/nginx/www/sshd.html"
servicesL={'ssh':'22','vpn':'1723','httpd':'80'}
actionsL=('open','close')

def createWWW(filename):
    try:
        with open(filename, 'w') as f: f.write(getTime())
    except Exception as e:
        print2Log(logFile,"Error opening "+filename+": "+str(e))

def print2Log(filename,text):
    try:
        with open(filename, 'w') as f:
            f.write(getTime() + " - " + text+"\n")
    except Exception as e:
        print2Log(logFile,"Error opening "+filename+": "+str(e))

def print2WWW(filename,service,status):
    try:
        with open(filename, 'a') as f:
            f.write(" - Service: " + service + " - Status: " + status)
    except Exception as e:
        print2Log(logFile,"Error opening "+filename+": "+str(e))

def getTime():
    return time.strftime("%Y-%m-%d %H:%M")

def runCommand(command,retVal=None):
    try:
        p = subprocess.Popen(command.split(), stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=False)
        (output, err) = p.communicate()
        if retVal: return output
        else: return None
    except Exception as e:
        print2Log(logFile,"Error executing "+str(command)+": "+str(e))

def checkDBFile():
    action="/usr/bin/curl -s -L " + dbFile
    command=runCommand(action,retVal=True)
    return command

def runIptables(service,action):
    print getTime() + " Running iptables... Service: " + service + " Action:" + action
    check_command="/sbin/iptables -C INPUT -p tcp -m tcp --dport " + servicesL[service] +" -j DROP"
    try:
        check_output = subprocess.check_call(check_command.split(),stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        if action == "close": 
            command="/sbin/iptables -A INPUT -p tcp --dport " + servicesL[service] + " -j DROP"
        else: 
            return True
    else:
        if action == "open":
            command="/sbin/iptables -D INPUT -p tcp --dport " + servicesL[service] + " -j DROP"
        else: 
            return

    runCommand(command)

if __name__ == "__main__":
    createWWW(htmlFile)

    for line in checkDBFile().splitlines():
        if not line: continue
        if len(line.split()) != 2:
            print2Log(logFile,"Error in dropbox file: wrong format")
            sys.exit(1)
        else:
            act = line.split()[0]
            serv = line.split()[1]

            if servicesL.has_key(serv) and act in actionsL:
                runIptables(serv,act)
                print2WWW(htmlFile,serv,act)
            else:
                print
                print2Log(logFile,"Service or Action found in file not valid... Skipping: " + act + "-" + serv)

4 - Now check these variables in the script:

dbFile="https://www.dropbox.com/s/XXXXXXXXXXXXXXXX/file"
logFile="/var/log/locker_log.log"
htmlFile="/usr/share/nginx/www/sshd.html"
servicesL={'ssh':'22','vpn':'1723','httpd':'80'}

Adapt the script to fit your environment:

  • Replace dbFile by the URL you got from Dropbox
  • Set the path you need for the logFile
  • The script will write the status of the services to my web server, so I can check the status easily. Set your path in htmlFile.
  • Define the list of services you want to manage in servicesL. This has to match with the info you define in the dropbox file.

5 - Create an entry in crontab with the periodicity you need. In my case, opening/closing ssh port is not critical, so, as I don't want Dropbox people to get angry at me, I set it up to 5 minutes :)

 */5 * * * * python /path/to/portLocker.py &>> /var/log/locker_log.log

Now all you need to do is modify your dropbox file, and after a few minutes, check if you get the result you expected ;)

There is one main improvement that could be done, and it is adding some kind of signature to the file in order to ensure nobody is messing with us, but I have pretty good reasons not to do it (at the moment):

  • I update the dropbox file from mobile devices every now and then, and adding a signature to the file would make this way too complex.
  • This file is stored in my dropbox folder, so theoretically, nobody but me is able to modify it.
  • The security of the server does not rely on this script. This is not meant to be "security through obscurity".

If you are after something more professional, I recommend you to check Latch.

And that's all folks... It is working pretty well for me, and I hope it works for you...

See you around

New look. Same engine

Dear reader, if you have ever been in a (long) business trip, you would problably agree with me on this:

"At the end, long business trips are pretty boring" (ok, ok, it depends on the place)

I have been in Amman (Jordan) for the past few weeks. I have seeing what could be seen around (not that much IMO), but since now I have some "spare" time, I have decided to take another look at Octopress. (I still think I would be better off without Ruby though :-P)

So, as you can see, I have modified the CSS of the site to make it cleaner and lighter, because I never really like dark styles.

Actually, this is how bynario.com looked like in 2005 (I found this in archive.org):

{% img center /img/bynarioSS-feb2006.JPG '2006' %}

And this is in 2011 (I hosted this site in blogger for a few months):

{% img center /img/bynarioSS-oct2011.JPG '2011' %}

And finally, since June 2012, I have been running Octopress with just some small modifications over the default theme:

{% img center /img/bynarioSS-oct2014.JPG '2014' %}

Now I just need to stop procrastinating and post more often... Easier said than done!

Bye