Initial commit

This commit is contained in:
Maurice
2025-08-20 17:11:32 +02:00
commit 8c2f438749
21 changed files with 392 additions and 0 deletions

11
README.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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" }
]
}

View File

@@ -0,0 +1,11 @@
{
"description": "Allow ping-pong",
"filter": [
{
"in": "internet",
"service": "ping",
"action": "accept",
"flow-limit": { "count": 10, "interval": 6 }
}
]
}

View 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"
}
]
}

View 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

View File

@@ -0,0 +1,11 @@
{
"description": "Allow incoming http (TCP 80 & 443) ports",
"filter": [
{
"in": "internet",
"out": "_fw",
"service": ["http", "https"],
"action": "accept"
}
]
}

View 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
View 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
View File

@@ -0,0 +1 @@
test

View 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
View 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
View File

@@ -0,0 +1,2 @@
#!/bin/sh
ln -sf ./sshd_config /etc/ssh/sshd_config

38
setup-alpine.answerfile Normal file
View 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

3
todo.txt Normal file
View File

@@ -0,0 +1,3 @@
backup(), restore()
Volume labels (label)

29
update.sh Normal file
View 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