Bezpieczeństwo PASOE Manager

O bezpieczeństwie PASOE pisałem już niejednokrotnie i jest to związane z różnymi aspektami zabezpieczeń. Tym razem będzie krótko o Tomcat Manager i OpenEdge Manager, o czym zresztą obiecałem w poprzednim artykule.

Służą one do zarządzania aplikacjami webowymi i wielosesyjnymi agentami i przy braku odpowiedniego zabezpieczenia mogą stać się potencjalnym celem ataków hakerskich.

Progress zdecydowanie zaleca podjęcie następujących działań w celu zapewnienia bezpieczeństwa instancji PASOE i wdrożonych aplikacji internetowych (ten proces jest dobrze opisany w dokumentacji):

  • Ograniczenie dostępu do adresów URL dla zarządzania zdalnego poprzez Tomcat Manager i OpenEdge Manager.
  • Zmiana domyślnego hasła dla serwera Tomcat (tomcat/tomcat).

Pierwsze z nich polega na edycji tekstowego pliku: CATALINA_BASE/webapps/[manager]/META-INF/context.xml, a konkretnie elementu: <Valve className=”org.apache.catalina.valves.RemoteAddrValve…”

Poniżej widać fragment pliku context.xml dla wersji OE 12.8 – element Valve nie był wzięty w komentarz, a więc dostęp zdalny był możliwy tylko dla adresu localhost (127.0.0.1), co widać poniżej (we wcześniejszych wersjach element był zakomentowany i domyślny dostęp był dla wszystkich adresów).

Dostęp do tego samego managera wpisując domyślny adres routera jest już zabroniony.

Drugim zabezpieczeniem jest zmiana domyślnego hasła tomcata (i ew. nazwy użytkownika, tomcat:tomcat) w plikach instancji PASOE xml oraz w OpenEdge Managaer/Explorer.

Zaczynamy od zrobienia kopii plików instancji ../conf/server.xml oraz ../conf/tomcat-users.xml.

W pliku server.xml wyłączamy domyślną bazę z danymi użytkowników UserDatabase, tzw. Realm.

<!-- feature:begin:UserDatabase:off
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
            resourceName="UserDatabase" />
 feature:end:UserDatabase:off -->

Dodajemy nową bazę, która używa klasy CredentialHandler class, np.:

<!-- feature:begin:UserDatabase-pbkdf2:on -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
            resourceName="UserDatabase" >
          <CredentialHandler className="org.apache.catalina.realm.SecretKeyCredentialHandler"
               algorithm="PBKDF2WithHmacSHA512"
               iterations="10000"
               saltLength="16"
               keyLength="256" />
        </Realm>
<!--     feature:end:UserDatabase-pbkdf2:on -->

PBKDF jest zaawansowanym i bardzo silnym algorytmem szyfrowania haseł, zgodnym z FIPS. Im więcej podamy iteracji tym lepiej ale i proces szyfrowania jest wolniejszy. Zapisujemy plik server.xml.

Teraz trzeba wygenerować nowe hasło. W proenv, przechodzimy do katalogu: DLC/servers/pasoe/bin. Przykładowa instrukcja, która uwzględnia parametry szyfrowania z pliku server.xml wygląda następująco:
digest -a PBKDF2WithHmacSHA512 -i 10000 -s 16 -k 256 -h “org.apache.catalina.realm.SecretKeyCredentialHandler” mycat
Mycat to oczywiście moje nowe hasło w miejsce tomcat.

Komenda zwraca zaszyfrowaną wartość hasła, którą oczywiście musimy skopiować i umieścić w pliku CATALINA_BASE/conf/tomcat-users.xml.
Warto przy okazji zrobić przegląd kont użytkowników i zostawić tylko niezbędne. Nazwę admina też można zmienić. Ja zmieniam teraz tylko hasło.

Startuję moją instancję testową i widzę, że OE Manager nie jest dostępny. Tak samo będzie jeśli będę chciał dostać się do managera: localhost:8833.

Widoki analizy sesji i żądań są wyłączone.

Podobnie metryki dla aplikacji.

Wchodzę do konfiguracji i w polach Tomcat manager password oraz OpenEdge manager password wpisuję moje nowe hasło jako zwykły tekst: mycat.
Trzeba chwilę odczekać aż zmiany będą widoczne, a następnie nacisnąć przycisk Test Connection.

Połączenie powinno się teraz udać.

A widoki są dostępne

Nowe hasła są zapisane w pliku %DLC%\properties\pasmgr.properties

Nowe hasło musimy podać także jeśli chcemy wejść z poziomu managera tomcata i przeglądać dane np. przy użyciu Swaggera.

Modele zabezpieczeń serwera PASOE

