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.

Kafka i OpenEdge cz. II

W poprzednim artykule pokazałem jak zainstalować i skonfigurować lokalne środowisko Kafki a teraz pokażę jak zintegrować je z OpenEdgem.
Niektórzy z Was, którzy będą potrzebować zintegrować się z istniejącą architekturą Kafki, będą zainteresowani od tego właśnie miejsca.

Najlepiej w tym celu sprawdzić dokumentację OE dla danej wersji np. online. Ważne, że trzeba posiadać wersję OE 12.5 lub wyższą.

Pierwszym krokiem jest pobranie biblioteki Apache Kafka C/C++ dla Windows lub Unixa (link Download package po prawej stronie). Minimalna wymagana wersja to 1.7, ale zaleca się pobranie ostatniej wersji.

Gdy plik mamy już pobrany zmieniamy jego rozszerzenie z nupkg na zip i rozpakowujemy. W przypadku Windows będą nas interesowały pliki z podkatalogu runtimes\win-x64\native\, które umieszczamy w katalogu roboczym naszej aplikacji ABL.

Dodajemy do PROPATH następujace biblioteki:

  • $DLC/tty/netlib/OpenEdge.Net.pl
  • $DLC/tty/messaging/OpenEdge.Messaging.pl

Teraz możemy wziąć się za napisanie procedury w ABL, a raczej za pobranie jej z dokumentacji i małą kastomizację.

/* producer.p */

using OpenEdge.Messaging.*.
using OpenEdge.Messaging.Kafka.*.

block-level on error undo, throw.

var RecordBuilder recordBuilder.
var KafkaProducerBuilder pb.
var IProducer producer.
var IProducerRecord record.
var ISendResponse response.

pb = cast(ProducerBuilder:Create("progress-kafka"), KafkaProducerBuilder).

pb:SetBootstrapServers("172.22.13.101:9092").
pb:SetClientId("test client").
pb:SetBodySerializer(new StringSerializer()).
pb:SetKeySerializer(new StringSerializer()).

pb:SetProducerOption("message.timeout.ms", "10000").

producer = pb:Build().

recordBuilder = producer:RecordBuilder.
recordBuilder:SetTopicName("mytopic").   

Find first customer.
recordBuilder:SetBody(name).

record = recordBuilder:Build().

response = producer:Send(record).

producer:Flush(1000).
    
repeat while not response:Completed:
    pause .1 no-message.
end.

if response:Success then do:
   message "Send successful" view-as alert-box.
end.
else do:
    undo, throw new Progress.Lang.AppError("Failed to send the record: " +
        response:ErrorMessage, 0).
end.
 

catch err as Progress.Lang.Error :
    message err:GetMessage(1) view-as alert-box.
end catch.

finally:
    delete object producer no-error.
end.

Po pierwsze wstawiam własny adres IP w metodzie SetBootstrapServers i nazwę topicu mytopic.
Potem ustawiam SetBodySerializer() (metoda ustawia proces serializacji dla treści wiadomości) na StringSerializer. Serializator zajmuje się konwersją danych na bajty w celu przesłania do topicu. OpenEdge udostępnia również MemptrSerializer i JsonSerializer, jak również tworzenie własnych niestandardowych serializatorów.
I wreszcie znajduję pierwszy rekord customer i ustawiam jego nazwę (name) jako wartość do przesłania.
Po uruchomieniu procedury sprawdzam co pojawiło się w topicu Kafki. Na czerwono zaznaczyłem wartość przesłanej wartości (Lift Tours).

Teraz pora na odczyt wartości z topicu Kafki w procedurze ABL.

/* consumer.p */

block-level on error undo, throw.
 
using OpenEdge.Messaging.ConsumerBuilder from propath.
using OpenEdge.Messaging.IConsumer from propath.
using OpenEdge.Messaging.IConsumerRecord from propath.
using Progress.Json.ObjectModel.JsonConstruct from propath.
using Progress.Json.ObjectModel.JsonObject from propath.
 
var ConsumerBuilder cb.
var IConsumer consumer.
var IConsumerRecord record.
//var Progress.Lang.Object messageBody.
var char messageBody.
 
cb = ConsumerBuilder:Create("progress-kafka").
 
// Kafka requires at least one bootstrap server host and port.
cb:SetConsumerOption("bootstrap.servers", "172.22.13.101:9092").

// Explicitly disable auto commit so it can be controlled within the application.
cb:SetConsumerOption("enable.auto.commit", "false").

// Identify the consumer group. The consumer group allows multiple clients to
//  coordinate the consumption of multiple topics and partitions
//cb:SetConsumerOption("group.id", "my.consumer.group").
cb:SetConsumerOption("group.id", "test-consumer-group").

// Specify whether the consumer group should automatically be deleted when
//  the consumer is garbage collected.
cb:SetConsumerOption("auto.delete.group", "true").

// Configure the consumer's deserializer in order to convert values from
//  the network messages to string objects.
cb:SetConsumerOption("value.deserializer", "OpenEdge.Messaging.StringDeserializer").

// Set the consumer starting position to the most recent message
cb:SetConsumerOption("auto.offset.reset", "latest").
     
// identify one or more topics to consume
cb:AddSubscription("mytopic").
     
// build the consumer
consumer = cb:Build().
          
