Initial commit
This commit is contained in:
11
README.md
Normal file
11
README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
Hardening security
|
||||
|
||||
https://hackviser.com/tactics/hardening/smb
|
||||
https://hackviser.com/tactics/hardening/caddy
|
||||
https://hackviser.com/tactics/hardening/ssh
|
||||
https://hackviser.com/tactics/hardening/smtp
|
||||
https://hackviser.com/tactics/hardening/rdp
|
||||
|
||||
https://hackviser.com/tactics/pentesting
|
||||
|
||||
https://hackviser.com/tactics/pentesting/services/ssh
|
||||
5
SETUP-WORKSTATION.md
Normal file
5
SETUP-WORKSTATION.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Chronyd blocks at startup
|
||||
|
||||
chronyd takes care of keeping the system clock in sync. When the system boots, chronyd will block start-up until it has resolved the time. This is useful on systems without a hardware clock (to avoid the system booting as 1970-01-01), but annoying for this setup.
|
||||
|
||||
This behaviour can be disabled by editing /etc/conf.d/chronyd and setting FAST_STARTUP=yes.
|
||||
38
SETUP.md
Normal file
38
SETUP.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Installation on hardware (Raspberry PI)
|
||||
Raspberry Pi imager (.img)
|
||||
|
||||
> If no internet connection: setup-interfaces with DHCP, rc-service networking restart
|
||||
|
||||
```sh
|
||||
setup-alpine
|
||||
us
|
||||
us
|
||||
|
||||
hostname: alpi
|
||||
ip: 192.168.2.22/24
|
||||
gateway: router (192.168.2.254)
|
||||
netmask 255.255.255.0
|
||||
|
||||
Europe/Amsterdam
|
||||
chrony
|
||||
1
|
||||
YOURUSERNAME
|
||||
YOURNAME
|
||||
github.com/YOURUSERNAME.keys
|
||||
|
||||
openssh
|
||||
y
|
||||
mmcblk0
|
||||
sys
|
||||
y
|
||||
reboot
|
||||
```
|
||||
|
||||
FIRST. Make sure your public key is configured for SSH! Else, next step will LOCK YOU OUT SSH!
|
||||
|
||||
Then, run install.sh by getting it from the internet (wget is in busybox):
|
||||
```sh
|
||||
wget https://TODO
|
||||
chmod +x install.sh
|
||||
./install.sh
|
||||
```
|
||||
22
install.sh
Executable file
22
install.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
# echo "Installing Git & cloning installation scripts"
|
||||
apk add git
|
||||
# git clone TODO
|
||||
|
||||
# Install podman-openrc tool
|
||||
apk add cargo
|
||||
cargo install podman-openrc
|
||||
|
||||
base_dir=$(pwd)
|
||||
|
||||
# Run installation scripts
|
||||
cd ./installation
|
||||
source ./basic.sh
|
||||
source ./podman.sh
|
||||
source ./firewall.sh
|
||||
|
||||
cd "$base_dir"
|
||||
|
||||
# Run update script
|
||||
source ./update.sh
|
||||
17
installation/basic.sh
Normal file
17
installation/basic.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
echo "Basic setup"
|
||||
|
||||
# Enable community repo
|
||||
sed -i 's|^#\(http.*/community\)$|\1|' /etc/apk/repositories
|
||||
apk update
|
||||
|
||||
# Cron jobs
|
||||
rc-update add crond
|
||||
cat << EOF > /etc/periodic/daily/chrony
|
||||
#!/bin/sh
|
||||
chronyc makestep
|
||||
EOF
|
||||
|
||||
# Allow local.d services
|
||||
rc-update add local default
|
||||
rc-service local start
|
||||
14
installation/firewall.sh
Normal file
14
installation/firewall.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
echo "Setting up firewall..."
|
||||
|
||||
apk add -u awall # important -u flag!
|
||||
apk add ip6tables iptables
|
||||
modprobe -v ip_tables
|
||||
modprobe -v ip6_tables
|
||||
modprobe -v iptable_nat #if NAT is used
|
||||
|
||||
# Register services
|
||||
rc-update add iptables
|
||||
rc-update add ip6tables
|
||||
rc-service iptables start
|
||||
rc-service ip6tables start
|
||||
28
installation/podman.sh
Normal file
28
installation/podman.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
echo "Installing Podman..."
|
||||
|
||||
apk add podman iptables podman-compose
|
||||
rc-update add cgroups
|
||||
rc-service cgroups start
|
||||
|
||||
# Rootless mode
|
||||
adduser -D podman
|
||||
modprobe tun
|
||||
echo tun >> /etc/modules
|
||||
echo podman:100000:65536 > /etc/subuid
|
||||
echo podman:100000:65536 > /etc/subgid
|
||||
doas su -c "podman system migrate" podman
|
||||
|
||||
# Get rid of podman compose docker warning
|
||||
touch /etc/containers/nodocker
|
||||
|
||||
# Fix shared mount with local service
|
||||
cat << EOF > /etc/local.d/mount-rshared.start
|
||||
#!/bin/sh
|
||||
mount --make-rshared /
|
||||
EOF
|
||||
|
||||
chmod +x /etc/local.d/mount-rshared.start
|
||||
|
||||
# Allow ports >= 53 to be rootless bound
|
||||
sysctl net.ipv4.ip_unprivileged_port_start=53
|
||||
11
services/basic/global.policy.json
Normal file
11
services/basic/global.policy.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"description": "Restrict all internet access",
|
||||
"variable": { "internet_if": "eth0" },
|
||||
"zone": {
|
||||
"internet": { "iface": "$internet_if" }
|
||||
},
|
||||
"policy": [
|
||||
{ "in": "internet", "action": "drop" },
|
||||
{ "action": "reject" }
|
||||
]
|
||||
}
|
||||
11
services/basic/icmp.policy.json
Normal file
11
services/basic/icmp.policy.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"description": "Allow ping-pong",
|
||||
"filter": [
|
||||
{
|
||||
"in": "internet",
|
||||
"service": "ping",
|
||||
"action": "accept",
|
||||
"flow-limit": { "count": 10, "interval": 6 }
|
||||
}
|
||||
]
|
||||
}
|
||||
11
services/basic/outgoing.policy.json
Normal file
11
services/basic/outgoing.policy.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"description": "Allow outgoing connections for http/https, dns, ssh, ntp, ssh and ping",
|
||||
"filter": [
|
||||
{
|
||||
"in": "_fw",
|
||||
"out": "internet",
|
||||
"service": ["http", "https", "dns", "ssh", "ntp", "ping"],
|
||||
"action": "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
62
services/caddy/config/Caddyfile
Normal file
62
services/caddy/config/Caddyfile
Normal file
@@ -0,0 +1,62 @@
|
||||
# https://hackviser.com/tactics/hardening/caddy
|
||||
{
|
||||
auto_https disable_redirects
|
||||
|
||||
# Do not write access logs to journald.
|
||||
log {
|
||||
exclude http.log.access
|
||||
}
|
||||
|
||||
# Write access logs to the logs volume in json
|
||||
# format. Only keep logs for the last 30 days.
|
||||
log access {
|
||||
format json
|
||||
output file /data/logs/access.log {
|
||||
roll_keep_for 720h
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Block with default http config that accepts requests on
|
||||
# fd/3 and redirects to https.
|
||||
(https-redir) {
|
||||
bind fd/3 {
|
||||
protocols h1
|
||||
}
|
||||
redir https://{host}{uri} 308
|
||||
}
|
||||
|
||||
# Block with default https config that accepts requests on
|
||||
# fd/4 and fdgram/5.
|
||||
(https) {
|
||||
bind fd/4 {
|
||||
protocols h1 h2
|
||||
}
|
||||
bind fdgram/5 {
|
||||
protocols h3
|
||||
}
|
||||
}
|
||||
|
||||
# Block with compression configuration.
|
||||
(compression) {
|
||||
encode zstd gzip
|
||||
}
|
||||
|
||||
# Block with headers that should be used by most
|
||||
# sites. Add HSTS and some other security headers.
|
||||
# Remove the server header because without it caddy
|
||||
# leaks the backend server version.
|
||||
# https://scotthelme.co.uk/a-new-security-header-referrer-policy/
|
||||
# https://scotthelme.co.uk/content-security-policy-an-introduction/
|
||||
(default-headers) {
|
||||
header {
|
||||
Strict-Transport-Security max-age=31536000; includeSubDomains; preload
|
||||
X-Content-Type-Options nosniff
|
||||
X-Frame-Options sameorigin
|
||||
Content-Security-Policy default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline';
|
||||
Referrer-Policy: same-origin
|
||||
-Server
|
||||
}
|
||||
}
|
||||
|
||||
import *.caddy
|
||||
11
services/caddy/http.policy.json
Normal file
11
services/caddy/http.policy.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"description": "Allow incoming http (TCP 80 & 443) ports",
|
||||
"filter": [
|
||||
{
|
||||
"in": "internet",
|
||||
"out": "_fw",
|
||||
"service": ["http", "https"],
|
||||
"action": "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
33
services/caddy/service.toml
Normal file
33
services/caddy/service.toml
Normal file
@@ -0,0 +1,33 @@
|
||||
user = "podman"
|
||||
capabilities = ["NET_BIND_SERVICE"]
|
||||
|
||||
[service]
|
||||
name = "caddy"
|
||||
image = "caddy:alpine"
|
||||
|
||||
[[mounts]]
|
||||
typ = "bind"
|
||||
source = "$HOME/caddy"
|
||||
target = "/etc/caddy"
|
||||
read_only = true
|
||||
|
||||
[[volumes]]
|
||||
source = "caddy-logs"
|
||||
target = "/data/logs"
|
||||
create = true
|
||||
|
||||
[[volumes]]
|
||||
source = "caddy-data"
|
||||
target = "/data/caddy"
|
||||
create = true
|
||||
|
||||
[[ports]]
|
||||
host = 80
|
||||
container = 80
|
||||
|
||||
[[ports]]
|
||||
host = 443
|
||||
container = 443
|
||||
|
||||
[[networks]]
|
||||
group = "caddy"
|
||||
8
services/caddy/update.sh
Normal file
8
services/caddy/update.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Symlink config files in base dir
|
||||
find "$base_dir" -name "*.caddy" -exec ln -sf {} "./config" \;
|
||||
|
||||
# Symlink config dir
|
||||
mkdir -p /home/podman/caddy
|
||||
ln -sf ./config /home/podman/caddy
|
||||
1
services/ssh/ssh.caddy
Normal file
1
services/ssh/ssh.caddy
Normal file
@@ -0,0 +1 @@
|
||||
test
|
||||
12
services/ssh/ssh.policy.json
Normal file
12
services/ssh/ssh.policy.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"description": "Allow limited incoming SSH access (TCP/22)",
|
||||
"filter": [
|
||||
{
|
||||
"in": "internet",
|
||||
"out": "_fw",
|
||||
"service": "ssh",
|
||||
"action": "accept",
|
||||
"conn-limit": { "count": 3, "interval": 30 }
|
||||
}
|
||||
]
|
||||
}
|
||||
25
services/ssh/sshd_config
Normal file
25
services/ssh/sshd_config
Normal file
@@ -0,0 +1,25 @@
|
||||
# SSHD config. See https://man.openbsd.org/sshd_config
|
||||
# https://hackviser.com/tactics/hardening/ssh
|
||||
|
||||
# Protocol 2 is more secure
|
||||
Protocol 2
|
||||
|
||||
# No root login or passwords
|
||||
PermitRootLogin no
|
||||
PasswordAuthentication no
|
||||
AuthenticationMethods publickey
|
||||
|
||||
# Allow tunneling, but not with option R (remote)
|
||||
AllowTcpForwarding local
|
||||
GatewayPorts yes
|
||||
|
||||
# override default of no subsystems
|
||||
Subsystem sftp internal-sftp
|
||||
|
||||
# Only allow users that are listed
|
||||
AllowUsers admin
|
||||
|
||||
# Only allow secure ciphers
|
||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,mlkem768x25519-sha256
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
|
||||
MACs hmac-sha2-256,hmac-sha2-512
|
||||
2
services/ssh/update.sh
Normal file
2
services/ssh/update.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ln -sf ./sshd_config /etc/ssh/sshd_config
|
||||
38
setup-alpine.answerfile
Normal file
38
setup-alpine.answerfile
Normal file
@@ -0,0 +1,38 @@
|
||||
# See: https://wiki.alpinelinux.org/wiki/Using_an_answerfile_with_setup-alpine
|
||||
|
||||
# Keymap
|
||||
KEYMAPOPTS="us us"
|
||||
|
||||
# Host name
|
||||
HOSTNAMEOPTS="-n alpi"
|
||||
DNSOPTS=none
|
||||
|
||||
# Contents of /etc/network/interfaces
|
||||
INTERFACESOPTS="auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
auto eth0
|
||||
iface eth0 inet static
|
||||
address 192.168.2.22
|
||||
netmask 255.255.255.0
|
||||
gateway 192.168.2.254
|
||||
"
|
||||
|
||||
TIMEZONEOPTS="-z Europe/Amsterdam"
|
||||
|
||||
PROXYOPTS=none
|
||||
|
||||
# User
|
||||
USEROPTS="-a -u -g audio,input,video,netdev admin"
|
||||
USERSSHKEY="https://github.com/maurictg.keys"
|
||||
|
||||
# First repo
|
||||
APKREPOSOPTS="-1"
|
||||
|
||||
SSHDOPTS="-c openssh"
|
||||
NTPOPTS="-c chrony"
|
||||
|
||||
# Data disk
|
||||
DISKOPTS="-m sys /dev/mmcblk0"
|
||||
LBUOPTS=none
|
||||
APKCACHEOPTS=none
|
||||
29
update.sh
Normal file
29
update.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
base_dir=$(pwd)
|
||||
|
||||
echo "Updating service scripts..."
|
||||
podman-openrc ./services /etc/init.d/
|
||||
|
||||
# Update services (awall policies, scripts)
|
||||
for service in "./services"/*/; do
|
||||
[ -d "$service" ] || continue
|
||||
cd "$service" || continue
|
||||
|
||||
# Run update.sh if present
|
||||
if [ -f "update.sh" ]; then
|
||||
source ./update.sh
|
||||
fi
|
||||
|
||||
# Symlink and activate each *.policy.json
|
||||
for policy in *.policy.json; do
|
||||
[ -e "$policy" ] || continue
|
||||
POLICY_NAME="${policy%.policy.json}"
|
||||
ln -sf "./$policy" "/etc/awall/optional/$POLICY_NAME.policy.json"
|
||||
awall enable "$POLICY_NAME.policy"
|
||||
done
|
||||
|
||||
cd "$base_dir"
|
||||
done
|
||||
|
||||
echo "Activating firewall..."
|
||||
awall activate
|
||||
Reference in New Issue
Block a user