mirror of
https://github.com/netbirdio/netbird-unraid.git
synced 2026-05-22 18:44:49 -07:00
Per-profile credentials fix plus three NetBird daemon/install bug fixes
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
# Slackware post-install hook for unraid-netbird-utils.
|
||||
# Runs once at upgradepkg time, with the package root as $PWD.
|
||||
|
||||
# Make sure the rc.d script is executable and symlinked from /etc/rc.d
|
||||
# Make sure the rc.d script is executable.
|
||||
chmod 0755 usr/local/etc/rc.d/rc.netbird
|
||||
( cd etc/rc.d ; rm -f rc.netbird ; ln -sf /usr/local/etc/rc.d/rc.netbird rc.netbird )
|
||||
# Symlink it into /etc/rc.d only when that's a distinct directory. On stock
|
||||
# Unraid /etc/rc.d is itself a symlink to /usr/local/etc/rc.d, so the payload
|
||||
# is already reachable as /etc/rc.d/rc.netbird and adding a link would just
|
||||
# point the file at itself (a symlink loop that breaks the daemon).
|
||||
if [ ! -e etc/rc.d/rc.netbird ]; then
|
||||
( cd etc/rc.d && ln -sf /usr/local/etc/rc.d/rc.netbird rc.netbird )
|
||||
fi
|
||||
|
||||
# logrotate ownership
|
||||
chmod 0644 etc/logrotate.d/netbird
|
||||
|
||||
@@ -12,15 +12,29 @@ require_once "{$docroot}/plugins/netbird/include/common.php";
|
||||
$plugin = 'netbird';
|
||||
$cfg = function_exists('parse_plugin_cfg') ? parse_plugin_cfg($plugin) : [];
|
||||
|
||||
// Global daemon options live in netbird.cfg.
|
||||
$enabled = $cfg['ENABLE_NETBIRD'] ?? '1';
|
||||
$mgmtUrl = $cfg['MANAGEMENT_URL'] ?? '';
|
||||
$setupKey = $cfg['SETUP_KEY'] ?? '';
|
||||
$hostname = $cfg['HOSTNAME'] ?? '';
|
||||
$preSharedKey = $cfg['PRESHARED_KEY'] ?? '';
|
||||
$logLevel = $cfg['LOG_LEVEL'] ?? 'info';
|
||||
|
||||
$profiles = Netbird\listProfiles();
|
||||
$activeProf = Netbird\activeProfile();
|
||||
|
||||
// Which profile's credentials is the form editing? ?nbprofile= wins, then the
|
||||
// active profile, then "default" (NetBird's built-in profile).
|
||||
$editProf = (string) ($_GET['nbprofile'] ?? '');
|
||||
if (!Netbird\validProfileName($editProf)) {
|
||||
$editProf = '';
|
||||
}
|
||||
if ($editProf === '') {
|
||||
$editProf = $activeProf !== '' ? $activeProf : 'default';
|
||||
}
|
||||
|
||||
// Credentials are stored per profile (profiles/<name>.cfg), not globally.
|
||||
$creds = Netbird\readProfileCfg($editProf);
|
||||
$mgmtUrl = $creds['MANAGEMENT_URL'];
|
||||
$setupKey = $creds['SETUP_KEY'];
|
||||
$hostname = $creds['HOSTNAME'];
|
||||
$preSharedKey = $creds['PRESHARED_KEY'];
|
||||
?>
|
||||
|
||||
<style>
|
||||
@@ -38,9 +52,9 @@ $activeProf = Netbird\activeProfile();
|
||||
<div class="nb-profile-bar">
|
||||
<label for="nb-profile-select">_(Profile)_:</label>
|
||||
<?php if ($profiles): ?>
|
||||
<select id="nb-profile-select">
|
||||
<select id="nb-profile-select" onchange="nbViewProfile(this.value)">
|
||||
<?php foreach ($profiles as $p): ?>
|
||||
<option value="<?=htmlspecialchars($p['name'])?>" <?=$p['active'] ? 'selected' : ''?>>
|
||||
<option value="<?=htmlspecialchars($p['name'])?>" <?=$p['name'] === $editProf ? 'selected' : ''?>>
|
||||
<?=htmlspecialchars($p['name'])?><?=$p['active'] ? ' (active)' : ''?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
@@ -48,7 +62,7 @@ $activeProf = Netbird\activeProfile();
|
||||
<input type="button" value="_(Switch)_" onclick="nbProfile('select', $('#nb-profile-select').val())">
|
||||
<input type="button" value="_(Delete)_" onclick="nbProfileDelete()">
|
||||
<?php else: ?>
|
||||
<span class="nb-profile-empty">No profiles found. Start the daemon and add one below.</span>
|
||||
<span class="nb-profile-empty">No profiles found yet — editing <code><?=htmlspecialchars($editProf)?></code>. Start the daemon and add one below.</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<input type="text" id="nb-profile-new" placeholder="new-profile-name" maxlength="32">
|
||||
@@ -59,9 +73,8 @@ $activeProf = Netbird\activeProfile();
|
||||
NetBird supports multiple profiles so you can keep separate identities (e.g., a personal NetBird Cloud account and a self-hosted tenant) on the same host. Switching profiles re-runs <code>netbird up</code> against the new profile using the credentials you fill in below.
|
||||
</blockquote>
|
||||
|
||||
<form markdown="1" method="POST" action="/update.php" target="progressFrame">
|
||||
<input type="hidden" name="#file" value="<?=$plugin?>/<?=$plugin?>.cfg">
|
||||
<input type="hidden" name="#command" value="/usr/local/emhttp/plugins/netbird/scripts/apply.sh">
|
||||
<form markdown="1" id="nb-settings-form" onsubmit="return false">
|
||||
<input type="hidden" id="nb-edit-profile" value="<?=htmlspecialchars($editProf)?>">
|
||||
|
||||
_(Enable NetBird)_:
|
||||
: <select name="ENABLE_NETBIRD">
|
||||
@@ -73,11 +86,11 @@ _(Enable NetBird)_:
|
||||
Start the NetBird daemon automatically when the array starts. Disabling stops the service on the next apply.
|
||||
</blockquote>
|
||||
|
||||
_(Active Profile)_:
|
||||
: <code><?=htmlspecialchars($activeProf ?: '(none — set one in the bar above)')?></code>
|
||||
_(Editing Profile)_:
|
||||
: <code><?=htmlspecialchars($editProf)?></code><?php if ($editProf === $activeProf): ?> _(active)_<?php endif; ?>
|
||||
|
||||
<blockquote class="inline_help">
|
||||
The credentials below are applied to whichever profile is currently active. Switch profiles using the picker above.
|
||||
The credentials below belong to the <b><?=htmlspecialchars($editProf)?></b> profile only. Pick a different profile in the bar above to edit its credentials. Clicking <b>Apply</b> saves these credentials to this profile, selects it, and reconnects — it does not touch your other profiles.
|
||||
</blockquote>
|
||||
|
||||
_(Management URL)_:
|
||||
@@ -123,13 +136,22 @@ _(Log Level)_:
|
||||
NetBird daemon log verbosity. Written to <code>/var/log/netbird.log</code>.
|
||||
</blockquote>
|
||||
|
||||
<input type="submit" name="#default" value="_(Default)_">
|
||||
: <input type="submit" name="#apply" value="_(Apply)_" disabled><input type="button" value="_(Done)_" onclick="done()">
|
||||
: <input type="button" value="_(Apply)_" onclick="nbSaveSettings()"><input type="button" value="_(Done)_" onclick="done()">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var nbCsrfToken = <?= json_encode((@parse_ini_file('/var/local/emhttp/var.ini') ?: [])['csrf_token'] ?? '') ?>;
|
||||
|
||||
// Where to send the browser after an action completes. Defaults to a plain
|
||||
// reload; pass a profile name to land on that profile's credentials.
|
||||
function nbReloadTo(profile) {
|
||||
if (profile) {
|
||||
location.href = location.pathname + '?nbprofile=' + encodeURIComponent(profile);
|
||||
} else {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
function nbProfile(action, name, extra) {
|
||||
var data = { action: 'profile-' + action, name: name, csrf_token: nbCsrfToken };
|
||||
if (extra) { $.extend(data, extra); }
|
||||
@@ -137,7 +159,7 @@ function nbProfile(action, name, extra) {
|
||||
if (resp && resp.message) {
|
||||
swal({ title: resp.title || 'NetBird', text: resp.message, type: resp.type || 'info' });
|
||||
}
|
||||
setTimeout(function(){ location.reload(); }, 1200);
|
||||
setTimeout(function(){ nbReloadTo(resp && resp.profile); }, 1200);
|
||||
}, 'json').fail(function(xhr){
|
||||
swal({ title: 'NetBird error', text: xhr.responseText || 'Request failed', type: 'error' });
|
||||
});
|
||||
@@ -150,6 +172,34 @@ function nbProfileAdd() {
|
||||
}
|
||||
nbProfile('add', name);
|
||||
}
|
||||
// View a profile's stored credentials without changing the active profile.
|
||||
function nbViewProfile(name) {
|
||||
if (name) { nbReloadTo(name); }
|
||||
}
|
||||
// Save the global options + the edited profile's credentials, then apply.
|
||||
function nbSaveSettings() {
|
||||
var profile = $('#nb-edit-profile').val();
|
||||
var form = document.getElementById('nb-settings-form');
|
||||
var data = {
|
||||
action: 'save',
|
||||
name: profile,
|
||||
ENABLE_NETBIRD: $('select[name=ENABLE_NETBIRD]', form).val(),
|
||||
LOG_LEVEL: $('select[name=LOG_LEVEL]', form).val(),
|
||||
MANAGEMENT_URL: $('input[name=MANAGEMENT_URL]', form).val(),
|
||||
SETUP_KEY: $('input[name=SETUP_KEY]', form).val(),
|
||||
HOSTNAME: $('input[name=HOSTNAME]', form).val(),
|
||||
PRESHARED_KEY: $('input[name=PRESHARED_KEY]', form).val(),
|
||||
csrf_token: nbCsrfToken
|
||||
};
|
||||
$.post('/plugins/netbird/include/action.php', data, function(resp){
|
||||
if (resp && resp.message) {
|
||||
swal({ title: resp.title || 'NetBird', text: resp.message, type: resp.type || 'info' });
|
||||
}
|
||||
setTimeout(function(){ nbReloadTo(resp && resp.profile); }, 1200);
|
||||
}, 'json').fail(function(xhr){
|
||||
swal({ title: 'NetBird error', text: xhr.responseText || 'Request failed', type: 'error' });
|
||||
});
|
||||
}
|
||||
function nbProfileDelete() {
|
||||
var name = $('#nb-profile-select').val();
|
||||
if (!name) { return; }
|
||||
|
||||
@@ -1,6 +1,2 @@
|
||||
ENABLE_NETBIRD="1"
|
||||
MANAGEMENT_URL=""
|
||||
SETUP_KEY=""
|
||||
HOSTNAME=""
|
||||
PRESHARED_KEY=""
|
||||
LOG_LEVEL="info"
|
||||
|
||||
@@ -10,6 +10,7 @@ log "Stopping NetBird"
|
||||
log "Erasing NetBird configuration and state"
|
||||
rm -rf /boot/config/plugins/netbird/etc/*
|
||||
rm -rf /boot/config/plugins/netbird/lib/*
|
||||
rm -rf /boot/config/plugins/netbird/profiles
|
||||
rm -f /boot/config/plugins/netbird/netbird.cfg
|
||||
|
||||
log "Restarting NetBird"
|
||||
|
||||
@@ -29,7 +29,6 @@ if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
|
||||
// run, the field is already gone. No redundant check here.
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$cfg = Netbird\readCfg();
|
||||
|
||||
/**
|
||||
* Validate a profile name. Allowed: letters, digits, dot, dash, underscore.
|
||||
@@ -40,17 +39,33 @@ function nb_valid_profile_name(string $name): bool
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the argument vector for `netbird up` using cfg values.
|
||||
* Build the argument vector for `netbird up` from a profile's credentials.
|
||||
*
|
||||
* @param array<string,string> $creds
|
||||
* @return string[]
|
||||
*/
|
||||
function nb_up_args(array $cfg): array
|
||||
/**
|
||||
* Normalize a management URL for comparison. Blank means NetBird's default
|
||||
* cloud; trailing slashes and the default :443 port are insignificant.
|
||||
*/
|
||||
function nb_mgmt_norm(string $url): string
|
||||
{
|
||||
$url = strtolower(trim($url));
|
||||
if ($url === '') {
|
||||
$url = 'https://api.netbird.io';
|
||||
}
|
||||
$url = rtrim($url, '/');
|
||||
$url = preg_replace('/:443$/', '', $url);
|
||||
return $url;
|
||||
}
|
||||
|
||||
function nb_up_args(array $creds): array
|
||||
{
|
||||
$args = ['up'];
|
||||
if (!empty($cfg['MANAGEMENT_URL'])) { $args[] = '--management-url'; $args[] = $cfg['MANAGEMENT_URL']; }
|
||||
if (!empty($cfg['SETUP_KEY'])) { $args[] = '--setup-key'; $args[] = $cfg['SETUP_KEY']; }
|
||||
if (!empty($cfg['HOSTNAME'])) { $args[] = '--hostname'; $args[] = $cfg['HOSTNAME']; }
|
||||
if (!empty($cfg['PRESHARED_KEY'])) { $args[] = '--preshared-key'; $args[] = $cfg['PRESHARED_KEY']; }
|
||||
if (!empty($creds['MANAGEMENT_URL'])) { $args[] = '--management-url'; $args[] = $creds['MANAGEMENT_URL']; }
|
||||
if (!empty($creds['SETUP_KEY'])) { $args[] = '--setup-key'; $args[] = $creds['SETUP_KEY']; }
|
||||
if (!empty($creds['HOSTNAME'])) { $args[] = '--hostname'; $args[] = $creds['HOSTNAME']; }
|
||||
if (!empty($creds['PRESHARED_KEY'])) { $args[] = '--preshared-key'; $args[] = $creds['PRESHARED_KEY']; }
|
||||
return $args;
|
||||
}
|
||||
|
||||
@@ -65,7 +80,13 @@ switch ($action) {
|
||||
usleep(500000);
|
||||
}
|
||||
}
|
||||
[$rc, $out] = Netbird\nb(nb_up_args($cfg));
|
||||
// Bring up the currently-active profile using its own stored credentials.
|
||||
$active = Netbird\activeProfile();
|
||||
if ($active !== '') {
|
||||
Netbird\nb(['profile', 'select', $active]);
|
||||
}
|
||||
$creds = $active !== '' ? Netbird\readProfileCfg($active) : [];
|
||||
[$rc, $out] = Netbird\nb(nb_up_args($creds));
|
||||
echo json_encode([
|
||||
'type' => $rc === 0 ? 'success' : 'error',
|
||||
'title' => $rc === 0 ? 'Connecting' : 'NetBird up failed',
|
||||
@@ -106,10 +127,16 @@ switch ($action) {
|
||||
break;
|
||||
}
|
||||
[$rc, $out] = Netbird\nb(['profile', 'add', $name]);
|
||||
if ($rc === 0) {
|
||||
// Seed an empty credential cfg so the new profile starts blank
|
||||
// rather than appearing to inherit another profile's settings.
|
||||
Netbird\writeProfileCfg($name, []);
|
||||
}
|
||||
echo json_encode([
|
||||
'type' => $rc === 0 ? 'success' : 'error',
|
||||
'title' => $rc === 0 ? 'Profile added' : 'Add failed',
|
||||
'message' => $out ?: "Profile '$name' added.",
|
||||
'profile' => $rc === 0 ? $name : null,
|
||||
]);
|
||||
break;
|
||||
|
||||
@@ -121,6 +148,9 @@ switch ($action) {
|
||||
break;
|
||||
}
|
||||
[$rc, $out] = Netbird\nb(['profile', 'remove', $name]);
|
||||
if ($rc === 0) {
|
||||
Netbird\deleteProfileCfg($name);
|
||||
}
|
||||
echo json_encode([
|
||||
'type' => $rc === 0 ? 'success' : 'error',
|
||||
'title' => $rc === 0 ? 'Profile removed' : 'Remove failed',
|
||||
@@ -144,8 +174,9 @@ switch ($action) {
|
||||
]);
|
||||
break;
|
||||
}
|
||||
// After switching, re-run `up` with current cfg so the new profile actually connects.
|
||||
[$rcUp, $outUp] = Netbird\nb(nb_up_args($cfg));
|
||||
// Re-run `up` using THIS profile's own stored credentials, so switching
|
||||
// never connects with another profile's settings.
|
||||
[$rcUp, $outUp] = Netbird\nb(nb_up_args(Netbird\readProfileCfg($name)));
|
||||
echo json_encode([
|
||||
'type' => 'success',
|
||||
'title' => 'Profile switched',
|
||||
@@ -153,6 +184,55 @@ switch ($action) {
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'save':
|
||||
// Save the Settings form: global daemon options + the selected
|
||||
// profile's credentials, then apply them scoped to that profile.
|
||||
$name = trim((string) ($_POST['name'] ?? ''));
|
||||
if (!nb_valid_profile_name($name)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['type' => 'error', 'message' => 'Invalid profile name.']);
|
||||
break;
|
||||
}
|
||||
|
||||
$enable = (($_POST['ENABLE_NETBIRD'] ?? '1') === '0') ? '0' : '1';
|
||||
$log = (string) ($_POST['LOG_LEVEL'] ?? 'info');
|
||||
if (!in_array($log, ['panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'], true)) {
|
||||
$log = 'info';
|
||||
}
|
||||
Netbird\writeGlobalCfg(['ENABLE_NETBIRD' => $enable, 'LOG_LEVEL' => $log]);
|
||||
|
||||
// Detect a registration-parameter change BEFORE overwriting the stored
|
||||
// cfg. NetBird bakes the management URL and hostname into the profile at
|
||||
// registration; changing either is ignored by a plain `up`, so the
|
||||
// profile must be re-registered (see apply.sh). The setup key is auth
|
||||
// material, not an identity, so it does not trigger re-registration.
|
||||
$old = Netbird\readProfileCfg($name);
|
||||
$creds = [];
|
||||
foreach (Netbird\PROFILE_KEYS as $k) {
|
||||
$creds[$k] = (string) ($_POST[$k] ?? '');
|
||||
}
|
||||
$mgmtChanged = nb_mgmt_norm($old['MANAGEMENT_URL']) !== nb_mgmt_norm($creds['MANAGEMENT_URL']);
|
||||
$hostChanged = strtolower(trim($old['HOSTNAME'])) !== strtolower(trim($creds['HOSTNAME']));
|
||||
$reregister = ($mgmtChanged || $hostChanged) ? '1' : '0';
|
||||
|
||||
if (!Netbird\writeProfileCfg($name, $creds)) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['type' => 'error', 'message' => "Could not write profile '$name'."]);
|
||||
break;
|
||||
}
|
||||
|
||||
// apply.sh selects the profile, ensures the daemon is running, and runs up
|
||||
// (re-registering first when the management URL changed).
|
||||
exec('/usr/local/emhttp/plugins/netbird/scripts/apply.sh '
|
||||
. escapeshellarg($name) . ' ' . escapeshellarg($reregister) . ' > /dev/null 2>&1 &');
|
||||
echo json_encode([
|
||||
'type' => 'success',
|
||||
'title' => 'Settings saved',
|
||||
'message' => "Saved profile '$name' and applying…",
|
||||
'profile' => $name,
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
http_response_code(400);
|
||||
echo json_encode(['type' => 'error', 'message' => "Unknown action: $action"]);
|
||||
|
||||
@@ -9,8 +9,12 @@ const PLUGIN = 'netbird';
|
||||
const NETBIRD_BIN = '/usr/local/sbin/netbird';
|
||||
const RC_SCRIPT = '/etc/rc.d/rc.netbird';
|
||||
const CFG_FILE = '/boot/config/plugins/netbird/netbird.cfg';
|
||||
const PROFILE_DIR = '/boot/config/plugins/netbird/profiles';
|
||||
const DAEMON_ADDR = 'unix:///var/run/netbird.sock';
|
||||
|
||||
// Credential keys stored per profile (the rest of netbird.cfg is daemon-global).
|
||||
const PROFILE_KEYS = ['MANAGEMENT_URL', 'SETUP_KEY', 'HOSTNAME', 'PRESHARED_KEY'];
|
||||
|
||||
/**
|
||||
* Run a netbird CLI subcommand and return [exitCode, stdout].
|
||||
*
|
||||
@@ -106,6 +110,99 @@ function readCfg(): array
|
||||
return parse_plugin_cfg(PLUGIN) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the given key/value pairs into the global netbird.cfg, preserving any
|
||||
* other keys already present. Values are written quoted; embedded quotes are
|
||||
* stripped.
|
||||
*
|
||||
* @param array<string,string> $updates
|
||||
*/
|
||||
function writeGlobalCfg(array $updates): bool
|
||||
{
|
||||
$existing = is_readable(CFG_FILE) ? (parse_ini_file(CFG_FILE) ?: []) : [];
|
||||
$merged = array_merge($existing, $updates);
|
||||
|
||||
$dir = dirname(CFG_FILE);
|
||||
if (!is_dir($dir) && !@mkdir($dir, 0755, true) && !is_dir($dir)) {
|
||||
return false;
|
||||
}
|
||||
$lines = '';
|
||||
foreach ($merged as $k => $v) {
|
||||
$v = str_replace('"', '', (string) $v);
|
||||
$lines .= $k . '="' . $v . "\"\n";
|
||||
}
|
||||
return file_put_contents(CFG_FILE, $lines) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject anything that isn't a valid profile name (mirrors action.php).
|
||||
* Used to keep profile names safe as filename components.
|
||||
*/
|
||||
function validProfileName(string $name): bool
|
||||
{
|
||||
return (bool) preg_match('/^[A-Za-z0-9_.\-]{1,32}$/', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute path to a profile's credential cfg file.
|
||||
*/
|
||||
function profileCfgPath(string $name): string
|
||||
{
|
||||
return PROFILE_DIR . '/' . $name . '.cfg';
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the per-profile credentials (MANAGEMENT_URL, SETUP_KEY, HOSTNAME,
|
||||
* PRESHARED_KEY) for a given profile. Profiles with no cfg yet start blank.
|
||||
*
|
||||
* @return array<string,string>
|
||||
*/
|
||||
function readProfileCfg(string $name): array
|
||||
{
|
||||
$creds = array_fill_keys(PROFILE_KEYS, '');
|
||||
|
||||
$path = profileCfgPath($name);
|
||||
if (is_readable($path)) {
|
||||
$vals = parse_ini_file($path) ?: [];
|
||||
foreach (PROFILE_KEYS as $k) {
|
||||
$creds[$k] = (string) ($vals[$k] ?? '');
|
||||
}
|
||||
}
|
||||
return $creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a profile's credentials to profiles/<name>.cfg.
|
||||
* Returns false if the name is invalid or the file can't be written.
|
||||
*
|
||||
* @param array<string,string> $creds
|
||||
*/
|
||||
function writeProfileCfg(string $name, array $creds): bool
|
||||
{
|
||||
if (!validProfileName($name)) {
|
||||
return false;
|
||||
}
|
||||
if (!is_dir(PROFILE_DIR) && !@mkdir(PROFILE_DIR, 0755, true) && !is_dir(PROFILE_DIR)) {
|
||||
return false;
|
||||
}
|
||||
$lines = '';
|
||||
foreach (PROFILE_KEYS as $k) {
|
||||
$v = str_replace('"', '', (string) ($creds[$k] ?? ''));
|
||||
$lines .= $k . '="' . $v . "\"\n";
|
||||
}
|
||||
return file_put_contents(profileCfgPath($name), $lines) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a profile's credential cfg (best effort).
|
||||
*/
|
||||
function deleteProfileCfg(string $name): void
|
||||
{
|
||||
if (validProfileName($name)) {
|
||||
@unlink(profileCfgPath($name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a byte count as a short human string (e.g., "12.4 MB").
|
||||
*/
|
||||
|
||||
Regular → Executable
+85
-25
@@ -1,15 +1,38 @@
|
||||
#!/bin/bash
|
||||
# Triggered by the Settings page Apply button (#command in the form post).
|
||||
# By the time we're called, /update.php has already written the new
|
||||
# netbird.cfg. Reconcile the running daemon with the new settings.
|
||||
# Apply NetBird settings for a single profile.
|
||||
#
|
||||
# Usage: apply.sh <profile-name> [reregister]
|
||||
#
|
||||
# Called by action.php's "save" handler after it has written the global
|
||||
# netbird.cfg (ENABLE_NETBIRD, LOG_LEVEL) and the per-profile credential cfg
|
||||
# (profiles/<name>.cfg). We reconcile the running daemon WITHOUT a full restart:
|
||||
# ensure it's up, select the target profile, then run `up` with that profile's
|
||||
# own credentials. Selecting before `up` is what keeps profiles from crossing.
|
||||
#
|
||||
# When <reregister> is "1", the profile is torn down and recreated before `up`.
|
||||
# NetBird bakes the management URL and hostname into the profile at first
|
||||
# registration; a plain `up --management-url X`/`--hostname Y` on an
|
||||
# already-registered profile is ignored ("Already connected"). So changing
|
||||
# either requires re-registering, which is what action.php requests via this flag.
|
||||
|
||||
. /usr/local/emhttp/plugins/netbird/include/log.sh 2>/dev/null || log() { echo "$*" ; }
|
||||
|
||||
CFG=/boot/config/plugins/netbird/netbird.cfg
|
||||
[ -f "$CFG" ] || { log "No netbird.cfg, nothing to apply." ; exit 0 ; }
|
||||
PROFILE="$1"
|
||||
REREGISTER="$2"
|
||||
if [ -z "$PROFILE" ]; then
|
||||
log "apply.sh: no profile name given; nothing to apply."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
. "$CFG"
|
||||
NB=/usr/local/sbin/netbird
|
||||
GLOBAL_CFG=/boot/config/plugins/netbird/netbird.cfg
|
||||
PROFILE_CFG="/boot/config/plugins/netbird/profiles/${PROFILE}.cfg"
|
||||
|
||||
# Global daemon options.
|
||||
if [ -f "$GLOBAL_CFG" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$GLOBAL_CFG"
|
||||
fi
|
||||
|
||||
if [ "$ENABLE_NETBIRD" = "0" ] || [ "$ENABLE_NETBIRD" = "false" ]; then
|
||||
log "Settings disabled NetBird; stopping daemon."
|
||||
@@ -17,28 +40,65 @@ if [ "$ENABLE_NETBIRD" = "0" ] || [ "$ENABLE_NETBIRD" = "false" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Applying settings; restarting daemon."
|
||||
/etc/rc.d/rc.netbird restart
|
||||
# Per-profile credentials (override any legacy values sourced from the global cfg).
|
||||
MANAGEMENT_URL="" ; SETUP_KEY="" ; HOSTNAME="" ; PRESHARED_KEY=""
|
||||
if [ -f "$PROFILE_CFG" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$PROFILE_CFG"
|
||||
fi
|
||||
|
||||
# Wait for the daemon socket before attempting login.
|
||||
# Ensure the daemon is running (start if down) — no forced restart on a save.
|
||||
if ! /usr/bin/pgrep -f "^${NB} service run" >/dev/null 2>&1; then
|
||||
log "Daemon not running; starting it."
|
||||
/etc/rc.d/rc.netbird start
|
||||
fi
|
||||
|
||||
# Wait for the daemon socket before talking to it.
|
||||
for _ in $(seq 1 20); do
|
||||
[ -S /var/run/netbird.sock ] && break
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# If a setup key is provided, attempt non-interactive login on apply.
|
||||
if [ -n "$SETUP_KEY" ]; then
|
||||
UP_ARGS="up --setup-key $SETUP_KEY"
|
||||
[ -n "$MANAGEMENT_URL" ] && UP_ARGS="$UP_ARGS --management-url $MANAGEMENT_URL"
|
||||
[ -n "$HOSTNAME" ] && UP_ARGS="$UP_ARGS --hostname $HOSTNAME"
|
||||
[ -n "$PRESHARED_KEY" ] && UP_ARGS="$UP_ARGS --preshared-key $PRESHARED_KEY"
|
||||
log "Running: netbird up (--setup-key ***)"
|
||||
OUT=$(/usr/local/sbin/netbird $UP_ARGS 2>&1)
|
||||
RC=$?
|
||||
echo "$OUT" >> /var/log/netbird-utils.log
|
||||
if [ "$RC" -ne 0 ]; then
|
||||
log "netbird up failed (rc=$RC): $OUT"
|
||||
else
|
||||
log "netbird up succeeded."
|
||||
fi
|
||||
# If the management URL changed, re-register: tear the profile down and
|
||||
# recreate it so `up` registers fresh against the new management server.
|
||||
# NetBird won't let you remove the active profile, so we park on a short-lived
|
||||
# temp profile while removing/recreating the target.
|
||||
if [ "$REREGISTER" = "1" ]; then
|
||||
log "Registration settings changed; re-registering profile '$PROFILE'."
|
||||
"$NB" down >/dev/null 2>&1
|
||||
TMP="__nbreset_$$"
|
||||
"$NB" profile add "$TMP" >/dev/null 2>&1
|
||||
"$NB" profile select "$TMP" >/dev/null 2>&1
|
||||
"$NB" profile remove "$PROFILE" >/dev/null 2>&1
|
||||
"$NB" profile add "$PROFILE" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Select the target profile so the subsequent `up` can't connect another one.
|
||||
log "Selecting profile '$PROFILE'."
|
||||
SEL=$("$NB" profile select "$PROFILE" 2>&1)
|
||||
SRC=$?
|
||||
if [ "$SRC" -ne 0 ]; then
|
||||
log "profile select '$PROFILE' failed (rc=$SRC): $SEL"
|
||||
fi
|
||||
|
||||
# Drop the temp parking profile now that the target is active again.
|
||||
if [ "$REREGISTER" = "1" ] && [ -n "$TMP" ]; then
|
||||
"$NB" profile remove "$TMP" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Bring the profile up with its own credentials.
|
||||
UP_ARGS="up"
|
||||
[ -n "$MANAGEMENT_URL" ] && UP_ARGS="$UP_ARGS --management-url $MANAGEMENT_URL"
|
||||
[ -n "$SETUP_KEY" ] && UP_ARGS="$UP_ARGS --setup-key $SETUP_KEY"
|
||||
[ -n "$HOSTNAME" ] && UP_ARGS="$UP_ARGS --hostname $HOSTNAME"
|
||||
[ -n "$PRESHARED_KEY" ] && UP_ARGS="$UP_ARGS --preshared-key $PRESHARED_KEY"
|
||||
|
||||
log "Running: netbird up (profile '$PROFILE')"
|
||||
OUT=$("$NB" $UP_ARGS 2>&1)
|
||||
RC=$?
|
||||
echo "$OUT" >> /var/log/netbird-utils.log
|
||||
if [ "$RC" -ne 0 ]; then
|
||||
log "netbird up failed (rc=$RC): $OUT"
|
||||
else
|
||||
log "netbird up succeeded for profile '$PROFILE'."
|
||||
fi
|
||||
|
||||
@@ -65,18 +65,31 @@ stop_netbird() {
|
||||
log "Stopping netbird daemon."
|
||||
# Try graceful shutdown first via the daemon's own command
|
||||
"$DAEMON" down >/dev/null 2>&1
|
||||
# Then kill the service run process
|
||||
# Then signal the service run process
|
||||
if [ -f "$PIDFILE" ]; then
|
||||
kill "$(cat "$PIDFILE")" 2>/dev/null
|
||||
rm -f "$PIDFILE"
|
||||
fi
|
||||
/usr/bin/pkill -f "^${DAEMON} service run" 2>/dev/null
|
||||
/usr/bin/pkill -TERM -f "^${DAEMON} service run" 2>/dev/null
|
||||
|
||||
# SIGTERM can take a few seconds to take effect; wait for the process to
|
||||
# actually exit before returning, escalating to SIGKILL if it lingers.
|
||||
# Without this, a quick stop+start (restart) races: start sees the daemon
|
||||
# still running and becomes a no-op, so the restart silently doesn't happen.
|
||||
for _ in $(seq 1 20); do
|
||||
is_running || break
|
||||
sleep 0.5
|
||||
done
|
||||
if is_running ; then
|
||||
log "Daemon still running after SIGTERM; sending SIGKILL."
|
||||
/usr/bin/pkill -9 -f "^${DAEMON} service run" 2>/dev/null
|
||||
sleep 1
|
||||
fi
|
||||
rm -f /var/run/netbird.sock
|
||||
}
|
||||
|
||||
restart_netbird() {
|
||||
stop_netbird
|
||||
sleep 1
|
||||
start_netbird
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user