initial commit of file from CVS for smeserver-phpki-ng on Sat Sep 7 20:50:40 AEST 2024

This commit is contained in:
Trevor Batley
2024-09-07 20:50:40 +10:00
parent 216095c0ea
commit c46ac6300b
35 changed files with 1065 additions and 2 deletions

View File

@@ -0,0 +1 @@
system

View File

@@ -0,0 +1 @@
940

View File

@@ -0,0 +1 @@
enabled

View File

@@ -0,0 +1 @@
service

View File

@@ -0,0 +1,9 @@
#!/bin/bash
chown root:phpki /opt/phpki/html/config.php
chown root:phpki /opt/phpki/html/openssl.cnf
chown phpki:phpki -R /opt/phpki/phpki-store*
chown root:phpki /opt/phpki/html/ca
chmod +x /opt/phpki/html/
chmod +x /opt/phpki/html/ca

View File

@@ -0,0 +1,8 @@
#!/bin/bash
if [[ -f /opt/phpki/phpki-store/CA/private/cakey.pem && ! -f /opt/phpki/phpki-store/CA/private/takey.pem ]]
then
echo "creating missing takey.pem"
runuser -u phpki -- openvpn --genkey --secret /opt/phpki/phpki-store/CA/private/takey.pem
fi

View File

@@ -0,0 +1,8 @@
<lexicon lang="fr" params="lexicon_params()">
<!-- vim: ft=xml
-->
<entry>
<base>Certificate Management</base>
<trans>Gestion des certificats</trans>
</entry>
</lexicon>

View File

@@ -0,0 +1,3 @@
FILTER=sub { $_[0] =~ /^\s*$/ ? '' : $_[0] }
GID='phpki'
PERMS=0660

View File

@@ -0,0 +1,69 @@
{
# vim: ft=perl:
$haveSSL = (exists ${modSSL}{status} and ${modSSL}{status} eq "enabled") ? 'yes' : 'no';
$OUT = '';
if ((${'httpd-pki'}{'status'} || 'disabled') eq 'enabled'){
if (($port eq "80") && ($haveSSL eq 'yes')){
$OUT .= " RewriteRule ^/phpki(/.*|\$) https://%{HTTP_HOST}/phpki\$1 [L,R]\n";
}
else{
$OUT .= " ProxyPass /phpki http://127.0.0.1:${'httpd-pki'}{TCPPort}/phpki\n";
$OUT .= " ProxyPassReverse /phpki http://127.0.0.1:${'httpd-pki'}{TCPPort}/phpki\n";
}
$OUT .=<<"HERE";
#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
#LoadModule proxy_connect_module modules/mod_proxy_connect.so
#LoadModule proxy_express_module modules/mod_proxy_express.so
#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
<Location /phpki>
SSLRequireSSL on
Require ip $localAccess $externalSSLAccess
</Location>
# we want Public access to ns_revoke_query.php
<Location /phpki/ns_revoke_query.php>
Require all granted
</Location>
# we want Public access to policy
<Location /phpki/policy.html>
Require all granted
</Location>
# we want Public access to help
<Location /phpki/help>
Require all granted
</Location>
<Location /phpki/help.php>
Require all granted
</Location>
# we want Public access to crl list
<Location /phpki/dl_crl.php>
Require all granted
</Location>
<Location /phpki/dl_crl_pem.php>
Require all granted
</Location>
# and we redirect old config to our new safer script
RewriteEngine On
RewriteCond %{QUERY_STRING} stage=dl_crl(&|\$)
RewriteRule ^ /phpki/dl_crl.php [QSD,R=302,L]
RewriteCond %{QUERY_STRING} stage=dl_crl_pem(&|\$)
RewriteRule ^ /phpki/dl_crl_pem.php [QSD,R=302,L]
HERE
# safely redirect crl request to php script striping all GET requests
# but would leave POST
#RewriteEngine On
#RewriteCond %{REQUEST_URI} ^/?phpki/dl_crl/?\$
#RewriteRule ^ /phpki/index.php?stage=dl_crl [P,NC]
}
}

View File

