Just a small suggestion for the PHP version running in Docker
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
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
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();
}