⌨ Labor omnia vincit ☮

pam-accesscontrol: PAM-based access control system written in python

Posted in python, Qt, security by anaumov on 27.12.2017

In traditional UNIX/Linux authentication there is not much granularity available in limiting a user’s ability to login. For example, how would you limit the number of users from a specific group? What if you want to allow some users to log in from a specific host, but disallow all others from a same host? Firewall can not help at this point. What if you want to allow SSH for someone, but not X- or login sessions on tty? And… what if you want to be able to decide about allowing SSH-session for someone at the same moment when connection will be established?

PAM, or pluggable authentication modules, allows for you just this sort of functionality (and more) without the need to patch all your services. PAM is flexible enough to provide solution for all of the above listed issues. All what you need at this point is just implement a new PAM-plugin for your needs.

Some times ago I came across pam_python — a PAM module that lets you write PAM modules in python. During paying with pam-python I implemented my own plugin as a hobby. At the beginning that was just notification window about every new incoming SSH connection. After that I added possibility to allow or denied every new established SSH connection by asking the owner of the X session (that was implemented just for desktop users in mind, of course). Next – define the list of users who may to login any time, who should wait for confirmation and who should never get the shell on my machine. I came on idea to put these lists to the config file. Project grew quickly. Now I would like to introduce it, show the sources and invite everyone (who love coding) to take a part of the development 😉


I called it pam-accesscontrol. Before we start I would like to remind you, that it was implemented as a hobby project just for fun and it’s still unstable, i.e. can broke something on your system. “Someting” is, for example, possibility to login via tty/login(1), SSH or display manager . So, you can just look at the source code or, if you really know what you’re doing, install the package.

To be able to understand how PAM and pam-accesscontrol communicate with other, take a look at doc-page where you can find the list of pam-python methods which are nothing other then a interface for Linux PAM APIs. Every PAM event like open_session, close_session, authenticate, etc. will call appropriate pam-python method. This method will call appropriate pam-accesscontrol function.
For example, OpenSSH: by using password authentication procedure PAM calls pam_authenticate function, which call pam-python’s pam_sm_authenticate function. You just need to implement pam_sm_authenticate-method in your plugin to intercept further steps in the authentication process. Pam-python is just a “bridge” between PAM and pam-accesscontrol:

As you can see, we also should tell PAM (add configuration to files in /etc/pam.d/ directory) to call plugin in some necessary cases. Necessary cases are services like, for example, sshd or login (depend on our wishes) and events like auth, session, etc.

List of all available PAM modules can be found in /lib/$(arch)-linux-gnu/security/ directory. After installiing pam-python package pam-python.so file should be there and after installing pam-accesscontrol package — accesscontrol.py script.


