diff --git a/docs/index.rst b/docs/index.rst
index c3010fc815711fcafb1f866118776d9578700a1e..f11dd702e6287dfa5c7363f587d68e2a8dd317e7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -65,6 +65,7 @@ Several Freifunk communities in Germany use Gluon as the foundation of their Fre
    package/gluon-ebtables-source-filter
    package/gluon-hoodselector
    package/gluon-mesh-batman-adv
+   package/gluon-mesh-wireless-sae
    package/gluon-radv-filterd
    package/gluon-scheduled-domain-switch
    package/gluon-web-admin
diff --git a/docs/package/gluon-mesh-wireless-sae.rst b/docs/package/gluon-mesh-wireless-sae.rst
new file mode 100644
index 0000000000000000000000000000000000000000..7572580e99a50e85ee300e25b4b3b718c2f93efe
--- /dev/null
+++ b/docs/package/gluon-mesh-wireless-sae.rst
@@ -0,0 +1,49 @@
+gluon-mesh-wireless-sae
+=======================
+
+This package adds support for SAE on 802.11s mesh connections.
+
+Enabling this package will require all 802.11s mesh connections
+to be encrypted using the SAE key agreement scheme. The security
+of SAE relies upon the authentication through a shared secret.
+
+In the context of public mesh networks a shared secret is an
+obvious oxymoron. Still, this functionality may provide an improvement
+over unencrypted mesh connections in that it protects against a
+passive attacker who did not observe the key agreement. In addition
+Management Frame Protection (802.11w) gets automatically enabled on
+wireless mesh interfaces to prevent protocol-level deauthentication attacks.
+
+If `wifi.mesh.sae` is enabled, a shared secret will automatically be
+derived from the `prefix6` variable. This is as secure as it gets
+for a public mesh network.
+
+For *private* mesh networks `wifi.mesh.sae_passphrase` should be
+set to your shared secret.
+
+site.conf
+---------
+These settings apply to all 802.11s mesh interfaces on all radios.
+
+wifi.mesh.sae \: optional
+  - ``true`` enables SAE on 802.11s mesh connections
+  - ``false`` disables SAE on 802.11s mesh connections
+  - defaults to ``false``
+
+wifi.mesh.sae_passphrase \: optional
+  - sets a shared secret used to authenticate any two mesh nodes,
+    crucial for private mesh networks
+  - should not be set, if the shared secret is shared with untrusted
+    third parties, like in a publish mesh network
+  - defaults to an autogenerated value derived from ``prefix6``
+
+
+Example::
+
+  wifi = {
+    mesh = {
+      sae = true,
+      -- sae_passphrase = "<shared secret>",
+    },
+  },
+
diff --git a/package/gluon-mesh-wireless-sae/Makefile b/package/gluon-mesh-wireless-sae/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..32b867a426b927ebde88f2e1ae4d53f902ce1741
--- /dev/null
+++ b/package/gluon-mesh-wireless-sae/Makefile
@@ -0,0 +1,13 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-mesh-wireless-sae
+PKG_VERSION:=1
+
+include ../gluon.mk
+
+define Package/gluon-mesh-wireless-sae
+  TITLE:=Encryption of 802.11s Mesh Links through SAE
+  DEPENDS:=+gluon-core +wpa-supplicant-openssl
+endef
+
+$(eval $(call BuildPackageGluon,gluon-mesh-wireless-sae))
diff --git a/package/gluon-mesh-wireless-sae/check_site.lua b/package/gluon-mesh-wireless-sae/check_site.lua
new file mode 100644
index 0000000000000000000000000000000000000000..be0f957bd3a15a4d6969f92decc23ec1402fcb3d
--- /dev/null
+++ b/package/gluon-mesh-wireless-sae/check_site.lua
@@ -0,0 +1,2 @@
+need_boolean({'wifi', 'mesh', 'sae'}, false)
+need_string({'wifi', 'mesh', 'sae_passphrase'}, false)
diff --git a/package/gluon-mesh-wireless-sae/luasrc/lib/gluon/upgrade/205-wireless-mesh-sae b/package/gluon-mesh-wireless-sae/luasrc/lib/gluon/upgrade/205-wireless-mesh-sae
new file mode 100755
index 0000000000000000000000000000000000000000..ee1fae0732b42adcbcab992cc8d71413d2a39c6e
--- /dev/null
+++ b/package/gluon-mesh-wireless-sae/luasrc/lib/gluon/upgrade/205-wireless-mesh-sae
@@ -0,0 +1,29 @@
+#!/usr/bin/lua
+
+local util = require 'gluon.util'
+local site = require 'gluon.site'
+local hash = require 'hash'
+local uci = require('simple-uci').cursor()
+
+
+local function configure_sae(vif)
+	uci:set('wireless', vif, 'encryption', 'sae')
+	uci:set('wireless', vif, 'key', site.wifi.mesh.sae_passphrase() or hash.md5(site.prefix6()))
+end
+
+util.foreach_radio(uci, function(radio, _, _)
+	local radio_name = radio['.name']
+	local vif = 'mesh_' .. radio_name
+	local enable = site.wifi.mesh.sae(false)
+
+	if uci:get('wireless', vif) then
+		uci:delete('wireless', vif, 'encryption')
+		uci:delete('wireless', vif, 'key')
+
+		if enable then
+			configure_sae(vif)
+		end
+	end
+end)
+
+uci:save('wireless')