macfw 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- macfw-0.1.1/LICENSE +21 -0
- macfw-0.1.1/PKG-INFO +386 -0
- macfw-0.1.1/README.md +359 -0
- macfw-0.1.1/macfw/__init__.py +3 -0
- macfw-0.1.1/macfw/__main__.py +5 -0
- macfw-0.1.1/macfw/cli.py +539 -0
- macfw-0.1.1/macfw/config.py +153 -0
- macfw-0.1.1/macfw/manager.py +246 -0
- macfw-0.1.1/macfw/pf.py +160 -0
- macfw-0.1.1/macfw.egg-info/PKG-INFO +386 -0
- macfw-0.1.1/macfw.egg-info/SOURCES.txt +19 -0
- macfw-0.1.1/macfw.egg-info/dependency_links.txt +1 -0
- macfw-0.1.1/macfw.egg-info/entry_points.txt +2 -0
- macfw-0.1.1/macfw.egg-info/top_level.txt +1 -0
- macfw-0.1.1/pyproject.toml +43 -0
- macfw-0.1.1/setup.cfg +4 -0
- macfw-0.1.1/setup.py +26 -0
- macfw-0.1.1/tests/test_cli.py +372 -0
- macfw-0.1.1/tests/test_config.py +79 -0
- macfw-0.1.1/tests/test_manager.py +114 -0
- macfw-0.1.1/tests/test_pf.py +87 -0
macfw-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tudoujun
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
macfw-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: macfw
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A small pf-based firewall manager for macOS with a ufw-like CLI
|
|
5
|
+
Author: tudoujun
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tudoujunha/macfw
|
|
8
|
+
Project-URL: Repository, https://github.com/tudoujunha/macfw
|
|
9
|
+
Project-URL: Issues, https://github.com/tudoujunha/macfw/issues
|
|
10
|
+
Keywords: macos,pf,firewall,ufw,cli
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Classifier: Topic :: System :: Systems Administration
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-python
|
|
27
|
+
|
|
28
|
+
English | [简体中文](./README.zh-CN.md)
|
|
29
|
+
|
|
30
|
+
# macfw
|
|
31
|
+
|
|
32
|
+
`macfw` is a small `pf`-based firewall manager for macOS with a `ufw`-like CLI.
|
|
33
|
+
|
|
34
|
+
It is designed for one common use case:
|
|
35
|
+
|
|
36
|
+
- keep loopback, LAN, and trusted private ranges reachable
|
|
37
|
+
- keep outbound traffic open
|
|
38
|
+
- deny public inbound traffic by default
|
|
39
|
+
- allow or deny explicit inbound exceptions with a short CLI
|
|
40
|
+
|
|
41
|
+
In practice, you can think of it as:
|
|
42
|
+
|
|
43
|
+
- a macOS firewall CLI
|
|
44
|
+
- a `ufw`-like firewall tool for macOS
|
|
45
|
+
- a simple way to manage public inbound exposure on a Mac that behaves more like a server than a personal laptop
|
|
46
|
+
|
|
47
|
+
## Why this exists
|
|
48
|
+
|
|
49
|
+
`macfw` exists to solve a gap that becomes obvious in IPv6-heavy home lab and self-hosting environments.
|
|
50
|
+
|
|
51
|
+
Many people now run a Mac, especially a Mac mini, as a small always-on server for tools, automation, coding agents, model runtimes, remote access, or NAS-like tasks. A particularly important example is people running OpenClaw on a Mac mini as a long-lived agent host. In that role, the machine is no longer just a daily personal computer. It starts behaving more like a Linux VPS, a home server, or a small self-hosted node that needs controlled inbound access.
|
|
52
|
+
|
|
53
|
+
On IPv4 networks, NAT often provides an extra layer between the machine and the public internet. On IPv6 networks, that assumption is much weaker. A Mac can end up directly reachable from the public internet, which means every listening service deserves deliberate review.
|
|
54
|
+
|
|
55
|
+
macOS does include firewall features, but the built-in experience is mostly app-oriented and GUI-oriented. It is not a close equivalent to the Linux `ufw` workflow many server users expect when they want to answer simple questions such as:
|
|
56
|
+
|
|
57
|
+
- which inbound ports are currently meant to be reachable from the public internet
|
|
58
|
+
- how do I allow only one port
|
|
59
|
+
- how do I allow only IPv4 or only IPv6
|
|
60
|
+
- how do I remove or deny a rule from the command line
|
|
61
|
+
|
|
62
|
+
`macfw` is built for that gap. It gives a Mac server or Mac mini a small command-line firewall workflow that feels much closer to `ufw`, while still using macOS `pf` underneath.
|
|
63
|
+
|
|
64
|
+
## Who this is for
|
|
65
|
+
|
|
66
|
+
`macfw` is especially meant for people who:
|
|
67
|
+
|
|
68
|
+
- run OpenClaw or similar always-on agent workloads on a Mac mini
|
|
69
|
+
- run a Mac mini or another macOS machine as an always-on server
|
|
70
|
+
- use IPv6 and want tighter control over public inbound exposure
|
|
71
|
+
- prefer terminal-based rule management over GUI-only firewall tools
|
|
72
|
+
- want a simpler mental model similar to `ufw` rather than raw `pf.conf` editing
|
|
73
|
+
|
|
74
|
+
## Status
|
|
75
|
+
|
|
76
|
+
`macfw` is currently an early `0.1.x` release. The rule model is usable, but the project should still be treated as a careful admin tool rather than a polished end-user app.
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- macOS
|
|
81
|
+
- Python 3.9+
|
|
82
|
+
- `pfctl`
|
|
83
|
+
- `sudo` access for commands that touch `/etc` or load `pf`
|
|
84
|
+
|
|
85
|
+
This project is for macOS only. It is not intended for Linux, BSD routers, or Windows.
|
|
86
|
+
|
|
87
|
+
## Install
|
|
88
|
+
|
|
89
|
+
### Option 1: install from a local clone
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/tudoujunha/macfw.git
|
|
93
|
+
cd macfw
|
|
94
|
+
python3 -m pip install .
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Option 2: install directly from GitHub
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
python3 -m pip install git+https://github.com/tudoujunha/macfw.git
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Option 3: isolated CLI install with `pipx`
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pipx install git+https://github.com/tudoujunha/macfw.git
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
After installation:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
macfw --version
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Once the package is published to PyPI, the intended installation command will be:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pipx install macfw
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Before you start
|
|
122
|
+
|
|
123
|
+
You need to know which network interface receives the inbound traffic you want to protect.
|
|
124
|
+
|
|
125
|
+
Common examples:
|
|
126
|
+
|
|
127
|
+
- Wi-Fi: often `en0` or `en1`
|
|
128
|
+
- Ethernet or USB adapters: often another `enX`
|
|
129
|
+
|
|
130
|
+
Useful commands:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
ifconfig
|
|
134
|
+
networksetup -listallhardwareports
|
|
135
|
+
route -n get default
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
If you are not sure, check which interface currently has your LAN or public IP address, then use that interface for `macfw install --interface ...`.
|
|
139
|
+
|
|
140
|
+
## Quick start
|
|
141
|
+
|
|
142
|
+
Install the managed anchor and choose the network interface to protect:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
sudo macfw install --interface en1
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Check the current state before enabling anything:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
macfw status
|
|
152
|
+
sudo macfw status
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
If you need SSH from the public internet, allow it before enabling the firewall:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
sudo macfw allow 22/tcp
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Enable the firewall:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
sudo macfw enable
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Show current state again:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
macfw status
|
|
171
|
+
sudo macfw status
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Disable or uninstall later:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
sudo macfw disable
|
|
178
|
+
sudo macfw uninstall
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## First-time setup checklist
|
|
182
|
+
|
|
183
|
+
Recommended order:
|
|
184
|
+
|
|
185
|
+
1. Install the anchor with the correct interface
|
|
186
|
+
2. Review the default policy with `macfw status`
|
|
187
|
+
3. Add any public inbound exceptions you need, such as `22/tcp`
|
|
188
|
+
4. Enable the firewall
|
|
189
|
+
5. Open a new test connection from the network you care about
|
|
190
|
+
6. Only then close the old session
|
|
191
|
+
|
|
192
|
+
This matters because enabling `pf` can interrupt an already-established SSH connection.
|
|
193
|
+
|
|
194
|
+
## Default policy
|
|
195
|
+
|
|
196
|
+
- allow loopback traffic
|
|
197
|
+
- allow inbound traffic from the active interface network
|
|
198
|
+
- allow inbound traffic from private RFC1918 IPv4 ranges
|
|
199
|
+
- allow inbound traffic from `100.64.0.0/10`
|
|
200
|
+
- allow inbound traffic from `fe80::/10` and `fd00::/8`
|
|
201
|
+
- allow outbound traffic
|
|
202
|
+
- deny public inbound traffic unless a rule explicitly allows it
|
|
203
|
+
|
|
204
|
+
## Rule model
|
|
205
|
+
|
|
206
|
+
Each rule answers four questions:
|
|
207
|
+
|
|
208
|
+
- `from <source>`: who may connect
|
|
209
|
+
- `port <port|all>`: which local port is reachable
|
|
210
|
+
- `proto <proto>`: which protocol applies
|
|
211
|
+
- `family <both|ipv4|ipv6>`: which IP family applies
|
|
212
|
+
|
|
213
|
+
Supported protocols today:
|
|
214
|
+
|
|
215
|
+
- `tcp`
|
|
216
|
+
- `udp`
|
|
217
|
+
- `icmp`
|
|
218
|
+
- `icmp6`
|
|
219
|
+
- `any`
|
|
220
|
+
|
|
221
|
+
## Common commands
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
macfw allow 22/tcp
|
|
225
|
+
macfw allow 53/udp
|
|
226
|
+
macfw allow 22/tcp ipv6
|
|
227
|
+
macfw allow 22/tcp from any ipv4
|
|
228
|
+
macfw allow 22/tcp from 1.2.3.4
|
|
229
|
+
macfw allow 22/tcp from 2001:db8::1
|
|
230
|
+
macfw allow proto icmp ipv4
|
|
231
|
+
macfw allow from 192.168.0.0/16
|
|
232
|
+
|
|
233
|
+
macfw deny 22/tcp ipv6
|
|
234
|
+
macfw deny 22/tcp from 2001:db8::1
|
|
235
|
+
|
|
236
|
+
macfw delete 22/tcp
|
|
237
|
+
macfw delete 22/tcp ipv6
|
|
238
|
+
macfw delete deny 22/tcp ipv6
|
|
239
|
+
macfw delete from 192.168.0.0/16
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Common scenarios
|
|
243
|
+
|
|
244
|
+
Allow public SSH on IPv6 only:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
sudo macfw allow 22/tcp ipv6
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Allow public DNS on UDP:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
sudo macfw allow 53/udp
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Allow SSH only from one specific address:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
sudo macfw allow 22/tcp from 203.0.113.10
|
|
260
|
+
sudo macfw allow 22/tcp from 2001:db8::10
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Explicitly block a public IPv6 SSH rule:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
sudo macfw deny 22/tcp ipv6
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Remove a single matching rule without caring whether it is `allow` or `deny`:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
sudo macfw delete 22/tcp
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Delete matching behavior
|
|
276
|
+
|
|
277
|
+
`delete` uses two matching modes:
|
|
278
|
+
|
|
279
|
+
- if you specify `allow` or `deny`, deletion is constrained to that action
|
|
280
|
+
- if you do not specify an action, `macfw` looks for all matching rules
|
|
281
|
+
|
|
282
|
+
That means:
|
|
283
|
+
|
|
284
|
+
- if exactly one rule matches, `delete` removes it even if it is a `deny`
|
|
285
|
+
- if multiple rules match, `delete` stops and asks you to disambiguate with `allow`, `deny`, `ipv4`, `ipv6`, or a more specific source
|
|
286
|
+
|
|
287
|
+
Family matching works like this:
|
|
288
|
+
|
|
289
|
+
- omit `ipv4` or `ipv6`: broader family match
|
|
290
|
+
- specify `ipv4` or `ipv6`: exact family match
|
|
291
|
+
|
|
292
|
+
Examples:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
# removes 22/tcp, 22/tcp ipv4, or 22/tcp ipv6 if there is only one match
|
|
296
|
+
sudo macfw delete 22/tcp
|
|
297
|
+
|
|
298
|
+
# removes only an IPv6 rule
|
|
299
|
+
sudo macfw delete 22/tcp ipv6
|
|
300
|
+
|
|
301
|
+
# removes only a deny rule
|
|
302
|
+
sudo macfw delete deny 22/tcp ipv6
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Status output
|
|
306
|
+
|
|
307
|
+
`macfw status` shows:
|
|
308
|
+
|
|
309
|
+
- whether `macfw` is enabled
|
|
310
|
+
- whether live `pf` is enabled
|
|
311
|
+
- which interface is managed
|
|
312
|
+
- your user-defined rules first
|
|
313
|
+
- built-in trusted defaults
|
|
314
|
+
- the final default deny
|
|
315
|
+
|
|
316
|
+
If `pf` shows as `unknown`, run:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
sudo macfw status
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## What `install`, `enable`, `reload`, and `disable` do
|
|
323
|
+
|
|
324
|
+
- `install`: creates the `macfw` anchor, injects the anchor hook into `/etc/pf.conf`, and writes the local config files
|
|
325
|
+
- `enable`: writes the active anchor rules, validates the `pf` config, and enables `pf` if needed
|
|
326
|
+
- `reload`: rewrites and reloads the anchor using the current config
|
|
327
|
+
- `disable`: keeps the config on disk but disables the active `macfw` policy
|
|
328
|
+
- `uninstall`: removes the anchor, restores the original `/etc/pf.conf` backup when available, and removes the local config files
|
|
329
|
+
|
|
330
|
+
## Safety notes
|
|
331
|
+
|
|
332
|
+
Be careful when enabling or reloading rules over SSH.
|
|
333
|
+
|
|
334
|
+
- enabling `pf` can drop an already-established SSH session
|
|
335
|
+
- a newly opened LAN SSH session should still match the LAN allow rules
|
|
336
|
+
- a public SSH session will be dropped unless you explicitly allow it first
|
|
337
|
+
|
|
338
|
+
If you rely on public SSH access, add the rule before enabling:
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
sudo macfw allow 22/tcp
|
|
342
|
+
sudo macfw enable
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
If you rely on LAN SSH only, the default trusted LAN rules should permit a newly opened LAN session after the firewall is enabled. An already-established session can still drop when `pf` first becomes active.
|
|
346
|
+
|
|
347
|
+
## How it works
|
|
348
|
+
|
|
349
|
+
`macfw` manages:
|
|
350
|
+
|
|
351
|
+
- a dedicated anchor at `/etc/pf.anchors/macfw`
|
|
352
|
+
- a hook in `/etc/pf.conf`
|
|
353
|
+
- a user config at `~/.config/macfw/config.json`
|
|
354
|
+
- a small state file at `~/.config/macfw/state.json`
|
|
355
|
+
|
|
356
|
+
`install` backs up the current `/etc/pf.conf` before modifying it. `uninstall` restores the backup when available.
|
|
357
|
+
|
|
358
|
+
## Limitations
|
|
359
|
+
|
|
360
|
+
- there is no Homebrew formula yet
|
|
361
|
+
- there is no PyPI release yet
|
|
362
|
+
- interface detection is not automatic yet
|
|
363
|
+
- this tool is focused on inbound filtering on macOS `pf`
|
|
364
|
+
- this tool assumes you are comfortable using `sudo` and reading firewall state before enabling changes
|
|
365
|
+
|
|
366
|
+
## Development
|
|
367
|
+
|
|
368
|
+
Run the test suite:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
python3 -m unittest discover -s tests -v
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Run directly from the repository checkout:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
PYTHONPATH=. python3 -m macfw --help
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
For maintainer release steps, see:
|
|
381
|
+
|
|
382
|
+
- [`docs/publishing.md`](./docs/publishing.md)
|
|
383
|
+
|
|
384
|
+
## License
|
|
385
|
+
|
|
386
|
+
MIT
|