summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorclarkzjw <[email protected]>2023-02-08 00:40:09 -0800
committerclarkzjw <[email protected]>2023-02-08 00:40:09 -0800
commit1204730924436ef9e1c7c49c9557837f9a5ed0e8 (patch)
tree129d79dfd11245751cee6d4082ff5d2f6e941610 /ansible/roles/common
parent9635ac4dedf69de5bff65785bcc16bef80b52d75 (diff)
downloadmail-1204730924436ef9e1c7c49c9557837f9a5ed0e8.tar.gz
fork https://github.com/mattsta/mailwebHEADmaster
Diffstat (limited to 'ansible/roles/common')
-rw-r--r--ansible/roles/common/defaults/main.yml3
-rw-r--r--ansible/roles/common/files/ffdhe2048.pem8
-rw-r--r--ansible/roles/common/files/ffdhe3072.pem11
-rw-r--r--ansible/roles/common/files/ffdhe4096.pem13
-rw-r--r--ansible/roles/common/files/inputrc61
-rwxr-xr-xansible/roles/common/files/net-listeners.py334
-rwxr-xr-xansible/roles/common/files/ssh-transfer-only.sh11
-rw-r--r--ansible/roles/common/files/vimrc.local25
-rw-r--r--ansible/roles/common/handlers/main.yml20
-rw-r--r--ansible/roles/common/tasks/main.yml301
10 files changed, 787 insertions, 0 deletions
diff --git a/ansible/roles/common/defaults/main.yml b/ansible/roles/common/defaults/main.yml
new file mode 100644
index 0000000..2410e85
--- /dev/null
+++ b/ansible/roles/common/defaults/main.yml
@@ -0,0 +1,3 @@
1---
2grub:
3 extras: ""
diff --git a/ansible/roles/common/files/ffdhe2048.pem b/ansible/roles/common/files/ffdhe2048.pem
new file mode 100644
index 0000000..9b182b7
--- /dev/null
+++ b/ansible/roles/common/files/ffdhe2048.pem
@@ -0,0 +1,8 @@
1-----BEGIN DH PARAMETERS-----
2MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
3+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
487VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
5YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
67MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
7ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
8-----END DH PARAMETERS-----
diff --git a/ansible/roles/common/files/ffdhe3072.pem b/ansible/roles/common/files/ffdhe3072.pem
new file mode 100644
index 0000000..fb31ccd
--- /dev/null
+++ b/ansible/roles/common/files/ffdhe3072.pem
@@ -0,0 +1,11 @@
1-----BEGIN DH PARAMETERS-----
2MIIBiAKCAYEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
3+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
487VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
5YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
67MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
7ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3
87lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32
9nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZsYu
10N///////////AgEC
11-----END DH PARAMETERS-----
diff --git a/ansible/roles/common/files/ffdhe4096.pem b/ansible/roles/common/files/ffdhe4096.pem
new file mode 100644
index 0000000..3cf0fcb
--- /dev/null
+++ b/ansible/roles/common/files/ffdhe4096.pem
@@ -0,0 +1,13 @@
1-----BEGIN DH PARAMETERS-----
2MIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
3+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
487VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
5YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
67MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
7ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3
87lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32
9nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e
108W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx
11iu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K
12zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI=
13-----END DH PARAMETERS-----
diff --git a/ansible/roles/common/files/inputrc b/ansible/roles/common/files/inputrc
new file mode 100644
index 0000000..47e7275
--- /dev/null
+++ b/ansible/roles/common/files/inputrc
@@ -0,0 +1,61 @@
1# do not bell on tab-completion
2#set bell-style none
3
4set meta-flag on
5set input-meta on
6set convert-meta off
7set output-meta on
8
9# Completed names which are symbolic links to
10# directories have a slash appended.
11set mark-symlinked-directories on
12
13$if mode=emacs
14
15# for linux console and RH/Debian xterm
16"\e[1~": beginning-of-line
17"\e[4~": end-of-line
18# commented out keymappings for pgup/pgdown to reach begin/end of history
19#"\e[5~": beginning-of-history
20#"\e[6~": end-of-history
21"\e[5~": history-search-backward
22"\e[6~": history-search-forward
23"\e[3~": delete-char
24"\e[2~": quoted-insert
25"\e[5C": forward-word
26"\e[5D": backward-word
27"\e[1;5C": forward-word
28"\e[1;5D": backward-word
29
30# for rxvt
31"\e[8~": end-of-line
32"\eOc": forward-word
33"\eOd": backward-word
34
35# for non RH/Debian xterm, can't hurt for RH/DEbian xterm
36"\eOH": beginning-of-line
37"\eOF": end-of-line
38
39# for freebsd console
40"\e[H": beginning-of-line
41"\e[F": end-of-line
42$endif
43
44# cd d<TAB> will match documents or Documents
45set completion-ignore-case on
46
47# front-of-command up and down completion
48"\e[A":history-search-backward
49"\e[B":history-search-forward
50
51# This is the magic command.
52# Enables sane tcsh-like ctrl-d completion showing.
53Control-d:delete-char-or-list
54
55# oddly, menu-complete makes the menu *not* appear
56# just cycle through each option
57Tab:menu-complete
58
59# stop asking if I "Really want to see 102 completions"
60set completion-query-items 350
61set page-completions off
diff --git a/ansible/roles/common/files/net-listeners.py b/ansible/roles/common/files/net-listeners.py
new file mode 100755
index 0000000..f8b39cd
--- /dev/null
+++ b/ansible/roles/common/files/net-listeners.py
@@ -0,0 +1,334 @@
1#!/usr/bin/env python3
2
3""" Output a colorized list of listening addresses with owners.
4
5This tool parses files in /proc directly to obtain the list
6of IPv4 and IPv6 addresses listening on tcp, tcp6, udp, and udp6 ports
7also with pids of processes responsible for the listening.
8
9Due to permission restrictions on Linux, script must be run as root
10to determine which pids match which listening sockets.
11
12This is also something like:
13 osqueryi "select po.pid, rtrim(p.cmdline), po.family, po.local_address, po.local_port from process_open_sockets as po JOIN processes as p ON po.pid=p.pid WHERE po.state='LISTEN';"
14
15"""
16
17import collections
18import subprocess
19import codecs
20import socket
21import struct
22import glob
23import sys
24import re
25import os
26
27TERMINAL_WIDTH = "/usr/bin/tput cols" # could also be "stty size"
28
29ONLY_LOWEST_PID = False
30
31# oooh, look, a big dirty global dict collecting all our data without being
32# passed around! call the programming police!
33inodes = {}
34
35
36class Color:
37 HEADER = '\033[95m'
38 OKBLUE = '\033[94m'
39 OKGREEN = '\033[92m'
40 WARNING = '\033[93m'
41 FAIL = '\033[91m'
42 BOLD = '\033[1m'
43 UNDERLINE = '\033[4m'
44 END = '\033[0m'
45
46
47COLOR_HEADER = Color.HEADER
48COLOR_OKAY = Color.OKBLUE
49COLOR_WARNING = Color.FAIL
50COLOR_END = Color.END
51
52# This should capture:
53# 127.0.0.0/8
54# 192.168.0.0/16
55# 10.0.0.0/8
56# 169.254.0.0/16
57# 172.16.0.0/12
58# ::1
59# fe80::/10
60# fc00::/7
61# fd00::/8
62NON_ROUTABLE_REGEX = r"""^((127\.) |
63 (192\.168\.) |
64 (10\.) |
65 (169\.254\.) |
66 (172\.1[6-9]\.) |
67 (172\.2[0-9]\.) |
68 (172\.3[0-1]\.) |
69 (::1) |
70 ([fF][eE]80)
71 ([fF][cCdD]))"""
72likelyLocalOnly = re.compile(NON_ROUTABLE_REGEX, re.VERBOSE)
73
74
75def run(thing):
76 """ Run any string as an async command invocation. """
77 # We don't use subprocess.check_output because we want to run all
78 # processes async
79 return subprocess.Popen(thing.split(), stdout=subprocess.PIPE)
80
81
82def readOutput(ranCommand):
83 """ Return array of rows split by newline from previous invocation. """
84 stdout, stderr = ranCommand.communicate()
85 return stdout.decode('utf-8').strip().splitlines()
86
87
88def procListeners():
89 """ Wrapper to parse all IPv4 tcp udp, and, IPv6 tcp6 udp6 listeners. """
90
91 def processProc(name):
92 """ Process IPv4 and IPv6 versions of listeners based on ``name``.
93
94 ``name`` is either 'udp' or 'tcp' so we parse, for each ``name``:
95 - /proc/net/[name]
96 - /proc/net/[name]6
97
98 As in:
99 - /proc/net/tcp
100 - /proc/net/tcp6
101 - /proc/net/udp
102 - /proc/net/udp6
103 """
104
105 def ipv6(addr):
106 """ Convert /proc IPv6 hex address into standard IPv6 notation. """
107 # turn ASCII hex address into binary
108 addr = codecs.decode(addr, "hex")
109
110 # unpack into 4 32-bit integers in big endian / network byte order
111 addr = struct.unpack('!LLLL', addr)
112
113 # re-pack as 4 32-bit integers in system native byte order
114 addr = struct.pack('@IIII', *addr)
115
116 # now we can use standard network APIs to format the address
117 addr = socket.inet_ntop(socket.AF_INET6, addr)
118 return addr
119
120 def ipv4(addr):
121 """ Convert /proc IPv4 hex address into standard IPv4 notation. """
122 # Instead of codecs.decode(), we can just convert a 4 byte hex
123 # string to an integer directly using python radix conversion.
124 # Basically, int(addr, 16) EQUALS:
125 # aOrig = addr
126 # addr = codecs.decode(addr, "hex")
127 # addr = struct.unpack(">L", addr)
128 # assert(addr == (int(aOrig, 16),))
129 addr = int(addr, 16)
130
131 # system native byte order, 4-byte integer
132 addr = struct.pack("=L", addr)
133 addr = socket.inet_ntop(socket.AF_INET, addr)
134 return addr
135
136 isUDP = name == "udp"
137
138 # Iterate four files: /proc/net/{tcp,udp}{,6}
139 # ipv4 has no prefix, while ipv6 has 6 appended.
140 for ver in ["", "6"]:
141 with open(f"/proc/net/{name}{ver}", 'r') as proto:
142 proto = proto.read().splitlines()
143 proto = proto[1:] # drop header row
144
145 for cxn in proto:
146 cxn = cxn.split()
147
148 # /proc/net/udp{,6} uses different constants for LISTENING
149 if isUDP:
150 # These constants are based on enum offsets inside
151 # the Linux kernel itself. They aren't likely to ever
152 # change since they are hardcoded in utilities.
153 isListening = cxn[3] == "07"
154 else:
155 isListening = cxn[3] == "0A"
156
157 # Right now this is a single-purpose tool so if process is
158 # not listening, we avoid further processing of this row.
159 if not isListening:
160 continue
161
162 ip, port = cxn[1].split(':')
163 if ver:
164 ip = ipv6(ip)
165 else:
166 ip = ipv4(ip)
167
168 port = int(port, 16)
169 inode = cxn[9]
170
171 # We just use a list here because creating a new sub-dict
172 # for each entry was noticably slower than just indexing
173 # into lists.
174 inodes[int(inode)] = [ip, port, f"{name}{ver}"]
175
176 processProc("tcp")
177 processProc("udp")
178
179
180def appendToInodePidMap(fd, inodePidMap):
181 """ Take a full path to /proc/[pid]/fd/[fd] for reading.
182
183 Populates both pid and full command line of pid owning an inode we
184 are interested in.
185
186 Basically finds if any inodes on this pid is a listener we previously
187 recorded into our ``inodes`` dict. """
188 _, _, pid, _, _ = fd.split('/')
189 try:
190 target = os.readlink(fd)
191 except FileNotFoundError:
192 # file vanished, can't do anything else
193 return
194
195 if target.startswith("socket"):
196 ostype, inode = target.split(':')
197 # strip brackets from fd string (it looks like: [fd])
198 inode = int(inode[1:-1])
199 inodePidMap[inode].append(int(pid))
200
201
202def addProcessNamesToInodes():
203 """ Loop over every fd in every process in /proc.
204
205 The only way to map an fd back to a process is by looking
206 at *every* processes fd and extracting backing inodes.
207
208 It's basically like a big awkward database join where you don't
209 have an index on the field you want.
210
211 Also, due to Linux permissions (and Linux security concerns),
212 only the root user can read fd listing of processes not owned
213 by the current user. """
214
215 # glob glob glob it all
216 allFDs = glob.iglob("/proc/*/fd/*")
217 inodePidMap = collections.defaultdict(list)
218
219 for fd in allFDs:
220 appendToInodePidMap(fd, inodePidMap)
221
222 for inode in inodes:
223 if inode in inodePidMap:
224 for pid in inodePidMap[inode]:
225 try:
226 with open(f"/proc/{pid}/cmdline", 'r') as cmd:
227 # /proc command line arguments are delimited by
228 # null bytes, so undo that here...
229 cmdline = cmd.read().split('\0')
230 inodes[inode].append((pid, cmdline))
231 except BaseException:
232 # files can vanish on us at any time (and that's okay!)
233 # But, since the file is gone, we want the entire fd
234 # entry gone too:
235 pass # del inodes[inode]
236
237
238def checkListenersProc():
239 terminalWidth = run(TERMINAL_WIDTH)
240
241 procListeners()
242 addProcessNamesToInodes()
243 tried = inodes
244
245 try:
246 cols = readOutput(terminalWidth)[0]
247 cols = int(cols)
248 except BaseException:
249 cols = 80
250
251 # Print our own custom output header...
252 proto = "Proto"
253 addr = "Listening"
254 pid = "PID"
255 process = "Process"
256 print(f"{COLOR_HEADER}{proto:^5} {addr:^25} {pid:>5} {process:^30}")
257
258 # Could sort by anything: ip, port, proto, pid, command name
259 # (or even the fd integer if that provided any insight whatsoever)
260 def compareByPidOrPort(what):
261 k, v = what
262 # v = [ip, port, proto, pid, cmd]
263 # - OR -
264 # v = [ip, port, proto]
265
266 # If we're not running as root we can't pid and command mappings for
267 # the processes of other users, so sort the pids we did find at end
268 # of list and show UNKNOWN entries first
269 # (because the lines will be shorter most likely so the bigger visual
270 # weight should be lower in the display table)
271 try:
272 # Pid available! Sort by first pid, subsort by IP then port.
273 return (1, v[3], v[0], v[1])
274 except BaseException:
275 # No pid available! Sort by port number then IP then... port again.
276 return (0, v[1], v[0], v[1])
277
278 # Sort results by pid...
279 for name, vals in sorted(tried.items(), key=compareByPidOrPort):
280 attachedPids = vals[3:]
281 if attachedPids:
282 desc = [f"{pid:5} {' '.join(cmd)}" for pid, cmd in vals[3:]]
283 else:
284 # If not running as root, we won't have pid or process, so use
285 # defaults
286 desc = ["UNKNOWN (must be root for global pid mappings)"]
287
288 port = vals[1]
289 try:
290 # Convert port integer to service name if possible
291 port = socket.getservbyport(port)
292 except BaseException:
293 # If no match, just use port number directly.
294 pass
295
296 addr = f"{vals[0]}:{port}"
297 proto = vals[2]
298
299 # If IP address looks like it could be visible to the world,
300 # throw up a color.
301 # Note: due to port forwarding and NAT and other issues,
302 # this clearly isn't exhaustive.
303 if re.match(likelyLocalOnly, addr):
304 colorNotice = COLOR_OKAY
305 else:
306 colorNotice = COLOR_WARNING
307
308 isFirstLine = True
309 for line in desc:
310 if isFirstLine:
311 output = f"{colorNotice}{proto:5} {addr:25} {line}"
312 isFirstLine = False
313 else:
314 output = f"{' ':31} {line}"
315
316 # Be a polite terminal citizen by limiting our width to user's width
317 # (colors take up non-visible space, so add it to our col count)
318 print(output[:cols + (len(colorNotice) if isFirstLine else 0)])
319
320 if ONLY_LOWEST_PID:
321 break
322
323 print(COLOR_END)
324
325
326if __name__ == "__main__":
327 # cheap hack garbage way of setting one option
328 # if we need more options, obviously pull in argparse
329 if len(sys.argv) > 1:
330 ONLY_LOWEST_PID = True
331 else:
332 ONLY_LOWEST_PID = False
333
334 checkListenersProc()
diff --git a/ansible/roles/common/files/ssh-transfer-only.sh b/ansible/roles/common/files/ssh-transfer-only.sh
new file mode 100755
index 0000000..c1f0624
--- /dev/null
+++ b/ansible/roles/common/files/ssh-transfer-only.sh
@@ -0,0 +1,11 @@
1#!/usr/bin/env bash
2
3# Only allow ssh commands starting with 'scp' or 'rsync'
4case $SSH_ORIGINAL_COMMAND in
5 scp*)
6 $SSH_ORIGINAL_COMMAND ;;
7 rsync*)
8 $SSH_ORIGINAL_COMMAND ;;
9 *)
10 echo "Not allowed with this key: $SSH_ORIGINAL_COMMAND" ;;
11esac
diff --git a/ansible/roles/common/files/vimrc.local b/ansible/roles/common/files/vimrc.local
new file mode 100644
index 0000000..378d91e
--- /dev/null
+++ b/ansible/roles/common/files/vimrc.local
@@ -0,0 +1,25 @@
1set encoding=utf-8
2
3set ignorecase
4set smartcase
5
6set title
7
8set backupdir=~/.vim-tmp,/tmp
9set directory=~/.vim-tmp,/tmp
10
11set ruler
12
13filetype plugin indent on
14
15set ai
16set expandtab
17set tabstop=4
18set shiftwidth=4
19
20autocmd BufEnter * :syntax sync fromstart
21
22set hlsearch
23colorscheme peachpuff
24
25set wildignore+=*/tmp/*,*.so,*.swp,*.zip,*.o,*.dSYM,tags
diff --git a/ansible/roles/common/handlers/main.yml b/ansible/roles/common/handlers/main.yml
new file mode 100644
index 0000000..ade4fea
--- /dev/null
+++ b/ansible/roles/common/handlers/main.yml
@@ -0,0 +1,20 @@
1---
2- name: reload sshd
3 service:
4 name: sshd
5 state: reloaded
6
7- name: reload grub
8 command: update-grub
9
10- name: double disable systemd ntp client
11 command: timedatectl set-ntp false
12
13- name: clear motd cache
14 file:
15 path: "{{ item }}"
16 state: absent
17 loop:
18 - /var/cache/motd-news
19 - /run/motd.dynamic
20 - /run/motd.dynamic.new
diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml
new file mode 100644
index 0000000..23de53c
--- /dev/null
+++ b/ansible/roles/common/tasks/main.yml
@@ -0,0 +1,301 @@
1---
2# You can manually view how your OS-provided packages are supported with:
3# ubuntu-support-status --show-all
4- name: update packages
5 apt:
6 update_cache: yes
7 upgrade: safe
8 cache_valid_time: 3600
9
10
11- name: fix inputrc
12 copy:
13 src: inputrc
14 dest: /etc/inputrc
15 owner: root
16 group: root
17 mode: 0644
18
19- name: fix vimrc
20 copy:
21 src: vimrc.local
22 dest: /etc/vim/
23 owner: root
24 group: root
25 mode: 0644
26
27
28- include_role:
29 name: ramdisk
30
31
32- name: remove ubuntu call home reporting cron
33 cron:
34 cron_file: popularity-contest
35 state: absent
36
37
38- name: remove low port restriction
39 sysctl:
40 name: net.ipv4.ip_unprivileged_port_start
41 value: 0
42 state: present
43 sysctl_set: yes
44
45
46# 3 means enable for outgoing and incoming connections
47# 2 means enable for incoming connections
48# 1 means enable for outgoing connections
49# 0 means disabled
50# Linux 3.13 (2014-01-19) and newer
51- name: enable server and client TCP_FASTOPEN
52 sysctl:
53 name: net.ipv4.tcp_fastopen
54 value: 3
55 state: present
56 sysctl_set: yes
57
58
59# These were taken from:
60# https://wiki.mozilla.org/Security/Server_Side_TLS#Pre-defined_DHE_groups
61- name: populate known-good dhparams
62 copy:
63 src: "{{ item }}"
64 dest: "/etc/ssl/{{ item }}"
65 loop:
66 - ffdhe2048.pem
67 - ffdhe3072.pem
68 - ffdhe4096.pem
69
70
71- name: configure /etc/hostname
72 hostname:
73 name: "{{ inventory_hostname }}"
74
75 #- name: Add IP address of all hosts to all hosts
76 # lineinfile:
77 # state: present
78 # dest: /etc/hosts
79 # regexp: '.*{{ item }}$'
80 # line: "{{ hostvars[item].ansible_default_ipv4.address }} {{item}}"
81 # when: hostvars[item].ansible_default_ipv4.address is defined
82 # with_items: "{{ groups['all'] }}"
83
84
85- name: configure sshd to only listen on IPv4
86 lineinfile:
87 dest: /etc/ssh/sshd_config
88 regexp: '^#?AddressFamily'
89 line: "AddressFamily inet" # no ipv6
90 state: present
91 notify: reload sshd
92
93
94 # Capture example:
95 #- replace:
96 # path: /etc/hosts
97 # regexp: '(\s+)old\.host\.name(\s+.*)?$'
98 # replace: '\1new.host.name\2'
99 # backup: yes
100
101
102- name: fix motd
103 replace:
104 path: /etc/default/motd-news
105 regexp: 'https://motd.ubuntu.com'
106 replace: 'https://matt.sh/motd'
107 notify:
108 - clear motd cache
109
110
111# Verify against:
112# systemctl list-timers
113- name: disable more automated call home reporting
114 systemd:
115 name: "{{ item }}"
116 state: stopped
117 enabled: False
118 loop:
119 - apt-daily-upgrade.timer
120 - apt-daily.timer
121 - motd-news.timer
122
123
124- name: remove ubuntu self-advertising
125 file:
126 path: "/etc/update-motd.d/{{ item }}"
127 state: absent
128 loop:
129 - 91-release-upgrade
130 - 80-livepatch
131 - 10-help-text
132 notify:
133 - clear motd cache
134
135
136# Ubuntu's pam_motd.so shows you /etc/legal
137# on login if you don't have ~/.cache/motd.legal-displayed
138# There is no way to disable the creation of that file in ~/.cache on login,
139# but we can wipe out the message for new users.
140- name: remove login disclaimer
141 file:
142 path: /etc/legal
143 state: absent
144
145
146- name: place net-listeners.py
147 copy:
148 src: net-listeners.py
149 dest: /usr/local/bin/
150 owner: root
151 group: root
152 mode: 0755
153
154- name: place scp/rsync-only ssh restriction capability
155 copy:
156 src: ssh-transfer-only.sh
157 dest: /usr/local/bin/
158 owner: root
159 group: root
160 mode: 0755
161
162# can't setsid 04755 scripts, so enable script with global passwordless sudo
163- name: enable all user running of net-listeners.py
164 lineinfile:
165 path: /etc/sudoers.d/net-listeners
166 regexp: "listeners.py"
167 line: "ALL ALL = (root) NOPASSWD: /usr/local/bin/net-listeners.py"
168 create: yes
169 mode: 0440
170
171- name: add uptime and uname to login motd
172 lineinfile:
173 dest: /etc/update-motd.d/00-header
174 line: "{{ item }}"
175 state: present
176 loop:
177 - printf "\n$(w -us)\n"
178
179- name: add listening watcher to global login config
180 lineinfile:
181 dest: /etc/bash.bashrc
182 line: "{{ item }}"
183 state: present
184 loop:
185 # Only show output when running a login, not when starting a sudo shell
186 - "[[ -z $SUDO_UID ]] && sudo /usr/local/bin/net-listeners.py"
187
188- name: ensure system grub template has serial access
189 lineinfile:
190 dest: /etc/default/grub
191 regexp: '^GRUB_CMDLINE_LINUX='
192 line: 'GRUB_CMDLINE_LINUX="console=ttyS0 {{ grub.extras }}"'
193 state: present
194 notify: reload grub
195
196
197# This is an ops opinion. For more advanced needs, modify here or just template
198# the entire sshd_config directly.
199- name: configure sshd to only listen on local IP
200 lineinfile:
201 dest: /etc/ssh/sshd_config
202 regexp: '^#?ListenAddress'
203 line: "ListenAddress {{ hostvars[inventory_hostname]['ansible_' + network.interface.private]['ipv4']['address'] }}"
204 state: present
205 notify: reload sshd
206
207
208- name: install system tools
209 apt:
210 pkg:
211 # acl is required for ansible to "become_user" as someone non-root because
212 # of permissions on its temporary files. Ansible will setfacl on temp files
213 # so it doesn't have to 0666 everything just so a new user can modify things.
214 - acl
215
216 # you aren't a linux server without sending nightly summary emails
217 - logwatch
218
219 # apt helpers for repo installs not included by default for some reason
220 - software-properties-common
221
222 # production CA bundles so we don't get unknown CA errors
223 - ca-certificates
224
225 # Maintains high numbers in /proc/sys/kernel/random/entropy_avail
226 - rng-tools
227
228 # should we use a more modern thing than collect? distributed osquery?
229 - collectd
230
231 # make sure 'install_recommends: no' or this installs lots of other stuff
232 - vim-nox
233
234 # rrdtool only installed so we can be lazy and generate graphs on-demand
235 # with: /usr/share/doc/collectd-core/examples/collectd2html.pl
236 # TODO: enable centralized reporting system
237 - rrdtool
238
239 # netstat, mii-tool, etc
240 - net-tools
241 install_recommends: no
242 state: latest
243
244# use a modern ntp client+server.
245#
246# systemd actually has a built-in ntp client called 'systemd-timesyncd'
247# You can view its status with:
248# journalctl -u systemd-timesyncd
249# timedatectl
250#
251# Installing chrony will disable systemd-timesyncd
252# (represented in apt with "Replaces: time-daemon")
253# but it doesn't _actually_ disable it according to timedatectl (bug?)
254# so we also manually run 'timedatectl set-ntp false' just to confirm.
255# A good writeup about systemd-timesyncd lives at:
256# https://wiki.archlinux.org/index.php/systemd-timesyncd
257#
258# You can view your live chrony status with:
259# chronyc tracking
260# chronyc sources
261# chronyc sourcestats
262#
263# ...and that's a lot more detail than the built-in garabage systemd-timesyncd
264# client will tell you about how your system time is being managed.
265#
266# chrony is both an ntp client with a remote administration interface
267# and an ntp server, but by default chrony does not enable remote admin
268# or ntp serving without additional explicit configuration (chrony.conf).
269#
270# For more details about becoming an ntp server and remote time administartion,
271# see sections 2.2 and 2.5 of:
272# https://chrony.tuxfamily.org/faq.html#_how_do_i_make_an_ntp_server_from_an_ntp_client
273- name: install ntp client
274 apt:
275 pkg: chrony
276 state: latest
277 notify:
278 - double disable systemd ntp client
279
280# If ansible facts aren't enough, we can get puppet and chef facts too:
281#- name: install facter
282# apt:
283# pkg: facter
284# state: latest
285#
286#- name: install ohai
287# apt:
288# pkg: facter
289# state: latest
290
291
292# cleanup
293- name: cleanup packaging
294 apt:
295 autoclean: yes
296 autoremove: yes
297
298# If needed, build and provide:
299#
300# Build for nsjail:
301# apt install protobu* bison flex pkg-config libprotobuf-dev
Powered by cgit v1.2.3 (git 2.41.0)