1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
<?php
class WebInterface
{
public const PROP_TYPE = 'webinterface.https-type';
public const PROP_HSTS = 'webinterface.https-hsts';
public const PROP_REDIRECT = 'webinterface.https-redirect';
public const PROP_CURRENT_CERT_DOMAINS = 'webinterface.https-domains';
public const PROP_REDIRECT_DOMAIN = 'webinterface.redirect-domain';
public const PROP_API_KEY = 'webinterface.api-key';
/**
* Read data all handled domains from current certificate.
* SAN takes precedence, if empty, we fall back to CN.
* @param string[] $certDomains
* @return bool success reading?
*/
public static function extractCurrentCertData(array &$certDomains, int &$expireTimestamp, string &$issuer): bool
{
if (!is_readable('/etc/lighttpd/pub-cert.pem'))
return false;
$cert = openssl_x509_parse(file_get_contents('/etc/lighttpd/pub-cert.pem'));
if ($cert === false)
return false;
// Domains
$certDomains = [];
if (isset($cert['extensions']['subjectAltName'])) {
$doms = preg_split('/[,\s]+/', $cert['extensions']['subjectAltName'], -1, PREG_SPLIT_NO_EMPTY);
foreach ($doms as $d) {
if (substr_compare($d, 'DNS:', 0, 4, true) !== 0)
continue;
$d = substr($d, 4);
if (preg_match('/^([a-z0-9_-]|\*\.)[a-z0-9_.-]+$/', $d) && !in_array($d, $certDomains)) {
$certDomains[] = $d;
}
}
}
if (empty($certDomains) && isset($cert['subject']['CN'])
&& preg_match('/^([a-z0-9_-]|\*\.)[a-z0-9_.-]+$/', $cert['subject']['CN'])) {
$certDomains[] = $cert['subject']['CN'];
}
foreach ($certDomains as &$d) {
if ($d[-1] === '.') {
$d = substr($d, 0, -1);
}
}
Property::set(self::PROP_CURRENT_CERT_DOMAINS, implode(' ', $certDomains));
// Expire time
$expireTimestamp = $cert['validTo_time_t'] ?? 0;
// Issuer
$issuer = $cert['issuer']['CN'] ?? 'Unknown';
return true;
}
public static function setDomainRedirect(bool $enable): void
{
Property::set(self::PROP_REDIRECT_DOMAIN, $enable ? '1' : false);
}
public static function getDomainRedirect(): bool
{
return !empty(Property::get(self::PROP_REDIRECT_DOMAIN, false));
}
public static function isHttpsRedirectEnabled(): bool
{
return Property::get(self::PROP_REDIRECT) === 'True';
}
public static function tmDisableHttps(): ?string
{
Property::set(WebInterface::PROP_TYPE, 'off');
Property::set(WebInterface::PROP_HSTS, 'off');
$task = Taskmanager::submit('LighttpdHttps', []);
return $task['id'] ?? null;
}
public static function tmGenerateRandomCert(): ?string
{
Property::set(WebInterface::PROP_TYPE, 'generated');
$task = Taskmanager::submit('LighttpdHttps', [
'proxyip' => Property::getServerIp(),
'redirect' => self::isHttpsRedirectEnabled(),
]);
return $task['id'] ?? null;
}
public static function tmImportCustomCert(string $key, string $cert, ?string $chain = null): ?string
{
Property::set(WebInterface::PROP_TYPE, 'supplied');
$task = Taskmanager::submit('LighttpdHttps', [
'importcert' => $cert,
'importkey' => $key,
'importchain' => $chain,
'redirect' => self::isHttpsRedirectEnabled(),
]);
return $task['id'] ?? null;
}
public static function tmSetHttpRedirectMode(): ?string
{
if (Property::get(WebInterface::PROP_TYPE) === 'off')
return null;
$task = Taskmanager::submit('LighttpdHttps', array(
'redirectOnly' => true,
'redirect' => self::isHttpsRedirectEnabled(),
));
return $task['id'] ?? null;
}
public static function getApiKey(): ?string
{
$key = Property::get(self::PROP_API_KEY, null);
if (empty($key))
return null;
return $key;
}
public static function setApiKey(?string $key): void
{
Property::set(self::PROP_API_KEY, empty($key) ? null : $key);
}
}
|