1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 00:31:58 +00:00

[wireguard] Store configuration strings in flash instead of heap

This commit is contained in:
J. Nick Koston
2026-01-17 13:31:02 -10:00
parent e4fb6988ff
commit e17602c386
3 changed files with 91 additions and 56 deletions

View File

@@ -30,6 +30,7 @@ _WG_KEY_REGEX = re.compile(r"^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw480]=$")
wireguard_ns = cg.esphome_ns.namespace("wireguard")
Wireguard = wireguard_ns.class_("Wireguard", cg.Component, cg.PollingComponent)
AllowedIP = wireguard_ns.struct("AllowedIP")
WireguardPeerOnlineCondition = wireguard_ns.class_(
"WireguardPeerOnlineCondition", automation.Condition
)
@@ -108,8 +109,18 @@ async def to_code(config):
)
)
for ip in allowed_ips:
cg.add(var.add_allowed_ip(str(ip.network_address), str(ip.netmask)))
cg.add(
var.set_allowed_ips(
[
cg.StructInitializer(
AllowedIP,
("ip", str(ip.network_address)),
("netmask", str(ip.netmask)),
)
for ip in allowed_ips
]
)
)
cg.add(var.set_srctime(await cg.get_variable(config[CONF_TIME_ID])))

View File

@@ -13,8 +13,7 @@
#include <esp_wireguard.h>
#include <esp_wireguard_err.h>
namespace esphome {
namespace wireguard {
namespace esphome::wireguard {
static const char *const TAG = "wireguard";
@@ -28,16 +27,16 @@ static const char *const LOGMSG_ONLINE = "online";
static const char *const LOGMSG_OFFLINE = "offline";
void Wireguard::setup() {
this->wg_config_.address = this->address_.c_str();
this->wg_config_.private_key = this->private_key_.c_str();
this->wg_config_.endpoint = this->peer_endpoint_.c_str();
this->wg_config_.public_key = this->peer_public_key_.c_str();
this->wg_config_.address = this->address_;
this->wg_config_.private_key = this->private_key_;
this->wg_config_.endpoint = this->peer_endpoint_;
this->wg_config_.public_key = this->peer_public_key_;
this->wg_config_.port = this->peer_port_;
this->wg_config_.netmask = this->netmask_.c_str();
this->wg_config_.netmask = this->netmask_;
this->wg_config_.persistent_keepalive = this->keepalive_;
if (!this->preshared_key_.empty())
this->wg_config_.preshared_key = this->preshared_key_.c_str();
if (this->preshared_key_ != nullptr)
this->wg_config_.preshared_key = this->preshared_key_;
this->publish_enabled_state();
@@ -131,6 +130,10 @@ void Wireguard::update() {
}
void Wireguard::dump_config() {
char private_key_masked[MASK_KEY_BUFFER_SIZE];
char preshared_key_masked[MASK_KEY_BUFFER_SIZE];
mask_key_to(private_key_masked, sizeof(private_key_masked), this->private_key_);
mask_key_to(preshared_key_masked, sizeof(preshared_key_masked), this->preshared_key_);
// clang-format off
ESP_LOGCONFIG(
TAG,
@@ -142,13 +145,13 @@ void Wireguard::dump_config() {
" Peer Port: " LOG_SECRET("%d") "\n"
" Peer Public Key: " LOG_SECRET("%s") "\n"
" Peer Pre-shared Key: " LOG_SECRET("%s"),
this->address_.c_str(), this->netmask_.c_str(), mask_key(this->private_key_).c_str(),
this->peer_endpoint_.c_str(), this->peer_port_, this->peer_public_key_.c_str(),
(!this->preshared_key_.empty() ? mask_key(this->preshared_key_).c_str() : "NOT IN USE"));
this->address_, this->netmask_, private_key_masked,
this->peer_endpoint_, this->peer_port_, this->peer_public_key_,
(this->preshared_key_ != nullptr ? preshared_key_masked : "NOT IN USE"));
// clang-format on
ESP_LOGCONFIG(TAG, " Peer Allowed IPs:");
for (auto &allowed_ip : this->allowed_ips_) {
ESP_LOGCONFIG(TAG, " - %s/%s", std::get<0>(allowed_ip).c_str(), std::get<1>(allowed_ip).c_str());
for (const AllowedIP &allowed_ip : this->allowed_ips_) {
ESP_LOGCONFIG(TAG, " - %s/%s", allowed_ip.ip, allowed_ip.netmask);
}
ESP_LOGCONFIG(TAG, " Peer Persistent Keepalive: %d%s", this->keepalive_,
(this->keepalive_ > 0 ? "s" : " (DISABLED)"));
@@ -176,18 +179,6 @@ time_t Wireguard::get_latest_handshake() const {
return result;
}
void Wireguard::set_address(const std::string &address) { this->address_ = address; }
void Wireguard::set_netmask(const std::string &netmask) { this->netmask_ = netmask; }
void Wireguard::set_private_key(const std::string &key) { this->private_key_ = key; }
void Wireguard::set_peer_endpoint(const std::string &endpoint) { this->peer_endpoint_ = endpoint; }
void Wireguard::set_peer_public_key(const std::string &key) { this->peer_public_key_ = key; }
void Wireguard::set_peer_port(const uint16_t port) { this->peer_port_ = port; }
void Wireguard::set_preshared_key(const std::string &key) { this->preshared_key_ = key; }
void Wireguard::add_allowed_ip(const std::string &ip, const std::string &netmask) {
this->allowed_ips_.emplace_back(ip, netmask);
}
void Wireguard::set_keepalive(const uint16_t seconds) { this->keepalive_ = seconds; }
void Wireguard::set_reboot_timeout(const uint32_t seconds) { this->reboot_timeout_ = seconds; }
void Wireguard::set_srctime(time::RealTimeClock *srctime) { this->srctime_ = srctime; }
@@ -274,9 +265,8 @@ void Wireguard::start_connection_() {
ESP_LOGD(TAG, "Configuring allowed IPs list");
bool allowed_ips_ok = true;
for (std::tuple<std::string, std::string> ip : this->allowed_ips_) {
allowed_ips_ok &=
(esp_wireguard_add_allowed_ip(&(this->wg_ctx_), std::get<0>(ip).c_str(), std::get<1>(ip).c_str()) == ESP_OK);
for (const AllowedIP &ip : this->allowed_ips_) {
allowed_ips_ok &= (esp_wireguard_add_allowed_ip(&(this->wg_ctx_), ip.ip, ip.netmask) == ESP_OK);
}
if (allowed_ips_ok) {
@@ -299,8 +289,25 @@ void Wireguard::stop_connection_() {
}
}
std::string mask_key(const std::string &key) { return (key.substr(0, 5) + "[...]="); }
void mask_key_to(char *buffer, size_t len, const char *key) {
// Format: "XXXXX[...]=\0" = MASK_KEY_BUFFER_SIZE chars minimum
if (len < MASK_KEY_BUFFER_SIZE || key == nullptr) {
if (len > 0)
buffer[0] = '\0';
return;
}
// Copy first 5 characters of the key
size_t i = 0;
for (; i < 5 && key[i] != '\0'; ++i) {
buffer[i] = key[i];
}
// Append "[...]="
const char *suffix = "[...]=";
for (size_t j = 0; suffix[j] != '\0' && (i + j) < len - 1; ++j) {
buffer[i + j] = suffix[j];
}
buffer[i + 6] = '\0';
}
} // namespace wireguard
} // namespace esphome
} // namespace esphome::wireguard
#endif

View File

@@ -2,10 +2,10 @@
#include "esphome/core/defines.h"
#ifdef USE_WIREGUARD
#include <ctime>
#include <vector>
#include <tuple>
#include <initializer_list>
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/time/real_time_clock.h"
#ifdef USE_BINARY_SENSOR
@@ -22,8 +22,13 @@
#include <esp_wireguard.h>
namespace esphome {
namespace wireguard {
namespace esphome::wireguard {
/// Allowed IP entry for WireGuard peer configuration.
struct AllowedIP {
const char *ip;
const char *netmask;
};
/// Main Wireguard component class.
class Wireguard : public PollingComponent {
@@ -37,15 +42,25 @@ class Wireguard : public PollingComponent {
float get_setup_priority() const override { return esphome::setup_priority::BEFORE_CONNECTION; }
void set_address(const std::string &address);
void set_netmask(const std::string &netmask);
void set_private_key(const std::string &key);
void set_peer_endpoint(const std::string &endpoint);
void set_peer_public_key(const std::string &key);
void set_peer_port(uint16_t port);
void set_preshared_key(const std::string &key);
void set_address(const char *address) { this->address_ = address; }
void set_netmask(const char *netmask) { this->netmask_ = netmask; }
void set_private_key(const char *key) { this->private_key_ = key; }
void set_peer_endpoint(const char *endpoint) { this->peer_endpoint_ = endpoint; }
void set_peer_public_key(const char *key) { this->peer_public_key_ = key; }
void set_peer_port(uint16_t port) { this->peer_port_ = port; }
void set_preshared_key(const char *key) { this->preshared_key_ = key; }
void add_allowed_ip(const std::string &ip, const std::string &netmask);
/// Prevent accidental use of std::string which would dangle
void set_address(const std::string &address) = delete;
void set_netmask(const std::string &netmask) = delete;
void set_private_key(const std::string &key) = delete;
void set_peer_endpoint(const std::string &endpoint) = delete;
void set_peer_public_key(const std::string &key) = delete;
void set_preshared_key(const std::string &key) = delete;
void set_allowed_ips(std::initializer_list<AllowedIP> ips) { this->allowed_ips_ = ips; }
/// Prevent accidental use of std::string which would dangle
void set_allowed_ips(std::initializer_list<std::tuple<std::string, std::string>> ips) = delete;
void set_keepalive(uint16_t seconds);
void set_reboot_timeout(uint32_t seconds);
@@ -83,14 +98,14 @@ class Wireguard : public PollingComponent {
time_t get_latest_handshake() const;
protected:
std::string address_;
std::string netmask_;
std::string private_key_;
std::string peer_endpoint_;
std::string peer_public_key_;
std::string preshared_key_;
const char *address_{nullptr};
const char *netmask_{nullptr};
const char *private_key_{nullptr};
const char *peer_endpoint_{nullptr};
const char *peer_public_key_{nullptr};
const char *preshared_key_{nullptr};
std::vector<std::tuple<std::string, std::string>> allowed_ips_;
FixedVector<AllowedIP> allowed_ips_;
uint16_t peer_port_;
uint16_t keepalive_;
@@ -142,8 +157,11 @@ class Wireguard : public PollingComponent {
void suspend_wdt();
void resume_wdt();
/// Size of buffer required for mask_key_to: 5 chars + "[...]=" + null = 12
static constexpr size_t MASK_KEY_BUFFER_SIZE = 12;
/// Strip most part of the key only for secure printing
std::string mask_key(const std::string &key);
void mask_key_to(char *buffer, size_t len, const char *key);
/// Condition to check if remote peer is online.
template<typename... Ts> class WireguardPeerOnlineCondition : public Condition<Ts...>, public Parented<Wireguard> {
@@ -169,6 +187,5 @@ template<typename... Ts> class WireguardDisableAction : public Action<Ts...>, pu
void play(const Ts &...x) override { this->parent_->disable(); }
};
} // namespace wireguard
} // namespace esphome
} // namespace esphome::wireguard
#endif