README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
????????????????? ? what is zoa ? ????????????????? zoa is a simple, opinionated, shell-based config management tool. it's the best. it's pull-based. zoa makes you feel good about config management. zoa config lives in a git repo, and all of the systems you manage simply point at the repo. zoa is intended for human-scale deployment, and generally works best if you're not trying to manage a crazy complicated fleet of systems between multiple teams. zoa is for small, tight-knit teams who want to keep it all in their heads with minimal effort. it's also for individuals who like understanding their systems, and hate maintaining them when things break for no reason. you might like zoa if: - you enjoy alpine, gentoo, linux from scratch, etc - you use a tiling window manager - you like messing with dotfiles - you dislike chef, ansible, saltstack, and puppet intensely - you like smallness and simplicity you might not like zoa if: - you are an abstraction enjoyer - you dislike shell, the language - you dislike the terminal in general why did you write this? (are you insane) first, yes, duh, but also: i manage systems with config management tools for a living. (6+ years) i know how impossible they all are to operate & keep up with years of painful, painful experience. i wrote zoa to use on my personal systems, because i hate the main config management tools a lot. also, i <3 shell :D ~~ zoa doesn't pretend ~~ chef & ansible & puppet & salt all pretend to be fully idempotent & declarative, but leave actual declarative-ness and idempotence as an exercise to the user. zoa doesn't pretend. zoa is not idempotent. zoa is not declarative. it assumes your state changes over time, and zoa makes it easy to keep up with those changes. you won't have to look at horrible doc websites or commit a week to learning a god damned stupid DSL other config management systems make their users feel stupid. zoa doesn't. zoa uses shell scripts like every single linux distro has used for millenia. it has exactly zero dependencies besides zoa itself. zoa adheres to standards, and uses well-known distro conventions. zoa is easy. and simple. and honestly, quite a smol guy the other tools want to do a lot - search across your nodes, deploy via their tooling, automate testing, manage AWS resources?!?!? zoa is only concerned with _managing configuration_ _on servers_ and nothing else. forever. the other tools break constantly because they try to do everything. zoa breaks rarely because it does almost nothing. other tools are slow because they have HUGE runtimes and scopes zoa is comparatively fast, because it's just running god dang shell scripts :3 zoa doesn't require: - ssh - python - ruby - a chef server or salt master other tools abstract too much zoa abstracts a few definitely useful functions, but otherwise gets out of the way and gives you a light framework to speak shell to your systems. in zoa, you write plain POSIX shell. Why? - posix shell is productive! - posix shell is portable! - posix shell is the language of system configuration! - posix shell is easy to remember! - posix shell rarely changes! zoa's design touchstones ------------------------ * no config on the nodes node-side config is cumbersome. everything should be adjustable via git and git alone. * expose 1 way to do common things zoa exposes only 1 hostname var, only 1 copy function, has 0 flags, and 1 way to run via git, and 1 way to format your repository. less overhead for you. * adopt functionality slowly zoa is starting with a minimal set of built-in functions because i only want to maintain functions in order to address severe pain-points. most things should just be handled via shell. * as standards-compliant as reasonable but not ball-breakingly so tbh, see $HOSTNAME for an example of zoa not being "fully" standards compliant. !!!!!!!!!!!!!!!!! ! DETAILS ! !!!!!!!!!!!!!!!!! - ~ - understand: there are three components - ~ - everything you need to know: 1: the utility 2: env vars and helpers 3: the layout --- 1: the utility --- zoa is a single binary. it expects to run as root (it is managing your system, after all), and it should probably run on a cron/timer/whatever schedule at whatever interval you want. installation process: wget https://j3s.sh/zoa/zoa # TODO mv zoa /usr/local/sbin chmod +x /usr/local/sbin/zoa to run zoa: zoa https://git.j3s.sh/j3s/config main ^ ^ ^ zoa, duh. git repo optional git branch zoa will clone the repo+branch specified to /var/lib/zoa/repo, and then it will execute the "main" script in that repo - more on that later. to run zoa in local-only mode: zoa /path/to/zoa/dir in which case, zoa will just execute main from that dir you specify that's it, you've run zoa! now set up a cronjob/systemd timer to run zoa on a schedule, if that's your thing. or just login and run zoa periodically. 👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀 you could even have the first zoa run set up a cronjob that runs zoa on a schedule, to make your life even easier. 👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀 --- 2: environment variables and helper functions --- ENVIRONMENT VARIABLES before zoa runs, it sets a few standard environment variables for your usage. here are _all of them_. displayed values are from my dev system caveats are marked with * $PATH - the search path for binaries. this is hardcoded. PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin $ARCH - the name of the hardware type on which the system is running ARCH=x86_64 $HOSTNAME - the name of this node. HOSTNAME=nostromo.j3s.sh * there is no shortname vs fqdn standard, so this env var may vary by distro $OS - the operating system name OS=Linux * on BSD systems, this var is a lot more useful * if you want your distro, take a look at OS_RELEASE_ID $RELEASE - the current release level of the operating system implementation RELEASE=5.19.5-arch1-1 ! WARNING: ALL $OS_RELEASE_* VARIABLES ARE UNRELIABLE ! be sure to check for their existence before using them. ty~ :3 $OS_RELEASE_ID - short uncapitalized name of your distro OS_RELEASE_ID=arch * see above warning $OS_RELEASE_VERSION_ID - version of your distro, if applicable OS_RELEASE_VERSION_ID= # note that arch has no version ID * see above warning TODO: expose hardware info, cpu cores, ip address, memory availability, etc HELPER FUNCTIONS zoa has some little helper functions for common operations. * zoa-file usage: zoa-file example.conf /etc/example.conf optional-command ^ ^ ^ source destination executed if the file changes description: zoa-file takes a file from $zoa_root/files/ and places it on-disk somewhere. if the file is changed, a command is optionally executed. this can be very useful for things like reloading configurations. when you think about zoa-file, just think of it as regular cp, with an extra argument for running arbitrary code if the cp does anything. examples: zoa-file motd /etc/motd zoa-file nginx/j3s.sh /etc/nginx/conf.d/j3s.sh systemctl reload nginx todo: add a template processing step of some kind? * zoa-script usage: zoa-script scriptname ^ script to execute description: zoa-script executes the given script from /var/lib/zoa/scripts/ this is basically shorthand for ". /var/lib/zoa/repo/script/scriptname", and also adds some nice decorators. examples: zoa-script nginx zoa-script 420/69 --- 3: the layout --- ah. finally. how do i lay out my zoa repo? here's the canonical repo layout: main <-- file, entrypoint files/ <-- dir, contains arbitrary text files scripts/ <-- dir, contains arbitrary shell scripts you need at least main. everything else is optional. main is your entrypoint. you could just stick everything in main and be done with it, if you want. however, if you want main to be a little more capable, here's one starting point: case $HOSTNAME in git.j3s.sh) # note that the CERTS env var will # pass into any scripts called after # it is defined, as if they're all 1 # long script CERTS='git.j3s.sh' zoa-script certs zoa-script git ;; j3s.sh) CERTS='j3s.sh' zoa-script certs zoa-script web ;; esac scripts/ contains arbitrary shell scripts. you can organize them how you'd like. dirs are supported. files/ contains text files (no big files or binaries) that you might be interested in placing on hosts. these can be accessed with the zoa-file function. wat is borked? - branch cloning doesn't work rn idk why - the cloned git repo has root perms & git hates that - the repo is re-cloned on every run *shrug* BONUS SECTION! :3 :3 <3 :3 common config management patterns in zoa ### define a reusable function in zoa, this is easy - just define a shell function: die() { printf "%s.\n" "$1" exit 1 } every sub-script that is called will automatically have access to it. env vars work the same way. ### install a package (this is distro dependent) apt update apt install -y cowsay ### install a package, but only if the distro is debian if [ "$OS_RELEASE_ID" = "debian" ]; then apt install -y cowsay fi ### place sshd_config, set permissions, and reload ssh when it changes zoa-file sshd /etc/ssh/sshd_config systemctl restart sshd chown root:root /etc/ssh/sshd_config chmod 0644 /etc/ssh/sshd_config ### append an iptables rule to the input chain rule='INPUT --protocol tcp --dport 69 --jump ACCEPT' iptables --check $rule || iptables --append $rule ### clone a remote git repo, pull it constantly git clone git@git.sr.ht:~example/example /opt/repo || git fetch /opt/repo