// loop forever receiving and processing records.
repeat while true:
    // request a record, waiting up to 1 second for some records to be available
    record = consumer:Poll(1000).
         
    if valid-object(record) then do:
           
        // acknowledge the message so the client can resume where it leaves off
        // the next time it is started
        consumer:CommitOffset(record).

        MESSAGE record:TopicName VIEW-AS ALERT-BOX.   

       messageBody = record:Body:ToString().      
       message messageBody view-as alert-box.
      
    end.
         
end.
     
catch err as Progress.Lang.Error :
    message err:GetMessage(1) view-as alert-box.
end catch.

finally:
    delete object consumer no-error.
end.

Uruchamiamy procedurę producer.p i consumer.p. Widać, że pierwsza wyświetla komunikat o poprawnym wysłaniu, a druga prawidłową wartość pobraną z kolejki.

Na koniec jeszcze jedna informacja związana z ustawianiem wartości parametrów. Otóż można to zrobić na dwa sposoby: poprzez użycie metod akceptujących wartości silnie typizowane (tzw. strongly-typed) lub używając par nazwa-wartość.
Obie poniższe linie kodu są równoważne:

pb:SetProducerOption("bootstrap.servers", "172.22.13.101:9092").
pb:SetBootstrapServers("172.22.13.101:9092").

Kafka i OpenEdge cz. I

O integracji OpenEdge z Apache Kafka chciałem napisać od dawna. Najpierw musiałem pokonać pewne kłopoty techniczne, tak  aby pokazać pełna ścieżkę od instalacji tejże Kafki do napisania programów w ABL do wysyłania i pobierania danych, ale po kolei. Co to jest Kafka.

Na głównej stronie Apache Kafki możemy przeczytać w tłumaczeniu:

Apache Kafka to rozproszona platforma do strumieniowego przesyłania komunikatów typu open-source, z której korzystają tysiące firm w celu zapewnienia wydajnego przetwarzania danych, analizy przesyłania strumieniowego, integracji danych i aplikacji o znaczeniu krytycznym.

Komunikaty w Kafce pogrupowane są w tzw. topiki (tematy, ang. topic). Nadawca (producer), jak i odbiorca (consumer) powiązani są z jednym lub wieloma topikami.
Rzućmy okiem na uproszczony schemat systemu Kafki.

Kafka zapewnia przesyłanie danych w czasie zbliżonym do rzeczywistego ze źródeł, takich jak bazy danych, aplikacje, czujniki, urządzenia mobilne, usługi w chmurze itd.

Nadawcy i odbiorcy mogą być procesami napisanymi w różnych językach programowania, na różnych systemach operacyjnych; musi istnieć tylko mechanizm aby wpięli się do systemu. Dla klientów ABL również jest taki mechanizm ale o tym napiszę później.

Klientom Progressa ta architektura może przypominać produkty z grupy Sonic, jednakże Kafka jest technologią nieporównywalnie popularniejszą, stosowaną przez wiele wielkich i rozpoznawalnych firm jak Netflix, Twitter, Spotify, Cisco, LinkedIn i dziesiątki innych. Zresztą różnic jest więcej. Kafka jest platformą o wysokiej wydajności i skalowalności, przesyłane komunikaty nie są automatycznie kasowane z kolejki po odczytaniu (mogą być przechowywane bez ograniczenia czasowego) itd…

Komunikaty (nazywane także zdarzeniami lub rekordami) z danego topiku dopisywane są na końcu tzw. partycji. Partycja to uporządkowany rejestr komunikatów, mówiąc niskopoziomowo, jest to plik na dysku brokera, do którego zapisywane są komunikaty. Aby konsument był w stanie odebrać określony komunikat / sekwencję komunikatów – musi znać pozycję ostatnio przeczytanego komunikatu.

OpenEdge udostępnia nowe OpenEdge.Messaging API do wysyłania wiadomości do klastra Kafki poprzez Kafka producer i odbierania wiadomości z tego klastra przez Kafka consumer.

Tyle wstępnych informacji. Chcę Wam pokazać cały proces od zainstalowania Kafki na lokalnej maszynie aby każdy mógł przetestować ten system.

Kafka jest przeznaczona do uruchamiania w systemie Linux i Mac i nie jest przeznaczona do natywnego uruchamiania w systemie Windows. Dlatego w przypadku tej platformy zaleca się stosować technologię Docker lub WSL2.

WSL2 (Windows Subsystem for Linux 2) zapewnia środowisko Linux dla komputera z systemem Windows 10+, które nie wymaga maszyny wirtualnej.

Moja konfiguracja wygląda następująco: Windows 10, zainstalowany WSL2 oraz Windows Terminal, w którym mam dostęp do Linuxa Ubuntu.

Instaluję Apache Kafka korzystając z oryginalnej strony. Polega to (ach ten Linux!) jedynie na pobraniu i rozpakowaniu plików. Najpierw niezbędne jest zainstalowanie infrastruktury do utrzymywania i koordynowania brokerów. Może to być KRaft lub ZooKeeper. Ja wybieram to drugie rozwiązanie.

W Windows Terminal otwieram kilka zakładek dla Ubuntu.

W pierwszej uruchamiam ZooKeepera:

sudo bin/zookeeper-server-start.sh config/zookeeper.properties

W drugiej Kafkę:

sudo bin/kafka-server-start.sh config/server.properties

Jak widać cała operacja jest bardzo prosta. Mogę dalej podążać za instrukcją: dodać topik, okno consumera, nadawać i odbierać komunikaty. Ta testowa konfiguracja odwołuje się do parametru bootstrap-server, który zawiera listę par „hostname:port”, które adresują jednego lub więcej brokerów. W tym prostym przypadku dodanie topicu wygląda następująco:

kafka-topics.sh --create --topic quickstart-events --bootstrap-server localhost:9092

Zastopujmy teraz wszystkie procesy Kafki poprzez zamknięcie zakładek.

Ponieważ będę potrzebował mieć dostęp z zewnętrznej aplikacji ABL, więc zamiast wartości localhost muszę wstawić wartość ip “maszyny” z Linuxem. Jeśli ip wynosi np. 172.22.13.101, to tę wartość wstawiam w następujących miejscach.
plik: config/server.properties
listeners=PLAINTEXT://172.22.13.101:9092
jak poniżej:

plik: config/producer.properties
bootstrap-servers=172.22.13.101:9092

plik: config/consumer.properties
bootstrap-servers=172.22.13.101:9092

OK, uruchamiam ponownie procesy ZooKeepera i Kafki. W trzeciej zakładce dodaję topic mytopic,

bin/kafka-topics.sh --create --topic mytopic --bootstrap-server 172.22.13.101:9092

a następnie uruchamiam proces producera:

bin/kafka-console-producer.sh --topic mytopic --bootstrap-server 172.22.13.101:9092

a w oknie czwartym consumera:

bin/kafka-console-consumer.sh --topic mytopic --bootstrap-server 172.22.13.101:9092

Wysyłam przykładowe wiadomości w oknie producera.

Pojawiają się one natychmiast w oknie consumera.

OK, mamy zatem skonfigurowany prosty system Kafki i w następnym wpisie pokażę jak przyłączyć się do niego z poziomu aplikacji ABL.

PASOE i SSL cz. II

W poprzednim artykule omówiłem czynności wstępne związane z wygenerowaniem kluczy i certyfikatów. Czas teraz zająć się konfiguracją po stronie klienta ABL i przetestowaniem połączenia.
Gdy serwer przedstawia swój certyfikat klientowi, klient musi mieć (w celu weryfikacji) certyfikat zaimportowany do swojego magazynu certyfikatów.

OpenEdge udostępnia narzędzie certutil do importowania certyfikatu CA do magazynu certyfikatów OpenEdge w katalogu %DLC%\certs. W naszym przypadku składnia wygląda następująco:
certutil -import C:\OpenEdge128\keys\requests\mycert.cer

Każdy klient łączący się z instancją musi mieć kopię pliku certyfikatu wygenerowanego podczas importu (tutaj: 1cafad6a). Należy więc skopiować ten plik do katalogu %DLC%\certs po stronie klienta.

Teraz zajmiemy się PASOE. Przechodzimy do katalogu conf dla naszej instancji (tu: C:\WrkOpenEdge128\mypasoe\conf) i wykonujemy kopię zapasową pliku tomcat-keystore.p12 np. do pliku tomcat-keystore.p12.original.

Wykonujemy polecenie sslc, aby wyeksportować informacje z pliku PEM do magazynu kluczy Tomcat o nast. składni:
sslc pkcs12 -export -in [signed public key file name].pem -out tomcat-keystore.p12 -name [alias name]
W nazwie aliasu należy unikać dużych liter. Musimy pamiętać ustanowione wcześniej hasło dla PEM oraz podać dwa razy nowe hasło dla exportu (tutaj: MyExpPass).

sslc pkcs12 -export -in C:\OpenEdge128\keys\mycert.pem -out tomcat-keystore.p12 -name mysslkey

Teraz trzeba zaktualizować plik mypasoe/conf/catalina.properties a dokładnie dwa wpisy w sekcji JSSE keystore: psc.as.https.keypass musi mieć wartość naszego hasła do exportu, a psc.as.https.keyalias wartość aliasu, jak poniżej.

Po tych wszystkich zabiegach można przetestować połączenie. Zaczniemy od REST, czyli https:localhost:8814… Widać, że usługa zwraca dane.

Powinniśmy dodać także certyfikat do przeglądarki, ale tylko w przypadku prawdziwego certyfikatu, wydanego przez CA. Tutaj nie ma to znaczenia.

Teraz przetestujemy warstwę transportową APSV. Z procedury ABL łączymy się po https i uruchamiamy znaną już procedurę na appserwerze. Wszystko działa poprawnie.

PASOE i SSL cz. I

W niniejszym i następnym artykule chciałbym poruszyć ważny temat certyfikatów i połączenia PASOE z aplikacja kliencką z zastosowaniem protokułu SSL (TLS). TLS wyewoluował z protokołu SSL (zastępując go gdy jest to możliwe) i jest to standardowy protokół implementujący szyfrowanie wymagane w komunikacji HTTPS.

PASOE umożliwia określenie portu HTTPS podczas tworzenia instancji. Przypomnijmy sobie przykładową komendę:
pasman create -v -f -p 8813 -P 8814 -s 8815 %WRKDIR%\mypasoe
gdzie port komunikacji po HTTPS jest określony przez parametr -P (tutaj 8814).

