Linux

Subversion, webdav, LDAP e restrizioni sulle cartelle

Se avete bisogno di configurare un server svn su Linux con autenticazione tramite LDAP, webdav ed inserire specifiche restrizioni su determinate cartelle, potete seguire le seguenti istruzioni.

  • Uno: dovete installare subversion e apache sul vostro server Linux (ometterò questa parte).
  • Two: dovete configurare webdav per accedere ad svn tramite http e configurare l’accesso tramite LDAP.

    Assicuratevi di avere i seguenti moduli di apache installati e configurati:

    LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
    LoadModule dav_module modules/mod_dav.so
    LoadModule dav_svn_module modules/mod_dav_svn.so
    LoadModule authz_svn_module modules/mod_authz_svn.so
    LoadModule authn_alias_module modules/mod_authn_alias.so
    

    Assunzioni:

    • Sono solito configurare subversion sotto la directory /srv/svn.
    • Gli utenti a cui è permesso di accedere a subversion devono appartenere al gruppo LDAP CN=SVN-AUTHORIZATION,OU=Groups GSO,DC=test,DC=example,DC=com

    Modificate il file /etc/apache2/mods-enabled/dav_svn.conf (su Ubuntu, su altre distribuzioni potrebbe essere posizionato altrove) ed assicuratevi di avere le seguenti configurazioni:

    <Location /svn/>
      # Abilitare svn su webdav
      DAV svn
      # Impostare il parent path per repositori multipli
      SVNParentPath /srv/svn/
      # Impostare il tipo di autenticazione
      AuthType Basic
      # Impostare il nome dell'autenticazione
      AuthName "FLR Subversion Repository"
      # Impostare il file che conterrà le autorizzazioni (permessi)
      AuthzSVNAccessFile /etc/apache2/dav_svn.authz
      # Consentire di listare il parent path
      SVNListParentPath On
      # Utilizzare LDAP per l'autenticazione
      AuthBasicProvider ldap
      # Il server LDAP è autoritativo (cioè l'autenticazione finale è delegata a LDAP)
      AuthzLDAPAuthoritative On
      # Utente di bin per LDAP
      AuthLDAPBindDN "CN=svnbind,OU=Users OS,DC=test,DC=example,DC=com"
      # Password dell'utente di bind per LDAP
      AuthLDAPBindPassword mypassword
      # LDAP URL
      AuthLDAPUrl "ldap://ldap_ip_address:389/DC=test,DC=example,DC=com?sAMAccountName?sub?(&(&(objectClass=user)(objectCategory=person))(memberof=CN=SVN-AUTHORIZATION,OU=Groups GSO,DC=test,DC=example,DC=com))"
    
      # E' necessario un utente valido
      Require valid-user
    </Location>
  • Three Create il file dei permessi /etc/apache2/dav_svn.authz
    Dovrà avere il seguente contenuto in base alle vostre esigenze:

    [groups]
    admin = matteo
    group1 = user1, user2, user3
    group2 = user2
    group3 = user4
    
    ###################################
    [/]
    * = r
    @admin = rw
    ###################################
    [repository1:/]
    * = rw
    ###################################
    [repository2:/]
    * =
    @admin = rw
    @group1 = rw
    ###################################
    [repository3:/]
    * =
    @admin = rw
    @group2 = rw
    @group1 = r
    ###################################
    [repository4:/]
    * = r
    @admin = rw
    [repository4:/trunk/sources]
    * = r
    @admin = rw
    @group3 = rw
    ###################################

    Adesso riavviate apache con /etc/init.d/apache2 restart

  • Quattro: create finalmente i repositori.

    Da root impartite i seguenti comandi:

    cd /srv/svn
    svnadmin create repository1
    chown www-data.www-data -R repository1
    svnadmin create repository2
    chown www-data.www-data -R repository2
    svnadmin create repository3
    chown www-data.www-data -R repository3
    svnadmin create repository4
    chown www-data.www-data -R repository4

    Siete adesso pronti per utilizzare subversion con account LDAP, webdav e restrizioni su directory personalizzate.

PHP Fatal error con PhpMyAdmin e APC

