Página de erro do SquidGuard customizada | Nova instancia NGINX (suporte PHP)



  • Olá PessoALL ! Salve !

    Depois de muita leitura em tópicos do Fórum, aos quais agradeço antecipadamente, consegui subir uma nova instância do NGINX com suporte ao PHP (sem fazer download do arquivo PHP como em alguns tutoriais). Também pode ser utilizada para hospedar os arquivos do WPAD, sem utilizar ou modificar os arquivos padrões do PFSense.

    Primeiro passo, criar um script para subir uma instancia do NGINX sem utilizar ou modificar os arquivos padrões do PFSense:

    Entrar pelo Putty no PFSense e com usuário root, na tela de opções escolher a opção 8.

    vi /usr/local/etc/rc.d/nginx2.sh

    Inserir com o conteúdo:

    #!/bin/sh
    /usr/local/sbin/nginx -c /usr/local/etc/nginx/nginx2.conf
    chmod go+rw /var/run/php-fpm.socket
    

    Dar permissão de execução ao script:

    #chmod +x /usr/local/etc/rc.d/nginx2.sh

    Criar o arquivo de configuração da nova instancia do NGINX

    #vi /usr/local/etc/nginx/nginx2.conf

    
    # nginx configuration file
    	worker_processes  1;
    	events {
    	    worker_connections  1024;
    	}
    	http {
    	        include       /usr/local/etc/nginx/mime.types;
    	        default_type  application/octet-stream;
    	        sendfile        on;
    	        keepalive_timeout 65;
    
    	        server {
    	                listen 81;
    	                server_name wpad.contoso.local
    	                server name 127.0.0.1
    
    	                #---------------------------------------------------------------#
    	                client_max_body_size 200m;
    	                #---------------------------------------------------------------#
    
    	                root "/usr/local/www/nginx-dist/";
    	                #altere o caminho caso seu sgerror.php fique em outra pasta
    
    	                location ~ \.php$ {
    	                        try_files $uri =404; #  This line closes a potential security hole
    	                        fastcgi_pass   unix:/var/run/php-fpm.socket;
    	                        fastcgi_index  index.php;
    	                        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    	                        fastcgi_read_timeout 180;
    	                        include        /usr/local/etc/nginx/fastcgi_params;
    	                }
    	        }
    	}
    
    

    Segundo passo é configurar o PFSense e o SquidGuard:

    A porta 81 deve estar aberta para acesso partindo da LAN. Neste caso utilizei o Squid+SquidGuard em modo não transparente.

    Nas regras de ACL do SquidGuard, deve escolher a opção EXT URL ERR PAGE (ENTER URL) ao invés do padrão INT ERROR PAGE (ENTER ERROR MESSAGE).

    No campo REDIRECT INFO colocar: http://IP_DO_SERVIDOR_PFSENSE:81/sgerror.php?url=403%20&a=%a&n=%n&i=%i&s=%s&t=%t&u=%u

    Salvar e dar o APPLY no SquidGuard.

    Com o WinSCP, se conectar no servidor PFSense com credenciais de root. Entrar na pasta: /usr/local/www/nginx-dist

    Criar o arquivo sgerror.php com o conteúdo:

    
    include "globals.inc";
    include "config.inc";
    $page_info = <<<eod<br># ----------------------------------------------------------------------------------------------------------------------
    # SquidGuard error page generator
    # (C)2006-2007 Serg Dvoriancev
    # ----------------------------------------------------------------------------------------------------------------------
    # This programm processed redirection to specified URL or generated error page for standart HTTP error code.
    # Redirection supported http and https protocols.
    # ----------------------------------------------------------------------------------------------------------------------
    # Format:
    #        sgerror.php?url=[http://myurl]or[https://myurl]or[error_code[space_code]output-message][incoming SquidGuard variables]
    # Incoming SquidGuard variables:
    #        a=client_address
    #        n=client_name
    #        i=client_user
    #        s=client_group
    #        t=target_group
    #        u=client_url
    # Example:
    #        sgerror.php?url=http://myurl.com&a=..&n=..&i=..&s=..&t=..&u=..
    #        sgerror.php?url=https://myurl.com&a=..&n=..&i=..&s=..&t=..&u=..
    #        sgerror.php?url=404%20output-message&a=..&n=..&i=..&s=..&t=..&u=..
    # ----------------------------------------------------------------------------------------------------------------------
    # Tags:
    #        myurl and output messages can include Tags
    #                [a] - client address
    #                [n] - client name
    #                [i] - client user
    #                [s] - client group
    #                [t] - target group
    #                [u] - client url
    # Example:
    #         sgerror.php?url=401 Unauthorized access to URL [u] for client [n]
    #      sgerror.php?url=http://my_error_page.php?cladr=%5Ba%5D&clname=%5Bn%5D // %5b=[ %d=]
    # ----------------------------------------------------------------------------------------------------------------------
    # Special Tags:
    #      blank     - get blank page
    #        blank_img - get one-pixel transparent image (for replace banners and etc.)
    # Example:
    #        sgerror.php?url=blank
    #        sgerror.php?url=blank_img
    # ----------------------------------------------------------------------------------------------------------------------
    EOD;
    
    define('ACTION_URL', 'url');
    define('ACTION_RES', 'res');
    define('ACTION_MSG', 'msg');
    
    define('TAG_BLANK',     'blank');
    define('TAG_BLANK_IMG', 'blank_img');
    
    # ----------------------------------------------------------------------------------------------------------------------
    # ?url=EMPTY_IMG
    #      Use this options for replace baners/ads to transparent picture. Thisbetter for viewing.
    # ----------------------------------------------------------------------------------------------------------------------
    # NULL GIF file
    # HEX: 47 49 46 38 39 61 - - -
    # SYM: G  I  F  8  9  a  01 00 | 01 00 80 00 00 FF FF FF | 00 00 00 2C 00 00 00 00 | 01 00 01 00 00 02 02 44 | 01 00 3B
    # ----------------------------------------------------------------------------------------------------------------------
    define(GIF_BODY, "GIF89a\x01\x00\x01\x00\x80\x00\x00\xFF\xFF\xFF\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B");
    
    $url  = '';
    $msg  = '';
    $cl   = Array(); // squidGuard variables: %a %n %i %s %t %u
    $err_code = array();
    
    $err_code[301] = "301 Moved Permanently";
    $err_code[302] = "302 Found";
    $err_code[303] = "303 See Other";
    $err_code[305] = "305 Use Proxy";
    
    $err_code[400] = "400 Bad Request";
    $err_code[401] = "401 Unauthorized";
    $err_code[402] = "402 Payment Required";
    $err_code[403] = "Acesso Indevido";
    $err_code[404] = "404 Not Found";
    $err_code[405] = "405 Method Not Allowed";
    $err_code[406] = "406 Not Acceptable";
    $err_code[407] = "407 Proxy Authentication Required";
    $err_code[408] = "408 Request Time-out";
    $err_code[409] = "409 Conflict";
    $err_code[410] = "410 Gone";
    $err_code[411] = "411 Length Required";
    $err_code[412] = "412 Precondition Failed";
    $err_code[413] = "413 Request Entity Too Large";
    $err_code[414] = "414 Request-URI Too Large";
    $err_code[415] = "415 Unsupported Media Type";
    $err_code[416] = "416 Requested range not satisfiable";
    $err_code[417] = "417 Expectation Failed";
    
    $err_code[500] = "500 Internal Server Error";
    $err_code[501] = "501 Not Implemented";
    $err_code[502] = "502 Bad Gateway";
    $err_code[503] = "503 Service Unavailable";
    $err_code[504] = "504 Gateway Time-out";
    $err_code[505] = "505 HTTP Version not supported";
    
    # ----------------------------------------------------------------------------------------------------------------------
    # check arg's
    # ----------------------------------------------------------------------------------------------------------------------
    
    if (count($_POST)) {
        $url  = trim($_POST['url']);
        $msg  = $_POST['msg'];
        $cl['a'] = $_POST['a'];
        $cl['n'] = $_POST['n'];
        $cl['i'] = $_POST['i'];
        $cl['s'] = $_POST['s'];
        $cl['t'] = $_POST['t'];
        $cl['u'] = $_POST['u'];
    }
    elseif (count($_GET)) {
        $url  = trim($_GET['url']);
        $msg  = $_GET['msg'];
        $cl['a'] = $_GET['a'];
        $cl['n'] = $_GET['n'];
        $cl['i'] = $_GET['i'];
        $cl['s'] = $_GET['s'];
        $cl['t'] = $_GET['t'];
        $cl['u'] = $_GET['u'];
    }
    else {
           # Show 'About page'
            echo get_page(get_about());
            exit();
    }
    
    # ----------------------------------------------------------------------------------------------------------------------
    # url's
    # ----------------------------------------------------------------------------------------------------------------------
    if ($url) {
        $err_id = 0;
    
        // check error code
        foreach ($err_code as $key => $val) {
                if (strpos(strtolower($url), strval($key)) === 0) {
                   $err_id = $key;
                   break;
                }
        }
    
        # blank page
        if ($url === TAG_BLANK) {
                echo get_page('');
        }
        # blank image
        elseif ($url === TAG_BLANK_IMG) {
               $msg = trim($msg);
               if(strpos($msg, "maxlen_") !== false) {
                  $maxlen = intval(trim(str_replace("maxlen_", "", $url)));
                  filter_by_image_size($cl['u'], $maxlen);
                  exit();
               }
               else {
                  # --------------------------------------------------------------
                  # return blank image
                  # --------------------------------------------------------------
                  header("Content-Type: image/gif;"); //  charset=windows-1251");
                  echo GIF_BODY;
               }
        }
        # error code
        elseif ($err_id !== 0) {
                $er_msg = strstr($_GET['url'], ' ');
                echo get_error_page($err_id, $er_msg);
        }
        # redirect url
        elseif ((strpos(strtolower($url), "http://") === 0) or (strpos(strtolower($url), "https://") === 0)) {
                # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                # redirect to specified url
                # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                header("HTTP/1.0");
                header("Location: $url", '', 302);
        }
        // error arguments
        else {
            echo get_page("sgerror: error arguments $url");
        }
    }
    else {
            echo get_page($_SERVER['QUERY_STRING']); //$url . implode(" ", $_GET));
    #        echo get_error_page(500);
    }
    
    # ~~~~~~~~~~
    # Exit
    # ~~~~~~~~~~
    exit();
    
    # ----------------------------------------------------------------------------------------------------------------------
    # functions
    # ----------------------------------------------------------------------------------------------------------------------
    function get_page($body) {
            $str = Array();
            $str[] = '';
            $str[] = "\n$body\n";
            $str[] = '';
            return implode("\n", $str);
    }
    
    #########################################################
    #                                                       #
    #  Página de Erro do SquidGuard customizada   - Inicio  #
    #                                                       #
    #########################################################
    
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # IE displayed self-page, if them size > 1024
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    function get_error_page($er_code_id, $err_msg='') {
            global $err_code;
            global $cl;
            global $g;
            global $config;
            $str = Array();
    
        $str[] = '';
            $str[] = '';
        $str[] = '';
            $str[] = '';
        $str[] = '';
        $str[] = '';
        $str[] = '';
        $str[] = '![logo](http://192.168.10.252:81/logo.jpg)';
        $str[] = '
    
    O endereço requisitado não pôde ser aberto:
    <a style="font-size:80%; font-family:Helvetica; color:#7f7f7f;"></a>';
        $str[] = '
    
    ';
                if ($cl['u'])        $str[] = "**{$cl['u']}**";
        $str[] = '';
        $str[] = "
    
    Motivo:   {$err_code[$er_code_id]} 
    
    ";
        $str[] = '
    O **controle de acessos** identificou este site em uma das regras de bloqueio definidas pelo sistema automático ou pela direção.
    _Todo ou qualquer acesso à internet é registrado em nosso sistema de relatórios._
    Se a regra automática não estiver correta ou precisar da liberação,
    entre em contato com a TI ou com a equipe de [NOC - BUGBUSTERS](mailto:noc@bugbusters.com.br?Subject=SOLICITACAO - DESBLOQUEIO)';
        $str[] = '
    
    ';
        $str[] = 'Informações do Bloqueio:';
        $str[] = '';
                if ($cl['n'])        $str[] = "Nome do Cliente: {$cl['n']} | ";
                if ($cl['a'])        $str[] = "IP do Cliente: {$cl['a']} | ";
                if ($cl['i'])        $str[] = "Usuário: {$cl['i']} | ";
                if ($cl['s'])        $str[] = "Grupo: {$cl['s']} | ";
                if ($cl['t'])        $str[] = "Categoria: {$cl['t']} ";
        $str[] = '';
    
        $str[] = "";
            $str[] = "";
    
            return implode("\n", $str);
    }
    
    #########################################################
    #                                                       #
    #  Página de Erro do SquidGuard customizada   - Fim     #
    #                                                       #
    #########################################################
    
    function filter_by_image_size($url, $val_size) {
    
              # load url header
              $ch = curl_init();
              curl_setopt($ch, CURLOPT_URL, $url);
              curl_setopt($ch, CURLOPT_HEADER, 1);
              curl_setopt($ch, CURLOPT_NOBODY, 1);
              curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
              $hd = curl_exec($ch);
              curl_close($ch);
    
             $size = 0;
             $SKEY = "content-length:";
             $s_tmp = strtolower($hd);
             $s_tmp = str_replace("\n", " ", $s_tmp); # replace all "\n"
             if (strpos($s_tmp, $SKEY) !== false) {
                 $s_tmp = trim(substr($s_tmp, strpos($s_tmp, $SKEY) + strlen($SKEY)));
                 $s_tmp = trim(substr($s_tmp, 0, strpos($s_tmp, " ")));
                 if (is_numeric($s_tmp))
                      $size = intval($s_tmp);
                 else $size = 0;
             }
    
             # === check url type and content size ===
             # redirect to specified url
             if (($size !== 0) && ($size < $val_size)) {
                  header("HTTP/1.0");
                  header("Location: $url", '', 302);
             }
             # return blank image
             else {
                  header("Content-Type: image/gif;");
                  echo GIF_BODY;
             }
    }
    ?>
    
    Inserir na mesma pasta o arquivo de imagem logo.jpg
    
    ============================================
    
    Não conheço de PHP e da estrutura interna do PFSense, mas tenho bastante experiência com tecnologia. Com certeza pessoas com mais calibre e especialização como o Marcelloc poderão contribuir para melhorar o processo. Não sei se a parte de inicialização, script, etc pode ser melhorado ou se está correto assim.
    
    Utilizei de muitos posts aqui descritos e posts como os do WPAD do www.javcasta.com, dentre outros.
    
    Vou acompanhar as ideias de melhorias e posteriormente gravo um video demonstrando os pequenos passos e acessos.
    
    Abraços, espero que tenha sido útil e devolver o favor de todos que compartilham e apoiam.
    
    ![squidguard_errorpage.png](/public/_imported_attachments_/1/squidguard_errorpage.png)
    ![squidguard_errorpage.png_thumb](/public/_imported_attachments_/1/squidguard_errorpage.png_thumb)[/u][/u][/s][/i]</eod<br>
    


  • Olá.

    Eu não tentei, mas parece boa ideia. Obrigado por citação minha página https://javcasta.com/ :)

    Cumprimentos



  • Olá javcasta,

    Eu quem agradeço por compartilhar conhecimento no seu site.

    Depois testa e compartilhe com o que pode ser melhorado !  ;D

    Abs.



  • Bom, depois de diversas tentativas, realmente este processo de subir uma segunda instância foi a única solução.

    Utilizo o webConfigurator com HTTPS e uma porta diferente do padrão, aí o SquidGuard se perde no redirecionamento.

    Desta forma, o problema foi contornado!  8)



  • Ótimo tutorial!

    Realizei os procedimentos e estou muito satisfeito com o resultado! Parabéns e continue sempre auxiliando a comunidade!



  • Olá,

    Esse procedimento só funciona com sites http, correto? No meu caso, com proxy não transparente, o squidguard já consegue exibir a mensagem de bloqueio para sites http conforme imagem abaixo, sem a necessidade de executar o procedimento do post:

    Porém, com sites https, eu só tenho a mensagem de erro do navegador, mesmo eu tentando levantar uma nova instância do nginx e executar os procedimentos citados pelo colega.

    Eu estava usando proxy transparente e interceptação SSL, usando alias no firewall e bypass do squid pra fazer com que sites de banco e do governo não viessem passar pelo filtro, mas ao longo dos meses comecei ter vários problemas, eu tinha erro de websocket em alguns sites (rejeição do certificado pelo que li), mesmo colocando no bypass e liberando a rede desses sites, eu tinha outros erros, o squidguard as vezes se perdia e não conseguia bloquear mais nada do dia pra noite, acabei desistindo no inicio desta semana e agora estou tentando usar o proxy não transparente.

    Agradeço desde já se alguém tiver uma luz, se eu encontrar algo, posto aqui.



  • Olá rafaelfborges,

    O procedimento é justamente para que a mensagem customizada seja exibida em qualquer acesso (http e https).
    Reveja todos os passos do procedimento que vai funcionar !

    Abs.

    WW



  • Quando rodo o script : /usr/local/etc/rc.d/nginx2.sh recebo um erro; Alguém poderia me explicar o que está acontecendo?

    Segue mensagem:

    /usr/local/etc/rc.d/nginx2.sh
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] still could not bind()




  • Boa noite. Podem me tirar uma dúvida?

    Ante de fazer como descrito no primeiro post, eu tenho que deixar o System>Advanced>Admin:

    web configurator em http ou https ?
    webgui redirector marcado ou desmarcado?

    Alguém pode me orientar?



  • @jcgeraldi:

    Boa noite. Podem me tirar uma dúvida?

    Ante de fazer como descrito no primeiro post, eu tenho que deixar o System>Advanced>Admin:

    web configurator em http ou https ?
    webgui redirector marcado ou desmarcado?

    Alguém pode me orientar?

    Fiz idêntico, mas https não aparece tela de bloqueio…



  • Boa noite meu caro,

    Para customização eu apenas edito o arquivo /usr/local/www/sgerror.php do pfsense, não entendo muito de php, mas consegui editar o arquivo conforme minha necessidade, pegando como base as próprias linhas que existiam lá logo após a linha 205, mas se quiser pode alterar o arquivo também por uma página html, mas aí perde aquelas customizações informando quem acessou.

    Fica a adica aí pra galera também quem quiser fazer desta forma alternativa que fiz, acredito que também que pode ser útil a vocês.

    Pra quem quiser um modelo, acrescentar e alterar o código abaixo no arquivo sgerror.php conforme sua necessidade (alterar ip, nome de imagens, etc), no meu caso foi logo apos a linha 205 e substituir da linha function get_error_page($er_code_id, $err_msg='') { até a linha return implode("\n", $str);

    Segue modelo, se alguém quiser testar numa máquina virtual de teste, não vão colocar direto no de produção hein…  ;D ;D:

    O código abaixo foi feito com base num acesso do squid autenticado no AD, por isso informa grupos, estações, usuários etc, mas isso vocês podem alterar também conforme sua necessidade, basta apagar as linhas. O IP 192.168.1.1 no código é ficticio, acho que não precisava falar isso, mas vai que né.. rsrs  :o :o

    
    function get_error_page($er_code_id, $err_msg='') {
            global $err_code;
            global $cl;
            global $g;
            global $config;
            $str = Array();
    
            header("HTTP/1.1 " . $err_code[$er_code_id]);
    
            $str[] = '';
            $str[] = '';
    
    	if ($config['installedpackages']['squidguarddefault']['config'][0]['deniedmessage']) {
    		$str[] = "
    
    ### {$config['installedpackages']['squidguarddefault']['config'][0]['deniedmessage']}: {$err_code[$er_code_id]}
    
    ";
    	} else {
    		$str[] = '
    
    <center> ![](http://192.168.1.1/imagens/logosuamepresa.png) </center>
    
    ';
    		$str[] = '   **ACESSO NÃO AUTORIZADO**  
    ';
    		$str[] = '   **O site solicitado não pode ser exibido.** 
    ';
    		$str[] = '
    
    * * *
    
    ';
    		$str[] = '
    
    ### Requisição não permitida no site'; $str[] = "  {$cl['u']}  
    
    ";
    
    	}
            if ($err_msg) $str[] = " **Informações:** $err_msg
    
    ";
            if ($cl['a'])        $str[] = " **IP Acesso:** {$cl['a']} 
     ";
            if ($cl['n'])        $str[] = " **Estacao:** {$cl['n']} 
    ";
            if ($cl['i'])        $str[] = "**  Usuario:    ** {$cl['i']} 
    ";
            if ($cl['s'])        $str[] = " **Grupo:** {$cl['s']} 
    ";
            if ($cl['t'])        $str[] = " **Target Bloqueada:** {$cl['t']} 
    ";
            if ($cl['u'])        $str[] = " **URL acessada:** {$cl['u']} 
    ";
    		$str[] = '
    
    O site foi negado. Verifique se a URL foi digitada corretamente
    
    ';
    		$str[] = '
    
    A tentativa foi gravada, caso não concorde, envie um email para seu administrador: [administrador@suaempresa.com.br](mailto:administrador@suaempresa.com.br)
    
    ';
    		$str[] = '
    
    * * *
    
    ';
    		$str[] = "
    
    ### Servidor de Proxy SuaEmpresa
    
    ";
            $str[] = "";
    
            return implode("\n", $str);
    
    

    Dica: Usem o winscp com notepad ++ para editar o arquivo.

    Lembre-se que logo ao final do código acima, abaixo da linha return implode("\n", $str); tem uma chave ( } ), mas essa eu não substituí, por isso não informei ela.
    Outro detalhe, NÃO FUNCIONA COM PÁGINAS HTTPS  :'(



  • @wanderton:

    Quando rodo o script : /usr/local/etc/rc.d/nginx2.sh recebo um erro; Alguém poderia me explicar o que está acontecendo?

    Segue mensagem:

    /usr/local/etc/rc.d/nginx2.sh
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] bind() to 0.0.0.0:81 failed (48: Address already in use)
    nginx: [emerg] still could not bind()

    Ao que tudo indica, já tem algum serviço rodando nessa porta. Mas, você pode ajustar a conf, para ouvir só na interface LAN.

    Quando é colocado somente a porta, ele escuta em todas as interfaces.

    Eu fiz um pequeno ajuste nisso, você encontra aqui:

    https://forum.pfsense.org/index.php?topic=118346.0



  • Ola

    Fiz todos os procedimentos conforme mencionado, inclusive troquei a porta do WebGUI para 9443…

    Os sites http estão redirecionando para a página de erro, mas não tem jeito de funcionar os sites https.

    Obs: uso o pfSense 2.3.3 integrado ao ad com script pf2ad.

    Obrigado desde já, abraços.