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

146
docs/services/infra/ca.md Normal file
View File

@@ -0,0 +1,146 @@
## Operation
Refer to Ansible playbook
## Configuration files
- ca.json
- defaults.json
### Provisioner
Provisioner is basically the object of issuing certificates as a RA. They verify CSR from client and when it is valid with its policy they will sign the certificates with CA's private key. Step-CA supports various type of provisioner. In this homelab, only ACME will be used. Because infrastructure's certificates is issued manually. Step-CA supports one root CA and one intermediate CA in one container, only one intermediate CA will be operated in this project.
#### jwk-ca@ilnmors.internal
This provisioner is to issue intermediate CA. It wouldn't be used in this project. The option for CA in X.509 format is optional and defined in as extension option. To define these option in step-ca, the template file is needed.
- file: ~/data/containers/step-ca/templates/ca.tpl
```json
{
"subject": {{ toJson .Subject }},
"keyUsage": ["certSign", "crlSign"],
"basicConstraints": {
"isCA": true,
"maxPathLen": 0
}
}
```
> keyUsage: Designate to manage certificates and CRL
> isCA: Designate the certificate to use CA
> maxPathLen: Designate allowed below CA's number
- Define provisioner
```bash
podman exec -it step-ca \
step ca provisioner add jwk-ca@ilnmors.internal \
--create \ # Generate key pair automatically
--type JWK \
--ca-config /home/step/config/ca.json \ # Sign on certificate with root CA's private key
--x509-template /home/step/template/ca.tpl \ # Use x509 template
--x509-max-dur 87600h \ #
--x509-default-dur 87600h
```
#### jwk@ilnmors.internal
This provisioner is to issue the certificates like DB communication based on its identity (Using JWK and JWT pre-shared). The certificate is issued based on enrolled key in provisioner. However, in this project all crt will be used central ACME client `Caddy`.
- Define provisioner
```bash
podman exec -it step-ca \
step ca provisioner add jwk@ilnmors.internal \
--create \ # Generate key pair automatically
--type JWK \
--x509-default-dur 2160h # To set default expire date as 90 days.
```
#### acme@ilnmors.internal
This provisioner is to issue the certificates for https communication. The certificate is issued based on challenge; the ownership of domain.
- Define provisioner
```bash
podman exec -it step-ca \
step ca provisioner add acme@ilnmors.internal \
--type ACME \
--x509-default-dur 2160h # To set default expire date as 90 days.
```
### Subject
Step-CA uses subject as a account. It is used to manage Step-CA remotely. To use this, it is necessary to use `--remote-management` option when the step-CA is initially set or fix `ca.json` authority.enableAdmin:true. When subject is enabled, provisioners aren't defined in ca.json but its own DB.
### Policy
Self-hosted Step-CA server doesn't support to give x509 policy for each provisioner. It only allows public policy. Only `ilnmors.internal` and `*.ilnmors.internal` certificates are required, so designate the policy in `ca.json`
> Policies can be administered using the step CLI application. The commands are part of the step ca policy namespace. In a self-hosted step-ca, policies can be configured on the authority level. Source: [here](https://smallstep.com/docs/step-ca/policies/)
- file: ~/data/containers/step-ca/config/ca.json
```json
...
"authority": {
"policy": {
"x509": {
"allow": {
"dns": [
"ilnmors.internal",
"*.ilnmors.internal"
]
},
"allowWildcardNames": true
}
},
"provisioners": [ ... ]
....
}
...
```
## Verify server
### Server health check
```bash
curl -k https://ca.ilnmors.internal:9000/health
> {"status":"ok"}
```
### Server policy check
```bash
podman exec -it ca step ca certificate test.com test.crt test_key --provisioner acme@ilnmors.internal
> error creating new ACME order: The server will not issue certificates for the identifier
```
---
## Set trust Root CRT
### Linux
#### Debian/ubuntu
- File: /usr/local/share/ca-certificates/{ca.crt, ca.pem}
- `update-ca-certificates`
#### Cent/RHEL/Fedora
- File: /etc/pki/ca-trust/source/anchors/{ca.crt, ca.pem}
- `update-ca-trust`
### Windows
- `Windows + R` + `certlm.msc`
- `All Task` - `Import`
### Firefox
- Setting - Security - view certificates - Authority - add
- \[x\] trust this ca to identify website
- \[x\] trust this ca to identify email users

View File

@@ -0,0 +1,20 @@
# Grafana
## Operation
Refer to Ansible playbook
\(Postgresql user and DB is needed\)
\(LDAP strict readonly account is needed\)
## Verification
- Check Caddyfile \(without caddy, use 3000 ports\)
- https://grafana.ilnmors.internal
- login with LDAP user
- connection:data sources: \[prometheus|loki\]: provisioned
- https://prometheus.ilnmors.internal:9090
- https://loki.ilnmors.internal:3100
- check drill down:metrics
## Dashboard
- Dashboard isn't saved on local directory. They are saved on DB \(Postgresql\).

154
docs/services/infra/ldap.md Normal file
View File

@@ -0,0 +1,154 @@
## Operation
Refer to Ansible playbook
\(Postgresql user and DB is needed\)
Integrate configuration with various app: https://github.com/lldap/lldap/blob/main/example_configs
## Configuration
### DB URL
Jinja2 `urlencode` module doesn't replace `/` as `%2F`. replace('/', '%2F') is necessary.
ex\) {{ var | urlencode | replace('/', '%2F') }}
### Reset administrator password
```bash
# infra
sudo nano $LDAP_PATH/data/lldap_config.toml
# Add below on file
ldap_user_pass = "REPLACE_WITH_PASSWORD"
force_ldap_user_pass_reset = true
# Restart lldap
systemctl --user restart ldap.service
# Delete added lines from lldap_config.toml
# ldap_user_pass = "REPLACE_WITH_PASSWORD"
# *YOU MUST DELETE PASSWORD PART*
# force_ldap_user_pass_reset = true
```
### Access web UI and Login
- URL: http://ldap.ilnmors.internal:17170 \(This is temporary access way before Caddy, which is reverse proxy, is set)
- ID: admin
- PW: $LLDAP_LDAP_USER_PASSWORD
### Create the groups
- Groups - \[\+\] Create a group
- Group: admins
- Group: users
It is necessary to manage ACL via authelia based on groups.
### Create the authelia user for OCID \(OP\)
- Users: \[\+\] Create a user
- Username (cn; uid): authelia
- Display name: Authelia
- First Name: Authelia
- Last Name (sn): Service
- Email (mail): authelia@ilnmors.internal
- Password: "$(openssl rand -base64 32)"
- Groups:lldap_strict_readonly: \[Add to group\]
- This group allow search authority.
- Users: \[\+\] Create a user
- Username (cn; uid): grafana
- Display name: Grafana
- First Name: Grafana
- Last Name (sn): Service
- Email (mail): grafana@ilnmors.internal
- Password: "$(openssl rand -base64 32)"
- Groups:lldap_strict_readonly: \[Add to group\]
- This group allow search authority.
> Save the password in .secret.yaml
### Create the normal users
- Users: \[\+\] Create a user
- Username (cn; uid): il
- First Name: Il
- Last Name (sn): Lee
- Email (mail): il@ilnmors.internal
- Password: "$PASSWORD"
- Groups:lldap_admin&admins&users: \[Add to group\]
- Users: \[\+\] Create a user
- Username (cn; uid): user
- First Name: John
- Last Name (sn): Doe
- Email (mail): john_doe@ilnmors.internal
- Password: "$PASSWORD"
- Groups:(admins|users): \[Add to group\]
> Custom schema in `User schema`, `Group schema` doesn't need to be added. This is for advanced function to add additional value such as `identity number` or `phone number`. Hardcoded schema, which means basic schema the lldap provides is enough to use Authelia.
> After all these steps, now you can integrate the Authelia for SSO.
## Usage of LDAP
### Service Bind
LDAP call `login` as Bind. When the authelia Bind to the LDAP server, it can get the authority to search in `lldap_strict_readonly` group.
### Search
authelia account has the authority to search, it can search to send the query.
#### Flow of search
- Client (authelia) sends the query
- `uid=user in dc=ilnmors,dc=internal`
- LDAP server searches the DN of entry
- `uid=user,ou=people,dc=ilnmors,dc=internal`
- LDAP sends the DN to Client (authelia)
## Authelia's work flow
### First login
#### User login query
User try to login on login page of Authelia.
- id: user
- password: 1234
#### Service Bind (Bind and search)
authelia binds to LLDAP server based on the information in configuration.yml.
- dn: authelia
- password: authelia's password
#### Search
authelia sends the query to LLDAP after bind.
- `uid=user in dc=ilnmors,dc=internal`
#### Request
LLDAP server searches the entry and send the DN information query to authelia.
- `uid=user,ou=people,dc=ilnmors,dc=internal`
### Verify the user login (Second login)
#### User Bind (Bind only)
authelia tries to bind LLDAP server based on the information that user input.
- dn: requested uid
- password: 1234
#### Verification from LLDAP
LLDAP verify the password from authelia with its hash value saved in LLDAP's database.
#### Request
LLDAP server sends the result as `Success` or `Fail`.
> Search authority is basic authority of user who binds to LDAP server. It is just the way to check success or fail bind is the charge of Authelia.
## verify
- openssl s_client -connect ldap.ilnmors.internal:636 -tls1_3

