Skip to content

Commit 56d5897

Browse files
issue #2 - tilføj sikkerhedsscripts. Disse vises under fanen 'Sikkerhedsscripts' i admin-site som følge af 'metadata.security=true' i md-filerne.
1 parent e8c48d0 commit 56d5897

File tree

6 files changed

+400
-0
lines changed

6 files changed

+400
-0
lines changed

detect_keyboard_event.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
title: "Detekter nytilsluttet keyboard"
3+
parent: "Sikkerhed"
4+
source: scripts/detect_keyboard_event.py
5+
compatibility:
6+
- "22.04"
7+
- "BorgerPC"
8+
metadata:
9+
security: true
10+
---
11+
12+
## Beskrivelse
13+
Sikkerhedsscriptet identificerer ny-tilsluttede keyboards helt tilbage fra sidste gang systemet blev tjekket. Findes det resulterer det i en sikkerhedshændelse.
14+
At fjerne et keyboard giver IKKE en advarsel.
15+
16+
BEMÆRK: Dette sikkerhedsscript er afhængigt af, om USB-enheden identificerer sig selv som et keyboard. Af denne grund, vil vi - såfremt det er muligt i jeres anvendelse - anbefale i stedet at benytte scriptet, der låser maskinen ved indsættelse af ALLE USB-enheder ("Bloker for login ved USB-event"), sammen med sikkerhedscriptet der giver en advarsel, når Borger-kontoen er blevet låst ("Detekter låst Borgerkonto").
17+
18+
Dette sikkerhedsscript virker både på OS2borgerPC og OS2borgerPC Kiosk, men vi mener det er mest relevant på førstnævnte, ift. faren ved keyloggers på publikumsmaskiner.
19+
20+
VIGTIG BEMÆRKNING:
21+
Sikkerhedsscriptet er kun aktivt, når maskinen er tændt!
22+
23+
Af ovenstående grund er det centralt, at besøgende ikke kan tilgå maskinerne mens de er slukket, uden at det opdages.
24+
25+
Derfor foreslår vi at kombinere det med følgende tre scripts:
26+
27+
1. Scriptet "Desktop - Fjern Luk Ned og Genstart fra sessionmenuen og blokér for nedlukning via systempolitik"
28+
29+
2. Scriptet "OS2borgerPC - Blokér for login ved hård nedlukning"
30+
31+
3. Sikkerhedsscriptet "Detekter låst Borgerkonto"
32+
33+
Sammen betyder de tre:
34+
1. At brugeren ikke kan lukke maskinen ned fra menuen.
35+
2. Trykker de på knappen for at lukke den ned, eller hiver de strømstikket ud, så låses der for login for Borger-kontoen. (Da de potentielt være have indsat en keylogger)
36+
3. Hvis Borger-kontoen låses modtager man, pga. script tre, en sikkerhedshændelse
37+
38+
Ønsker man slet ikke at Borgere skal kunne rode med USB-enheder så kan man bruge scriptet
39+
"OS2borgerPC - Blokér for login ved USB-event"
40+
Med dette andet script vil både at tilføje eller fjerne en USB-enhed mens maskinen er tændt, betyde at Borgeren logges øjeblikkeligt ud, og at der derefter låses for login.
41+
42+
Når Borger-kontoen er låst kan man fra adminsitet køre scriptet "OS2borgerPC - Sæt bruger aktiv efter blokeret login (lås op)" for at åbne for login på Borger-kontoen igen.
43+
44+
## Parametre
45+
Ingen

detect_sudo_event.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: "Detekter sudo-kørsel"
3+
parent: "Sikkerhed"
4+
source: scripts/detect_sudo_event.py
5+
compatibility:
6+
- "22.04"
7+
- "BorgerPC"
8+
metadata:
9+
security: true
10+
---
11+
12+
## Beskrivelse
13+
Dette Sikkerhedsscript giver en sikkerhedshændelse ved sudo-kørsel.
14+
15+
Dette script virker både på OS2borgerPC og OS2borgerPC Kiosk.
16+
17+
Der gives både en advarsel hvis sudo køres med succes, hvis det fejler pga. det køres fra Borger fremfor superuser, eller den indtastede kode er forkert.
18+
Nærmere specifikt:
19+
sudo-kommandoen giver én tre forsøg på at indtaste koden - taster man forkert tre gange vil det give en advarsel. Taster man korrekt vil det ligeledes give en advarsel.
20+
21+
Derfor: Hvis du har tilføjet en regel for en maskine, og du selv er inde på den fra superuser, vil der også komme en advarsel, hvis du kører sudo.
22+
23+
Når du modtager en advarsel vil der ofte stå USER=root i beskeden. Dette betyder ikke, at brugeren allerede har root-adgang (dvs. administrator-adgang), men alene at brugeren, personen forsøger at køre kommandoer som, er administrator-kontoen.
24+
25+
## Parametre
26+
Ingen

