[PJATK][SKJ 2016] Serwer Proxy z ograniczaniem dostępu

Zadanie na 17.01.2016 z SKJ.

Niestety nie mam gotowego rozwiązania, ale mam serwer proxy z filtrem po IP (z maską sieci) i domenie, więc zostało dopisać protokół do ustawiania różnych ograniczeń zgodnie z treścią zadania.

Aby z niego skorzystać należy zainstalować 'Maven’ w Eclipse. W moim 'Eclipse for Java’ był już dołączony, a w necie jest dużo kursów jak to zrobić. Aby sprawdzić czy jest zainstalowany wejdź w 'Window -> Preferences’, po lewej stronie powinien być 'Maven’ na liście . Po instalacji Mavena trzeba załadować listę bibliotek:

Kliknij na górze Eclipse: 'Window -> Show View -> Other..’, a w otwartym oknie w 'Maven -> Maven Repositories’. Na dole Eclipse otworzy się nowe okno. Rozwinąć opcję 'Global’ i nacisnij prawym myszki na 'centel’. W menu kliknij 'Rebuild Index’. Może to zająć kilka-kilkanaście minut.

Kolejne kroki konfiguracji to dużo pisania. Mam nadzieję, że projekt Maven który eksportowałem jako archiwum z Eclipse wam zadziała:
http://skalski.at/files/files/PJWSTK/SKJ_proxy_filters/LittleProxyWithIpAndDomainFilters.zip

Program składa się z LittleProxy-1.1.0-beta1 (to coś powinien wam zainstalować Maven automatycznie jak wgracie mój projekt, bo jest w nim plik 'pom.xml’ w którym jest takie 'dependency’) oraz tylko 2 plików:

MyProxyApp.java (z mainem):

package prx2.prx2;

import org.littleshoot.proxy.HttpFiltersAdapter;
import org.littleshoot.proxy.HttpFiltersSourceAdapter;
import org.littleshoot.proxy.impl.DefaultHttpProxyServer;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpRequest;

public class MyProxyApp 
{
    public static void main( String[] args )
    {
        System.out.println( "Proxy started!" );
        
	    DefaultHttpProxyServer.bootstrap()
	        .withPort(8088)
	        .withFiltersSource(new HttpFiltersSourceAdapter()
    	        {
    	            public HttpFiltersAdapter filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx)
    	            {
    	               return new MyHttpFilter(originalRequest, ctx);
    	            }
    	        })
	        .start();
	    
        System.out.println( "Proxy is working!" );
        
        /*
         * TUTAJ KOD ODPOWIEDZIALNY ZA KOMUNIKACJE Z KLIENTEM KONTROLUJACYM LISTY DOSTEPU
         * postawienie jakiegos serwera, na jakims porcie, nasluchiwanie polaczen, obsluga ich w nowych watkach itd.
         */
    }
}

 

oraz pliku MyHttpFilter.java odpowiedzialnego za filtrowanie połączeń:

package prx2.prx2;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;

import org.littleshoot.proxy.HttpFiltersAdapter;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;

public class MyHttpFilter extends HttpFiltersAdapter
{
	ChannelHandlerContext ctx;

	public MyHttpFilter(HttpRequest originalRequest, ChannelHandlerContext ctx)
	{
		super(originalRequest, ctx);
		this.ctx = ctx;
	}