@@ -0,0 +1,28 @@
{
use esmith::AccountsDB;
sub getUsersList ($){
my ($panelName) = @_;
my $a = esmith::AccountsDB->open_ro || die "Error opening accounts db";
my @users = $a->users();
my @groups = $a->groups();
my @Users = ();
foreach my $user (@users){
my $panels = $user->prop('AdminPanels') || '';
push(@Users,$user->key) if ($panels =~ /^(.*,)?$panelName(,.*)?$/);
}
foreach my $group (@groups){
$panels = $group->prop('AdminPanels') || '';
if ($panels =~ /^(.*,)?$panelName(,.*)?$/){
my @members = split(/,/,($group->prop('Members') || ''));
push(@Users,@members);
}
}
my %seen = ();
my $u = join (' ', grep { ! $seen{ $_ }++ } @Users);
return $u;
}
}

View File

@@ -0,0 +1,23 @@
{
#---------------------------------------------------------------------
# Grab ValidFrom access list property of httpd-admin
# SSL enabled virtual hosts should only allow access from IP's in
# this list, as well as local networks.
#---------------------------------------------------------------------
use esmith::NetworksDB;
my $ndb = esmith::NetworksDB->open_ro();
my @localAccess = $ndb->local_access_spec();
my $validFrom = ${'httpd-admin'}{'ValidFrom'};
if ($validFrom)
{
push @localAccess, split /,/, $validFrom;
}
$localAccess .= join ' ',
map { s:/255.255.255.255::; $_ }
@localAccess;
"";
}

View File

@@ -0,0 +1,8 @@
{
$OUT .= "LoadModule auth_tkt_module modules/mod_auth_tkt.so\n";
my $secret = ${'httpd-admin'}{TKTAuthSecret} || "34322500-7330-4400-423A-3A00434F5245";
$OUT .= "TKTAuthSecret \"$secret\"\n";
$OUT .= "TKTAuthDigestType SHA256\n";
}

View File

