A small Ansible role to manage your OpenWRT devices. It will check for updates using the OpenWRT Firmware Selector for your device, and apply the latest version if yours doesn't have it installed.
You can also manage any packages and services you want to have installed or removed, as well as add an authorized SSH key for easy access.
This role will install python3-light
and some other small packages on the device when install cron
-jobs.
The amount of space needed is minimal, and most devices should be able to accomodate this just fine.
You will need to run this role from a playbook, and have gather_facts
enabled.
Otherwise gekmihesg.openwrt
will not be able to run Ansible tasks on OpenWRT.
Variable | type | default | example | description |
---|---|---|---|---|
openwrt_version |
string |
latest |
"SNAPSHOT" |
OpenWRT version to be installed. More information |
openwrt_platform |
string |
~ |
"realtek/rtl838x" |
OpenWRT platform for your device. Check OpenWRT Firmware Selector for your device. |
openwrt_id |
string |
~ |
"netgear_gs308t-v1" |
OpenWRT id for your device. Check OpenWRT Firmware Selector for your device. |
openwrt_version_upgrade_enabled |
boolean |
true |
true |
Enable option to upgrade OpenWRT version of the device |
openwrt_dns_validation_domain |
string |
"google.com" |
"github.com" |
FQDN used during name resolution test |
openwrt_dns_validation_server |
string |
"1.1.1.1" |
"8.8.8.8" |
DNS server added to resolv.conf after failed name resolution test. |
openwrt_disabled_services |
list |
[] |
["odhcpd", "dnsmasq"] |
List of services to be disabled on your device. |
openwrt_enabled_services |
list |
[] |
["acme", "uhttpd", "prometheus-node-exporter-lua"] |
List of services to be enabled on your device. |
openwrt_packages_upgrade_enabled |
boolean |
true |
true |
Enable option to upgrade all installed packages if possible |
openwrt_packages_upgrade_reboot_enabled |
boolean |
true |
true |
Reboot device after packages have been upgraded |
openwrt_packages_upgrade_excluded |
list |
[] |
["luci", "dawn"] |
Exclude certain packages from being upgraded |
openwrt_installed_packages |
list |
[] |
["acme", luci", "luci-app-acme", "wpad-wolfssl"] |
List of packages to be installed on your device. |
openwrt_uninstalled_packages |
list |
[] |
["wpad-basic-wolfssl"] |
List of packages to uninstalled on your device. |
openwrt_ssh_public_key |
string |
~ |
"ssh-rsa AAAAB3N...f6f+K8=" |
SSH public-key to be added to the list of Dropbear authored keys. |
openwrt_back_up_enabled |
boolean |
true |
true |
Create back-up of the OpenWRT configuration before updating. |
openwrt_back_up_local_dir |
string |
~ |
"~/Downloads" |
Directory on your local machine to store back-ups. |
openwrt_disk_resize_enabled |
boolean |
false |
false |
Enable option to resize to max available (e.g. SD-card) |
openwrt_cron_scripts_dir |
string |
/root/cron_scripts |
/root/cron_scripts |
Target location of any scripts to be executed by cron . |
openwrt_cron_jobs |
list[dict] |
[] |
[{"name":"Hello", "minute":"*", "hour":"*", "day":"*", "month":"*", "weekday":"*", "job":"echo 'Hello!'"}] |
List of cronjobs to be installed. |
Set to a specific version of OpenWRT to be installed on your device (e.g., "22.03.5"
).
Defaults to latest stable version of OpenWRT available (i.e., "latest"
).
Use "release_candidate"
to consider release candidate builds as latest.
Use "SNAPSHOT"
if there are no versioned builds for your device.
This role relies on the gekmihesg.openwrt
to allow managing OpenWRT
without having Python installed on the device.
My thanks to gekmihesg!
- hosts: openwrt_accesspoint
gather_facts: true
serial: 1
tasks:
- name: "[OpenWRT] - Import role"
ansible.builtin.import_role:
name: openwrt
vars:
openwrt_version: "latest"
openwrt_platform: "ramips/mt7621"
openwrt_id: "xiaomi_mi-router-ac2100"
openwrt_disabled_services:
- "odhcpd"
- "dnsmasq"
openwrt_enabled_services:
- "acme"
- "uhttpd"
- "prometheus-node-exporter-lua"
openwrt_installed_packages:
- acme
- ca-bundle
- coreutils-base64
- coreutils-sha1sum
- curl
- htop
- luaposix
- luci
- luci-app-acme
- luci-app-uhttpd
- luci-mod-rpc
- prometheus-node-exporter-lua
- prometheus-node-exporter-lua-openwrt
- uhttpd
- uhttpd-mod-ubus
openwrt_uninstalled_packages:
- "wpad-basic-wolfssl"
openwrt_ssh_public_key: ~
openwrt_cron_jobs:
- name: UMDNS_RESTART
minute: "*"
job: |
#!/bin/ash
# UMDNS has a tendency to crash; both UMDNS and DAWN need to be restarted
OPENWRT_LOGGER_TAG="umdns_check"
logger -t ${OPENWRT_LOGGER_TAG} "Checking if UMDNS is running as expected"
UMDNS_OUTPUT_LINES_AMOUNT="$(ubus call umdns browse | wc -l)"
if [[ "${UMDNS_OUTPUT_LINES_AMOUNT}" -lt 5 ]]; then
logger -p err -t ${OPENWRT_LOGGER_TAG} "Restarting UMDNS and DAWN"
/etc/init.d/umdns restart
/etc/init.d/dawn restart
else
logger -t ${OPENWRT_LOGGER_TAG} "Everything looks fine"
fi
The following (extensive) example shows how to use this role to manage an entire fleet of OpenWRT devices.
Click to show the example
By leveraging the lookup(ansible.builtin.varnames)
plugin we can assemble the lists we need as input for the role execution.
By concatenating lists ending with a certain string (e.g., *openwrt_installed_packages
) we can easily manage what gets installed in which (group of) device(s).
We only need to be mindful of not re-using variable names.
In this example all variables for a group have the prefix group_<group_name>_
to mitigate that issue.
- Every individual device gets their device configuration set in
inventory/hosts.yml
. - Defaults for all devices (e.g., installing
luci
) get set using theopenwrt
group variables ininventory/group_vars/openwrt.yaml
. - Defaults for device groups (e.g., access points) get set using the their respective group variables (e.g.,
inventory/group_vars/openwrt_access_point.yaml
)
Click to show all the example files
# inventory/hosts.yaml
---
all:
hosts:
switch1:
ansible_host: 10.0.0.2
openwrt:
id: netgear_gs308t-v1
platform: "realtek/rtl838x"
version: latest
switch2:
ansible_host: 10.0.0.3
openwrt:
id: netgear_gs308t-v1
platform: "realtek/rtl838x"
version: latest
ap1:
ansible_host: 10.0.0.4
openwrt:
id: xiaomi_ax3600
platform: "ipq807x/generic"
version: release_candidate
ap2:
ansible_host: 10.0.0.5
openwrt:
id: xiaomi_mi-router-ac2100
platform: "ramips/mt7621"
version: latest
children:
openwrt:
children:
openwrt_switch:
openwrt_accesspoint:
openwrt_switch:
hosts:
switch1:
switch2:
openwrt_accesspoint:
hosts:
ap1:
ap2:
# inventory/group_vars/openwrt.yaml
---
# Settings applicable to all devices
ansible_user: root
ansible_scp_extra_args: "-O"
group_openwrt_openwrt_enabled_services:
- uhttpd
group_openwrt_openwrt_disabled_services: []
group_openwrt_openwrt_installed_packages:
- acme
- luci
- luci-app-acme
- luci-app-nlbwmon
- luci-app-uhttpd
- uhttpd
- uhttpd-mod-ubus
group_openwrt_openwrt_uninstalled_packages: []
# inventory/group_vars/openwrt_accesspoint.yaml
---
# Settings applicable to access points only
group_access_point_openwrt_installed_packages:
- dawn
- luci-app-dawn
- wpad-wolfssl
group_access_point_openwrt_uninstalled_packages:
- wpad-basic-mbedtls
- wpad-basic-wolfssl
group_access_point_openwrt_enabled_services: []
group_access_point_openwrt_disabled_services:
- dnsmasq
- odhcpd
# inventory/group_vars/openwrt_switch.yaml
---
# Settings applicable to switches only
group_switch_openwrt_installed_packages: []
group_switch_openwrt_uninstalled_packages: []
group_switch_openwrt_enabled_services: []
group_switch_openwrt_disabled_services:
- dnsmasq
- odhcpd
The final assembly takes place in the playbook itself.
# playbook.yaml
- name: "Patch OpenWRT devices"
hosts: openwrt
gather_facts: true
serial: 1
tasks:
- name: "[OpenWRT] - Gather packages to be uninstalled"
ansible.builtin.set_fact:
openwrt_uninstalled_packages: >
{{ openwrt_uninstalled_packages | default([]) + vars[item] }}
loop: "{{ lookup('varnames', '.*openwrt_uninstalled_packages').split(',') }}"
- name: "[OpenWRT] - Gather services to be enabled"
ansible.builtin.set_fact:
openwrt_enabled_services: >
{{ openwrt_enabled_services | default([]) + vars[item] }}
loop: "{{ lookup('varnames', '.*openwrt_enabled_services').split(',') }}"
- name: "[OpenWRT] - Gather services to be disabled"
ansible.builtin.set_fact:
openwrt_disabled_services: >
{{ openwrt_disabled_services | default([]) + vars[item] }}
loop: "{{ lookup('varnames', '.*openwrt_disabled_services').split(',') }}"
- name: "[OpenWRT] - Gather packages to be installed"
ansible.builtin.set_fact:
openwrt_installed_packages: >
{{ openwrt_installed_packages | default([]) + vars[item] }}
loop: "{{ lookup('varnames', '.*openwrt_installed_packages').split(',') }}"
- name: "[OpenWRT] - Import role"
ansible.builtin.import_role:
name: ansible-role-openwrt
vars:
openwrt_version: "{{ openwrt.version | default('latest') }}"
openwrt_platform: "{{ openwrt.platform }}"
openwrt_id: "{{ openwrt.id }}"
openwrt_disabled_services: "{{ openwrt_disabled_services | default([]) }}"
openwrt_enabled_services: "{{ openwrt_enabled_services | default([]) }}"
openwrt_installed_packages: "{{ openwrt_installed_packages | default([]) }}"
openwrt_uninstalled_packages: "{{ openwrt_uninstalled_packages | default([]) }}"
openwrt_back_up_local_dir: "~/Downloads/OpenWRT"
Python 3
direnv
Running direnv allow
should setup your development environment for this role.