A full fledged web server (Apache?) on pfSense?



  • Hi everybody.
    I'm relatively new to pfSense, so please forgive any triviality I my say.
    What I'm trying to set up is a Captive Portal. So far I've managed to reach a functioning configuration by following deajan's excellent tutorial (https://forum.pfsense.org/index.php?topic=108493.0). What I'd need now is to add some sort of additional web page for configuring CaptivePortal custom/specific parameters (actually, I need to manipulate FreeRADIUS' mysql tables directly). Again, so far I managed to write a PHP page, which I placed in folder /usr/local/www and is doing what I need.
    My problem now, is with page reload. Let me explain, step by step.

    1. I navigate with my browser to my "custom" interface, i.e. https://pfsense.local/userconfig.php
    2. The page opens up, so that I can fill the different fields with the values I need.
    3. I hit the "submit" button.
    4. My form gets submitted and the FreeRADIUS' (mysql) database is updated accordingly.

    So far, so good. The problem comes now.

    1. I hit the "reload" button of my browser, and the form is submitted a second time!

    I'm trying to avoid/prevent this by using the PRG (Post/Redirect/Get) Pattern. As an example, I'm using code like this:

    
        session_start();
    
        $message = "";
    
        if(count($_POST) > 0) {
            $_SESSION['_post'] = serialize($_POST);
    
            header("HTTP/1.1 303 See Other");
            header("Location: https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
            die();
        }
        else if (isset($_SESSION['_post'])){
            $_myPost = unserialize($_SESSION['_post']);
    
            $message = var_export($_myPost, true);
    
            /*
                Put database-affecting code here (based on contents of $_myPost).
            */
    
            session_unset();
            session_destroy();
        }
    ?>
    
    <title>PRG Pattern Demonstration</title>
    
        $message" ?>
    
    

    which is working fine on my local development server (PHP 5.6, Apache 2.4, MySQL 5.6) but is failing miserably on pfSense (latest version). If I leave the die() in place the scripts fails silently with no hint at all (it dies?). If I remove the die() instruction, redirection doesn't work at all. Apparently, the die() instructions simply kills the script before any output/header is sent to the browser, whereas, by not using the die() instruction, the header is sent but the script doesn't stop and redirection doesn't take place.

    My question, then: is there a way to adjust the above quoted code? Am I missing something? Or, alternatively, is it possible to install Apache as a web-server in place of the (unknown? mysterious?) version pfSense is using by default (and is breaking the 303 redirect mechanism)?

    Thanks everybody for the help.



  • pfSense 2.3.x uses nginx.

    I'm not a web dev but it seems to me that this is standard behaviour.  If you submit a form and then refresh, the browser will warn you if you want to submit again.



  • Of course this is standard behavior, but browser's behavior. That's precisely the reason why PRG patterns have been invented (http://wordsideasandthings.blogspot.it/2013/04/post-redirect-get-pattern-in-php.html) and that's why my set-up requires additional PHP code.
    What is NON-standard is this weird NGINX behavior, or at least the behavior of the NGINX version used by pfSense.
    It's definitely a problem of the web server not outputting the headers (which is clearly against HTTP 1.1 specifications about 303 return code). I've done additional test. If I leave the die() instruction in place, the PHP script simply (and silently) stops, leaving me with a totally blank page in the browser.
    (code like this:

    
    if(count($_POST) > 0) {
            $_SESSION['_post'] = serialize($_POST);
    
            header("HTTP/1.1 303 See Other");
            header("Location: https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
            die(); // <<--------------------- ONLY die() script ends silently, no output at all.
        }
        else if (isset($_SESSION['_post'])){
            $_myPost = unserialize($_SESSION['_post']);
    
            $message = var_export($_myPost, true);
    
            /*
                Put database-affecting code here (based on contents of $_myPost).
            */
    
            session_unset();
            session_destroy();
        }
    
    ```)
    If I omit the _die()_ instruction, or if I add a _flush()_ before the _die()_, the script doesn't stop, the html is output but absolutely _no redirect_ takes place, again, which is against HTTP 1.1 specifications! It looks like if headers with response code of type 303 are simply not output at all!
    (code like this
    

    if(count($_POST) > 0) {
            $_SESSION['_post'] = serialize($_POST);

    header("HTTP/1.1 303 See Other");
            header("Location: https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
            flush(); // <<---+--------- flush() + die() the script runs to the end but NO REDIRECTION AT ALL takes place.
            die();  // <<----|
        }
        else if (isset($_SESSION['_post'])){
            $_myPost = unserialize($_SESSION['_post']);

    $message = var_export($_myPost, true);

    /*
                Put database-affecting code here (based on contents of $_myPost).
            */

    session_unset();
            session_destroy();
        }

    )
    I repeat, this type of code works fantastically in my local web dev environment (apache 2.4, PHP 5.6, MySQL 5.6). The only difference is the web server, that's why I asked if it's possible to replace the webserver. I think I'm going to file a bug report against NGINX/pfSense.
    In any case, thanks for the reply.

  • Rebel Alliance Developer Netgate

    The web server on pfSense is not intended to be used for anything but pfSense, not for hosting custom pages/code.

    If you absolutely must host a web server on the firewall, you'll have to setup a separate instance manually with whatever custom config you want. Dig around the forum and you'll find some examples where people have done just that.