ex0ch icon indicating copy to clipboard operation
ex0ch copied to clipboard

Just a small suggestion for the PHP version running in Docker

Open paigeadelethompson opened this issue 1 year ago • 2 comments

I cannot attest as to whether this is a better option however PHP has a built-in webserver:

php -S 0.0.0.0:80 index.php

it's sorta like how NodeJS has Express or whatever it's called but the -S flag instructs PHP to start a web server of it's own and service requests sort of the same way mod_php does with Apache but it's just PHP which in my experience is pretty convenient especially when you're trying to package something as a Docker container because it just minimizes the complexity and requirements a little (don't need nginx just to simply use it.)

As a consequence of being a minimalist webserver it doesn't inherently support TLS termination; it would be kind of nice if it did and you could just simply specify additional ca/crt/key arguments to the command because even if you are running this behind Cloudflare it's still nice to have everything encrypted end-to-end and Cloudflare and it's a little more than just a suggestion to use TLS between CF and your backend servers.

Still you could totally do this and just let TLS termination be somebody else's problem. I have a composer based PHP project that I own and for it I use composer serve but you can also use php -S with it, too:

https://github.com/paigeadelethompson/pscr_demo/blob/master/Dockerfile

*** EDIT: if you want to use the composer serve method like I do you need to also add something like this to your composer.json file: https://github.com/paigeadelethompson/pscr_demo/blob/master/composer.json#L14

To be honest though I'm a little more interested in the perl/cgi setup myself, it amazes me to see something this old still around but I thought I might share this since after looking over your Dockerfiles it wasn't real clear to me how they're supposed to work because there's no default CMD or ENTRYPOINT in any of them (there never is really I think Docker is a bit of a steep learning curve for most people.)

*** EDIT: Also I don't know if you could really call this a feature request as much as it is just supporting TLS so that it can actually interface with the real world on its own, but I went ahead and made a request to PHP: https://github.com/php/php-src/issues/17033 but YMMV, this may just give them a reason to get rid of -S rather than improve it so what will become of it remains to be seen

paigeadelethompson avatar Dec 03 '24 16:12 paigeadelethompson

There actually is a threaded, perl HTTP server: https://metacpan.org/pod/HTTP::Daemon::Threaded and it also supports CGI handling which might be cool, you could very easily modify that to take optional ca/crt/key arguments which would make setting it up to be encrypted end-to-end behind cloudflare a piece of cake. Also just FYI you should create a user / group in those containers, and specify the name of the user for the container with the USER instruction in the Dockerfile otherwise they just default to root which is still problematic even though it's a container.

*** EDIT: HTTP-Daemon might be a bit of a problem if you want HTTPS: https://github.com/libwww-perl/HTTP-Daemon/issues/7 but there's interest in the topic (as there should be)

It looks like this would be your next best option: https://metacpan.org/pod/HTTP::Server::Simple

This is a simple standalone HTTP server. By default, it doesn't thread or fork. It does, however, act as a simple frontend which can be used to build a standalone web-based application or turn a CGI into one.

It is possible to use Net::Server classes to create forking, pre-forking, and other types of more complicated servers; see "net_server".

So there are some options to explore there to make this serve concurrent requests HTTPS requests, there's also this:

https://metacpan.org/pod/HTTP::Server::Simple::CGI::PreFork

HTTP::Server::Simple::CGI::PreFork - Turn HSS into a preforking webserver and enable SSL

which sounds exactly like the right thing

paigeadelethompson avatar Dec 03 '24 16:12 paigeadelethompson

I managed to put together a better Dockerfile for the Perl version of this

FROM ubuntu:noble

RUN apt -y update

RUN apt -y install apache2 perl git cpanminus

WORKDIR /tmp

RUN git clone https://github.com/PrefKarafuto/ex0ch.git

WORKDIR /tmp/ex0ch/test

RUN cp -rvp . /var/www/html

WORKDIR / 

RUN a2enmod cgid ssl

RUN cpanm -n CGI::Cookie JSON LWP::UserAgent

ADD apache.conf /etc/apache2/apache2.conf

RUN ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/default-ssl.conf

RUN echo "Options +ExecCGI +FollowSymLinks" > /var/www/html/.htaccess

RUN echo "AddHandler cgi-script .cgi" >> /var/www/html/.htaccess

RUN echo "DirectoryIndex search.cgi" >> /var/www/html/.htaccess

RUN echo "RedirectMatch 301 ^/info/ /" >> /var/www/html/.htaccess

RUN echo "RedirectMatch 301 ^/admin/ /" >> /var/www/html/.htaccess

RUN echo "RedirectMatch 301 ^/module/ /" >> /var/www/html/.htaccess

RUN echo "RedirectMatch 301 ^/plugin/ /" >> /var/www/html/.htaccess

RUN echo "RedirectMatch 301 ^/perllib/ /" >> /var/www/html/.htaccess

RUN echo "RedirectMatch 301 ^/plugin_conf/ /" >> /var/www/html/.htaccess