@@ -0,0 +1,162 @@
{
my $port = ${'httpd-pki'}{TCPPort} || '940';
$OUT .= "Listen 127.0.0.1:$port\n";
$OUT .= <<HERE;
HostnameLookups off
ServerAdmin admin@$DomainName
ServerRoot /etc/httpd
ServerTokens ProductOnly
User phpki
Group phpki
ErrorLog /var/log/httpd/pki_error_log
LogLevel warn
HERE
foreach (qw(
env
log_config
mime
negotiation
status
info
include
autoindex
dir
asis
imagemap
actions
userdir
proxy
proxy_http
alias
rewrite
auth
auth_anon
auth_digest
expires
headers
usertrack
setenvif
ssl
cgi
mpm_prefork
unixd
authn_core
authz_core
authz_user
authz_host
proxy_ajp
proxy_connect
proxy_express
proxy_fcgi
proxy_ftp
proxy_html
proxy_scgi
proxy_wstunnel
))
{
next unless -f "/usr/lib/httpd/modules/mod_${_}.so" ||
-f "/usr/lib64/httpd/modules/mod_${_}.so";
$OUT .= "LoadModule ${_}_module modules/mod_${_}.so\n";
}
$OUT .= "# we do not use php module anymore, but php-fpm";
$OUT .=<<"HERE";
PidFile /var/run/httpd-pki.pid
ScoreBoardFile /var/run/httpd-pki.scoreboard
UseCanonicalName off
LogFormat "%h %l %u %t \\"%r\\" %>s %b" common
LogFormat "%{User-agent}i" agent
CustomLog /var/log/httpd/pki_access_log common
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 15
MaxClients 150
MaxRequestsPerChild 100
ServerName www.$DomainName
MinSpareServers 1
MaxSpareServers 5
StartServers 1
Timeout 300
DefaultIcon /icons/unknown.gif
DirectoryIndex index.htm index.html index.php index.cgi
IndexOptions FancyIndexing VersionSort NameWidth=*
IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
AccessFileName .htaccess
AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
AddIconByType (TXT,/icons/text.gif) text/*
AddIconByType (IMG,/icons/image2.gif) image/*
AddIconByType (SND,/icons/sound2.gif) audio/*
AddIconByType (VID,/icons/movie.gif) video/*
TypesConfig /etc/mime.types
AddEncoding x-compress Z
AddEncoding x-gzip gz
AddIcon /icons/binary.gif .bin .exe
AddIcon /icons/binhex.gif .hqx
AddIcon /icons/tar.gif .tar
AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
AddIcon /icons/a.gif .ps .ai .eps
AddIcon /icons/layout.gif .html .shtml .htm .pdf
AddIcon /icons/text.gif .txt
AddIcon /icons/c.gif .c
AddIcon /icons/p.gif .pl .py
AddIcon /icons/f.gif .for
AddIcon /icons/dvi.gif .dvi
AddIcon /icons/uuencoded.gif .uu
AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
AddIcon /icons/tex.gif .tex
AddIcon /icons/bomb.gif core
AddIcon /icons/back.gif ..
AddIcon /icons/hand.right.gif README
AddIcon /icons/folder.gif ^^DIRECTORY^^
AddIcon /icons/blank.gif ^^BLANKICON^^
AddLanguage en .en
AddLanguage fr .fr
AddLanguage de .de
AddLanguage da .da
AddLanguage el .el
AddLanguage it .it
LanguagePriority en fr de
AddType text/html .shtml
AddType application/x-pkcs7-crl .crl
AddType application/x-x509-ca-cert .crt
BrowserMatch "Mozilla/2" nokeepalive
BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
BrowserMatch "RealPlayer 4\.0" force-response-1.0
BrowserMatch "Java/1\.0" force-response-1.0
BrowserMatch "JDK/1\.0" force-response-1.0
AddHandler cgi-script .cgi
AddHandler server-parsed .shtml
AddHandler imap-file map
DocumentRoot /opt/phpki/html
HERE
}

View File

@@ -0,0 +1,11 @@
# First, we configure the "default" to be a very restrictive set of
# permissions.
<Directory />
Options None
AllowOverride None
Require all denied
</Directory>

View File

@@ -0,0 +1,52 @@
Alias /phpki /opt/phpki/html/
# Main access allowed for valid user
<Directory /opt/phpki/html>
AddType application/x-httpd-php .php
Options FollowSymLinks
{
my $key = "phpki";
my $pool_name = lc $key;
my $version = ${httpd-pki}{'PHPVersion'} || '73';
$OUT .="
<FilesMatch .php\$>
SetHandler \"proxy:unix:/var/run/php-fpm/php${version}-${pool_name}.sock|fcgi://localhost\"
</FilesMatch>\n";
}
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
SetEnvIfNoCase Cookie ".*auth_tkt=(.*);?" HTTP_AUTH_TKT=$1
AddType application/x-x509-ca-cert .crt .pem
AddType application/pkix-crl .crl
AddType application/pkix-cert .cer .der
AllowOverride None
Require ip 127.0.0.1
</Directory>
# /ca is only allowed for admin and explicitely authorized users
<Location /phpki/ca>
AuthName "PHPKI Admin"
AuthType Basic
TKTAuthLoginURL /server-common/cgi-bin/login
<RequireAll>
Require user admin {getUsersList("phpki");}
Require ip 127.0.0.1
</RequireAll>
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
SetEnvIfNoCase Cookie ".*auth_tkt=(.*);?" HTTP_AUTH_TKT=$1
{
my $ManagerTimeout = ${'httpd-admin'}{ManagerTimeout} || "30m";
$OUT = " TKTAuthTimeout $ManagerTimeout\n";
my $Cookie = ${'httpd-admin'}{Cookie} || "disabled";
$OUT .= " TKTAuthCookieExpires $ManagerTimeout\n" if "$Cookie" eq "enabled";
my $ManagerTimeoutReset = ${'httpd-admin'}{ManagerTimeoutReset} || "0.66";
$OUT .= " TKTAuthTimeoutRefresh $ManagerTimeoutReset\n";
}
</Location>
# Disable access to /admin, which is used to configure user/password
# via an htaccess file
<Directory /opt/phpki/html/admin>
Require all denied
</Directory>

View File

@@ -0,0 +1,69 @@
{
use esmith::ConfigDB;
my $c = esmith::ConfigDB->open_ro || die "Couldn't open the configuration database\n";
my $httpdpki = $c->get( 'httpd-pki' );
my $version = $httpdpki->prop('PHPVersion') || '73';
# we enable both the httpd server and php pool with same status
my $status = $httpdpki->prop('status') || 'disabled';
return unless ($status eq 'enabled' && $version eq $PHP_VERSION);
my $key = 'phpki';
my $pool_name = lc $key;
my $include_path = ".:/usr/share/pear-addons:/usr/share/pear:/usr/share/pear-data:/usr/share/php:/usr/sbin/:/usr/bin:/opt/phpki/html:/opt/phpki/html/include";
my $open_basedir = "/opt/phpki:/var/lib/php/phpki:/usr/sbin/openvpn:/usr/bin/which:/usr/bin/cat:/usr/bin/egrep:$include_path";
my $disabled_functions = 'show_source,dl,passthru'
;
# Format vars
$disabled_functions = join(', ', split /[,;:]/, $disabled_functions);
$open_basedir = join(':', split(/[,;:]/, $open_basedir . ",/usr/share/php"));
$OUT .=<<"_EOF" if ($version eq $PHP_VERSION);
[$pool_name]
user = phpki
group = phpki
listen.owner = root
listen.group = phpki
listen.mode = 0660
listen = /var/run/php-fpm/php$version-$pool_name.sock
catch_workers_output = yes
pm = dynamic
pm.max_children = 15
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 4
pm.max_requests = 1000
slowlog = /var/log/$key/slow.log
php_admin_value[session.save_path] = /var/lib/php/$key/session
php_admin_value[opcache.file_cache] = /var/lib/php/$key/opcache
php_admin_value[upload_tmp_dir] = /var/lib/php/$key/tmp
php_admin_value[sys_temp_dir] = /var/lib/php/$key/tmp
php_admin_flag[display_errors] = off
php_admin_value[error_reporting] =E_ERROR | E_WARNING | E_PARSE
php_admin_value[error_log] = /var/log/$key/error.log
php_admin_flag[log_errors] = on
; php_admin_value[max_execution_time] = $max_execution_time
php_admin_value[disable_functions] = $disabled_functions
php_admin_flag[allow_url_fopen] = off
php_admin_flag[file_upload] = off
php_admin_flag[session.cookie_httponly] = on
php_admin_flag[allow_url_include] = off
php_admin_value[session.save_handler] = files
php_admin_value[open_basedir] = $open_basedir
php_admin_value[auto_prepend_file] = /usr/share/php/auth_translation.php
php_value[include_path] = $include_path
php_flag[magic_quotes_gpc] = off
php_flag[track_vars] = on
php_flag[session.use_trans_sid] = off
php_flag[register_globals] = off
php_flag[register_long_arrays] = on
; Needed so shell_exec does it right
env[PATH] = $include_path
_EOF
}

View File

@@ -0,0 +1,38 @@
{
# use Data::Validate::IP;
use Net::IP qw(ip_is_ipv4 ip_is_ipv6);
our $KeySize = $modSSL{KeySize} ||'4096';
our $FQDN = "$SystemName.$DomainName";
our $Country = $modSSL{Country} || "--";
our $State = $modSSL{State} || "----";
our $commonName = $modSSL{CommonName} || $FQDN;
our $crt = "/home/e-smith/ssl.crt/$FQDN.crt";
our $key = "/home/e-smith/ssl.key/$FQDN.key";
our $defaultCity = $ldap{defaultCity} || '-';
our $defaultCompany = $ldap{defaultCompany} || $commonName ;
our $defaultDepartment = $ldap{defaultDepartment} || '-';
our $email = "admin\@$DomainName";
our @subjectAlt = `/sbin/e-smith/generate-subjectaltnames`;
chomp @subjectAlt;
our $subjectAltName = "";
my $i=0;
for my $elem (@subjectAlt) {
$subjectAltName .= ", " if $i>0;
$i++;
if (ip_is_ipv4($elem) || ip_is_ipv6($elem) ){
$subjectAltName .= "IP:$elem";
next;
}
$subjectAltName .= "DNS:$elem";
}
$subjectAltName = ( $subjectAltName eq "DNS: ")? "": $subjectAltName;
# crop fields that are too long for X509:
$Country = substr($Country, 0, 2);
$defaultCity = substr($defaultCity, 0, 128);
$defaultCompany = substr($defaultCompany, 0, 64);
$defaultDepartment = substr($defaultDepartment, 0, 64);
$email = substr($email, 0, 64);
$commonName = substr($commonName, 0, 64);
$OUT="";
}

View File

@@ -0,0 +1,30 @@
{
my $phone = ${ldap}{defaultPhoneNumber} || "none";
my $zip = ${ldap}{postalCode} || "H0H 0H0";
my $street = ${ldap}{defaultStreet} || "Address Line #1";
@lines = map {
m:\$config\['common_name'\]: && s/.*/\$config['common_name']='$commonName';/;
m:\$config\['unit'\]: && s/.*/\$config['unit']='$defaultDepartment';/;
m:\$config\['keysize'\]: && s/.*/\$config['keysize']='4096';/;
m:\$config\['country'\]: && s/.*/\$config['country']='$Country';/;
m:\$config\['province'\]: && s/.*/\$config['province']='$State';/;
m:\$config\['locality'\]: && s/.*/\$config['locality']='$defaultCity';/;
m:\$config\['organization'\]: && s/.*/\$config['organization']='$defaultCompany';/;
m:\$config\['contact'\]: && s/.*/\$config['contact']='$email';/;
m:\$config\['base_url'\]: && s/.*/\$config['base_url']='https:\/\/$commonName\/phpki\/';/;
s/(^|\n)[\n\s]*/$1/g;;
$_
} @lines;
push @lines, "\$config['common_name']='$commonName';" unless grep( /\$config\['common_name'\]/ ,@lines);
push @lines, "\$config['unit']='$defaultDepartment';" unless grep( /\$config\['unit'\]/ ,@lines);
push @lines, "\$config['keysize']='4096';" unless grep( /\$config\['keysize'\]/ ,@lines);
push @lines, "\$config['country']='$Country';" unless grep( /\$config\['country'\]/ ,@lines);
push @lines, "\$config['province']='$State';" unless grep( /\$config\['province'\]/ ,@lines);
push @lines, "\$config['locality']='$defaultCity';" unless grep( /\$config\['locality'\]/ ,@lines);
push @lines, "\$config['organization']='$defaultCompany';" unless grep( /\$config\['organization'\]/ ,@lines);
push @lines, "\$config['contact']='$email';" unless grep( /\$config\['contact'\]/ ,@lines);
push @lines, "\$config['base_url']='https://$commonName/phpki/';" unless grep( /\$config\['base_url'\]/ ,@lines);
# we do not update the following as it will mess up the file.
push @lines, "\$config[\'getting_help\']=\'<b>Contact:</b><br>\nFirst-Name Last-Name<br>\n$defaultCompany/$defaultDepartment<br>\n$street<br>\n$defaultCity, $State, $zip<br>\n<br>\nPhone: $phone<br>\nE-mail: <a href=mailto:$email>$email</a>&nbsp;&nbsp;&nbsp;<i><b>E-mail is preferred.</b></i><br>\';" unless grep( /\$config\['getting_help'\]/ ,@lines);
"";
}

