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
????????????????? ? what is zoa ? ????????????????? zoa is the best config management tool. it's the best because it's the simplest. it's written in shell, has a few nice little VERY USEFUL helpers, and doesn't go out of its way to make you hate yourself. zoa requires access to a git repo, though it may be used in local development mode. 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. why did you write this? i manage systems with config management tools for a living. i know how impossible they all are firsthand, i have 6+ years of painful, painful experience. i wrote zoa to use on my personal systems, because i hate the primary 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. !!!!!!!!!!!!!!!!!! ! QUICKSTART ! !!!!!!!!!!!!!!!!!! - ~ - understand: there are three components - ~ - everything you need to know: 1: the utility 2: env vars and helpers 3: the layout --- 1: the utility --- simply run the "zoa" tool on all of your systems. it's a simple binary (XXX: or shell script?) that can be installed trivially. it expects to run as root, and it should run on a cron/timer/whatever schedule at whatever interval you want. install: wget -O https://j3s.sh/zoa/zoa # TODO mv zoa /usr/local/sbin chmod +x /usr/local/sbin/zoa design guideline: all configuration data comes from a git repository. the nodes themselves keep no state at all. to run zoa: zoa https://git.j3s.sh/config main ^ ^ ^ zoa, duh. git repo git ref (branch or tag) zoa will clone the repo+branch specified in your config (or attempt to fetch it, if it's already cloned) to /var/lib/zoa/<repo>/<branch> zoa will then execute /var/lib/zoa/<repo>/<branch>/main if you run "zoa" with 0 arguments, it will execute if it finds exactly 1 repo/branch combo in /var/lib/zoa - if you run zoa this way, it will not clone the remote repo. this can be very useful for local testing. note that the next run of zoa with a repo+branch will wipe any changes you make in /var/lib. that's it, you've configured your server! set up a cronjob/systemd timer to run zoa on a schedule, if that's your thing. or just login and run it periodically. you could even have the first zoa run set up a cronjob that runs it on a schedule, to make your life even easier. 👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀👀 --- 2: environment variables and helper functions --- in zoa, you write plain POSIX shell. Why? - posix shell is productive - posix shell is portable - posix shell is hard to make too complicated 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 $zoa_root/scripts/ 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 fs layout: main <-- entrypoint files/ <-- arbitrary text files you'd like to place scripts/ <-- arbitrary shell scripts you need at least main! everything else is optional. main is your entrypoint. if your whole vibe is just doing basic configuration of nodes, you _could_ put literally everything in main and just be done with it. however, you may want main to be a little more complex: case $NODENAME in git.j3s.sh) . ./ esac scripts/ contains arbitrary shell scripts. you can organize them how you'd like. files/ contains text files (no big files or binaries pls) that you might be interested in placing on hosts. these can be accessed with the zoa_file function. *~~~ warts ~~~* * zoa doesn't support function definitions YET * zoa reserved words must be all-on-one-line atm BONUS SECTION! :3 :3 <3 :3 common config management patterns to zoa ### install a package, ### but only if the distro is debian zoa if [ "$DISTRO" = "debian" ]; then apt install -y cowsay fi ansible - name: add package when: ansible_facts['os_family'] == "Debian" package: name: 'cowsay' state: present ### place sshd_config, ### and reload ssh when it changes ansible # handlers/main.yml - name: restart-sshd service: name: sshd state: restarted # tasks/main.yml - name: Configure sshd template: src: sshd_config.j2 dest: /etc/ssh/sshd_config owner: root group: root mode: 0644 notify: restart-sshd zoa 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 ansible - name: do the thing iptables: chain: INPUT protocol: tcp destination_port: '22' ctstate: NEW syn: match jump: ACCEPT zoa rule='INPUT --protocol tcp --dport 69 --jump ACCEPT' iptables --check $rule || iptables --append $rule