Se state utilizzando PhpMyAdmin insieme ad APC può capitare che abbiate degli errori come questi:
PHP Fatal error: Call to undefined function PMA_log_user() in /usr/share/webapps/phpMyAdmin/libraries/common.inc.php on line 914
PHP Fatal error: Call to undefined function PMA_select_language() in /usr/share/webapps/phpMyAdmin/libraries/auth/cookie.auth.lib.php on line 220
PHP Fatal error: Call to undefined function pma_generate_common_url() in /usr/share/webapps/phpMyAdmin/libraries/header_meta_style.inc.php on line 48
PHP Fatal error: Call to undefined function PMA_DBI_connect() in /srv/http/librolandia.it/test/phpmyadmin/libraries/common.inc.php on line 916
PHP Fatal error: Class 'PMA_Error_Handler' not found in /path/to/phpMyAdmin/libraries/common.inc.php on line 58
PHP Fatal error: Call to undefined function PMA_getenv() in /path/to/phpMyAdmin/libraries/common.inc.php on line 143

Se questo è il vostro caso avrete bisogno di disabilitare APC nella configurazione del virtualhost di PhpMyAdmin:

Alias /phpmyadmin "/usr/share/webapps/phpMyAdmin"
<Directory "/usr/share/webapps/phpMyAdmin">
      AllowOverride All
      Options FollowSymlinks
      Order allow,deny
      Allow from all
      php_admin_value open_basedir "/srv/:/tmp/:/usr/share/webapps/:/etc/webapps:/usr/share/pear/"
      php_admin_value apc.enabled 0
</Directory>

Inoltre modificate il filtro di APC nella sezione [APC] di php.ini:

[APC]
apc.filter="-/usr/share/webapps/phpMyAdmin/.*"

Riavviate Apache e phpMyAdmin dovrebbe a questo punto funzionare regolarmente.

Estrarre un’immagine multi-file u-boot in Python

Questo semplice pezzo di codice mostra come estrarre/decomprimere un’immagine u-boot multi-file creata con il comando mkimage, utilizzando Python. Il formato dell’immagine del file u-boot è abbastanza semplice:

64 byte per l'image header.
4 byte per la dimensione della prima immagine.
4 byte per la dimensione della seconda immagine.
...
4 byte a zero per la terminazione.
immagine1.
immagine2.
...

Dovete anche ricordarvi che ogni immagine deve essere paddata a 4 byte.

#!/usr/bin/env python

import sys

def toNumber(buf):
        """ Convert string in number """
        size = 0
        for b in buf: size=size*256+ord(b)
        return size

if len(sys.argv)!=2:
        sys.stdout.write('Usage %s uboot_image_file\n' % sys.argv[0])
        sys.exit(1)

f = open(sys.argv[1],'rb')
f.seek(64) # skip image header

parts = []

# get file size of all images
while True:
        buf = f.read(4)
        size = toNumber(buf)
        if size==0: break
        parts.append(size)

i = 0
for size in parts:
        pattern = 'file%d.img' % i
        p = open(pattern, 'wb')
        buf = f.read(size)
        p.write(buf)
        p.close()
        if size%4 != 0:
                # 4 byte padding
                f.read(4-(size%4))
        i+=1

f.close()

Esempio di Segnali e Slot con QThread su PySide

In questi giorni ho iniziato a studiare PySide. Dopo alcuni giorni spesi a leggere un bel po’ di materiale online, ho pensato che un esempio reale potesse essere utile per chi come come me intende cimentarsi con PySide. In questo esempio vi vieme mostrato come potete implementare un segnale custom (MySignal) insieme all’uso dei thread con QThread.

Il codice seguente crea una finestra con due pulsanti: il primo avvia e ferma un thread (MyThread) che lancia un processo il quale stampa sullo standard output della console un punto ogni secondo continuamente. Il secondo bottone vi permette solo di avviare un altro thread (MyLongThread) che stampa un asterisco sullo standard output ogni secondo per 10 secondi e poi esce.

Questo esempio utilizza la versione delle API 2 (introdotta con PyQt 4.5) per connettere i segnali agli slot.

#!/usr/bin/env python2

import sys, time
from PySide.QtGui import *
from PySide.QtCore import *

class MySignal(QObject):
	sig = Signal(str)

class MyLongThread(QThread):
	def __init__(self, parent = None):
		QThread.__init__(self, parent)
		self.exiting = False
		self.signal = MySignal()

	def run(self):
		end = time.time()+10
		while self.exiting==False:
			sys.stdout.write('*')
			sys.stdout.flush()
			time.sleep(1)
			now = time.time()
			if now>=end:
				self.exiting=True
		self.signal.sig.emit('OK')

class MyThread(QThread):
	def __init__(self, parent = None):
		QThread.__init__(self, parent)
		self.exiting = False

	def run(self):
		while self.exiting==False:
			sys.stdout.write('.')
			sys.stdout.flush()
			time.sleep(1)