View File

@@ -0,0 +1,12 @@
# loki
## Operation
Refer to Ansible playbook
## Verification
- fw@fw:/var/lib/bind$ curl -k https://loki.ilnmors.internal:3100/ready \(Node which is in NET_SERVER except infra itself\)
- ready
- fw@fw:/var/lib/bind$ curl -k https://loki.ilnmors.internal:3100/metrics
- metrics lists
- fw@fw:/var/lib/bind$ curl -k https://loki.ilnmors.internal:3100/loki/api/v1/labels
- no org id
- JSON format labels when alloy is set

View File

@@ -0,0 +1,64 @@
# Postgresql
## Operation
Refer to Ansible playbook
## File management
```bash
# console
## cluster
scp infra@infra:$POSTGRESQL_BACKUP_PATH/pg_cluster.sql $HOMELAB_PATH/data/backups/infra/postgresql/pg_cluster.sql
## data
scp infra@infra:$POSTGRESQL_BACKUP_PATH/pg_backup.sql $HOMELAB_PATH/data/backups/infra/postgresql/pg_backup.sql
## The data is managed by kopia.
```
## Verification
```bash
# ... Start postgresql service
# Create user and database
podman exec -it -u postgres postgresql "psql -U postgres"
> CREATE USER service WITH PASSWORD 'abc';
> CREATE DATABASE service_db;
> ALTER DATABASE service_db OWNER TO service;
> \du
> \l
> \q
# Reset database
> SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'service_db'; # connection reset
> DROP DATABASE service_db;
> CREATE DATABASE service_db;
> ALTER DATABASE service_db OWNER TO service;
> \du
> \l
> \q
# Restor database (manually)
podman exec -u postgres postgresql "psql -U postgres -f $POSTGRESQL_BACKUP_PATH_IN_CONTAINER/script.sql"
# Backup service executes
systemctl --user start postgresql-cluster-backup.service
# Stop and remove all data
systemctl --stop postgresql
sudo find "/home/infra/data/containers/postgresql/data" -mindepth 1 -delete
# Restore database
# Just locate sql files on data_path, and use playbooks
# Check restoring
podman exec -it -u postgres postgresql psql -U postgres
> \du
> \l
# Check extension
postgres=# SHOW shared_preload_libraries;
shared_preload_libraries
--------------------------
vchord.so
(1 row)
```

View File

@@ -0,0 +1,12 @@
# Prometheus
## Operation
Refer to Ansible playbook
## Verification
- Check Caddyfile \(without caddy, use 9090 ports\)
- https://prometheus.ilnmors.internal
- Status:Target Health
- Check `Endpoint localhost:9090 ` with green circle
- Status:command-line flag
- Check `--web.enable-remote-write-receiver: true`