View File

@@ -0,0 +1,12 @@
{
$OUT .= "";
foreach my $line (@lines)
{
chomp $line;
next if grep { /^$/ } $line ;
push @lines, $_;
$OUT .= "$line\n";
}
$OUT .= "?>";
}

View File

@@ -0,0 +1,17 @@
{
# vim: ft=perl:
%lines = ();
@lines = ();
open (RD, "</opt/phpki/html/config.php")
|| warn "Cannot open input file /opt/phpki/html/config.php: $!\n";
while (<RD>)
{
chomp;
next if grep { /^$/ } $_ ;
next if grep { /^\?/ } $_;
push @lines, $_;
$lines{$_} = 1;
}
close(RD);
"";
}

View File

@@ -0,0 +1,32 @@
#!/usr/bin/perl
#----------------------------------------------------------------------
# heading : Security
# description : Certificate Management
# navigation : 4000 4200
#----------------------------------------------------------------------
use strict;
use CGI':all';
use CGI::Carp qw(fatalsToBrowser);
BEGIN
{
$ENV {'PATH'} = '/bin:/usr/bin:/sbin';
$ENV {'SHELL'} = '/bin/bash';
delete $ENV {'ENV'};
}
my $q = new CGI;
my $content="0; url=https://".$ENV {'HTTP_X_FORWARDED_HOST'}."/phpki/ca/";
$q->default_dtd('-//W3C//DTD XHTML 1.0 Transitional//EN');
print $q->header ('text/html');
print $q->start_html (-head=>meta({-http_equiv=>'refresh', -content=>$content}));
print $q->end_html;