class MainWindow(QMainWindow):
	def __init__(self, parent=None):
		QMainWindow.__init__(self,parent)
		self.centralwidget = QWidget(self)
		self.batchbutton = QPushButton('Start batch',self)
		self.longbutton = QPushButton('Start long (10 seconds) operation',self)
		self.label1 = QLabel('Continuos batch')
		self.label2 = QLabel('Long batch')
		self.vbox = QVBoxLayout()
		self.vbox.addWidget(self.batchbutton)
		self.vbox.addWidget(self.longbutton)
		self.vbox.addWidget(self.label1)
		self.vbox.addWidget(self.label2)
		self.setCentralWidget(self.centralwidget)
		self.centralwidget.setLayout(self.vbox)
		self.thread = MyThread()
		self.longthread = MyLongThread()
		self.batchbutton.clicked.connect(self.handletoggle)
		self.longbutton.clicked.connect(self.longoperation)
		self.thread.started.connect(self.started)
		self.thread.finished.connect(self.finished)
		self.thread.terminated.connect(self.terminated)
		self.longthread.signal.sig.connect(self.longoperationcomplete)

	def started(self):
		self.label1.setText('Continuous batch started')

	def finished(self):
		self.label1.setText('Continuous batch stopped')

	def terminated(self):
		self.label1.setText('Continuous batch terminated')

	def handletoggle(self):
		if self.thread.isRunning():
			self.thread.exiting=True
			self.batchbutton.setEnabled(False)
			while self.thread.isRunning():
				time.sleep(0.01)
				continue
			self.batchbutton.setText('Start batch')
			self.batchbutton.setEnabled(True)
		else:
			self.thread.exiting=False
			self.thread.start()
			self.batchbutton.setEnabled(False)
			while not self.thread.isRunning():
				time.sleep(0.01)
				continue
			self.batchbutton.setText('Stop batch')
			self.batchbutton.setEnabled(True)

	def longoperation(self):
		if not self.longthread.isRunning():
			self.longthread.exiting=False
			self.longthread.start()
			self.label2.setText('Long operation started')
			self.longbutton.setEnabled(False)

	def longoperationcomplete(self,data):
		self.label2.setText('Long operation completed with: '+data)
		self.longbutton.setEnabled(True)

if __name__=='__main__':
	app = QApplication(sys.argv)
	window = MainWindow()
	window.show()
	sys.exit(app.exec_())

Per ulteriori informazioni potete dare un’occhiata ai seguenti link:
Documentazione sul modulo QThread: http://doc.qt.nokia.com/latest/qthread.html
Segnali e Slot di PySide: http://developer.qt.nokia.com/wiki/Signals_and_Slots_in_PySide
PyQt Api 2 con PySide: http://www.pyside.org/docs/pseps/psep-0101.html

Esportare una tabella MySQL in formato CSV utilizzando PHP

Il seguente codice PHP può essere utilizzato per exportare una tabella MySQL in formato CSV e quindi facilmente modificabile con MS Excel.

$link = mysql_connect($mysql_host,$mysql_user,$mysql_pass) or die('Could not connect: '.mysql_error());
mysql_select_db($mysql_db,$link) or die('Could not select database: '.$mysql_db);

$query = "SELECT * FROM $tablename ORDER BY id";
$result = mysql_query($query) or die("Error executing query: ".mysql_error());
$row = mysql_fetch_assoc($result);
$line = "";
$comma = "";
foreach($row as $name => $value)
{
	$line .= $comma . '"' . str_replace('"', '""', $name) . '"';
	$comma = ";";
}
$line .= "\n";
$out = $line;

mysql_data_seek($result, 0);

while($row = mysql_fetch_assoc($result))
{
	$line = "";
	$comma = "";
	foreach($row as $value)
	{
		$line .= $comma . '"' . str_replace('"', '""', $value) . '"';
		$comma = ";";
	}
	$line .= "\n";
	$out.=$line;
}
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=listino.csv");
echo $out;
exit;

Come eseguire comandi con privilegi di uno specifico utente in C and Python su Linux

Se avete accesso da root ma avete bisogno di eseguire alcuni applicazioni o script con credenziali di altri utenti potete semplicemente farlo direttamente dalla shell con:

su - username -c "command to execute"

Ma se invece avete bisogno di eseguire tale programma dalla vostra applicazione C/C++ avete bisogno di seguire questa procedura:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>

