• Tip: SBBS Web behind Apache Reverse Proxy

    From Imzadi@1:103/705 to All on Wed Dec 9 10:55:44 2020
    Hi,

    My SBBS is running on a Raspberry Pi, but inside my private LAN.
    I don't want to open port 80 and 443 of the Pi directly to the Internet, as I already have an Apache web server running on another machine which is available via my IPv4 address.

    So I'm using the VirtualHost function of Apache to make my SBBS Web (v4) portal available via this webserver and I'm "tunneling" everything, including the Websocket for fTelnet, through it.

    Here is my configuration on the Apache side:

    ==== nail here [x] for new monitor ====
    <VirtualHost *:80>
    ServerName box.my.imzadi.de
    ServerAlias box.imzadi.de
    Redirect permanent / https://box.my.imzadi.de/
    </VirtualHost>

    <VirtualHost *:443>
    ServerAdmin webmaster@imzadi.de
    ServerName box.my.imzadi.de
    ServerAlias box.imzadi.de

    ErrorLog ${APACHE_LOG_DIR}/box-error.log
    CustomLog ${APACHE_LOG_DIR}/box-access.log combined

    SSLEngine on
    SSLCertificateFile /etc/acme.sh/box.my.imzadi.de/box.my.imzadi.de.cer
    SSLCertificateKeyFile /etc/acme.sh/box.my.imzadi.de/box.my.imzadi.de.key

    ProxyRequests Off
    ProxyPreserveHost On
    RequestHeader set X-Forwarded-Proto "http"

    ProxyPass "/ftelnet" "ws://192.168.14.85:1123/"
    ProxyPassReverse "/ftelnet" "ws://192.168.14.85:1123/"

    ProxyPass "/" "http://192.168.14.85:8080/"
    ProxyPassReverse "/" "http://192.168.14.85:8080/"
    </VirtualHost>
    ==== nail here [x] for new monitor ====

    Some information here:
    The first VirualHost is for port 80, and will tell the browser to switch to HTTPS - so the web connection is HTTPS only.
    I'm using Let's Encrypt with acme.sh, but on my Apache server, so SBBS does not need any SSL certificates - and I'm guessing that using HTTP inside my local LAN is not insecure :)
    I don't know it the "X-Forwarded-Proto" "http" is needed, I left it there, because in another proxy setting it worked.
    The first ProxyPass lines will create a virtual subdirectory "/ftelnet" which will point to the WS service of SBBS on port 1123.
    And the second ProxyPass lines will forward the rest ("/") to the SBBS HTTP port, which I've set to port 8080.

    But the fTelnet side in SBBS Web v4 does need some changes, as it assumes that the WS port is on the same machine.
    I did the following changes:

    In "000-home.xjs", the Options part for fTelnet needs the option "Options.WebSocketUrlPath" to point to the "virtual subdirectory" from the configuration above. Also the "Options.Hostname" needs to point to the external hostname of my Apache. And the "Options.Port" has to point to Apache's HTTPS port, 443.
    The complete part looks like this now:

    ---- pages/000-home.xjs
    <script id="fTelnetScript" src="<?xjs write(get_url()); ?>"></script>
    <script>
    var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>;
    var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>;
    var Options = new fTelnetOptions();
    Options.BareLFtoCRLF = false;
    Options.BitsPerSecond = 57600;
    Options.ButtonBarVisible = true;
    Options.ConnectionType = 'telnet';
    Options.Emulation = 'ansi-bbs';
    Options.Enter = '\r';
    Options.Font = 'CP437';
    Options.ForceWss = false;
    Options.Hostname = 'box.my.imzadi.de';
    Options.WebSocketUrlPath = '/ftelnet';
    Options.LocalEcho = false;
    Options.Port = 443;
    Options.ScreenColumns = 80;
    Options.ScreenRows = 25;
    Options.SplashScreen = '<?xjs write(get_splash()); ?>';
    var fTelnet = new fTelnetClient('fTelnetContainer', Options);
    fTelnet.ButtonBarVisible = true;
    if ($('#ftelnet-connect').length) {
    $('#ftelnet-connect').click(function() {
    fTelnet.Connect();
    });
    }
    </script>
    ---- pages/000-home.xjs

    Similar changes have to apply to 003-games.xjs:
    Here, the "WebSockerUrlPath" has the option for connecting to the RLogin port added - this also does work :)

    ---- pages/003-games.xjs
    <script type="text/javascript">
    var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>;
    var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>;
    var Options = new fTelnetOptions();
    Options.BareLFtoCRLF = false;
    Options.BitsPerSecond = 57600;
    Options.ConnectionType = 'rlogin';
    Options.Emulation = 'ansi-bbs';
    Options.Enter = '\r';
    Options.Font = 'CP437';
    Options.ForceWss = false;
    Options.Hostname = 'box.my.imzadi.de';
    Options.WebSocketUrlPath = '/ftelnet?Port=<?xjs write(GetRLoginPort()); ?>';
    Options.LocalEcho = false;
    Options.Port = 443;
    Options.RLoginClientUsername = '<?xjs write(user.security.password); ?>';
    Options.RLoginServerUsername = '<?xjs write(user.alias); ?>';
    Options.ScreenColumns = 80;
    Options.ScreenRows = 25;
    Options.SplashScreen = Options.SplashScreen = '<?xjs write(get_splash()); ?>';
    var fTelnet = new fTelnetClient('fTelnetContainer', Options);
    fTelnet.OnConnectionClose = function () {
    window.location.reload();
    };
    ---- pages/003-games.xjs

    Also, the "auth.js" function needed a change, as it creates a cookie that did not work for me -- I cut the corner here by just setting it to my external domain in this function:

    ---- webv4/lib/auth.js ----
    function setCookie(usr, sessionKey) {
    if (usr instanceof User && usr.number > 0) {
    set_cookie(
    'synchronet',
    usr.number + ',' + sessionKey,
    (time() + settings.timeout),
    'box.my.imzadi.de', // <==== this line
    '/'
    );
    setSessionValue(usr.number, 'key', sessionKey);
    }
    }
    ---- webv4/lib/auth.js ----


    Maybe some of these "hacks" can be made configurable via a .INI file in the future?
    Or did I miss something? :)

    I hope this is useful for someone else.

    Regards,
    Anna

    ---
    ï¿­ Synchronet ï¿­ Imzadi Box
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From echicken@1:103/705 to Imzadi on Wed Dec 9 13:37:12 2020
    Re: Tip: SBBS Web behind Apache Reverse Proxy
    By: Imzadi to All on Wed Dec 09 2020 10:55:44

    Maybe some of these "hacks" can be made configurable via a .INI file in

    They should be made configurable. Hacks on the stock JS and XJS files are strongly discouraged as they make updating difficult and support harder to provide.

    Also, the "auth.js" function needed a change, as it creates a cookie that did not work for me -- I cut the corner here by just setting it to my external domain in this function:

    'box.my.imzadi.de', // <==== this line

    Maybe just use http_request.host instead of a hard-coded hostname value. This is whatever hostname came in with the HTTP request. Alternately, you could use system.host_name, or add a configurable value to modopts.ini->[web].

    Options.Hostname = 'box.my.imzadi.de';
    Options.WebSocketUrlPath = '/ftelnet';

    Add two entries to ctrl/modopts.ini -> [web]:

    ftelnet_host = box.my.imzadi.de
    ftelnet_path = /ftelnet

    and then in the XJS files, do this instead:

    Options.Hostname = <? write(settings.ftelnet_host || system.host_name); ?> Options.WebSocketURLPath = <? write(settings.ftelnet_path || '/'); ?>

    If you want to test that and let me know if it works, I can put these changes in place later.

    ---
    echicken
    electronic chicken bbs - bbs.electronicchicken.com
    þ Synchronet þ electronic chicken bbs - bbs.electronicchicken.com
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Imzadi@1:103/705 to echicken on Thu Dec 10 15:21:43 2020
    Hi echicken,

    They should be made configurable. Hacks on the stock JS and XJS files are strongly discouraged as they make updating difficult and support harder to provide.

    Yep, I was aware of that - so it's great that we can make it 'the right way' now :)

    Maybe just use http_request.host instead of a hard-coded hostname value. This is whatever hostname came in with the HTTP request. Alternately, you could use system.host_name, or add a configurable value to modopts.ini->[web].

    I'm using http_request.host now, see below.

    Add two entries to ctrl/modopts.ini -> [web]:
    ftelnet_host = box.my.imzadi.de
    ftelnet_path = /ftelnet

    Check - done :)
    I also added:
    ftelnet_port = 443

    If you want to test that and let me know if it works, I can put these changes in place later.

    I made it this way now:

    Options.Hostname = '<?xjs write(settings.ftelnet_host || system.host_name); ?>';
    Options.WebSocketUrlPath = '<?xjs write(settings.ftelnet_path || '/'); ?>';
    Options.Port = <?xjs write(settings.ftelnet_port); ?>;

    The "Port" option is not nice at the moment, there should be some differentiation between the default WS/WSS and the configurable 'ftelnet_port'...

    In the file 003-games.xjs, I did it this way:

    Options.Hostname = '<?xjs write(settings.ftelnet_host || system.host_name); ?>';
    Options.WebSocketUrlPath = '<?xjs write(settings.ftelnet_path || '/'); write('?Port=' + GetRLoginPort()); ?>';
    Options.Port = <?xjs write(settings.ftelnet_port); ?>;

    Thank you for your help and support!

    Regards,
    Anna

    ---
    ï¿­ Synchronet ï¿­ Imzadi Box
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From echicken@1:103/705 to Imzadi on Thu Dec 10 16:08:57 2020
    Re: Tip: SBBS Web behind Apache Reverse Proxy
    By: Imzadi to echicken on Thu Dec 10 2020 15:21:43

    Check - done :)

    Great, I'll see about adding ftelnet_host and ftelnet_path in. Thank you.

    I also added:
    ftelnet_port = 443

    There are already two settings available for this: 'wsp' and 'wssp' (insecure and secure respectively).

    The "Port" option is not nice at the moment, there should be some differentiation between the default WS/WSS and the configurable 'ftelnet_port'...

    The current Options.Port line looks like this:

    Options.Port = location.protocol == 'https:' ? wssp : wsp;

    The values of wsp and wssp are initialized thusly:

    var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>;
    var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>;

    GetWebSocketServicePort() finds the Port value in the [WS] or [WSS] sections of ctrl/services.ini. If you haven't specified wsp or wssp and you also don't have those sections in services.ini, you'll get an error (and that's okay).

    So I don't think your ftelnet_port value is needed. Provision has already been made for the sysop to override that setting as needed. Please let me know if I'm misunderstanding something / if you tried this already and it didn't work.

    ---
    echicken
    electronic chicken bbs - bbs.electronicchicken.com
    þ Synchronet þ electronic chicken bbs - bbs.electronicchicken.com
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)