consist 0.1.0 → 0.1.2
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/LICENSE.txt +256 -21
- data/README.md +624 -9
- data/lib/consist/cli.rb +38 -1
- data/lib/consist/commands/check.rb +36 -0
- data/lib/consist/commands/exec.rb +20 -0
- data/lib/consist/commands/includes/erbable.rb +24 -0
- data/lib/consist/commands/includes/stream_logger.rb +21 -0
- data/lib/consist/commands/mutate.rb +27 -0
- data/lib/consist/commands/upload.rb +37 -0
- data/lib/consist/consistfile.rb +71 -0
- data/lib/consist/recipe.rb +42 -0
- data/lib/consist/recipes.rb +28 -0
- data/lib/consist/step.rb +74 -0
- data/lib/consist/thor_ext.rb +2 -2
- data/lib/consist/version.rb +1 -1
- data/lib/recipes/kamal_single_server.rb +8 -0
- data/lib/steps/install_apt_packages/step.rb +19 -0
- data/lib/steps/update_apt_packages/apt_auto_upgrades +3 -0
- data/lib/steps/update_apt_packages/step.rb +11 -0
- metadata +51 -9
data/README.md
CHANGED
@@ -2,41 +2,656 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/consist)
|
4
4
|
[](https://www.ruby-toolbox.com/projects/consist)
|
5
|
-
[](https://github.com/consist-sh/consist/actions/workflows/ci.yml)
|
6
|
+
[](https://codeclimate.com/github/consist-sh/consist)
|
7
7
|
|
8
|
-
|
8
|
+
**THIS IS BETA SOFTWARE UNDER ACTIVE DEVELOPMENT. APIs AND FEATURES WILL CHANGE.**
|
9
|
+
|
10
|
+
> consist - (noun): a set of railroad vehicles forming a complete train.
|
11
|
+
|
12
|
+
`consist` is the one person framework server scaffolder. It is stone age tech.
|
13
|
+
|
14
|
+
You can use it to quickly baseline a raw server using a given recipe provided
|
15
|
+
by Consist. I use it to baseline new Droplets to be ready to run Kamal in
|
16
|
+
single server setup for a Rails monolith. While Kamal will setup Docker
|
17
|
+
for you, it does not do anything else related to configuring the underlying
|
18
|
+
server, such as firewalls, general hardening, enabling swapfile etc.
|
19
|
+
|
20
|
+
## Project Principles
|
21
|
+
|
22
|
+
- Minimal tool specific language / knowledge required to use Consist
|
23
|
+
- Procedural declaration execution - no converging, orchestration or
|
24
|
+
event driven operation
|
25
|
+
- If you can shell script it, you can `consist` it directly
|
9
26
|
|
10
27
|
---
|
11
28
|
|
12
29
|
- [Quick start](#quick-start)
|
13
30
|
- [Support](#support)
|
31
|
+
- [Rationale](#rationale)
|
32
|
+
- [Key Concepts](#key-concepts)
|
33
|
+
- [Is It Good?](#is-it-good%3F)
|
14
34
|
- [License](#license)
|
15
35
|
- [Code of conduct](#code-of-conduct)
|
16
36
|
- [Contribution guide](#contribution-guide)
|
17
37
|
|
18
38
|
## Quick start
|
19
39
|
|
20
|
-
```
|
40
|
+
```sh
|
21
41
|
gem install consist
|
22
42
|
```
|
23
43
|
|
44
|
+
You must be already auth'd with the server you want to scaffold. `consist` will use
|
45
|
+
your SSH id to perform actions.
|
46
|
+
|
47
|
+
Then, you have two ways of interacting with Consist. First is the `scaffold` command:
|
48
|
+
|
49
|
+
```sh
|
50
|
+
consist scaffold <recipe_name> root <ip_address>
|
51
|
+
```
|
52
|
+
|
53
|
+
Will kick off the scaffolding of that given server with the given
|
54
|
+
recipe, using the `root` user.
|
55
|
+
|
56
|
+
The other way of using `consist` is to go with a `Consistfile` in
|
57
|
+
your project root that describes the recipe and steps. Then you can say:
|
58
|
+
|
59
|
+
```sh
|
60
|
+
consist up <ip_address>
|
61
|
+
```
|
62
|
+
|
63
|
+
And `consist` will do it's thing with that given IP address.
|
64
|
+
|
65
|
+
## Features
|
66
|
+
|
67
|
+
- Simple Ruby based DSL
|
68
|
+
- ERB interpolation of config on shell commands and file contents
|
69
|
+
- Small API surface area - quick to learn
|
70
|
+
|
71
|
+
## Rationale
|
72
|
+
|
73
|
+
I wanted a super-simple tool, that was baked in Ruby, for setting up
|
74
|
+
random servers to specific configurations. This is the result.
|
75
|
+
|
76
|
+
On a scale of 1 to 10, with 10 being Terraform, this tool is basically
|
77
|
+
as low-rent you can get to hand running scripts yourself, so about a 3
|
78
|
+
on the scale.
|
79
|
+
|
80
|
+
If you know how to shell script what you want, you can stick it in a step,
|
81
|
+
and add it to a recipe.
|
82
|
+
|
83
|
+
The more I work in this industry, the less I see using other people's code
|
84
|
+
and tools as a benefit, and more of a liability. I appreciate the paradox I'm
|
85
|
+
creating here for you 😅
|
86
|
+
|
87
|
+
### Why not use Terraform / Ansible / Salt etc?
|
88
|
+
|
89
|
+
I think they are bad tools for my needs. I wanted something simple
|
90
|
+
I could hack on, grow only when needed, and will work specifically
|
91
|
+
without ambiguity. For example, Ansible has a lot of nonsense with case sensitivity,
|
92
|
+
Terraform does [weird unexpected things](https://github.com/hashicorp/terraform/issues/16330).
|
93
|
+
|
94
|
+
I didn't want to keep maintaining specific knowledge of these infrastructure
|
95
|
+
as code tools in my brain anymore, along with all of their peculiarities and oddities.
|
96
|
+
|
97
|
+
**If you prefer those tools, go ahead and use them.**
|
98
|
+
|
99
|
+
Ain't nobody stopping you.
|
100
|
+
|
101
|
+
## Key Concepts
|
102
|
+
|
103
|
+
Consist leans on three primary ideas: recipes, steps and files. Recipes contain
|
104
|
+
one or more steps. Steps tend to be atomic and idempotent.
|
105
|
+
|
106
|
+
### Recipes
|
107
|
+
|
108
|
+
Example of a recipe:
|
109
|
+
|
24
110
|
```ruby
|
25
|
-
|
111
|
+
name "Kamal Single Server"
|
112
|
+
description "Sets up a single server to run Kamal"
|
113
|
+
user :root
|
114
|
+
|
115
|
+
steps do
|
116
|
+
step :update_apt_packages
|
117
|
+
step :install_apt_packages
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
### Steps
|
122
|
+
|
123
|
+
Example of a step:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
name "Install APT packages"
|
127
|
+
required_user :root
|
128
|
+
|
129
|
+
shell "Installing essential packages" do
|
130
|
+
<<~EOS
|
131
|
+
apt-get -y remove systemd-timesyncd
|
132
|
+
timedatectl set-ntp no
|
133
|
+
apt-get -y install build-essential curl fail2ban git ntp vim
|
134
|
+
apt-get autoremove
|
135
|
+
apt-get autoclean
|
136
|
+
EOS
|
137
|
+
end
|
138
|
+
|
139
|
+
shell "Start NTP and Fail2Ban" do
|
140
|
+
<<~EOS
|
141
|
+
service ntp restart
|
142
|
+
service fail2ban restart
|
143
|
+
EOS
|
144
|
+
end
|
26
145
|
```
|
27
146
|
|
147
|
+
### Files
|
148
|
+
|
149
|
+
Example of a file:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
file :hostname do
|
153
|
+
<<~EOS
|
154
|
+
<%= hostname %>
|
155
|
+
EOS
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
### Consistfile
|
160
|
+
|
161
|
+
A `Consistfile` is a portable giant file of a recipe and all its
|
162
|
+
steps. Something like (this is a _full_ example, in practice you
|
163
|
+
would reference some of Consist's built in steps):
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
consist do
|
167
|
+
config :hostname, "testexample.com"
|
168
|
+
config :site_fqdn, "textexample.com"
|
169
|
+
config :admin_email, "j@jmd.fm"
|
170
|
+
config :swap_size, "2G"
|
171
|
+
config :swap_swappiness, "60"
|
172
|
+
config :timezone, "UTC"
|
173
|
+
|
174
|
+
file :apt_auto_upgrade do
|
175
|
+
<<~EOS
|
176
|
+
APT::Periodic::AutocleanInterval "7";
|
177
|
+
APT::Periodic::Update-Package-Lists "1";
|
178
|
+
APT::Periodic::Unattended-Upgrade "1";
|
179
|
+
EOS
|
180
|
+
end
|
181
|
+
|
182
|
+
file :hostname do
|
183
|
+
<<~EOS
|
184
|
+
<%= hostname %>
|
185
|
+
EOS
|
186
|
+
end
|
187
|
+
|
188
|
+
file :timezone do
|
189
|
+
<<~EOS
|
190
|
+
<%= timezone %>
|
191
|
+
EOS
|
192
|
+
end
|
193
|
+
|
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
|
+
recipe :kamal_single_server do
|
360
|
+
name "Kamal Single Server Scaffold"
|
361
|
+
|
362
|
+
steps do
|
363
|
+
step :set_hostname do
|
364
|
+
upload_file message: "Setting hostname",
|
365
|
+
local_file: :hostname,
|
366
|
+
remote_path: "/etc/hostname"
|
367
|
+
|
368
|
+
shell do
|
369
|
+
<<~EOS
|
370
|
+
hostname <%= Consist.config[:hostname] %>
|
371
|
+
EOS
|
372
|
+
end
|
373
|
+
|
374
|
+
mutate_file mode: :replace, target_file: "/etc/hosts", match: "^127.0.0.1 localhost$",
|
375
|
+
target_string: "127.0.0.1 localhost <%= hostname %>"
|
376
|
+
end
|
377
|
+
|
378
|
+
step :setup_timezone do
|
379
|
+
shell do
|
380
|
+
<<~EOS
|
381
|
+
rm /etc/localtime
|
382
|
+
EOS
|
383
|
+
end
|
384
|
+
|
385
|
+
upload_file message: "Setting Timezone",
|
386
|
+
local_file: :timezone,
|
387
|
+
remote_path: "/etc/timezone"
|
388
|
+
|
389
|
+
shell do
|
390
|
+
<<~EOS
|
391
|
+
chmod 0644 /etc/timezone
|
392
|
+
ln -s /usr/share/zoneinfo/<%= timezone %> /etc/localtime
|
393
|
+
chmod 0644 /etc/localtime
|
394
|
+
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure -f noninteractive tzdata
|
395
|
+
EOS
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
step :update_apt_packages do
|
400
|
+
name "Updating APT packages"
|
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"
|
536
|
+
|
537
|
+
shell do
|
538
|
+
<<~EOS
|
539
|
+
echo "postfix postfix/mailname string <%= site_fqdn %>" | debconf-set-selections
|
540
|
+
EOS
|
541
|
+
end
|
542
|
+
|
543
|
+
shell do
|
544
|
+
<<~EOS
|
545
|
+
echo "postfix postfix/main_mailer_type string 'Internet Site'" | debconf-set-selections
|
546
|
+
EOS
|
547
|
+
end
|
548
|
+
|
549
|
+
shell do
|
550
|
+
<<~EOS
|
551
|
+
DEBIAN_FRONTEND=noninteractive apt-get install --assume-yes postfix
|
552
|
+
EOS
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
step :setup_logwatch do
|
557
|
+
name "Setup Logwatch to automate log reporting"
|
558
|
+
|
559
|
+
shell do
|
560
|
+
<<~EOS
|
561
|
+
DEBIAN_FRONTEND=noninteractive apt-get install --assume-yes logwatch
|
562
|
+
EOS
|
563
|
+
end
|
564
|
+
|
565
|
+
mutate_file mode: :replace, target_file: "/etc/cron.daily/00logwatch", match: "^/usr/sbin/logwatch --output mail$",
|
566
|
+
target_string: "/usr/sbin/logwatch --output mail --mailto <%= admin_email %> --detail high", delim: "#"
|
567
|
+
|
568
|
+
upload_file message: "Uploading Logwatch confing", local_file: :logwatch_config,
|
569
|
+
remote_path: "/etc/logwatch/conf"
|
570
|
+
end
|
571
|
+
|
572
|
+
step :setup_docker do
|
573
|
+
name "Setup Docker"
|
574
|
+
|
575
|
+
shell do
|
576
|
+
<<~EOS
|
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
|
600
|
+
|
601
|
+
shell "Create docker group", params: {raise_on_non_zero_exit: false} do
|
602
|
+
<<~EOS
|
603
|
+
# Create group
|
604
|
+
sudo groupadd docker
|
605
|
+
sudo usermod -aG docker $USER
|
606
|
+
EOS
|
607
|
+
end
|
608
|
+
|
609
|
+
shell "Create default private network", params: {raise_on_non_zero_exit: false} do
|
610
|
+
<<~EOS
|
611
|
+
# Create default private network
|
612
|
+
docker network create private
|
613
|
+
EOS
|
614
|
+
end
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
# vim: filetype=ruby
|
621
|
+
```
|
622
|
+
|
623
|
+
Given a `Consistfile` you could then say `consist up <ip_address>` and
|
624
|
+
it would just work.
|
625
|
+
|
626
|
+
## Is it good?
|
627
|
+
|
628
|
+
I think so. But I don't know, use your own brain or something. Don't listen to
|
629
|
+
me.
|
630
|
+
|
28
631
|
## Support
|
29
632
|
|
30
|
-
If you want to report a bug, or have ideas, feedback or questions about the gem,
|
633
|
+
If you want to report a bug, or have ideas, feedback or questions about the gem,
|
634
|
+
[let me know via GitHub issues](https://github.com/johnmcdowall/consist/issues/new)
|
635
|
+
and I will do my best to provide a helpful answer.
|
31
636
|
|
32
637
|
## License
|
33
638
|
|
34
|
-
The gem is available as open source under the terms of the [
|
639
|
+
The gem is available as open source under the terms of the [LGPLv3 License](LICENSE.txt).
|
35
640
|
|
36
641
|
## Code of conduct
|
37
642
|
|
38
|
-
Everyone interacting in this project’s codebases, issue trackers, chat
|
643
|
+
Everyone interacting in this project’s codebases, issue trackers, chat
|
644
|
+
rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
|
39
645
|
|
40
646
|
## Contribution guide
|
41
647
|
|
42
|
-
Pull requests are welcome
|
648
|
+
Pull requests are welcome, but I want you to open an Issue first to discuss your
|
649
|
+
ideas. Thanks.
|
650
|
+
|
651
|
+
## Development
|
652
|
+
|
653
|
+
1. Clone the repo
|
654
|
+
2. Run `bundle install`
|
655
|
+
3. Run `bin/dev` to execute consist locally without having to build and install.
|
656
|
+
|
657
|
+
Make sure any PRs have been formatted with `standard`.
|