“Temat PASOE nie schodzi z afisza”, można powiedzieć, ale co zrobić jeśli to wciąż kluczowy produkt w architekturze aplikacji progressowych i w dodatku wciąż rozwijany. Zwykle piszę na temat, z którym związane są pewne niejasności i pytania. Tak samo jest tym razem. Chodzi o tworzenie instancji PASOE jako model deweloperski i produkcyjny. Każdy z tych modeli związany jest z inną licencją, jako że jest to nieco inny produkt. Zestawmy ich krótkie porównanie:

Model deweloperski:

  • kompilacja procedur
  • brak zabezpieczeń w konfiguracji
  • wbudowana aplikacja web oeabl.w (ROOT). Wszystkie warstwy transportowe wdrożone i włączone
  • ograniczenie do 5 jednoczesnych żądań. 1 agent
  • dołączona instancja dla testów (oepas1)

Model produkcyjny:

  • brak możliwości kompilacja procedur w locie
  • konfiguracja zabezpieczona
  • wbudowana aplikacja web oeabl.w (ROOT). Wszystkie warstwy transportowe wdrożone ale wyłączone
  • liczba jednoczesnych żądań jest ograniczona jedynie licencją
  • brak instancji testowej

Jeśli mamy utworzoną już instancję i chcemy sprawdzić jej tryb to możemy zrobić to na różne sposoby np.:

  • w pliku openedge.properties:srvrAppMode=development | production
  • w pliku appserver.properties:spsc.as.security.model=development | production
  • uruchomić komendę:pasman env –I nazwa_instancji

OK, przejdżmy teraz do kilku opcji tworzenia instancji. Po pierwsze mamy do dyspozycji parametr -f, którego użycie spowoduje wdrożenie wszystkich aplikacji webowych (pliki .war) z ${DLC}/servers/pasoe/webapps do nowej instancji. Przyda on się nam za chwilę.

Po drugie, możemy tworzyć instancję o określonym trybie, korzystając z parametru -Z. Mamy tu trzy wartości do wyboru: dev, prod i pas.

Większość z Was słyszała zapewne o dwóch pierwszych ale mało kto wie po co jest i jak działa trzecia wartość (pas). Napiszę o tym za chwilę.

Pierwsza watość (dev) najmniej nas tutaj interesuje; druga jest o wiele ciekawsza ponieważ stwarza wrażenie, że jeśli jej użyjemy to mamy od razu wersję produkcyjną. Załóżmy, że mamy licencję deweloperską i tworzymy nową instancję PASOE o nazwie myProdInst (numery portów są pominięte):

pasman create -Z prod %WRKDIR%\myProdInst

Pytanie brzmi: czy stworzyliśmy wersję produkcyjną czy wciąż deweloperską?

Sprawdzamy np. komendą pasman.


Widać, że znacznik security model = production.

Teraz zobaczmy warstwy transportowe dla domyślnej aplikacji webowej ROOT w narzędziu OEM/OEE (mozna alternatywnie sprawdzić w pliku konfiguracyjnym):


Wszystkie warstwy są wdrożone i wyłączone, jak dla modelu production.

Pomimo tego nasza instancja nie jest do końca produkcyjna i firma Progress zdecydowanie zaleca używanie w środowisku produkcyjnym licencji modelu produkcyjnego. Po pierwsze w naszej nazwijmy quasi-produkcyjnej instancji nie są wymagane r-kody, a więc procedury mogą być kompilowane w locie, co stanowi duże zagrożenie. Po drugie instancja nie może zmienić uprawnień w katalogu $DLC/servers/pasoe. I wreszcie nie można uruchomić więcej niż 1 agenta z 5 sesjami.

Powróćmy teraz do trzeciej wartości dla opcji -Z pas. Jest to instancja podobna do tej -Z dev ale domyślną aplikację ROOT (oeabl.war) zastępuje aplikacja noaccess.war. W rzeczywistości nie jest to serwer PASOE, a serwer aplikacji Tomcat.

Zróbmy test i stwórzmy przykładową instancję poleceniem (numery portów pominięte):

pasman create -Z pas %WRKDIR%\myProdPas


Widać, że nie ma żadnej aplikacji ABL oraz wdrożonej aplikacji webowej opartej na oeabl.war i start tekiej instancji przy pomocy komend OpenEdge np. pasman start czy tcman pasoestart nie powiedzie się. Uda się, natomiast start przy pomocy tcman start (to komenda Tomcata).

Jakakolwiek próba zdalnego dostępu do takiej instancji kończy się komunikatem “Request refused”. Instancja taka to zabezpieczony pusty kontener. Żeby z niego skorzystać musimy wdrożyć w nim aplikacje webowe. Metoda ta jest szczególnie wygodna gdy mamy wygenerowane pliki .OEAR (OpenEdge Application Archive), przy pomocy których możemy importować całe fragmenty architektury PASOE: aplikacje ABL, aplikacje webowe, zabezpieczenia… Pisałem o tym kiedyś krótko (pasman export/import). Po więcej zapraszam do dokumentacji.

