W linuksach, żeby otworzyć gniazdo nasłuchujące na porcie od 0 do 1024, trzeba mieć prawa roota. Teoretycznie można uruchomić serwer z prawami roota, ale czy to bezpiecznie? Są aż dwa sposoby rozwiązania tego problemu, umożliwiające nasłuchiwanie na porcie 80, bez praw roota i bez rekompilacji jądra.
Pierwszy sposób to ustawienie przekierowania pakietów. Można do tego użyć programu socat. Najpierw musimy ustawić w aplikacji nasłuchiwanie na jakimś z „wyższych” portów, a następnie uruchomić program socat na koncie roota:
socat TCP-LISTEN:80,fork,su=nobody,reuseaddr TCP:127.0.0.1:8080– przy takiej konfiguracji program socat przekieruje pakiety z portu 80, na port 8080.
Wadą sposobu z przekierowaniem jest to, że zużywamy w ten sposób więcej systemowych zasobów, a po drugie, nasza aplikacja nie może odczytać adresu ip osoby, która się połączyła z serwerem.
Lepszy sposób
O wiele lepszym sposobem byłoby uruchomienie aplikacji z prawami roota, wykonanie wszystkich akcji wymagających uprawnień (np. otwarcie gniazda serwera na porcie 80) i utracenie przywilejów – przejście do trybu zwykłego użytkownika. Tylko jak to zrobić w javie? Z pomocą przychodzi nam biblioteka Apache Commons Daemon. Uruchamia ona naszą aplikację jako systemowy Daemon. Z prawami roota wywołuje funkcję init() naszego programu, a następnie traci prawa roota i wywołuje funkcję start(). Można ją pobrać tu. Biblioteka składa się z dwóch częsci. Jedna – to bilbioteka napisana w javie – musimy ją dołączyć do aplikacji, a druga, to narzędzie jsvc, które możemy pobrać z powyższej strony, a w niektórych systemach zainstalować z repozytorium: apt-get install jsvc.
Na początek musimy zmodyfikować naszą aplikację. Tworzymy nową klasę, która będzie implementowała interfejs Daemon:
1 package myapp; 2 3 import org.apache.commons.daemon.Daemon; 4 import org.apache.commons.daemon.DaemonContext; 5 import org.apache.commons.daemon.DaemonInitException; 6 7 /** 8 * 9 * @author jblew 10 */ 11 public class Main implements Daemon { 12 13 public void init(DaemonContext dc) throws DaemonInitException, Exception { 14 /** 15 * Ta funkcja jest uruchamiana pierwsza, z prawami roota. Tutaj należy otworzyć sockety na portach mniejszych niż 1024, np. 80. 16 */ 17 } 18 19 public void start() throws Exception { 20 /** 21 * Ta metoda jest uruchamiana druga, po init(), ale już bez praw roota. Tutaj uruchamiamy aplikację. 22 */ 23 } 24 25 public void stop() throws Exception { 26 /** 27 * Tutaj zakańczamy aplikację. 28 */ 29 } 30 31 public void destroy() { 32 /** 33 * A tu powinniśmy zniszczyć obiekty utworzone przez funkcję init(). 34 */ 35 } 36 } 37 38
Ta klasa będzie odpowiedzialna za uruchamianie naszego programu. Teraz pozostała jeszcze tylko kwestia, jak to zrobić.
Do uruchamiania tak spreparowanej aplikacji służy polecenie jsvc. Postaram się to wytłumaczyć na przykładzie:
sudo jsvc -user [nazwa użytkownika] -debug -cp MyApp.jar myapp.Main– To polecenie uruchomi opisane wyżej funkcje klasy myapp.Main, z pliku MyApp.jar. Dzięki parametrowi-debugzobaczymy wyjście apikacji i wszystkie błędy, jakie wystąpią. Koniecznie musimy dołączyć parametr-user, bo inaczej cała aplikacja będzie działała z prawami roota, a po dołączeniu tego parametru, dalsza część aplikacji przejdzie na konto użytkownika, którego podamy.
I to w zasadzie wszystko. Chciałbym jeszcze tylko pokazać, jak można restartować i wyłączać daemona, bo nie da się tego zrobić w standardowy sposób. Najłatwiej to zrobić z wewnątrz naszego programu. Zauważ, że funkcja init(DaemonContext dc) przyjmue jako parametr obiekt DaemonContext, który umożliwia pobranie kontrolera daemona (metodagetController()). Właśnie Kontroler Daemona (DaemonController) umożliwia wyłączenie aplikacji metodą shutdown() i restart – metodą reload(). Wystarczy więc zachować kontroler po uruchomieniu funkcji init() i w odpowiednim czasie wykonać funkcje wyłączające.
| JBLew, 26 sierpnia 2011 | Skomentuj » |