Do nawiązania połączenia serwer-klient wykorzystywana jest technologia szyfrowania asymetrycznego lub inaczej Public Key Infrastructure (PKI). Po nawiązaniu połączenia i wymianie kluczy można już stosować mniej skomplikowane algorytmy szyfrowania symetrycznego. Prześledzimy tutaj przykładowy proces prowadzący do uzyskania i zaimportowania certyfikatu oraz zaimportowania certyfikatów do magazynów klucza po obu stronach. Każdy certyfikat cyfrowy powinien być podpisany przez wystawiający go urząd certyfikacji (CA). Może on być również certyfikatem z podpisem własnym (tzw. self-signed) ale tylko dla zastosowania w fazie testowania i rozwoju, i w żadnym razie nie w środowisku produkcyjnym.
Cały ten proces wymaga użycia narzędzi OpenEdge:

  • PKIUTIL – do generowania pary kluczy: prywatny, publiczny, a następnie importowania certyfikatu do magazynu kluczy dla serwerów
  • CERTUTIL – do importowania nowych certyfikatów dla klientów
  • SSLC – w OpenEdge dodano implementacje OpenSSL. Można ją wykorzystać opcjonalnie do utworzenia certyfikatu z podpisem własnym wygenerowanym dla klucza prywatnego (dla testów) oraz do wielu innych operacji.

Zaczniemy od wygenerowanie pary kluczy prywatny/publiczny.

Otwieramy proenv jako administrator
Przechodzimy do katalogu %DLC%\keys\requests

pkiutil -keysize 2048 -newreq mycert
Będziemy musieli utworzyć hasło PEM (tutaj: Mypass1) – to nasze hasło należy zapisać bo będzie jeszcze nam potrzebne.
Będziemy pytani o szereg informacji składających się na tzw. Distinguished Name (DN): symbol kraju, województwo, organizacja itd. Nie wszystkie są niezbędne, ale najważniejsze z nich to nazwa organizacji i domeny dla naszego serwera. Przykładowe dane widać poniżej.


Otwieramy proenv jako administrator
Przechodzimy do katalogu %DLC%\keys\requests

Zostały w nim utworzone pliki:
1. mycert.pk1 – klucz prywatny, którego pilnie strzeżemy
2. mycert.pk10 – klucz publiczny + informacje o Twojej organizacji. Służy on do utworzenia tzw. żądania podpisania certyfikatu (CSR), które należy przesłać do urzędu certyfikacji (CA).

Po przesłaniu pliku klucza publicznego (mycert.pk10 w tym przykładzie) do CA, powinniśmy uzyskać podpisany certyfikat TLS, który jest zazwyczaj plikiem z rozszerzeniem .crt lub .cer.
My posłużymy się jednak tutaj certyfikatem z własnym podpisem wygenerowanym dla klucza prywatnego. Przypominam, że można go stosować wyłącznie dla celów testowych.
(Również dla celów testowych możemy skorzystać z darmowego serwisu getaCert).

sslc req -key C:\OpenEdge128\keys\requests\mycert.pk1 -new -x509 -days 360 -out C\OpenEdge128\keys\requests\mycert.cer

Jeśli otrzymalibyśmy certyfikat z CA należałoby go skopiować do katalogu %DLC%\keys\requests\

Następnie musimy wygenerować plik PEM (Privacy-enhanced Electronic Mail), zawierający prywatny klucz serwera oraz certyfikat.
Wykonujemy następujące polecenie (będziemy musieli podać wcześniej utworzone hasło!):
pkiutil -import mycert C:\OpenEdge128\keys\requests\mycert.cer

Plik mycert.pem pojawi się w katalogu %DLC%\keys


Na tym kończę ten odcinek, w następnym zajmiemy się klientem i przetestowaniem połączenia SSL.

Rejestrowanie instancji PASOE

Pisząc o rejestrowaniu instancji serwera aplikacji, miałem na myśli komendę tcman register. Jest to bardzo istotna komenda przy przenoszeniu instancji do innej lokalizacji (np. z serwera deweloperskiego do produkcyjnego). Ponieważ, jak zauważyłem, nie jest ona dostatecznie znana lub mylona z inną podobną komendą, postanowiłem przybliżyć Wam ten temat.

Zacznijmy od przykładowej instancji o nazwie mypasoe z web serwisem CustRest, połączonej z bazą danych sports2000. Pierwszym krokiem jest zatrzymanie tej instancji i skopiowanie całego katalogu mypasoe wraz ze wszystkimi podkatalogami.
Na drugiej maszynie jest instalacja tej samej wersji OpenEdge z jedną instancją PASOE oepas1.

Wgrywamy skopiowany katalog do katalogu roboczego i uruchamiamy polecenie:
pasman register mypasoe %WRKDIR%\mypasoe
Fizycznie polega to na dodaniu wpisu do pliku (w windows): %DLC%\servers\pasoe\conf\instances.windows (w systemie UNIX jest to plik instances.unix).

oepas1=C:\WrkOpenedge128\oepas1
mypasoe=C:\WrkOpenedge128\mypasoe

W środowisku OEE/OEM pojawia się nowy PASOE.

Upewniamy się czy zostały przeniesione aplikacje webowe (CustRest).

Żeby wszystko działało poprawnie należy podłączyć instancję do bazy danych. W parametrach agenta sprawdzamy jak takie podłączenie zostało zdefiniowane. Należy je odtworzyć na bieżącej maszynie lub zmodyfikować parametry.