Pozostaje jeszcze pytanie czy korzystać w instancjach produkcyjnych z managerów? Jeśli widzimy taką konieczność to oczywiście tak, ale trzeba je najpierw zabezpieczyć, o czym napiszę w następnym artykule.

PASOE i Load Balancing

Użytkownicy klasycznego AppServera pamiętają zapewne możliwość dość łatwej implementacji load balancingu, wykorzystującego proces przekierowujący NameServer.
W nowym serwerze aplikacji PASOE nie ma NameServera, nawet proces AdminServera nie jest niezbędny, jak zatem można zrealizować usługę LB?

Aby to osiągnąć musimy wykorzystać istniejące rozwiązania innych firm. Dostępnych jest wiele produktów i ciężko jest wybrać jedną najlepszą opcję load balancingu do wszystkich zastosowań, dla każdego z klientów. Najbardziej znane to: Apache proxy LB, Tomcat LB i Amazon Elastic LB. Skupmy się na dwóch pierwszych rozwiązaniach. Oba wykorzystują popularny serwer Apache.

Apache proxy LB – popularny serwer Apache HTTP działa tutaj jako serwer proxy przekierowujący żądania HTTP do adresu URL, który jest unikalny dla każdej instancji PASOE. Ta metoda nie wymaga konfiguracji po stronie PASOE gdyż całe równoważenie obciążenia wykonywane jest przez serwer Apache.

Wadą tego typu rozwiązania jest to, że serwer Apache nie monitoruje statusu uruchomionych instancji i jeśli któraś z nich ulegnie awarii, serwer kontynuuje próbę wysyłania żądań do niedostępnej instancji i żądania zostają utracone.

Tomcat load balancing – równoważenie obciążenia realizuje tutaj wyznaczona instancja PASOE, która musi nazywać się lb (lub jklb). Jest to jej jedyne zadanie i jeśli instancja ma wdrożone jakiekolwiek aplikacje (np. ROOT) należy je usunąć (undeploy). Poniższy rysunek ilustruje przykładową konfigurację. Oprócz instancji lb można utworzyć opcjonalnie instancję do monitorowania procesu równoważenia. Musi się ona nazywać status (lub jkstatus) i także nie może mieć wdrożonych żadnych aplikacji.

Jednakże, wdrożenie load balancingu jest także możliwe gdy obie w/w instancje są skonfigurowane tylko wirtualnie, tzn. bez ich fizycznego utworzenia (bez komendy tcman create lub pasman create). Jaka jest różnica tego drugiego rozwiązania? Jest nieco łatwiejsze i bardziej nadaje się do demonstracji. Można je także wykorzystać w lokalnej instalacji. W produkcyjnym środowisku lepiej skorzystać z pierwszego sposobu i możliwości jakie dają nam fizyczne instancje PASOE, szczególnie jeśli idzie o bezpieczeństwo.

Tutaj omówię drugi, krótszy sposób. Na początek musimy mieć zainstalowany web serwer  Apache np. 2.4 (w sumie można użyć innego web serwera ale Apache jest najczęściej stosowany ze względu choćby na wspólna konfigurację Apache i Tomcata). Działanie serwera można rozszerzyć poprzez załadowanie odpowiednich modułów, i tak w pliku  conf\httpd.conf odkomentowujemy linie:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so

To powinno wystarczyć, ale dla konkretnej wersji Apache może okazać się, że potrzebny jeszcze jakiś moduł; sprawdzajcie logi jeśli są kłopoty z uruchomieniem.

Teraz tworzymy przykładowe 2 instancje PASOE , między którymi będzie rozkładać się obciążenie. Nazwiemy je node1 i node2.

pasman create -p 8815 -P 8816 -s 8817 -j 8818 %WRKDIR%\node1
pasman create -p 8825 -P 8826 -s 8827 -j 8828 %WRKDIR%\node2

Teraz powiem kilka słów o powyższych instrukcjach. Pasman jest tą samą instrukcją co tcman, ale ma charakter jedynie globalny, tzn. nie ma tej instrukcji w podkatalogu bin dla instancji, przez co łatwiej z niej korzystać w przypadku zarządzania kilkoma instancjami. Stosuje się tutaj parametr -I, po którym podajemy nazwę instancji.

Zwróćcie uwagę, że oprócz 3 portów, podałem jeszcze czwarty oznaczony jako -j dla protokołu AJP13

Jest to binarny protokół, który umożliwia przesyłanie żądań z serwera WWW do serwera aplikacji. Jest on powszechnie stosowany w systemach ze równoważeniem obciążenia; obsługuje również monitorowanie stanu serwera.

