Bezpieczeństwo w PASOE cz. III – LDAP

W tym artykule powracamy do tematu związanego z bezpieczeństwem w PASOE, które było omawiane ok. pół roku temu. Dla przypomnienia: część I oraz część II. Warto przypomnieć sobie te wiadomości bo będziemy z nich zaraz korzystać.

Spring Security to elastyczny framework, w którym administrator może wybrać poziom  zabezpieczeń w zależności od wymagań związanych z aplikacją. Pokażę jak przeprowadzić uwierzytelnienie użytkowników, którzy nie znajdują się w bazie, nie są zdefiniowani w plikach tekstowych, ale w zewnętrznym systemie LDAP, na przykładzie Apache Directory.

Instalujemy Apache Directory Studio, które zawiera serwer LDAP oraz środowisko oparte na Eclipse, do stworzenia hierarchicznej struktury, zawierającej m.in. konta użytkowników.

Na temat tworzenia takiej struktury w ADS czy innym systemie LDAP (Active Directory, OpenLDAP, itp) można znaleźć wiele informacji w sieci. Bardzo przydatne na początku są filmiki instruktażowe na youtube. Ja omówię ten proces bardzo ogólnie, na potrzebę niniejszego artykułu. Po pierwsze dodajemy nowy serwer, nazwę go localhost.


W widoku Connections definiuję połączenie dla tego serwera o nazwie local: Hostname: localhost (lub IP), domyślny port 10389.

Następnie w zakładce Authentication, wybieram Bind DN or user: uid=admin,ou=system. Domyślne hasło: secret.

Uruchamiamy zdefiniowany serwer oraz połączenie i możemy przystąpić do tworzenia schematu. Trzeba pamiętać, że będzie on dostępny tylko przy aktywnym połączeniu.
Ten schemat można utworzyć na różne sposoby. Ja zacznę od stworzenia obiektu organizacji.
Pod głównym węzłem dc=example,dc=com dodajemy obiekt typu organization o nazwie o=progress
Pod tym elementem tworzymy dwa obiekty typu organizationalUnit ou=groups oraz ou=users.

Pod users dodajemy przykładowych użytkowników typu inetOrgPerson. Dodałem dwóch użytkowników – nasuwa się pytanie jak ich identyfikować, mamy pola cn i sn… – otóż dla identyfikacji z zewnątrz ważny jest atrybut uid (admin1 i user1) oraz userPassword, taki sam jak nazwa użytkownika. Czyli logując się jako zwykły użytkownik (Marek Webowy) podamy dane: user1, user1.

Po uwierzytelnieniu przychodzi pora na autoryzację. Ta jest realizowana za pomocą ról. Zaglądając do poprzednich artykułów możemy zobaczyć, że w pliku pasoe oeablSecurity.csv są zdefiniowane role ROLE_PSCUser, ROLE_PSCAdmin, ROLE_PSCDebug. Dla demonstracji wykorzystamy tylko jedna rolę i stworzymy jedną grupę PSCUser typu groupOfUniqueNames.

W grupie tej umieszczamy użytkowników, tzn. ich atrybuty cn (Common Name). Pamiętajmy, że pełna nazwa zawiera elementy aż do węzła głównego, czyli np. dla użytkownika user1:
cn=Marek Webowy,ou=users,o=progress,dc=example,dc=com.

OK, zrestartujmy połączenie. Możemy teraz przystąpić do konfiguracji PASOE. Ja tworzę nową instancję o nazwie ldapas bez dostępu do bazy.
Konfigurację tę przeprowadzimy w, omawianym już w poprzednich częściach, pliku [working directory]\[instancja pasoe]\webapps\ROOT\WEB-INF\oeablSecurity.properties.
Wybieramy proces zarządzający uwierzytelnieniem ldap oraz model logowania form.

http.all.authmanager=ldap
client.login.model=form

Następnie dodajemy parametry logowania do serwera ldap oraz filtry wyszukiwania informacji o użytkowniku oraz grupie, do której należy.

ldap.root.dn to definicja węzła, od którego zaczyna się wyszukiwanie informacji w strukturze, ldap.usersearch.base to z kolei węzeł, pod którym znajdują się definicje użytkowników, ldap.usersearch.filter określa zakończenie wyszukiwania po znalezieniu elementu uid.

Podobne parametry definiujemy dla grup. Całość wygląda mniej więcej tak:

 ## Required LDAP Server Authentication Manager configuration properties 
 ldap.url=ldap://localhost:10389
 ldap.manager-dn=uid=admin,ou=system
 ldap.manager-password=secret
 
 ldap.root.dn=o=progress,dc=example,dc=com

 ldap.usersearch.base=ou=users  
 ldap.usersearch.searchSubtree=true
 ldap.usersearch.filter=(uid={0})
 
 ldap.grouprole.attribute=cn
 ldap.groupsearch.base=ou=groups
 ldap.groupsearch.searchSubtree=true
 ldap.groupsearch.filter=(uniqueMember={0})

 ldap.authpopulator.rolePrefix=ROLE_