detect_user_expired_event.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: "Detekter låst Borgerkonto"
3+
parent: "Sikkerhed"
4+
source: scripts/detect_user_expired_event.py
5+
compatibility:
6+
- "22.04"
7+
- "BorgerPC"
8+
metadata:
9+
security: true
10+
---
11+
12+
## Beskrivelse
13+
Dette Sikkerhedsscript giver en Sikkerhedshændelse hvis Borger bliver låst ude/sat til udløbet.
14+
15+
Dette script virker kun på OS2borgerPC, ikke OS2borgerPC Kiosk.
16+
17+
Bruges sammen med en eller begge af følgende:
18+
- "Bloker for login ved USB-event" + "Sæt bruger aktiv efter blokeret login"
19+
- "OS2borgerPC - Bloker for login ved hård nedlukning" + "Sæt bruger aktiv efter blokeret login"
20+
21+
## Parametre
22+
Ingen

scripts/detect_keyboard_event.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Security Script for finding USB keyboard attachment events
5+
"""
6+
7+
import sys
8+
from datetime import datetime, timedelta
9+
import re
10+
11+
__copyright__ = "Copyright 2017-2024 Magenta ApS"
12+
__license__ = "GPL"
13+
14+
15+
def log_read(last_security_check, log_name):
16+
"""Search a (system) log for events that occurred
17+
between "last_security_check" and now."""
18+
log_event_tuples = []
19+
now = datetime.now()
20+
21+
with open(log_name) as f:
22+
for line in f.readlines():
23+
line = str(line.replace("\0", ""))
24+
log_event_timestamp = line[:15]
25+
log_event = line.strip("\n")
26+
# convert from log event timestamp to security event log timestamp.
27+
log_event_datetime = datetime.strptime(
28+
str(now.year) + " " + log_event_timestamp, "%Y %b %d %H:%M:%S"
29+
)
30+
security_event_log_timestamp = datetime.strftime(
31+
log_event_datetime, "%Y%m%d%H%M%S"
32+
)
33+
# Detect lines from within the last x seconds to now.
34+
if last_security_check <= log_event_datetime <= now:
35+
log_event_tuples.append((security_event_log_timestamp, log_event))
36+
37+
return log_event_tuples
38+
39+
40+
def csv_writer(security_events):
41+
"""Write security events to security events file."""
42+
with open("/etc/os2borgerpc/security/securityevent.csv", "at") as csvfile:
43+
for timestamp, security_problem_uid, log_event in security_events:
44+
event_line = log_event.replace("\n", " ").replace("\r", "").replace(",", "")
45+
csvfile.write(f"{timestamp},{security_problem_uid},{event_line}\n")
46+
47+
48+
def filter_duplicate_events(security_events):
49+
"""This function filters duplicate events related to
50+
the same keyboard"""
51+
52+
unique_tuples = []
53+
unique_keyboards = []
54+
55+
for security_event in security_events:
56+
# This identifier is based on the ID of the USB device.
57+
# The ID is identical for identical USB devices so
58+
# if two identical keyboards are inserted simultaneously,
59+
# only one event will be generated.
60+
regex = (
61+
r"[0-9a-z]{4}:[0-9a-z]{4}:[0-9a-z]{4}"
62+
r"(?!.*/[0-9a-z]{4}:[0-9a-z]{4}:[0-9a-z]{4})"
63+
)
64+
match = re.search(regex, security_event[2], flags=re.IGNORECASE)
65+
# Keyboard event lines should always contain a match, but in order
66+
# to prevent possibly overlooking a relevant event, we always include
67+
# events with no match
68+
if match:
69+
keyboard_identifier = match.group(0)
70+
if keyboard_identifier not in unique_keyboards:
71+
unique_tuples.append(security_event)
72+
unique_keyboards.append(keyboard_identifier)
73+
else: # This part should never be relevant, but it is here just in case
74+
unique_tuples.append(security_event)
75+
76+
return unique_tuples
77+
78+
79+
# The file to inspect for events
80+
log_name = "/var/log/syslog"
81+
82+
now = datetime.now()
83+
# The default value in case lastcheck.txt is nonexisting or empty:
84+
last_security_check = now - timedelta(hours=24)
85+
try:
86+
with open("/etc/os2borgerpc/security/lastcheck.txt", "r") as fp:
87+
timestamp = fp.read()
88+
if timestamp:
89+
last_security_check = datetime.strptime(timestamp, "%Y%m%d%H%M%S")
90+
except IOError:
91+
pass
92+
93+
log_event_tuples = log_read(last_security_check, log_name)
94+
95+
security_problem_uid_template_var = "%SECURITY_PROBLEM_UID%"
96+
97+
# Match keyboard events that are after 9.9999 seconds of boot up
98+
# (so we don't match upstart keyboard events),
99+
# Also remove system control and consumer control entries:
100+
# The reason is that some keyboards add three keyboard entries when connected.
101+
# Example from inserting a keyboard once:
102+
# Jun 28 14:24:43 kbh-nuc-venstre kernel: [ 1948.130701] input: Logitech HID compliant keyboard as /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/0003:046D:C30E.000A/input/input25
103+
# Jun 28 14:24:43 kbh-nuc-venstre kernel: [ 1948.264053] input: Logitech HID compliant keyboard System Control as /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.1/0003:046D:C30E.000B/input/input27
104+
# Jun 28 14:24:43 kbh-nuc-venstre kernel: [ 1948.204460] input: Logitech HID compliant keyboard Consumer Control as /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.1/0003:046D:C30E.000B/input/input26
105+
# Fortunately it seems Consumer Control and System Control aren't Logitech specific, as we've seen the exact same with Lenovo keyboards.
106+
# The second regex matches certain keyboards that do not generate a log line with "input:" and "Keyboard"
107+
# Most regular keyboards also generate a log line that matches the second regex, but
108+
# the duplicate events are removed by the filtering
109+
regexes = [
110+
r".*\[[ ]{0,3}[0-9]{2,}\..*\] input: .*Keyboard "
111+
r"(?!((mouse )?system control))(?!((mouse )?consumer control)).*",
112+
r".*\[[ ]{0,3}[0-9]{2,}\..*\].*input,hidraw.*keyboard (?!mouse)",
113+
]
114+
115+
# Filter log_event_tuples based on regex matches and put them
116+
# on the form the admin site expects:
117+
# (timestamp, security_problem_uid, summary)
118+
log_event_tuples = [
119+
(log_timestamp, security_problem_uid_template_var, log_event)
120+
for (log_timestamp, log_event) in log_event_tuples
121+
if any([re.search(regex, log_event, flags=re.IGNORECASE) for regex in regexes])
122+
]
123+
124+
log_event_tuples = filter_duplicate_events(log_event_tuples)
125+
126+
if not log_event_tuples:
127+
sys.exit()
128+
129+
csv_writer(log_event_tuples)

scripts/detect_sudo_event.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Security Script for finding sudo events
5+
"""
6+
7+
import sys
8+
from datetime import datetime, timedelta
9+
import re
10+
11+
__copyright__ = "Copyright 2017-2024 Magenta ApS"
12+
__license__ = "GPL"
13+
14+
15+
def log_read(last_security_check, log_name):
16+
"""Search a (system) log for events that occurred
17+
between "last_security_check" and now."""
18+
log_event_tuples = []
19+
now = datetime.now()
20+
21+
with open(log_name) as f:
22+
for line in f.readlines():
23+
line = str(line.replace("\0", ""))
24+
log_event_timestamp = line[:15]
25+
log_event = line.strip("\n")
26+
# convert from log event timestamp to security event log timestamp.
27+
log_event_datetime = datetime.strptime(
28+
str(now.year) + " " + log_event_timestamp, "%Y %b %d %H:%M:%S"
29+
)
30+
security_event_log_timestamp = datetime.strftime(
31+
log_event_datetime, "%Y%m%d%H%M%S"
32+
)
33+
# Detect lines from within the last x seconds to now.
34+
if last_security_check <= log_event_datetime <= now:
35+
log_event_tuples.append((security_event_log_timestamp, log_event))
36+
37+
return log_event_tuples
38+
39+
40+
def csv_writer(security_events):
41+
"""Write security events to security events file."""
42+
with open("/etc/os2borgerpc/security/securityevent.csv", "at") as csvfile:
43+
for timestamp, security_problem_uid, log_event in security_events:
44+
event_line = log_event.replace("\n", " ").replace("\r", "").replace(",", "")
45+
csvfile.write(f"{timestamp},{security_problem_uid},{event_line}\n")
46+
47+
48+
# The file to inspect for events
49+
log_name = "/var/log/auth.log"
50+
51+
now = datetime.now()
52+
# The default value in case lastcheck.txt is nonexisting or empty:
53+
last_security_check = now - timedelta(hours=24)
54+
try:
55+
with open("/etc/os2borgerpc/security/lastcheck.txt", "r") as fp:
56+
timestamp = fp.read()
57+
if timestamp:
58+
last_security_check = datetime.strptime(timestamp, "%Y%m%d%H%M%S")
59+
except IOError:
60+
pass
61+
62+
log_event_tuples = log_read(last_security_check, log_name)
63+
64+
security_problem_uid_template_var = "%SECURITY_PROBLEM_UID%"
65+
# Ignore if not a sudo event or if a sudo event from root
66+
regexes = [r"sudo:(?!\s*root).*COMMAND"]
67+
68+
# Filter log_event_tuples based on regex matches and put them
69+
# on the form the admin site expects:
70+
# (timestamp, security_problem_uid, summary)
71+
log_event_tuples = [
72+
(log_timestamp, security_problem_uid_template_var, log_event)
73+
for (log_timestamp, log_event) in log_event_tuples
74+
if any([re.search(regex, log_event, flags=re.IGNORECASE) for regex in regexes])
75+
]
76+
77+
if not log_event_tuples:
78+
sys.exit()
79+
80+
csv_writer(log_event_tuples)

0 commit comments

Comments
 (0)