Project

General

Profile

Feature #16561 ยป pfsense-import-certificate.php

PHP Script for installing SSL Cert/Key - Dennis Adler, 12/01/2025 06:56 AM

 
1
#!/usr/bin/env php
2
<?php
3

    
4
/**
5
 * Import SSL certificates from a pre-determined place on the filesystem.
6
 * Once imported, set them for use in the GUI
7
 */
8

    
9
if (empty($argc)) {
10
	echo "Only accessible from the CLI.\r\n";
11
	die(1);
12
}
13

    
14
if ($argc != 3) {
15
	echo "Usage: php " . $argv[0] . " /path/to/certificate.crt /path/to/private/key.pem \r\n";
16
	die(1);
17
}
18

    
19
require_once "certs.inc";
20
require_once "pfsense-utils.inc";
21
require_once "functions.inc";
22
require_once "filter.inc";
23
require_once "shaper.inc";
24

    
25
$certificate = trim(file_get_contents($argv[1]));
26
$key = trim(file_get_contents($argv[2]));
27

    
28
// Do some quick verification of the certificate, similar to what the GUI does
29
if (empty($certificate)) {
30
	echo "The certificate is empty.\r\n";
31
	die(1);
32
}
33
if (!strstr($certificate, "BEGIN CERTIFICATE") || !strstr($certificate, "END CERTIFICATE")) {
34
	echo "This certificate does not appear to be valid.\r\n";
35
	die(1);
36
}
37

    
38
// Verification that the certificate matches the key
39
if (empty($key)) {
40
	echo "The key is empty.\r\n";
41
	die(1);
42
}
43
if (cert_get_publickey($certificate, false) != cert_get_publickey($key, false, 'prv')) {
44
	echo "The private key does not match the certificate.\r\n";
45
	die(1);
46
}
47

    
48
$cert = array();
49
//$cert['refid'] = uniqid();
50
$cert['refid'] = hash('sha256', $certificate); // if we don't take a hash each iteration will have a new unique id and update each time
51
$cert['descr'] = "Certificate added to pfsense through " . $argv[0] . " on " . date("Y/m/d");
52

    
53
echo "Certificate sha256: ".$cert['refid']."\r\n";
54

    
55
cert_import($cert, $certificate, $key);
56

    
57
// Set up the existing certificate store
58
// Copied from system_certmanager.php
59
if (!is_array($config['ca'])) {
60
	$config['ca'] = array();
61
}
62

    
63
$a_ca =& $config['ca'];
64

    
65
if (!is_array($config['cert'])) {
66
	$config['cert'] = array();
67
}
68

    
69
$a_cert =& $config['cert'];
70

    
71
echo "Current Web GUI Certificate ID: `".$config['system']['webgui']['ssl-certref']."`\r\n";
72
echo "Current Unbound Certificate ID: `".$config['unbound']['sslcertref']."`\r\n";
73

    
74
$updateConfig = true;
75
// Check if the certificate we just parsed is already imported (we'll check the certificate portion)
76
foreach ($a_cert as $existing_cert) {
77
	if ($existing_cert['crt'] === $cert['crt']) {
78
		$updateConfig = false; // no need to update cert configuration
79
	}
80
}
81

    
82
if ($updateConfig) {
83
	// Append the final certificate
84
	$a_cert[] = $cert;
85
	// Write out the updated configuration
86
	write_config("Save new certificate config, from pfsense-import-certificate.php");
87
	sleep(3); //sleep to space out the write_config calls so they show distinctly
88
} else {
89
	echo "Certificate is already installed, skipping configuration update.\r\n";
90
}
91

    
92
echo "Checking if services are using the latest certificate.\r\n";
93

    
94
$updateCertWebGui = false;
95

    
96
if ($config['system']['webgui']['ssl-certref'] !== $cert['refid']) {
97
	echo "Web GUI certificate changed, installing!\r\n";
98
	$updateCertWebGui = true;
99
}
100

    
101
// Assuming that all worked, we now need to set the new certificate for use in the GUI
102
$config['system']['webgui']['ssl-certref'] = $cert['refid'];
103

    
104
$updateCertUnbound = false;
105

    
106
// If Unbound is set to use TLS, then set the new certificate to be used by Unbound also.
107
if (isset($config['unbound']['enable']) and isset($config['unbound']['enablessl'])) {
108
	if ($config['unbound']['sslcertref'] !== $cert['refid']) {
109
		echo "Unbound certificate changed, installing!\r\n";
110
		$updateCertUnbound = true;
111
	}
112
	$config['unbound']['sslcertref'] = $cert['refid'];
113
} else {
114
	echo "Unbound SSL is disabled, ignoring.\r\n";
115
}
116

    
117
$updateCertCaptivePortal = false;
118

    
119
// If captive portal is set to use TLS, then set the new certificate
120
//   to be used by captive portals
121
foreach ($config['captiveportal'] as $cpid => $cportal) {
122
	if(isset($cportal['enable']) and isset($cportal['httpslogin'])){
123
		if ($config['captiveportal'][$cpid]['certref'] !== $cert['refid']) {
124
			echo "Captive Portal certificate changed, installing!\r\n";
125
			$updateCertCaptivePortal = true;
126
		}
127
		$config['captiveportal'][$cpid]['certref'] = $cert['refid'];
128
	}
129
}
130

    
131
if (!$updateCertWebGui and !$updateCertUnbound and !$updateCertCaptivePortal) {
132
	echo "No certificate changes, no need to update config.\r\n";
133
	die(); // exit with a valid error code, as this is intended behaviour
134
}
135

    
136
write_config("Set new certificate as active for webgui, from pfsense-import-certificate.php");
137
sleep(3); //sleep to space out the write_config calls
138

    
139
//Use cert_get_all_services to grab all services now using the new cert.
140

    
141
$services = cert_get_all_services($cert['refid']);
142

    
143
//Restart all services that are using the new cert.
144
echo "Restart services that are using the new certificate.\r\n";
145
var_dump($services);
146
log_error(gettext("Restart services that are using the new cert"));
147

    
148
cert_restart_services($services);
149
//All except unbound, there is a bug in cert_restart_services... it doesn't restart unbound
150
//https://redmine.pfsense.org/issues/15062
151

    
152
echo "Completed! New certificate installed.\r\n";
153

    
154
// If unbound cert was updated, then restart unbound.
155
// Remove once bug fixed
156
if ($updateCertUnbound) {
157
  echo "Restart Unbound\n";
158
  log_error(gettext("Unbound configuration has changed. Restarting Unbound."));
159
  service_control_restart('unbound','');
160
}
161

    
162

    
163
// Delete unused certificates added by this script
164

    
165
$a_cert =& $config['cert'];
166
$name = '';
167
foreach ($a_cert as $cid => $acrt) {
168
	echo "Validating certificate $cid for deletion.\r\n";
169
	if (!cert_in_use($acrt['refid']) and preg_match("/pfsense-import-certificate\.php/",$acrt['descr'])) {
170
		echo "--> Delete, no longer in use.\r\n";
171
		// cert not in use and matches description pattern
172
		$name.=htmlspecialchars($acrt['descr'])." ";
173
		unset($a_cert[$cid]);
174
	} else {
175
		echo "--> Ignoring.\r\n";
176
	}
177
}
178

    
179
if($name){
180
        echo "Deleted old certificates: save the config.\r\n";
181
        $savemsg = sprintf(gettext("Deleted certificate: %s , from pfsense-import-certificate.php"), $name);
182
        write_config($savemsg);
183
}
    (1-1/1)