Pozostało przetestowanie dostępu do web serwisu.
http://localhost:8813/CustRest/rest/CustRestService/customer
Jak widać poniżej wszystko działa.

Na koniec jeszcze istotna uwaga: rejestrujemy tę samą wersję co na maszynie żródłowej! Przeniesienie na inną wersję (różniącą się niekiedy tylko wersją Service Pack) może się nie udać.

Obiecałem wspomnieć o podobnej komendzie typu pasman (tcman) register. Chodzi tu o rejestrowanie instancji PASOE jako serwis Windows.
Składnia jej jest następująca: pasman service [nazwa_serwisu] -I [nazwa_instancji] register
Ja użyję tej samej nazwy serwisu co nazwa instancji. Komenda będzie wyglądała więc następująco:
pasman service mypasoe -I mypasoe register
Należy uruchomić ją oczywiście jako administrator a potem odnaleźć usługę na liście jako Progress Application Server.

Podobną komendą można wyrejestrować serwis.

Problem z AdminServerem OE12

Chyba każdy użytkownik OpenEdge’a miał lub miewa problemy z AdminServerem – centralną usługą do zarządzania innymi procesami OE. Problemy te mogą być różnej natury i różne są sposoby radzenia sobie z nimi (np. zmiana portu, restart usługi czy całego systemu itd).

Ja opiszę inny problem, który może pojawić się dla OE 12.2 i wyższej, co jest związane z instalacją Javy, która jest wymagana podczas instalacji OE. Miałem ostatnio taki właśnie problem dla OE 12.8.3 i wykorzystam go aby pokazać jak sobie radzić w takiej sytuacji tym bardziej, że mam już sygnały od Was, że macie podobny problem.

Po pierwsze, AdminServer pracuje, co możemy sprawdzić prostą komendą proadsv -query.

Problem pojawia się w środowisku OE Explorer/OE Management gdzie proces jest widoczny jako Offline a dostęp do niego zastrzeżony (Access Denied). Z tego powodu nie możemy również zarządzać innymi zasobami OE z poziomu tego środowiska.

Od wersji OE 12.2 instalator tworzy usługę Fathom_12.x z danymi uwierzytelniania LOCAL SERVICE zamiast LOCAL SYSTEM (ze względu na bezpieczeństwo i obsługę Java 11 dla serwera WWW Tomcat). Te uprawnienia mogą być niewystarczające i musimy jest zmienić. Wchodzimy do ustawień dla zdalnego AdminServera (od 12-tki każdy AdminServer jest zdalny).

Wybieramy Edit.

I podajemy login i hasło administratora systemu operacyjnego.

Po zmianie AdminServer jest już Online.

Na koniec trzeba pamiętać, że przy każdej zmianie hasła do systemu (czasem jest ona wymuszona co jakiś czas) zaktualizować je także dla AdminServera.

OpenEdge 12.8

Nareszcie mamy na rynku następną wersję LTS (Long-Term Support) OE 12.8. Poprzednia OE 12.2 pojawiła się 4 lata temu. 12.8 zawiera elementy wprowadzane w kolejnych wersjach Innovation 12.3-12.7 włącznie, co z grubsza opisałem na tym blogu. Teraz zobaczymy co ciekawego jest w samej wersji 12.8.

Zacznijmy od administracji PASOE. W poprzednim artykule opisałem nową komendę OEMANAGER, stosowaną do przeglądu statystyk i podstawowych operacji. Osoby zainteresowane odsyłam bezpośrednio do tego wpisu.

Inną ciekawą i bardzo ważną funkcją jest zapowiadana technologia Dynamic Data Masking (DDM) służąca do dynamicznego zaciemniania lub maskowania wrażliwych danych w bazie przed nieautoryzowanymi użytkownikami. Przykładem może być maskowanie w danych osobowych pola “pesel” lub w danych finansowych pola “wynagrodzenie”.

Technologia ta składa się niejako z dwóch etapów. Pierwszy należy do administratora bazy i polega na włączeniu funkcji poleceniem PROUTIL [baza] -C ENABLEDDM. Komenda ta sprawdza czy baza jest licencjonowana do użycia DDM. Następne polecenie PROUTIL [baza] -C ACTIVATEDDM uaktywnia DDM w bazie (dostępne są także opcje DEACTIVATEDDM i DISABLEDDM).

DDM do kontrolowania uprawnień przyznawanych użytkownikom w zakresie maskowania danych wykorzystuje konfigurację opartą na rolach. Administrator DDM może skonfigurować maskę nad polami tabeli, która ukrywa wrażliwe dane w zestawie wynikowym zapytania, a także utworzyć i przypisać nowe tagi autoryzacyjne do ról zdefiniowanych przez użytkownika.

Drugim etapem jest zatem zdefiniowanie własnych ról oraz znaczników autoryzacji i przypisanie ich do zdefiniowanych ról, przydzieleniu ról uwierzytelnionym użytkownikom, konfiguracja masek i znaczników autoryzacji dla pól tabeli. Etap ten realizujemy wykorzystując interfejs IDataAdminService, który udostępnia zestaw właściwości i metod obsługujących operacje CRUD związane z DDM. Poniżej zamieszczam wzięty z dokumentacji przykład utworzenia własnej roli TestUser.

USING OpenEdge.DataAdmin.*.

VAR DataAdminService oDAS. VAR IRole oRole.
VAR LOGICAL lResult.