Podawanie hasła w postaci jawnej (secret), to niezbyt dobry pomysł. Można to wyeliminować kodując hasło przy pomocy komendy: stspwdutil encrypt [hasło].

Ostatni parametr (wartość domyślna ROLE_) określa prefix dla roli jaki będzie dodany do nazwy grupy, czyli dla PSCUser pełna nazwa będzie ROLE_PSCUser.

Według konwencji nazewnictwa w LDAP, nazwy ról mają być pisane dużymi literami. W LDAP są one zamieniane z automatu, my musimy zmienić je w pliku oeablSecurity.csv, np. z ROLE_PSCUser na ROLE_PSCUSER itd.

Restartujemy PASOE. Wchodzimy do linku testującego:
http://localhost:[port]/rest/_oepingService/_oeping
Podajemy dane: user1, user1.

Mamy dostęp do serwisu.

OK, ale zazwyczaj nie ma tak dobrze. Często zanim wszystko skonfigurujemy pojawia się komunikat o braku dostępu. W logach nic nie ma, gdzie szukać informacji? Musimy odblokować ilość informacji zapisywanych do logów. W pliku [working directory]\[instancja pasoe]\conf\logging-pasoe.properties zmieniamy poziom zapisów z WARN na DEBUG, a ERROR na INFO, jak poniżej. Konfigurację zapisów do logów możemy zrobić także z poziomu danej aplikacji, ale tutaj poniższa zmiana wystarczy. Uwaga! ta zmiana powoduje znaczne zwiększenie tekstu w logach.

W pliku ldapas_authn.[data].log (przypominam, że moja instancja nazywa się ldapas) znajduje się informacja o udanym uwierzytelnieniu. Informacje o nieudanych logowaniach znajdują się w pliku ldapas_authz.[data].log

[data i czas] ldapas ROOT:f:0000000a success user1

W logu ldapas.[data].log znajduja się m.in. wpisy:

...LdapAuthenticationProvider - Processing authentication request for user: user1...
...FilterBasedLdapUserSearch - Searching for user 'user1', with user search [ searchFilter: '(uid={0})', searchBase: 'ou=users  '...
...Searching for entry under DN 'o=progress,dc=example,dc=com', base = 'ou=users', filter = '(uid={0})'... 
...Found DN: cn=Marek Webowy,ou=users...
......
...Searching for roles for user 'user1', DN = 'cn=Marek Webowy,ou=users,o=progress,dc=example,dc=com', with filter (uniqueMember={0}) in search base 'ou=groups'...
......
...Granted Authorities: ROLE_PSCUSER;...

Wpisów jest wiele, ale można prześledzić proces wyszukiwania oraz uwierzytelniania i autoryzacji.

Następnym razem chciałbym pokazać jak PASOE korzysta z obiektu CLIENT-PRINCIPAL przy autoryzacji LDAP.

Bezpieczeństwo w PASOE cz. II

Część druga bezpieczeństwa w PASOE to raczej tylko suplement, związany z uwierzytelnieniem i autoryzacją użytkowników opartym o dane w plikach tekstowych w podkatalogu serwera aplikacji.

Po pierwsze, autoryzacja użytkowników z pliku users.properites. Każdy z nich ma przypisana rolę np.: ROLE_PSCUser, ROLE_PSCAdmin, ROLE_PSCDebug czy ROLE_None.

Skąd wiadomo, co te role oznaczają i gdzie są zdefiniowane? Żeby się dowiedzieć trzeba otworzyć plik: [working directory]\[instancja pasoe]\webapps\ROOT\WEB-INFF\oeablSecurity.csv.

Pliku oeablSecurity.csv określa kontrolę dostępu do adresów URL dla aplikacji webowych. Każdy wiersz w pliku jest uporządkowanym zestawem trzech wartości.

Odpowiadają one trzem atrybutom elementu przechwytującego URL w Spring Security, a mianowicie:
– wzorzec – wzorzec adresu URL, który może zawierać symbole wieloznaczne i wyrażenia regularne
– metoda – metoda dostępu HTTP
– dostęp – dozwolone role dostępu do zasobu.

Na poniższym obrazku widzimy kontrole dostępu dla poszczególnych warst transportowych. Najpierw APSV (metody HAD, GET, POST) i role, potem SOAP, REST, WEB, a następnie bardziej szczegółowe definicje dla wybranych URI.

Dodajmy, że ustawienia w tym pliku oraz w oeablSecurity.properties znajdują się w kilku lokalizacjach i mają charakter hierarchiczny, np. plik oeablSecurity.csv znajdziemy także w: [working directory]\[instancja pasoe]\conf\oeablSecurity.properties.csv, a oeablSecurity.properties jeszcze w kilku miejscach.

Pliki te bardziej zagnieżdżone dziedziczą ustawienia od plików znajdujących się w podkatalogach powyżej. Dzięki temu można precyzyjnie określić zabezpieczenia dla całej instancji PASOE, aplikacji ABL (agenta wielosesyjnego) czy aplikacji WEB.

Do tej pory podawane przez nas hasła były w postaci jawnej. Co jednak zrobić żeby je zaszyfrować, tak aby haker, który skopiuje pliki z dysku nie miał z nich pożytku?

Sprawa jest bardzo prosta. Po pierwsze zmieniamy nazwę procesu managera uwierzytelniającego dane z local na extlocal.

 

[working directory]\[instancja pasoe]\webapps\ROOT\WEB-INFF\oeablSecurity.properties.

Wartość parametru client.login.model=form lub basic.

Szyfrujemy hasło poleceniem: genspringpwd [hasło], np. genspringpwd password

Otrzymujemy: $2a$09$EU0wp9hga2zmfKBUg21nAeVObPBQQ3erbW53XCcJiQYr8s4QwoCki i tę wartość wstawiamy do pliku users.properites w miejsce jawnego hasła np. dla użytkownika myrestuser.

Restartujemy PASOE i logujemy się wpisując hasło niezaszyfrowane.

Sprawdzamy, że logowanie się powiodło.

Bezpieczeństwo w PASOE cz. I

Większość nowoczesnych, rozproszonych aplikacji korzysta z serwera aplikacji
dla logiki biznesowej. Korzystanie z serwera PASOE jest bardzo dobrym rozwiązaniem
ponieważ może on bezpośrednio obsługiwać wszystkie typy klientów, jest łatwo skalowalny oraz
może zapewnić bezpieczeństwo aplikacji przy użyciu Spring Security.

Spring Security to framework, który koncentruje się na zapewnianiu uwierzytelniania i autoryzacji dla aplikacji.
Spring Security można rozszerzyć w celu spełnienia niestandardowych wymagań. Poziom bezpieczeństwa zależy od nas, ponieważ to my decydujemy, którą technologię zabezpieczeń wybieramy (lokalne pliki, LDAP, OEREALM, STS i inne).

PASOE zawsze uruchamia Spring Security w sposób domyślny. Nawet próba dostępu do serwisu REST przez użytkownika anonymous wywoła proces uwierzytelniania, autoryzacji i ew. utworzenia tokena Spring Security oraz obiektu ABL CLIENT-PRINCIPAL.

Żeby to zademonstrować, stwórzmy i wystartujmy przykładowy serwer PASOE (ja nazwę go mysec). Od wersji OE12.1 każdy serwer aplikacji startuje serwis ping – jest on dostępny pod adresem URL:
http://localhost:[port]/rest/_oepingService/_oeping


Oznacza to, że PASOE wystartował i mamy dostęp do serwisu ping.

Zobaczmy jakie są domyślne ustawienia dla tego serwisu, który procuje w web aplikacji ROOT. Otwieram plik oeablSecurity.properties w lokalizacji C:\WrkOpenEdge121\mysec\webapps\ROOT\WEB-INF.

Znajdujemy wpis: client.login.model=anonymous.

Oznacza on, że każdy użytkownik (anonymous) ma dostęp do wszystkich aplikacji w ROOT.

Powyżej, widzimy wpis: http.all.authmanager=local. Oznacza on, że uwierzytelnienie jest lokalne w oparciu o użytkowników zdefiniowanych w pliku:
C:\WrkOpenEdge121\mysec\webapps\ROOT\WEB-INFF\users.properites.
Zawartość tego pliku jest następująca:

restuser=password,ROLE_PSCUser,enabled
restdebug=password,ROLE_PSCUser,ROLE_PSCAdmin,ROLE_PSCDebug,enabled
restadmin=password,ROLE_PSCUser,ROLE_PSCAdmin,enabled
restnone=password,ROLE_None,enabled

Chcemy teraz wymusić logowanie ze strony użytkownika – ustawiamy wartość client.login.model=form.
Restartujemy PASOE i ponawiamy dostęp do URL oeping.
Bazując na danych w pliku users.properties wpisujemy: restuser i password. W efekcie dostajemy dostęp do serwisu (patrz. pierwszy rysunek).


Żeby pokazać, że dostaliśmy token od systemu, otwórzmy nową kartę w przeglądarce (ta sama sesja) i wpiszmy URL dla serwisu ping. Dostajemy od razu dostęp, bez logowania.

Jeśli podamy dane restnone i password lub jakiekolwiek inne, nie występujące w pliku, dostęp jest zabroniony.

Dodajmy teraz do pliku users.properties własnego użytkownika, o uprawnieniach takich jak restuser i zmieńmy nazwę na myrestuser.

restuser=password,ROLE_PSCUser,enabled
myrestuser=password,ROLE_PSCUser,enabled
restdebug=password,ROLE_PSCUser,ROLE_PSCAdmin,ROLE_PSCDebug,enabled
restadmin=password,ROLE_PSCUser,ROLE_PSCAdmin,enabled
restnone=password,ROLE_None,enabled

Aby zmiany zostały wczytane, restartujemy PASOE komendą:
pasman pasoestart -v -I mysec -restart

Logujemy się jako nowy użytkownik…

…który ma dostęp do serwisu rest.

O dalszych podstawach zabezpieczeń napiszę w drugiej części.