baseciq.org

Autoryzacja w Apache 2.2 w zewnętrznym programie (i/lub bazie danych MySQL) za pomocą FastCGI

Apache ma sporo sposobów na autoryzację. Jednakże, jakoś nikt za bardzo nie pomyślał o autoryzacji w bazie danych MySQL. Oczywiście, są moduły auth_mysql przeróżne, ale mi się niestety nie udało ich skompilować. Jest także moduł mod_authnz_external, jednakże ma jedną wadę - uruchamia proces autoryzacyjny przy praktycznie każdym requeście, a to nie jest zbyt porządane.

Jedną z metod, na szybkie wykonywanie CGI, jest mechanizm FastCGI. Poza swoimi podstawowymi funkcjami, jest on w stanie także uruchomić jakiś proces i korzystać z niego jako z backendu do autoryzacji.

Co będziesz potrzebować?

Z pierwszymi dwoma rzeczami powinieneś sobie poradzić bez większych problemów instalując PLD :) Pozycja nr. 3 rozumie się chyba sama przez siebie jeżeli tutaj zawitałeś. Z czwartym składnikiem rozwiązania, niestety musisz poradzić sobie sam. Ale i na to postaram się coś poradzić.

Let's go

Kilka słów wstępu. Od czasów Apache 2.2, moduł auth (czy może powinienem go nazwać AuthBasic?) powoduje to, iż Apache wymaga podania "dostawcy" podstawowej autoryzacji za pomocą dyrektywy AuthBasicProvider. Niestety, z tego co mi wiadomo (pamiętaj: mogę się mylić), FastCGI nie rejestruje się w Apache jako dostawca tejże autoryzacji. Problem został zauważony, opisany i załatany w Debianie (więcej informacji tutaj) a także w PLD. Jeżeli posiadasz inną dystrybucję, na stronie z raportem błędu debiana jest link do łatki debianowej która łata także i ten problem. Niestety, ja nie posiadam nic innego niż PLD i Debian, więc nie poprowadzę Cię na piechotę.

Przejdźmy do rzeczy. Oto przykładowa konfiguracja Apache aby się autoryzował za pomocą zewnętrznego skryptu:

<Directory /home/cokolwiek>
                AuthType Basic
                AuthName "ProtectedRealm"
                AuthBasicProvider fastcgi
                FastCgiAuthenticator /usr/local/sbin/http-auth-lms.pl
                FastCgiAuthenticatorAuthoritative On
                require valid-user
</Directory>

Jak widać, konfiguracja w sumie zbyt dużo nie odbiega od normalnej autoryzacji, poza podaniem ścieżki do skryptu w perlu. W moim wypadku jest to skrypt który autoryzuje się w bazie LMS'a. Po co i dlaczego, piszę w tekście który też gdzieś tutaj jest :)

Skrypt znalazłem na jednej ze stron i go odpowiednio dostosowałem (czytaj: wykorzystałem tylko szkielet obsługi CGI), ale niestety nie pamiętam gdzie i u kogo. w perlu wygląda tak:

#!/usr/bin/perl -Tw

use FCGI;
use CGI (':standard');
use DBI;

# konfiguracja dostępu do bazy danych:

my $dbhost="localhost";
my $dbuser="mysql";
my $dbpw="p4ssw0rd";
my $dbname="lms";
my $dbport="3306";

# połączenie do bazy jako takiej:

my $dbh = DBI->connect(
    "DBI:mysql:database=$dbname:host=$dbhost:port=$dbport",
    $dbuser,
    $dbpw,
    {PrintError=>0}
);

# zapytanie o hasło:

my $verify_passwd = $dbh->prepare(
    'select passwd from users where login=?'
);

my $s200 = '200 OK';
my $s401 = '401 Authorization Required';

my $q = FCGI::Request();

while ($q->Accept >= 0) {
    my $u = '';
    my $p = '';
    
    if (exists $ENV{REMOTE_USER})   { $u = $ENV{REMOTE_USER}; }
    if (exists $ENV{REMOTE_PASSWD}) { $p = $ENV{REMOTE_PASSWD}; }
    
    if (not ($u and $p)) {
        spit($s401);
    } else {

        # na wszelki wypadek - jeżeli baza danych się rozłączyła,
        # połączmy się ponownie i przygotujmy zapytanie:

        unless(defined $dbh and defined $verify_passwd) {
            $dbh = DBI->connect("DBI:mysql:database=$dbname:host=$dbhost:port=$dbport",$dbuser,$dbpw,{PrintError=>0});
            $verify_passwd = $dbh->prepare('select passwd from users where login=?');
        }

        $verify_passwd->execute($u) if(defined $verify_passwd);
        
        my ($dp) = $verify_passwd->fetchrow_array;

        if (not ($dp)) {
            spit($s401);
        } elsif ($dp eq crypt($p, $dp)) {
            spit($s200);
        } else {
            spit($s401);
        }
    }
}

undef $verify_passwd;
$dbh->disconnect if(defined $dbh);
undef $dbh;
exit 0;

sub spit {
    my $s = shift;
    print header('text/plain', $s), $s;
}

Jak widać, skrypt ten autoryzuje się w bazie LMSa. Dzięki niemu, masz jakiś przykład jak to dalej pociągnąć i jak sobie z tym poradzić :)