ASSIGN oDAS = NEW DataAdminService(LDBNAME("DICTDB")).

oRole = oDAS:NewRole("TestUser"). 
oRole:Description = "A Test User".
// This role will be used for DDM
oRole:IsDDM = true.
lResult = oDAS:CreateRole(oRole). 

DELETE OBJECT oDAS.

W OpenEdge jest systematycznie rozwijana technologia wizualizacji danych poprzez standard OpenTelemetry i wykorzystanie zewnętrznych aplikacji typu APM (Application Performance Monitoring).
W 12.8 dodano możliwość monitorowania procesów agentów PASOE oraz klientów ABL. W tym drugim przypadku należy uruchomić sesję ABL z parametrem -otelconfig myotelConfig.json, wskazującym na plik konfiguracyjny w standardzie JSON. Przykładowy taki plik zamieszczam poniżej:

{
"OpenTelemetryConfiguration": {
        "exporters": {
            "otlp": {
                "grpc": [
                    {
                        "endpoint": "http://localhost:4317",
                        "span_processor": "batch",              
                     }
            ]
        }
      }
    },
    "OpenEdgeTelemetryConfiguration": {
        "trace_procedures": "Customer*.p",
		"trace_classes": "*",
        "trace_abl_transactions": true,
        "trace_requires_parent": true,
        "trace_request_start": true
    }
}

Plik ten określa m.in. proces OTel Collectora (port:4317), a także maskę monitorowanych plików np. każda procedura zawierająca w nazwie słowo Customer i wszystkie klasy (*). O pełnej konfiguracji monitorowania przy pomocy Open Telemetry napiszę innym razem.

A teraz kilka słów na temat administrowania bazą. Pojawiła się komenda PROUTIL [baza] -C PROBE [state] która sprawdza aktualny operacyjny stan bazy. Sprawdzane są trzy stany (parametr state):

  • startup – czy baza danych zakończyła uruchamianie
  • liveness – czy broker bazy danych działa i odpowiada
  • readiness – czy baza danych może wykonywać operacje (np. CRUD)

Jeśli wykonanie komendy z parametrem –verbose nie wyświetli żadnych informacji, będzie to oznaczać sukces.

Administratorzy wykorzystujący możliwość zwiększania wartości niektórych parametrów poleceniem PROUTIL… INCREASETO mogą teraz wygenerować plik .pf z aktualnymi ustawieniami podczas runtime’u. Daje to możliwość zapamiętywania i porównywania różnych konfiguracji. Plik możemy wygenerować wykorzystując tablice VST lub z poziomu narzędzia PROMON -> R&D Advanced Options -> 4. Administrative Functions -> 17. Generate parameter file

Dużym zainteresowaniem cieszyła się nowa komenda PROUTIL… TABLEREORG wprowadzona w OE 12.7, która pozwala na przeładowanie tablicy i polepszenie stopnia fragmentacji i rozrzucenia rekordów przy pracującej bazie. Podczas ładowania wykorzystuje się sortowanie rekordów wg indeksu. W OE 12.7 musiał to być indeks unikalny, jednakże czasami lepszą wydajność można uzyskać dla indeksu nieunikalnego ale często używanego w aplikacji. W OE 12.8 indeksy mogą już być nieunikalne.

Inne ciekawe i ważne nowości to np. podniesie wersji Spring Security 6.1.4 oraz Tomcata 10.1.15. Ma to swoje implikacje podczas migracji z wcześniejszych wersji PASOE.
W Developer’s Studio dodano typ projektu .NET z narzędziami: Class Browser, Content Assist, Outline View, co ma pomóc w programowaniu przy użyciu .NET 6.

Jest jeszcze sporo innych nowinek po które zapraszam do dokumentacji.

PASOE – OEMANAGER

Właśnie pojawiła się nowa wersja OpenEdge LTS 12.8 a wraz z nią sporo nowości. Poświęcę im cały artykuł, ale to następnym razem. Teraz chciałbym skupić się na jednym elemencie dotyczącym serwera aplikacji PASOE, a mianowicie na narzędziu OEMANAGER.
Pamiętacie może, że dla klasycznego appservera czy webspeeda można było uruchomić z poziomu wiersza poleceń (proenv) komendę ASBMAN czy WTBMAN aby uzyskać status procesu i podstawowe metryki.
Podobnie jest z OEMANAGER – można uzyskać wszystkie potrzebne metryki w jednym wywołaniu, w przeciwieństwie do innych sposobów jak wywołania API. Ponadto, narzędzie OEMANAGER może być wykorzystywane podczas runtime’u.
Aby można było z niego skorzystać instancja musi zawierać wdrożone aplikacje: OpenEdge Manager (oemanager.war) i Tomcat Manager (manager.war). Możemy wdrożyć je odpowienimi poleceniami do istniejącej instancji lub stworzyć nową z już wdrożonymi tymi aplikacjami używając opcji -f. Poniżej zamieszczam komendę utworzenia takiej instancji mypasoe. Do niedawna zalecani nie instalować tych aplikacji w środowisku produkcyjnym ale obecnie nie ma takich przeciwskazań o ile odpowienio je zabezpieczymy. Jednym takim elementem zabezpieczeń jest oczywista zmiana domyślnych haseł. Po resztę informacji odsyłam do dokumentacji. Ja w tym przykładzie pozostanę przy domyślnych hasłach, a dlaczego, to zaraz pokażę.