int main(int argc, char* argv[])
{
        if(argc != 3)
        {
                printf("Usage: %s [USERNAME] [COMMAND]\n",argv[0]);
                return 1;
        }
        char *env[16];
        char envc[16][64];
        struct passwd *pw = getpwnam(argv[1]);
        if(pw==NULL)
        {
                printf("User %s does not exists!\n",argv[1]);
                return 1;
        }

        sprintf(env[0]=envc[0],"TERM=xterm");
        sprintf(env[1]=envc[1],"USER=%s",pw->pw_name);
        sprintf(env[2]=envc[2],"HOME=%s",pw->pw_dir);
        sprintf(env[3]=envc[3],"SHELL=%s",pw->pw_shell);
        sprintf(env[4]=envc[4],"LOGNAME=%s",pw->pw_name);
        sprintf(env[5]=envc[5],"PATH=/usr/bin:/bin:/opt/bin");
        env[6]=0;

        initgroups(argv[1],pw->pw_gid);
        setgid(pw->pw_gid);
        setuid(pw->pw_uid);
        execve(argv[2],NULL,env);

        return 0;
}

Potete compilare ed eseguire il codice in questo modo:

[root@barracuda ~]# gcc mysu.c -o mysu
[root@barracuda ~]# id
uid=0(root) gid=0(root) gruppi=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),19(log)
[root@barracuda ~]# ./mysu matteo /bin/bash
[matteo@barracuda /root]$ id
uid=1000(matteo) gid=100(users) groups=100(users),3(sys),10(wheel),14(uucp),91(video),92(audio),93(optical),95(storage),96(scanner),97(camera),98(power),108(vboxusers)

Lo stesso risultato si puo’ ottenere anche in Python con pochissimo sforzo:

#!/usr/bin/env python

import sys,pwd,os

pw = pwd.getpwnam(sys.argv[1])
os.initgroups(sys.argv[1],pw.pw_gid)
env={"TERM":"xterm","USER":pw.pw_name,"HOME":pw.pw_dir,"SHELL":pw.pw_shell,"LOGNAME":pw.pw_name,"PATH":"/usr/bin:/bin:/opt/bin"};
os.setgid(pw.pw_gid);
os.setuid(pw.pw_uid);
os.execve(sys.argv[2],[],env);

Ridimensionamento partizione in ext4 su VMware player

Qualche giorno fa ho speso un pò di tempo per provare a ridimensionare una macchina virtuale con sopra Ubuntu 11.04 con la partizione di root formattata con il filesystem EXT4.

Il problema principale è che la versione attuale di VMware tools (8.4.6, build-385536) non supporta lo shrink su filesystem ext4. se infatti digitate sudo vmware-toolbox, la vostra partizione di root è formattata in ext4 e provate ad eseguire uno shrink, otterrete un messaggio di errore di questo tipo.

Tuttavia c’è un trucco per snellire l’immagine vmdk finale.
Eseguite questo comando da dentro la shell del sistema guest:

sudo dd if=/dev/zero of=/zero.raw bs=20480
rm -f /zero.raw

Poi, spengete la macchina virtuale e scaricate il tool vdiskmanager da VMware website.
A questo punto eseguite vmware-vdiskmanager con il parametro -k:

vmware-diskmanager.exe -k /path/to/image.vmdk

Questa operazione prenderà un pò di tempo ma alla fine otterrete un’immagine vmdk considerevolmente più piccola.

Come implementare l’algoritmo MAC_X919 in Python

Oggi insieme al mio amico Nicola stavo cercando in rete l’implementazione dell’algoritmo X9.19 in python. Purtroppo non l’abbiamo trovato da nessuna parte per cui ce lo siamo fatti da soli:

#!/usr/bin/env python2

import Crypto.Cipher.DES as des
import Crypto.Cipher.DES3 as des3

def mac_x919(key,data):
  while len(data) % 8 != 0:
    data += '\x00'
  des_key1 = des.new(key[0:8],des.MODE_CBC)
  des_key2 = des.new(key[0:8],des.MODE_ECB)
  buf = des_key1.encrypt(data)
  buf = buf[len(buf)-8:]
  buf = des_key2.decrypt(buf)
  des3_key = des3.new(key,des3.MODE_ECB)
  buf = des3_key.encrypt(buf)
  return buf[0:4]

mac = mac_x919('0123456789abcdef','test data to be encrypted')

Come clonare lo schema di un database MySQL in PHP

Per un mio cliente ho avuto bisogno di creare uno script in PHP che fosse in grado di esportare lo schema di un database MySQL su un altro database. Questo script supporta anche la gestione dei constraints.

Avrete solamente bisogno di configurare le variabili $DB_SRC_* and $DB_DST_* secondo le vostre necessità.

Qui di seguito trovate il codice che ho creato per questo scopo:

<?php

/********************* START CONFIGURATION *********************/
$DB_SRC_HOST='localhost';
$DB_SRC_USER='root';
$DB_SRC_PASS='password';
$DB_SRC_NAME='database1';

$DB_DST_HOST='localhost';
$DB_DST_USER='root';
$DB_DST__PASS='password';
$DB_DST_NAME='database2';

/*********************** GRAB OLD SCHEMA ***********************/
$db1 = mysql_connect($DB_SRC_HOST,$DB_SRC_USER,$DB_SRC_PASS) or die(mysql_error());
mysql_select_db($DB_SRC_NAME, $db1) or die(mysql_error());

$result = mysql_query("SHOW TABLES;",$db1) or die(mysql_error());
$buf="set foreign_key_checks = 0;\n";
$constraints='';
while($row = mysql_fetch_array($result))
{
        $result2 = mysql_query("SHOW CREATE TABLE ".$row[0].";",$db1) or die(mysql_error());
        $res = mysql_fetch_array($result2);
        if(preg_match("/[ ]*CONSTRAINT[ ]+.*\n/",$res[1],$matches))
        {
                $res[1] = preg_replace("/,\n[ ]*CONSTRAINT[ ]+.*\n/","\n",$res[1]);
                $constraints.="ALTER TABLE ".$row[0]." ADD ".trim($matches[0]).";\n";
        }
        $buf.=$res[1].";\n";
}
$buf.=$constraints;
$buf.="set foreign_key_checks = 1";

/**************** CREATE NEW DB WITH OLD SCHEMA ****************/
$db2 = mysql_connect($DB_DST_HOST,$DB_DST_USER,$DB_DST_PASS) or die(mysql_error());
$sql = 'CREATE DATABASE '.$DB_DST_NAME;
if(!mysql_query($sql, $db2)) die(mysql_error());
mysql_select_db($DB_DST_NAME, $db2) or die(mysql_error());
$queries = explode(';',$buf);
foreach($queries as $query)
        if(!mysql_query($query, $db2)) die(mysql_error());
?>

Utenti virtuali su vsftpd

Sono solito configurare vsftp come FTP server sui web server per permettere un accesso FTP sulla base del nome a dominio. Qualche giorno fa un mio cliente mi ha chiesto di creare multipli utenti ftp per un singolo dominio, ognuno con una root differente all’interno dello stesso dominio.

Questa è sempre stata la configurazione base di vsftpd (/etc/vsftpd.conf) che utilizzo:

listen=YES
anonymous_enable=NO
local_enable=YES
virtual_use_local_privs=YES
write_enable=YES
connect_from_port_20=YES
xferlog_enable=YES
pam_service_name=vsftpd
guest_enable=YES
guest_username=www-data
user_sub_token=$USER
local_root=/var/www/$USER
chroot_local_user=YES
hide_ids=YES
force_dot_files=YES
ftpd_banner=Welcome to my private FTP service.
local_umask=022

e questo è il mio /etc/pam.d/vsftpd

auth required pam_pwdfile.so pwdfile /etc/ftpd.passwd
account required pam_permit.so

La prima volta ho creato il file /etc/ftpd.passwd in questo modo:

htpasswd -c -d -b /etc/ftpd.passwd domain1.com <password>

Per gli utenti futuri, semplicemente va evitato il parametro ‘-c’:

htpasswd -d -b /etc/ftpd.passwd domain2.com <password>

Con questa configurazione tutti gli utenti hanno le seguenti credenziali:

  • host: domain1.com
  • username: domain1.com
  • password: password
  • port: 21
  • Root folder: /var/www/domain1/

Adesso il punto è: come possiamo creare utenti multipli per un singolo dominio e ognuno con una cartella root diversa?
La risposta è abbastanza semplice, seguitemi!

Create la directory /var/www/users e aggiungete la seguente linea alla fine di /etc/vsftpd.conf:

user_config_dir=/var/www/users

All’interno della directory /var/www/users create un file per ogni utente virtuale (per esempio per l’utente user1.domain1.com) contenente una linea con la directory base per quell’utente:

echo "local_root=/var/www/domain1.com/pub/user1" > /var/www/users/user1.domain1.com

Adesso aggiungete il nuovo nome utente e password al file /etc/ftpd.passwd come al solito:

htpasswd -d -b /etc/ftpd.passwd user1.domain1.com <password>

Riavviate il demone vsftpd e testate la vostra nuova configurazione!