6 Commits

Author SHA1 Message Date
RTechSn
0c726cacbf Fix Generate Configuration (#36)
* Fix "generate client configuration"

* Add qrencode dependency

* Added client IP and DNS settings in client config generator

* Added Download Config button

* fix
2025-06-26 01:34:20 +07:00
EugeneTM
d91b8929de fix var names in config import (#35)
Исправлены названия переменных для корректного импорта текстовой конфигурации
2025-03-27 21:21:14 +07:00
Slava-Shchipunov
33e363e9d9 feat: disable sync owrt releases 2025-03-13 09:24:17 +07:00
Slava-Shchipunov
3d865a8d4d feat: disable build to snapshots 2025-03-13 09:22:29 +07:00
Slava-Shchipunov
e285146833 docs: fix script link 2024-10-27 20:30:00 +07:00
Slava-Shchipunov
1dd92441aa Feat/add install script (#31)
* feat: add awg install script

* fix: replace curl with wget

* fix: add opkg update run

* feat: add firewall settings

* fix: fix allowed_ips set

* fix: add timeout

* fix: fix peer create

* docs: update readme
2024-10-26 04:36:27 +07:00
6 changed files with 334 additions and 44 deletions

View File

@@ -7,8 +7,8 @@ on:
push:
tags:
- "SNAPSHOT"
schedule:
- cron: '0 */4 * * *'
# schedule:
# - cron: '0 */4 * * *'
workflow_dispatch:
jobs:

View File

@@ -1,8 +1,8 @@
name: Sync OpenWRT Releases
on:
schedule:
- cron: '0 0 */3 * *' # Проверка новых релизов раз в три дня
# schedule:
# - cron: '0 0 */3 * *' # Проверка новых релизов раз в три дня
workflow_dispatch: # Возможность вручную запустить Action
jobs:
@@ -63,4 +63,4 @@ jobs:
if: needs.sync-releases.outputs.release_exists == 'false'
uses: Slava-Shchipunov/awg-openwrt/.github/workflows/build-module.yml@master
with:
tag_name: ${{ needs.sync-releases.outputs.release_tag }}
tag_name: ${{ needs.sync-releases.outputs.release_tag }}

View File

@@ -1,6 +1,12 @@
# Автоматическая настройка AmneziaWG для OpenWRT версии 23.05.0 и более новых
Для автоматической настройки рекомендую использовать [скрипт](https://github.com/itdoginfo/domain-routing-openwrt) от пользователя itdog. Этот скрипт позволяет автоматически скачать нужные пакеты из собранных здесь и настроить [точечный обход блокировок по доменам](https://habr.com/ru/articles/767464/).
Если же вам нужно только установить пакеты, я добавил скрипт amneziawg-install - он автоматически скачает пакеты из этого репозитория под ваше устройство (только для стабильной версии OpenWRT), а также предложит сразу настроить интерфейс с протоколом AmneziaWG. Если пользователь согласится, нужно будет ввести параметры конфига, которые запросит скрипт. При этом скрипт создаст интерфейс, настроит для него правила фаерволла, а также **включит перенаправление всего траффика через тунель AmneziaWG** (установит в настройках Peer галочку Route Allowed IPs).
Для запуска скрипта подключитесь к роутеру по SSH, введите команду и следуйте инструкциям на экране:
```
sh <(wget -O - https://raw.githubusercontent.com/Slava-Shchipunov/awg-openwrt/refs/heads/master/amneziawg-install.sh)
```
# Сборка пакетов для всех устройств, поддерживающих OpenWRT
В репозиторий добавлен скрипт, который парсит данные о поддерживаемых платформах со страницы OpenWRT и автоматически запускает сборку пакетов AmneziaWG для всех устройств.
На данный момент я собрал пакеты для всех устройств для OpenWRT версий:
@@ -9,6 +15,7 @@
3) [23.05.2](https://github.com/Slava-Shchipunov/awg-openwrt/releases/tag/v23.05.2)
4) [23.05.3](https://github.com/Slava-Shchipunov/awg-openwrt/releases/tag/v23.05.3)
5) [23.05.4](https://github.com/Slava-Shchipunov/awg-openwrt/releases/tag/v23.05.4)
6) [23.05.5](https://github.com/Slava-Shchipunov/awg-openwrt/releases/tag/v23.05.5)
И собрал пакеты для популярных устройств для OpenWRT [SNAPSHOT](https://github.com/Slava-Shchipunov/awg-openwrt/releases/tag/SNAPSHOT)
@@ -41,6 +48,12 @@
# Automatic configuration of AmneziaWG for OpenWRT version 23.05.0 and newer
For automatic configuration, I recommend using the [script](https://github.com/itdoginfo/domain-routing-openwrt) from the user itdog. This script allows you to automatically download the necessary packages from those collected here and configure [point-by-point bypass of blocking by domains](https://habr.com/ru/articles/767464/) (instructions in Russian).
If you only need to install packages, I added the amneziawg-install script - it will automatically download packages from this repository for your device (only for the stable version of OpenWRT), and also offer to immediately configure the interface with the AmneziaWG protocol. If the user agrees, you will need to enter the config parameters that the script will request. The script will create an interface, configure firewall rules for it, and also **enable redirection of all traffic through the AmneziaWG tunnel** (check the Route Allowed IPs box in the Peer settings).
To run the script, connect to the router via SSH, enter the command and follow the instructions on the screen:
```
sh <(wget -O - https://raw.githubusercontent.com/Slava-Shchipunov/awg-openwrt/refs/heads/master/amneziawg-install.sh)
```
# Building packages for all devices that support OpenWRT
A script has been added to the repository that parses data on supported platforms from the OpenWRT page and automatically starts building AmneziaWG packages for all devices.
At the moment I have collected packages for all devices for OpenWRT versions:

207
amneziawg-install.sh Normal file
View File

@@ -0,0 +1,207 @@
#!/bin/sh
#set -x
#Репозиторий OpenWRT должен быть доступен для установки зависимостей пакета kmod-amneziawg
check_repo() {
printf "\033[32;1mChecking OpenWrt repo availability...\033[0m\n"
opkg update | grep -q "Failed to download" && printf "\033[32;1mopkg failed. Check internet or date. Command for force ntp sync: ntpd -p ptbtime1.ptb.de\033[0m\n" && exit 1
}
install_awg_packages() {
# Получение pkgarch с наибольшим приоритетом
PKGARCH=$(opkg print-architecture | awk 'BEGIN {max=0} {if ($3 > max) {max = $3; arch = $2}} END {print arch}')
TARGET=$(ubus call system board | jsonfilter -e '@.release.target' | cut -d '/' -f 1)
SUBTARGET=$(ubus call system board | jsonfilter -e '@.release.target' | cut -d '/' -f 2)
VERSION=$(ubus call system board | jsonfilter -e '@.release.version')
PKGPOSTFIX="_v${VERSION}_${PKGARCH}_${TARGET}_${SUBTARGET}.ipk"
BASE_URL="https://github.com/Slava-Shchipunov/awg-openwrt/releases/download/"
AWG_DIR="/tmp/amneziawg"
mkdir -p "$AWG_DIR"
if opkg list-installed | grep -q kmod-amneziawg; then
echo "kmod-amneziawg already installed"
else
KMOD_AMNEZIAWG_FILENAME="kmod-amneziawg${PKGPOSTFIX}"
DOWNLOAD_URL="${BASE_URL}v${VERSION}/${KMOD_AMNEZIAWG_FILENAME}"
wget -O "$AWG_DIR/$KMOD_AMNEZIAWG_FILENAME" "$DOWNLOAD_URL"
if [ $? -eq 0 ]; then
echo "kmod-amneziawg file downloaded successfully"
else
echo "Error downloading kmod-amneziawg. Please, install kmod-amneziawg manually and run the script again"
exit 1
fi
opkg install "$AWG_DIR/$KMOD_AMNEZIAWG_FILENAME"
if [ $? -eq 0 ]; then
echo "kmod-amneziawg file downloaded successfully"
else
echo "Error installing kmod-amneziawg. Please, install kmod-amneziawg manually and run the script again"
exit 1
fi
fi
if opkg list-installed | grep -q amneziawg-tools; then
echo "amneziawg-tools already installed"
else
AMNEZIAWG_TOOLS_FILENAME="amneziawg-tools${PKGPOSTFIX}"
DOWNLOAD_URL="${BASE_URL}v${VERSION}/${AMNEZIAWG_TOOLS_FILENAME}"
wget -O "$AWG_DIR/$AMNEZIAWG_TOOLS_FILENAME" "$DOWNLOAD_URL"
if [ $? -eq 0 ]; then
echo "amneziawg-tools file downloaded successfully"
else
echo "Error downloading amneziawg-tools. Please, install amneziawg-tools manually and run the script again"
exit 1
fi
opkg install "$AWG_DIR/$AMNEZIAWG_TOOLS_FILENAME"
if [ $? -eq 0 ]; then
echo "amneziawg-tools file downloaded successfully"
else
echo "Error installing amneziawg-tools. Please, install amneziawg-tools manually and run the script again"
exit 1
fi
fi
if opkg list-installed | grep -q luci-app-amneziawg; then
echo "luci-app-amneziawg already installed"
else
LUCI_APP_AMNEZIAWG_FILENAME="luci-app-amneziawg${PKGPOSTFIX}"
DOWNLOAD_URL="${BASE_URL}v${VERSION}/${LUCI_APP_AMNEZIAWG_FILENAME}"
wget -O "$AWG_DIR/$LUCI_APP_AMNEZIAWG_FILENAME" "$DOWNLOAD_URL"
if [ $? -eq 0 ]; then
echo "luci-app-amneziawg file downloaded successfully"
else
echo "Error downloading luci-app-amneziawg. Please, install luci-app-amneziawg manually and run the script again"
exit 1
fi
opkg install "$AWG_DIR/$LUCI_APP_AMNEZIAWG_FILENAME"
if [ $? -eq 0 ]; then
echo "luci-app-amneziawg file downloaded successfully"
else
echo "Error installing luci-app-amneziawg. Please, install luci-app-amneziawg manually and run the script again"
exit 1
fi
fi
rm -rf "$AWG_DIR"
}
configure_amneziawg_interface() {
INTERFACE_NAME="awg1"
CONFIG_NAME="amneziawg_awg1"
PROTO="amneziawg"
ZONE_NAME="awg1"
read -r -p "Enter the private key (from [Interface]):"$'\n' AWG_PRIVATE_KEY_INT
while true; do
read -r -p "Enter internal IP address with subnet, example 192.168.100.5/24 (from [Interface]):"$'\n' AWG_IP
if echo "$AWG_IP" | egrep -oq '^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]+$'; then
break
else
echo "This IP is not valid. Please repeat"
fi
done
read -r -p "Enter the public key (from [Peer]):"$'\n' AWG_PUBLIC_KEY_INT
read -r -p "If use PresharedKey, Enter this (from [Peer]). If your don't use leave blank:"$'\n' AWG_PRESHARED_KEY_INT
read -r -p "Enter Endpoint host without port (Domain or IP) (from [Peer]):"$'\n' AWG_ENDPOINT_INT
read -r -p "Enter Endpoint host port (from [Peer]) [51820]:"$'\n' AWG_ENDPOINT_PORT_INT
AWG_ENDPOINT_PORT_INT=${AWG_ENDPOINT_PORT_INT:-51820}
if [ "$AWG_ENDPOINT_PORT_INT" = '51820' ]; then
echo $AWG_ENDPOINT_PORT_INT
fi
read -r -p "Enter Jc value (from [Interface]):"$'\n' AWG_JC
read -r -p "Enter Jmin value (from [Interface]):"$'\n' AWG_JMIN
read -r -p "Enter Jmax value (from [Interface]):"$'\n' AWG_JMAX
read -r -p "Enter S1 value (from [Interface]):"$'\n' AWG_S1
read -r -p "Enter S2 value (from [Interface]):"$'\n' AWG_S2
read -r -p "Enter H1 value (from [Interface]):"$'\n' AWG_H1
read -r -p "Enter H2 value (from [Interface]):"$'\n' AWG_H2
read -r -p "Enter H3 value (from [Interface]):"$'\n' AWG_H3
read -r -p "Enter H4 value (from [Interface]):"$'\n' AWG_H4
uci set network.${INTERFACE_NAME}=interface
uci set network.${INTERFACE_NAME}.proto=$PROTO
uci set network.${INTERFACE_NAME}.private_key=$AWG_PRIVATE_KEY_INT
uci set network.${INTERFACE_NAME}.listen_port='51821'
uci set network.${INTERFACE_NAME}.addresses=$AWG_IP
uci set network.${INTERFACE_NAME}.awg_jc=$AWG_JC
uci set network.${INTERFACE_NAME}.awg_jmin=$AWG_JMIN
uci set network.${INTERFACE_NAME}.awg_jmax=$AWG_JMAX
uci set network.${INTERFACE_NAME}.awg_s1=$AWG_S1
uci set network.${INTERFACE_NAME}.awg_s2=$AWG_S2
uci set network.${INTERFACE_NAME}.awg_h1=$AWG_H1
uci set network.${INTERFACE_NAME}.awg_h2=$AWG_H2
uci set network.${INTERFACE_NAME}.awg_h3=$AWG_H3
uci set network.${INTERFACE_NAME}.awg_h4=$AWG_H4
if ! uci show network | grep -q ${CONFIG_NAME}; then
uci add network ${CONFIG_NAME}
fi
uci set network.@${CONFIG_NAME}[0]=$CONFIG_NAME
uci set network.@${CONFIG_NAME}[0].name="${INTERFACE_NAME}_client"
uci set network.@${CONFIG_NAME}[0].public_key=$AWG_PUBLIC_KEY_INT
uci set network.@${CONFIG_NAME}[0].preshared_key=$AWG_PRESHARED_KEY_INT
uci set network.@${CONFIG_NAME}[0].route_allowed_ips='1'
uci set network.@${CONFIG_NAME}[0].persistent_keepalive='25'
uci set network.@${CONFIG_NAME}[0].endpoint_host=$AWG_ENDPOINT_INT
uci set network.@${CONFIG_NAME}[0].allowed_ips='0.0.0.0/0'
uci add_list network.@${CONFIG_NAME}[0].allowed_ips='::/0'
uci set network.@${CONFIG_NAME}[0].endpoint_port=$AWG_ENDPOINT_PORT_INT
uci commit network
if ! uci show firewall | grep -q "@zone.*name='${ZONE_NAME}'"; then
printf "\033[32;1mZone Create\033[0m\n"
uci add firewall zone
uci set firewall.@zone[-1].name=$ZONE_NAME
uci set firewall.@zone[-1].network=$INTERFACE_NAME
uci set firewall.@zone[-1].forward='REJECT'
uci set firewall.@zone[-1].output='ACCEPT'
uci set firewall.@zone[-1].input='REJECT'
uci set firewall.@zone[-1].masq='1'
uci set firewall.@zone[-1].mtu_fix='1'
uci set firewall.@zone[-1].family='ipv4'
uci commit firewall
fi
if ! uci show firewall | grep -q "@forwarding.*name='${ZONE_NAME}'"; then
printf "\033[32;1mConfigured forwarding\033[0m\n"
uci add firewall forwarding
uci set firewall.@forwarding[-1]=forwarding
uci set firewall.@forwarding[-1].name="${ZONE_NAME}-lan"
uci set firewall.@forwarding[-1].dest=${ZONE_NAME}
uci set firewall.@forwarding[-1].src='lan'
uci set firewall.@forwarding[-1].family='ipv4'
uci commit firewall
fi
}
check_repo
install_awg_packages
printf "\033[32;1mDo you want to configure the amneziawg interface? (y/n): \033[0m\n"
read IS_SHOULD_CONFIGURE_AWG_INTERFACE
if [ "$IS_SHOULD_CONFIGURE_AWG_INTERFACE" = "y" ] || [ "$IS_SHOULD_CONFIGURE_AWG_INTERFACE" = "Y" ]; then
configure_amneziawg_interface
else
printf "\033[32;1mSkipping amneziawg interface configuration.\033[0m\n"
fi
service network restart

View File

@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Support for AmneziaWG VPN
LUCI_DEPENDS:=+amneziawg-tools +ucode
LUCI_DEPENDS:=+amneziawg-tools +ucode +qrencode
LUCI_PKGARCH:=all
PKG_PROVIDES:=luci-app-amneziawg

View File

@@ -64,15 +64,29 @@ function generateDescription(name, texts) {
]);
}
function invokeQREncode(data, code) {
function invokeQREncode(data, div) {
var code = div.children[0];
var btn = div.children[1];
dom.content(btn, [
E('a', {
'class': 'btn cbi-button-action',
'style': 'text-align: center',
'href': 'data:text/plain;charset=utf-8,' + encodeURIComponent(data),
'download': 'amneziawg.conf'
}, ['Download Configuration']),
]);
return fs.exec_direct('/usr/bin/qrencode', [
'--inline', '--8bit', '--type=SVG',
'--output=-', '--', data
]).then(function(svg) {
code.style.opacity = '';
div.style.opacity = '';
dom.content(code, Object.assign(E(svg), { style: 'width:100%;height:auto' }));
}).catch(function(error) {
code.style.opacity = '';
div.style.opacity = '';
if (L.isObject(error) && error.name == 'NotFoundError') {
dom.content(code, [
@@ -402,15 +416,15 @@ return network.registerProtocol('amneziawg', {
s.getOption('public_key').getUIElement(s.section).setValue(keypair.pub);
s.getOption('listen_port').getUIElement(s.section).setValue(config.interface_listenport || '');
s.getOption('addresses').getUIElement(s.section).setValue(config.interface_address);
s.getOption('awg_jc').getUIElement(s.section).setValue(config.awg_jc);
s.getOption('awg_jmin').getUIElement(s.section).setValue(config.awg_jmin);
s.getOption('awg_jmax').getUIElement(s.section).setValue(config.awg_jmax);
s.getOption('awg_s1').getUIElement(s.section).setValue(config.awg_s1);
s.getOption('awg_s2').getUIElement(s.section).setValue(config.awg_s2);
s.getOption('awg_h1').getUIElement(s.section).setValue(config.awg_h1);
s.getOption('awg_h2').getUIElement(s.section).setValue(config.awg_h2);
s.getOption('awg_h3').getUIElement(s.section).setValue(config.awg_h3);
s.getOption('awg_h4').getUIElement(s.section).setValue(config.awg_h4);
s.getOption('awg_jc').getUIElement(s.section).setValue(config.interface_jc);
s.getOption('awg_jmin').getUIElement(s.section).setValue(config.interface_jmin);
s.getOption('awg_jmax').getUIElement(s.section).setValue(config.interface_jmax);
s.getOption('awg_s1').getUIElement(s.section).setValue(config.interface_s1);
s.getOption('awg_s2').getUIElement(s.section).setValue(config.interface_s2);
s.getOption('awg_h1').getUIElement(s.section).setValue(config.interface_h1);
s.getOption('awg_h2').getUIElement(s.section).setValue(config.interface_h2);
s.getOption('awg_h3').getUIElement(s.section).setValue(config.interface_h3);
s.getOption('awg_h4').getUIElement(s.section).setValue(config.interface_h4);
if (config.interface_dns)
s.getOption('dns').getUIElement(s.section).setValue(config.interface_dns);
@@ -743,39 +757,65 @@ return network.registerProtocol('amneziawg', {
o.modalonly = true;
o.createPeerConfig = function(section_id, endpoint, ips) {
o.createPeerConfig = function (section_id, endpoint, ips, eips, dns) {
var pub = s.formvalue(s.section, 'public_key'),
port = s.formvalue(s.section, 'listen_port') || '51820',
jc = s.formvalue
prv = this.section.formvalue(section_id, 'private_key'),
psk = this.section.formvalue(section_id, 'preshared_key'),
eport = this.section.formvalue(section_id, 'endpoint_port'),
keep = this.section.formvalue(section_id, 'persistent_keepalive');
// If endpoint is IPv6 we must escape it with []
port = s.formvalue(s.section, 'listen_port') || '51820',
prv = this.section.formvalue(section_id, 'private_key'),
psk = this.section.formvalue(section_id, 'preshared_key'),
eport = this.section.formvalue(section_id, 'endpoint_port'),
keep = this.section.formvalue(section_id, 'persistent_keepalive'),
jc = s.formvalue(s.section, 'awg_jc'),
jmin = s.formvalue(s.section, 'awg_jmin'),
jmax = s.formvalue(s.section, 'awg_jmax'),
s1 = s.formvalue(s.section, 'awg_s1'),
s2 = s.formvalue(s.section, 'awg_s2'),
h1 = s.formvalue(s.section, 'awg_h1'),
h2 = s.formvalue(s.section, 'awg_h2'),
h3 = s.formvalue(s.section, 'awg_h3'),
h4 = s.formvalue(s.section, 'awg_h4');
if (endpoint.indexOf(':') > 0) {
endpoint = '['+endpoint+']';
endpoint = '[' + endpoint + ']';
}
return [
var configLines = [
'[Interface]',
'PrivateKey = ' + prv,
eport ? 'ListenPort = ' + eport : '# ListenPort not defined',
eips && eips.length ? 'Address = ' + eips.join(', ') : '# Address not defined',
dns && dns.length ? 'DNS = ' + dns.join(', ') : '# DNS not defined',
''
];
if (jc) configLines.push('jc = ' + jc);
if (jmin) configLines.push('jmin = ' + jmin);
if (jmax) configLines.push('jmax = ' + jmax);
if (s1) configLines.push('s1 = ' + s1);
if (s2) configLines.push('s2 = ' + s2);
if (h1) configLines.push('h1 = ' + h1);
if (h2) configLines.push('h2 = ' + h2);
if (h3) configLines.push('h3 = ' + h3);
if (h4) configLines.push('h4 = ' + h4);
configLines.push(
'',
'[Peer]',
'PublicKey = ' + pub,
psk ? 'PresharedKey = ' + psk : '# PresharedKey not used',
ips && ips.length ? 'AllowedIPs = ' + ips.join(', ') : '# AllowedIPs not defined',
endpoint ? 'Endpoint = ' + endpoint + ':' + port : '# Endpoint not defined',
keep ? 'PersistentKeepAlive = ' + keep : '# PersistentKeepAlive not defined'
].join('\n');
};
keep ? 'PersistentKeepAlive = ' + keep : '# PersistentKeepAlive not defined',
''
);
return configLines.join('\n');
};
o.handleGenerateQR = function(section_id, ev) {
var mapNode = ss.getActiveModalMap(),
headNode = mapNode.parentNode.querySelector('h4'),
configGenerator = this.createPeerConfig.bind(this, section_id),
parent = this.map;
headNode = mapNode.parentNode.querySelector('h4'),
configGenerator = this.createPeerConfig.bind(this, section_id),
parent = this.map,
eips = this.section.formvalue(section_id, 'allowed_ips');
return Promise.all([
network.getWANNetworks(),
@@ -804,21 +844,33 @@ return network.registerProtocol('amneziawg', {
var ips = [ '0.0.0.0/0', '::/0' ];
var dns = [];
var qrm, qrs, qro;
qrm = new form.JSONMap({ config: { endpoint: hostnames[0], allowed_ips: ips } }, null, _('The generated configuration can be imported into a AmneziaWG client application to set up a connection towards this device.'));
qrm = new form.JSONMap({
config: {
endpoint: hostnames[0],
allowed_ips: ips,
addresses: eips,
dns_servers: dns
}
}, null, _('The generated configuration can be imported into a AmneziaWG client application to set up a connection towards this device.'));
qrm.parent = parent;
qrs = qrm.section(form.NamedSection, 'config');
function handleConfigChange(ev, section_id, value) {
var code = this.map.findElement('.qr-code'),
conf = this.map.findElement('.client-config'),
endpoint = this.section.getUIElement(section_id, 'endpoint'),
ips = this.section.getUIElement(section_id, 'allowed_ips');
conf = this.map.findElement('.client-config'),
endpoint = this.section.getUIElement(section_id, 'endpoint'),
ips = this.section.getUIElement(section_id, 'allowed_ips');
eips = this.section.getUIElement(section_id, 'addresses');
dns = this.section.getUIElement(section_id, 'dns_servers');
if (this.isValid(section_id)) {
conf.firstChild.data = configGenerator(endpoint.getValue(), ips.getValue());
conf.firstChild.data = configGenerator(endpoint.getValue(), ips.getValue(), eips.getValue(), dns.getValue());
code.style.opacity = '.5';
invokeQREncode(conf.firstChild.data, code);
@@ -836,18 +888,36 @@ return network.registerProtocol('amneziawg', {
ips.forEach(function(ip) { qro.value(ip) });
qro.onchange = handleConfigChange;
qro = qrs.option(form.DynamicList, 'dns_servers', _('DNS Servers'), _('DNS servers for the remote clients using this tunnel to your openwrt device. Some wireguard clients require this to be set.'));
qro.datatype = 'ipaddr';
qro.default = dns;
qro.onchange = handleConfigChange;
qro = qrs.option(form.DynamicList, 'addresses', _('Addresses'), _('IP addresses for the peer to use inside the tunnel. Some clients require this setting.'));
qro.datatype = 'ipaddr';
qro.default = eips;
eips.forEach(function(eip) { qro.value(eip) });
qro.onchange = handleConfigChange;
qro = qrs.option(form.DummyValue, 'output');
qro.renderWidget = function() {
var peer_config = configGenerator(hostnames[0], ips);
var peer_config = configGenerator(hostnames[0], ips, eips, dns);
var node = E('div', {
'style': 'display:flex;flex-wrap:wrap;align-items:center;gap:.5em;width:100%'
}, [
E('div', {
'class': 'qr-code',
'style': 'width:320px;flex:0 1 320px;text-align:center'
'style': 'display:flex; flex-direction: column; text-align: center',
}, [
E('em', { 'class': 'spinning' }, [ _('Generating QR code…') ])
E('div', {
'style': 'width:320px;flex:0 1 320px;text-align:center'
}, [
E('em', { 'class': 'spinning' }, [ _('Generating QR code…') ])
]),
E('div', {
}, ['Download Configuration']),
]),
E('pre', {
'class': 'client-config',