mirror of
https://github.com/opnsense/core.git
synced 2026-02-03 20:39:42 -05:00
System: Trust: Certificates - offer config directory (/usr/local/etc/ssl/ext_sources/) to store locations for certificates not managed by us, but practical to know about their existence. closes https://github.com/opnsense/core/issues/8279
This is useful for services like OPNWAF and Caddy. This commit only adds the facility and changes the admin page, the widget is left unaltered.
This commit is contained in:
parent
63b9f2e1aa
commit
f90e5445db
5 changed files with 114 additions and 2 deletions
14
src/etc/ssl/ext_sources/README
Normal file
14
src/etc/ssl/ext_sources/README
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
OPNsense certificates used by external applications and registered in the gui for viewing purposes only.
|
||||
|
||||
In this directory you can create files with a .conf extension (e.g. myapp.conf) with the following (sample) config:
|
||||
|
||||
[location]
|
||||
base=/usr/local/md/domains
|
||||
pattern=pubcert.pem
|
||||
description=OPNWAF
|
||||
|
||||
|
||||
"base" is the directory to recursively iterate, "pattern" is a regex matcher and each item returned will have a description
|
||||
attached to it for the frontend.
|
||||
|
||||
Files found can be identified by their id, which is an md5 hash of the contents.
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
namespace OPNsense\Trust\FieldTypes;
|
||||
|
||||
use OPNsense\Core\Backend;
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\Base\FieldTypes\ArrayField;
|
||||
use OPNsense\Base\FieldTypes\ContainerField;
|
||||
|
|
@ -105,6 +106,26 @@ class CertificatesField extends ArrayField
|
|||
return $container_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of externally managed certificates
|
||||
*/
|
||||
protected static function getStaticChildren()
|
||||
{
|
||||
$result = [];
|
||||
$ext_data = json_decode((new Backend())->configdRun('system trust ext_sources') ?? '', true);
|
||||
if (is_array($ext_data)) {
|
||||
foreach ($ext_data as $data) {
|
||||
$payload = \OPNsense\Trust\Store::parseX509($data['cert'] ?? '');
|
||||
if ($payload !== false && !empty($data['id'])) {
|
||||
$payload['crt_payload'] = $data['cert'];
|
||||
$payload['descr'] = $data['descr'] ?? '';
|
||||
$result[$data['id']] = $payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function actionPostLoadingEvent()
|
||||
{
|
||||
$usernames = [];
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@
|
|||
},
|
||||
formatters: {
|
||||
in_use: function (column, row) {
|
||||
if (row.in_use === '1') {
|
||||
if (!row.uuid.includes('-')) {
|
||||
return "<span class=\"fa fa-fw fa-sign-out\" data-value=\"1\" data-row-id=\"" + row.uuid + "\"></span>";
|
||||
} else if (row.in_use === '1') {
|
||||
return "<span class=\"fa fa-fw fa-check\" data-value=\"1\" data-row-id=\"" + row.uuid + "\"></span>";
|
||||
} else if (row.is_user === '1') {
|
||||
return "<span class=\"fa fa-fw fa-user-o\" data-value=\"1\" data-row-id=\"" + row.uuid + "\"></span>";
|
||||
|
|
@ -148,7 +150,17 @@
|
|||
}
|
||||
});
|
||||
grid_cert.on("loaded.rs.jquery.bootgrid", function (e){
|
||||
// reload categories before grid load
|
||||
/* should probably live in the "commands" section to optionally render items */
|
||||
grid_cert.find('tr').each(function(){
|
||||
let tr = $(this);
|
||||
if (tr.data('row-id') !== undefined && !tr.data('row-id').includes('-')) {
|
||||
tr.find('button.command-edit').hide();
|
||||
tr.find('button.command-copy').hide();
|
||||
tr.find('button.command-delete').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// reload categories after grid load
|
||||
if ($("#ca_filter > option").length == 0) {
|
||||
ajaxGet('/api/trust/cert/ca_list', {}, function(data, status){
|
||||
if (data.rows !== undefined) {
|
||||
|
|
|
|||
59
src/opnsense/scripts/system/cert_fetch_local.py
Executable file
59
src/opnsense/scripts/system/cert_fetch_local.py
Executable file
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/local/bin/python3
|
||||
"""
|
||||
Copyright (c) 2025 Ad Schellevis <ad@opnsense.org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---------------------------------------------------------------------------------------------------
|
||||
fetch a pluggable set of locally managed certificates which are not available in the configuration
|
||||
"""
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import ujson
|
||||
from configparser import ConfigParser
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
result = []
|
||||
for conffile in glob.glob("/usr/local/etc/ssl/ext_sources/*.conf"):
|
||||
cnf = ConfigParser()
|
||||
cnf.read(conffile)
|
||||
if cnf.has_section('location') and cnf.has_option('location', 'base') and cnf.has_option('location', 'pattern'):
|
||||
if cnf.has_option('location', 'description'):
|
||||
loc_descr = cnf.get('location', 'description')
|
||||
else:
|
||||
loc_descr = os.path.basename(conffile)[0:-5]
|
||||
match_pattern = re.compile(cnf.get('location', 'pattern'))
|
||||
for root, dirs, files in os.walk(cnf.get('location', 'base')):
|
||||
for filename in files:
|
||||
full_path = "%s/%s" % (root, filename)
|
||||
if match_pattern.match(filename) and os.path.getsize(full_path) < 1024*1024:
|
||||
payload = open(full_path).read()
|
||||
result.append({
|
||||
'cert': payload,
|
||||
'descr': loc_descr,
|
||||
'id': hashlib.md5(payload.encode()).hexdigest()
|
||||
})
|
||||
print(ujson.dumps(result))
|
||||
|
|
@ -196,6 +196,12 @@ command:/usr/local/sbin/pluginctl -c crl
|
|||
type:script
|
||||
message: trigger CRL changed event
|
||||
|
||||
[trust.ext_sources]
|
||||
command:/usr/local/opnsense/scripts/system/cert_fetch_local.py
|
||||
type:script_output
|
||||
message: fetch list of externally managed certificates
|
||||
cache_ttl: 60
|
||||
|
||||
[cpu.stream]
|
||||
command:/usr/local/opnsense/scripts/system/cpu.py
|
||||
parameters:--interval %s
|
||||
|
|
|
|||
Loading…
Reference in a new issue