pasman create -v -f -p 8813 -P 8814 -s 8815 %WRKDIR%\mypasoe
Teraz uruchamiam instancję poleceniem:
pasman oeserver -start -I mypasoe
Jest to nowsza forma komendy tcman (pasman) pasoestart, wprowadzona od OE 12.5. Po prawidłowym uruchomieniu procesu Tomcata nie oznacza, że wszystkie komponenty OpenEdge zostały uruchomione. Polecenie tcman -oeserver start sprawdza logi pod kątem takich błędów. Polecenie posiada kilka jeszcze innych opcji, a więc ci, którzy mają wersję 12.5+ powinni korzystać właśnie z niej.

Po uruchomieniu oemanager (z poziomu katalogu bin dla danej instancji) dostajemy poniższą długą listę opcji.

     [echo]  TCMAN Shortcuts:
     [echo]
     [echo]  oemanager query    - Use TCMAN to query the PAS instance
     [echo]
     [echo]  oemanager startup  - Use TCMAN to start the PAS instance
     [echo]                       [OPTIONAL] timeout=300 - Time (seconds) to wait for a proper startup
     [echo]
     [echo]  oemanager shutdown - Use TCMAN to stop the PAS instance
     [echo]                       [OPTIONAL] timeout=300 - Time (seconds) to wait for a proper shutdown
     [echo]
     [echo]
     [echo]  Support Tools:
     [echo]
     [echo]  oemanager inventory - Bundle useful PAS instance files (as .zip) for support tickets
     [echo]
     [echo]
     [echo]  Status/Info:
     [echo]
     [echo]  oemanager status - [RO] Obtain MSAgent/connection status information for an ABL App
     [echo]                     [OPTIONAL] basemem=819200 - Minimum memory threshold (bytes) to consider as 'unused' agent sessions
     [echo]
     [echo]  oemanager stacks - [RO] Obtain stack information for all MSAgents for an ABL App
     [echo]
     [echo]  oemanager flush  - [RO] Flush the available deferred log buffer to agent log file
     [echo]
     [echo]  oemanager locks  - [RO] Display database users and their table locks related to an MSAgent-Session
     [echo]                     This utilizes a single DBConnection; edit the 'locks' task in build.xml to add more as necessary
     [echo]                     Note: Only provides session data if using self-service DB connections for OE versions under 12.5
     [echo]                     [REQUIRED] pf=[PF_NAME] - PF file to use for database connection(s)
     [echo]
     [echo]  oemanager users  - [RO] Alias for 'locks' task
     [echo]
     [echo]
     [echo]  Agent Management:
     [echo]
     [echo]  oemanager add     - Add (read: start) one new MSAgent for an ABL App
     [echo]
     [echo]  oemanager close   - Perform a 'soft restart' of an ABL App (runs: status, flush + trimhttp + stop, status)
     [echo]                                       For this task the 'trimhttp' will be called with the termination option 1 (forced)
     [echo]                      [REQUIRED] webapp=[WEBAPP_NAME] - WebApp for Tomcat Manager to terminate active client sessions
     [echo]                                  The given WebApp is expected to be associated with the provided ablapp name
     [echo]                      [OPTIONAL] sleep=1 - Sleep time in minutes after stop, prior to final 'status' output
     [echo]
     [echo]  oemanager clean   - Alias for 'close' task [Deprecated]
     [echo]
     [echo]  oemanager refresh - Refresh ABL Sessions for each MSAgent for an ABL App (OE 12 Only)
     [echo]                      Note: This will essentially terminate all sessions (gracefully),
     [echo]                            and prepare the Agent to pick up any R-code changes
     [echo]
     [echo]  oemanager reset   - Reset an aspect of each MSAgent for an ABL App
     [echo]                      [REQUIRED] resettype=stats [stats|logs]
     [echo]
     [echo]  oemanager stop    - Gracefully stop one or all MSAgents (+stacks output) for an ABL App
     [echo]                      [OPTIONAL] waitfinish=120000 - How long to wait (milliseconds) if the MSAgent is busy serving a request
     [echo]                      [OPTIONAL]  waitafter=60000  - Additional time to wait (milliseconds) before killing [hard stop] the MSAgent
     [echo]                      [OPTIONAL]        pid=[AGENT_PID] - Numeric process ID for a specific MSAgent to be stopped
     [echo]
     [echo]
     [echo]  Session Management:
     [echo]
     [echo]  Note: All trim actions listed below will write application stack information to a file.
     [echo]
     [echo]  oemanager trimsingle - Trim a single ABL Session (via the Agent Manager) for a specific MSAgent
     [echo]                         [REQUIRED]          pid=[AGENT_PID]  - Numeric process ID of the MSAgent for context
     [echo]                         [REQUIRED]       sessid=[SESSION_ID] - Numeric ID for the ABL Session to be stopped
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish+stop
     [echo]
     [echo]  oemanager trimall    - Trim all available ABL Sessions (via the Agent Manager) for each MSAgent for an ABL App
     [echo]                         Note: For any busy sessions considered stuck use 'trimhttp' with a specific Session ID
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish/stop
     [echo]
     [echo]  oemanager trimidle   - Trim only the IDLE ABL Sessions (via the Agent Manager) for each MSAgent for an ABL App
     [echo]                         Allows for manually scaling down an MSAgent which may have many unused ABL Sessions
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish+stop
     [echo]
     [echo]  oemanager trimhttp   - Trim one or all Client HTTP Sessions (via the Session Manager) for an ABLApp + WebApp
     [echo]                         Terminating a client HTTP session will also terminate its associated ABL Session
     [echo]                         [REQUIRED]       webapp=[WEBAPP_NAME] - WebApp for Tomcat Manager to terminate active sessions
     [echo]                                           The given WebApp is expected to be associated with the provided ablapp name
     [echo]                         [OPTIONAL]       sessid=[SESSION_ID]  - Alphanumeric Client Session ID to be stopped
     [echo]                                           When no session ID provided, all available Client HTTP Sessions will be expired
     [echo]                         [OPTIONAL] terminateopt=0 - Termination Option: 0=graceful, 1=forced, 2=finish+stop
     [echo]
     [echo]
     [echo] Current parameter values, override via command line or 'oemanager.properties':
     [echo]     scheme=http
     [echo]       host=localhost
     [echo]       port=8813
     [echo]     userid=tomcat
     [echo]     passwd=tomcat
     [echo]     ablapp=mypasoe

