Mamy pewne aplikacje, które czasami popadają w zły stan, ale tylko w produkcji (oczywiście!). Podczas wykonywania zrzutu sterty może pomóc w zbieraniu informacji o stanie, często łatwiej jest użyć zdalnego debuggera. Ustawienie to jest prosta - Wystarczy tylko dodać to do jego linii poleceń:Bezpieczne debugowanie dla maszyn produkcyjnych JVM
-Xdebug -Xrunjdwp: transport = dt_socket, server = y, zawiesić = n, address = PORT
Wydaje się, że nie dostępny mechanizm bezpieczeństwa, więc włączenie debugowania w produkcji skutecznie umożliwiłoby wykonanie dowolnego kodu (poprzez hotswap).
Mamy zestaw JVM 1.4.2 i 1.5 Sun działających na Solaris 9 i Linux (Redhat Enterprise 4). Jak możemy włączyć bezpieczne debugowanie? Jakieś inne sposoby osiągnięcia naszego celu związanego z inspekcją serwera produkcyjnego?
Aktualizacja: Dla JVD JDK 1.5+ można określić interfejs i port, z którym debugger powinien się połączyć. Tak więc propozycja KarlP wiązania do pętli zwrotnej i po prostu użycie tunelu SSH do lokalnego pudełka deweloperskiego powinna działać pod warunkiem, że SSH jest poprawnie skonfigurowany na serwerach.
Jednak wydaje się, że JDK1.4x nie zezwala na podanie interfejsu dla portu debugowania. Możemy zablokować dostęp do portu debugowania w sieci lub wykonać blokowanie specyficzne dla systemu w samym systemie operacyjnym (IPChains, jak zasugerował Jared itp.)?
Aktualizacja # 2: To jest hack, który pozwoli nam ograniczyć nasze ryzyko, nawet na 1.4.2 JVMs:
poleceń params linia:
-Xdebug
-Xrunjdwp:
transport=dt_socket,
server=y,
suspend=n,
address=9001,
onthrow=com.whatever.TurnOnDebuggerException,
launch=nothing
kodu Java, aby włączyć debuggera:
try {
throw new TurnOnDebuggerException();
} catch (TurnOnDebugger td) {
//Nothing
}
Wyjątkiem TurnOnDebuggerException może być każdy wyjątek gwarantowany, którego nie można nigdzie rzucać.
Przetestowałem to na skrzynce systemu Windows, aby udowodnić, że (1) port debuggera nie odbiera początkowo połączeń, oraz (2) wyrzucenie wyjątku TurnOnDebugger, jak pokazano powyżej, powoduje, że debugger ożyje. Parametr uruchamiania był wymagany (przynajmniej w JDK1.4.2), ale wartość Javy była obsługiwana z wdzięcznością przez maszynę JVM.
Planujemy zrobić mały aplet, który za odpowiednimi zabezpieczeniami pozwoli nam włączyć debugger. Oczywiście później nie można go wyłączyć, a debugger nadal słucha rozwiązłowo po jego uruchomieniu. Ale są to ograniczenia, które możemy zaakceptować, ponieważ debugowanie systemu produkcyjnego zawsze spowoduje ponowne uruchomienie.
Aktualizacja # 3: skończyło się na piśmie trzy klasy: (1) TurnOnDebuggerException, zwykły „ol wyjątek Java (2) DebuggerPoller, tło przewlec sprawdza istnienie określonego pliku w systemie plików, oraz (3) DebuggerMainWrapper, klasa, która uruchamia wątek odpytywania, a następnie odbija refleksyjnie główną metodę innej określonej klasy.
ten sposób jego zastosowanie:
- Wymień „main” klasę z DebuggerMainWrapper w skryptach startowych
- dodać dwa systemu (-D) params, jednego określenia prawdziwego główną klasę i drugi określa plik w systemie plików.
- Skonfiguruj debugger w wierszu poleceń z dodaną częścią onthrow = com.whatever.TurnOnDebuggerException
- Dodaj słoik z wymienionymi powyżej trzema klasami do ścieżki klasy.
Teraz, po uruchomieniu JVM wszystko jest takie samo, z wyjątkiem uruchomienia wątku polleru tła. Zakładając, że plik (nasz nazywa się TurnOnDebugger) początkowo nie istnieje, poller sprawdza to co N sekund. Kiedy poller najpierw zauważy to, rzuca i natychmiast łapie wyjątek TurnOnDebuggerException. Następnie agent zostaje wyrzucony.
Nie można wyłączyć urządzenia, a urządzenie nie jest bardzo bezpieczne, gdy jest włączone. Z drugiej strony nie sądzę, że debugger pozwala na wiele jednoczesnych połączeń, więc utrzymanie połączenia debugowania jest najlepszą obroną. Wybraliśmy metodę powiadamiania o plikach, ponieważ pozwalała nam na podciąganie się z istniejącego authenta/autora Uniksa, określając plik wyzwalacza w katalogu, w którym tylko właściwe zastosowania mają prawa. Możesz łatwo zbudować mały plik wojenny, który osiągnął ten sam cel poprzez połączenie z gniazdem. Oczywiście, ponieważ nie możemy wyłączyć debuggera, użyjemy go tylko do zebrania danych przed zabiciem chorego wniosku. Jeśli ktoś chce tego kodu, proszę dać mi znać. Jednak samo samodzielne wrzucenie go zajmie Ci tylko kilka minut.
Jeśli to działa (testowanie teraz), wydaje się najlepszą opcją dla nas. To nie jest tak, że regularnie debugujemy, ale chcemy, aby złapać JVM w niewłaściwym stanie. – ShabbyDoo
Myślę, że to zadziała tylko na JDK 1.5+: http://java.sun.com/j2se/1.5.0/docs/guide/jpda/enhancements.html Zobacz "transport dt_socket został zmieniony na wziąć lokalny adres, gdy działasz w trybie serwera "w powyższym linku. – ShabbyDoo
@Shabby - yep - wygląda na to, że działa 1.5+ i jest świetnym rozwiązaniem. Alternatywą jest zablokowanie portów debugowania za pomocą zapory ogniowej (oprogramowania lub sprzętu). Może wypytać ipchains dla hostów Linuxa? (http://tldp.org/HOWTO/IPCHAINS-HOWTO.html) – Jared