PAM-accesscontrol’s behavior depend on its config file. You add rules for /etc/pam.d/* services like sddm or login. It’s similar to iptables rules: by an authenticate new user or creating new session, pam-accesscontrol will read config file, parse it and try to make a decision. Actually, pam-accessconfig is nothing else then just a parser. It’s possible to close access for everybody and specify in what case and for whem it will be open, or vice versa — just define who should have no access. At the end, decision has made by pam-accesscontrol will be returned to PAM.

Right after package installing it has just one rule in its config file: DEFAULT:OPEN, i.e. access open for everyone. If this DEFAULT variable will be not set in config file, pam-accesscontrol initialize it with CLOSE value.

Let me show some configuration examples.

> cat /etc/pam-accesscontrol/pam-accesscontrol.conf

sshd OPEN GROUP lp,users
sddm OPEN USER bob,alice
login OPEN USER bob,alice

First line of this configuration closes access for all users. After that we open SSH (password authentication) for all users from lp and users groups (it supports LDAP groups, POSIX groups and primary user groups); open access via sddm for users bob and alice; open access via login for users bob and alice. Pretty easy. Every rule should have exactly 4 fields. If rule is broken, it will be just ignored.

It’s possible to set limit for number of users from specific group.

sshd NUMBER GROUP users:2,lp:3

For example, this line sets limit to 2 for group users and to 3 for group lp. In other words, new SSH connection will be possible for users from group lp, if only 2 or less users will be logged on this system at the same time.

It’s also possible to configure it so that it will ask you for every new incoming connection. With this line in config file everyone from group lp will wait for your confirmation:

sshd-key ask group operation

It means, access will be granted via SSH by using public-key authentication only for someone from group operation… only if X-session owner allow it.

It calls QMessageBox from PyQt5 that returns 0 or 1 to pam-accesscontrol depend on your choice. This value will be interpreted as allow or not allow. By the way, if there is no active Xorg session, pam-accesscontrol will not be able to ask you… so in this case this will be interpreted as an OPEN rule. Also keep in mind that pam-accesscontrol ask owner of the Xorg session only for the first SSH incoming connection. Remote user would like, for example, to copy 100 files on its host; in that case also just one confirmation at the begging will be needed.


X-session owner also will be informed when remote user (whose SSH session was confirmed through QMessageBox) ends its SSH session.


Pam-accesscontrol uses syslog. It creates logs after every successful authentication. As usual, on systemd-based systems you can use journalctl(1). Also some logs can be found in /var/log/auth.log (authentication phase) and /var/log/syslog files. By default, there is not so much information. For debugging and during development it’s a good idea to enable debug/verbose mode. Add debug:true to the config file and it will put much more info about why it made this or that decision.
It also creates its own logfile /var/log/pam-accesscontrol-YEAR-MON.log where is stored short statistic about when, who, via which interface tried to login and also — was it successful or not.

Use ldd(1) to check PAM compatibility for supported interfaces:

# ldd $(which sshd) | grep pam
        libpam.so.0 => /lib64/libpam.so.0 (0x00007f82cdfff000)
# ldd $(which login) | grep pam
        libpam.so.0 => /lib64/libpam.so.0 (0x00007fee7f3c9000)
        libpam_misc.so.0 => /lib64/libpam_misc.so.0 (0x00007fee7f1c5000)

pam-python is avaliable for openSUSE

Posted in openSUSE, python, security by anaumov on 21.04.2017

Last week I came across pam_python, a PAM module that lets you write PAM modules in Python. It seems interesting to play in this direction, but I had to install it manually. It seems that there was no official packages for openSUSE until now…

Yesterday I built version 1.0.6 for Tumbleweed. Please test it. It’s in our security repo. Feel free to send submit requests.

After installing it we will get /lib64/security/pam_python.so PAM modul. It’s just an interface between PAM and your own plugin (that you have to implement). To test it, you will need to add PATH of your plugin to the /etc/pam.d/login file (in case of getty-access test, for example), like described here.

This code can be used as an example. It will close access for all getty.

> cat /lib64/security/access.py

def pam_sm_authenticate(pamh, flags, argv):
  if str(pamh.service) == "login":
    return pamh.PAM_AUTH_ERR

You will also need to add this line to the /etc/pam.d/login file:

auth required pam_python.so access.py

This is just an example with login service or getty. Pam-python supports also, for example, ssh- and kdm-services. It supports many other interesting things. For more info look at documentation page.

OpenBSD SNMP v2c check

Posted in OpenBSD, python by anaumov on 11.03.2016

It seems like there are no OpenBSD SNMP checks on exchange.nagios.org or exchange.icinga.org. I found few checks for this operating system, but NO scripts use SNMPv2c or SNMPv3. Nobody trusts SNMP? Well, as we know, the most common alternative acronym for SNMP is “Security? Not My Problem!”, right? On the other hand, SNMP is the de facto standard for gathering information from network devices or servers.
Anyway, I have this free operating system at home and on this weekend I spent a bit time to play with SNMP. It makes fun to learn something new. SNMP servers on different systems support different OIDs/MIBs. OpenBSD includes an SNMP agent, snmpd(8), which supports all of the usual SNMP functions, and also offers visibility into OpenBSD-specific feautures such as packet filtering.

I concentrated on standard MIBs and wrote a small python script to monitor swap/memory usage, CPU load average and file system space usage. It looks like a common nagios plugin. In addition, it also shows information about network interfaces, file systems in tabular form and the list of running processes. Make it sense for people? 🙂
Keep reading…

HP-UX SNMP check

Posted in python, UNIX by anaumov on 29.02.2016

A few days ago I upload my first nagios plugin to exchange.nagios.org. This is a small python script to monitor HP-UX servers using SNMPv2c. SNMP is the de facto standart for gathering information from network devices or servers. Many different devices from many different vendors support it as a management protocol.

SNMP manages information via a Management Information Base (MIB), which is a three-like structure that contains hierarchical information in ASN.1 format. MIBs can be referred to by name or numbers. I use following MIBs:

cpu 1 min
cpu 5min
cpu 15 min
memory total
memory free
FS mount point
FS usage size
FS total size
FS free space

This plugin checks the file system space usage, the CPU load average and the memory usage. Very simple, but maybe somebody will find it usefull. To get information via SNMP I use snmpwalk. I was not sure about python SNMP library and I think to install snmpwalk should be no problem for everybody.

Unfortunately HP-UX doesn’t support hrStorageTable, i.e. we can’t get storage information in tabular form. So, I implement it himself. It looks a bit like “df -h”:

> ./check_snmp_hpux.py public file-systems

    SIZE                USED                AVALIABLE           FILE SYSTEM
    2.0 Gb        765.3 Mb (37.37 %)        1.3 Gb (62.63 %)    /                             
    6.0 Gb         22.9 Mb (0.37 %)         6.0 Gb (99.63 %)    /home                         
   32.0 Gb         11.0 Gb (34.31 %)       21.0 Gb (65.69 %)    /opt                          
    8.0 Gb        444.4 Mb (5.42 %)         7.6 Gb (94.58 %)    /tmp                          
    8.0 Gb          3.1 Gb (38.15 %)        4.9 Gb (61.85 %)    /usr                          
   54.8 Gb         39.5 Gb (72.05 %)       15.3 Gb (27.95 %)    /var                          
   15.0 Gb         11.6 Gb (77.63 %)        3.4 Gb (22.37 %)    /oracle                       
  300.0 Mb        228.6 Mb (76.19 %)       71.4 Mb (23.81 %)    /oracle/origlog               
  300.0 Mb        228.6 Mb (76.19 %)       71.4 Mb (23.81 %)    /oracle/mirrlog               
    1.7 Tb          1.0 Tb (61.17 %)      667.9 Gb (38.83 %)    /oracle/oradata               
  100.0 Gb        129.6 Mb (0.13 %)        99.9 Gb (99.87 %)    /oracle/archives              
   64.0 Gb          3.6 Gb (5.69 %)        60.4 Gb (94.31 %)    /oracle/flash_area        
   15.0 Gb         10.0 Gb (66.96 %)        5.0 Gb (33.04 %)    /oraclog                      
  300.0 Mb        199.7 Mb (66.57 %)      100.3 Mb (33.43 %)    /oraclog/oraclog              
  300.0 Mb        199.7 Mb (66.57 %)      100.3 Mb (33.43 %)    /oraclog/mirrlog              
   12.0 Gb          5.8 Gb (48.63 %)        6.2 Gb (51.37 %)    /oraclog/oradata              
   20.0 Gb         79.7 Mb (0.39 %)        19.9 Gb (99.61 %)    /oraclog/archives             
    2.4 Gb         17.7 Mb (0.72 %)         2.4 Gb (99.28 %)    /oraclog/flash_area

> ./check_snmp_hpux.py public fs:/ 80 90
OK: FS usage 37.37 % [ 765.4 Mb / 2.0 Gb ]|usage=37;80;90;0;100
> echo $?

> ./check_snmp_hpux.py public mem 80 90
OK: Memory usage 77.89 % [99.6 Gb / 127.8 Gb]|usage=77;80;90;0;100
> echo $?

> ./check_snmp_hpux.py public cpu 1 2
OK: CPU load average 0.18, 0.19, 0.21 |'1 min'=0.18;1.0;2.0;0;0 \
'5 min'=0.19;1.0;2.0;0;0 '15 min'=0.21;1.0;2.0;0;0

As you can see, the output of all checks includes performance data to be able to create graphs in Nagios, Icinga, Centreon or another monitoring tools. In case of CPU check we will get 3 graphs: for 1 min, 5 min and 15 min CPU load average.

If plugin gets no SNMP answer, it returns UNKNOWN status (return code 3).

> ./check_snmp_hpux.py public file-systems
Sorry, no SNMP (hrStorage) answer from
> echo $?

> ./check_snmp_hpux.py public fs:/ 80 90
UNKNOWN: No SNMP answer from
> echo $?

> ./check_snmp_hpux.py public mem 80 90
UNKNOWN: No SNMP answer from
> echo $?

Tested on the HP-UX 11i v3 running on Itanium:

> ./check_snmp_hpux.py public os
HP-UX rasr0177 B.11.31 U ia64 0826591079

This plugin is free software. I use github as a hosting service and development platform. Feel free to ask me about details, report bugs or just give a feedback. Have fun 😉

SQLAlchemy – examples, tips and tricks

Posted in python by anaumov on 06.06.2014

Мне не так часто приходится работать с базами данных, поэтому каждый раз, когда все же нужно тянуть какие-нибудь данные или наоборот – загружать их в БД, приходиться вспоминать базовые команды и искать свои старые python-скрипты. Решил написать для себя маленькую шпаргалку, дабы не приходилось каждый раз спрашивать google. Это просто python-примеры для SQLAlchemy. Пост будет постоянно дополняться и обновляться в зависимости от изменений в самой библиотеке. Другие примеры вы можете найти на wikibooks.org или на Python Central.
Если у вас есть какие-нибудь замечания или вопрсоы по поводу кода, не стесняйтесь оставлять комментарии.
Читать дальше…

Redirection and subprocess.Popen()

Posted in python by anaumov on 24.05.2013

Потерял вчера несколько часов из-за неправильного использования перенаправления в python. Как мы знаем, чтобы запустить новый процесс и прочитать его вывод, в python существует метод Popen() из модуля subprocess. Вот так, к примеру, мы можем прочитать вывод “ls -la“:

#!/usr/bin/env python
import subprocess as sp

ls = sp.Popen(["ls", "-la"], stdin=sp.PIPE, stdout=sp.PIPE).communicate()[0]
print ls

Т.е. аргументы поочереди передаются в качестве первого параметра Popen. Исходя из этой логики, команда с перенаправлением просто должна содержать символ “>” в качестве одного из аргументов. Однако эта фича shell, и в python, как я понял, либо не работет вообще, либо работает как-то совсем по-другому. Притом интерпретатор в этом случае ведет себя тоже достаточно странно: мы не получаем ни сообщений об ошибке, ни вывода команды, которую хотим вызвать.

Чтобы выполнить что-то типа “prog –option < something" мы должны передать это something через communicate() метод:

msg = sp.Popen(["/usr/bin/spamc", "--headers"], stdin=sp.PIPE, stdout=sp.PIPE)
(stdout, stderr) = msg.communicate(str(message))
print stdout

В этом случае перенаправление будет выполнено без проблем: мы вызываем прогамму /usr/bin/spamc с параметром –headers и перанаправляем ей message. Message должна быть строкой, поэтому сначала скармливаем ее str().

Migration to PySide

Posted in python, Qt by anaumov on 08.09.2012

Как вы знаете, в начале этого года официально закончилась миграция PySide в инфраструктуру Qt project.

В связи с этим я решил начать переписывать все свои PyQt4 приложения на PySide. Есть несколько способов оптимизации кода для работы с обоими bindings, но это по моему мнению добавление “костылей”, которые только испортят красоту кода вашего проекта. К тому же я не вижу никаких причин оставаться на PyQt4. Похоже, что большинство подсистем будет мигрированно в PySide в ближайшем будущем.

О различиях в API можно проконсультироваться в вики, на странице Differences_Between_PySide_and_PyQt.
Кстати, книга Rapid GUI Programming with Python and Qt хоть и рассматривает PyQt4 (все примеры в книге), будет так же полезна PySide программистам. Как я уже сказал, различия в API есть, но миграция не должна вызвать больших проблем.

PyCon 2012

Posted in python by anaumov on 14.08.2012

Programming Paradigms (Stanford)

Posted in C, Lisp, python by anaumov on 03.02.2012

Lecture by Professor Jerry Cain for Programming Paradigms (CS107) in the Stanford University Computer Science department.

Programming Paradigms (CS107) introduces several programming languages, including C, Assembly, C++, Concurrent Programming, Scheme (Lisp), and Python. The class aims to teach students how to write code for each of these individual languages and to understand the programming paradigms behind these languages.

Thanks a lot for sharing 😉

Guido van Rossum on the History of Python

Posted in python by anaumov on 26.11.2011

Guido van Rossum created the Python language – http://www.python.org/ – and is Benevolent Dictator for Life of the Python community. He has taken Python from a hobby into one of the most popular languages today, powering internet giants like Google. He is currently employed by Google, where he spends half his time on Python. On October 20th, 2011, he spoke at Dropbox Headquarters on the roots of the language and its evolution over the last 21 years.

RPM and Python

Posted in python by anaumov on 19.07.2011

В процессе тестирования, особенно если проводится мониторинг всей системы, часто нужно получить информацию об изменениях в пакетах (распаковать, сравнить версии, создать git-репозиторий, сравнить изменения), а так же возможно составить на основании этого какую-то статистику. Каждая компания-разработчик имеет свой арсенал подобного ПО. Как правило это небольшие скрипты, заточенные под конкретные задачи.
Создание подобного ПО дело совсем не хитрое. Давайте рассмотрим случай работы с RPM. В качестве языка будем использовать python.
Читать полностью…