mirror of
https://github.com/opnsense/core.git
synced 2026-06-09 00:42:36 -04:00
VPN: OpenVPN: add tls-crypt-v2 support (#10069)
* VPN: OpenVPN: Add tls-crypt-v2 support, initial implementation * Unify key generation into a single bash script that handles stdout parsing and always emits base64, consume that in the key generator * plist fix * Add comment that explains stuff a bit better * VPN: OpenVPN: add tls-crypt-v2 support - refactor https://github.com/opnsense/core/pull/10069 --------- Co-authored-by: Ad Schellevis <ad@opnsense.org>
This commit is contained in:
parent
45b3d35761
commit
84ec45409d
10 changed files with 118 additions and 21 deletions
1
plist
1
plist
|
|
@ -1328,6 +1328,7 @@
|
|||
/usr/local/opnsense/scripts/openssh/ssh_query.py
|
||||
/usr/local/opnsense/scripts/openvpn/client_connect.php
|
||||
/usr/local/opnsense/scripts/openvpn/client_disconnect.sh
|
||||
/usr/local/opnsense/scripts/openvpn/genkey.py
|
||||
/usr/local/opnsense/scripts/openvpn/kill_session.py
|
||||
/usr/local/opnsense/scripts/openvpn/ovpn_event.py
|
||||
/usr/local/opnsense/scripts/openvpn/ovpn_service_control.php
|
||||
|
|
|
|||
|
|
@ -96,16 +96,17 @@ class InstancesController extends ApiMutableModelControllerBase
|
|||
return $this->delBase('StaticKeys.StaticKey', $uuid);
|
||||
}
|
||||
|
||||
public function genKeyAction($type = 'secret')
|
||||
public function genKeyAction(string $type = 'secret'): array
|
||||
{
|
||||
if (in_array($type, ['secret', 'auth-token', 'tls-auth', 'tls-crypt'])) {
|
||||
$key = (new Backend())->configdpRun("openvpn genkey", [$type]);
|
||||
if (strpos($key, '-----BEGIN') !== false) {
|
||||
return [
|
||||
'result' => 'ok',
|
||||
'key' => trim($key)
|
||||
];
|
||||
}
|
||||
// If openvpn is run in client mode, the user must supply their own tls-crypt-v2-client key.
|
||||
// Generating it is pointless since the server key should remain with the server only.
|
||||
// We only generate keys here that can be used verbatim in server mode.
|
||||
if (!in_array($type, ['secret', 'auth-token', 'tls-auth', 'tls-crypt', 'tls-crypt-v2-server'], true)) {
|
||||
return ['result' => 'failed', 'message' => gettext('unknown key type')];
|
||||
}
|
||||
$key = (new Backend())->configdpRun("openvpn genkey", [$type]);
|
||||
if ($key !== null) {
|
||||
return ['result' => 'ok', 'key' => trim($key)];
|
||||
}
|
||||
return ['result' => 'failed'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
namespace OPNsense\OpenVPN;
|
||||
|
||||
use OPNsense\Base\UserException;
|
||||
use OPNsense\Core\AppConfig;
|
||||
use OPNsense\Core\Shell;
|
||||
|
||||
|
|
@ -99,12 +100,21 @@ class ArchiveOpenVPN extends PlainOpenVPN
|
|||
}
|
||||
}
|
||||
if (!empty($this->config['tls'])) {
|
||||
if ($this->config['tlsmode'] === 'crypt') {
|
||||
$conf[] = "tls-crypt {$base_filename}-tls.key";
|
||||
$keyfile = "{$base_filename}-tls.key";
|
||||
if ($this->config['tlsmode'] === 'crypt-v2') {
|
||||
$clientKey = $this->export_crypt_v2_client_key($this->config['tls']);
|
||||
if (empty($clientKey)) {
|
||||
throw new UserException(gettext('Failed to generate tls-crypt-v2 client key'));
|
||||
}
|
||||
file_put_contents("{$content_dir}/{$keyfile}", trim(base64_decode($clientKey, true)));
|
||||
$conf[] = "tls-crypt-v2 {$keyfile}";
|
||||
} elseif ($this->config['tlsmode'] === 'crypt') {
|
||||
file_put_contents("{$content_dir}/{$keyfile}", trim(base64_decode($this->config['tls'])));
|
||||
$conf[] = "tls-crypt {$keyfile}";
|
||||
} else {
|
||||
$conf[] = "tls-auth {$base_filename}-tls.key 1";
|
||||
file_put_contents("{$content_dir}/{$keyfile}", trim(base64_decode($this->config['tls'])));
|
||||
$conf[] = "tls-auth {$keyfile} 1";
|
||||
}
|
||||
file_put_contents("{$content_dir}/{$base_filename}-tls.key", trim(base64_decode($this->config['tls'])));
|
||||
}
|
||||
file_put_contents("{$content_dir}/{$base_filename}.ovpn", implode("\n", $conf));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Deciso B.V.
|
||||
* Copyright (C) 2018-2026 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
namespace OPNsense\OpenVPN;
|
||||
|
||||
use OPNsense\Core\Backend;
|
||||
|
||||
/**
|
||||
* Export stub file, contains shared logic for all types
|
||||
* @package OPNsense\Backup
|
||||
|
|
@ -79,4 +81,13 @@ abstract class BaseExporter
|
|||
openssl_pkcs12_export($crt, $p12, $prv, $pass, $args);
|
||||
return $p12;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $serverKey single line base64 encoded key
|
||||
* @return string crypt-v2 client key
|
||||
*/
|
||||
protected function export_crypt_v2_client_key(string $serverKey)
|
||||
{
|
||||
return trim((new Backend())->configdpRun("openvpn genkey", ['tls-crypt-v2-client', $serverKey]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
namespace OPNsense\OpenVPN;
|
||||
|
||||
use OPNsense\Base\UserException;
|
||||
|
||||
class PlainOpenVPN extends BaseExporter implements IExportProvider
|
||||
{
|
||||
/**
|
||||
|
|
@ -196,7 +198,15 @@ class PlainOpenVPN extends BaseExporter implements IExportProvider
|
|||
$conf[] = "</key>";
|
||||
}
|
||||
if (!empty($this->config['tls'])) {
|
||||
if ($this->config['tlsmode'] === 'crypt') {
|
||||
if ($this->config['tlsmode'] === 'crypt-v2') {
|
||||
$clientKey = $this->export_crypt_v2_client_key($this->config['tls']);
|
||||
if (empty($clientKey)) {
|
||||
throw new UserException(gettext('Failed to generate tls-crypt-v2 client key'));
|
||||
}
|
||||
$conf[] = "<tls-crypt-v2>";
|
||||
$conf = array_merge($conf, explode("\n", trim(base64_decode($clientKey, true))));
|
||||
$conf[] = "</tls-crypt-v2>";
|
||||
} elseif ($this->config['tlsmode'] === 'crypt') {
|
||||
$conf[] = "<tls-crypt>";
|
||||
$conf = array_merge($conf, explode("\n", trim(base64_decode($this->config['tls']))));
|
||||
$conf[] = "</tls-crypt>";
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
namespace OPNsense\OpenVPN;
|
||||
|
||||
use OPNsense\Base\UserException;
|
||||
use OPNsense\Core\AppConfig;
|
||||
use OPNsense\Core\Shell;
|
||||
|
||||
|
|
@ -129,12 +130,20 @@ class ViscosityVisz extends PlainOpenVPN
|
|||
}
|
||||
}
|
||||
if (!empty($this->config['tls'])) {
|
||||
if ($this->config['tlsmode'] === 'crypt') {
|
||||
if ($this->config['tlsmode'] === 'crypt-v2') {
|
||||
$clientKey = $this->export_crypt_v2_client_key($this->config['tls']);
|
||||
if (empty($clientKey)) {
|
||||
throw new UserException(gettext('Failed to generate tls-crypt-v2 client key'));
|
||||
}
|
||||
file_put_contents("{$content_dir}/ta.key", trim(base64_decode($clientKey, true)));
|
||||
$conf[] = "tls-crypt-v2 ta.key";
|
||||
} elseif ($this->config['tlsmode'] === 'crypt') {
|
||||
file_put_contents("{$content_dir}/ta.key", trim(base64_decode($this->config['tls'])));
|
||||
$conf[] = "tls-crypt ta.key";
|
||||
} else {
|
||||
file_put_contents("{$content_dir}/ta.key", trim(base64_decode($this->config['tls'])));
|
||||
$conf[] = "tls-auth ta.key 1";
|
||||
}
|
||||
file_put_contents("{$content_dir}/ta.key", trim(base64_decode($this->config['tls'])));
|
||||
}
|
||||
file_put_contents("{$content_dir}/config.conf", implode("\n", $conf));
|
||||
|
||||
|
|
|
|||
|
|
@ -437,6 +437,7 @@
|
|||
<OptionValues>
|
||||
<auth>auth (Authenticate control channel packets)</auth>
|
||||
<crypt>crypt (Encrypt and authenticate all control channel packets)</crypt>
|
||||
<crypt-v2>crypt-v2 (Encrypt and authenticate all control channel packets)</crypt-v2>
|
||||
</OptionValues>
|
||||
</mode>
|
||||
<key type="TextField">
|
||||
|
|
|
|||
|
|
@ -84,8 +84,9 @@
|
|||
$("#keygen").click(function() {
|
||||
let statickey_mode = $("#statickey\\.mode").val();
|
||||
const mode_map = {
|
||||
auth: "tls-auth",
|
||||
crypt: "tls-crypt"
|
||||
"auth": "tls-auth",
|
||||
"crypt": "tls-crypt",
|
||||
"crypt-v2": "tls-crypt-v2-server",
|
||||
};
|
||||
ajaxGet("/api/openvpn/instances/gen_key/" + mode_map[statickey_mode], {}, function(data){
|
||||
if (data.result === 'ok') {
|
||||
|
|
|
|||
53
src/opnsense/scripts/openvpn/genkey.py
Executable file
53
src/opnsense/scripts/openvpn/genkey.py
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/local/bin/python3
|
||||
|
||||
"""
|
||||
Copyright (c) 2026 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.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('type', help='type', type=str)
|
||||
parser.add_argument('--server_key', help='server key, base64 encoded', type=str)
|
||||
args = parser.parse_args()
|
||||
with tempfile.NamedTemporaryFile(mode='w') as fh:
|
||||
cmd = [
|
||||
'/usr/local/sbin/openvpn',
|
||||
'--genkey',
|
||||
args.type
|
||||
]
|
||||
if args.type == 'tls-crypt-v2-client':
|
||||
fh.write(base64.b64decode(args.server_key).decode())
|
||||
fh.flush()
|
||||
cmd.append('--tls-crypt-v2')
|
||||
cmd.append(fh.name)
|
||||
|
||||
sp = subprocess.run(cmd, capture_output=True, text=True)
|
||||
print(sp.stdout.strip())
|
||||
|
|
@ -11,8 +11,8 @@ type:script_output
|
|||
message:Kill OpenVPN session %s - %s
|
||||
|
||||
[genkey]
|
||||
command:/usr/local/sbin/openvpn
|
||||
parameters:--genkey %s /dev/stdout
|
||||
command:/usr/local/opnsense/scripts/openvpn/genkey.py
|
||||
parameters: %s --server_key %s
|
||||
type:script_output
|
||||
message: Generate new OpenVPN static %s key
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue