diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 38dca35d4904f40ba28bcdbcf83abc9e4ce774a2..84ede3c9b321c1f859faca06c0c3ad8f7a429e5d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,7 +13,8 @@ repos:
     rev: v0.14.3
     hooks:
       - id: detect-secrets
-        exclude: nsd/shore\.co\.il|roles/router/vars/main\.yaml
+        exclude: |-
+          nsd/shore\.co\.il|roles/router/vars/main\.yaml|roles/wap/vars/main\.yaml
 
   - repo: https://github.com/adrienverge/yamllint
     rev: v1.25.0
diff --git a/roles/wap/README.md b/roles/wap/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..23e99ed392003dc7b10bedb135de89b170ded945
--- /dev/null
+++ b/roles/wap/README.md
@@ -0,0 +1,3 @@
+# Wirelss Access Point
+
+Configure a wireless access point running OpenWRT 19.07.
diff --git a/roles/wap/tasks/main.yaml b/roles/wap/tasks/main.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6ea54f25227603bda7cc7769ac14ec5b240e4ba8
--- /dev/null
+++ b/roles/wap/tasks/main.yaml
@@ -0,0 +1,63 @@
+---
+- name: Validate assertions
+  ansible.builtin.assert:
+    that:
+      - ansible_distribution == "OpenWrt"
+      - ansible_distribution_major_version == "19"
+
+- name: Create the www directory
+  ansible.builtin.file:
+    path: /var/www
+    src: /www
+    state: link
+
+- name: Generate the SSL directory
+  ansible.builtin.file:
+    mode: 0o0755
+    path: /var/ssl
+    state: directory
+
+- name: Generate an SSL key locally
+  delegate_to: localhost
+  community.crypto.openssl_privatekey:
+    mode: 0o0444
+    path: &local_private_key '{{ playbook_dir }}/{{ ansible_hostname }}.key'
+    size: 4096
+    state: present
+    type: RSA
+
+- name: Generate a self-signed SSL certificate locally
+  delegate_to: localhost
+  community.crypto.x509_certificate:
+    path: &local_public_key '{{ playbook_dir }}/{{ ansible_hostname }}.crt'
+    privatekey_path: *local_private_key
+    provider: selfsigned
+    state: present
+
+- name: Copy private and public keys
+  loop:
+    - dest: /var/ssl/site.key
+      mode: 0o0444
+      src: *local_private_key
+    - dest: /var/ssl/site.crt
+      mode: 0o644
+      src: *local_public_key
+  ansible.builtin.copy:
+    backup: true
+    dest: '{{ item["dest"] }}'
+    force: false
+    mode: '{{ item["mode"] }}'
+    src: '{{ item["src"] }}'
+
+- name: Render UCI configuration
+  ansible.builtin.template:
+    dest: /root/uci.conf
+    src: uci.conf.j2
+
+- name: Import UCI configuration
+  ansible.builtin.shell:
+    cmd: uci import < /root/uci.conf
+
+- name: Commit UCI configuration
+  ansible.builtin.command:
+    cmd: uci commit
diff --git a/roles/wap/templates/uci.conf.j2 b/roles/wap/templates/uci.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..1ec0a5cdbdaac3bf525b420bc14e8a10c44cd13d
--- /dev/null
+++ b/roles/wap/templates/uci.conf.j2
@@ -0,0 +1,435 @@
+package dhcp
+
+config dnsmasq
+	option domainneeded '1'
+	option boguspriv '1'
+	option filterwin2k '0'
+	option localise_queries '1'
+	option rebind_protection '1'
+	option rebind_localhost '1'
+	option local '/lan/'
+	option domain 'lan'
+	option expandhosts '1'
+	option nonegcache '0'
+	option authoritative '1'
+	option readethers '1'
+	option leasefile '/tmp/dhcp.leases'
+	option resolvfile '/tmp/resolv.conf.auto'
+	option nonwildcard '1'
+	option localservice '1'
+
+config dhcp 'lan'
+	option interface 'lan'
+	option start '100'
+	option limit '150'
+	option leasetime '12h'
+	option dhcpv6 'server'
+	option ra 'server'
+	option ra_management '1'
+
+config dhcp 'wan'
+	option interface 'wan'
+	option ignore '1'
+
+config odhcpd 'odhcpd'
+	option maindhcp '0'
+	option leasefile '/tmp/hosts/odhcpd'
+	option leasetrigger '/usr/sbin/odhcpd-update'
+	option loglevel '4'
+
+package dropbear
+
+config dropbear
+	option Port '22'
+	option RootPasswordAuth 'off'
+	option PasswordAuth 'off'
+
+package firewall
+
+config defaults
+	option syn_flood '1'
+	option output 'ACCEPT'
+	option forward 'REJECT'
+	option input 'REJECT'
+
+config zone
+	option name 'lan'
+	option input 'ACCEPT'
+	option output 'ACCEPT'
+	option forward 'ACCEPT'
+	option network 'lan'
+
+config include
+	option path '/etc/firewall.user'
+
+config rule
+	option dest_port '22'
+	option src '*'
+	option name 'ssh'
+	option target 'ACCEPT'
+	list proto 'tcp'
+
+package firewall-opkg
+
+config defaults
+	option syn_flood '1'
+	option input 'ACCEPT'
+	option output 'ACCEPT'
+	option forward 'REJECT'
+
+config zone
+	option name 'lan'
+	list network 'lan'
+	option input 'ACCEPT'
+	option output 'ACCEPT'
+	option forward 'ACCEPT'
+
+config zone
+	option name 'wan'
+	list network 'wan'
+	list network 'wan6'
+	option input 'REJECT'
+	option output 'ACCEPT'
+	option forward 'REJECT'
+	option masq '1'
+	option mtu_fix '1'
+
+config forwarding
+	option src 'lan'
+	option dest 'wan'
+
+config rule
+	option name 'Allow-DHCP-Renew'
+	option src 'wan'
+	option proto 'udp'
+	option dest_port '68'
+	option target 'ACCEPT'
+	option family 'ipv4'
+
+config rule
+	option name 'Allow-Ping'
+	option src 'wan'
+	option proto 'icmp'
+	option icmp_type 'echo-request'
+	option family 'ipv4'
+	option target 'ACCEPT'
+
+config rule
+	option name 'Allow-IGMP'
+	option src 'wan'
+	option proto 'igmp'
+	option family 'ipv4'
+	option target 'ACCEPT'
+
+config rule
+	option name 'Allow-DHCPv6'
+	option src 'wan'
+	option proto 'udp'
+	option src_ip 'fc00::/6'
+	option dest_ip 'fc00::/6'
+	option dest_port '546'
+	option family 'ipv6'
+	option target 'ACCEPT'
+
+config rule
+	option name 'Allow-MLD'
+	option src 'wan'
+	option proto 'icmp'
+	option src_ip 'fe80::/10'
+	list icmp_type '130/0'
+	list icmp_type '131/0'
+	list icmp_type '132/0'
+	list icmp_type '143/0'
+	option family 'ipv6'
+	option target 'ACCEPT'
+
+config rule
+	option name 'Allow-ICMPv6-Input'
+	option src 'wan'
+	option proto 'icmp'
+	list icmp_type 'echo-request'
+	list icmp_type 'echo-reply'
+	list icmp_type 'destination-unreachable'
+	list icmp_type 'packet-too-big'
+	list icmp_type 'time-exceeded'
+	list icmp_type 'bad-header'
+	list icmp_type 'unknown-header-type'
+	list icmp_type 'router-solicitation'
+	list icmp_type 'neighbour-solicitation'
+	list icmp_type 'router-advertisement'
+	list icmp_type 'neighbour-advertisement'
+	option limit '1000/sec'
+	option family 'ipv6'
+	option target 'ACCEPT'
+
+config rule
+	option name 'Allow-ICMPv6-Forward'
+	option src 'wan'
+	option dest '*'
+	option proto 'icmp'
+	list icmp_type 'echo-request'
+	list icmp_type 'echo-reply'
+	list icmp_type 'destination-unreachable'
+	list icmp_type 'packet-too-big'
+	list icmp_type 'time-exceeded'
+	list icmp_type 'bad-header'
+	list icmp_type 'unknown-header-type'
+	option limit '1000/sec'
+	option family 'ipv6'
+	option target 'ACCEPT'
+
+config rule
+	option name 'Allow-IPSec-ESP'
+	option src 'wan'
+	option dest 'lan'
+	option proto 'esp'
+	option target 'ACCEPT'
+
+config rule
+	option name 'Allow-ISAKMP'
+	option src 'wan'
+	option dest 'lan'
+	option dest_port '500'
+	option proto 'udp'
+	option target 'ACCEPT'
+
+config include
+	option path '/etc/firewall.user'
+
+package luci
+
+config core 'main'
+	option lang 'auto'
+	option mediaurlbase '/luci-static/bootstrap'
+	option resourcebase '/luci-static/resources'
+	option ubuspath '/ubus/'
+
+config extern 'flash_keep'
+	option uci '/etc/config/'
+	option dropbear '/etc/dropbear/'
+	option openvpn '/etc/openvpn/'
+	option passwd '/etc/passwd'
+	option opkg '/etc/opkg.conf'
+	option firewall '/etc/firewall.user'
+	option uploads '/lib/uci/upload/'
+
+config internal 'languages'
+
+config internal 'sauth'
+	option sessionpath '/tmp/luci-sessions'
+	option sessiontime '3600'
+
+config internal 'ccache'
+	option enable '1'
+
+config internal 'themes'
+
+config internal 'apply'
+	option rollback '90'
+	option holdoff '4'
+	option timeout '5'
+	option display '1.5'
+
+config internal 'diag'
+	option dns 'openwrt.org'
+	option ping 'openwrt.org'
+	option route 'openwrt.org'
+
+package network
+
+config interface 'loopback'
+	option ifname 'lo'
+	option proto 'static'
+	option ipaddr '127.0.0.1'
+	option netmask '255.0.0.0'
+
+config globals 'globals'
+	option ula_prefix 'fd3a:a5ff:4867::/48'
+
+config interface 'lan'
+	option type 'bridge'
+	option ifname 'eth0'
+	option proto 'dhcp'
+
+config device 'lan_eth0_dev'
+	option name 'eth0'
+	option macaddr '60:38:e0:ae:19:4a'
+
+config device 'wan_eth1_dev'
+	option name 'eth1'
+	option macaddr '60:38:e0:ae:19:49'
+
+config switch
+	option name 'switch0'
+	option reset '1'
+
+config switch_vlan
+	option device 'switch0'
+	option vlan '1'
+	option ports '0 1 2 3 4'
+	option vid '1'
+
+package nut_server
+
+package rpcd
+
+config rpcd
+	option socket '/var/run/ubus.sock'
+	option timeout '30'
+
+config login
+	option username 'root'
+	option password '$p$root'
+	list read '*'
+	list write '*'
+
+package system
+
+config system
+	option ttylogin '0'
+	option log_size '64'
+	option urandom_seed '0'
+	option zonename 'UTC'
+	option hostname 'ea6350.shore.co.il'
+	option log_proto 'udp'
+	option conloglevel '8'
+	option cronloglevel '5'
+
+config timeserver 'ntp'
+	list server '0.openwrt.pool.ntp.org'
+	list server '1.openwrt.pool.ntp.org'
+	list server '2.openwrt.pool.ntp.org'
+	list server '3.openwrt.pool.ntp.org'
+
+package ubootenv
+
+config ubootenv
+	option dev '/dev/mtd7'
+	option offset '0x0'
+	option envsize '0x20000'
+	option secsize '0x20000'
+
+package ucitrack
+
+config network
+	option init 'network'
+	list affects 'dhcp'
+	list affects 'radvd'
+
+config wireless
+	list affects 'network'
+
+config firewall
+	option init 'firewall'
+	list affects 'luci-splash'
+	list affects 'qos'
+	list affects 'miniupnpd'
+
+config olsr
+	option init 'olsrd'
+
+config dhcp
+	option init 'dnsmasq'
+	list affects 'odhcpd'
+
+config odhcpd
+	option init 'odhcpd'
+
+config dropbear
+	option init 'dropbear'
+
+config httpd
+	option init 'httpd'
+
+config fstab
+	option exec '/sbin/block mount'
+
+config qos
+	option init 'qos'
+
+config system
+	option init 'led'
+	option exec '/etc/init.d/log reload'
+	list affects 'luci_statistics'
+	list affects 'dhcp'
+
+config luci_splash
+	option init 'luci_splash'
+
+config upnpd
+	option init 'miniupnpd'
+
+config ntpclient
+	option init 'ntpclient'
+
+config samba
+	option init 'samba'
+
+config tinyproxy
+	option init 'tinyproxy'
+
+package uhttpd
+
+config uhttpd 'main'
+	list listen_http '0.0.0.0:80'
+	list listen_http '[::]:80'
+	list listen_https '0.0.0.0:443'
+	list listen_https '[::]:443'
+	option redirect_https '1'
+	option home '/www'
+	option rfc1918_filter '1'
+	option max_requests '3'
+	option max_connections '100'
+	option cert '/etc/uhttpd.crt'
+	option key '/etc/uhttpd.key'
+	option cgi_prefix '/cgi-bin'
+	list lua_prefix '/cgi-bin/luci=/usr/lib/lua/luci/sgi/uhttpd.lua'
+	option script_timeout '60'
+	option network_timeout '30'
+	option http_keepalive '20'
+	option tcp_keepalive '1'
+
+config cert 'defaults'
+	option days '730'
+	option key_type 'rsa'
+	option bits '2048'
+	option ec_curve 'P-256'
+	option country 'ZZ'
+	option state 'Somewhere'
+	option location 'Unknown'
+	option commonname 'OpenWrt'
+
+package wireless
+
+config wifi-device 'radio0'
+	option type 'mac80211'
+	option hwmode '11g'
+	option path 'platform/soc/a000000.wifi'
+	option country 'IL'
+	option htmode 'HT40'
+	option channel '6'
+
+config wifi-iface 'default_radio0'
+	option device 'radio0'
+	option network 'lan'
+	option mode 'ap'
+	option key '{{ wifi_password }}'
+	option encryption 'psk2'
+	option ssid 'Shore Inc. (2.4ghz)'
+
+config wifi-device 'radio1'
+	option type 'mac80211'
+	option channel '36'
+	option hwmode '11a'
+	option path 'platform/soc/a800000.wifi'
+	option htmode 'VHT80'
+	option country 'IL'
+
+config wifi-iface 'default_radio1'
+	option device 'radio1'
+	option network 'lan'
+	option mode 'ap'
+	option key '{{ wifi_password }}'
+	option encryption 'psk2'
+	option ssid 'Shore Inc. (5ghz)'
+
diff --git a/roles/wap/vars/main.yaml b/roles/wap/vars/main.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..99d6a6afb99b277b47e95dea3628a42fae2c95c0
--- /dev/null
+++ b/roles/wap/vars/main.yaml
@@ -0,0 +1,8 @@
+---
+wifi_password: !vault |
+          $ANSIBLE_VAULT;1.1;AES256
+          33316135383266396635313635346337616233386236616365623261653962623465633631316232
+          6132666632616534666663313866313862663164313238310a353132383533373732613937353863
+          64646363343039333863653461383830343264343732366131313831643837356631313466333465
+          3062333863646139370a326530323032366463636262613635656232663738316131336662663738
+          3338
diff --git a/wap.yaml b/wap.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a2b7bfafaa5eba00e2d19667108ce2ab1ac8c823
--- /dev/null
+++ b/wap.yaml
@@ -0,0 +1,5 @@
+---
+- hosts:
+    - ea6350
+  roles:
+    - wap