Niektóre są oczywiste jak: start, stop, status itp. ale niektóre wyglądają bardzo ciekawie, np. opcja flush – pomaga znaleźć przyczynę powtarzających się awarii agenta, poprzez opóżnione zrzucenie informacji ze specjalnego bufora do pliku.
Na samym dole widać użytkownika i hasło do procesu Tomcata. Należy dla bezpieczeństwa wejść do pliku oemanager.properties, znajdującego się w podkatalogu bin dla instancji, odkomentować dwie linie i ustawić puste dane logowania, jak na poniższym obrazku.

OK, poniżej zamieściłem wynik działania komendy oemanager status.

 [PCTRun]  PASOE Instance: http://localhost:8813
   [PCTRun]
   [PCTRun] ABL Application Information [mypasoe - v12.8.0 ( 2023-12-21 )]
   [PCTRun]     WebApp: ROOT
   [PCTRun]       APSV: ENABLED
   [PCTRun]       SOAP: ENABLED
   [PCTRun]       REST: ENABLED
   [PCTRun]        WEB: ENABLED
   [PCTRun]
   [PCTRun] Manager Properties
   [PCTRun]             Maximum Agents:           2
   [PCTRun]             Minimum Agents:           1
   [PCTRun]             Initial Agents:           1
   [PCTRun]     Max. Connections/Agent:         200
   [PCTRun]     Max. ABLSessions/Agent:         200
   [PCTRun]         Idle Conn. Timeout:     300.000 ms (00:00:05:00.000)
   [PCTRun]       Idle Session Timeout:   1.800.000 ms (00:00:30:00.000)
   [PCTRun]         Idle Agent Timeout:   1.800.000 ms (00:00:30:00.000)
   [PCTRun]      Idle Resource Timeout:           0 ms (00:00:00:00.000)
   [PCTRun]         Conn. Wait Timeout:       3.000 ms (00:00:00:03.000)
   [PCTRun]       Request Wait Timeout:      15.000 ms (00:00:00:15.000)
   [PCTRun]     Initial Sessions/Agent:           2
   [PCTRun]     Min. Avail. Sess/Agent:           1
   [PCTRun]
   [PCTRun] > Agent PID 13208: AVAILABLE
   [PCTRun]     Est. Agent Lifetime: 00:00:00:00.000
   [PCTRun]     DynMax ABL Sessions:          200
   [PCTRun]      Total ABL Sessions:            2
   [PCTRun]      Avail ABL Sessions:            2
   [PCTRun]        Open Connections:            1
   [PCTRun]         Overhead Memory: 13.897 KB
   [PCTRun]
   [PCTRun]     SESSION ID      STATE           STARTED                         LIFETIME          SESS. MEMORY     ACTIVE MEM.   REQUESTS       BOUND/ACTIVE CLIENT SESSION
   [PCTRun]                4    IDLE            18/02/2024 15:18:40,229-01:00   00:00:01:28.600         742 KB         742 KB           0        -
   [PCTRun]                7    IDLE            18/02/2024 15:18:40,229-01:00   00:00:01:28.600         742 KB         742 KB           0        -
   [PCTRun]       Active Agent-Sessions: 0 of 2 (0% Busy)
   [PCTRun]     Utilized Agent-Sessions: 0 of 2 (>801 KB)
   [PCTRun]        Approx. Agent Memory: 15.380 KB
   [PCTRun]
   [PCTRun] Session Manager Metrics (Count-Based)
   [PCTRun]            # Requests to Session:           0
   [PCTRun]           # Agent Responses Read:           0 (0 Errors)
   [PCTRun]         # Agent Requests Written:           0 (0 Errors)
   [PCTRun]     Concurrent Connected Clients:           0 (Max: 0)
   [PCTRun]       # Reserve ABLSession Waits:           0
   [PCTRun]     # Reserve ABLSession Timeout:           0
   [PCTRun]
   [PCTRun] Client HTTP Sessions: 0

Administrator otrzymuje natychmiastową całościową informację o warstwach transportowych, właściwościach agentów itd.
Zachęcam do przetestowania i korzystania z opcji komendy OEMANAGER.

1 2 3 4