	public HttpResponse clientToProxyRequest(HttpObject httpObject)
	{

		if(httpObject instanceof HttpRequest)
		{
			HttpRequest httpRequest = (HttpRequest) httpObject;

			try
			{
				// cos jak: http://www.onet.pl/podstrona/index.html
				// LUB w przypadku HTTPS: www.onet.pl:443
				String uri = httpRequest.getUri();
	
				// nalezy z 'uri' wyciagnac 'www.onet.pl'
				String[] domainData = uri.split("/", 4);
				String domain = null;
				if(domainData.length == 1)
				{
					domain = domainData[0].split(":")[0];
				}
				else if(domainData.length >= 3)
				{
					domain = domainData[2].split(":")[0];
				}
				else
				{
					throw new Exception("INVALID HOST FORMAT");
				}
				
	
				// IP otwierajacego strone www w formacie: 127.0.0.1
				String clientIP = ctx.channel().remoteAddress().toString().split(":")[0].split("/")[1];
			
				// IP w formacie ulatwiajacym porownywanie przy uzyciu maski
				int clientIPint = MyHttpFilter.ipStringToInt(clientIP);

				boolean blockWebsite = false;

				
				// KOD REGULUJACY DOSTEP DO DANEJ STRONY

	
				// PRZYKLADOWE BLOKADY
				if(domain.contains("google")) // dostepne: contains, equals [nie porownywac uzywajac '==', to nie C++!], startsWith, endsWith
					blockWebsite = true;
				
				if(domain.equals("www.wp.pl") || domain.equals("wp.pl")) // mozna zamiast tego 'endsWith'
					blockWebsite = true;

				if(isIpInNetwork(clientIPint, MyHttpFilter.ipStringToInt("127.0.2.2"), 24)) // czy klient w sieci: 127.0.2.2/24 (znaczy 127.0.2.*)
					blockWebsite = true;
	
				
				// JESLI ZABLOKOWANY, TO NIE WYSYLAJ ZAPYTANIA DO WSKAZANEGO SERWERA, TYLKO OD RAZU ZWROC KLIENTOWI KOD 403
				if(blockWebsite)
				{
					System.out.println("ACCESS DENIED  FOR CLIENT IP: " + clientIP + " (" + clientIPint  + "), DOMAIN: " + domain);
					
					return new DefaultFullHttpResponse(httpRequest.getProtocolVersion(), HttpResponseStatus.FORBIDDEN, Unpooled.copiedBuffer("403 PROXY: ACCESS DENIED".getBytes()));
				}
				
				System.out.println("ACCESS GRANTED FOR CLIENT IP: " + clientIP + " (" + clientIPint  + "), DOMAIN: " + domain);
			}
			catch (Exception e)
			{
				// jesli wystapil blad przy zamianie IP (stringa np. "127.0.0.0") na ciag bajtow, to zwroc blad 500
				return new DefaultFullHttpResponse(httpRequest.getProtocolVersion(), HttpResponseStatus.INTERNAL_SERVER_ERROR, Unpooled.copiedBuffer(("500 PROXY: " + e.getMessage()).getBytes()));
			}
		}

		return null;
	}

    public static int ipStringToInt(String ipString) throws Exception
    {
    	Inet4Address ipv4;
		try
		{
			ipv4 = (Inet4Address) InetAddress.getByName(ipString);
		}
		catch (UnknownHostException e)
		{
			throw new Exception("CANNOT RESOLVE ADDRESS " + ipString);
		}
    	byte[]       ipAsByteArray = ipv4.getAddress();
    	int          ipAsInt = ((ipAsByteArray[0] & 0xFF) << 24) |
    	                 ((ipAsByteArray[1] & 0xFF) << 16) |
    	                 ((ipAsByteArray[2] & 0xFF) << 8)  |
    	                 ((ipAsByteArray[3] & 0xFF) << 0);
    	return ipAsInt;
    }

    public static boolean isIpInNetwork(int testIp, int networkIp, int networkMask) throws Exception
    {
    	// magia na bitach (operacja AND na bitach IP i bitach maski)
    	int maskInt = ~((1 << (32 - networkMask)) - 1);
    	return ((testIp & maskInt) == (networkIp & maskInt));
    }
}

Jako przykład dodałem do filtra 3 reguły:

// dostepne: contains, equals [nie porownywac uzywajac '==', to nie C++!], startsWith, endsWith
if(domain.contains("google"))
	blockWebsite = true;

// mozna zamiast tego 'endsWith'
if(domain.equals("www.wp.pl") || domain.equals("wp.pl"))
	blockWebsite = true;

// czy klient w sieci: 127.0.2.2/24 (znaczy 127.0.2.*)
if(isIpInNetwork(clientIPint, MyHttpFilter.ipStringToInt("127.0.2.2"), 24))
	blockWebsite = true;

Wasze zadanie to zrobić protokół komunikacji na jakimś innym porcie, przez który użytkownik będzie mógł dodawać listy IP i listy domen, a potem wiązać je dodając zasadę 'allow’ czy 'deny’.

Następnie trzeba tylko dopisać, żeby te listy były uwzględniane przez proxy i opisać wszystko dokładnie w dokumentacji.

Zgodnie z poleceniem, serwer proxy można ściągnąć z sieci i nie trzeba wiedzieć jak on działa.

Ten wpis został opublikowany w kategorii PJWSTK - SKJ. Dodaj zakładkę do bezpośredniego odnośnika.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *