This is part of a periodic installment of happenings on Discord, a community chat staffed by the developers and curated by its users.
Tracing API commands
Basing the API on SOAP is often much lamented and misunderstood. SOAP is a clunky interface compared to REST, which shares the same HTTP transport layer, but encapsulates its payload on top of XML. One enormous benefit of SOAP, however, is its API integrity amid fluidity. Surrogates can alter visibility, which may change module signatures ("Providers" do not modify module signatures). APNSCP regenerates its WSDL (or schema) on boot taking into account loaded surrogates. A WSDL provides a strict definition of services offered by an endpoint and how those parameters should be formatted. REST doesn't publish such a definition, leaving API details - including data structures - in the dark. Compare for example the WSDLs generated by Hostineer, which is a whitelabel brand of APNSCP and the official demo on Paixhans. Hostineer WSDL is 1,008 KB. Paixhans is a mere 958 KB. SOAP allows structure to morph while ensuring integrity.
Let's look at a simple surrogate that adds a new method called "soap" on the "test" module. This module is only available in debug mode, which can be enabled on-the-fly.
Create the following file in lib/modules/surrogates/test.php
.
<?php declare(strict_types=1);
class Test_Module_Surrogate extends \Test_Module {
public function soap($fn, ...$args) {
$key = getenv('KEY');
$uri = 'http://localhost:2082/soap';
$wsdl = 'http://localhost:2082/apnscp.wsdl';
$connopts = array(
'connection_timeout' => 30,
'location' => $uri,
'uri' => 'urn:apnscp.api.soap',
'trace' => true
);
$connopts['location'] = $uri . '?authkey=' . $key;
$client = new class($wsdl, $connopts) extends \SoapClient {
public function __doRequest($request, $location, $action, $version, $one_way = 0)
{
echo "REQUEST:\n", $request, "\n\n";
$resp = parent::__doRequest($request, $location, $action, $version,
$one_way);
echo "RESPONSE:\n", $resp, "\n\n-----------------\n";
return $resp;
}
};
$fn = str_replace([':','-'], '_', $fn);
return $client->$fn(...$args);
}
}
Create an API key within the panel via Dev > API Keys. Run the following command to see the XML payload as it's sent to and received from the API server.
env DEBUG=1 KEY=key-from-dev-keys-in-panel test:soap common:whoami
Congrats on creating your very first API command. Now if you restart APNSCP in debug mode you’ll see the WSDL is updated with the new “test_soap” command.
Evasive maneuvers
Let's say a system is under a deluge of connections from an offending IP or a dozen and conventional threshold-activated blacklisting through mod_evasive fails to fire. How do we implement a routine to examine all open connections to the server and ban the most egregious candidates?
A little shell scripting to the rescue! Name this file /usr/local/sbin/ban_spam.sh
.
#!/bin/sh
# Block IPs with over 100 connections open to server
function find_spam {
netstat -nptu | egrep 'TIME_W|SYN_' | awk '{print $5}' | sort -n | cut -d: -f1 | sort -n \
| uniq -c | sort -n | tail -n 5 \
| egrep '^[[:space:]]*[[:digit:]][[:digit:]][[:digit:]][[:digit:]]*'
}
# ban excessive apache connections
function ban_spam {
MYIPS=($(ip -o addr | awk '!/^[0-9]*: ?link\/ether/ {gsub("/", " "); print $4}'))
find_spam | awk '{print $2}' | while read IP ; do
[[ "${MYIPS[@]}" =~ "${IP}" ]] && continue
echo "Banning $IP"
/sbin/iptables -A INPUT -s $IP -j DROP
done
}
ban_spam
Just run ban_spam.sh
when load creeps beyond acceptable levels or better yet, add this as an action to Monit under your physical checks. For example, in /etc/monit.d/physical.conf
add a loadavg check beneath the "check system SYSNAME" group,
if loadavg (1min) > 50 then exec "/usr/local/sbin/ban_spam.sh"
Then restart Monit, systemctl restart monit
.
Deciphering failed integrity reports
Integrity reports run every month and scrub your system for anomalies. Any changes are reported back in digest form that should clue you in on exactly what has changed, rectifying any changes along the way. Periodically you may see a failed integrity check with no log and a nonsensical duration.
This happens when the APNSCP PHP version updates during integrity checks, which triggers a restart of the process group. Abrupt termination forces Ansible to become disassociated from the group and we end up with an incomplete log file that APNSCP tries summarize. There are some ideas to workaround this including spinning off the worker into its own session leader that should resolve this; however, that is work for another time.
Importing SSL
Are there docs on manually installing SSL for a customer? I have a customer that wants to use an EV cert. They've opened a ticket as instructed but I don't know how to tie it into the panel
Two options, programmatically via cpcmd -d domain ssl:install
or you can do it the old fashioned way: move the key in siteXX/fst/etc/httpd/conf/ssl.key/server.key
, CRT as server.crt
in ssl.crt/
, chain in ssl.crt/bundle.crt
, then in /etc/httpd/conf/siteXX.ssl/
, create a file named custom with:
SSLCertificateChainFile /home/virtual/siteXX/fst/etc/httpd/conf/ssl.crt/bundle.crt
Followed by a configuration rebuild and reload: htrebuild && systemctl reload httpd
Alternatively, the API method ssl:install
does this automatically. Arguments are key file, certificate, and optional bundle:
cpcmd -d domain.com ssl:install "$(cat /path/to/server.key)" "$(cat /path/to/server.crt)" "$(cat /path/to/bundle.crt)"
SSL will activate in 2 minutes or less depending upon what [httpd] => reload_delay is set to in config.ini.
But a greater question exists, why bother with EV when EV certificates are dead?