1.0.0 Release IaaS

This commit is contained in:
2026-03-15 04:41:02 +09:00
commit a7365da431
292 changed files with 36059 additions and 0 deletions

View File

@@ -0,0 +1,657 @@
Tags: #common, #configuration, #os
## Installation
### General setting
- Language: English - English
- Location: Other > Asia > South Korea
- Locale: United State - en_US.UTF-8
- Keymap to use: American English
### Network setting
#### Auto Configuration
- Auto Configuration: Using DHCP
#### Manual configuration
> `net` server should be configured manually - It has no private DHCP server and DNS server
- `cancel` auto configuration is needed
- IP address: 192.168.10.11/24
- Gateway: 192.168.10.1
- Name server address: 1.1.1.2
> After AdGuard home and BIND are set, change 192.168.10.11 on `/etc/resolv.conf`
#### Common setting
- Hostname: \[vmm, net, auth, dev, app\]
- Domain: ilnmors.internal
### User setting
- Root Password
- User name: \[vmm, net, auth, dev, app\]
- User account: \[vmm, net, auth, dev, app\]
- User Password
### Partition setting
- Partitioning method: Manual
#### Common
- 512MiB - EFI system partition (Boot flag: on)
- 1GiB - Ext4 Journaling (Mount: `/boot`)
#### Hypervisor
- 780GiB - Physical volume for LVM
- Configure the Logical Volume Manager
- Create volume group - vmm
- Create logical volume - vmm - \[root, swap, data\]
- Finish
- vmm-root: 64GiB - Ext Journaling (Mount: `/`)
- vmm-swap: 16GiB - SWAP area
- vmm-libvirt: 700GiB - Ext4 Journaling (Mount: `/var/lib/libvirt`)
#### VM servers
- 32GiB(net), 64GiB(auth), 256GiB(dev,app) - Ext4 Journaling(mount: /)
> Don't set swap partition, in qcow2 it is not effective but causes over head(CoW)
#### Application server
- Don't set HDD's partition
- Each HDD (2TB)
> btrfs supports RAID itself. After installing, set RAID10. These will be set with `fdisk`
### Debian package manager setting
- Scan extra installation media: no
- Mirror country: South Korea
- Archive mirror: deb.debian.org
- Proxy: \[blank\]
- Popularity-contest: no
### Installing packages setting
- \[\*\] SSH server
- \[\*\] Standard system utilities
## Environment configuration
### Package configuration
#### Common packages
```bash
apt update && apt upgrade
apt install sudo iptables-persistent crowdsec acl
```
- wait-for-it.sh
- Download it [here](https://github.com/vishnubob/wait-for-it/blob/master/wait-for-it.sh)
- ~/data/config/scripts
#### Hypervisor
```bash
apt install qemu-system-x86 ksmtuned libvirt-daemon-system virtinst virt-top ifupdown2 openvswitch-switch
# ksmtuned automatically adjusts ksm following its usage
# `cat /sys/kernel/mm/ksm/pages_sharing` shows the page shared by ksmtuned, result * 4KiB is sharing volume
```
#### VMs
```bash
apt install podman curl jq age
# app
apt install btrfs-progs
# SOPS package
curl -LO https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64
mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
chmod +x /usr/local/bin/sops
# Check update this via Diun
```
### User configuration
```bash
usermod [user_name] -u [uid]
# Change default group as svadmins
groupmod [group_name] -g 2000 -n svadmins
chown -R [user_name]:svadmins /home/[user_name]
chmod 770 /home/[user_name]
usermod -aG sudo [user_name]
# Hypervisor
usermod -aG kvm vmm
usermod -aG libvirt vmm
# After user configuration, proceed all step as user with sudo
```
### Make directory structure
#### Common
- /etc/secrets (root:root 711)
- /etc/secrets/\$UID (\$UID:root 500, files: $UID:root 400)
#### Hypervisor
- (vmm:svadmins 700)
- ~/data/config/{scripts,server,services,vms}
- ~/data/config/vms/{networks,storages,dumps}
- /var/lib/libvirt/images
#### VMs
- (\[vms\]:svadmins 700)
- ~/data/{config,containers}
- ~/data/config/{containers,scripts,secrets,server,services}
- ~/data/containers/apps/{certs,etc.}
- ~/kopia
#### Application server
##### SSD
- (app:svadmins 700)
- ~/data/{config,containers}
- ~/data/config/{containers,scripts,secrets,server,services}
- ~/data/containers/app/{certs,etc.}
- ~/kopia
##### HDD
RAID10 HDDs mount on this directory. Following 08_01_app_vm before you set.
- (app:svadmins 700)
- ~/hdd/data/containers
- (app:svadmins 770)
- ~/hdd/backups
- The scrub timer systemd is required for its integrity.
### SSH configuration
- File:
- /etc/ssh/sshd_config
- ~/.ssh/authorized_keys
```ini
# /etc/ssh/sshd_config
# key generation
ssh-keygen -t ed25519 -f ~/.ssh/key_name -C "comment"
# ...
PermitRootLogin no
# ...
```
```bash
# Reload sshd
sudo systemctl restart sshd
# Deploy publicly key for ssh
mkdir ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
# Paste public key from the client
```
> If the ICMP protocol doesn't work towards Windows, check Windows' firewall inbound `파일 및 프린터 공유(에코 요청 - ICMPv4-In)" and "파일 및 프린터 공유(에코 요청 - ICMPv6-In)` activatation
### Linger configuration
```bash
sudo loginctl enable-linger [user_name]
loginctl show-user $(whoami)
# Linger=yes
```
> Linger is necessary to execute services regardless their login session. If linger option were not activated, all the process would be terminated after logout.
### Network configuration
- File:
- /etc/hosts
- /etc/resolv.conf
- /etc/network/interfaces
#### Hostname
```ini
# /etc/hosts
[ip_address] [FQDN] [hostname]
```
#### DNS
- Only for `hypervisor` and `net` which are set manually.
```ini
# /etc/resolv.conf
# Before setting local DNS
nameserver 1.1.1.2
# After setting local DNS (net server)
# nameserver 192.168.10.11
```
#### Interface
- Only for `hypervisor` and `net` which are set manually
##### Hypervisor
```ini
# /etc/network/interfaces
# ifupdown2 and openvswitch-switch are required
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
# The openvswitch virtual bridge interfaces
auto ovsbr0
iface ovsbr0 inet manual
ovs_type OVSBridge
ovs_ports enp2s0
auto ovsbr1
iface ovsbr1 inet manual
ovs_type OVSBridge
ovs_ports enp3s0 vlan1 vlan10
# The primary network interfaces
auto enp2s0
iface enp2s0 inet manual
ovs_type OVSPort
ovs_bridge ovsbr0
auto enp3s0
iface enp3s0 inet manual
ovs_type OVSPort
ovs_bridge ovsbr1
ovs_options vlan_mode=native-untagged tag=1 trunks=10
# The vlan interfaces
auto vlan1
iface vlan1 inet static
ovs_type OVSIntPort
ovs_bridge ovsbr1
ovs_options tag=1
address 192.168.1.10/24
auto vlan10
iface vlan10 inet static
ovs_type OVSIntPort
ovs_bridge ovsbr1
ovs_options tag=10
address 192.168.10.10/24
gateway 192.168.10.1
# The primary network interfaces for IPv6
# iface enp2s0 inet6 auto
# iface enp3s0 inet6 auto
# openvswitch vlan options
# ovs_options tag=n : access mode
# ovs_options trunk=n : trunk mode
# ovs_options vlan_mode=native-untagged tag=n trunk=m: native untagged mode
```
> Having two IP addresses in a client can cause some problems. One of them is `Asymmetric routing`. When it gets the packet via vlan10 interface from vlan1 client, the server uses vlan1 interface, because client and server are in the same subnet (L2). It can cause problem, like disconnection usually. There is the solution to solve this.
>
> - Use `mangle` table in iptables, you can solve this, but it is complex to use.
> - Use `IP rule` command, it is easier to set.
##### net
```ini
# /etc/network/interfaces
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto enp1s0
iface enp1s0 inet static
address 192.168.10.11/24
gateway 192.168.10.1
# Don't set dns options. It will be set on /etc/resolv.conf
```
### Hypervisor
#### VFIO configuration
##### IOMMU setting
- File: /etc/default/grub
```ini
# /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on"
# Intel CPU: intel_iommu=on, AMD CPU: amd_iommu=on
```
##### Device setting
- File: /etc/modprobe.d/vfio.conf
```bash
# Check device
lspci -nn | grep -i sata
# 04:00.0 STAT controller [0106] ... [1b21:1064]
# PCI address: 04:00.00 / Device ID 1b21:1064
# IOMMU group checking
readlink /sys/bus/pci/devices/0000\:04\:00.0/iommu_group
# ../../../../kernel/iommu_groups/14
# The group of PCI device which is needed to passthrough is 14
ls /sys/kernel/iommu_groups/devices/14
# 04:00.00 # To passthrough device, it has to be only device in its IOMMU group
```
```ini
# /etc/modprobe.d/vfio.conf
options vfio-pci ids=1b21:1064
softdep ahci pre: vfio-pci
```
##### Apply all configuration
```bash
sudo update-grub
sudo update-initramfs -u
sudo reboot
# Check IOMMU enable message
dmesg | grep -e DMAR -e IOMMU
# Check the driver
lspci -nnk -d 1b21:1064
# Kernel driver in user: vfio-pci
```
### VM servers
#### Serial console setting
```bash
# It is necessary to connect via ssh to set serial setting
sudo systemctl enable getty@ttyS0
sudo systemctl start getty@ttyS0
```
#### Secret management
##### SOPS setting
- File:
- ~/data/config/secrets/age-key.gpg
- ~/data/config/secrets/.sops.yaml
- ~/data/config/secrets/.secret.yaml
- /etc/secrets/\$UID (\$UID:root 500)
```bash
# Generate the key for sops
age-keygen -o ~/data/config/secrets/age-key
# # created: 2025-10-17T13:30:00Z
# # public key: age1ql3z7h0cfscg......
# AGE-SECRET-KEY-1.....
# Public key is printed when key generated
gpg --symmetric age-key && rm age-key
> GPG password: password
nano ~/data/config/secrets/.sops.yaml && chmod 600 ~/data/config/secrets/.sops.yaml
```
```yaml
# ~/data/config/secrets/.sops.yaml
creation_rules:
- path_regex: \.secret\.yaml$
age: [public_key]
```
##### Create Secret file
```yaml
# ~/data/config/secrets/.secret.yaml
# Format of .secret.yaml
# app1.env:
1SECRET: '1secret'
2SECRET: '2secret'
app1.file: |
-----TEXT-AREA-----
contents of 3secret
-----END-AREA-----
# app2.env
3SECRET: '3secret'
4SECRET: '4secret'
# ...
```
```bash
sops --encrypt --in-place ~/data/config/secrets/.secret.yaml
```
##### Secret scripts
- File:
- ~/data/config/scripts/secrets/edit_secret.sh
- ~/data/config/scripts/secrets/extract_secret.sh
```bash
#!/bin/bash
# edit_secret.sh /path/of/secret
set -e
KEY_PATH="$HOME/data/config/secrets"
SECRET_FILE="$1"
usage() {
echo "Usage: $0 \"/path/of/secret/file\""
exit 1
}
if [ -z "$SECRET_FILE" -o ! -f "$SECRET_FILE" ]; then
echo "Error: Secret file path is needed"
usage
fi
if [ ! -f "$KEY_PATH/age-key.gpg" ]; then
echo "Error: There is no key file"
exit 1
fi
# Delete password file after script
cleanup() {
if [ -f "/run/user/$UID/age-key" ]; then
rm -f "/run/user/$UID/age-key"
fi
}
trap cleanup EXIT
echo -n "Enter GPG passphrase: "
read -s GPG_PASSPHRASE
echo
echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \
--output "/run/user/$UID/age-key" \
--decrypt "$KEY_PATH/age-key.gpg" && \
chmod 600 "/run/user/$UID/age-key"
if [ -z "/run/user/$UID/age-key" ]; then
echo "Error: Key file does not exist"
exit 1
fi
gpgconf --kill gpg-agent
SOPS_AGE_KEY="$(cat "/run/user/$UID/age-key")"
SOPS_AGE_KEY="$SOPS_AGE_KEY" sops "$SECRET_FILE"
```
```bash
#!/bin/bash
# extract_secret.sh /path/of/secret (-f|-e <value>)
set -e
KEY_PATH="$HOME/data/config/secrets"
SECRET_FILE=$1
# shift the $2 as $1 ($1 < $2)
shift
# usage() function
usage() {
echo "Usage: $0 \"/path/of/secret/file\" (-f|-e \"yaml section name\")" >&2
echo "-f <type name>: Print secret file" >&2
echo "-e <type name>: Print secret env file" >&2
exit 1
}
while getopts "f:e:" opt; do
case $opt in
f)
VALUE="$OPTARG"
TYPE="FILE"
;;
e)
VALUE="$OPTARG"
TYPE="ENV"
;;
\?) # unknown options
echo "Invalid option: -$OPTARG" >&2
usage
;;
:) # parameter required option
echo "Option -$OPTARG requires an argument." >&2
usage
;;
esac
done
# Get option and move to parameters - This has no functional thing, because it only use arguments with parameters
shift $((OPTIND - 1))
# Check necessary options
if [ ! -f "$SECRET_FILE" ]; then
echo "Error: secret file path is required" >&2
usage
fi
if [ -z "$TYPE" ]; then
echo "Error: -f or -e option requires" >&2
usage
fi
if [ ! -f "$KEY_PATH/age-key.gpg" ]; then
echo "Error: There is no key file" >&2
usage
fi
# Delete password file after script
cleanup() {
if [ -f "/run/user/$UID/age-key" ]; then
rm -f "/run/user/$UID/age-key"
fi
}
trap cleanup EXIT
echo -n "Enter GPG passphrase: " >&2
read -s GPG_PASSPHRASE
echo >&2
echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \
--output "/run/user/$UID/age-key" \
--decrypt "$KEY_PATH/age-key.gpg" && \
chmod 600 "/run/user/$UID/age-key"
if [ ! -f "/run/user/$UID/age-key" ]; then
echo "Error: Key file does not exist" >&2
exit 1
fi
gpgconf --kill gpg-agent
SOPS_AGE_KEY="$(cat "/run/user/$UID/age-key")"
if [ "$TYPE" == "FILE" ]; then
if RESULT=$(SOPS_AGE_KEY="$SOPS_AGE_KEY" sops --decrypt --extract "[\"$VALUE\"]" --output-type binary "$SECRET_FILE") ; then
echo -n "$RESULT"
exit 0
else
echo "Error: SOPS extract error" >&2
exit 1
fi
fi
if [ "$TYPE" == "ENV" ]; then
if RESULT=$(SOPS_AGE_KEY="$SOPS_AGE_KEY" sops --decrypt --extract "[\"$VALUE\"]" --output-type dotenv "$SECRET_FILE") ; then
echo -n "$RESULT"
exit 0
else
echo "Error: SOPS extract error" >&2
exit 1
fi
fi
```
##### Secret value management
- Using `extract_secret.sh`
- Inject secret value to `podman secret` or `/etc/secrets/$UID`
```bash
# /etc/secrets/$UID
# Before use sudo tee, make sure sudo doesn't need password.
# i.e. sudo ps -ef command execute before this command.
# Env file
extract_secret.sh ~/data/config/secrets/.secret.yaml -e "$value" > /run/user/$UID/tmp.env \
&& sudo mv /run/user/$UID/tmp.env /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chown $UID:root /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chmod 400 /etc/secrets/$UID/"$FILE_NAME"
# Normal file
extract_secret.sh ~/data/config/secrets/.secret.yaml -f "$value" > /run/user/$UID/tmp.env \
&& sudo mv /run/user/$UID/tmp.env /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chown $UID:root /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chmod 400 /etc/secrets/$UID/"$FILE_NAME"
# Podman secret
# Podman doesn't supports .env file parsing, you have to enroll all values
extract_secret.sh ~/data/config/secrets/.secret.yaml -f "$value" | podman secret create "[$FILE_NAME|$ENV_NAME]" -
```
> The reason why apply the secret value manually is its complexity. The `yaml` file can hold a lot of values in one section. It is hard to make the script to deal with a lot of exception and senario. The simple way is the best way.

View File

@@ -0,0 +1,273 @@
Tags: #common, #configuration, #network, #security
## iptables
iptables is the firewall program to manage netfilter which is in the linux kernel. Basically, the iptables' settings are temporary(when you reboot the computer they disappear). So, you can use netfilter-persistent program to make settings permanent. iptables has 3 modes(tables), which are filter, nat, and mangle. And each table has their chain like input, forward or prerouting, etc. iptables' setting is temporary, when the machine reboot, all rules will be reset. After set the rules use `netfilter-persistent save` to make the rules permanently.
### tables
#### filter
filter is the basic and most important table in iptables. Its role is to simply judge, to ACCEPT or DROP the packets. When you use the iptables command without table option (-t), the filter table is default option. There are chians of filter table below.
- INPUT: check the packets in
- OUTPUT: check the packets out
- FORWARD: check the packets which are passing through
#### nat
nat is the table changing packets' address and port without changing contents of packets. There are chians of nat table below.
- PREROUTING: change the destination address or port right after packets arrived
- POSTROUTING: change the source address or port before packets depart
- OUTPUT: change the destination address or port of packets which produced by itself (This doesn't change the source IP; DNAT)
#### mangle
mangle is a special table to mark on the packets. It works on the every chain and it works on special purpose like asymmetric routing.
### grammar
#### Commands
- -A \[--append\]: create the new rules
- -C \[--check\]: check the packets
- -D \[--delete\]: delete the rules
- -F \[--flush\]: delete all rules from the chain
- -I \[--insert\]: Insert the new rules
- -L \[--list\]: print the rules
- -N \[--new\]: create the new chain
- -P \[--policy\]: change the default policy
- -R \[--replace\]: change the rules as a new rule
- -X \[--delete-chain\]: delete chain
- -Z \[--zero\]: reset the packet and byte counter value of all chain
#### match
- -s \[--source\]: designate source ip address or networks
- -d \[--destination\]: designate destination ip address or networks
- -p \[--protocol\]: match protocol(tcp/udp/icmp.. etc.)
- --dport: designate specific protocol number (When the protocol is already defined - tcp or udp)
- --syn: match syn packets. when starting new TCP connection, apply the rule.
- -i \[--in-interface\]: input interface
- -o \[--out-interface\]: output interface
- --comment: comment(max 256byte)
- -f \[--fragment\]
- -t \[--table\]: designate table set(default: filter)
- -j \[--jump\]: designate targets
- -m \[--match\]: match with specific module
- conntrack --ctstate: current linked connection
#### target
- ACCEPT: allow packets
- DROP: deny packets without response (Hide server existence)
- REJECT: deny packets with response (Show server existance)
- LOG: log the packets on syslog
- RETURN: stop current rules, and return to the previous chain
#### Command
```bash
iptables -L -v -n # Print all rules in filter table
iptables -L -v -n -t nat # Print all rules in nat table
```
## netfilter-persistent
### Save the rules
```bash
sudo netfilter-persistent save
```
- File:
- /etc/iptables/rules.v4
- /etc/iptables/rules.v6
### Reload the rules (manual)
```bash
# Edit the file
# Test
sudo bash -c 'iptables-restore --test < /etc/iptables/rules.v4'
# If there were no message, it would have no error
sudo netfilter-persistent start
# or
sudo netfilter-persistent reload
```
### Rule files
- File: /etc/iptables/rules.v4
#### Hypervisor (vmm)
```ini
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "allow established connection" -j ACCEPT
-A INPUT -i lo -m comment --comment "allow local connection" -j ACCEPT
-A INPUT -p icmp -m comment --comment "allow ICMP connection" -j ACCEPT
-A INPUT -s 192.168.1.11/32 -p tcp -m tcp --dport 22 -m comment --comment "allow emergemcy LAN console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.2/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.3/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.4/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.13/32 -p tcp -m tcp --dport 22 -m comment --comment "allow code-server ssh connection" -j ACCEPT
COMMIT
```
#### net
```ini
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "allow established connection" -j ACCEPT
-A INPUT -i lo -m comment --comment "allow local connection" -j ACCEPT
-A INPUT -p icmp -m comment --comment "allow ICMP connection" -j ACCEPT
-A INPUT -s 10.10.10.2/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.3/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.4/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.1/32 -p tcp -m tcp --dport 22 -m comment --comment "allow OPNsense ssh connection for ACME update" -j ACCEPT
-A INPUT -s 192.168.10.10/32 -p tcp -m tcp --dport 22 -m comment --comment "allow hypervisor ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.13/32 -p tcp -m tcp --dport 22 -m comment --comment "allow code-server ssh connection" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 2053 -m conntrack --ctorigdstport 53 -m comment --comment "allow tcp DNS connection which is only prerouted from 53" -j ACCEPT
-A INPUT -p udp -m udp --dport 2053 -m conntrack --ctorigdstport 53 -m comment --comment "allow udp DNS connection which is only prerouted from 53" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 2443 -m conntrack --ctorigdstport 443 -m comment --comment "allow tcp DoH(https) connection which is only prerouted from 443" -j ACCEPT
-A INPUT -p udp -m udp --dport 2443 -m conntrack --ctorigdstport 443 -m comment --comment "allow udp DoH(https) connection which is only prerouted from 443" -j ACCEPT
-A INPUT -s 192.168.10.1/32 -p tcp -m tcp --dport 2253 -m comment --comment "allow opnsense tcp nsupdate connection" -j ACCEPT
-A INPUT -s 192.168.10.1/32 -p udp -m udp --dport 2253 -m comment --comment "allow opnsense udp nsupdate connection" -j ACCEPT
-A INPUT -s 192.168.10.12/32 -p tcp -m tcp --dport 2253 -m comment --comment "allow auth tcp nsupdate connection" -j ACCEPT
-A INPUT -s 192.168.10.12/32 -p udp -m udp --dport 2253 -m comment --comment "allow auth udp nsupdate connection" -j ACCEPT
-A INPUT -s 192.168.10.13/32 -p tcp -m tcp --dport 2253 -m comment --comment "allow dev tcp nsupdate connection" -j ACCEPT
-A INPUT -s 192.168.10.13/32 -p udp -m udp --dport 2253 -m comment --comment "allow dev udp nsupdate connection" -j ACCEPT
-A INPUT -s 192.168.10.14/32 -p tcp -m tcp --dport 2253 -m comment --comment "allow app tcp nsupdate connection" -j ACCEPT
-A INPUT -s 192.168.10.14/32 -p udp -m udp --dport 2253 -m comment --comment "allow app udp nsupdate connection" -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp -m tcp --dport 53 -m comment --comment "allow and preroute tcp DNS connection 53 to 2053" -j REDIRECT --to-ports 2053
-A PREROUTING -p udp -m udp --dport 53 -m comment --comment "allow and preroute udp DNS connection 53 to 2053" -j REDIRECT --to-ports 2053
-A PREROUTING -p tcp -m tcp --dport 443 -m comment --comment "allow and preroute tcp DoH(https) connection 443 to 2443" -j REDIRECT --to-ports 2443
-A PREROUTING -p udp -m udp --dport 443 -m comment --comment "allow and preroute udp DoH(https) connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 53 -m comment --comment "NAT local tcp DNS connection 53 to 2053" -j REDIRECT --to-ports 2053
-A OUTPUT -d 127.0.0.1/32 -p udp -m udp --dport 53 -m comment --comment "NAT local udp DNS connection 53 to 2053" -j REDIRECT --to-ports 2053
-A OUTPUT -d 192.168.10.11/32 -p tcp -m tcp --dport 53 -m comment --comment "NAT local tcp DNS connection 53 to 2053" -j REDIRECT --to-ports 2053
-A OUTPUT -d 192.168.10.11/32 -p udp -m udp --dport 53 -m comment --comment "NAT local udp DNS connection 53 to 2053" -j REDIRECT --to-ports 2053
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp DoH(https) connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 127.0.0.1/32 -p udp -m udp --dport 443 -m comment --comment "NAT local udp DoH(https) connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 192.168.10.11/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp DoH(https) connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 192.168.10.11/32 -p udp -m udp --dport 443 -m comment --comment "NAT local udp DoH(https) connection 443 to 2443" -j REDIRECT --to-ports 2443
COMMIT
```
#### auth
```ini
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [204:15800]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "allow established connection" -j ACCEPT
-A INPUT -i lo -m comment --comment "allow local connection" -j ACCEPT
-A INPUT -p icmp -m comment --comment "allow ICMP connection" -j ACCEPT
-A INPUT -s 10.10.10.2/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.3/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.4/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.1/32 -p tcp -m tcp --dport 22 -m comment --comment "allow OPNsense ssh connection for ACME update" -j ACCEPT
-A INPUT -s 192.168.10.10/32 -p tcp -m tcp --dport 22 -m comment --comment "allow hypervisor ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.13/32 -p tcp -m tcp --dport 22 -m comment --comment "allow code-server ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.1/32 -p tcp -m tcp --dport 9000 -m comment --comment "allow opnsense step-ca connection" -j ACCEPT
-A INPUT -s 192.168.10.13/32 -p tcp -m tcp --dport 9000 -m comment --comment "allow dev step-ca connection" -j ACCEPT
-A INPUT -s 192.168.10.14/32 -p tcp -m tcp --dport 9000 -m comment --comment "allow app step-ca connetcion" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 2080 -m conntrack --ctorigdstport 80 -m comment --comment "allow tcp http connection which is only from 80" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 2443 -m conntrack --ctorigdstport 443 -m comment --comment "allow tcp https connection which is only from 443" -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp -m tcp --dport 80 -m comment --comment "allow and preroute tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A PREROUTING -p tcp -m tcp --dport 443 -m comment --comment "allow and preroute tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 80 -m comment --comment "NAT local tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A OUTPUT -d 192.168.10.12/32 -p tcp -m tcp --dport 80 -m comment --comment "NAT local tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 192.168.10.12/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 636 -m comment --comment "NAT local tcp ldaps connection 636 to 6360" -j REDIRECT --to-ports 6360
-A OUTPUT -d 192.168.10.12/32 -p tcp -m tcp --dport 636 -m comment --comment "NAT local tcp ldaps connection 636 to 6360" -j REDIRECT --to-ports 6360
COMMIT
```
#### dev
```ini
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "allow established connection" -j ACCEPT
-A INPUT -i lo -m comment --comment "allow local connection" -j ACCEPT
-A INPUT -p icmp -m comment --comment "allow ICMP connection" -j ACCEPT
-A INPUT -s 10.10.10.2/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.3/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.4/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.1/32 -p tcp -m tcp --dport 22 -m comment --comment "allow OPNsense ssh connection for ACME update" -j ACCEPT
-A INPUT -s 192.168.10.10/32 -p tcp -m tcp --dport 22 -m comment --comment "allow hypervisor ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.12/32 -p tcp -m tcp --dport 5432 -m comment --comment "allow auth postgresql connection" -j ACCEPT
-A INPUT -s 192.168.10.14/32 -p tcp -m tcp --dport 5432 -m comment --comment "allow app postgresql connection" -j ACCEPT
-A INPUT -s 192.168.10.12/32 -p tcp -m tcp --dport 2080 -m conntrack --ctorigdstport 80 -m comment --comment "allow tcp http connection which is only from 80 and main caddy" -j ACCEPT
-A INPUT -s 192.168.10.12/32 -p tcp -m tcp --dport 2443 -m conntrack --ctorigdstport 443 -m comment --comment "allow tcp https connection which is only from 443 and main caddy" -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp -m tcp --dport 80 -m comment --comment "allow and preroute tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A PREROUTING -p tcp -m tcp --dport 443 -m comment --comment "allow and preroute tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 80 -m comment --comment "NAT local tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A OUTPUT -d 192.168.10.13/32 -p tcp -m tcp --dport 80 -m comment --comment "NAT local tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 192.168.10.13/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
COMMIT
```
#### app
```ini
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "allow established connection" -j ACCEPT
-A INPUT -i lo -m comment --comment "allow local connection" -j ACCEPT
-A INPUT -p icmp -m comment --comment "allow ICMP connection" -j ACCEPT
-A INPUT -s 10.10.10.2/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.3/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 10.10.10.4/32 -p tcp -m tcp --dport 22 -m comment --comment "allow vpn console ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.1/32 -p tcp -m tcp --dport 22 -m comment --comment "allow OPNsense ssh connection for ACME update" -j ACCEPT
-A INPUT -s 192.168.10.10/32 -p tcp -m tcp --dport 22 -m comment --comment "allow hypervisor ssh connection" -j ACCEPT
-A INPUT -s 192.168.10.13/32 -p tcp -m tcp --dport 22 -m comment --comment "allow code-server ssh connection" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 2080 -m conntrack --ctorigdstport 80 -m comment --comment "allow tcp http connection which is only from 80" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 2443 -m conntrack --ctorigdstport 443 -m comment --comment "allow tcp https connection which is only from 443" -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp -m tcp --dport 80 -m comment --comment "allow and preroute tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A PREROUTING -p tcp -m tcp --dport 443 -m comment --comment "allow and preroute tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 80 -m comment --comment "NAT local tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A OUTPUT -d 192.168.10.14/32 -p tcp -m tcp --dport 80 -m comment --comment "NAT local tcp http connection 80 to 2080" -j REDIRECT --to-ports 2080
-A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
-A OUTPUT -d 192.168.10.14/32 -p tcp -m tcp --dport 443 -m comment --comment "NAT local tcp https connection 443 to 2443" -j REDIRECT --to-ports 2443
COMMIT
```

View File

@@ -0,0 +1,306 @@
systTags: #common, #configuration, #virtualization, #container, #os
## podman
The container is one of virtual technology to run applications independently regardless of what the host system is. One of them, Docker uses daemon(dockerd) and docker socket(root authority), so all container can access hosts' root authority. It causes danger that hackers can take down root authority via docker containers. Additionally, docker daemon system makes it hard to combine each container to systemd, because all containers are in charge of dockerd.
Podman is a new technology to solve docker's problems. It implements daemonless, and rootless container environment. It reduces the danger of the hacker getting root authority via containers, and it is easy to combine each container to systemd.
### Configuration
- File: /etc/containers/registries.conf
```bash
# Check the linger
loginctl show-user $(whoami)
# Linger=yes
```
```ini
# /etc/containers/registries.conf
unqualified-search-registries = ["docker.io"]
```
### Networking
Podman uses a specific IP address and domain names to communicate with its host system using pasta. Pasta is a new default network mode in podman 4.0 which can allow communication with the host system directly. Therefore, it doesn't need a bridge or network host. `169.254.1.2` is 'link-local address' to communicate with host's system. The pasta conduct the SNAT. Therefore, even though the packet's original source IP were container's IP, when the packet passes the pasta its source IP changes as 127.0.0.1 or its own IP (localhost).
- `/etc/hosts` in container
- 169.254.1.2 host.containers.internal host.docker.internal
When you use the command below, you can add domain to the line.
- --host-name mydomain.internal:host-gateway
Additionally, rootless podman containers cannot bind host's privileged ports number(=<1024). Therefore, if container needed to use these ports, you would have to use iptables' nat table. The example of iptables' usage is [here](./03_02_iptables.md).
#### Bridge mode
Bridge mode create a separated virtual IP network from host's network. This mode supports simple DNS function between containers belonging to the same network. This mode basically uses SNAT(Source NAT) mutually; both inbound and outbond. It is because basically podman runs as rootless. So, the container can't distinguish where the packets come from, except from the container belonging to the same podman network. At the same time, client also can't distinguish where the packets come from. Because every packet seems like come from host server. It makes inspection hard for both of client and containers.
### ID mapping
Podman basically mapped container's root to host's executing user. Despite the root, podman uses subuid, subgid system. They are set on `/etc/subuid`, `/etc/subgid`.
- host(1000:1000) < container(0:0)
- host(100999:100999) < container(1000:1000) : subuid, subgid
When podman runs and executes commands with -u option, --userns=keep-id option, -uid, -gid option, it can adjust mapping.
- -u uid:gid: excute container with hosts's uid (bring the host's UID/GID towards container's `/etc/passwd` directly.)
- --userns=keep-id: mapping all container's file permission to host's file permission.
- --cap-add=DAC_READ_SEARCH option: without root authority, container can access every file regardless permission.
#### Mapping error
Some container doesn't execute a container with root permission. They execute the container with their specific uid (i.e. UID:53 - BIND). In this case, when the container runs with `-u` option or `--userns=keep-id` option can make mapping error very frequently.
- `-u` option
When the container runs with `-u` option, the entrypoint can't work properly in many cases because they were already set that runs entrypoint as specific uid. So, if `-u` option were set, then it would cause permission error.
- `--userns=keep-id` option
This option makes container's directory/file UID as the same as the host's directory/file UID. So, it turns off the UID/GID mapping itself. When some directory which has root authorization mapped with hosts' file it occurs UID mapping error.
#### Permission management
Use ACL packages, to give additional permission of directory. It can give the extra permission to subuid or host's uid.
```bash
# u:[subuid]:rwx
sudo getfacl /path/of/podman_directory
# `-d` option is to set permission for file or directory which are created automatically
# `-R` option is to set permission for file or directory which already existed
sudo setfacl -d -m u:[subuid]:rwx /path/of/podman_directory
sudo setfacl -d -m u:[hostuid]:rwx /path/of/podman_directory
```
### Usage of podman
#### Containerfile and build
Containerfile's format is compatible with dockerfile. Here is the example below. Containerfile can be built as podman image with `podman build` command.
```containerfile
FROM caddy:2.10.2-builder-alpine AS builder
RUN xcaddy build \
--with github.com/caddy-dns/rfc2136 \
--with github.com/hslatman/caddy-crowdsec-bouncer/crowdsec \
--with github.com/hslatman/caddy-crowdsec-bouncer/http \
FROM caddy:2.10.2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
```
```bash
# Build container file as podman image
podman build -t caddy:2.10.2-main -f /path/of/containterfile-caddy-main . && podman image prune -f
# Delete source images
podman rmi caddy:2.10.2-builder-alpine
podman rmi caddy:2.10.2
```
#### Podman images
- `podman images`: Print list of all local podman images
- `podman image pull [image_name]`: Download podman images from repository
- `podman pull` is the same command
- `podman image prune`: Remove unused and untagged images
- `podman image rm [images]`: Remove local podman image
- `podman rmi` is the same command
#### Run and exec
podman ps \[--all\]: it shows podman container lists
- podman run
- --name: container's name
- --restart: restart mode - unless-stopped
- --add-host: additional host domain name on 169.254.1.2
- --cap-add: add some specific privileges without root authority
- -p host_ports:container_port
- -v host_path:container_path:permission(rw, ro, and when you use SELinux, you can use Z or z)
- -e environment_value
- -d: run background
- image_name
- podman exec -it \[container_name\] \[command\]
#### Pod
Pod makes each container which are in the same pod share some specific resources. The network(IP address), storage volumes. However, each container has their own file system, process, and resource limits. So, this is very useful to use various containers which has close relationship like application and Redis(cache db).
- podman pod create
- --name: pod's name
- -p: host_ports:pod_ports
- podman run
- ...
- --pod pod's name
> Don't use `-p` option. Pod already has `-p` option.
#### Container and file management
#### container
- check pure container
```bash
podman run --rm -it --entrypoint sh [image_name] --args
# or
podman run --rm -it [image_name] sh --args
```
#### file management
- Using `podman exec` to manage file
- Use ACL package `setfacl`
- `--cap-add=DAC_READ_SEARCH` option allows to read all file without permission to backup (for kopia)
### Quadlet and systemd
#### Register the secret on podman secret
- Using `edit_secret.sh` and `extract_secret.sh`
- Inject secret value to `podman secret` or `/etc/secrets/$UID`
```bash
# /etc/secrets/$UID
# Before use sudo tee, make sure sudo doesn't need password.
# i.e. sudo ps -ef command execute before this command.
# Env file
extract_secret.sh ~/data/config/secrets/.secret.yaml -e "$value" > /run/user/$UID/tmp.env \
&& sudo mv /run/user/$UID/tmp.env /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chown $UID:root /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chmod 400 /etc/secrets/$UID/"$FILE_NAME"
# Normal file
extract_secret.sh ~/data/config/secrets/.secret.yaml -f "$value" > /run/user/$UID/tmp.env \
&& sudo mv /run/user/$UID/tmp.env /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chown $UID:root /etc/secrets/$UID/"$FILE_NAME" \
&& sudo chmod 400 /etc/secrets/$UID/"$FILE_NAME"
# Podman secret
# Podman doesn't supports .env file parsing, you have to enroll all values
extract_secret.sh ~/data/config/secrets/.secret.yaml -f "$value" | podman secret create "[$FILE_NAME|$ENV_NAME]" -
```
> ` podman secret inspect --showsecret --format '{{.SecretData}}' $secret_name` shows the content of secret
#### Define quadlet file
- File:
- ~/data/config/containers/app/app.container
- ~/data/config/containers/app/app.pod
Quadlet is to define of specification as `.quadlet` or `.container`. Quadlet uses these file to make `.service` file to combine container to systemd. Here is the example of `.container` file below.
```ini
# app.container
[Quadlet]
# Don't make a dependencies
DefaultDependencies=false
[Unit]
Description=app
After=a.service
Wants=a.service
Requires=a.service
[Service]
ExecStartPre=%h/data/config/scripts/wait-for-it.sh -h 192.168.10.1 -p 8080 -t 20
[Container]
# Pod=app.pod
Image=localhost/app:1.0.0
ContainerName=app
PublishPort=2080:80/tcp
PublishPort=2443:443/tcp
AddHost=app.service.internal:host-gateway
Volume=%h/data/containers/app:/home/app:rw
Environment="ENV1=ENV1"
Secret=ENV_NAME,type=env
Secret=app.file,target=/path/of/secret/file/name
# podman run [options] [image] example --config exconfig
Exec=example --config exconfig
# If you want to change Entrypoint itself, use
Entrypoint=sh -c 'command'
# For Diun
Label=diun.enable=true
# For Diun to track repository new version
Label=diun.watch_repo=true
# For Diun, and it needs `diun.yml` configuration
Label=diun.regopt=container-source
[Install]
WantedBy=default.target
```
```ini
# app.pod
[Quadlet]
# Don't make a dependencies
DefaultDependencies=false
[Pod]
Name=app
PublishPort=2080:80/tcp
```
#### Create systemd `.service` file
```bash
# linger has to be activated
mkdir -p ~/.config/containers/systemd
ln -s ~/data/config/containers/app/app.container ~/.config/containers/systemd/app.container
# This command makes ~/.config/systemd/user/my-app.service
systemctl --user daemon-reload
```
#### Enable and start service
```bash
systemctl --user enable app.service
systemctl --user start app.service
```
---
## Following goal
### Health check
```ini
# i.e. caddy
# Podman [Container] section
[Container]
# Health check configuration
# Health check command
HealthcheckCommand=curl -f http://localhost/ || exit 1
# Health check interval
HealthcheckInterval=30s
# the time to wait for health check
HealthcheckTimeout=5s
# the number to try to health check
HealthcheckRetries=3
# the time to wait to start first health check
HealthcheckStartPeriod=15s
# override.conf [Service] section
[Service]
# Restart, if it is not healthy
Restart=on-failure
```

View File

@@ -0,0 +1,321 @@
Tags: #common, #configuration, #network, #security
## CrowdSec
CrowdSec is the free, open-source IPS(Intrusion Prevention System). It has a distributed architecture. When an agent detects malicious IPs from log, it reports the information to LAPI. This information is shared to CrowdSec's central server anonymously and is spread across the world. Additionally, bouncer receives a blacklist from LAPI and when they approach the server, the bouncer blocks them.
### Collection
#### Parser
It is how to organize the raw logs to the parsed log that scenario can understand. It is works on agent, and parsed log is transferred to LAPI to decide.
#### Scenario
It is how to analyze the malicious attack from the parsed log from the Agent. The LAPI decides what to do for malicious attack, and transfers the result to Bouncer to block.
### Agent
Agent is the detector in each server. They analyze the logs. When they find malicious approaches, or abnormal and harmful actions, they report the information to LAPI(Local API). It analizes the log following `Parsers`.
### LAPI
LAPI server is a local central collector and reporter of malicious attack information. It decides what to apply for traffic following `Scenarios`. In this homelab it is located in OPNsense, because the center of gateway of home network is Firewall. When agent reports threats to the LAPI server, LAPI decides whether or not to block them, and reports to the central CrowdSec server. This information will be spread to all CrowdSec users in the world.
### Bouncer
When LAPI decides to block some IPs, they create a blacklist and give it to bouncer. Bouncer blocks and bans some IPs depending on LAPI's blacklist. `Caddy-auth (L7)` and `OPNsense (L4)` will be bouncer to ban. The most important thing is LAPI just decide what to ban, and Bouncer conducts ban practically.
## CrowdSec in OPNsense
OPNsense supports CrowdSec with community plugin. This is not a basic function so when you want to use it in OPNsense you should install the community plugin.
### Installation
- System:Firmware:Plugins
- \[\*\] Show community plugins
- `os-crowdsec`
### General configuration
Services:CrowdSec:Settings
- \[\*\] Enable Log Processor (IDS)
- \[\*\] Enable LAPI
- \[\*\] Enable Remediation Component (IPS)
- \[ \] Manual LAPI configuration
- LAPI listen address: \[opnsense IP: 192.168.10.1\]
- LAPI listen port: 8080
- \[\*\] Create blocklist rules
- `Apply`
> Enable Remediation Component (IPS) option means, Bouncer will be integrated with OPNsense's firewall rules
> Set LAPI configuration manually, `Manual LAPI configuration` is needed.
### Machines configuration
#### OPNsense console
```sh
# 8) shell
cscli machines add [server_name] -a -f -
# ---
# Machine 'server_name' successfully added to the local API.
# url: http://192.168.10.1:8080
# login: [server_name]
# password: (API key)
# ---
```
#### Each server
```ini
# /etc/crowdsec/local_api_credentials.yaml
url: http://192.168.10.1:8080
login: [server_name]
password: (API key)
# /etc/crowdsec/acquis.d/sshd.yaml
---
source: journalctl
journalctl_filter:
- "_SYSTEMD_UNIT=ssh.service" labels:
type: sshd
# origin value is syslog
---
```
```bash
sudo systemctl restart crowdsec
```
#### OPNsense web UI
- Services:CrowdSec:Machines
- checking the lists of server
- Main CLI commands of CrowdSec
```sh
# View active decisions(Ban list)
cscli decisions list
# View alerts
cscli alerts list
# Check connected machines(agents)
cscli machines list
```
---
## TLS on crowdsec communication
TLS can be applied in CrowdSec communication when internal PKI are set (BIND, Step-CA, ACME-Client in OPNsense). CrowdSec communication can contain sensitive information such as API key, it is recommended to set TLS.
### General configuration
- Services:CrowdSec:Settings
- \[\*\] Manual LAPI configuration
### DNS setting
### Add new domain in BIND
Following [here](../06_network/06_03_net_bind.md).
- net server
- file: ~/data/containers/bind/lib/db.ilnmors.internal
```text
...
crowdsec IN CNAME opnsense.ilnmors.internal.
...
```
### ACME setting
#### Use opnsense certificates
- Services:ACME Client:Certificates - Certificates
- Alt name: crowdsec.ilnmors.internal
#### Certificate and key file
- opnsense
- file:
- /var/etc/acme-client/cert-home/\[ramdom_string\]/opnsense.ilnmors.internal/
- fullchain.cer
- opnsense.ilnmors.internal.key
> There is `opnsense.ilnmors.internal.cer` file. However, when client verify the certificate, it verify the intermediate CA's certificate and root CA's certificate both. Therefore, in this case, use fullchain.cer
- /usr/local/etc/ssl/cert.pem
### Add TLS setting in LAPI configuration
- opnsense
- file:
- /usr/local/etc/crowdsec/config.yaml
- /usr/local/etc/crowdsec/local_api_credentials.yaml
- /usr/local/etc/crowdsec/bouncer/crowdsec-firewall-bouncer.yaml
```yaml
# config.yaml
api:
client:
# ...client configurations
server:
enable: true
listen_uri: 192.168.10.1:8080 # actual IP address is required. (Do not use FQDN in here, the service listener is binded on network interface)
# ... server configurations
tls:
cert_file: /var/etc/acme-client/cert-home/[random_string]/opnsense.ilnmors.internal/fullchain.cer
key_file: /var/etc/acme-client/cert-home/[ramdom_string]/opnsense.ilnmors.internal/opnsense.ilnmors.internal.key
# random string is generated by opnsense itself. In real environment, check it first.
```
```yaml
# local_api_credentials.yaml
...
url: https://crowdsec.ilnmors.internal:8080/
```
```yaml
# crowdsec-firewall-bouncer.yaml
...
api_url: https://crowdsec.ilnmors.internal:8080/
...
```
```sh
service crowdsec restart
```
#### CrowdSec LAPI restart setting
- opnsense
- file: /usr/local/etc/cron.d/crowdsec
```text
#minute hour mday month wday who command
0 3 * * * root /usr/local/libexec/crowdsec/upgrade-hub
30 3 * * * root /usr/sbin/service crowdsec reload # Add this line to reload every day.
```
### Each server configuration
#### Server's certificate trust
- each server
- file: /usr/local/share/ca-certificates/root_ca.crt
```bash
sudo update-ca-certificates
```
#### CrowdSec Agent setting
```ini
# /etc/crowdsec/local_api_credentials.yaml
url: https://crowdsec.ilnmors.internal:8080
login: [server_name]
password: (API key)
```
```bash
sudo systemctl restart crowdsec
```
---
## Crowdsec in Caddy-auth
Caddy supports bouncer. Also, it can be work as agent via auth server where Caddy-auth is located.
### Bouncer configuration
> Caddy has to contain local CA's root_ca.crt (Step-CA). - containerfile already includes root_ca.crt in container when it was built
#### OPNsense console
```sh
cscli bouncer add caddy-auth
> API key for 'caddy-auth':
> Secret_value
> Please keep this key since you will not be able to retrieve it!
>
>
cscli collections install crowdsecurity/caddy
```
#### Caddyfile
```ini
# ...
# Crowdsec bouncer setting
{
crowdsec {
# CrowdSec LAPI
api_url https://crowdsec.ilnmors.internal:8080
api_key "{env.CADDY_CROWDSEC_KEY}"
}
}
# ...
```
- `podman exec caddy-auth caddy reload --config /etc/caddy/Caddyfile`
### Agent configuration
#### auth sv
- File:
- /etc/crowdsec/acquis.yaml
- ~/data/container/caddy_auth/data/access.log
```yaml
# /etc/crowdsec/acquis.d/caddy-auth.yaml
filenames:
- /var/log/caddy.log
labels:
type: caddy
```
```ini
# Caddyfile
# ...
(crowdsec_log) {
log {
output file /data/access.log {
roll_size 10mb
roll_keep 5
}
}
}
# ...
caddy.ilnmors.com {
import crowdsec_log
route {
crowdsec
root * /usr/share/caddy
file_server
}
}
```
```bash
podman exec caddy-auth caddy reload --config /etc/caddy/Caddyfile
sudo mkdir /etc/crowdsec/acquis.d
sudo nano /etc/crowdsec/acquis.d/caddy-auth.yaml
ln -s /home/auth/data/containers/caddy-auth/data/access.log /var/log/caddy.log
# install collection(senario + parser) crowdsecurity/caddy
sudo cscli collections install crowdsecurity/caddy
sudo systemctl restart crowdsec
sudo cscli metrics
```
---
## PLAN
- distributed bouncer
- [x] Caddy bouncer
- [ ] dash board

View File

@@ -0,0 +1,107 @@
Tags: #os, #configuration, #virtualization, #container, #database, #cache
## Redis
Redis is the cache database. It doesn't use SQL, it is just key-value data storage.
### Redis in Pod
Redis is combined with master services in the pod. It allows master services and Redis can communication in localhost. It doesn't need TLS, password, or even ACL itself.
There is the list of services which use Redis in Pod.
#### Preperation
##### Create directory for container
```bash
mkdir -p ~/data/containers/app/redis
setfacl -m d:g::0 ~/data/containers/app/redis
setfacl -m d:o::0 ~/data/containers/app/redis
setfacl -m u:
$UID:rwx ~/data/containers/app/redis
setfacl -m u:100998:rwx ~/data/containers/app/redis
setfacl -d -m u:$UID:rwx ~/data/containers/app/redis
setfacl -d -m u:100998:rwx ~/data/containers/app/redis
```
>Redis container executes as 999:999(redis:redis) permission in container. It is mapped host's 100998. Therefore, directories have to have ACL via `setfacl`
### Podman Image
```bash
podman pull redis:8.2.2 # Do not use latest version to management
```
### Quadlet
- File:
- ~/data/config/containers/app/app.pod
- ~/data/config/containers/app/app-redis.container
- ~/data/config/containers/app/app.container
```ini
# ~/data/config/containers/app/app.pod
[Quadlet]
DefaultDependencies=false
[Pod]
PodName=app
# web port
PublishPort=9080:9000/tcp
# LDAP port
#PublishPort=[set_port]:3389
# Prometheus Port
#PublishPort=[set_port]:9300
```
```ini
# ~/data/config/containers/app/app-redis.container
[Quadlet]
DefaultDependencies=false
[Unit]
Description=app - redis
Before=app.service
[Container]
Pod=app.pod
Image=redis:8.2.2
ContainerName=app-redis
# Port 6379
Volume=%h/data/containers/app/redis:/data:rw
Environment="TZ=Asia/Seoul"
[Install]
WantedBy=default.target
```
```ini
# ~/data/config/containers/app/app.container
[Quadlet]
DefaultDependencies=false
[Unit]
Description=app
After=app-redis.service
Requires=app-redis.service
[Container]
Pod=app.pod
Image=app:version
Volume=%h/data/containers/app/app:/data:rw
Environment="TZ=Asia/Seoul"
[Install]
WantedBy=default.target
```

View File

@@ -0,0 +1,130 @@
Tags: #common, #configuration, #os, #filesystem
## BTRFS usage
### Setting
```bash
# All hdd needs partition, but it has no filesystem. To make a partition use `fdisk`.
sudo fdisk "$DIVICE_PATH"
> n # create the new parition
> 1 # Partition number
> Default # First Sector
> Default # Last Sectort
> w # write the new partition
# check btrfs-progs package
sudo apt list --installed | grep btrfs-progs
# btrfs-progs/stable,now 6.14-1 amd64 [installed]
sudo mkfs.btrfs -d raid10 -m raid10 -L hdd /dev/sda1 /dev/sdb1 /dev/sdc1 /dev/sdd1
# These are partition device files
```
### Snapshot
Usually, the Read-Only snapshot is used.
```bash
# Create snapshot_subvolume
sudo btrfs subvolume create /home/app/hdd/data
# Create snapshot_directory
mkdir /home/app/hdd/.snapshot
# Create snapshot
sudo btrfs subvolume snapshot -r /home/app/hdd/data /home/app/hdd/.snapshot/data_[date]
# Rollback (file)
cp /home/app/hdd/.snapshot/data_[date]/file /home/app/hdd/data/
# Roleback (volume)
# Current subvolume move
mv /home/app/hdd/data /home/app/hdd/data_fail
sudo btrfs subvolume snapshot /home/app/hdd/.snapshot/data_[date] /home/app/hdd/data
# If the data successfully recovered
sudo btrfs subvolume delete /home/app/hdd/data_fail
```
### replace HDD to new system
btrfs has its own volume manangement data and filesystem in hdd, as metadata. It has no dependency on specific OS or hardware but it can work on every linux system which supports btrfs.
```bash
# unmount filesystem
sudo umount /home/app/hdd
# turn off the system, and remove all hdd
# add all disk to new hardware(server), and turn on
# scan btrfs
sudo btrfs device scan
sudo btrfs filesystem show
# mount
sudo nano /etc/fstab
# LABEL=hdd /home/app/hdd btrfs defaults,compress=zstd,autodefrag 0 0
sudo mount -a
```
### add extra HDD
```bash
# Add HDD and check the device file
lsblk
# Add hdd to btrfs RAID
sudo btrfs device add /dev/xxx /dev/xxy /home/app/hdd
# Expand the volume
sudo btrfs balance start /home/app/hdd
```
### change HDD
#### btrfs replace
When sata slot is enough to connect new HDD and old HDD, you can use this way.
```bash
# check devid of old HDD
sudo btrfs device stats /home/app/hdd # you can check IO error
sudo btrfs filesystem show /home/app/hdd
# check the new disk's path
lsblk
# /dev/xxx
# Replace
sudo btrfs replace start [old HDD\'s devid] /dev/xxx /home/app/hdd
# check
sudo btrfs replace status /home/app/hdd
```
#### btrfs device add and delete
When sata slot is not enough to connect new HDD and old HDD simultaneously, you can use this way.
```bash
# Check the HDD which will change
sudo btrfs device stats /home/app/hdd # you can check IO error
sudo btrfs filesystem show /home/app/hdd # check disk's devid
# turn off the system and change the broken HDD to the new HDD
# Make sure the hardware supports Hot-Swap, if it didn't support, you would have to turn off your system fully when you are changing the HDD
# if the system couldn't mount automatically, mount it manually
# sudo mount -o degraded /dev/xxx /home/app/hdd # /dev/xxx is one of ordinary HDD
lsblk
# /dev/xxy # new HDD device file path
# Add new HDD
sudo btrfs device add /dev/xxy /home/app/hdd
# Delete broken HDD
sudo btrfs device delete missing /home/app/hdd
# To major balance
sudo btrfs balance start /home/app/hdd
```