consist 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/README.md +57 -390
- data/lib/consist/cli.rb +28 -9
- data/lib/consist/consistfile.rb +11 -6
- data/lib/consist/recipe.rb +13 -14
- data/lib/consist/resolver.rb +16 -0
- data/lib/consist/step.rb +8 -2
- data/lib/consist/templates/Consistfile.tt +19 -0
- data/lib/consist/utils.rb +28 -0
- data/lib/consist/version.rb +1 -1
- metadata +5 -7
- data/lib/consist/recipes.rb +0 -28
- data/lib/recipes/kamal_single_server.rb +0 -8
- data/lib/steps/install_apt_packages/step.rb +0 -19
- data/lib/steps/update_apt_packages/apt_auto_upgrades +0 -3
- data/lib/steps/update_apt_packages/step.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce1770d8755d16d374ad05d69e36645c67b819926bd7a163e497714ca6400a20
|
4
|
+
data.tar.gz: '09ce200d04369a78c1d66bd4b389df1ff402951be094189d3d13c7042a18ff83'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b487a6385b295f593cd3477afe5be8031f1cee5de7742c6fe99892da0f28eba0a855f91fbb7cc84750538826e5bd763164a49c4ac172d448dec477c09bdc4847
|
7
|
+
data.tar.gz: 65cb3135f0ea5afc548b321282f289b7fe66e4642b80eecfa530174235f173616781d60cb976ec362d05f1496f9b8fc2ca3a025a7c96c6435129c941e375c5b8
|
data/README.md
CHANGED
@@ -23,6 +23,7 @@ server, such as firewalls, general hardening, enabling swapfile etc.
|
|
23
23
|
- Procedural declaration execution - no converging, orchestration or
|
24
24
|
event driven operation
|
25
25
|
- If you can shell script it, you can `consist` it directly
|
26
|
+
- Encouraging sharing of portable `Consistfiles` across the community
|
26
27
|
|
27
28
|
---
|
28
29
|
|
@@ -30,6 +31,13 @@ server, such as firewalls, general hardening, enabling swapfile etc.
|
|
30
31
|
- [Support](#support)
|
31
32
|
- [Rationale](#rationale)
|
32
33
|
- [Key Concepts](#key-concepts)
|
34
|
+
- [Recipes](#recipes)
|
35
|
+
- [Steps](#steps)
|
36
|
+
- [Files](#files)
|
37
|
+
- [Consistfile](#consistfile)
|
38
|
+
- [Artifacts](#artifacts)
|
39
|
+
- [.consist directory](#.consist-directory)
|
40
|
+
- [Comunity Consistfiles](#community-consistfiles)
|
33
41
|
- [Is It Good?](#is-it-good%3F)
|
34
42
|
- [License](#license)
|
35
43
|
- [Code of conduct](#code-of-conduct)
|
@@ -41,26 +49,32 @@ server, such as firewalls, general hardening, enabling swapfile etc.
|
|
41
49
|
gem install consist
|
42
50
|
```
|
43
51
|
|
44
|
-
You must be already
|
45
|
-
your SSH id to perform actions.
|
52
|
+
You must be already authenticated with the server you want to scaffold.
|
53
|
+
`consist` will use your account's SSH id to perform actions.
|
46
54
|
|
47
|
-
|
55
|
+
The main way of using `consist` is to go with a `Consistfile` in
|
56
|
+
your project root that describes the recipe and steps. Then you can say:
|
48
57
|
|
49
58
|
```sh
|
50
|
-
consist
|
59
|
+
consist up <ip_address> [--consistfile=/path/to/consistfile] [--consistdir=/path/to/.consistdir]
|
51
60
|
```
|
52
61
|
|
53
|
-
|
54
|
-
recipe, using the `root` user.
|
62
|
+
And `consist` will do it's thing with that given IP address.
|
55
63
|
|
56
|
-
|
57
|
-
your project root that describes the recipe and steps. Then you can say:
|
64
|
+
To create a blank `Consistfile` in your project, execute:
|
58
65
|
|
59
|
-
```
|
60
|
-
consist
|
66
|
+
```ruby
|
67
|
+
consist init
|
61
68
|
```
|
62
69
|
|
63
|
-
|
70
|
+
## Commands
|
71
|
+
|
72
|
+
Other commands available:
|
73
|
+
|
74
|
+
- `consist init [account/repo]` - initialize your project with a new Consist file. Optionally,
|
75
|
+
you can specify a Github `account/repo` path and that location will be used to clone down a
|
76
|
+
Consistfile, and any associated artifacts needed by the Consistfile.
|
77
|
+
- `consist ping <ip_address>` - checks you can connect and authenticate with the given IP
|
64
78
|
|
65
79
|
## Features
|
66
80
|
|
@@ -160,9 +174,10 @@ end
|
|
160
174
|
|
161
175
|
A `Consistfile` is a portable giant file of a recipe and all its
|
162
176
|
steps. Something like (this is a _full_ example, in practice you
|
163
|
-
would reference some of
|
177
|
+
would reference some of Consists' built in steps):
|
164
178
|
|
165
179
|
```ruby
|
180
|
+
# This is a shortened non-complete example.
|
166
181
|
consist do
|
167
182
|
config :hostname, "testexample.com"
|
168
183
|
config :site_fqdn, "textexample.com"
|
@@ -191,171 +206,6 @@ consist do
|
|
191
206
|
EOS
|
192
207
|
end
|
193
208
|
|
194
|
-
file :fail2ban_config do
|
195
|
-
<<~EOS
|
196
|
-
# Fail2Ban configuration file.
|
197
|
-
#
|
198
|
-
|
199
|
-
# to view current bans, run one of the following:
|
200
|
-
# fail2ban-client status ssh
|
201
|
-
# iptables --list -n | fgrep DROP
|
202
|
-
|
203
|
-
# The DEFAULT allows a global definition of the options. They can be overridden
|
204
|
-
# in each jail afterwards.
|
205
|
-
|
206
|
-
[DEFAULT]
|
207
|
-
|
208
|
-
ignoreip = 127.0.0.1
|
209
|
-
bantime = 600
|
210
|
-
maxretry = 3
|
211
|
-
backend = auto
|
212
|
-
usedns = warn
|
213
|
-
destemail = <%= admin_email %>
|
214
|
-
|
215
|
-
#
|
216
|
-
# ACTIONS
|
217
|
-
#
|
218
|
-
|
219
|
-
banaction = iptables-multiport
|
220
|
-
mta = sendmail
|
221
|
-
protocol = tcp
|
222
|
-
chain = INPUT
|
223
|
-
|
224
|
-
#
|
225
|
-
# Action shortcuts. To be used to define action parameter
|
226
|
-
|
227
|
-
# The simplest action to take: ban only
|
228
|
-
action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
229
|
-
|
230
|
-
# ban & send an e-mail with whois report to the destemail.
|
231
|
-
action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
232
|
-
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
|
233
|
-
|
234
|
-
# ban & send an e-mail with whois report and relevant log lines
|
235
|
-
# to the destemail.
|
236
|
-
action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
237
|
-
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
|
238
|
-
|
239
|
-
# default action
|
240
|
-
action = %(action_mw)s
|
241
|
-
|
242
|
-
[ssh]
|
243
|
-
|
244
|
-
enabled = true
|
245
|
-
port = 987
|
246
|
-
filter = sshd
|
247
|
-
logpath = /var/log/auth.log
|
248
|
-
maxretry = 6
|
249
|
-
|
250
|
-
[ssh-ddos]
|
251
|
-
|
252
|
-
enabled = true
|
253
|
-
port = 987
|
254
|
-
filter = sshd-ddos
|
255
|
-
logpath = /var/log/auth.log
|
256
|
-
maxretry = 6
|
257
|
-
EOS
|
258
|
-
end
|
259
|
-
|
260
|
-
file :logwatch_config do
|
261
|
-
<<~EOS
|
262
|
-
Output = mail
|
263
|
-
MailTo = <%= admin_email %>
|
264
|
-
MailFrom = logwatch@host1.mydomain.org
|
265
|
-
Detail = Low
|
266
|
-
Service = All
|
267
|
-
EOS
|
268
|
-
end
|
269
|
-
|
270
|
-
file :sysctl_config do
|
271
|
-
<<~EOS
|
272
|
-
# Do not accept ICMP redirects (prevent MITM attacks)
|
273
|
-
net.ipv4.conf.all.accept_redirects = 0
|
274
|
-
net.ipv6.conf.all.accept_redirects = 0
|
275
|
-
# Do not send ICMP redirects (we are not a router)
|
276
|
-
net.ipv4.conf.all.send_redirects = 0
|
277
|
-
# Log Martian Packets
|
278
|
-
net.ipv4.conf.all.log_martians = 1
|
279
|
-
# Controls IP packet forwarding
|
280
|
-
net.ipv4.ip_forward = 0
|
281
|
-
# Controls source route verification
|
282
|
-
net.ipv4.conf.default.rp_filter = 1
|
283
|
-
# Do not accept source routing
|
284
|
-
net.ipv4.conf.default.accept_source_route = 0
|
285
|
-
# Controls the System Request debugging functionality of the kernel
|
286
|
-
kernel.sysrq = 0
|
287
|
-
# Controls whether core dumps will append the PID to the core filename
|
288
|
-
# Useful for debugging multi-threaded applications
|
289
|
-
kernel.core_uses_pid = 1
|
290
|
-
# Controls the use of TCP syncookies
|
291
|
-
net.ipv4.tcp_synack_retries = 2
|
292
|
-
######## IPv4 networking start ###########
|
293
|
-
# Send redirects, if router, but this is just server
|
294
|
-
net.ipv4.conf.all.send_redirects = 0
|
295
|
-
net.ipv4.conf.default.send_redirects = 0
|
296
|
-
# Accept packets with SRR option? No
|
297
|
-
net.ipv4.conf.all.accept_source_route = 0
|
298
|
-
# Accept Redirects? No, this is not router
|
299
|
-
net.ipv4.conf.all.accept_redirects = 0
|
300
|
-
net.ipv4.conf.all.secure_redirects = 0
|
301
|
-
# Log packets with impossible addresses to kernel log? Yes
|
302
|
-
net.ipv4.conf.all.log_martians = 1
|
303
|
-
net.ipv4.conf.default.accept_source_route = 0
|
304
|
-
net.ipv4.conf.default.accept_redirects = 0
|
305
|
-
net.ipv4.conf.default.secure_redirects = 0
|
306
|
-
# Ignore all ICMP ECHO and TIMESTAMP requests sent to it via broadcast/multicast
|
307
|
-
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
308
|
-
# Prevent against the common 'syn flood attack'
|
309
|
-
net.ipv4.tcp_syncookies = 1
|
310
|
-
# Enable source validation by reversed path, as specified in RFC1812
|
311
|
-
net.ipv4.conf.all.rp_filter = 1
|
312
|
-
net.ipv4.conf.default.rp_filter = 1
|
313
|
-
######## IPv6 networking start ###########
|
314
|
-
# Number of Router Solicitations to send until assuming no routers are present.
|
315
|
-
# This is host and not router
|
316
|
-
net.ipv6.conf.default.router_solicitations = 0
|
317
|
-
# Accept Router Preference in RA?
|
318
|
-
net.ipv6.conf.default.accept_ra_rtr_pref = 0
|
319
|
-
# Learn Prefix Information in Router Advertisement
|
320
|
-
net.ipv6.conf.default.accept_ra_pinfo = 0
|
321
|
-
# Setting controls whether the system will accept Hop Limit settings from a router advertisement
|
322
|
-
net.ipv6.conf.default.accept_ra_defrtr = 0
|
323
|
-
#router advertisements can cause the system to assign a global unicast address to an interface
|
324
|
-
net.ipv6.conf.default.autoconf = 0
|
325
|
-
#how many neighbor solicitations to send out per address?
|
326
|
-
net.ipv6.conf.default.dad_transmits = 0
|
327
|
-
# How many global unicast IPv6 addresses can be assigned to each interface?
|
328
|
-
net.ipv6.conf.default.max_addresses = 1
|
329
|
-
######## IPv6 networking ends ###########
|
330
|
-
# Disabled, not used anymore
|
331
|
-
#Enable ExecShield protection |
|
332
|
-
#kernel.exec-shield = 1
|
333
|
-
#kernel.randomize_va_space = 1
|
334
|
-
# TCP and memory optimization
|
335
|
-
# increase TCP max buffer size setable using setsockopt()
|
336
|
-
#net.ipv4.tcp_rmem = 4096 87380 8388608
|
337
|
-
#net.ipv4.tcp_wmem = 4096 87380 8388608
|
338
|
-
# increase Linux auto tuning TCP buffer limits
|
339
|
-
#net.core.rmem_max = 8388608
|
340
|
-
#net.core.wmem_max = 8388608
|
341
|
-
#net.core.netdev_max_backlog = 5000
|
342
|
-
#net.ipv4.tcp_window_scaling = 1
|
343
|
-
# increase system file descriptor limit
|
344
|
-
fs.file-max = 65535
|
345
|
-
#Allow for more PIDs
|
346
|
-
kernel.pid_max = 65536
|
347
|
-
#Increase system IP port limits
|
348
|
-
net.ipv4.ip_local_port_range = 2000 65000
|
349
|
-
# Disable IPv6 autoconf
|
350
|
-
#net.ipv6.conf.all.autoconf = 0
|
351
|
-
#net.ipv6.conf.default.autoconf = 0
|
352
|
-
#net.ipv6.conf.eth0.autoconf = 0
|
353
|
-
#net.ipv6.conf.all.accept_ra = 0
|
354
|
-
#net.ipv6.conf.default.accept_ra = 0
|
355
|
-
#net.ipv6.conf.eth0.accept_ra = 0
|
356
|
-
EOS
|
357
|
-
end
|
358
|
-
|
359
209
|
recipe :kamal_single_server do
|
360
210
|
name "Kamal Single Server Scaffold"
|
361
211
|
|
@@ -395,233 +245,50 @@ consist do
|
|
395
245
|
EOS
|
396
246
|
end
|
397
247
|
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
398
251
|
|
399
|
-
|
400
|
-
|
401
|
-
required_user :root
|
402
|
-
|
403
|
-
upload_file message: "Uploading APT config...",
|
404
|
-
local_file: :apt_auto_upgrade,
|
405
|
-
remote_path: "/etc/apt/apt.conf.d/20auto-upgrades"
|
406
|
-
|
407
|
-
shell do
|
408
|
-
<<~EOS
|
409
|
-
apt-get update && apt-get upgrade -y
|
410
|
-
apt-get autoremove
|
411
|
-
apt-get autoclean
|
412
|
-
EOS
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
step :install_apt_packages do
|
417
|
-
name "Installing essential APT packages"
|
418
|
-
required_user :root
|
419
|
-
|
420
|
-
shell "Installing essential packages" do
|
421
|
-
<<~EOS
|
422
|
-
apt-get -y install build-essential curl git vim
|
423
|
-
EOS
|
424
|
-
end
|
425
|
-
end
|
426
|
-
|
427
|
-
step :setup_ntp do
|
428
|
-
name "Installing NTP daemon"
|
429
|
-
required_user :root
|
430
|
-
|
431
|
-
shell "Configuring NTP daemon", params: {raise_on_non_zero_exit: false} do
|
432
|
-
<<~EOS
|
433
|
-
apt-get -y remove systemd-timesyncd
|
434
|
-
timedatectl set-ntp no 2>1
|
435
|
-
apt-get -y install ntp
|
436
|
-
EOS
|
437
|
-
end
|
438
|
-
|
439
|
-
shell "Start NTP and Fail2Ban" do
|
440
|
-
<<~EOS
|
441
|
-
service ntp restart
|
442
|
-
EOS
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
step :install_fail2ban do
|
447
|
-
name "Installing fail2ban"
|
448
|
-
required_user :root
|
449
|
-
|
450
|
-
shell "Installing essential packages" do
|
451
|
-
<<~EOS
|
452
|
-
apt-get -y install fail2ban
|
453
|
-
EOS
|
454
|
-
end
|
455
|
-
|
456
|
-
upload_file message: "Uploading fail2ban confing", local_file: :fail2ban_config,
|
457
|
-
remote_path: "/etc/fail2ban/jail.local"
|
458
|
-
|
459
|
-
shell "Start Fail2Ban" do
|
460
|
-
<<~EOS
|
461
|
-
service fail2ban restart
|
462
|
-
systemctl enable fail2ban.service
|
463
|
-
EOS
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
step :setup_swap do
|
468
|
-
name "Configure and enable the swapfile"
|
469
|
-
required_user :root
|
470
|
-
|
471
|
-
check status: :nonexistant, file: "/swapfile" do
|
472
|
-
shell do
|
473
|
-
<<~EOS
|
474
|
-
fallocate -l <%= swap_size %> /swapfile
|
475
|
-
chmod 600 /swapfile
|
476
|
-
mkswap /swapfile
|
477
|
-
swapon /swapfile
|
478
|
-
echo "\n/swapfile swap swap defaults 0 0\n" >> /etc/fstab
|
479
|
-
sysctl vm.swappiness=<%=swap_swappiness%>
|
480
|
-
echo "\nvm.swappiness=<%=swap_swappiness%>\n" >> /etc/sysctl.conf
|
481
|
-
EOS
|
482
|
-
end
|
483
|
-
end
|
484
|
-
end
|
485
|
-
|
486
|
-
step :harden_ssh do
|
487
|
-
name "Harden the SSH config"
|
488
|
-
|
489
|
-
mutate_file mode: :replace, target_file: "/etc/ssh/sshd_config", match: "^#PasswordAuthentication yes$",
|
490
|
-
target_string: "PasswordAuthentication no"
|
491
|
-
mutate_file mode: :replace, target_file: "/etc/ssh/sshd_config", match: "^#PubkeyAuthentication yes$",
|
492
|
-
target_string: "PubkeyAuthentication yes"
|
493
|
-
|
494
|
-
shell do
|
495
|
-
<<~EOS
|
496
|
-
service ssh restart
|
497
|
-
EOS
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
step :harden_system do
|
502
|
-
name "Harden the SYSCTL settings"
|
503
|
-
|
504
|
-
upload_file message: "Uploading sysctl config...",
|
505
|
-
local_file: :sysctl_config,
|
506
|
-
remote_path: "/tmp/sysctl_config"
|
507
|
-
|
508
|
-
shell do
|
509
|
-
<<~EOS
|
510
|
-
cat /etc/sysctl.conf /tmp/sysctl_config > /etc/sysctl.conf
|
511
|
-
rm /tmp/sysctl_config
|
512
|
-
sysctl -p
|
513
|
-
EOS
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
step :setup_ufw do
|
518
|
-
name "Setup UFW"
|
519
|
-
|
520
|
-
shell do
|
521
|
-
<<~EOS
|
522
|
-
ufw logging on
|
523
|
-
ufw default deny incoming
|
524
|
-
ufw default allow outgoing
|
525
|
-
ufw allow 22
|
526
|
-
ufw allow 80
|
527
|
-
ufw allow 443
|
528
|
-
ufw --force enable
|
529
|
-
service ufw restart
|
530
|
-
EOS
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
|
-
step :setup_postfix do
|
535
|
-
name "Install Postfix for admin emails"
|
252
|
+
# vim: filetype=ruby
|
253
|
+
```
|
536
254
|
|
537
|
-
|
538
|
-
|
539
|
-
echo "postfix postfix/mailname string <%= site_fqdn %>" | debconf-set-selections
|
540
|
-
EOS
|
541
|
-
end
|
255
|
+
Given a `Consistfile` you could then say `consist up <ip_address>` and
|
256
|
+
it would just work.
|
542
257
|
|
543
|
-
|
544
|
-
<<~EOS
|
545
|
-
echo "postfix postfix/main_mailer_type string 'Internet Site'" | debconf-set-selections
|
546
|
-
EOS
|
547
|
-
end
|
258
|
+
### Artifacts
|
548
259
|
|
549
|
-
|
550
|
-
<<~EOS
|
551
|
-
DEBIAN_FRONTEND=noninteractive apt-get install --assume-yes postfix
|
552
|
-
EOS
|
553
|
-
end
|
554
|
-
end
|
260
|
+
Artifacts allow you to split out your `Consistfile` into separate files.
|
555
261
|
|
556
|
-
|
557
|
-
|
262
|
+
You can create blocks in the `Consistfile` as shown above, but you can also only
|
263
|
+
specify an `id`, and that `id` will be used to try and attempt to load a file of that
|
264
|
+
name in the `.consist/<type>/<id>` directory. For example, referencing a file:
|
558
265
|
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
EOS
|
563
|
-
end
|
266
|
+
```ruby
|
267
|
+
file :apt_auto_upgrade
|
268
|
+
```
|
564
269
|
|
565
|
-
|
566
|
-
|
270
|
+
Will attempt to load a file in `.consist/files/apt_auto_upgrade`. The same is
|
271
|
+
possible for any of the main types: `files`, `steps`, and `recipes`
|
567
272
|
|
568
|
-
|
569
|
-
remote_path: "/etc/logwatch/conf"
|
570
|
-
end
|
273
|
+
### `.consist` directory
|
571
274
|
|
572
|
-
|
573
|
-
|
275
|
+
The `.consist` directory is assumed to be in the root of your project, and should
|
276
|
+
contain three subdirectories for each of the types: `files`, `steps`, `recipes`.
|
574
277
|
|
575
|
-
|
576
|
-
|
577
|
-
# Add Docker's official GPG key:
|
578
|
-
apt-get update
|
579
|
-
apt-get install ca-certificates curl gnupg -y
|
580
|
-
install -m 0755 -d /etc/apt/keyrings
|
581
|
-
rm /etc/apt/keyrings/docker.gpg
|
582
|
-
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --batch --no-tty --dearmor -o /etc/apt/keyrings/docker.gpg
|
583
|
-
chmod a+r /etc/apt/keyrings/docker.gpg
|
584
|
-
|
585
|
-
# Add the repository to Apt sources:
|
586
|
-
echo \
|
587
|
-
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
588
|
-
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
589
|
-
tee /etc/apt/sources.list.d/docker.list > /dev/null
|
590
|
-
apt-get update
|
591
|
-
|
592
|
-
# Install Docker
|
593
|
-
apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
|
594
|
-
|
595
|
-
# Make Docker start on boot
|
596
|
-
sudo systemctl enable docker.service
|
597
|
-
sudo systemctl enable containerd.service
|
598
|
-
EOS
|
599
|
-
end
|
278
|
+
You can specify an alternate directory location by passing the `--consistdir` switch
|
279
|
+
to the `up` command.
|
600
280
|
|
601
|
-
|
602
|
-
<<~EOS
|
603
|
-
# Create group
|
604
|
-
sudo groupadd docker
|
605
|
-
sudo usermod -aG docker $USER
|
606
|
-
EOS
|
607
|
-
end
|
281
|
+
## Community Consistfiles
|
608
282
|
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
docker network create private
|
613
|
-
EOS
|
614
|
-
end
|
615
|
-
end
|
616
|
-
end
|
617
|
-
end
|
618
|
-
end
|
283
|
+
If you create a Github repo, and all it contains is a `Consistfile` and any associated
|
284
|
+
artifacts under a `.consist` directory, other people will be able to use it by
|
285
|
+
executing `consist init <gh_repo_path>` in their project root.
|
619
286
|
|
620
|
-
|
621
|
-
```
|
287
|
+
If you create one, please open a PR to include it here:
|
622
288
|
|
623
|
-
|
624
|
-
|
289
|
+
| Name | Repo | Description |
|
290
|
+
| ------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------- |
|
291
|
+
| Kamal Single Server Setup | [consist-sh/kamal-single-server](https://github.com/consist-sh/kamal-single-server) | Setup a single server with good defaults ready to run Kamal |
|
625
292
|
|
626
293
|
## Is it good?
|
627
294
|
|
data/lib/consist/cli.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
1
3
|
require "thor"
|
2
4
|
require "sshkit"
|
3
5
|
require "sshkit/dsl"
|
4
6
|
|
7
|
+
require "consist/utils"
|
8
|
+
require "consist/resolver"
|
5
9
|
require "consist/recipe"
|
6
|
-
require "consist/recipes"
|
7
10
|
require "consist/step"
|
8
11
|
require "consist/consistfile"
|
9
12
|
require "consist/commands/includes/stream_logger"
|
@@ -16,16 +19,22 @@ require "consist/commands/check"
|
|
16
19
|
module Consist
|
17
20
|
class CLI < Thor
|
18
21
|
extend ThorExt::Start
|
22
|
+
include Thor::Actions
|
23
|
+
include SSHKit::DSL
|
19
24
|
|
20
25
|
map %w[-v --version] => "version"
|
21
26
|
|
27
|
+
def self.source_root
|
28
|
+
File.dirname(__FILE__)
|
29
|
+
end
|
30
|
+
|
22
31
|
desc "version", "Display consist version"
|
23
32
|
def version
|
24
33
|
say "consist/#{VERSION} #{RUBY_DESCRIPTION}"
|
25
34
|
end
|
26
35
|
|
27
|
-
desc "
|
28
|
-
def
|
36
|
+
desc "ping", "Attempt to connect to a server and execute an idempotent statement."
|
37
|
+
def ping(user, server)
|
29
38
|
puts "---> Attempting to connect to #{server} as #{user}"
|
30
39
|
on("#{user}@#{server}") do
|
31
40
|
as user do
|
@@ -34,18 +43,28 @@ module Consist
|
|
34
43
|
end
|
35
44
|
end
|
36
45
|
|
37
|
-
desc "scaffold", "Apply a given recipe to (a) server(s)"
|
38
|
-
def scaffold(_recipe, server_ip)
|
39
|
-
Consist::Recipes.new(server_ip)
|
40
|
-
end
|
41
|
-
|
42
46
|
option :step, type: :string
|
43
47
|
option :consistfile, type: :string
|
48
|
+
option :consistdir, type: :string
|
44
49
|
desc "up", "Run a Consistfile against a server"
|
45
50
|
def up(server_ip)
|
46
51
|
specified_step = options[:step]
|
47
52
|
consistfile = options[:consistfile]
|
48
|
-
|
53
|
+
consist_dir = options[:consistdir]
|
54
|
+
Consist::Consistfile.new(server_ip, consist_dir:, consistfile:, specified_step:)
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "init", "Initialize a project with Consist, optionally specifying a GH path to a Consistfile"
|
58
|
+
def init(gh_path = nil)
|
59
|
+
if gh_path
|
60
|
+
full_url = "https://github.com/#{gh_path}"
|
61
|
+
Consist::Utils.clone_repo_contents(full_url, Dir.pwd)
|
62
|
+
else
|
63
|
+
puts "Creating new Consistfile..."
|
64
|
+
directory "templates/.consist", File.join(Dir.pwd, ".consist")
|
65
|
+
template "templates/Consistfile.tt", File.join(Dir.pwd, "Consistfile")
|
66
|
+
puts "...done"
|
67
|
+
end
|
49
68
|
end
|
50
69
|
end
|
51
70
|
end
|
data/lib/consist/consistfile.rb
CHANGED
@@ -2,18 +2,21 @@
|
|
2
2
|
|
3
3
|
module Consist
|
4
4
|
class << self
|
5
|
-
attr_accessor :files, :config
|
5
|
+
attr_accessor :files, :config, :consist_dir
|
6
6
|
end
|
7
7
|
|
8
8
|
@files = []
|
9
9
|
@config = {}
|
10
|
+
@consist_dir = ""
|
10
11
|
|
11
12
|
class Consistfile
|
12
13
|
include SSHKit::DSL
|
13
14
|
|
14
|
-
def initialize(server_ip, specified_step:, consistfile:)
|
15
|
+
def initialize(server_ip, specified_step:, consist_dir:, consistfile:)
|
15
16
|
@server_ip = server_ip
|
16
17
|
@specified_step = specified_step
|
18
|
+
Consist.consist_dir = consist_dir || ".consist"
|
19
|
+
|
17
20
|
consistfile_path = if consistfile
|
18
21
|
File.expand_path(consistfile, Dir.pwd)
|
19
22
|
else
|
@@ -30,7 +33,6 @@ module Consist
|
|
30
33
|
|
31
34
|
def recipe(id, &definition)
|
32
35
|
recipe = Consist::Recipe.new(id, &definition)
|
33
|
-
|
34
36
|
puts "Executing Recipe: #{recipe.name}"
|
35
37
|
|
36
38
|
if @specified_step.nil?
|
@@ -47,9 +49,12 @@ module Consist
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def file(id, &definition)
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
if definition
|
53
|
+
contents = yield
|
54
|
+
else
|
55
|
+
file_contents = Consist::Resolver.new(pwd: Dir.pwd).resolve_artifact(type: :file, id:)
|
56
|
+
contents = file_contents
|
57
|
+
end
|
53
58
|
|
54
59
|
Consist.files << {id:, contents:}
|
55
60
|
end
|
data/lib/consist/recipe.rb
CHANGED
@@ -2,9 +2,16 @@
|
|
2
2
|
|
3
3
|
module Consist
|
4
4
|
class Recipe
|
5
|
-
def initialize(
|
5
|
+
def initialize(id = nil, &definition)
|
6
6
|
@steps = []
|
7
|
-
|
7
|
+
@id = id
|
8
|
+
|
9
|
+
if definition
|
10
|
+
instance_eval(&definition)
|
11
|
+
else
|
12
|
+
contents = Consist::Resolver.new(pwd: Dir.pwd).resolve_artifact(type: :recipe, id:)
|
13
|
+
instance_eval(contents)
|
14
|
+
end
|
8
15
|
end
|
9
16
|
|
10
17
|
def name(name = nil)
|
@@ -22,21 +29,13 @@ module Consist
|
|
22
29
|
@user
|
23
30
|
end
|
24
31
|
|
25
|
-
def steps(&
|
26
|
-
instance_eval(&
|
32
|
+
def steps(&definition)
|
33
|
+
instance_eval(&definition) if definition
|
27
34
|
@steps
|
28
35
|
end
|
29
36
|
|
30
|
-
def step(
|
31
|
-
|
32
|
-
@steps << Step.new(id: step_name, &block)
|
33
|
-
return
|
34
|
-
end
|
35
|
-
|
36
|
-
target_path = File.join("../../", "steps", step_name.to_s, "step.rb")
|
37
|
-
step_path = File.expand_path(target_path, __FILE__)
|
38
|
-
step_content = File.read(step_path)
|
39
|
-
@steps << Step.new(id: step_name) { instance_eval(step_content) }
|
37
|
+
def step(id, &definition)
|
38
|
+
@steps << Step.new(id:, &definition)
|
40
39
|
end
|
41
40
|
end
|
42
41
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Consist
|
4
|
+
class Resolver
|
5
|
+
def initialize(pwd:)
|
6
|
+
@pwd = pwd
|
7
|
+
end
|
8
|
+
|
9
|
+
def resolve_artifact(type:, id:)
|
10
|
+
file_name = %i[recipe step].include?(type) ? "#{id}.rb" : id.to_s
|
11
|
+
target_path = File.join(Consist.consist_dir, "#{type}s", file_name)
|
12
|
+
artifact_path = File.expand_path(target_path, @pwd)
|
13
|
+
File.read(artifact_path)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/consist/step.rb
CHANGED
@@ -6,11 +6,17 @@ module Consist
|
|
6
6
|
class Step
|
7
7
|
include SSHKit::DSL
|
8
8
|
|
9
|
-
def initialize(id:, &
|
9
|
+
def initialize(id:, &definition)
|
10
10
|
@commands = []
|
11
11
|
@id = id
|
12
12
|
@required_user = :root
|
13
|
-
|
13
|
+
|
14
|
+
if definition
|
15
|
+
instance_eval(&definition)
|
16
|
+
else
|
17
|
+
contents = Consist::Resolver.new(pwd: Dir.pwd).resolve_artifact(type: :step, id:)
|
18
|
+
instance_eval(contents)
|
19
|
+
end
|
14
20
|
end
|
15
21
|
|
16
22
|
def id(id = nil)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
consist do
|
2
|
+
config :example, "example"
|
3
|
+
|
4
|
+
# Define a recipe block:
|
5
|
+
#
|
6
|
+
# recipe :my_recipe do
|
7
|
+
# name "My recipe"
|
8
|
+
#
|
9
|
+
# steps do
|
10
|
+
# step do
|
11
|
+
# shell do
|
12
|
+
# <<~EOS
|
13
|
+
# echo 'hello'
|
14
|
+
# EOS
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "tmpdir"
|
3
|
+
|
4
|
+
module Consist
|
5
|
+
module Utils
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def clone_repo_contents(source_repo, target_dir)
|
9
|
+
temp_dir = Dir.mktmpdir
|
10
|
+
|
11
|
+
puts "Using #{source_repo} as template to initialize Consistfile..."
|
12
|
+
system("git clone --depth=1 #{source_repo} #{temp_dir} >/dev/null 2>&1")
|
13
|
+
|
14
|
+
Dir.foreach(temp_dir) do |item|
|
15
|
+
next if [".", "..", ".git"].include?(item)
|
16
|
+
next unless ["Consistfile", ".consist"].include?(item)
|
17
|
+
|
18
|
+
source_path = File.join(temp_dir, item)
|
19
|
+
target_path = File.join(target_dir, item)
|
20
|
+
FileUtils.cp_r(source_path, target_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
FileUtils.remove_entry_secure(temp_dir)
|
24
|
+
|
25
|
+
puts "...done"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/consist/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: consist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John McDowall
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-ssh
|
@@ -73,14 +73,12 @@ files:
|
|
73
73
|
- lib/consist/commands/upload.rb
|
74
74
|
- lib/consist/consistfile.rb
|
75
75
|
- lib/consist/recipe.rb
|
76
|
-
- lib/consist/
|
76
|
+
- lib/consist/resolver.rb
|
77
77
|
- lib/consist/step.rb
|
78
|
+
- lib/consist/templates/Consistfile.tt
|
78
79
|
- lib/consist/thor_ext.rb
|
80
|
+
- lib/consist/utils.rb
|
79
81
|
- lib/consist/version.rb
|
80
|
-
- lib/recipes/kamal_single_server.rb
|
81
|
-
- lib/steps/install_apt_packages/step.rb
|
82
|
-
- lib/steps/update_apt_packages/apt_auto_upgrades
|
83
|
-
- lib/steps/update_apt_packages/step.rb
|
84
82
|
homepage: https://github.com/consist-sh/consist
|
85
83
|
licenses:
|
86
84
|
- LGPL-3.0
|
data/lib/consist/recipes.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Consist
|
4
|
-
class Recipes
|
5
|
-
include SSHKit::DSL
|
6
|
-
|
7
|
-
def initialize(server_ip)
|
8
|
-
recipe_directory = File.expand_path("../recipes", __dir__)
|
9
|
-
recipes = Dir[File.join(recipe_directory, "*.rb")]
|
10
|
-
|
11
|
-
recipes.each do |recipe_file|
|
12
|
-
recipe_content = File.read(recipe_file)
|
13
|
-
recipe = Recipe.new { instance_eval(recipe_content) }
|
14
|
-
|
15
|
-
puts "Executing Recipe: #{recipe.name}"
|
16
|
-
recipe.steps.each do |step|
|
17
|
-
puts "Executing Step: #{step.name}"
|
18
|
-
|
19
|
-
on("#{step.required_user}@#{server_ip}") do
|
20
|
-
step.perform(self)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
puts "Execution of #{recipe.name} has completed."
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
name "Install APT packages"
|
2
|
-
required_user :root
|
3
|
-
|
4
|
-
shell "Installing essential packages" do
|
5
|
-
<<~EOS
|
6
|
-
apt-get -y remove systemd-timesyncd
|
7
|
-
timedatectl set-ntp no
|
8
|
-
apt-get -y install build-essential curl fail2ban git ntp vim
|
9
|
-
apt-get autoremove
|
10
|
-
apt-get autoclean
|
11
|
-
EOS
|
12
|
-
end
|
13
|
-
|
14
|
-
shell "Start NTP and Fail2Ban" do
|
15
|
-
<<~EOS
|
16
|
-
service ntp restart
|
17
|
-
service fail2ban restart
|
18
|
-
EOS
|
19
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
name "Update the APT packages"
|
2
|
-
required_user :root
|
3
|
-
|
4
|
-
upload_file message: "Uploading APT config...", local_file: "apt_auto_upgrades",
|
5
|
-
remote_path: "/etc/apt/apt.conf.d/20auto-upgrades"
|
6
|
-
|
7
|
-
shell do
|
8
|
-
<<~EOS
|
9
|
-
apt-get update && apt-get upgrade -y
|
10
|
-
EOS
|
11
|
-
end
|