Po utworzeniu instancji musimy aktywować protokół AJP13 w pliku conf/server.xml dla każdej instancji.

pasman feature -I node1 AJP13=on
pasman feature -I node2 AJP13=on

Krótka uwaga – zdecydowanie nie zalecam dokonywania zmian w pliku server.xml ręcznie, gdyż łatwo przy tym o błędny wpis i kłopot z prawidłowym uruchomieniem instancji PASOE.

Teraz trzeba skonfigurować obie instancje tak, aby obsługiwały naszą aplikację: ustawić połączenie z bazą, sprawdzić czy są włączone protokoły APSV / REST / WEB / SOAP,
warto wdrożyć (jeśli nie są) manager.war i oemanager.war oraz na koniec jakąś aplikację do testowania. Tych czynności nie będę tutaj opisywał bo była już wcześniej o nich mowa.

Uruchamiamy instancji node1 komendę tcman workers (lub pasman workers -I node1). W podkatalogu utworzy się plik w podkatalogu node1\temp\workers.properties z wygenerowanymi ustawieniami dla load balancingu. Ustawienia te należy traktować raczej jako wzorzec ponieważ wymagają pewnej edycji.

Do tego pliku za chwilę wrócimy; teraz w pliku Apache conf\httpd.conf na samym końcu dodajemy ustawienia:

#workers.properties load balancing config
LoadModule jk_module modules/mod_jk.so
JkWorkersFile C:/Apache24/lbfiles/conf/workers.properties
JkShmFile C:/Apache24/lbfiles/logs/mod_jk.shm
JkLogFile C:/Apache24/lbfiles/logs/mod_jk.log
JkLogLevel info
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
JkMount /* jklb
JkMount /status jkstatus 

Jest tutaj ładowany jeszcze jeden moduł mod_jk.so, jeśli nie ma go w instalacji trzeba pobrać go z sieci i dodać katalogu modules. Wygodnie jest umieścić dane związane z LB w osobnym katalogu, tutaj jest to katalog lbfiles.

W tym katalogu musimy umieścić plik workers.properties odpowiednio skorygowany. Załączam plik z ustawieniami dla niniejszego przykładu.

# List of worker server instances
worker.list=jklb,jkstatus
#
# Global properties
#
# worker.maintain=60
# Common worker properties referenced by individual workers
worker.common.type=ajp13
worker.common.host=PCPTU3
worker.common.socket_timeout=10
worker.common.connect_timeout=10000
worker.common.socket_keepalive=true
worker.common.ping_mode=I
worker.common.ping_timeout=10000
worker.common.retry_interval=100
worker.common.recovery_options=7
# worker.common.connection_ping_interval=10000
# worker.common.fail_on_status=0
# worker.common.max_packet_size=8192
# worker.common.recover_time=60

 

# properties for alias node1 with jvmRoute node1
worker.node1.port=8818
worker.node1.reference=worker.common
worker.node1.lbfactor=1

# properties for alias node2 with jvmRoute node2
worker.node2.port=8828
worker.node2.reference=worker.common
worker.node2.lbfactor=1

# Load balancer directives
worker.jklb.type=lb
#worker.jklb.port=8012
worker.jklb.balance_workers=node1,node2
worker.jklb.method=Request

# Status worker directives
worker.jkstatus.type=status
worker.jkstatus.read_only=False

Omówię niektóre ustawienia: worker.list=jklb,jkstatus – to lista instancji wirtualnych do LB i do monitorowania. Sa również ustawienia dla maszyn z obiema instancjami node1 i node2 (tutaj wszystko znajduje się na jednej maszynie).

worker.jklb.balance_workers=node1,node2 – to lista instancji obsługujących żądania klienckie. Na uwagę zasługują ustawienia: worker.node1.lbfactor=1 oraz worker.node2.lbfactor=1. Określają one udział instancji w procesie obsługi żądań. W tym przypadku podział jest 50%/50%. Jeśli np. node1 ma obsługiwać 2 razy więcej żądań to współczynnik trzeba ustawić 2 razy większy niż node2.

Startujemy obie instancje. Jaki będzie port w adresie URL jeśli mamy kilka instancji PASOE, obsługujących tą samą aplikację? Port w adresie będzie wskazywać na Apache serwer (domyślnie 80), reszta pozostaje bez zmian. Odświeżamy kilka razu adres URL, generując żądania do obsługi.

Jeśli chcemy podejrzeć status LB to podajemy w adresie URL: nazwa_hosta_lb/status, czyli tutaj: localhost/status.

Widać m.in, że obie instancje są aktywne, node2 ma mniej obsłużonych żądań i 2 błędy, co jest związane z moim czasowym wyłączeniem tej instancji (dla testu). Większość oznaczeń jest opisana po najechaniu na nie kursorem. To na razie tyle, jeśli chodzi o Load Balancing.