jueves, 12 de noviembre de 2015

Memorias de un administrador. El acceso a Internet.


Recomiendo leer un post anterior en el que describo como está estructurada la red en el Centro.





El Profesor:



El profesor se conecta vía web a una intranet alojada en el equipo virtualizado VWEBINTERNA en cual tiene Internet Information Server como servidor web y ASP.NET habilitado (versión 4.0).

Me gusta utilizar el IIS ya que permite la seguridad integrada de NT, de tal forma que puedo indicar en el IIS no sólo que se pueda conectar desde una IP determinada, sino que puedo cambiar los permisos de las carpetas donde se encuentra las páginas, para que sólo tengan acceso los usuarios de NT que pertenezcan al grupo de profesores del centro. De esta forma el profesor con su usuario de entrada NT ya está validado y no necesitamos crear protocolos de acceso Login-Password.


Cuando un profesor inicia sesión cargará la siguiente página ASP.NET:


En ella el profesor puede escoger:
  • Dejar pasar a todos los equipos o solo a algunos (aparece una lista en la que el profesor añade a los equipos).
  • Dejar navegar (puerto 80) junto acceso a google; Dejar navegación segura (puertos 80 y 443); Acceso a puertos específicos (aparece una lista en la que el profesor añade los puertos); Dejar pasar todo el tráfico.
  • Indicar hasta que hora se deja Internet abierto. Al llegar a esa hora el tráfico se cierra automaticamente sin que el profesor tenga que  hacer nada.



En la página web, las horas que se cargan en el combo ATA (hasta en gallego) vienen de una base de datos SQL Server.

Esta entrada no pretende ser un manual de IP Tables, para eso tenéis múltiples entradas en Internet. Simplemente comentar que todas las opciones que aparecen en esta página van a tener una traducción en una entrada IPtables del ordenador VProxy.

Cuando el Profesor determina a qué equipos y puertos va a permitir acceder y hasta que hora estará habilitado Internet para esa aula, pulsará el botón 'Aplicar Reglas'.

Al hacerlo realizamos dos procesos:

  • Creamos un archivo en el Servidor NT(VWebInterna) donde está alojado la página ASP.NET con las órdenes IPTables necesarias para dar acceso a Internet.
  • Transferimos dicho archivo por SSH al equipo VProxy y procedemos a su ejecución. Entre las órdenes de dicho archivo se encuentran unas que crean un fichero en /etc/cron.d/AULAXX con lo necesario para eliminar las reglas que dan acceso a la hora indicada por el profesor.


Servidor Virtualizado: VPROXY

 


Recomiendo leer el post anterior en el que describo el equipo VProxy que es el que va a permitir salir el tráfico hacia Internet.

Enruta los paquetes desde la red interna (192.168.0.0/255.255.0.0 y 172.30.0.0/255.255.0.0) a la red conectada a Internet. Partimos de la base de que ya está configurado para enrutar el tráfico.

Dispone de dos tarjetas de red con las siguientes IP´s:
  • eth1: 69.57.157.200 (red externa)
  • eht0: 172.30.128.1  (red interna)
VProxy tiene instalado el servidor SQUID como proxy-caché. Por lo tanto las peticiones que le lleguen al puerto 80 serán redirigidas al puerto 3128 (puerto del SQUID).

El resto de direcciones harán FORWARD.


Por defecto, se permite pasar todo el tráfico menos el que provenga de las aulas y no sea un profesor (dirección 192.168.aula.100).

Se producirá un enmascaramiento de la dirección para que las peticiones salgan con la IP externa de VProxy


El script que ejecuta el equipo es el siguiente:

IFACEIN=eth0
IFACEOUT=eth1

modprobe ip_nat_ftp

#// Activar el enrutamiento en un sistema Linux
echo "1" > /proc/sys/net/ipv4/ip_forward


# Limpiar reglas

iptables -F INPUT
iptables -F FORWARD
iptables -F OUTPUT
iptables -F -t nat
iptables -F -t mangle

iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT




iptables -A FORWARD -i ${IFACEIN} -o ${IFACEOUT} -j ACCEPT
iptables -t nat -A PREROUTING -i ${IFACEIN} -p tcp --dport 80 -j REDIRECT --to-ports 3128
iptables -t nat -A POSTROUTING -o ${IFACEOUT} -j MASQUERADE
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i ${IFACEIN} -j ACCEPT
iptables -A INPUT -i ${IFACEOUT} -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -j DROP


#Dejamos pasar los servidores
iptables -I INPUT 1 -s 192.168.130.0/24 -m comment --comment "SERVIDORES" -j ACCEPT
 

# AULA E PROFESORES
iptables -I INPUT 1 -s 192.168.0.100/255.255.0.255 -m comment --comment "REGRAFIXA_PROFESORES" -j ACCEPT
iptables -I INPUT 3 -s 192.168.0.0/16 -m comment --comment "REGRAFIJA_ALUMNOS" -j DROP




iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -o ${IFACEIN} -j ACCEPT
iptables -A OUTPUT -o ${IFACEOUT} -j ACCEPT
iptables -A OUTPUT -j DROP


# SERVIDORES

iptables -I FORWARD 1 -s 192.168.130.0/24 -m comment --comment "SERVIDORES" -j ACCEPT

# PROFESORES
iptables -I FORWARD 2 -s 192.168.0.100/255.255.0.255 -m comment --comment "REGRAFIJA_PROFESORES" -j ACCEPT
# ALUMNOS

iptables -I FORWARD 3 -s 192.168.0.0/16 -m comment --comment "REGRA_FIXA" -j DROP

En negrilla están las reglas que impiden que las aulas puedan tener acceso a Internet. Fijarse que se
INSERTAN en las primeras posiciones por lo que van primero que las que creé inicialmente que dejaban pasar todo el tráfico....

 

MODIFICANDO IPTABLES: ACCESO A INTERNET


Ahora llega el momento de enseñar como hago para que desde una página web desarrollada en un servidor Windows con ASP.Net le lleguen las órdenes al Proxy Linux.

Objetivo:

La idea es crear un fichero con las órdenes para dejar pasar el tráfico a un aula determinada.
Esas órdenes se aplicarán sobre el iptables. Al mismo tiempo se creará un archivo en /etc/cron.d/ donde se indicará la hora a la que el acceso a Internet debe desactivarse (es decir, borrar la entrada del iptables que afecta a esa aula).

El archivo con las órdenes se creará en el servidor windows VWebInterna y se pasará por ssh al equipo Linux VProxy.

La idea inicial era utilizar la herramienta plink . Para quien no lo sepa es un comando que permite iniciar una sesión ssh y ejecutar un archivo o comandos.
Pero resulta que plink, cuando se conecta por primera vez a un servidor, espera recibir un texto (la tecla y de yes) para guardar en caché el host-key del servidor al que se conecta.

Resulta que en versiones anteriores de plink se podía indicar que se guardara sin pedir confirmación, pero ahora ya no.

Buscando por internet encuentras esta solución:
echo y | plink -ssh root@REMOTE_IP_HERE "exit"
 
Pero desde ASP.Net ejecutar este comando no está permitido, ya que la cuenta desde la que se ejecuta (servicio de red) no tiene permiso para ejecutar una orden consola.

También intenté por medio de un Process enviar al flujo de entrada el caracter 'y' pero no funcionó, o más bien, algunas veces parece que funcionaba, pero al cabo del tiempo, y de forma aleatoria, en el servidor quedaban ejecutándose procesos plink, supongo que esperando pulsar la tecla 'y'.

Al final buscando por Internet encontré unas librerías que permiten abrir sesiones ssh en ASP.NET: https://sshnet.codeplex.com/

Una vez incorporada a nuestro proyecto ASP.NET ya tenemos las clases necesarias para abrir una consola ssh y mandar ejecutar los comandos en el equipo con el Squid instalado (VProxy).

En este apartado tenemos que diferenciar la parte en que la página web ASP.NET va a generar los comandos y la parte de enviar dichos comandos al servidor por SSH.


NOTA: Vuelvo a comentar que mi sistema es un poco 'complicado' ya que se podría desarrollar una página web php en el propio servidor VProxy y ejecutar las órdenes directamente sin necesidad de utilizar ssh, pero de esta forma me parece más seguro y aprovechando la seguridad integrada de NT en el IIS como mencioné anteriormente....

GENERANDO LOS COMANDOS:

La idea es muy simple: Debemos de añadir una orden iptables en la que dejemos pasar el tráfico de la red del aula en que nos encontremos.

Primero: Identificamos el aula (en base a la IP del equipo del profesor). Así si el profesor tiene la ip 192.168.1.100 sabemos que el aula es la Aula 1 (red 192.168.1.X)


Segundo: Guardamos en un archivo de nombre firewall_aula (en el ejemplo sería firewall_1) las órdenes de iptables para habilitar o quitar el acceso a internet.

Así, cuando añadimos una regla de dejar paso hacia a Internet a el aula, añadimos a esa regla una etiqueta con el valor _AULAXX_

Cuando queremos quitar el acceso al aula, eliminamos del iptables todas las líneas con dicha etiqueta.


Operación CORTAR EL TRAFICO (sería pulsar el botón 'Pechar Internet' de la página ASP.NET'):

        cadea = String.Format("iptables -S | sed '/_AULA{0}_/s/-A/iptables -D/e'", obterAula())
        operacionArquivo(obterNomeArquivo(), cadea, True)

        ' Borramos o arquivo de crontab
        cadea = String.Format("rm /etc/cron.d/AULA{0}", obterAula())
        operacionArquivo(obterNomeArquivo(), cadea, False)

Estas dos órdenes son las que se guardarían en el archivo. La segunda elimina el fichero a ejecutar por el CRON para cortar el acceso a Internet a una hora determinada.
  • obterAula() es una función que en base a la ip obtiene el número de aula (en el ejemplo sería 1)
  • obterNomeArquivo() es una función que devuelve el nombre del archivo donde se guardan estas sentencias (en el ejemplo sería firewall_1)


Operación HABILITAR EL TRAFICO (sería pulsar el botón 'Aplicar Reglas' de la página ASP.NET'):

Dependiendo de las opciones escogidas gráficamente, añadiremos más o menos reglas. Recordar que todo lo que vaya al puerto 80 es capturado por el SQUID y el resto pasa (FORWARD) a través del equipo VPROXY.

Por ejemplo, si queremos dar acceso a Internet a todos los equipos de un aula, escribiremos en el archivo:
    regras(1) = String.Format("iptables -I INPUT 1 -s 192.168.{0}.0/24 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
    operacionArquivo(obterNomeArquivo(), regras(1), False)
    regras(2) = String.Format("iptables -I FORWARD 1 -d www.google.es -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
    
    operacionArquivo(obterNomeArquivo(), regras(2), False)

Como vemos, en el caso de acceso a la web la regla es de tipo INPUT y a mayores, como queremos que puedan ir a google (ahora usa conexiones https) dejamos pasar el tráfico FORWARD a la dirección de www.google.es.
Fijarse como en el comentario pongo la etiqueta _AULA1_ que será utilizada después para eliminar todas las reglas que permiten el acceso al AULA1.

Ahora dependiendo de las opciones que marque el profesor en la página se van escribiendo en el archivo las órdenes IPTABLES.

Un ejemplo del archivo creado con la opción de abrir Internet con conexiones seguras sería:

iptables -S | sed '/_AULA4_/s/-A/iptables -D/e'
iptables -I INPUT 1 -s 192.168.4.0/24 -p tcp -m comment --comment '_AULA4_' -j ACCEPT
iptables -I FORWARD 1 -p tcp --dport 443 -s 192.168.4.0/24 -m comment --comment '_AULA4_' -j ACCEPT

Después añadimos al archivo las órdenes necesarias para crear en /etc/cron.d/ un archivo de cron con la hora a la que debe eliminar las reglas (de esta forma se cierra Internet automáticamente sin que el profesor haga nada).

La combo de la página ASP.NET donde está la hora que cierra Internet guarda los valores de la forma: hh:mm

        ' Borramos o arquivo de crontab
        cadea = String.Format("rm /etc/cron.d/AULA{0}", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("touch /etc/cron.d/AULA{0}", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("echo ""SHELL=/bin/sh"" | sudo tee -a /etc/cron.d/AULA{0} ", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("echo ""PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"" | sudo tee -a /etc/cron.d/AULA{0} ", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        ' HORA PARA PECHAR INTERNET
        Dim hora As String = ddlHorario.SelectedValue.Substring(0, ddlHorario.SelectedValue.IndexOf(":"))
        Dim min As String = ddlHorario.SelectedValue.Substring(ddlHorario.SelectedValue.IndexOf(":") + 1)
        Dim dia As String = Date.Now.Day.ToString
        Dim mes As String = Date.Now.Month.ToString

        cadea = String.Format("echo ""{1} {2} {3} {4} *   root  iptables -S | sed '/_AULA{0}_/s/-A/iptables -D/e'"" | sudo tee -a /etc/cron.d/AULA{0} ", obterAula, min.Trim, hora.Trim, dia.Trim, mes.Trim)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("chmod 744 /etc/cron.d/AULA{0}", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        'Recargamos o cron
        cadea = String.Format("service cron reload")
        operacionArquivo(obterNomeArquivo(), cadea, False)


La orden de cron sería:
String.Format("echo ""{1} {2} {3} {4} *   root  iptables -S | sed '/_AULA{0}_/s/-A/iptables -D/e'"" | sudo tee -a /etc/cron.d/AULA{0} ", obterAula, min.Trim, hora.Trim, dia.Trim, mes.Trim)

Esta orden borrará a la hora indicada las entradas de IPTABLES que tengan como etiqueta _AULAX_
 
Recordar que obterAula devuelve el número de aula y el método operacionArquivo añade al archivo la orden que le indiquemos.

Un ejemplo del archivo creado para el AULA4:

rm /etc/cron.d/AULA4
touch /etc/cron.d/AULA4
echo "SHELL=/bin/sh" | sudo tee -a /etc/cron.d/AULA4
echo "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin" | sudo tee -a /etc/cron.d/AULA4
echo "00 15 14 10 *   root  iptables -S | sed '/_AULA4_/s/-A/iptables -D/e'" | sudo tee -a /etc/cron.d/AULA4
chmod 744 /etc/cron.d/AULA4
service cron reload


PASANDO LOS COMANDOS POR SFTP A VPROXY:

Partimos de la base que el servidor VPROXY tiene instalado y configurado el servicio SSH.

Una vez tenemos el archivo creado en el Servidor VWEBINTERNA, debemos de pasar dicho archivo por SSH al servidor Linux VPROXY y ejecutarlo.

Para ello hacemos uso de las librerías comentadas anteriormente (https://sshnet.codeplex.com/).

Primero pasamos el archivo al servidor. El codigo es el siguinte:

    Private ReadOnly RUTA_SERVIDOR_WINDOWS As String = "C:\firewall\"
    Private ReadOnly RUTA_SERVIDOR_LINUX As String = "/firewall/"
 

    Sub chamarProcedemento(ByVal nomearquivo As String)
        Dim SERVIDOR_IP As String = "AA.BB.CC.DD"
        Dim PUERTO_SSH As Int16 = XXXX



        '//////////////////////////////////PASAMOS O ARQUIVO AO SERVIDOR////////////////////////////////////
        Dim cliente As SftpClient

        Try
            cliente = New SftpClient(SERVIDOR_IP, PUERTO_SSH,"USUARIO_SSH", "PASSWORD_SSH")
            cliente.Connect()

        Catch ex As Exception
            MsgBox(ex.Message)
            Return
        End Try

        cliente.ChangeDirectory(
RUTA_SERVIDOR_LINUX)
        Dim filestream As New FileStream(
RUTA_SERVIDOR_WINDOWS + nomearquivo, FileMode.Open, FileAccess.Read)
        cliente.BufferSize = 1024
        cliente.UploadFile(filestream, nomearquivo)
        filestream.Close()
        filestream.Dispose()

        cliente.Disconnect()
        cliente.Dispose()

Como vemos, el código lo tengo dentro de un procedimiento al que le paso el nombre del archivo (recordemos que el nombre es 'firewall_NUMAULA'). Lo que hago es pasar el archivo desde C:\FIREWALL de la máquina WINDOWS al directorio /firewall/ de la máquina LINUX.

Una vez pasado, el segundo paso es ejecutarlo con una consola SSH:

        '//////////////////////////////////EXECUTAMOS O SCRIPT ////////////////////////////////////


        Dim ssh = New SshClient(SERVIDOR_IP, PUERTO_SSH, "USER_SSH", "PASSWORD_SSH")
        ssh.Connect()

        Dim answer As StringBuilder
        Dim ss As ShellStream = ssh.CreateShellStream("COMANDO", 80, 24, 800, 600, 1024)
        Dim reader = New StreamReader(ss)
        Dim writer = New StreamWriter(ss)
        writer.AutoFlush = True

        While (ss.Length = 0)
            Thread.Sleep(500)
        End While
        reader.ReadToEnd()

        WriteStream("sudo su", writer, ss)
        reader.ReadToEnd()

        WriteStream("cd " + RUTA_SERVIDOR_LINUX, writer, ss)
        reader.ReadToEnd()

        WriteStream("dos2unix " + RUTA_SERVIDOR_LINUX + obterNomeArquivo(), writer, ss)      ' CONVERTIMOS A FORMAT UNIX. INSTALADO PREVIAMENTE NO SERVIDOR O executable dos2unix
        reader.ReadToEnd()

        WriteStream("chmod 755 " + RUTA_SERVIDOR_LINUX + obterNomeArquivo(), writer, ss)
        reader.ReadToEnd()

        WriteStream(RUTA_SERVIDOR_LINUX + obterNomeArquivo(), writer, ss)        answer = ReadStream(reader)

        ss.Dispose()
        ssh.Disconnect()
        ssh.Dispose()


La orden que ejecuta el archivo está marcada en negrilla.
Comentar que debido al formato de los archivos y a pesar que el archivo se crea como UTF-8, daba problemas y tuve que instalar en el equipo LINUX la utilidad dos2unix para pasar el formato del archivo a LINUX.


Os dejo a continuación el código completo de la página ASP.NET, pero recordando que está hecha para funcionar con mi BD en SQLServer, por lo que cada uno tendría que adaptarlo a su caso:


PAGINA ASP.NET

Comentar que guardo registro de las operaciones que realiza el profesor, por lo que en la parte inferior de la página aparece la última operación realizada y quien la hizo. 

firewall.aspx:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="firewall.aspx.vb" Inherits="firewall" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Internet nas AULAS</title>
    <link href="App_Themes/StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div id='superior'>
        <asp:Label ID="lblAula" runat="server" Style="text-align:center; margin:20px auto; display:block;width:60%; border-bottom:solid 1px Maroon; border-top:solid 1px Maroon;" Text="Label" Font-Bold="True" Font-Size="X-Large" ForeColor="Maroon"></asp:Label>
        <asp:Label ID="lblNome" runat="server"
            Style="text-align:center; margin:20px auto; display:block;width:60%;"
            Text="Label" Font-Bold="True" Font-Size="Large" ForeColor="#FF6600"></asp:Label>
        <br />
        <asp:SqlDataSource ID="SqlDataRexistroError" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
            InsertCommand="GLOBAL_RexistroError" InsertCommandType="StoredProcedure" SelectCommand="GLOBAL_RexistroError"
            SelectCommandType="StoredProcedure"
            ProviderName="<%$ ConnectionStrings:ConnectionString.ProviderName %>">
            <SelectParameters>
                <asp:Parameter Name="aplicacion" Type="String" />
                <asp:Parameter Name="evento" Type="String" />
                <asp:Parameter Name="mensaxe" Type="String" />
            </SelectParameters>
            <InsertParameters>
                <asp:Parameter Name="aplicacion" Type="String" />
                <asp:Parameter Name="evento" Type="String" />
                <asp:Parameter Name="mensaxe" Type="String" />
            </InsertParameters>
        </asp:SqlDataSource>
        <asp:Panel ID="pnlIEquiposOpcions" runat="server" GroupingText="Lista de equipos" style="margin:0px auto"
            Width="70%">
            <asp:RadioButtonList ID="rblEquipos" runat="server" AutoPostBack="True"
                RepeatDirection="Horizontal" CellSpacing="10" ForeColor="#336699">
                <asp:ListItem Selected="True" Value="0">Todos os equipos</asp:ListItem>
                <asp:ListItem Value="1">Algúns equipos</asp:ListItem>
            </asp:RadioButtonList>
            <br />
            <asp:Panel ID="pnlEquiposLista" runat="server" ForeColor="#336699" style="margin-left:10px"
                HorizontalAlign="Left" Visible="False" Height="226px">
                <asp:Label ID="Label1" runat="server" ForeColor="#006699"
                    Text="Número de equipo:"></asp:Label>
                <asp:TextBox ID="txtNumEquipo" runat="server" MaxLength="3" Width="43px"></asp:TextBox>
                <asp:Button ID="btnEngadirEquipo" runat="server" Text="Engadir Equipo" />
                <asp:RangeValidator ID="RangeValidator1" runat="server"
                    ControlToValidate="txtNumEquipo"
                    ErrorMessage="Só son válidos números dende 1 a 199" MaximumValue="199"
                    MinimumValue="1" Type="Integer"></asp:RangeValidator>
                <br />
                <br />
                <asp:ListBox ID="lstEquipos" runat="server" Height="170px" Width="178px"
                    ToolTip="Nota: Premer dúas veces para eliminar o equipo da lista">
                </asp:ListBox>
                <p /><br />
            </asp:Panel>
        </asp:Panel>
        <asp:Panel ID="pnlProtocolos" runat="server"
            GroupingText="Lista de portos e tipo de tráfico" style="margin:0px auto"
            Width="70%">
            <asp:CheckBox ID="chkICMP" runat="server" Text="Permitir tráfico ICMP" style="float:left;margin:5px 0px 0px 5px"
                TextAlign="Left" ForeColor="#336699"/>
            <p /><br />
            <asp:RadioButtonList ID="rblProtocolosOpcions" runat="server" AutoPostBack="True"
                RepeatDirection="Horizontal" CellSpacing="10" ForeColor="#336699">
                <asp:ListItem Selected="True" Value="0">Acceso Internet (incluido google)</asp:ListItem>
                <asp:ListItem Value="1">Acceso Internet con conexións seguras (Dropbox,correo,...)</asp:ListItem>
                <asp:ListItem Value="2">Acceso Internet a portos específicos</asp:ListItem>
                <asp:ListItem Value="3">Acceso Internet todos os portos</asp:ListItem>
            </asp:RadioButtonList>
            <br />
            <asp:Panel ID="pnlProtocolosLista" runat="server" ForeColor="#336699" style="margin-left:10px"
                HorizontalAlign="Left" Visible="False">
                <asp:Label ID="Label2" runat="server" ForeColor="#006699"
                    Text="Número de porto:"></asp:Label>
                <asp:TextBox ID="txtNumPorto" runat="server" MaxLength="5" Width="43px"></asp:TextBox>
                <asp:Button ID="btnEngadirPorto" runat="server" Text="Engadir Porto" />
                <asp:RangeValidator ID="RangeValidator2" runat="server"
                    ControlToValidate="txtNumPorto"
                    ErrorMessage="Só son válidos números dende 1 a 65535" MaximumValue="65535"
                    MinimumValue="1" Type="Integer"></asp:RangeValidator>
                <br />
                <br />
                <asp:RadioButtonList ID="rbtlTipoProtocolo" runat="server"
                    RepeatDirection="Horizontal">
                    <asp:ListItem Selected="True" Value="tcp">TCP</asp:ListItem>
                    <asp:ListItem Value="udp">UDP</asp:ListItem>
                </asp:RadioButtonList>
                <br />
                <asp:ListBox ID="lstPortos" runat="server" Height="170px"
                    ToolTip="Nota: Premer dúas veces para eliminar o porto da lista" Width="168px">
                </asp:ListBox>
                <p /><br />
            </asp:Panel>
        </asp:Panel>
        <br />
        <asp:Panel ID="pnlHabilitar" runat="server" GroupingText="Habilitar Internet" style="margin:0px auto"
            Width="70%" Height="133px">
            <br />
            <asp:Label ID="Label3" runat="server" Text="ATA: " Font-Bold="True"
                Font-Size="Large"></asp:Label>
            <asp:DropDownList ID="ddlHorario" runat="server" DataSourceID="SqlDataHoras"
                DataTextField="desc_horas" DataValueField="horas">
            </asp:DropDownList>
            <br />
            <br />
            <u><b>Nota:</b></u> Internet pecharase automaticamente cando se chegue á hora seleccionada<br />
            <br />
            <asp:SqlDataSource ID="SqlDataHoras" runat="server"
                ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
                SelectCommand="DevolucionHoras" SelectCommandType="StoredProcedure">
                <SelectParameters>
                    <asp:Parameter Name="tarde" Type="Boolean" />
                </SelectParameters>
            </asp:SqlDataSource>
            <asp:Button ID="btnAbrirInternet" runat="server" Text="Aplicar Regras" />
            <asp:Button ID="btnPecharInternet" runat="server" Text="Pechar Internet" />
            <br />
            <br />
        </asp:Panel>
        <br />
        <br />
        <asp:Panel ID="pnlUltimoAcceso" runat="server"
            GroupingText="Información Ultimo Acceso" style="margin:0px auto"
            Width="70%" Height="180px">
            <br />
            <asp:DetailsView ID="dtvwDatosRexistro" runat="server" AutoGenerateRows="False"  style="margin:0px auto"
                CellPadding="4" DataSourceID="SqlDataRexistroActividade"
                EmptyDataText="Sen Datos" ForeColor="#333333" GridLines="None" Height="50px"
                HorizontalAlign="Center" Width="820px">
                <AlternatingRowStyle BackColor="White" />
                <CommandRowStyle BackColor="#D1DDF1" Font-Bold="True" HorizontalAlign="Left" />
                <EditRowStyle BackColor="#2461BF" />
                <FieldHeaderStyle BackColor="#DEE8F5" Font-Bold="True" HorizontalAlign="Left"
                    Width="30%" />
                <Fields>
                    <asp:BoundField DataField="nome_prof" HeaderText="Nome do Profesor:"
                        SortExpression="nome_prof" />
                    <asp:BoundField DataField="hora_permiso" HeaderText="Dia/Hora do permiso:"
                        SortExpression="hora_permiso" />
                    <asp:BoundField DataField="ata_hora" HeaderText="Ata a hora:"
                        SortExpression="ata_hora" />
                    <asp:TemplateField HeaderText="Tipo de permiso:" SortExpression="tipo_permiso">
                        <EditItemTemplate>
                            <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("tipo_permiso") %>'></asp:TextBox>
                        </EditItemTemplate>
                        <InsertItemTemplate>
                            <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("tipo_permiso") %>'></asp:TextBox>
                        </InsertItemTemplate>
                        <ItemTemplate>
                            <br />
                            <asp:TextBox ID="TextBox2" runat="server" ReadOnly="True" Rows="4"
                                Text='<%# Bind("tipo_permiso", "{0}") %>' TextMode="MultiLine" Width="80%"></asp:TextBox>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Fields>
                <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
                <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
                <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
                <RowStyle BackColor="#EFF3FB" />
            </asp:DetailsView>
            <br />
            <br />
            <asp:SqlDataSource ID="SqlDataHoras0" runat="server"
                ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
                SelectCommand="DevolucionHoras"
                SelectCommandType="StoredProcedure">
                <SelectParameters>
                    <asp:Parameter Name="tarde" Type="Boolean" />
                </SelectParameters>
            </asp:SqlDataSource>
            <br />
        </asp:Panel>
        
        <asp:SqlDataSource ID="SqlDataRexistroActividade" runat="server"
            ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
            InsertCommand="AltaRexistro" InsertCommandType="StoredProcedure"
            SelectCommand="ConsultaRexistro" SelectCommandType="StoredProcedure">
            <InsertParameters>
                <asp:Parameter Name="aula" Type="String" />
                <asp:Parameter Name="ip" Type="String" />
                <asp:Parameter Name="ata_hora" Type="String" />
                <asp:Parameter Name="tipo_permiso" Type="String" />
            </InsertParameters>
            <SelectParameters>
                <asp:ControlParameter ControlID="ddlAulas" Name="aula"
                    PropertyName="SelectedItem.Text" Type="String" />
                <asp:Parameter DefaultValue="0" Name="tipo" />
            </SelectParameters>
        </asp:SqlDataSource>
        <br />
    </div>
        <asp:Label ID="lblErro" runat="server" Text="Label" EnableViewState="False" Visible="False" Font-Size="X-Large" ForeColor="#C04000"></asp:Label>
    <asp:SqlDataSource ID="SqlDataDatosProfesor" runat="server"
        ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
        ProviderName="<%$ ConnectionStrings:ConnectionString.ProviderName %>"
        SelectCommand="Devolucion_DatosProfesor"
        SelectCommandType="StoredProcedure"></asp:SqlDataSource>
    </form>
</body>
</html>




firewall.aspx.vb:

Imports System.IO
Imports System.Net
Imports System.Diagnostics
Imports System.Security
Imports System.Threading
Imports Renci.SshNet

Partial Class firewall
    Inherits System.Web.UI.Page

    Private ReadOnly RUTA_SERVIDOR_WINDOWS As String = "C:\firewall\"
    Private ReadOnly RUTA_SERVIDOR_LINUX As String = "/firewall/"
    Private ip_conectado As String


    Sub chamarProcedemento(ByVal nomearquivo As String)

        Dim SERVIDOR_IP As String = "AA.BB.CC.DD"
        Dim PUERTO_SSH As Int16 = PUERTO_SSH



        '//////////////////////////////////PASAMOS O ARQUIVO AO SERVIDOR////////////////////////////////////
        Dim cliente As SftpClient

        Try
            cliente = New SftpClient(SERVIDOR_IP, PUERTO_SSH, "USER_SSH", "PASSWORD_SSH")
            cliente.Connect()

        Catch ex As Exception
            MsgBox(ex.Message)
            Return
        End Try

        cliente.ChangeDirectory("/firewall")
        Dim filestream As New FileStream(
RUTA_SERVIDOR_WINDOWS + nomearquivo, FileMode.Open, FileAccess.Read)
        cliente.BufferSize = 1024
        cliente.UploadFile(filestream, nomearquivo)
        filestream.Close()
        filestream.Dispose()

        cliente.Disconnect()
        cliente.Dispose()


        '//////////////////////////////////EXECUTAMOS O SCRIPT ////////////////////////////////////


        Dim ssh = New SshClient(SERVIDOR_IP, PUERTO_SSH, "USER_SSH", "PASSWORD_SSH")
        ssh.Connect()

        Dim answer As StringBuilder
        Dim ss As ShellStream = ssh.CreateShellStream("COMANDO", 80, 24, 800, 600, 1024)
        Dim reader = New StreamReader(ss)
        Dim writer = New StreamWriter(ss)
        writer.AutoFlush = True

        While (ss.Length = 0)
            Thread.Sleep(500)
        End While
        reader.ReadToEnd()

        WriteStream("sudo su", writer, ss)
        reader.ReadToEnd()

        WriteStream("cd " + RUTA_SERVIDOR_LINUX, writer, ss)
        reader.ReadToEnd()

        WriteStream("dos2unix " + RUTA_SERVIDOR_LINUX + obterNomeArquivo(), writer, ss)      ' CONVERTIMOS A FORMAT UNIX. INSTALADO PREVIAMENTE NO SERVIDOR O executable dos2unix
        reader.ReadToEnd()

        WriteStream("chmod 755 " + RUTA_SERVIDOR_LINUX + obterNomeArquivo(), writer, ss)
        reader.ReadToEnd()

        WriteStream(RUTA_SERVIDOR_LINUX + obterNomeArquivo(), writer, ss)
        answer = ReadStream(reader)

        ss.Dispose()
        ssh.Disconnect()
        ssh.Dispose()


    End Sub
 

    Private Sub gardarErro(ByVal aplicacion As String, ByVal mensaxe As String, ByVal evento As String)

        Me.SqlDataRexistroError.InsertParameters("aplicacion").DefaultValue = aplicacion
        Me.SqlDataRexistroError.InsertParameters("evento").DefaultValue = evento
        Me.SqlDataRexistroError.InsertParameters("mensaxe").DefaultValue = mensaxe
        Me.SqlDataRexistroError.Insert()

    End Sub
    Private Sub amosarErro(ByVal mensaxe As String)
        lblErro.Text = mensaxe
        lblErro.Visible = True
    End Sub

    Private Function obterNomeArquivo() As String

        Return "firewall_" + obterAula() + ".txt"

    End Function
 

    Private Function obterAula() As String
        Try
            Dim ip As String

            If (ddlAulas.Visible = True) Then
                If ddlAulas.SelectedIndex <> -1 Then
                    ip = ddlAulas.SelectedValue
                Else
                    ip = ddlAulas.Items(0).Value
                End If
            Else
                ip = ip_conectado
            End If

            Dim tercerpunto As Integer = ip.LastIndexOf(".") - 1
            Dim segundopunto As Integer = ip.LastIndexOf(".", tercerpunto - 1)

            Dim aula As String = ip.Substring(segundopunto + 1, tercerpunto - segundopunto)
            'If aula.Length = 1 Then
            '    aula = "0" + aula
            'End If

            Return aula
        Catch ex As Exception
            Return Nothing
        End Try

    End Function
 

    Private Sub operacionArquivo(ByVal nomearquivo As String, ByVal cadea As String, ByVal borrar As Boolean)
        ' Xera o arquivo para abrir ou pechar Internet nunha aula

        Dim arquivoescritura As IO.StreamWriter
        Dim aberto As Boolean = False

        Try

            If (borrar) Then
                IO.File.Delete(RUTA_SERVIDOR_WINDOWS + nomearquivo)
            End If


            If Not IO.File.Exists((RUTA_SERVIDOR_WINDOWS + nomearquivo)) Then
                arquivoescritura = New StreamWriter((RUTA_SERVIDOR_WINDOWS + nomearquivo), False, Encoding.UTF8)
            Else
                arquivoescritura = New StreamWriter((RUTA_SERVIDOR_WINDOWS + nomearquivo), True, Encoding.UTF8)

            End If

            arquivoescritura.NewLine = ControlChars.Lf          ' PARA S.O. LINUX
            ' DE TODAS FORMAS DEIXA CARACTERES 'RAROS' POLO TIPO UTF8. => Convertimos no servidor a linux
            arquivoescritura.WriteLine(cadea)
            arquivoescritura.Close()


        Catch ex As Exception
            amosarErro("ERRO: " + ex.Message)
        End Try

    End Sub

    Private Function establecerEstado() As Boolean
        Dim primerpunto As Integer = ip_conectado.IndexOf(".") + 1
        Dim segundopunto As Integer = ip_conectado.IndexOf(".", primerpunto + 1) + 1
        Dim tercerpunto As Integer = ip_conectado.IndexOf(".", segundopunto + 1) + 1

        Dim resul As Data.DataView

        Try
            resul = Me.SqlDataDatosProfesor.Select(DataSourceSelectArguments.Empty)
            Me.lblNome.Text = "Profesor conectado: " + resul.Item(0).Row.Item("nombre")

             dtvwDatosRexistro.Visible = True
              lblAula.Text = "AULA " + obterAula()

            Return True
        Catch ex As Exception
            gardarErro("FIREWALL", ex.Message, "Metodo EstablecerEstado")
            amosarErro(ex.Message)
            Return False
        End Try

    End Function


    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        ip_conectado = Request.ServerVariables("REMOTE_ADDR")

        If (Not IsPostBack) Then
            establecerEstado()
        End If

        ' Xestionamos o double click das listas
        lstEquipos.Attributes.Add("ondblclick", ClientScript.GetPostBackEventReference(lstEquipos, "event_lstEquipos_doubleclicklista"))
        If (Request("__EVENTARGUMENT") IsNot vbNullString) And (Request("__EVENTARGUMENT") = "event_lstEquipos_doubleclicklista") Then
            lstEquipos.Items.RemoveAt(lstEquipos.SelectedIndex)
        End If

        lstPortos.Attributes.Add("ondblclick", ClientScript.GetPostBackEventReference(lstEquipos, "event_lstPortos_doubleclicklista"))
        If (Request("__EVENTARGUMENT") IsNot vbNullString) And (Request("__EVENTARGUMENT") = "event_lstPortos_doubleclicklista") Then
            lstPortos.Items.RemoveAt(lstPortos.SelectedIndex)
        End If


    End Sub


    Protected Sub rblEquipos_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles rblEquipos.SelectedIndexChanged
        If rblEquipos.SelectedValue = 0 Then
            lstEquipos.Items.Clear()
            pnlEquiposLista.Visible = False
        Else
            pnlEquiposLista.Visible = True
        End If
    End Sub

    Protected Sub btnEngadirEquipo_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEngadirEquipo.Click

        Dim elem As New ListItem("EQUIPO " + txtNumEquipo.Text, txtNumEquipo.Text)
        Dim elemLista As ListItem = lstEquipos.Items.FindByText("EQUIPO " + txtNumEquipo.Text)

        If (txtNumEquipo.Text <> "") Then
            If Not IsNothing(elemLista) Then
                enviarMensaxeCliente("O EQUIPO XA ESTÁ NA LISTA")
            Else
                lstEquipos.Items.Add(elem)
            End If
            txtNumEquipo.Text = ""

        End If
    End Sub


    Protected Sub btnEngadirPorto_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEngadirPorto.Click
        If txtNumPorto.Text <> "" Then

            Dim elemLista As ListItem = lstPortos.Items.FindByText("PORTO " + txtNumPorto.Text)
            If Not IsNothing(elemLista) Then
                enviarMensaxeCliente("O PORTO XA ESTÁ NA LISTA")
            Else
                lstPortos.Items.Add(New ListItem("PORTO " + txtNumPorto.Text + "(" + rbtlTipoProtocolo.Text + ")", txtNumPorto.Text + "-" + rbtlTipoProtocolo.Text))
            End If

            txtNumPorto.Text = ""
        End If
    End Sub

    Protected Sub rblProtocolosOpcions_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles rblProtocolosOpcions.SelectedIndexChanged
        If rblProtocolosOpcions.SelectedValue = 2 Then
            pnlProtocolosLista.Visible = True
        Else
            lstPortos.Items.Clear()
            pnlProtocolosLista.Visible = False
        End If

    End Sub

    Protected Sub SqlDataHoras_Selecting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceSelectingEventArgs) Handles SqlDataHoras.Selecting
        ' Cargamos as horas de maña ou tarde
        If (DateTime.Now.Hour < 15) Then
            e.Command.Parameters.Item("@tarde").Value = 0
        Else
            e.Command.Parameters.Item("@tarde").Value = 1
        End If

    End Sub
 

    Private Sub WriteStream(ByVal cmd As String, ByVal writer As StreamWriter, ByVal stream As ShellStream)

        writer.WriteLine(cmd)
        While (stream.Length = 0)
            Thread.Sleep(500)
        End While
    End Sub

    Private Function ReadStream(ByVal reader As StreamReader) As StringBuilder
        Dim result As StringBuilder = New StringBuilder()

        Dim line As String = "EMPEZANDO"
        While (line IsNot Nothing)
            line = reader.ReadLine()
            result.AppendLine(line)
        End While
        Return result


    End Function

    Protected Sub btnPecharInternet_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPecharInternet.Click


        Dim cadea As String

        ' Primeiro eliminamos as regras que teríamos postas no iptables e borramos o arquivo de cron.d desa aula
        cadea = String.Format("iptables -S | sed '/_AULA{0}_/s/-A/iptables -D/e'", obterAula())
        operacionArquivo(obterNomeArquivo(), cadea, True)

        ' Borramos o arquivo de crontab
        cadea = String.Format("rm /etc/cron.d/AULA{0}", obterAula())
        operacionArquivo(obterNomeArquivo(), cadea, False)

        lstEquipos.Items.Clear()
        lstPortos.Items.Clear()

        rblEquipos.SelectedIndex = 0
        rblProtocolosOpcions.SelectedIndex = 0
        pnlProtocolosLista.Visible = False
        pnlEquiposLista.Visible = False

        chkICMP.Checked = False

        chamarProcedemento(obterNomeArquivo())
        enviarMensaxeCliente("OPERACION REALIZADA")

    End Sub
 

    Private Sub enviarMensaxeCliente(ByVal mensaxe As String)

        Dim codigo As String = String.Format("<script type=""text/javascript"">alert('{0}');</script>", mensaxe)
        Page.ClientScript.RegisterClientScriptBlock(Me.GetType, "detallesocio", codigo)

    End Sub
 

    Protected Sub btnAbrirInternet_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAbrirInternet.Click

        Dim numregra As Integer

        Dim regra As String
        Dim cadea As String

        Dim hora_fin_cadea As String = ddlHorario.SelectedValue

        Dim hora_actual As New DateTime
        Dim hora_fin As New DateTime(Date.Now.Year, Date.Now.Month, Date.Now.Day, Integer.Parse(hora_fin_cadea.Substring(0, hora_fin_cadea.IndexOf(":"))), Integer.Parse(hora_fin_cadea.Substring(hora_fin_cadea.IndexOf(":") + 1)), 0)


        ' LANZAMOS O SCRIPT AO SERVIDOR
        hora_actual = DateTime.Now

        If (hora_actual.CompareTo(hora_fin) > 0) Then
            enviarMensaxeCliente("A hora de fin de acceso a Internet non pode ser menor que a hora actual...")
            Return
        End If


        ' Primeiro eliminamos as regras que teríamos postas no iptables e borramos o arquivo de cron.d desa aula
        cadea = String.Format("iptables -S | sed '/_AULA{0}_/s/-A/iptables -D/e'", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, True)

        If rblEquipos.SelectedValue = 1 And lstEquipos.Items.Count > 0 Then     ' MARCAMOS ALGUN EQUIPO

            If rblProtocolosOpcions.SelectedValue = 0 Or rblProtocolosOpcions.SelectedValue = 1 Then

                ' ============================REGRAS DE ACCESO A INTERNET BASICO PARA ALGUNS EQUIPOS==================================
                For Each equipo As ListItem In lstEquipos.Items
                    If rblProtocolosOpcions.SelectedValue = 0 Then
                        ' EQUIPOS
                        regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.{1}/32 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                        operacionArquivo(obterNomeArquivo(), regra, False)
                        regra = String.Format("iptables -I FORWARD 1 -d www.google.es -s 192.168.{0}.{1}/32 -m comment --comment '_AULA{0}_' -j ACCEPT ", obterAula, equipo.Value)
                        operacionArquivo(obterNomeArquivo(), regra, False)
                    End If
                Next
            End If
            If rblProtocolosOpcions.SelectedValue = 1 Then  ' ACCESO SEGURO PARA ALGUNS EQUIPOS

                For Each equipo As ListItem In lstEquipos.Items
                    regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.{1}/32 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                    operacionArquivo(obterNomeArquivo(), regra, False)
                    regra = String.Format("iptables -I FORWARD 1 -p tcp --dport 443 -s 192.168.{0}.{1}/32 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                    operacionArquivo(obterNomeArquivo(), regra, False)
                Next

            End If

            If rblProtocolosOpcions.SelectedValue = 2 Then  ' ACCESO A ALGUNS PORTOS
                ' ============================regras de acceso a portos especificos==================================
                If (lstPortos.Items.Count > 0) Then

                    For Each equipo As ListItem In lstEquipos.Items
                        For Each porto As ListItem In lstPortos.Items
                            Dim separacion = porto.Value.IndexOf("-")
                            Dim porto_ = porto.Value.Substring(0, separacion)
                            Dim proto_ = porto.Value.Substring(separacion + 1)

                            If (porto_ = 80) Then      ' Se é porto 80 vai por SQUID => INPUT
                                regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.{1}/32 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                            Else
                                regra = String.Format("iptables -I FORWARD 1 -p " + proto_ + " --dport {1} -s 192.168.{0}.{2}/32 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, porto_, equipo.Value)
                            End If
                            operacionArquivo(obterNomeArquivo(), regra, False)
                        Next
                    Next

                End If
            End If
            If rblProtocolosOpcions.SelectedValue = 3 Then  ' ACCESO TOTAL A ALGUNS EQUIPOS
 

                For Each equipo As ListItem In lstEquipos.Items

                    regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.{1}/32 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                    operacionArquivo(obterNomeArquivo(), regra, False)
                    regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.{1}/32 -p udp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                    operacionArquivo(obterNomeArquivo(), regra, False)
                    regra = String.Format("iptables -I FORWARD 1 -p tcp -s 192.168.{0}.{1}/32 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                    operacionArquivo(obterNomeArquivo(), regra, False)
                    regra = String.Format("iptables -I FORWARD 1 -p udp -s 192.168.{0}.{1}/32 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                    operacionArquivo(obterNomeArquivo(), regra, False)
                Next


            End If


            If chkICMP.Checked Then ' TRAFICO ICMP
                For Each equipo As ListItem In lstEquipos.Items
                    regra = String.Format("iptables -I FORWARD 1 -s 192.168.{0}.{1}/24 -p icmp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, equipo.Value)
                    operacionArquivo(obterNomeArquivo(), regra, False)
                Next

            End If

        Else    ' TODOS OS EQUIPOS DA AULA
            ' ============================REGRAS DE ACCESO A INTERNET BASICO PARA TODOS OS EQUIPOS==================================
            If rblProtocolosOpcions.SelectedValue = 0 Then
                ' EQUIPOS
                regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.0/24 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)
                regra = String.Format("iptables -I FORWARD 1 -d www.google.es -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)

            End If

            '' ============================REGRAS DE ACCESO A INTERNET SEGURO==================================
            If (rblProtocolosOpcions.SelectedValue = 1) Then
                regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.0/24 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)
                regra = String.Format("iptables -I FORWARD 1 -p tcp --dport 443 -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)
            End If

            ' ============================regras de acceso a portos especificos==================================
            If (rblProtocolosOpcions.SelectedValue = 2) And (lstPortos.Items.Count > 0) Then

                For Each porto As ListItem In lstPortos.Items
                    Dim separacion As String = porto.Value.IndexOf("-")
                    Dim porto_ As String = porto.Value.Substring(0, separacion)
                    Dim proto_ As String = porto.Value.Substring(separacion + 1)

                    If (porto_ = "80") Then      ' Se é porto 80 vai por SQUID => INPUT
                        regra = String.Format("iptables -I INPUT 1 -s 192.168.{0}.0/24 -p tcp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                    Else
                        regra = String.Format("iptables -I FORWARD 1 -p " + proto_ + " --dport {1} -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula, porto_)

                    End If
                    operacionArquivo(obterNomeArquivo(), regra, False)
                Next
            End If

            If rblProtocolosOpcions.SelectedValue = 3 Then  ' ACCESO TOTAL A TODOS EQUIPOS
                regra = String.Format("iptables -I INPUT 1 -p tcp -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)
                numregra += 1
                regra = String.Format("iptables -I INPUT 1 -p udp -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)
                regra = String.Format("iptables -I FORWARD 1 -p tcp -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)
                regra = String.Format("iptables -I FORWARD 1 -p udp -s 192.168.{0}.0/24 -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)

            End If

            If chkICMP.Checked Then
                regra = String.Format("iptables -I FORWARD 1 -s 192.168.{0}.0/24 -p icmp -m comment --comment '_AULA{0}_' -j ACCEPT", obterAula)
                operacionArquivo(obterNomeArquivo(), regra, False)

            End If
        End If



        '===============CRONTAB=====================

        ' Cambiamnos os permisos para que se poida borrar
        cadea = String.Format("chmod 777 /etc/cron.d/AULA{0}.sh", obterAula)

        ' Borramos o arquivo de crontab
        cadea = String.Format("rm /etc/cron.d/AULA{0}", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("touch /etc/cron.d/AULA{0}", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("echo ""SHELL=/bin/sh"" | sudo tee -a /etc/cron.d/AULA{0} ", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("echo ""PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"" | sudo tee -a /etc/cron.d/AULA{0} ", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        ' HORA PARA PECHAR INTERNET
        Dim hora As String = ddlHorario.SelectedValue.Substring(0, ddlHorario.SelectedValue.IndexOf(":"))
        Dim min As String = ddlHorario.SelectedValue.Substring(ddlHorario.SelectedValue.IndexOf(":") + 1)
        Dim dia As String = Date.Now.Day.ToString
        Dim mes As String = Date.Now.Month.ToString

        cadea = String.Format("echo ""{1} {2} {3} {4} *   root  iptables -S | sed '/_AULA{0}_/s/-A/iptables -D/e'"" | sudo tee -a /etc/cron.d/AULA{0} ", obterAula, min.Trim, hora.Trim, dia.Trim, mes.Trim)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        cadea = String.Format("chmod 744 /etc/cron.d/AULA{0}", obterAula)
        operacionArquivo(obterNomeArquivo(), cadea, False)

        'Recargamos o cron
        cadea = String.Format("service cron reload")
        operacionArquivo(obterNomeArquivo(), cadea, False)



        ' REXISTRAMOS A ENTRADA NA BD.
        SqlDataRexistroActividade.InsertParameters("aula").DefaultValue = lblAula.Text
        SqlDataRexistroActividade.InsertParameters("ip").DefaultValue = ip_conectado
        SqlDataRexistroActividade.InsertParameters("ata_hora").DefaultValue = ddlHorario.SelectedValue

        Dim mensaxe As String = "EQUIPOS:" + rblEquipos.SelectedItem.Text + vbNewLine
        If rblEquipos.SelectedValue = 1 Then    ' Indicamos os equipos
            For Each equipo As ListItem In lstEquipos.Items
                mensaxe += vbTab + "   ----> " + equipo.Text + vbNewLine
            Next
        End If
        mensaxe += "TIPO CONEXION:" + vbNewLine
        If chkICMP.Checked Then
            mensaxe += vbTab + chkICMP.Text + vbNewLine
        End If

        mensaxe += vbTab + rblProtocolosOpcions.SelectedItem.Text + vbNewLine
        If rblProtocolosOpcions.SelectedValue = 2 Then
            For Each porto As ListItem In lstPortos.Items
                mensaxe += vbTab + "   ----> " + porto.Text + vbNewLine
            Next
        End If

        SqlDataRexistroActividade.InsertParameters("tipo_permiso").DefaultValue = mensaxe

        SqlDataRexistroActividade.Insert()

        chamarProcedemento(obterNomeArquivo)

        enviarMensaxeCliente("OPERACION FEITA.")


    End Sub


    Protected Sub SqlDataRexistroActividade_Selecting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceSelectingEventArgs) Handles SqlDataRexistroActividade.Selecting
        If ddlAulas.Visible = False Then
            e.Command.Parameters("@aula").Value = lblAula.Text
        End If
    End Sub
End Class


Y eso es todo.

Un saludo y hasta la próxima entrada.

domingo, 8 de noviembre de 2015

Plan de comunicación de un Centro Educativo


Siguiendo con las actividades del curso Educación conectada en tiempos de redes impartido por el INTEF, vamos a especificar las bases para crear un Plan de comunicación de un Centro Educativo.


Primero diremos que un Plan de Comunicación viene a ser el conjunto de recursos digitales (redes sociales, blog´s , sitio web, correo....) que dispone un Centro para comunicarse con: Equipo directivo, Profesorado, Alumnado, Familia, Comunidad Educativa y Entorno Social.

Como vemos, la comunicación puede ir dirigida a múltiples agentes diferentes y los medios, mensajes y forma de comunicar van a variar dependiendo de a quien vaya ir dirigido el mensaje.

El plan que voy a exponer está pensado para aplicarse a un C.I.F.P. (Centro Integrado de Formación Profesional) en el que los alumnos son mayoritariamente mayores de edad, De todas formas, quitando un par de detalles podría llevarse a cabo en otros Centros de características diferentes.

Objetivos:



Los objetivos que tendría que cumplir serían:

  • Una comunicación a todo el Entorno Social en el que se informe de la oferta educativa del Centro.
  • Una comunicación entre las personas que integran el equipo directivo, concretamente entre Director y Jefe de estudio-Secretario-Subdirector.
  • Una comunicación hacia el profesorado informando de las novedades a nivel de Centro que les afecten (como claustros, auditorías, festivos, reuniones, cursos,...)
  • Una comunicación entre el Profesorado informando de los recursos que pueden ser utilizados en Internet para el desarrollo de las clases.
  • Una comunicación dentro de cada Departamento, informando de los recursos que pueden ser utilizados en Internet para el desarrollo de las clases. Dicha información la expondrían los profesores del departamento.
  • Una comunicación hacia las familias. Aquí tendríamos varios niveles dependiendo de quien parta la información. Si es a nivel de Centro, sería información relacionada con el funcionamiento del mismo, si es a nivel de Profesorado, se podrían publicar las actividades que deben desarrollar los alumnos en casa.
  • Una comunicación desde los Alumnos al Profesorado, informando de los recursos que pueden ser utilizados en Internet para el desarrollo de las clases, ideas o mejoras sobre los recursos que ya hay.
  • Una comunicación desde los Ex-Alumnos al Alumnado en la que puedan informar de experiencias en sus puestos de trabajo que les puedan servir de ayuda al Alumnado así como el de añadir nuevas herramientas desde su experiencia profesional y que puedan ser adoptadas por el Profesorado,


Para mí esto sería lo ideal para crear una Comunicación con todos los actores que integran la Comunidad Educativa.

  • Desde el punto de vista de las Familias, éstas tendrían información sobre las actividades de sus hijos y estarían al día de los contenidos que están recibiendo.
  • Desde el punto de vista del Profesorado, éstos tendrían información de todas aquellas novedades que les afecten a nivel de Centro y tendrían un 'banco' de recursos de la Red para impartir clases. Habría una colaboración a nivel de Centro y a nivel de Departamento más específica.
  • Desde el punto de vista del Alumnado, éstos tendrían información sobre los recursos en la red para el desarrollo de las asignaturas, estarían informados de las novedades del Centro que les afecten y tendrían al alcance consejos y experiencias de ex-alumnos.
  • Desde el punto de vista del Centro Educativo éste sería visible para toda la Comunidad Educativa y Entorno Social. Expondría toda su oferta educativa actualizada e informaría de las novedades que pudieran ser de interés para dicho Entorno.


Uno de los problemas (y quizás el más importante) es el tiempo que se le tendría que dedicar, en cada uno de los niveles indicados, a la incorporación de información de interés. Habría que buscar mecanismos para 'obligar' a los alumnos / profesorado a compartir y buscar información.



Canales de comunicación y recursos a utilizar:



Comunicación entre el Centro y la Comunidad Escolar / Entorno Social: 
  • Desarrollo de una Web: información sobre la oferta educativa y novedades.
  • Página en Facebook enlazada con la página web: Publicación de novedades, noticias, excursiones, matrícula...
  • Google Calendar: Eventos relacionados con el Centro, como plazos de matriculación, inicio de curso, vacaciones, festivos,...
  • MediaWiki; Donde los profesores podrán publicar su material para poder ser compartido con la Comunidad Escolar.
  • Blog´s  enlazados con el Facebook: 
  • Blog de la Biblioteca: Blog en el que se publicarán todas las noticias que tengan relación con la biblioteca y el entorno literario (premios, publicación de libros, actividades para el fomento de la lectura, intercambio de libros de segunda mano,....). Esta información abarcaría todo el Entorno Social del Centro, no sólo eventos internos.
  • Blog de Orientación: Blog orientado a informar a la Comunidad Educativa sobre becas, trabajos, estudios,...

Comunicación entre el equipo directivo (reuniones, toma de decisiones, compras,...): 
  • Lista de correo.
  • Whatsapp 
  • Google Calendar
  • Google Drive

Comunicación del Centro al Profesorado (claustros, eventos, reuniones,cursos...): 
  • Lista de correo.
  • Twitter

Comunicación entre el Profesorado (recursos educativos en la red):
  • Pinterest.

Comunicación entre Ex-Alumnos y Profesorado/Alumnos (recursos educativos en la red, experiencias laborales, mejoras, ideas,...)

  • Facebook (privado)
  • Pinterest

Comunicación entre Profesorado y Alumnos (apuntes, tareas, recursos, avisos, novedades,...):

  • Moodle (privado)
  • Pinterest (privado)
  • Twitter
  • ClassRoom (privado)

Comunicación entre Profesorado y Familia (tareas, apuntes, novedades,...):
  • Facebook (privado)


sábado, 7 de noviembre de 2015

Memorias de un administrador. Control del ancho de banda.



Introducción:



Recomiendo leer un post anterior en el que describo como está estructurada la red en el Centro.


Nuestra conexión a Internet dispone de una velocidad de bajada y subida determinada. Si existen muchas peticiones (atascos) puede darse el caso de que deje responder o que tarde mucho en hacerlo, dando lugar a una navegación por la Web lenta.


 Uno de los mayores problemas al que me enfrenté fue el de controlar el ancho de banda de la salida a Internet para que tanto profesores como alumnos pudieran navegar de forma decente.

Por defecto, las tarjetas de red utilizan una cola pfifo_fast que lo que hace es que el primer paquete que llega es el primer paquete que sale. Pero ¿ qué pasa con el tipo de paquete...? Esta cola no lo tiene en cuenta, y para mi es deseable dar prioridad al tráfico web que al tráfico ftp, por ejemplo.

Además no distribuye el ancho de banda entre las diferentes redes del Centro. Por lo tanto puede darse el caso que un alumno empieza a descargar una imagen y obtenga mucho ancho de banda, dejando al resto del centro con una velocidad muy pequeña.

Cabe indicar que el ordenador que da acceso a Internet al Centro (de nombre VPROXY) tiene un S.O. Linux Ubuntu 14.04 y dispone de dos tarjetas de red (una para la red interna y otra conectada a Internet). 

También dispone de un proxy SQUID configurado para dar servicio al tráfico http (puerto 80) y poder hacer uso de la caché para tener una navegación más rápida.


El equipo VProxy tiene añadidas a la Iptables las siguientes líneas:
#INTERNA
IFACEIN=eth0
#EXTERNA
IFACEOUT=eth1

modprobe ip_nat_ftp
#// Activar el enrutamiento en un sistema Linux
echo "1" > /proc/sys/net/ipv4/ip_forward

 
#SQUID
iptables -t nat -A PREROUTING -i ${IFACEIN} -p tcp --dport 80 -j REDIRECT --to-ports 3128
# SALIMOS CON LA IP EXTERNA

iptables -t nat -A POSTROUTING -o ${IFACEOUT} -j MASQUERADE

Como vemos, todo lo que se dirige al puerto 80 es redirigido al puerto 3128 del proxy. Este tipo de proxy es transparente de tal forma que los equipos clientes no tienen que configurar nada para poder salir a través del proxy. Simplemente se cambia el gateway para que apunte al proxy (en nuestro Centro eso lo hace el Switch HP de nivel 3 que tenemos).
 
La IP que sale a Internet del equipo VProxy es la de la tarjeta externa (MASQUERADE).

Cabe indicar que con el SQUID también se puede realizar el control del ancho de banda, pero dicho control no tiene en cuenta el tipo de tráfico y sólo se limita al tráfico web (dirigido al puerto 80).


Direcciones IP:

Las aulas están en una red 192.168.XX.YY siendo:

  • XX: El número de aula.
  • YY: El número de equipo.

Por ejemplo: 192.168.1.10, sería el equipo 10 del aula1.


Cabe señalar que todas las aulas están conectadas a un switch HP 3500yl de nivel 3, de tal forma que cada boca del switch lo he configurado para atender a un aula/departamento a nivel de red, no de Mac. Es decir, he habilitado el enrutamiento en el switch.

Así, la boca del switch que está conectada al Aula1 tiene una ip en el switch 192.168.1.250 y está configurada para pedir los datos al DHCP en el ámbito de esta red (192.168.1.0/255.255.255.0). Explicaremos esto en otra entrada posterior.

El Gateway que tiene cada aula es la boca IP del switch, así el aula 1 (192.168.1.X) tiene como gateway la ip 192.168.1.250.
A su vez, el Switch tiene como gateway predeterminado la dirección 172.30.1.1 que se corresponde con el equipo virtualizado VPROXY explicado a continuación....




Servidor Virtualizado: VPROXY

 


Enruta los paquetes desde la red interna (192.168.0.0/255.255.0.0) a la red conectada a Internet. Partimos de la base de que ya está configurado para enrutar el tráfico.

Dispone de dos tarjetas de red:
  • eth1: 69.57.157.200 (red externa)
  • eht0: 172.30.128.1 (red interna)
A la red Interna llegan los paquetes a través del switch HP 3500yl que hace la labor de 'enrutador' y por eso recibe paquetes de las aulas (red 192.168.NUM_AULA.NUM_EQUIPO). En un caso 'normal' tendréis los equipos en la misma red que la tarjeta interna del proxy.

Este artículo no va a tratar en cómo se configura un Proxy-SQUID en Linux. Para eso disponéis de múltiples entradas en Internet.

Partimos por lo tanto que ya tenéis instalado y configurado el proxy SQUID (puerto 3128).

El problema que tenía de utilizar este software es la gestión del ancho de banda. Y sí, el squid tiene esa función, pero sólo afecta al tráfico http, no teniendo en cuenta el resto del tráfico.

Control del ancho de banda


Para solucionar el problema leí por internet un manual sobre la gestión del tráfico utilizando colas de prioridad.

En resumidas cuentas, podemos configurar en la tarjeta de red diferentes colas de prioridad en las que definimos la velocidad de bajada/subida y la velocidad de bajada/subida máxima. Estas colas estás asociadas a clases las cuales pueden tener un ancho de banda diferente en base a diferentes criterios, como puertos, direcciones IP, tipo de tráfico....

Así, puedo definir por cada aula, que tenga una velocidad de bajada de 1Mbps y en caso de que haya ancho de banda, llegar hasta 5Mbps. Estas colas van a afectar a todo el tráfico que pase por la tarjeta, con lo que solucionamos el problema del SQUID. Las colas de prioridad veremos que las definiremos sobre la tarjeta interna, por el problema de que el tráfico que viene de fuera y llega a la red externa estará 'enmascarado' con la dirección IP del Proxy SQUID.

Cabe señalar que las colas de prioridades no conocen lo que te vas a descargar, por lo que la limitación del ancho de banda la realizan en base al descarte de paquetes.

A continuación os dejo el script que tengo, basado en varios encontrados por Internet, pero de los que no guardo el enlace original ya que lo hice hace bastante tiempo:

Este es el script que tenía en un principio. Comentar que el ancho de banda (parámetro rate de las colas) reservado a cada red es el resultado de dividir el ancho de banda total entre el número de redes. De esta forma, cada red (aula - departamento) tendrá un mínimo ancho de banda garantizado

Nota:  Mirar actualización del script a continuación.

RED.sh:

#!/bin/bash
# INTERFACE EXTERNA CONECTADA A INTERNET
IFACE=eth1
#INTERFACE INTERNAS: ALUMNOS
IFACEIN=eth0
# IP SQUID EXTERNA
IPSQUID=69.57.157.249
# En kbits . 1mbps = 1024 Kilo bit por segundo
VELOCIDAD_BAJADA=81920  

VELOCIDAD_REDSERVIDORES=1024
VELOCIDAD_SUBIDA_SQUID=7168

#Velocidad de Bajada
VELOCIDAD_MAXIMA_ALUMNOS=8 # en Mbps
VELOCIDAD_MAXIMA_SERVIDORES=4


#NUMERO DE REDES PARA DIVIDIR EL ANCHO DE BANDA DE BAJADA
# Formato red: ALUMNOS => 192.168.X ; 
REDES_ALUMNOS=( 1 2 3 4 6 7 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 50 51 52 53 54 55 56 57 )
NUMREDES_ALUMNOS=${#REDES_ALUMNOS[@]} 
NUMREDES_TOTAL=$[$NUMREDES_ALUMNOS + 1] # RED SERVIDORES




# BORRAMOS LAS COLAS
tc qdisc del dev ${IFACE} root
tc qdisc del dev ${IFACE} ingress
# CREAMOS LAS COLAS
tc qdisc add dev ${IFACE} root handle 1: htb default 99
tc class add dev ${IFACE} parent 1: classid 1:1 htb rate ${VELOCIDAD_SUBIDA_SQUID}kbit ceil ${VELOCIDAD_SUBIDA_SQUID}kbit

#TRAFICO WEB el de mayor prioridad
tc class add dev ${IFACE} parent 1:1 classid 1:10 htb rate $[$VELOCIDAD_SUBIDA_SQUID*60/100]kbit ceil ${VELOCIDAD_SUBIDA_SQUID}kbit prio 1
#TRAFICO WEB SEGURO, DROPBOX Y RESTO
tc class add dev ${IFACE} parent 1:1 classid 1:20 htb rate $[$VELOCIDAD_SUBIDA_SQUID*30/100]kbit ceil $[$VELOCIDAD_SUBIDA_SQUID*60/100]kbit prio 2
#TRAFICO FTP
tc class add dev ${IFACE} parent 1:1 classid 1:30 htb rate $[$VELOCIDAD_SUBIDA_SQUID*10/100]kbit ceil $[$VELOCIDAD_SUBIDA_SQUID*40/100]kbit prio 3


# Hacemos sfq para que se reparta entre todos y nadie cope todo el ancho de banda
tc qdisc add dev ${IFACE} parent 1:10 handle 100: sfq perturb 10
tc qdisc add dev ${IFACE} parent 1:20 handle 200: sfq perturb 10
tc qdisc add dev ${IFACE} parent 1:30 handle 300: sfq perturb 10


#Mandamos por la de mas capacidad el icmp, web
iptables -F -t mangle

# EN EL SQUID SOLO EL TRAFICO WEB. COMO HACE NAT SIEMPRE SALE CON LA DIRECCION EXTERNA. 
# 443 ES EL QUE USA DROPBOX
iptables -t mangle -A POSTROUTING -p tcp -m tcp --dport 443 -j CLASSIFY --set-class 1:20
iptables -t mangle -A POSTROUTING -p tcp -m tcp --dport 443 -j RETURN# FTP
iptables -t mangle -A POSTROUTING -p tcp -m tcp --dport 20:21 -j CLASSIFY --set-class 1:30
iptables -t mangle -A POSTROUTING -p tcp -m tcp --dport 20:21 -j RETURN
iptables -t mangle -A POSTROUTING -o ${IFACE} -j CLASSIFY --set-class 1:10
iptables -t mangle -A POSTROUTING -o ${IFACE} -j RETURN

#############################################DESCARGA#######################################
#############################################ALUMNOS######################################
tc qdisc del dev ${IFACEIN} root
tc qdisc add dev ${IFACEIN} root handle 1:0 htb default 99
tc class add dev ${IFACEIN} parent 1:0 classid 1:1 htb rate ${VELOCIDAD_BAJADA}kbit ceil ${VELOCIDAD_BAJADA}kbit


for RED in ${REDES_ALUMNOS[@]}
do

    CLASS=${RED}
    # TEMOS UN PROBLEMA COA REDE 1
    if [ ${RED} -eq 1 ]
    then
       CLASS=100
    fi
 
  tc class add dev ${IFACEIN} parent 1:1 classid 1:${CLASS} htb rate $[$VELOCIDAD_BAJADA/$NUMREDES_TOTAL]kbit ceil ${VELOCIDAD_MAXIMA_ALUMNOS}Mbit prio 2 
  tc qdisc add dev ${IFACEIN} parent 1:${CLASS} handle $[$CLASS+200]: sfq perturb 10

  iptables -t mangle -A POSTROUTING -p tcp -d 192.168.${RED}.0/24 -j CLASSIFY --set-class 1:${CLASS}
  iptables -t mangle -A POSTROUTING -p tcp -d 192.168.${RED}.0/24 -j RETURN

  iptables -t mangle -A POSTROUTING -p udp -d 192.168.${RED}.0/24 -j CLASSIFY --set-class 1:${CLASS}
  iptables -t mangle -A POSTROUTING -p udp -d 192.168.${RED}.0/24 -j RETURN
done

#############################################SERVIDORES###################################### 
#REDE DE SERVIDORES
  tc class add dev ${IFACEIN} parent 1:1 classid 1:600 htb rate ${VELOCIDAD_REDSERVIDORES}kbit ceil ${VELOCIDAD_MAXIMA_SERVIDORES}Mbit prio 2
  tc qdisc add dev ${IFACEIN} parent 1:600 handle 700: sfq perturb 10

  iptables -t mangle -A POSTROUTING -p tcp -d 172.30.0.0/16 -j CLASSIFY --set-class 1:600
  iptables -t mangle -A POSTROUTING -p tcp -d 172.30.0.0/16 -j RETURN
  iptables -t mangle -A POSTROUTING -p udp -d 172.30.0.0/16 -j CLASSIFY --set-class 1:600
  iptables -t mangle -A POSTROUTING -p udp -d 172.30.0.0/16 -j RETURN


#POR DEFECTO PARA LA NO CLASIFICADA

tc class add dev ${IFACEIN} parent 1:1 classid 1:99 htb rate 500kbit ceil 1Mbit prio 3
tc qdisc add dev ${IFACEIN} parent 1:99 handle 99: sfq perturb 10


============ACTUALIZADO A NOVIEMBRE DEL 2015==============


Buscando por Internet como controlar el ancho  de banda de las conexiones de subida, encontré esto: https://github.com/firehol/firehol/wiki

Resulta que es una herramienta que actúa igual que el script que hice anteriormente, pero de una forma más intuitiva.

Lo que más me llevó tiempo fue entender que:

  • Necesita aplicar las colas a la interface interna, ya que por el mascarade del Iptables las direcciones llegan desde fuera teniendo como destino la IP pública del equipo VPROXY y no las direcciones ip´s internas.
  • Al aplicar las reglas sobre la red interna, el tráfico de bajada se aplica sobre la cola de SALIDA de la interface interna y las IP´s a controlar serán las internas y por lo tanto la de DESTINO.
                 interface $DEVICEIN world-in ouput rate $VELOCIDAD_BAJADA minrate 

          Y para controlar el tráfico de bajada de un aula tendré que poner:

             class group departamento1 rate 1000 ceil 5000 prio 1 balanced
                     match dst 192.168.2.0/24    # Aula 2
 
Fijarse que pongo destino ya que los paquetes vienen de fuera hacia dentro (por lo tanto es tráfico de bajada) y se dirigen a la IP interna.
 Esta línea lo que hace básicamente es crear una cola para todo el tráfico que vaya a 192.168.2.X dando un ancho de banda de 1000kbps y siempre que haya un ancho de banda disponible aumentar el tráfico a 5000Kbps.

  • El tráfico de subida se aplica sobre la cola de ENTRADA de la interface interna
    interface $DEVICEIN world-out input rate $VELOCIDAD_SUBIDA minrate 


Aquí os deje el script tal cual está, guardado en el directorio /etc/firehol

SCRIPT fireqos.conf

#!/bin/bash
# INTERFACE EXTERNA CONECTADA A INTERNET
DEVICEOUT=eth1
#INTERFACE INTERNA
DEVICEIN=eth0
# En kbits . 1mbps = 1024 Kilo bit por segundo
VELOCIDAD_BAJADA=100000   # No test de velocidade daba sobre 60
VELOCIDAD_SUBIDA=12000

# VELOCIDAD MAXIMA BAJADA SERVIDORES
VELOCIDAD_RED_SERVIDORES=2048

# VELOCIDADE DE BAJADA MAXIMA PARA ALUMNOS

VELOCIDAD_MAXIMA_ALUMNOS=12000 # en kbps

# VELOCIDADE DE BAJADA MAXIMA PARA SERVIDORES

VELOCIDAD_MAXIMA_SERVIDORES=4000

# VELOCIDADE DE SUBIDA MAXIMA PARA ALUMNOS

VELOCIDAD_MAXIMA_SUBIDA=1500


#NUMERO DE REDES PARA DIVIDIR EL ANCHO DE BANDA DE BAJADA
REDES_ALUMNOS=( 1 2 3 4 6 7 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 50 51 52 53 54 55 56 57 )
NUMREDES_ALUMNOS=${#REDES_ALUMNOS[@]}
NUMREDES_TOTAL=$[$NUMREDES_ALUMNOS +1]  # RED DE SERVIDORES

PORCENTAJE_maspequeno=5/100
   # Por el minrate. No puede ser más grande que el menor ancho de banda

##################################BAJADA######################################## 

interface $DEVICEIN world-in ouput rate $VELOCIDAD_BAJADA minrate $[($VELOCIDAD_BAJADA/$NUMREDES_TOTAL)*$PORCENTAJE_maspequeno] balanced

      class group servidores rate 500Kbit  ceil ${VELOCIDAD_RED_SERVIDORES} prio 1  balanced
      match dst 172.30.1.0/24
      class interactive                 commit 50%
         match udp port 53             # <<< DNS
         match icmp                    # <<< ping
      class synacks                     commit 20%      # <<< the new synacks class
         match tcp syn                    # <<< TCP packets with SYN set
         match tcp ack                    # <<< small TCP packets with ACK set

    class group end

for RED in ${REDES_ALUMNOS[@]}
do

    class group aula${RED} rate $[$VELOCIDAD_BAJADA/$NUMREDES_TOTAL] ceil ${VELOCIDAD_MAXIMA_ALUMNOS} prio 1 balanced
      match dst 192.168.${RED}.0/24
      class interactive                 commit 10%
         match udp port 53             # <<< DNS
         match tcp port 22             # <<< SSH
         match icmp                    # <<< ping
      class surfing                     commit 40%
         match tcp sport 80
      class surfing_seguro        commit 30%
     match tcp sport 443       
      class synacks                     commit 5%      # <<< the new synacks class
         match tcp syn                    # <<< TCP packets with SYN set
         match tcp ack                    # <<< small TCP packets with ACK set

    class group end


done


##################################SUBIDA########################################
interface $DEVICEIN world-out input rate $VELOCIDAD_SUBIDA minrate $[($VELOCIDAD_SUBIDA/$NUMREDES_TOTAL)*$PORCENTAJE_
maspequeno]  balanced

   class group servidores rate 256  ceil ${VELOCIDAD_MAXIMA_SUBIDA}  prio 1 balanced
     match src 172.30.1.0/24
      class interactive                 commit 60%
         match udp port 53             # <<< DNS
         match icmp                    # <<< ping
      class synacks                     commit 10%      # <<< the new synacks class
         match tcp syn                    # <<< TCP packets with SYN set
         match tcp ack                    # <<< small TCP packets with ACK set


   class group end


for RED in ${REDES_ALUMNOS[@]}
do

    class group aula{RED} rate $[$VELOCIDAD_SUBIDA/$NUMREDES_TOTAL] ceil ${VELOCIDAD_MAXIMA_SUBIDA} prio 1 balanced
      match src 192.168.${RED}.0/24
      class surfing                     commit 45%
         match tcp dports 80
      class surfing_seguro        commit 30%
         match tcp dports 443
      class synacks                     commit 15%      # <<< the new synacks class
         match udp port 53             # <<< DNS
         match icmp                    # <<< ping
         match tcp port 22             # <<< SSH
         match tcp syn                    # <<< TCP packets with SYN set
         match tcp ack                    # <<< small TCP packets with ACK set

    class group end


done

Para iniciar las colas pondremos:
  • fireqos.in start
Para parar:
  • fireqos.in stop




Añadimos una entrada al archivo /etc/rc.local (al final de todo) para lanzar este script...


Espero que os haya servido de ayuda.
Hasta la siguiente entrada.