RUN chown -R www-data:www-data /var/www/html /usr/lib/cgi-bin

RUN mkdir -p /var/run/apache2

RUN chown -R www-data:www-data /var/run/apache2

RUN chown -R www-data:www-data /var/log/apache2

EXPOSE 80

EXPOSE 443

VOLUME /var/www/html/info

VOLUME /var/log/apache2

CMD apachectl -D FOREGROUND

this requires another file apache.conf to be present in the same directory:

PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
Include ports.conf
<Directory />
	Options FollowSymLinks
	AllowOverride None
	Require all denied
</Directory>
<Directory /usr/share>
	AllowOverride None
	Require all granted
</Directory>
<Directory /var/www/>
	Options Indexes FollowSymLinks
	AllowOverride All
	Require all granted
</Directory>
AccessFileName .htaccess
<FilesMatch "^\.ht">
	Require all denied
</FilesMatch>
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
IncludeOptional conf-enabled/*.conf
IncludeOptional sites-enabled/*.conf

then you can build with docker build -t ex0ch -t ex0ch:latest . and you can run this as default with root since it wants root and uses setuid: docker run -it --rm --net host -v ex0ch_data:/var/www/html/info ex0ch

I sorta put together a Perl-based HTTP server script as well, but POST does not work for it yet:

#!/usr/bin/env perl

use Env;
use HTTP::Server::Simple::CGI::PreFork;
{
    package WebServer;
    use base 'HTTP::Server::Simple::CGI::PreFork';
    use File::Slurp;

    sub handle_request {
        $ENV{SCRIPT_FILENAME} = $0;
        my ($self, $cgi) = @_;
        my $pid = fork();
        if($pid == 0) {
            my $path = $cgi->path_info();
            if($path eq "/admin" || $path eq "/admin.cgi") {
                print "HTTP/1.1 200 OK\r\n";
                require "./cgi-bin/admin.cgi";
            } elsif($path eq "/bbs" || $path eq "/bbs.cgi" || $path eq "/") {
                print "HTTP/1.1 200 OK\r\n";
                require "./cgi-bin/bbs.cgi";
            } elsif($path eq "/read" || $path eq "/read.cgi") {
                print "HTTP/1.1 200 OK\r\n";
                require "./cgi-bin/read.cgi";
            } elsif($path eq "/search" || $path eq "/search.cgi") {
                print "HTTP/1.1 200 OK\r\n";
                require "./cgi-bin/search.cgi";
            } elsif($path eq "/remake" || $path eq "/remake.cgi") {
                print "HTTP/1.1 200 OK\r\n";
                require "./cgi-bin/remake.cgi";
            } elsif($path eq "/madakana" || $path eq "/madakana.cgi") {
                print "HTTP/1.1 200 OK\r\n";
                require "./cgi-bin/madakana.cgi";
            } elsif($path eq "/datas/admin.css") {
                print "HTTP/1.1 200 OK\r\n";
                print "Content-Type: text/css;\r\n";
                print "\r\n";
                my $content = read_file('./cgi-bin/datas/admin.css');
                print $content;
            } elsif($path eq "/datas/search.css") {
                print "HTTP/1.1 200 OK\r\n";
                print "Content-Type: text/css;\r\n";
                print "\r\n";
                my $content = read_file('./cgi-bin/datas/search.css');
                print $content;
            } elsif($path eq "/datas/design.css") {
                print "HTTP/1.1 200 OK\r\n";
                print "Content-Type: text/css;\r\n";
                print "\r\n";
                my $content = read_file('./cgi-bin/datas/design.css');
                print $content;
            } elsif($path eq "/datas/script.js") {
                print "HTTP/1.1 200 OK\r\n";
                print "Content-Type: application/javascript;\r\n";
                print "\r\n";
                my $content = read_file('./cgi-bin/datas/script.js');
                print $content;
            } elsif($path eq "/datas/default_bac.gif") {
                print "HTTP/1.1 200 OK\r\n";
                print "Content-Type: image/gif;\r\n";
                print "\r\n";
                my $content = read_file('./cgi-bin/datas/default_bac.gif');
                print $content;
            } else {
                print "HTTP/1.1 204 NO CONTENT\r\n";
            }
        }
    }
}

my $ca = $ENV{CA_CERT};
my $cert = $ENV{CERT};
my $key = $ENV{KEY};
my $port = $ENV{PORT};
my $prefork = $ENV{WORKERS};

if($port eq "") {
    if(!$ca eq "" && !$cert eq "" && !key eq "") {
        $port = 8443;
    } else {
        $port = 8080;
    }
}

if($prefork eq "") {
    $prefork = 32;
}

my $server = WebServer->new($port);

if(!$ca eq "" && !$cert eq "" && !key eq "") {
    $server->run(prefork => $prefork, usessl => 1, proto => 'ssleay', "--SSL_key_file"=> $key, "--SSL_cert_file"=> $cert);
} else {
    $server->run();
}

paigeadelethompson avatar Dec 04 '24 22:12 paigeadelethompson