diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 3ccdfe8e0c047..4e8ebd64aaf55 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -613,6 +613,7 @@
./services/networking/hylafax/default.nix
./services/networking/i2pd.nix
./services/networking/i2p.nix
+ ./services/networking/create_ap.nix
./services/networking/iodine.nix
./services/networking/iperf3.nix
./services/networking/ircd-hybrid/default.nix
diff --git a/nixos/modules/services/networking/create_ap.nix b/nixos/modules/services/networking/create_ap.nix
new file mode 100644
index 0000000000000..3d5e2da98e682
--- /dev/null
+++ b/nixos/modules/services/networking/create_ap.nix
@@ -0,0 +1,134 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+ cfg = config.services.create_ap;
+
+ valueToText =
+ let
+ conversions = {
+ string = id;
+ int = toString;
+ bool = value: if value then "1" else "0";
+ };
+ in value: conversions.${builtins.typeOf value} value;
+
+ configFile = pkgs.writeText "create_ap.conf"
+ (concatStrings (mapAttrsToList (name: value:
+ "${name}=${valueToText value}\n"
+ ) (filterAttrs (_: value: value != null) cfg.settings)));
+
+ wifiIface = cfg.settings.WIFI_IFACE;
+ inetIface = cfg.settings.INTERNET_IFACE;
+
+in {
+
+ options.services.create_ap = with types; {
+ enable = mkEnableOption "Enable Create Access Point Service";
+
+ settings = mkOption {
+ type = attrsOf (nullOr (oneOf [ str int bool ]));
+ description = ''
+ create_ap configuration, see
+ for information on supported values.
+ '';
+ example = literalExample ''
+ {
+ HIDDEN = true;
+ FREQ_BAND = 5;
+ DHCP_DNS = "1.1.1.1";
+ COUNTRY = "US";
+ }
+ '';
+ };
+
+ wifiInterface = mkOption {
+ description = ''
+ Wi-Fi interface to use (Use ip link show to list available).
+ '';
+ type = str;
+ example = "wlan0";
+ };
+
+ internetInterface = mkOption {
+ description = ''
+ Interface to use for internet connection (Use ip link show to list available).
+ '';
+ type = str;
+ example = "enp0";
+ };
+
+ ssid = mkOption {
+ description = "SSID of the access point.";
+ type = str;
+ example = "MyAccessPoint";
+ };
+
+ passphrase = mkOption {
+ description = "Passphrase to use for access point.";
+ type = str;
+ example = "12345678";
+ };
+
+ gateway = mkOption {
+ description = "IPv4 Gateway for the Access Point.";
+ type = str;
+ default = "192.168.12.1";
+ example = "10.0.0.1";
+ };
+
+ channel = mkOption {
+ type = either str int;
+ example = 1;
+ default = "default";
+ description = "WLAN Channel number.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+
+ services.create_ap.settings = {
+ DAEMONIZE = false;
+ CHANNEL = mkDefault cfg.channel;
+ GATEWAY = mkDefault cfg.gateway;
+ WIFI_IFACE = mkDefault cfg.wifiInterface;
+ INTERNET_IFACE = mkDefault cfg.internetInterface;
+ SSID = mkDefault cfg.ssid;
+ PASSPHRASE = mkDefault cfg.passphrase;
+ };
+
+ environment.systemPackages = [ pkgs.create_ap ];
+
+ systemd.services.create_ap = {
+ description = "Create AP Service";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target"
+ "sys-subsystem-net-devices-${wifiIface}.device"
+ "sys-subsystem-net-devices-${inetIface}.device" ];
+ bindsTo = [ "sys-subsystem-net-devices-${wifiIface}.device"
+ "sys-subsystem-net-devices-${inetIface}.device" ];
+ serviceConfig =
+ let capabilities = [
+ "CAP_CHOWN"
+ "CAP_DAC_OVERRIDE"
+ "CAP_DAC_READ_SEARCH"
+ "CAP_KILL"
+ "CAP_NET_ADMIN"
+ "CAP_NET_RAW"
+ "CAP_NET_BIND_SERVICE"
+ ]; in {
+ KillSignal = "SIGINT";
+ Restart = "on-failure";
+ RestartSec = 5;
+ DynamicUser = true;
+ ProtectSystem = "strict";
+ ProtectHome = true;
+ # create_ap parses and updates NetworkManager.conf
+ ReadWritePaths = "-/etc/NetworkManager/";
+ AmbientCapabilities = capabilities;
+ CapabilityBoundingSet = capabilities;
+ ExecStart = "${pkgs.create_ap}/bin/create_ap --config ${configFile}";
+ };
+ };
+ };
+}
diff --git a/pkgs/tools/networking/create_ap/default.nix b/pkgs/tools/networking/create_ap/default.nix
new file mode 100644
index 0000000000000..6699a59b7203c
--- /dev/null
+++ b/pkgs/tools/networking/create_ap/default.nix
@@ -0,0 +1,93 @@
+{ stdenv, pkgs, fetchFromGitHub, iw, procps, hostapd, iproute, getopt, bash
+, dnsmasq, iptables, coreutils, gnugrep, gnused, gawk, which, flock
+
+, haveged ? null
+, networkmanager ? null
+# for iwconfig
+, wirelesstools ? null
+
+# To fall back to haveged if entropy is low.
+#
+# Defaulting to false because not having it does not break things.
+# If it is really needed, warnings will be logged to journal.
+, useHaveged ? false
+
+# nmcli is not required for create_ap.
+# (defaulting to true because it is very likely already present)
+, useNetworkManager ? true
+
+# You only need this if 'iw' can not recognize your adapter
+, useWirelessTools ? true }:
+
+assert useHaveged -> !isNull haveged;
+assert useNetworkManager -> !isNull networkmanager;
+assert useWirelessTools -> !isNull wirelesstools;
+
+with stdenv.lib;
+with stdenv.lib.lists;
+
+let binPath = makeBinPath ([ iw procps hostapd iproute getopt bash dnsmasq
+ iptables coreutils which flock gnugrep gnused gawk ]
+ ++ optional useHaveged haveged
+ ++ optional useNetworkManager networkmanager
+ ++ optional useWirelessTools wirelesstools);
+in
+
+stdenv.mkDerivation rec {
+ name = "${pname}-${version}";
+ version = "0.4.6";
+ pname = "create_ap";
+
+ src = fetchFromGitHub {
+ owner = "oblique";
+ repo = "create_ap";
+ rev = "v${version}";
+ sha256 = "0wwwdc63vg987512nac46h91lg0a1gzigdqrgijk4b4andr94cp4";
+ };
+
+ nativeBuildInputs = [ pkgs.makeWrapper ];
+
+ patches = [ ./noroot.patch ];
+
+ dontBuild = true;
+
+ installPhase = ''
+ mkdir -p $out/bin/
+ mv create_ap $out/bin/create_ap
+ wrapProgram $out/bin/create_ap --prefix PATH : ${binPath}
+ '';
+
+ meta = {
+ homepage = https://github.com/oblique/create_ap;
+ description = "Creates a NATed or Bridged WiFi Access Point";
+ longDescription = ''
+ Features:
+
+ - Create an AP (Access Point) at any channel.
+ - Choose one of the following encryptions: WPA, WPA2, WPA/WPA2, Open (no encryption).
+ - Hide your SSID.
+ - Disable communication between clients (client isolation).
+ - IEEE 802.11n & 802.11ac support
+ - Internet sharing methods: NATed or Bridged or None (no Internet sharing).
+ - Choose the AP Gateway IP (only for 'NATed' and 'None' Internet sharing methods).
+ - You can create an AP with the same interface you are getting your Internet connection.
+ - You can pass your SSID and password through pipe or through arguments (see examples).
+
+ See for details.
+
+ Example service configuration:
+
+ services.create_ap = {
+ enable = true;
+ wifi-iface = "wlan0";
+ internet-iface = "eth0";
+ gateway = "10.0.0.1";
+ ssid = "MyAccessPoint";
+ passphrase = "12345678";
+ };
+ '';
+ license = licenses.bsd2;
+ maintainers = with maintainers; [ klntsky ];
+ platforms = platforms.linux;
+ };
+}
diff --git a/pkgs/tools/networking/create_ap/noroot.patch b/pkgs/tools/networking/create_ap/noroot.patch
new file mode 100644
index 0000000000000..d3da19611f0e6
--- /dev/null
+++ b/pkgs/tools/networking/create_ap/noroot.patch
@@ -0,0 +1,27 @@
+diff --git a/create_ap b/create_ap
+index 8fa6671..3949c75 100755
+--- a/create_ap
++++ b/create_ap
+@@ -138,10 +138,6 @@ init_lock() {
+ eval "exec $LOCK_FD>$LOCK_FILE" > /dev/null 2>&1 || return 1
+ umask $SCRIPT_UMASK
+
+- # there is a case where lock file was created from a normal
+- # user. change the owner to root as soon as we can.
+- [[ $(id -u) -eq 0 ]] && chown 0:0 $LOCK_FILE
+-
+ # create mutex counter lock file
+ echo 0 > $COUNTER_LOCK_FILE
+
+@@ -1267,11 +1263,6 @@ if [[ -n "$LIST_CLIENTS_ID" ]]; then
+ exit 0
+ fi
+
+-if [[ $(id -u) -ne 0 ]]; then
+- echo "You must run it as root." >&2
+- exit 1
+-fi
+-
+ if [[ -n "$STOP_ID" ]]; then
+ echo "Trying to kill $PROGNAME instance associated with $STOP_ID..."
+ send_stop "$STOP_ID"
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 58f114d1b1ae4..a7d8880abae4d 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -813,6 +813,8 @@ in
cue = callPackage ../development/tools/cue { };
+ create_ap = callPackage ../tools/networking/create_ap { };
+
deskew = callPackage ../applications/graphics/deskew { };
detect-secrets = python3Packages.callPackage ../development/tools/detect-secrets { };