Merge pull request #2601 from AiyionPrime/key-translate
gluon-mesh-vpn-wireguard: add fastd key migration
This commit is contained in:
commit
75c62fd2aa
@ -191,6 +191,16 @@ negative effects. Only when a previously connected node reboots the effect
|
||||
comes into play, as the gateway still knows about the old timestamp of the gluon
|
||||
node.
|
||||
|
||||
gluon-mesh-vpn-key-translate
|
||||
""""""""""""""""""""""""""""
|
||||
|
||||
Many communities already possess a collection of active fastd-keys when they
|
||||
plan migrating their community to WireGuard.
|
||||
These public keys known on the server-side can be derived into their WireGuard
|
||||
equivalent using `gluon-mesh-vpn-key-translate <https://github.com/AiyionPrime/gluon-mesh-vpn-key-translate>`__.
|
||||
The routers do the necessary reencoding of the private key seamlessly
|
||||
when updating firmware from fastd to the WireGuard variant.
|
||||
|
||||
Gateway / Supernode Configuration
|
||||
"""""""""""""""""""""""""""""""""
|
||||
|
||||
|
@ -6,7 +6,13 @@ include ../gluon.mk
|
||||
|
||||
define Package/gluon-mesh-vpn-wireguard
|
||||
TITLE:=Support for connecting meshes via wireguard
|
||||
DEPENDS:=+gluon-core +libgluonutil +gluon-mesh-vpn-core +wireguard-tools +wgpeerselector +libubus
|
||||
DEPENDS:=+gluon-core +libgluonutil +gluon-mesh-vpn-core +wireguard-tools +wgpeerselector +libubox +libubus
|
||||
endef
|
||||
|
||||
define Package/gluon-mesh-vpn-wireguard/install
|
||||
$(Gluon/Build/Install)
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-hex-to-b64 $(1)/usr/sbin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackageGluon,gluon-mesh-vpn-wireguard))
|
||||
|
@ -1,18 +1,63 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local uci = require('simple-uci').cursor()
|
||||
local unistd = require 'posix.unistd'
|
||||
local util = require('gluon.util')
|
||||
local site = require 'gluon.site'
|
||||
local sp = util.subprocess
|
||||
local wait = require 'posix.sys.wait'
|
||||
|
||||
local private_key = uci:get("network_gluon-old", 'wg_mesh', "private_key")
|
||||
local wg_private_key = uci:get("network_gluon-old", 'wg_mesh', "private_key")
|
||||
|
||||
if not private_key or not private_key:match("^" .. ("[%a%d+/]"):rep(42) .. "[AEIMQUYcgkosw480]=$") then
|
||||
private_key = "generate"
|
||||
local function valid_fastd_key(fastd_key)
|
||||
return fastd_key and fastd_key:match(('%x'):rep(64))
|
||||
end
|
||||
|
||||
local function valid_wireguard_key(wireguard_key)
|
||||
return wireguard_key and wireguard_key:match("^" .. ("[%a%d+/]"):rep(42) .. "[AEIMQUYcgkosw480]=$")
|
||||
end
|
||||
|
||||
local function migrate_from_fastd_secret(fastd_secret)
|
||||
local options = {
|
||||
stdin = sp.PIPE,
|
||||
stdout = sp.PIPE,
|
||||
}
|
||||
local pid, pipe = sp.popen('gluon-hex-to-b64', {}, options)
|
||||
|
||||
if not pid then
|
||||
return
|
||||
end
|
||||
|
||||
local inw = pipe.stdin
|
||||
local out = pipe.stdout
|
||||
|
||||
unistd.write(inw, string.format('%s\n', fastd_secret))
|
||||
unistd.close(inw)
|
||||
|
||||
local wpid, status, code = wait.wait(pid)
|
||||
if wpid and status == 'exited' and code == 0 then
|
||||
local result = unistd.read(out, 44)
|
||||
unistd.close(out)
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
if not valid_wireguard_key(wg_private_key) then
|
||||
local fastd_secret = uci:get('fastd', 'mesh_vpn', 'secret')
|
||||
if valid_fastd_key(fastd_secret) then
|
||||
wg_private_key = migrate_from_fastd_secret(fastd_secret)
|
||||
end
|
||||
end
|
||||
|
||||
if not valid_wireguard_key(wg_private_key) then
|
||||
wg_private_key = "generate"
|
||||
end
|
||||
|
||||
|
||||
uci:section('network', 'interface', 'wg_mesh', {
|
||||
proto = 'wireguard',
|
||||
fwmark = 1,
|
||||
private_key = private_key,
|
||||
private_key = wg_private_key,
|
||||
})
|
||||
|
||||
uci:section('network', 'interface', 'mesh_wg_mesh', {
|
||||
|
@ -1,6 +1,9 @@
|
||||
all: respondd.so
|
||||
all: respondd.so gluon-hex-to-b64
|
||||
|
||||
CFLAGS += -Wall -Werror-implicit-function-declaration
|
||||
|
||||
gluon-hex-to-b64: gluon-hex-to-b64.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -lubox
|
||||
|
||||
respondd.so: respondd.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -lubus
|
||||
|
101
package/gluon-mesh-vpn-wireguard/src/gluon-hex-to-b64.c
Normal file
101
package/gluon-mesh-vpn-wireguard/src/gluon-hex-to-b64.c
Normal file
@ -0,0 +1,101 @@
|
||||
// SPDX-FileCopyrightText: 2022 Jan-Niklas Burfeind <gluon@aiyionpri.me>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// SPDX-FileContributor: read_hex() by Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
|
||||
#include <libubox/utils.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* how many blocks should be encoded at once - can be configured
|
||||
*/
|
||||
#define BLOCK_AMOUNT 32
|
||||
|
||||
/**
|
||||
* smallest possible block size to encode in b64 without further contex
|
||||
* is three bytes - do not change
|
||||
*/
|
||||
#define CHUNK_SIZE (3*BLOCK_AMOUNT)
|
||||
|
||||
/** print usage info and exit as failed */
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "Usage: gluon-hex-to-b64\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* read a string of hexadecimal characters and return them as bytes
|
||||
* return false in case any non-hexadecimal characters are provided
|
||||
* return true on success
|
||||
*/
|
||||
static bool read_hex(uint8_t key[CHUNK_SIZE], const char *hexstr) {
|
||||
if (strspn(hexstr, "0123456789abcdefABCDEF") != strlen(hexstr))
|
||||
return false;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < CHUNK_SIZE; i++)
|
||||
sscanf(&hexstr[2 * i], "%02hhx", &key[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 1)
|
||||
usage();
|
||||
|
||||
unsigned char hex_input[CHUNK_SIZE * 2 + 1];
|
||||
uint8_t as_bytes[CHUNK_SIZE];
|
||||
int byte_count;
|
||||
int b64_buflen = B64_ENCODE_LEN(CHUNK_SIZE);
|
||||
int b64_return;
|
||||
size_t ret;
|
||||
|
||||
char str[b64_buflen];
|
||||
|
||||
do {
|
||||
ret = fread(hex_input, 1, sizeof(hex_input) - 1, stdin);
|
||||
hex_input[ret] = '\0';
|
||||
|
||||
/* in case fread did not fill six characters */
|
||||
if (ret != sizeof(hex_input)-1) {
|
||||
/* drop newline by replacing it with a null character */
|
||||
hex_input[strcspn(hex_input, "\n")] = 0;
|
||||
|
||||
/*
|
||||
* count length of resulting string and make sure it's even,
|
||||
* as bytes are represented using pairs of hex characters
|
||||
*/
|
||||
ret = strlen(hex_input);
|
||||
if (ret % 2 == 1) {
|
||||
fprintf(stderr, "Error: Incomplete hex representation of a byte.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
byte_count = ret / 2;
|
||||
b64_buflen = B64_ENCODE_LEN(byte_count);
|
||||
|
||||
/* in case read_hex fails due to invalid characters */
|
||||
if (!read_hex(as_bytes, hex_input)) {
|
||||
fprintf(stderr, "Error: Invalid hexadecimal input.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
b64_return = b64_encode(as_bytes, byte_count, str, b64_buflen);
|
||||
|
||||
/* trailing '\0' is not counted by b64_encode(), so we subtract one character */
|
||||
if (b64_buflen - 1 != b64_return) {
|
||||
fprintf(stderr, "Error: Encoding bytes as b64 failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("%s", str);
|
||||
/* repeat until a non full block is read */
|
||||
} while (ret == sizeof(hex_input)-1);
|
||||
printf("\n");
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
Loading…
Reference in New Issue
Block a user