Compare commits
103 Commits
de8b827cfb
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
d4571c9b70
|
|||
|
4c0a263d50
|
|||
|
70259d9542
|
|||
|
4f3102a2ff
|
|||
|
ed65f8924d
|
|||
|
737a58a13c
|
|||
|
a2cef18efe
|
|||
|
1c245a593a
|
|||
|
b65ef9cbb7
|
|||
|
ea3e8f9c10
|
|||
|
b5aecf1565
|
|||
|
380d8f8e48
|
|||
|
07a297f818
|
|||
|
d7224b038b
|
|||
|
fc62219db7
|
|||
|
2ebd97c345
|
|||
|
270e86bfd0
|
|||
|
7305e3a35b
|
|||
|
aabbd8286f
|
|||
|
37f7d442a1
|
|||
|
3ff805fa39
|
|||
|
1ae62e70ed
|
|||
|
91f4687c07
|
|||
|
dc2df62d04
|
|||
|
b75aac76c2
|
|||
|
5161dced6e
|
|||
|
d9ed144578
|
|||
|
5516f9530b
|
|||
|
621be95870
|
|||
|
b526901546
|
|||
|
b328081b59
|
|||
|
113b859927
|
|||
|
57ff005186
|
|||
|
7ccedb9768
|
|||
|
ef527abef4
|
|||
|
75f4aaebf1
|
|||
|
1396e09227
|
|||
|
cbe8c4a369
|
|||
|
2f88c75655
|
|||
|
0f4b73720c
|
|||
|
b97f41eb70
|
|||
|
6df02e8dff
|
|||
|
57ae6b7e72
|
|||
|
e3ba1759c4
|
|||
|
af70d1d396
|
|||
|
5b474c7190
|
|||
|
d94cd01008
|
|||
|
afb27c512c
|
|||
|
a500c8a572
|
|||
|
c5748d81da
|
|||
|
b38390029f
|
|||
|
b116ea73ec
|
|||
|
920aeef7f3
|
|||
|
9038962f29
|
|||
|
3fed164193
|
|||
|
487e03c0bd
|
|||
|
cf0a7373d4
|
|||
|
e0adee5362
|
|||
|
8f3e624925
|
|||
| e1e551c5cc | |||
| 23d3949421 | |||
| 714dd32ff6 | |||
| 8035fa38dc | |||
| b91cc1adc3 | |||
| 4fe56de990 | |||
| 9ef631b266 | |||
| 8c39f749c7 | |||
| 1361c726d9 | |||
| 1879158b6c | |||
| 7b9968762a | |||
| 250ffeb266 | |||
| de6c1941c5 | |||
| 9bc09a4b98 | |||
| 79377b3653 | |||
| d44bca3f2b | |||
| 660735f0ae | |||
| 6dfd30e175 | |||
| 0e5250d84d | |||
| 556149c583 | |||
| 72e13f53aa | |||
| e9c68abeb9 | |||
| 69e8e89e72 | |||
| 85e74541c2 | |||
| cb66fb6195 | |||
| 8d98cd06fa | |||
| a85627b3b2 | |||
| f046e6edc2 | |||
| a32f055ede | |||
| 0c6509cc17 | |||
| 82b60c086c | |||
| 999869cab6 | |||
| 548cdc8b87 | |||
| 4832b283bb | |||
| 9e83048248 | |||
| f2d684fa7c | |||
| 7980bfb381 | |||
| 20690c48e5 | |||
| ca582333f1 | |||
| dae4063f25 | |||
| 5184c84d50 | |||
| 3f3a03ee05 | |||
| 22c1d635c6 | |||
| 5512c266eb |
30
.gitea/workflows/caddy.yaml
Normal file
30
.gitea/workflows/caddy.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: Podman DDNS Image
|
||||||
|
run-name: Build and Push the Custom Caddy Image with Route53 DNS Certbot
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- active/podman_caddy/**
|
||||||
|
- .gitea/workflows/caddy.yaml
|
||||||
|
schedule:
|
||||||
|
- cron: '@daily'
|
||||||
|
jobs:
|
||||||
|
build-and-push-ddns:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: gitea.ref == 'refs/heads/main'
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Login to Gitea Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: gitea.reeseapps.com
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: https://github.com/docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ${{ gitea.workspace }}/active/podman_caddy
|
||||||
|
file: ${{ gitea.workspace }}/active/podman_caddy/Containerfile
|
||||||
|
push: true
|
||||||
|
tags: "gitea.reeseapps.com/services/caddy:latest,gitea.reeseapps.com/services/caddy:${{gitea.sha}}"
|
||||||
|
no-cache: true
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,3 +7,7 @@ __pycache__/
|
|||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
.venv/
|
.venv/
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
|
TODO.md
|
||||||
|
eicar.com
|
||||||
|
*.pp
|
||||||
|
*.mod
|
||||||
19
.vscode/code_oss_extensions.txt
vendored
19
.vscode/code_oss_extensions.txt
vendored
@@ -1,14 +1,17 @@
|
|||||||
charliermarsh.ruff
|
charliermarsh.ruff
|
||||||
continue.continue
|
eamodio.gitlens
|
||||||
davidanson.vscode-markdownlint
|
franneck94.vscode-python-config
|
||||||
ms-python.black-formatter
|
franneck94.vscode-python-dev-extension-pack
|
||||||
|
ms-pyright.pyright
|
||||||
ms-python.debugpy
|
ms-python.debugpy
|
||||||
|
ms-python.mypy-type-checker
|
||||||
ms-python.python
|
ms-python.python
|
||||||
ms-toolsai.jupyter
|
ms-python.vscode-python-envs
|
||||||
ms-toolsai.jupyter-keymap
|
njpwerner.autodocstring
|
||||||
ms-toolsai.jupyter-renderers
|
njqdev.vscode-python-typehint
|
||||||
ms-toolsai.vscode-jupyter-cell-tags
|
redhat.vscode-yaml
|
||||||
ms-toolsai.vscode-jupyter-slideshow
|
|
||||||
stkb.rewrap
|
stkb.rewrap
|
||||||
streetsidesoftware.code-spell-checker
|
streetsidesoftware.code-spell-checker
|
||||||
|
tamasfe.even-better-toml
|
||||||
|
vue.volar
|
||||||
yzhang.markdown-all-in-one
|
yzhang.markdown-all-in-one
|
||||||
207
README.md
207
README.md
@@ -1,19 +1,47 @@
|
|||||||
# Homelab
|
# Homelab
|
||||||
|
|
||||||
A project to store homelab stuff.
|
Welcome to my homelab!
|
||||||
|
|
||||||
Just here for the Arch distoolbox?
|
This repo is an in-flux collection of my personal notes, docs, and tutorials of
|
||||||
|
things I find interesting and self-host.
|
||||||
|
|
||||||
[Arch Distoolbox](active/software_distoolbox/distoolbox.md)
|
Take a look around!
|
||||||
|
|
||||||

|
categories:
|
||||||
|
- `aws_` is for aws notes
|
||||||
|
- `device_` is for hardware
|
||||||
|
- `kubernetes_` is for helm charts or other kubernetes hosted software
|
||||||
|
- `os_` is for operating system setup guides and notes
|
||||||
|
- `podman_` is for containerized projects
|
||||||
|
- `software_` is for cli tools, projects without a specific way to host them,
|
||||||
|
or other misfits
|
||||||
|
|
||||||
|
All active projects will have a markdown file named after the project. This is
|
||||||
|
for quick access via shortcuts like `ctrl + p` in vscode. For example, I want
|
||||||
|
to check my notes for `virsh` so I would type `ctrl + p` "virsh" to open
|
||||||
|
"virsh.md".
|
||||||
|
|
||||||
|
"Retired" projects (/retired) is a graveyard of things I didn't want to delete.
|
||||||
|
|
||||||
|
"Template" projects (/templates) are quick templates for creating new active
|
||||||
|
projects with sane defaults.
|
||||||
|
|
||||||
|
I keep my GPG and SSH keys in `keys` if you want to add those to your keyring
|
||||||
|
or give me access to your servers.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Homelab](#homelab)
|
- [Homelab](#homelab)
|
||||||
- [Table of Contents](#table-of-contents)
|
- [Table of Contents](#table-of-contents)
|
||||||
- [Fun Facts](#fun-facts)
|
- [Fun Facts](#fun-facts)
|
||||||
|
- [Keyboard Shortcuts](#keyboard-shortcuts)
|
||||||
|
- [inputrc](#inputrc)
|
||||||
|
- ["find ." shortcuts](#find--shortcuts)
|
||||||
|
- [tmux](#tmux)
|
||||||
|
- [bash](#bash)
|
||||||
|
- [SSH Setup](#ssh-setup)
|
||||||
|
- [Git GPG Commit Signing](#git-gpg-commit-signing)
|
||||||
- [Important Dates and Times](#important-dates-and-times)
|
- [Important Dates and Times](#important-dates-and-times)
|
||||||
- [Project Lifecycle](#project-lifecycle)
|
- [Project Lifecycle](#project-lifecycle)
|
||||||
- [Project Types](#project-types)
|
- [Project Types](#project-types)
|
||||||
@@ -25,33 +53,160 @@ Status](https://gitea.reeseapps.com/services/homelab/actions/workflows/distoolbo
|
|||||||
|
|
||||||
## Fun Facts
|
## Fun Facts
|
||||||
|
|
||||||
|
### Keyboard Shortcuts
|
||||||
|
|
||||||
On linux, <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>u</kbd>, then, while holding
|
On linux, <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>u</kbd>, then, while holding
|
||||||
<kbd>ctrl</kbd>+<kbd>shift</kbd>, typing <kbd>b</kbd>+<kbd>0</kbd> will type a
|
<kbd>ctrl</kbd>+<kbd>shift</kbd>, typing <kbd>b</kbd>+<kbd>0</kbd> will type a
|
||||||
° (degree) symbol. Also you can enter any unicode symbol this way.
|
° (degree) symbol. Also you can enter any unicode symbol this way.
|
||||||
|
|
||||||
To generate an SSH key with the correct comment and type run:
|
In vim: `esc + o` will take you to the end of a file and insert a new line.
|
||||||
|
|
||||||
|
### inputrc
|
||||||
|
|
||||||
|
Add this to your `~/.inputrc` to allow ctrl + backspace to delete whole words.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export KEYGEN_USER=myuser
|
"\C-h": backward-kill-word
|
||||||
export KEYGEN_HOST=something.com
|
|
||||||
ssh-keygen -C ${KEYGEN_USER}@${KEYGEN_HOST} -f ~/.ssh/id_${KEYGEN_HOST} -t ed25519
|
|
||||||
|
|
||||||
cat <<EOF >> ~/.ssh/config
|
|
||||||
Host ${KEYGEN_HOST}
|
|
||||||
Hostname ${KEYGEN_HOST}
|
|
||||||
IdentityFile ~/.ssh/id_${KEYGEN_HOST}
|
|
||||||
User ${KEYGEN_USER}
|
|
||||||
Port 22
|
|
||||||
EOF
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In vim: `esc + o` will take you to the end of a file and insert a new line.
|
### "find ." shortcuts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Change file mode for a bunch of directories
|
||||||
|
find . -type d -exec chmod 755 {} \;
|
||||||
|
```
|
||||||
|
|
||||||
|
### tmux
|
||||||
|
|
||||||
|
- Vertical: ctrl + b + "
|
||||||
|
- Horizontal: ctrl + b + %
|
||||||
|
- Event Horizontal Distribution: ctrl + b + alt + 1
|
||||||
|
- Even Vertical Distribution: ctrl + b + alt + 2
|
||||||
|
- Swap pane order: ctrl + b + : -> swap-pane -t 0
|
||||||
|
|
||||||
|
### bash
|
||||||
|
|
||||||
|
<https://tecadmin.net/bash-special-variables/>
|
||||||
|
|
||||||
|
Here are some handy references for default bash variables
|
||||||
|
|
||||||
|
```text
|
||||||
|
$0 – The name of the script being executed.
|
||||||
|
$1-$9 – The first nine command-line arguments.
|
||||||
|
$# – The number of command-line arguments.
|
||||||
|
$* – All command-line arguments as a single string.
|
||||||
|
$@ – All command-line arguments as an array.
|
||||||
|
$? – The exit status of the last executed command.
|
||||||
|
$$ – The process ID of the current shell.
|
||||||
|
$! – The process ID of the last background command.
|
||||||
|
$- – Shows the current shell options or flags.
|
||||||
|
```
|
||||||
|
|
||||||
|
And here are the meanings of the shell options
|
||||||
|
|
||||||
|
```text
|
||||||
|
h – Remember the location of commands as they are looked up
|
||||||
|
i – Interactive shell
|
||||||
|
m – Job control is enabled
|
||||||
|
B – Brace expansion is enabled
|
||||||
|
H – History substitution is enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
So to check if you are in an interactive shell:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
[ $- == *i* ]] && Some command here
|
||||||
|
```
|
||||||
|
|
||||||
|
## SSH Setup
|
||||||
|
|
||||||
|
Generate a key (password protect it!)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pick one of the below key types
|
||||||
|
# ed25519
|
||||||
|
ssh-keygen -C ssh@ducoterra.net -t ed25519
|
||||||
|
# rsa 4096
|
||||||
|
ssh-keygen -C ssh@ducoterra.net -t rsa -b 4096
|
||||||
|
|
||||||
|
# Inspect a key
|
||||||
|
ssh-keygen -l -f ~/.ssh/id_rsa
|
||||||
|
|
||||||
|
# Change the password
|
||||||
|
ssh-keygen -p -f ~/.ssh/id_rsa
|
||||||
|
```
|
||||||
|
|
||||||
|
In your ~/.ssh/config, add the following line to set the default key
|
||||||
|
|
||||||
|
```conf
|
||||||
|
IdentityFile ~/.foo/identity
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add a host to your local computer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Host <hostname>
|
||||||
|
Hostname <host.something.com or IP address>
|
||||||
|
User <remote user>
|
||||||
|
Port <remote port>
|
||||||
|
```
|
||||||
|
|
||||||
|
And copy the key to a remote computer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy the generated key to the server using password auth. Assumes password auth enabled.
|
||||||
|
ssh-copy-id -f -i ~/.ssh/id_ed25519 ${REMOTE_USER}@${REMOTE_HOST}
|
||||||
|
|
||||||
|
# Log into the server with your key
|
||||||
|
ssh -i ${KEY_NAME} ${REMOTE_HOST}
|
||||||
|
# Copy authorized_keys to root
|
||||||
|
sudo mkdir -p /root/.ssh
|
||||||
|
sudo cp ~/.ssh/authorized_keys /root/.ssh/authorized_keys
|
||||||
|
exit
|
||||||
|
|
||||||
|
# login and disable password auth
|
||||||
|
ssh ${REMOTE_HOST}
|
||||||
|
mkdir -p /etc/ssh/sshd_config.d
|
||||||
|
echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/01-prohibit-password.conf
|
||||||
|
systemctl restart sshd
|
||||||
|
|
||||||
|
# OPTIONAL: Disable sudo password
|
||||||
|
echo '%wheel ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/01-nopasswd-wheel
|
||||||
|
|
||||||
|
exit
|
||||||
|
|
||||||
|
# Test if you can SSH with a password
|
||||||
|
ssh -o PubkeyAuthentication=no ducoterra@${SSH_HOST}.reeselink.com
|
||||||
|
|
||||||
|
# Test that you can log into the server with ssh config
|
||||||
|
ssh $SSH_HOST
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git GPG Commit Signing
|
||||||
|
|
||||||
|
1. Use `gpg --list-key 'git@ducoterra.net'` to find your key
|
||||||
|
2. Use `git config --global user.signingkey 0A46826A...` to set the signing key
|
||||||
|
3. Use `gpg --export -a 'git@ducoterra.net'` to export the key to copy into Gitea/Github/Gitlab
|
||||||
|
|
||||||
|
Now you can sign commits with `git commit -S`.
|
||||||
|
|
||||||
|
Alternatively, you can sign every commit by default with `git config --global commit.gpgsign true`.
|
||||||
|
|
||||||
|
You can verify a commit with `git verify-commit e1e551c`. If the commit is
|
||||||
|
signed you'll see an output. If not, nothing will show.
|
||||||
|
|
||||||
## Important Dates and Times
|
## Important Dates and Times
|
||||||
|
|
||||||
- Machine updates happen at 4am on on Saturday
|
| Time | Day | Description |
|
||||||
- VM updates happen at 5am on Saturday
|
| ----- | -------- | ---------------------------------- |
|
||||||
- Backups happen at 6am every day
|
| 00:00 | All | Automated builds |
|
||||||
|
| 00:00 | All | NAS Snapshots |
|
||||||
|
| 02:00 | All | Backups |
|
||||||
|
| 04:00 | All | Bare Metal Server Security Updates |
|
||||||
|
| 05:00 | All | VM Server Security Updates |
|
||||||
|
| 05:00 | All | Unifi Protect Firmware Updates |
|
||||||
|
| 06:00 | All | Unifi Network Firmware Updates |
|
||||||
|
| 06:00 | Saturday | Truenas Disk Scrub |
|
||||||
|
|
||||||
## Project Lifecycle
|
## Project Lifecycle
|
||||||
|
|
||||||
@@ -77,8 +232,7 @@ All projects will be prefixed with one of the following categories:
|
|||||||
|
|
||||||
- `device_`
|
- `device_`
|
||||||
- `os_`
|
- `os_`
|
||||||
- `cloud_`
|
- `software_`
|
||||||
- `systemd_`
|
|
||||||
- `podman_`
|
- `podman_`
|
||||||
- `docker_`
|
- `docker_`
|
||||||
- `kubernetes_`
|
- `kubernetes_`
|
||||||
@@ -103,15 +257,12 @@ be prefixed with the cloud's name, not the word "cloud". So AWS services will
|
|||||||
be prefixed with `aws_` and azure would be `azure_`. This should make them more
|
be prefixed with `aws_` and azure would be `azure_`. This should make them more
|
||||||
searchable.
|
searchable.
|
||||||
|
|
||||||
`systemd_` projects are designed to be installed with ansible and run via
|
`software_` projects record configuration for common software agnostic to
|
||||||
systemd on a linux VM or other linux hardware.
|
operating system or linux flavor.
|
||||||
|
|
||||||
`podman_` projects are either designed to be run as quadlets or as podman
|
`podman_` projects are either designed to be run as quadlets or as podman
|
||||||
containers outright.
|
containers outright.
|
||||||
|
|
||||||
`docker_` projects are either docker-compose or some form of docker run
|
|
||||||
command.
|
|
||||||
|
|
||||||
`kubernetes_` projects are helm, kustomize, kubectl, or some other kubernetes
|
`kubernetes_` projects are helm, kustomize, kubectl, or some other kubernetes
|
||||||
compliant deployment.
|
compliant deployment.
|
||||||
|
|
||||||
|
|||||||
14
active/device_3dserver/3dserver.md
Normal file
14
active/device_3dserver/3dserver.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# 3D Server Hardware
|
||||||
|
|
||||||
|
## Motherboard
|
||||||
|
|
||||||
|
B650 GAMING X AX rev 1.5
|
||||||
|
|
||||||
|
<https://www.gigabyte.com/Motherboard/B650-GAMING-X-AX-rev-15/support#dl>
|
||||||
|
|
||||||
|
- Enable PBO
|
||||||
|
- Enable XMP
|
||||||
|
- Enable SVM
|
||||||
|
- Enable PCIe x4x4x4x4 bifurcation
|
||||||
|
- Enable Power always back on
|
||||||
|
- Fans to full speed
|
||||||
11
active/device_epson_et_2800/epson_et_2800.md
Normal file
11
active/device_epson_et_2800/epson_et_2800.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Epson ET 2800
|
||||||
|
|
||||||
|
## Printer Setup
|
||||||
|
|
||||||
|
1. Download and install the drivers at <https://support.epson.net/linux/Printer/LSB_distribution_pages/en/escpr.php>
|
||||||
|
2. Settings -> Printers -> Add
|
||||||
|
3. Select LPD/LPR Host or Printer
|
||||||
|
4. Enter the address: `lpd://<ip_address>`
|
||||||
|
5. Select Epson, then select Epson ET-2800 Series
|
||||||
|
6. Save
|
||||||
|
7. Print
|
||||||
5
active/device_esphome/.gitignore
vendored
Normal file
5
active/device_esphome/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Gitignore settings for ESPHome
|
||||||
|
# This is an example and may include too much for your use-case.
|
||||||
|
# You can modify this file to suit your needs.
|
||||||
|
/.esphome/
|
||||||
|
/secrets.yaml
|
||||||
370
active/device_esphome/default-atom-echo.yaml
Normal file
370
active/device_esphome/default-atom-echo.yaml
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
substitutions:
|
||||||
|
name: m5stack-atom-echo
|
||||||
|
friendly_name: M5Stack Atom Echo
|
||||||
|
|
||||||
|
esphome:
|
||||||
|
name: ${name}
|
||||||
|
name_add_mac_suffix: true
|
||||||
|
friendly_name: ${friendly_name}
|
||||||
|
min_version: 2025.5.0
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
board: m5stack-atom
|
||||||
|
cpu_frequency: 240MHz
|
||||||
|
framework:
|
||||||
|
type: esp-idf
|
||||||
|
|
||||||
|
logger:
|
||||||
|
api:
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
id: ota_esphome
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ap:
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: factory_reset
|
||||||
|
id: factory_reset_btn
|
||||||
|
name: Factory reset
|
||||||
|
|
||||||
|
i2s_audio:
|
||||||
|
- id: i2s_audio_bus
|
||||||
|
i2s_lrclk_pin: GPIO33
|
||||||
|
i2s_bclk_pin: GPIO19
|
||||||
|
|
||||||
|
microphone:
|
||||||
|
- platform: i2s_audio
|
||||||
|
id: echo_microphone
|
||||||
|
i2s_din_pin: GPIO23
|
||||||
|
adc_type: external
|
||||||
|
pdm: true
|
||||||
|
sample_rate: 16000
|
||||||
|
correct_dc_offset: true
|
||||||
|
|
||||||
|
speaker:
|
||||||
|
- platform: i2s_audio
|
||||||
|
id: echo_speaker
|
||||||
|
i2s_dout_pin: GPIO22
|
||||||
|
dac_type: external
|
||||||
|
bits_per_sample: 16bit
|
||||||
|
sample_rate: 16000
|
||||||
|
channel: stereo # The Echo has poor playback audio quality when using mon audio
|
||||||
|
buffer_duration: 60ms
|
||||||
|
|
||||||
|
media_player:
|
||||||
|
- platform: speaker
|
||||||
|
name: None
|
||||||
|
id: echo_media_player
|
||||||
|
announcement_pipeline:
|
||||||
|
speaker: echo_speaker
|
||||||
|
format: WAV
|
||||||
|
codec_support_enabled: false
|
||||||
|
buffer_size: 6000
|
||||||
|
volume_min: 0.4
|
||||||
|
files:
|
||||||
|
- id: timer_finished_wave_file
|
||||||
|
file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav
|
||||||
|
on_announcement:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- microphone.is_capturing:
|
||||||
|
then:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
on_idle:
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
- script.execute: reset_led
|
||||||
|
|
||||||
|
voice_assistant:
|
||||||
|
id: va
|
||||||
|
micro_wake_word:
|
||||||
|
microphone:
|
||||||
|
microphone: echo_microphone
|
||||||
|
channels: 0
|
||||||
|
gain_factor: 4
|
||||||
|
media_player: echo_media_player
|
||||||
|
noise_suppression_level: 2
|
||||||
|
auto_gain: 31dBFS
|
||||||
|
on_listening:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
effect: "Slow Pulse"
|
||||||
|
on_stt_vad_end:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
effect: "Fast Pulse"
|
||||||
|
on_tts_start:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
on_end:
|
||||||
|
# Handle the "nevermind" case where there is no announcement
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
- media_player.is_announcing:
|
||||||
|
timeout: 0.5s
|
||||||
|
# Restart only mWW if enabled; streaming wake words automatically restart
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- wait_until:
|
||||||
|
- and:
|
||||||
|
- not:
|
||||||
|
voice_assistant.is_running:
|
||||||
|
- not:
|
||||||
|
speaker.is_playing:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- micro_wake_word.start:
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_error:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 100%
|
||||||
|
green: 0%
|
||||||
|
blue: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
- delay: 2s
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_client_connected:
|
||||||
|
- delay: 2s # Give the api server time to settle
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
on_client_disconnected:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
on_timer_finished:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
- wait_until:
|
||||||
|
not:
|
||||||
|
microphone.is_capturing:
|
||||||
|
- switch.turn_on: timer_ringing
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 0%
|
||||||
|
green: 100%
|
||||||
|
blue: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: "Fast Pulse"
|
||||||
|
- wait_until:
|
||||||
|
- switch.is_off: timer_ringing
|
||||||
|
- light.turn_off: led
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
# button does the following:
|
||||||
|
# short click - stop a timer
|
||||||
|
# if no timer then restart either microwakeword or voice assistant continuous
|
||||||
|
- platform: gpio
|
||||||
|
pin:
|
||||||
|
number: GPIO39
|
||||||
|
inverted: true
|
||||||
|
name: Button
|
||||||
|
disabled_by_default: true
|
||||||
|
entity_category: diagnostic
|
||||||
|
id: echo_button
|
||||||
|
on_multi_click:
|
||||||
|
- timing:
|
||||||
|
- ON for at least 50ms
|
||||||
|
- OFF for at least 50ms
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
switch.is_on: timer_ringing
|
||||||
|
then:
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
else:
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
- timing:
|
||||||
|
- ON for at least 10s
|
||||||
|
then:
|
||||||
|
- button.press: factory_reset_btn
|
||||||
|
|
||||||
|
light:
|
||||||
|
- platform: esp32_rmt_led_strip
|
||||||
|
id: led
|
||||||
|
name: None
|
||||||
|
disabled_by_default: true
|
||||||
|
entity_category: config
|
||||||
|
pin: GPIO27
|
||||||
|
default_transition_length: 0s
|
||||||
|
chipset: SK6812
|
||||||
|
num_leds: 1
|
||||||
|
rgb_order: grb
|
||||||
|
effects:
|
||||||
|
- pulse:
|
||||||
|
name: "Slow Pulse"
|
||||||
|
transition_length: 250ms
|
||||||
|
update_interval: 250ms
|
||||||
|
min_brightness: 50%
|
||||||
|
max_brightness: 100%
|
||||||
|
- pulse:
|
||||||
|
name: "Fast Pulse"
|
||||||
|
transition_length: 100ms
|
||||||
|
update_interval: 100ms
|
||||||
|
min_brightness: 50%
|
||||||
|
max_brightness: 100%
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: reset_led
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
- switch.is_on: use_listen_light
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 100%
|
||||||
|
green: 89%
|
||||||
|
blue: 71%
|
||||||
|
brightness: 60%
|
||||||
|
effect: none
|
||||||
|
else:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state != "On device";
|
||||||
|
- switch.is_on: use_listen_light
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 0%
|
||||||
|
green: 100%
|
||||||
|
blue: 100%
|
||||||
|
brightness: 60%
|
||||||
|
effect: none
|
||||||
|
else:
|
||||||
|
- light.turn_off: led
|
||||||
|
- id: start_wake_word
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- not:
|
||||||
|
- voice_assistant.is_running:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- micro_wake_word.start:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- not:
|
||||||
|
- voice_assistant.is_running:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(true);
|
||||||
|
- voice_assistant.start_continuous:
|
||||||
|
- id: stop_wake_word
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- voice_assistant.stop:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- micro_wake_word.stop:
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: template
|
||||||
|
name: Use listen light
|
||||||
|
id: use_listen_light
|
||||||
|
optimistic: true
|
||||||
|
restore_mode: RESTORE_DEFAULT_ON
|
||||||
|
entity_category: config
|
||||||
|
on_turn_on:
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_turn_off:
|
||||||
|
- script.execute: reset_led
|
||||||
|
- platform: template
|
||||||
|
id: timer_ringing
|
||||||
|
optimistic: true
|
||||||
|
restore_mode: ALWAYS_OFF
|
||||||
|
on_turn_off:
|
||||||
|
# Turn off the repeat mode and disable the pause between playlist items
|
||||||
|
- lambda: |-
|
||||||
|
id(echo_media_player)
|
||||||
|
->make_call()
|
||||||
|
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
|
||||||
|
.set_announcement(true)
|
||||||
|
.perform();
|
||||||
|
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
|
||||||
|
# Stop playing the alarm
|
||||||
|
- media_player.stop:
|
||||||
|
announcement: true
|
||||||
|
on_turn_on:
|
||||||
|
# Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
|
||||||
|
- lambda: |-
|
||||||
|
id(echo_media_player)
|
||||||
|
->make_call()
|
||||||
|
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
|
||||||
|
.set_announcement(true)
|
||||||
|
.perform();
|
||||||
|
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
|
||||||
|
- media_player.speaker.play_on_device_media_file:
|
||||||
|
media_file: timer_finished_wave_file
|
||||||
|
announcement: true
|
||||||
|
- delay: 15min
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
|
||||||
|
select:
|
||||||
|
- platform: template
|
||||||
|
entity_category: config
|
||||||
|
name: Wake word engine location
|
||||||
|
id: wake_word_engine_location
|
||||||
|
optimistic: true
|
||||||
|
restore_value: true
|
||||||
|
options:
|
||||||
|
- In Home Assistant
|
||||||
|
- On device
|
||||||
|
initial_option: On device
|
||||||
|
on_value:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return x == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- micro_wake_word.stop:
|
||||||
|
- delay: 500ms
|
||||||
|
- lambda: id(va).set_use_wake_word(true);
|
||||||
|
- voice_assistant.start_continuous:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return x == "On device";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- voice_assistant.stop:
|
||||||
|
- delay: 500ms
|
||||||
|
- micro_wake_word.start:
|
||||||
|
|
||||||
|
micro_wake_word:
|
||||||
|
on_wake_word_detected:
|
||||||
|
- voice_assistant.start:
|
||||||
|
wake_word: !lambda return wake_word;
|
||||||
|
vad:
|
||||||
|
models:
|
||||||
|
- model: okay_nabu
|
||||||
|
- model: hey_mycroft
|
||||||
|
- model: hey_jarvis
|
||||||
249
active/device_esphome/esphome.md
Normal file
249
active/device_esphome/esphome.md
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
# ESP32
|
||||||
|
|
||||||
|
- [ESP32](#esp32)
|
||||||
|
- [Install](#install)
|
||||||
|
- [Devices](#devices)
|
||||||
|
- [Lilygo tdongle](#lilygo-tdongle)
|
||||||
|
- [Local Flashing](#local-flashing)
|
||||||
|
- [Adding a New Device](#adding-a-new-device)
|
||||||
|
- [Controlling Home Assistant](#controlling-home-assistant)
|
||||||
|
- [Configuration Sections](#configuration-sections)
|
||||||
|
- [esphome](#esphome)
|
||||||
|
- [esp32](#esp32-1)
|
||||||
|
- [logger](#logger)
|
||||||
|
- [api](#api)
|
||||||
|
- [wifi](#wifi)
|
||||||
|
- [ota](#ota)
|
||||||
|
- [captive portal](#captive-portal)
|
||||||
|
- [button](#button)
|
||||||
|
- [i2s audio](#i2s-audio)
|
||||||
|
- [microphone](#microphone)
|
||||||
|
- [speaker](#speaker)
|
||||||
|
- [media player](#media-player)
|
||||||
|
- [voice assistant](#voice-assistant)
|
||||||
|
- [micro wake word](#micro-wake-word)
|
||||||
|
- [light](#light)
|
||||||
|
- [binary sensor](#binary-sensor)
|
||||||
|
- [lambda](#lambda)
|
||||||
|
- [Display](#display)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check that you have python 3.11 installed
|
||||||
|
uv python list --only-installed
|
||||||
|
|
||||||
|
# Create the venv (python 3.11 is recommended in the docs)
|
||||||
|
uv venv --python 3.11
|
||||||
|
|
||||||
|
# Install esphome
|
||||||
|
uv pip install esphome wheel pip
|
||||||
|
source .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Devices
|
||||||
|
|
||||||
|
### Lilygo tdongle
|
||||||
|
|
||||||
|
Display: 80 X 160
|
||||||
|
|
||||||
|
## Local Flashing
|
||||||
|
|
||||||
|
Make sure your permissions are set correctly
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo usermod -a -G dialout ducoterra
|
||||||
|
```
|
||||||
|
|
||||||
|
Then "run" your config file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd active/device_esp32
|
||||||
|
uv venv
|
||||||
|
uv pip install esphome
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
esphome run m5stack-atom-echo.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding a New Device
|
||||||
|
|
||||||
|
1. Create a new yaml configuration file called "my-device-device-type.yaml"
|
||||||
|
|
||||||
|
## Controlling Home Assistant
|
||||||
|
|
||||||
|
<https://esphome.io/components/api/#api-actions>
|
||||||
|
|
||||||
|
## Configuration Sections
|
||||||
|
|
||||||
|
<https://esphome.io/components/>
|
||||||
|
|
||||||
|
### esphome
|
||||||
|
|
||||||
|
### esp32
|
||||||
|
|
||||||
|
<https://esphome.io/components/esp32/#configuration-variables>
|
||||||
|
|
||||||
|
### logger
|
||||||
|
|
||||||
|
<https://esphome.io/components/logger/>
|
||||||
|
|
||||||
|
### api
|
||||||
|
|
||||||
|
<https://esphome.io/components/api/>
|
||||||
|
|
||||||
|
### wifi
|
||||||
|
|
||||||
|
<https://esphome.io/components/wifi/>
|
||||||
|
|
||||||
|
### ota
|
||||||
|
|
||||||
|
<https://esphome.io/components/ota/>
|
||||||
|
|
||||||
|
<https://esphome.io/components/ota/esphome/>
|
||||||
|
|
||||||
|
### captive portal
|
||||||
|
|
||||||
|
<https://esphome.io/components/captive_portal/>
|
||||||
|
|
||||||
|
### button
|
||||||
|
|
||||||
|
<https://esphome.io/components/button/>
|
||||||
|
|
||||||
|
### i2s audio
|
||||||
|
|
||||||
|
<https://esphome.io/components/i2s_audio/>
|
||||||
|
|
||||||
|
### microphone
|
||||||
|
|
||||||
|
<https://esphome.io/components/microphone/>
|
||||||
|
|
||||||
|
<https://esphome.io/components/microphone/i2s_audio/>
|
||||||
|
|
||||||
|
### speaker
|
||||||
|
|
||||||
|
<https://esphome.io/components/speaker/i2s_audio/>
|
||||||
|
|
||||||
|
### media player
|
||||||
|
|
||||||
|
<https://esphome.io/components/media_player/speaker/>
|
||||||
|
|
||||||
|
Sometimes you'll need to convert media files to supported encoders.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ffmpeg -i input.flac output.wav
|
||||||
|
```
|
||||||
|
|
||||||
|
To play media on other devices from home assistant, put the
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
action: media_player.play_media
|
||||||
|
target:
|
||||||
|
entity_id: media_player.kitchen_google_home
|
||||||
|
data:
|
||||||
|
media_content_type: "audio/wav"
|
||||||
|
media_content_id: "media-source://media_source/local/wake_word_triggered.wav"
|
||||||
|
```
|
||||||
|
|
||||||
|
### voice assistant
|
||||||
|
|
||||||
|
<https://esphome.io/components/voice_assistant/>
|
||||||
|
|
||||||
|
In Home Assistant's configuration.yaml, add the following to listen to
|
||||||
|
audio recordings of your voice request:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
assist_pipeline:
|
||||||
|
debug_recording_dir: /share/assist_pipeline
|
||||||
|
```
|
||||||
|
|
||||||
|
### micro wake word
|
||||||
|
|
||||||
|
<https://esphome.io/components/micro_wake_word/>
|
||||||
|
|
||||||
|
### light
|
||||||
|
|
||||||
|
<https://esphome.io/components/light/#light-effects>
|
||||||
|
|
||||||
|
### binary sensor
|
||||||
|
|
||||||
|
<https://esphome.io/components/binary_sensor/>
|
||||||
|
|
||||||
|
### lambda
|
||||||
|
|
||||||
|
<https://esphome.io/automations/templates/#config-lambda>
|
||||||
|
|
||||||
|
> id(...) is a helper function that makes ESPHome fetch an object with the
|
||||||
|
> supplied ID (which you defined somewhere else, like top_end_stop ) and lets
|
||||||
|
> you call any of ESPHome’s many APIs directly. For example, here we’re
|
||||||
|
> retrieving the current state of the end stop using .state and using it to
|
||||||
|
> construct our cover state.
|
||||||
|
|
||||||
|
### Display
|
||||||
|
|
||||||
|
Display pages
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
display:
|
||||||
|
- platform: st7735
|
||||||
|
spi_id: spi_lcd
|
||||||
|
model: "INITR_MINI160X80"
|
||||||
|
reset_pin: GPIO1
|
||||||
|
cs_pin: GPIO4
|
||||||
|
dc_pin: GPIO2
|
||||||
|
rotation: 270
|
||||||
|
device_width: 82
|
||||||
|
device_height: 161
|
||||||
|
col_start: 0
|
||||||
|
row_start: 0
|
||||||
|
eight_bit_color: true
|
||||||
|
invert_colors: true
|
||||||
|
use_bgr: true
|
||||||
|
auto_clear_enabled: true
|
||||||
|
id: my_display
|
||||||
|
pages:
|
||||||
|
- id: page1
|
||||||
|
lambda: |-
|
||||||
|
it.print(0, 10, id(font_roboto), "Connecting to");
|
||||||
|
it.print(0, 30, id(font_roboto), "Home Assistant...");
|
||||||
|
- id: page2
|
||||||
|
lambda: |-
|
||||||
|
it.print(0, 10, id(font_roboto), "Configuring");
|
||||||
|
it.print(0, 30, id(font_roboto), "sensors...");
|
||||||
|
- id: page3
|
||||||
|
lambda: |-
|
||||||
|
it.print(0, 10, id(font_roboto), "Loading");
|
||||||
|
it.print(0, 30, id(font_roboto), "important");
|
||||||
|
it.print(0, 50, id(font_roboto), "update...");
|
||||||
|
- id: page4
|
||||||
|
lambda: |-
|
||||||
|
it.image(0, 0, id(my_image), COLOR_OFF, COLOR_ON);
|
||||||
|
```
|
||||||
|
|
||||||
|
Switch pages
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
interval:
|
||||||
|
- interval: 5s
|
||||||
|
then:
|
||||||
|
- display.page.show_next: my_display
|
||||||
|
- component.update: my_display
|
||||||
|
```
|
||||||
|
|
||||||
|
Show an image
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image:
|
||||||
|
- file: "test_tdongle_image.png"
|
||||||
|
type: RGB
|
||||||
|
id: my_image
|
||||||
|
```
|
||||||
|
|
||||||
|
Specify a font
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_roboto
|
||||||
|
size: 20
|
||||||
|
```
|
||||||
386
active/device_esphome/great-room-atom-echo.yaml
Normal file
386
active/device_esphome/great-room-atom-echo.yaml
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
esphome:
|
||||||
|
name: great-room-atom-echo
|
||||||
|
friendly_name: Great Room Atom Echo
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
board: m5stack-atom
|
||||||
|
framework:
|
||||||
|
type: esp-idf
|
||||||
|
|
||||||
|
# Enable logging
|
||||||
|
logger:
|
||||||
|
level: debug
|
||||||
|
|
||||||
|
# Enable Home Assistant API
|
||||||
|
api:
|
||||||
|
encryption:
|
||||||
|
key: !secret great_room_atom_echo_key
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
|
password: !secret wifi_password
|
||||||
|
domain: .reeselink.com
|
||||||
|
fast_connect: true
|
||||||
|
enable_btm: true
|
||||||
|
on_disconnect:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 0%
|
||||||
|
red: 100%
|
||||||
|
green: 0%
|
||||||
|
effect: "Slow Pulse"
|
||||||
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||||
|
ap:
|
||||||
|
ssid: "Great-Room-Atom-Echo"
|
||||||
|
password: !secret hotspot_password
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
password: !secret ota_password
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: factory_reset
|
||||||
|
id: factory_reset_btn
|
||||||
|
name: Factory reset
|
||||||
|
|
||||||
|
i2s_audio:
|
||||||
|
- id: i2s_audio_bus
|
||||||
|
i2s_lrclk_pin: GPIO33
|
||||||
|
i2s_bclk_pin: GPIO19
|
||||||
|
|
||||||
|
microphone:
|
||||||
|
- platform: i2s_audio
|
||||||
|
id: echo_microphone
|
||||||
|
i2s_din_pin: GPIO23
|
||||||
|
adc_type: external
|
||||||
|
pdm: true
|
||||||
|
sample_rate: 16000
|
||||||
|
correct_dc_offset: true
|
||||||
|
|
||||||
|
speaker:
|
||||||
|
- platform: i2s_audio
|
||||||
|
id: echo_speaker
|
||||||
|
i2s_dout_pin: GPIO22
|
||||||
|
dac_type: external
|
||||||
|
bits_per_sample: 16bit
|
||||||
|
sample_rate: 16000
|
||||||
|
channel: stereo # The Echo has poor playback audio quality when using mon audio
|
||||||
|
buffer_duration: 60ms
|
||||||
|
|
||||||
|
media_player:
|
||||||
|
- platform: speaker
|
||||||
|
name: None
|
||||||
|
id: echo_media_player
|
||||||
|
announcement_pipeline:
|
||||||
|
speaker: echo_speaker
|
||||||
|
format: WAV
|
||||||
|
codec_support_enabled: false
|
||||||
|
buffer_size: 6000
|
||||||
|
volume_min: 1
|
||||||
|
volume_max: 1
|
||||||
|
volume_initial: 1
|
||||||
|
files:
|
||||||
|
- id: timer_finished_wave_file
|
||||||
|
file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav
|
||||||
|
on_announcement:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- microphone.is_capturing:
|
||||||
|
then:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
on_idle:
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
- script.execute: reset_led
|
||||||
|
|
||||||
|
voice_assistant:
|
||||||
|
id: va
|
||||||
|
micro_wake_word:
|
||||||
|
microphone:
|
||||||
|
microphone: echo_microphone
|
||||||
|
channels: 0
|
||||||
|
gain_factor: 64
|
||||||
|
media_player: echo_media_player
|
||||||
|
noise_suppression_level: 2
|
||||||
|
auto_gain: 31dBFS
|
||||||
|
on_listening:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
effect: "Slow Pulse"
|
||||||
|
on_stt_vad_end:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
effect: "Fast Pulse"
|
||||||
|
on_tts_start:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
on_end:
|
||||||
|
# Handle the "nevermind" case where there is no announcement
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
- media_player.is_announcing:
|
||||||
|
timeout: 0.5s
|
||||||
|
# Restart only mWW if enabled; streaming wake words automatically restart
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- wait_until:
|
||||||
|
- and:
|
||||||
|
- not:
|
||||||
|
voice_assistant.is_running:
|
||||||
|
- not:
|
||||||
|
speaker.is_playing:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- micro_wake_word.start:
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_error:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 100%
|
||||||
|
green: 0%
|
||||||
|
blue: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
- delay: 2s
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_client_connected:
|
||||||
|
- delay: 2s # Give the api server time to settle
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
on_client_disconnected:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
on_timer_finished:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
- wait_until:
|
||||||
|
not:
|
||||||
|
microphone.is_capturing:
|
||||||
|
- switch.turn_on: timer_ringing
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 0%
|
||||||
|
green: 100%
|
||||||
|
blue: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: "Fast Pulse"
|
||||||
|
- wait_until:
|
||||||
|
- switch.is_off: timer_ringing
|
||||||
|
- light.turn_off: led
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
# button does the following:
|
||||||
|
# short click - stop a timer
|
||||||
|
# if no timer then restart either microwakeword or voice assistant continuous
|
||||||
|
- platform: gpio
|
||||||
|
pin:
|
||||||
|
number: GPIO39
|
||||||
|
inverted: true
|
||||||
|
name: Button
|
||||||
|
disabled_by_default: true
|
||||||
|
entity_category: diagnostic
|
||||||
|
id: echo_button
|
||||||
|
on_multi_click:
|
||||||
|
- timing:
|
||||||
|
- ON for at least 50ms
|
||||||
|
- OFF for at least 50ms
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
switch.is_on: timer_ringing
|
||||||
|
then:
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
else:
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
- timing:
|
||||||
|
- ON for at least 10s
|
||||||
|
then:
|
||||||
|
- button.press: factory_reset_btn
|
||||||
|
|
||||||
|
light:
|
||||||
|
- platform: esp32_rmt_led_strip
|
||||||
|
id: led
|
||||||
|
name: None
|
||||||
|
disabled_by_default: true
|
||||||
|
entity_category: config
|
||||||
|
pin: GPIO27
|
||||||
|
default_transition_length: 0s
|
||||||
|
chipset: SK6812
|
||||||
|
num_leds: 1
|
||||||
|
rgb_order: grb
|
||||||
|
effects:
|
||||||
|
- pulse:
|
||||||
|
name: "Slow Pulse"
|
||||||
|
transition_length: 250ms
|
||||||
|
update_interval: 250ms
|
||||||
|
min_brightness: 50%
|
||||||
|
max_brightness: 100%
|
||||||
|
- pulse:
|
||||||
|
name: "Fast Pulse"
|
||||||
|
transition_length: 100ms
|
||||||
|
update_interval: 100ms
|
||||||
|
min_brightness: 50%
|
||||||
|
max_brightness: 100%
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: reset_led
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
- switch.is_on: use_listen_light
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 100%
|
||||||
|
green: 89%
|
||||||
|
blue: 71%
|
||||||
|
brightness: 60%
|
||||||
|
effect: none
|
||||||
|
else:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state != "On device";
|
||||||
|
- switch.is_on: use_listen_light
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 0%
|
||||||
|
green: 100%
|
||||||
|
blue: 100%
|
||||||
|
brightness: 60%
|
||||||
|
effect: none
|
||||||
|
else:
|
||||||
|
- light.turn_off: led
|
||||||
|
- id: start_wake_word
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- not:
|
||||||
|
- voice_assistant.is_running:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- micro_wake_word.start:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- not:
|
||||||
|
- voice_assistant.is_running:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(true);
|
||||||
|
- voice_assistant.start_continuous:
|
||||||
|
- id: stop_wake_word
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- voice_assistant.stop:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- micro_wake_word.stop:
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: template
|
||||||
|
name: Use listen light
|
||||||
|
id: use_listen_light
|
||||||
|
optimistic: true
|
||||||
|
restore_mode: RESTORE_DEFAULT_ON
|
||||||
|
entity_category: config
|
||||||
|
on_turn_on:
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_turn_off:
|
||||||
|
- script.execute: reset_led
|
||||||
|
- platform: template
|
||||||
|
id: timer_ringing
|
||||||
|
optimistic: true
|
||||||
|
restore_mode: ALWAYS_OFF
|
||||||
|
on_turn_off:
|
||||||
|
# Turn off the repeat mode and disable the pause between playlist items
|
||||||
|
- lambda: |-
|
||||||
|
id(echo_media_player)
|
||||||
|
->make_call()
|
||||||
|
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
|
||||||
|
.set_announcement(true)
|
||||||
|
.perform();
|
||||||
|
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
|
||||||
|
# Stop playing the alarm
|
||||||
|
- media_player.stop:
|
||||||
|
announcement: true
|
||||||
|
on_turn_on:
|
||||||
|
# Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
|
||||||
|
- lambda: |-
|
||||||
|
id(echo_media_player)
|
||||||
|
->make_call()
|
||||||
|
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
|
||||||
|
.set_announcement(true)
|
||||||
|
.perform();
|
||||||
|
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
|
||||||
|
- media_player.speaker.play_on_device_media_file:
|
||||||
|
media_file: timer_finished_wave_file
|
||||||
|
announcement: true
|
||||||
|
- delay: 15min
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
|
||||||
|
select:
|
||||||
|
- platform: template
|
||||||
|
entity_category: config
|
||||||
|
name: Wake word engine location
|
||||||
|
id: wake_word_engine_location
|
||||||
|
optimistic: true
|
||||||
|
restore_value: true
|
||||||
|
options:
|
||||||
|
- In Home Assistant
|
||||||
|
- On device
|
||||||
|
initial_option: On device
|
||||||
|
on_value:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return x == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- micro_wake_word.stop:
|
||||||
|
- delay: 500ms
|
||||||
|
- lambda: id(va).set_use_wake_word(true);
|
||||||
|
- voice_assistant.start_continuous:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return x == "On device";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- voice_assistant.stop:
|
||||||
|
- delay: 500ms
|
||||||
|
- micro_wake_word.start:
|
||||||
|
|
||||||
|
micro_wake_word:
|
||||||
|
on_wake_word_detected:
|
||||||
|
- voice_assistant.start:
|
||||||
|
wake_word: !lambda return wake_word;
|
||||||
|
vad:
|
||||||
|
models:
|
||||||
|
- model: okay_nabu
|
||||||
|
- model: hey_mycroft
|
||||||
|
- model: hey_jarvis
|
||||||
118
active/device_esphome/lilygo-tdongle.yaml
Normal file
118
active/device_esphome/lilygo-tdongle.yaml
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
esphome:
|
||||||
|
name: tdongle
|
||||||
|
friendly_name: tdongle
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
board: esp32-s3-devkitc-1
|
||||||
|
framework:
|
||||||
|
type: esp-idf
|
||||||
|
flash_size: 16MB
|
||||||
|
|
||||||
|
logger:
|
||||||
|
|
||||||
|
# Enable Home Assistant API
|
||||||
|
api:
|
||||||
|
encryption:
|
||||||
|
key: !secret lilygo_tdongle_key
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
|
password: !secret wifi_password
|
||||||
|
domain: .reeselink.com
|
||||||
|
fast_connect: true
|
||||||
|
enable_btm: true
|
||||||
|
id: wifithing
|
||||||
|
# on_connect:
|
||||||
|
# - component.update: my_online_image
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
password: !secret ota_password
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
pin: GPIO0
|
||||||
|
name: Button
|
||||||
|
|
||||||
|
spi:
|
||||||
|
- id: spi_led
|
||||||
|
clk_pin: GPIO39
|
||||||
|
mosi_pin: GPIO40
|
||||||
|
- id: spi_lcd
|
||||||
|
clk_pin: GPIO5
|
||||||
|
mosi_pin: GPIO3
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: ledc
|
||||||
|
frequency: 2000
|
||||||
|
pin: GPIO38
|
||||||
|
inverted: True
|
||||||
|
id: backlight_output
|
||||||
|
|
||||||
|
light:
|
||||||
|
- platform: monochromatic
|
||||||
|
output: backlight_output
|
||||||
|
name: "LCD Backlight"
|
||||||
|
id: lcd_backlight
|
||||||
|
restore_mode: ALWAYS_ON
|
||||||
|
# RGB Led, APA102 on GPIO39/GPIO40
|
||||||
|
- platform: spi_led_strip
|
||||||
|
spi_id: spi_led
|
||||||
|
num_leds: 1
|
||||||
|
name: "FastLED SPI Light"
|
||||||
|
data_rate: 1MHz # Adjust as needed, APA102 supports up to 20MHz, 1MHz is a safe starting point
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: st7735
|
||||||
|
spi_id: spi_lcd
|
||||||
|
model: "INITR_MINI160X80"
|
||||||
|
reset_pin: GPIO1
|
||||||
|
cs_pin: GPIO4
|
||||||
|
dc_pin: GPIO2
|
||||||
|
rotation: 270
|
||||||
|
device_width: 82
|
||||||
|
device_height: 161
|
||||||
|
col_start: 0
|
||||||
|
row_start: 0
|
||||||
|
eight_bit_color: true
|
||||||
|
invert_colors: true
|
||||||
|
use_bgr: true
|
||||||
|
auto_clear_enabled: true
|
||||||
|
id: my_display
|
||||||
|
pages:
|
||||||
|
- id: page1
|
||||||
|
lambda: |-
|
||||||
|
it.print(0, 10, id(font_roboto), "Connecting to");
|
||||||
|
it.print(0, 30, id(font_roboto), "Home Assistant...");
|
||||||
|
- id: page2
|
||||||
|
lambda: |-
|
||||||
|
it.print(0, 10, id(font_roboto), "Configuring");
|
||||||
|
it.print(0, 30, id(font_roboto), "sensors...");
|
||||||
|
- id: page3
|
||||||
|
lambda: |-
|
||||||
|
it.print(0, 10, id(font_roboto), "Loading");
|
||||||
|
it.print(0, 30, id(font_roboto), "important");
|
||||||
|
it.print(0, 50, id(font_roboto), "update...");
|
||||||
|
- id: page4
|
||||||
|
lambda: |-
|
||||||
|
it.image(0, 0, id(my_image), COLOR_OFF, COLOR_ON);
|
||||||
|
|
||||||
|
image:
|
||||||
|
- file: "test_tdongle_image.png"
|
||||||
|
type: RGB
|
||||||
|
id: my_image
|
||||||
|
|
||||||
|
http_request:
|
||||||
|
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_roboto
|
||||||
|
size: 20
|
||||||
|
|
||||||
|
interval:
|
||||||
|
- interval: 5s
|
||||||
|
then:
|
||||||
|
- display.page.show_next: my_display
|
||||||
|
- component.update: my_display
|
||||||
387
active/device_esphome/loft-atom-echo.yaml
Normal file
387
active/device_esphome/loft-atom-echo.yaml
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
esphome:
|
||||||
|
name: loft-atom-echo
|
||||||
|
friendly_name: Loft Atom Echo
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
board: m5stack-atom
|
||||||
|
cpu_frequency: 240MHz
|
||||||
|
framework:
|
||||||
|
type: esp-idf
|
||||||
|
|
||||||
|
# Enable logging
|
||||||
|
logger:
|
||||||
|
level: debug
|
||||||
|
|
||||||
|
# Enable Home Assistant API
|
||||||
|
api:
|
||||||
|
encryption:
|
||||||
|
key: !secret loft_atom_echo_key
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
|
password: !secret wifi_password
|
||||||
|
domain: .reeselink.com
|
||||||
|
fast_connect: true
|
||||||
|
enable_btm: true
|
||||||
|
on_disconnect:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 0%
|
||||||
|
red: 100%
|
||||||
|
green: 0%
|
||||||
|
effect: "Slow Pulse"
|
||||||
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||||
|
ap:
|
||||||
|
ssid: "Loft-Atom-Echo"
|
||||||
|
password: !secret hotspot_password
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
password: !secret ota_password
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: factory_reset
|
||||||
|
id: factory_reset_btn
|
||||||
|
name: Factory reset
|
||||||
|
|
||||||
|
i2s_audio:
|
||||||
|
- id: i2s_audio_bus
|
||||||
|
i2s_lrclk_pin: GPIO33
|
||||||
|
i2s_bclk_pin: GPIO19
|
||||||
|
|
||||||
|
microphone:
|
||||||
|
- platform: i2s_audio
|
||||||
|
id: echo_microphone
|
||||||
|
i2s_din_pin: GPIO23
|
||||||
|
adc_type: external
|
||||||
|
pdm: true
|
||||||
|
sample_rate: 16000
|
||||||
|
correct_dc_offset: true
|
||||||
|
|
||||||
|
speaker:
|
||||||
|
- platform: i2s_audio
|
||||||
|
id: echo_speaker
|
||||||
|
i2s_dout_pin: GPIO22
|
||||||
|
dac_type: external
|
||||||
|
bits_per_sample: 16bit
|
||||||
|
sample_rate: 16000
|
||||||
|
channel: stereo # The Echo has poor playback audio quality when using mon audio
|
||||||
|
buffer_duration: 60ms
|
||||||
|
|
||||||
|
media_player:
|
||||||
|
- platform: speaker
|
||||||
|
name: None
|
||||||
|
id: echo_media_player
|
||||||
|
announcement_pipeline:
|
||||||
|
speaker: echo_speaker
|
||||||
|
format: WAV
|
||||||
|
codec_support_enabled: false
|
||||||
|
buffer_size: 6000
|
||||||
|
volume_min: 1
|
||||||
|
volume_max: 1
|
||||||
|
volume_initial: 1
|
||||||
|
files:
|
||||||
|
- id: timer_finished_wave_file
|
||||||
|
file: https://github.com/esphome/wake-word-voice-assistants/raw/main/sounds/timer_finished.wav
|
||||||
|
on_announcement:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- microphone.is_capturing:
|
||||||
|
then:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
on_idle:
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
- script.execute: reset_led
|
||||||
|
|
||||||
|
voice_assistant:
|
||||||
|
id: va
|
||||||
|
micro_wake_word:
|
||||||
|
microphone:
|
||||||
|
microphone: echo_microphone
|
||||||
|
channels: 0
|
||||||
|
gain_factor: 64
|
||||||
|
media_player: echo_media_player
|
||||||
|
noise_suppression_level: 2
|
||||||
|
auto_gain: 31dBFS
|
||||||
|
on_listening:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
effect: "Slow Pulse"
|
||||||
|
on_stt_vad_end:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
effect: "Fast Pulse"
|
||||||
|
on_tts_start:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
blue: 100%
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
on_end:
|
||||||
|
# Handle the "nevermind" case where there is no announcement
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
- media_player.is_announcing:
|
||||||
|
timeout: 0.5s
|
||||||
|
# Restart only mWW if enabled; streaming wake words automatically restart
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- wait_until:
|
||||||
|
- and:
|
||||||
|
- not:
|
||||||
|
voice_assistant.is_running:
|
||||||
|
- not:
|
||||||
|
speaker.is_playing:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- micro_wake_word.start:
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_error:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 100%
|
||||||
|
green: 0%
|
||||||
|
blue: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
- delay: 2s
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_client_connected:
|
||||||
|
- delay: 2s # Give the api server time to settle
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
on_client_disconnected:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
on_timer_finished:
|
||||||
|
- script.execute: stop_wake_word
|
||||||
|
- wait_until:
|
||||||
|
not:
|
||||||
|
microphone.is_capturing:
|
||||||
|
- switch.turn_on: timer_ringing
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 0%
|
||||||
|
green: 100%
|
||||||
|
blue: 0%
|
||||||
|
brightness: 100%
|
||||||
|
effect: "Fast Pulse"
|
||||||
|
- wait_until:
|
||||||
|
- switch.is_off: timer_ringing
|
||||||
|
- light.turn_off: led
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
# button does the following:
|
||||||
|
# short click - stop a timer
|
||||||
|
# if no timer then restart either microwakeword or voice assistant continuous
|
||||||
|
- platform: gpio
|
||||||
|
pin:
|
||||||
|
number: GPIO39
|
||||||
|
inverted: true
|
||||||
|
name: Button
|
||||||
|
disabled_by_default: true
|
||||||
|
entity_category: diagnostic
|
||||||
|
id: echo_button
|
||||||
|
on_multi_click:
|
||||||
|
- timing:
|
||||||
|
- ON for at least 50ms
|
||||||
|
- OFF for at least 50ms
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
switch.is_on: timer_ringing
|
||||||
|
then:
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
else:
|
||||||
|
- script.execute: start_wake_word
|
||||||
|
- timing:
|
||||||
|
- ON for at least 10s
|
||||||
|
then:
|
||||||
|
- button.press: factory_reset_btn
|
||||||
|
|
||||||
|
light:
|
||||||
|
- platform: esp32_rmt_led_strip
|
||||||
|
id: led
|
||||||
|
name: None
|
||||||
|
disabled_by_default: true
|
||||||
|
entity_category: config
|
||||||
|
pin: GPIO27
|
||||||
|
default_transition_length: 0s
|
||||||
|
chipset: SK6812
|
||||||
|
num_leds: 1
|
||||||
|
rgb_order: grb
|
||||||
|
effects:
|
||||||
|
- pulse:
|
||||||
|
name: "Slow Pulse"
|
||||||
|
transition_length: 250ms
|
||||||
|
update_interval: 250ms
|
||||||
|
min_brightness: 50%
|
||||||
|
max_brightness: 100%
|
||||||
|
- pulse:
|
||||||
|
name: "Fast Pulse"
|
||||||
|
transition_length: 100ms
|
||||||
|
update_interval: 100ms
|
||||||
|
min_brightness: 50%
|
||||||
|
max_brightness: 100%
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: reset_led
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
- switch.is_on: use_listen_light
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 100%
|
||||||
|
green: 89%
|
||||||
|
blue: 71%
|
||||||
|
brightness: 60%
|
||||||
|
effect: none
|
||||||
|
else:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: return id(wake_word_engine_location).state != "On device";
|
||||||
|
- switch.is_on: use_listen_light
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: led
|
||||||
|
red: 0%
|
||||||
|
green: 100%
|
||||||
|
blue: 100%
|
||||||
|
brightness: 60%
|
||||||
|
effect: none
|
||||||
|
else:
|
||||||
|
- light.turn_off: led
|
||||||
|
- id: start_wake_word
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- not:
|
||||||
|
- voice_assistant.is_running:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- micro_wake_word.start:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- not:
|
||||||
|
- voice_assistant.is_running:
|
||||||
|
- lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(true);
|
||||||
|
- voice_assistant.start_continuous:
|
||||||
|
- id: stop_wake_word
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return id(wake_word_engine_location).state == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- voice_assistant.stop:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return id(wake_word_engine_location).state == "On device";
|
||||||
|
then:
|
||||||
|
- micro_wake_word.stop:
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: template
|
||||||
|
name: Use listen light
|
||||||
|
id: use_listen_light
|
||||||
|
optimistic: true
|
||||||
|
restore_mode: RESTORE_DEFAULT_ON
|
||||||
|
entity_category: config
|
||||||
|
on_turn_on:
|
||||||
|
- script.execute: reset_led
|
||||||
|
on_turn_off:
|
||||||
|
- script.execute: reset_led
|
||||||
|
- platform: template
|
||||||
|
id: timer_ringing
|
||||||
|
optimistic: true
|
||||||
|
restore_mode: ALWAYS_OFF
|
||||||
|
on_turn_off:
|
||||||
|
# Turn off the repeat mode and disable the pause between playlist items
|
||||||
|
- lambda: |-
|
||||||
|
id(echo_media_player)
|
||||||
|
->make_call()
|
||||||
|
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF)
|
||||||
|
.set_announcement(true)
|
||||||
|
.perform();
|
||||||
|
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 0);
|
||||||
|
# Stop playing the alarm
|
||||||
|
- media_player.stop:
|
||||||
|
announcement: true
|
||||||
|
on_turn_on:
|
||||||
|
# Turn on the repeat mode and pause for 1000 ms between playlist items/repeats
|
||||||
|
- lambda: |-
|
||||||
|
id(echo_media_player)
|
||||||
|
->make_call()
|
||||||
|
.set_command(media_player::MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE)
|
||||||
|
.set_announcement(true)
|
||||||
|
.perform();
|
||||||
|
id(echo_media_player)->set_playlist_delay_ms(speaker::AudioPipelineType::ANNOUNCEMENT, 1000);
|
||||||
|
- media_player.speaker.play_on_device_media_file:
|
||||||
|
media_file: timer_finished_wave_file
|
||||||
|
announcement: true
|
||||||
|
- delay: 15min
|
||||||
|
- switch.turn_off: timer_ringing
|
||||||
|
|
||||||
|
select:
|
||||||
|
- platform: template
|
||||||
|
entity_category: config
|
||||||
|
name: Wake word engine location
|
||||||
|
id: wake_word_engine_location
|
||||||
|
optimistic: true
|
||||||
|
restore_value: true
|
||||||
|
options:
|
||||||
|
- In Home Assistant
|
||||||
|
- On device
|
||||||
|
initial_option: On device
|
||||||
|
on_value:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return x == "In Home Assistant";
|
||||||
|
then:
|
||||||
|
- micro_wake_word.stop:
|
||||||
|
- delay: 500ms
|
||||||
|
- lambda: id(va).set_use_wake_word(true);
|
||||||
|
- voice_assistant.start_continuous:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return x == "On device";
|
||||||
|
then:
|
||||||
|
- lambda: id(va).set_use_wake_word(false);
|
||||||
|
- voice_assistant.stop:
|
||||||
|
- delay: 500ms
|
||||||
|
- micro_wake_word.start:
|
||||||
|
|
||||||
|
micro_wake_word:
|
||||||
|
on_wake_word_detected:
|
||||||
|
- voice_assistant.start:
|
||||||
|
wake_word: !lambda return wake_word;
|
||||||
|
vad:
|
||||||
|
models:
|
||||||
|
- model: okay_nabu
|
||||||
|
- model: hey_mycroft
|
||||||
|
- model: hey_jarvis
|
||||||
BIN
active/device_esphome/test_tdongle_image.png
Normal file
BIN
active/device_esphome/test_tdongle_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
active/device_esphome/test_tdongle_image.xcf
Normal file
BIN
active/device_esphome/test_tdongle_image.xcf
Normal file
Binary file not shown.
BIN
active/device_esphome/wake_word_triggered.flac
Normal file
BIN
active/device_esphome/wake_word_triggered.flac
Normal file
Binary file not shown.
BIN
active/device_esphome/wake_word_triggered.wav
Normal file
BIN
active/device_esphome/wake_word_triggered.wav
Normal file
Binary file not shown.
@@ -11,3 +11,13 @@ sudo curl -o /etc/udev/rules.d/50-qmk.rules https://raw.githubusercontent.com/qm
|
|||||||
sudo udevadm control --reload-rules
|
sudo udevadm control --reload-rules
|
||||||
sudo udevadm trigger
|
sudo udevadm trigger
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Beta Bios Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# With charger attached
|
||||||
|
sudo fwupdmgr enable-remote lvfs-testing
|
||||||
|
sudo fwupdmgr refresh --force
|
||||||
|
sudo fwupdmgr get-updates
|
||||||
|
sudo fwupdmgr update
|
||||||
|
```
|
||||||
|
|||||||
BIN
active/device_framework_16/icc_profile.icm
Normal file
BIN
active/device_framework_16/icc_profile.icm
Normal file
Binary file not shown.
297
active/device_framework_desktop/framework_desktop.md
Normal file
297
active/device_framework_desktop/framework_desktop.md
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
# Framework Desktop
|
||||||
|
|
||||||
|
- [Framework Desktop](#framework-desktop)
|
||||||
|
- [BIOS](#bios)
|
||||||
|
- [References](#references)
|
||||||
|
- [Notes](#notes)
|
||||||
|
- [Volume Locations](#volume-locations)
|
||||||
|
- [Setup](#setup)
|
||||||
|
- [Create the AI user](#create-the-ai-user)
|
||||||
|
- [Helper aliases](#helper-aliases)
|
||||||
|
- [Create the models dir](#create-the-models-dir)
|
||||||
|
- [Install the Hugging Face CLI](#install-the-hugging-face-cli)
|
||||||
|
- [Download models](#download-models)
|
||||||
|
- [Text models](#text-models)
|
||||||
|
- [Image models](#image-models)
|
||||||
|
- [Create the systemd-ai pod](#create-the-systemd-ai-pod)
|
||||||
|
- [llama.cpp](#llamacpp)
|
||||||
|
- [stable-diffusion.cpp](#stable-diffusioncpp)
|
||||||
|
- [open-webui](#open-webui)
|
||||||
|
- [Install the whole thing with quadlets (TM)](#install-the-whole-thing-with-quadlets-tm)
|
||||||
|
- [Install the update script](#install-the-update-script)
|
||||||
|
|
||||||
|
## BIOS
|
||||||
|
|
||||||
|
<https://knowledgebase.frame.work/en_us/changing-memory-allocation-amd-ryzen-ai-max-300-series-By1LG5Yrll>
|
||||||
|
|
||||||
|
1. Set GPU memory to 512MB
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
<https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html>
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Volume Locations
|
||||||
|
|
||||||
|
`~/.local/share/containers/storage/volumes/`
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Create the AI user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create your local ai user. This will be the user you launch podman processes from.
|
||||||
|
useradd -m ai
|
||||||
|
loginctl enable-linger ai
|
||||||
|
su -l ai
|
||||||
|
mkdir -p ~/.config/containers/systemd/
|
||||||
|
```
|
||||||
|
|
||||||
|
Models are big. You'll want some tools to help find large files quickly when space runs out.
|
||||||
|
|
||||||
|
### Helper aliases
|
||||||
|
|
||||||
|
Add these to your .bashrc:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Calculate all folder sizes in current dir
|
||||||
|
alias {dudir,dud}='du -h --max-depth 1 | sort -h'
|
||||||
|
|
||||||
|
# Calculate all file sizes in current dir
|
||||||
|
alias {dufile,duf}='ls -lhSr'
|
||||||
|
|
||||||
|
# Restart llama-server / follow logs
|
||||||
|
alias llama-reload="systemctl --user daemon-reload && systemctl --user restart llama-server.service"
|
||||||
|
alias llama-logs="journalctl --user -fu llama-server"
|
||||||
|
|
||||||
|
# Restart stable diffusion gen and edit server / follow logs
|
||||||
|
alias sd-gen-reload='systemctl --user daemon-reload && systemctl --user restart stable-diffusion-gen-server'
|
||||||
|
alias sd-gen-logs='journalctl --user -xeu stable-diffusion-gen-server'
|
||||||
|
alias sd-edit-reload='systemctl --user daemon-reload && systemctl --user restart stable-diffusion-edit-server'
|
||||||
|
alias sd-edit-logs='journalctl --user -xeu stable-diffusion-edit-server'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create the models dir
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /home/ai/models/{text,image,video}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install the Hugging Face CLI
|
||||||
|
|
||||||
|
<https://huggingface.co/docs/huggingface_hub/en/guides/cli#getting-started>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install
|
||||||
|
curl -LsSf https://hf.co/cli/install.sh | bash
|
||||||
|
|
||||||
|
# Login
|
||||||
|
hf auth login
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download models
|
||||||
|
|
||||||
|
#### Text models
|
||||||
|
|
||||||
|
<https://huggingface.co/ggml-org/collections>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# gpt-oss-120b
|
||||||
|
mkdir /home/ai/models/text/gpt-oss-120b
|
||||||
|
hf download --local-dir /home/ai/models/text/gpt-oss-120b ggml-org/gpt-oss-120b-GGUF
|
||||||
|
|
||||||
|
# gpt-oss-20b
|
||||||
|
mkdir /home/ai/models/text/gpt-oss-20b
|
||||||
|
hf download --local-dir /home/ai/models/text/gpt-oss-20b ggml-org/gpt-oss-20b-GGUF
|
||||||
|
|
||||||
|
# devstral-2-123b
|
||||||
|
mkdir /home/ai/models/text/devstral-2-123b
|
||||||
|
hf download --local-dir /home/ai/models/text/devstral-2-123b unsloth/Devstral-2-123B-Instruct-2512-GGUF Q4_K_M/Devstral-2-123B-Instruct-2512-Q4_K_M-00001-of-00002.gguf
|
||||||
|
hf download --local-dir /home/ai/models/text/devstral-2-123b unsloth/Devstral-2-123B-Instruct-2512-GGUF Q4_K_M/Devstral-2-123B-Instruct-2512-Q4_K_M-00002-of-00002.gguf
|
||||||
|
|
||||||
|
# devstral-small-2-24b
|
||||||
|
mkdir /home/ai/models/text/devstral-small-2-24b
|
||||||
|
hf download --local-dir /home/ai/models/text/devstral-small-2-24b unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF Devstral-Small-2-24B-Instruct-2512-Q4_K_M.gguf
|
||||||
|
|
||||||
|
# ministral-3-14b
|
||||||
|
mkdir /home/ai/models/text/ministral-3-14b
|
||||||
|
hf download --local-dir /home/ai/models/text/ministral-3-14b ggml-org/Ministral-3-14B-Reasoning-2512-GGUF
|
||||||
|
|
||||||
|
# ministral-3-3b-instruct
|
||||||
|
mkdir /home/ai/models/text/ministral-3-3b-instruct
|
||||||
|
hf download --local-dir /home/ai/models/text/ministral-3-3b-instruct ggml-org/Ministral-3-3B-Instruct-2512-GGUF
|
||||||
|
|
||||||
|
# nemotron-nano-30b
|
||||||
|
mkdir /home/ai/models/text/nemotron-nano-30b
|
||||||
|
hf download --local-dir /home/ai/models/text/nemotron-nano-30b ggml-org/Nemotron-Nano-3-30B-A3B-GGUF Nemotron-Nano-3-30B-A3B-Q4_K_M.gguf
|
||||||
|
|
||||||
|
# qwen3-30b-a3b-instruct
|
||||||
|
mkdir /home/ai/models/text/qwen3-30b-a3b-instruct
|
||||||
|
hf download --local-dir /home/ai/models/text/qwen3-30b-a3b-instruct ggml-org/Qwen3-30B-A3B-Instruct-2507-Q8_0-GGUF
|
||||||
|
|
||||||
|
# qwen3-coder-30b-a3b-instruct
|
||||||
|
mkdir /home/ai/models/text/qwen3-coder-30b-a3b-instruct
|
||||||
|
hf download --local-dir /home/ai/models/text/qwen3-coder-30b-a3b-instruct ggml-org/Qwen3-Coder-30B-A3B-Instruct-Q8_0-GGUF
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Image models
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# z-turbo
|
||||||
|
mkdir /home/ai/models/image/z-turbo
|
||||||
|
hf download --local-dir /home/ai/models/image/z-turbo QuantStack/FLUX.1-Kontext-dev-GGUF flux1-kontext-dev-Q4_K_M.gguf
|
||||||
|
hf download --local-dir /home/ai/models/image/z-turbo black-forest-labs/FLUX.1-schnell ae.safetensors
|
||||||
|
hf download --local-dir /home/ai/models/image/z-turbo unsloth/Qwen3-4B-Instruct-2507-GGUF Qwen3-4B-Instruct-2507-Q4_K_M.gguf
|
||||||
|
|
||||||
|
# flux-1-kontext
|
||||||
|
mkdir /home/ai/models/image/flux-1-kontext
|
||||||
|
hf download --local-dir /home/ai/models/image/flux-1-kontext leejet/Z-Image-Turbo-GGUF z_image_turbo-Q4_K.gguf
|
||||||
|
hf download --local-dir /home/ai/models/image/flux-1-kontext black-forest-labs/FLUX.1-dev ae.safetensors
|
||||||
|
hf download --local-dir /home/ai/models/image/flux-1-kontext comfyanonymous/flux_text_encoders clip_l.safetensors
|
||||||
|
hf download --local-dir /home/ai/models/image/flux-1-kontext comfyanonymous/flux_text_encoders t5xxl_fp16.safetensors
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create the systemd-ai pod
|
||||||
|
|
||||||
|
You'll at least want the ai pod and network. Copy `ai.pod` and `ai.network` out
|
||||||
|
of `quadlets` into `~/.config/containers/systemd`.
|
||||||
|
|
||||||
|
Then run `systemctl --user daemon-reload && systemctl --user start ai-pod`
|
||||||
|
|
||||||
|
## llama.cpp
|
||||||
|
|
||||||
|
<https://github.com/ggml-org/llama.cpp/tree/master/tools/server>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the llama.cpp container image
|
||||||
|
git clone https://github.com/ggml-org/llama.cpp.git
|
||||||
|
cd llama.cpp
|
||||||
|
export BUILD_TAG=$(date +"%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
|
# Vulkan
|
||||||
|
podman build -f .devops/vulkan.Dockerfile -t llama-cpp-vulkan:${BUILD_TAG} -t llama-cpp-vulkan:latest .
|
||||||
|
|
||||||
|
# Run llama server (Available on port 8000)
|
||||||
|
# Add `--n-cpu-moe 32` to gpt-oss-120b to keep minimal number of expert in GPU
|
||||||
|
podman run \
|
||||||
|
--rm \
|
||||||
|
--name llama-server-demo \
|
||||||
|
--pod systemd-ai \
|
||||||
|
--device=/dev/kfd \
|
||||||
|
--device=/dev/dri \
|
||||||
|
-v /home/ai/models/text:/models:z \
|
||||||
|
localhost/llama-cpp-vulkan:latest \
|
||||||
|
--port 8000 \
|
||||||
|
-c 64000 \
|
||||||
|
-b 64000 \
|
||||||
|
-ub 500 \
|
||||||
|
--perf \
|
||||||
|
--n-gpu-layers all \
|
||||||
|
--jinja \
|
||||||
|
--models-max 1 \
|
||||||
|
--models-dir /models
|
||||||
|
```
|
||||||
|
|
||||||
|
## stable-diffusion.cpp
|
||||||
|
|
||||||
|
Server: <https://github.com/leejet/stable-diffusion.cpp/tree/master/examples/server>
|
||||||
|
|
||||||
|
CLI: <https://github.com/leejet/stable-diffusion.cpp/tree/master/examples/cli>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/leejet/stable-diffusion.cpp.git
|
||||||
|
cd stable-diffusion.cpp
|
||||||
|
git submodule update --init --recursive
|
||||||
|
export BUILD_TAG=$(date +"%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
|
# Vulkan
|
||||||
|
podman build -f Dockerfile.vulkan -t stable-diffusion-cpp:${BUILD_TAG} -t stable-diffusion-cpp:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# z-turbo
|
||||||
|
podman run --rm \
|
||||||
|
-v /home/ai/models:/models:z \
|
||||||
|
-v /home/ai/output:/output:z \
|
||||||
|
--device /dev/kfd \
|
||||||
|
--device /dev/dri \
|
||||||
|
localhost/stable-diffusion-cpp:latest \
|
||||||
|
--diffusion-model /models/image/z-turbo/z_image_turbo-Q4_K.gguf \
|
||||||
|
--vae /models/image/z-turbo/ae.safetensors \
|
||||||
|
--llm /models/image/z-turbo/Qwen3-4B-Instruct-2507-Q4_K_M.gguf \
|
||||||
|
--cfg-scale 1.0 \
|
||||||
|
-v \
|
||||||
|
-H 1024 \
|
||||||
|
-W 1024 \
|
||||||
|
--seed -1 \
|
||||||
|
--steps 8 \
|
||||||
|
--vae-conv-direct \
|
||||||
|
-o /output/output.png \
|
||||||
|
-p "A photorealistic dragon"
|
||||||
|
|
||||||
|
# Edit with flux kontext
|
||||||
|
podman run --rm \
|
||||||
|
-v /home/ai/models:/models:z \
|
||||||
|
-v /home/ai/output:/output:z \
|
||||||
|
--device /dev/kfd \
|
||||||
|
--device /dev/dri \
|
||||||
|
localhost/stable-diffusion-cpp:latest \
|
||||||
|
--diffusion-model /models/image/flux-1-kontext/flux1-kontext-dev-Q4_K_M.gguf \
|
||||||
|
--vae /models/image/flux-1-kontext/ae.safetensors \
|
||||||
|
--clip_l /models/image/flux-1-kontext/clip_l.safetensors \
|
||||||
|
--t5xxl /models/image/flux-1-kontext/t5xxl_fp16.safetensors \
|
||||||
|
--cfg-scale 1.0 \
|
||||||
|
--sampling-method euler \
|
||||||
|
--seed -1 \
|
||||||
|
--steps 28 \
|
||||||
|
--vae-conv-direct \
|
||||||
|
-v \
|
||||||
|
-H 512 \
|
||||||
|
-W 512 \
|
||||||
|
-o /output/output.png \
|
||||||
|
-r /output/everquest_logo.png \
|
||||||
|
-p "Add the text 'EverQuest'"
|
||||||
|
```
|
||||||
|
|
||||||
|
## open-webui
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir /home/ai/.env
|
||||||
|
# Create a file called open-webui-env with `WEBUI_SECRET_KEY="some-random-key"
|
||||||
|
scp active/device_framework_desktop/secrets/open-webui-env deskwork-ai:.env/
|
||||||
|
|
||||||
|
# Will be available on port 8080
|
||||||
|
podman run \
|
||||||
|
-d \
|
||||||
|
--pod ai \
|
||||||
|
-v open-webui:/app/backend/data \
|
||||||
|
--name open-webui \
|
||||||
|
--restart always \
|
||||||
|
ghcr.io/open-webui/open-webui:main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install the whole thing with quadlets (TM)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Installs and runs all services in `quadlets/`
|
||||||
|
scp -r active/device_framework_desktop/quadlets/* deskwork-ai:.config/containers/systemd/
|
||||||
|
ssh deskwork-ai
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user restart ai-pod.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Note, all services will be available at `host.containers.internal`. So llama.cpp
|
||||||
|
will be up at `http://host.containers.internal:8000`.
|
||||||
|
|
||||||
|
### Install the update script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Builds the latest llama.cpp and stable-diffusion.cpp
|
||||||
|
# 2. Pulls the latest open-webui
|
||||||
|
# 3. Restarts all services
|
||||||
|
scp active/device_framework_desktop/update-script.sh deskwork:
|
||||||
|
ssh deskwork-ai
|
||||||
|
chmod +x update-script.sh
|
||||||
|
./update-script.sh
|
||||||
|
```
|
||||||
2
active/device_framework_desktop/quadlets/ai.network
Normal file
2
active/device_framework_desktop/quadlets/ai.network
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[Network]
|
||||||
|
IPv6=true
|
||||||
8
active/device_framework_desktop/quadlets/ai.pod
Normal file
8
active/device_framework_desktop/quadlets/ai.pod
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Pod]
|
||||||
|
Network=ai.network
|
||||||
|
# llama.cpp
|
||||||
|
PublishPort=8000:8000/tcp
|
||||||
|
# open-webui
|
||||||
|
PublishPort=8080:8080/tcp
|
||||||
|
# stable-diffusion.cpp
|
||||||
|
PublishPort=1234:1234/tcp
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=A Llama CPP Server Running GPT OSS 120b
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
# Shared AI pod
|
||||||
|
Pod=ai.pod
|
||||||
|
|
||||||
|
# Image is built locally via podman build
|
||||||
|
Image=localhost/llama-cpp-vulkan:latest
|
||||||
|
|
||||||
|
# Downloaded models volume
|
||||||
|
Volume=/home/ai/models/text:/models:z
|
||||||
|
|
||||||
|
# GPU Device
|
||||||
|
AddDevice=/dev/kfd
|
||||||
|
AddDevice=/dev/dri
|
||||||
|
|
||||||
|
# Server command
|
||||||
|
Exec=--port 8000 \
|
||||||
|
-c 48000 \
|
||||||
|
-b 48000 \
|
||||||
|
-ub 500 \
|
||||||
|
--perf \
|
||||||
|
--n-gpu-layers all \
|
||||||
|
--jinja \
|
||||||
|
--models-max 1 \
|
||||||
|
--models-dir /models
|
||||||
|
|
||||||
|
# Health Check
|
||||||
|
HealthCmd=CMD-SHELL curl --fail http://127.0.0.1:8000/props?model=gpt-oss-120b || exit 1
|
||||||
|
HealthInterval=10s
|
||||||
|
HealthRetries=3
|
||||||
|
HealthStartPeriod=10s
|
||||||
|
HealthTimeout=30s
|
||||||
|
HealthOnFailure=kill
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
# Extend Timeout to allow time to pull the image
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
# Start by default on boot
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=An Open Webui Frontend for Local AI Services
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
# Shared AI pod
|
||||||
|
Pod=ai.pod
|
||||||
|
|
||||||
|
# Open Webui base image
|
||||||
|
Image=ghcr.io/open-webui/open-webui:main
|
||||||
|
|
||||||
|
# Nothing too complicated here. Open Webui will basically configure itself.
|
||||||
|
Volume=open-webui-data:/app/backend/data
|
||||||
|
|
||||||
|
# WEBUI_SECRET_KEY is required to prevent logout on Restart
|
||||||
|
EnvironmentFile=/home/ai/.env/open-webui-env
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
# Extend Timeout to allow time to pull the image
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
# Start by default on boot
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=A Stable Diffusion CPP Server for Editing Images
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
# Shared AI pod
|
||||||
|
Pod=ai.pod
|
||||||
|
|
||||||
|
# Vulkan image for AMD GPU
|
||||||
|
Image=localhost/stable-diffusion-cpp:latest
|
||||||
|
|
||||||
|
# Shared models directory
|
||||||
|
Volume=/home/ai/models:/models:z
|
||||||
|
|
||||||
|
# GPU Device
|
||||||
|
AddDevice=/dev/kfd
|
||||||
|
AddDevice=/dev/dri
|
||||||
|
|
||||||
|
# Override entrypoint to use server
|
||||||
|
Entrypoint=/sd-server
|
||||||
|
|
||||||
|
# Server args
|
||||||
|
Exec=-l 0.0.0.0 \
|
||||||
|
--listen-port 1235 \
|
||||||
|
--diffusion-model /models/image/flux-1-kontext/flux1-kontext-dev-Q4_K_M.gguf \
|
||||||
|
--vae /models/image/flux-1-kontext/ae.safetensors \
|
||||||
|
--clip_l /models/image/flux-1-kontext/clip_l.safetensors \
|
||||||
|
--t5xxl /models/image/flux-1-kontext/t5xxl_fp16.safetensors \
|
||||||
|
--cfg-scale 1.0 \
|
||||||
|
--sampling-method euler \
|
||||||
|
--vae-conv-direct \
|
||||||
|
--seed -1 \
|
||||||
|
--steps 28 \
|
||||||
|
-v
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
# Extend Timeout to allow time to pull the image
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
# Start by default on boot
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=A Stable Diffusion CPP Server for Generating Images
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
# Shared AI pod
|
||||||
|
Pod=ai.pod
|
||||||
|
|
||||||
|
# Vulkan image for AMD GPU
|
||||||
|
Image=localhost/stable-diffusion-cpp:latest
|
||||||
|
|
||||||
|
# Shared models directory
|
||||||
|
Volume=/home/ai/models:/models:z
|
||||||
|
|
||||||
|
# GPU Device
|
||||||
|
AddDevice=/dev/kfd
|
||||||
|
AddDevice=/dev/dri
|
||||||
|
|
||||||
|
# Override entrypoint to use server
|
||||||
|
Entrypoint=/sd-server
|
||||||
|
|
||||||
|
# Server args
|
||||||
|
Exec=-l 0.0.0.0 \
|
||||||
|
--listen-port 1234 \
|
||||||
|
--diffusion-model /models/image/z-turbo/z_image_turbo-Q4_K.gguf \
|
||||||
|
--vae /models/image/z-turbo/ae.safetensors \
|
||||||
|
--llm /models/image/z-turbo/qwen_3_4b.safetensors \
|
||||||
|
-l 0.0.0.0 \
|
||||||
|
--listen-port 1234 \
|
||||||
|
--cfg-scale 1.0 \
|
||||||
|
--vae-conv-direct \
|
||||||
|
-v \
|
||||||
|
--seed -1 \
|
||||||
|
--steps 8
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
# Extend Timeout to allow time to pull the image
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
# Start by default on boot
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
20
active/device_framework_desktop/update-script.sh
Normal file
20
active/device_framework_desktop/update-script.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
export BUILD_TAG=$(date +"%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
|
echo "Updates stable-diffusion.cpp, llama.cpp, and open-webui"
|
||||||
|
|
||||||
|
cd /home/ai/llama.cpp
|
||||||
|
git pull
|
||||||
|
podman build -t llama-cpp-vulkan:${BUILD_TAG} -t llama-cpp-vulkan:latest -f .devops/vulkan.Dockerfile .
|
||||||
|
|
||||||
|
cd /home/ai/stable-diffusion.cpp
|
||||||
|
git pull
|
||||||
|
git submodule update --init --recursive
|
||||||
|
podman build -f Dockerfile.vulkan -t stable-diffusion-cpp:${BUILD_TAG} -t stable-diffusion-cpp:latest .
|
||||||
|
|
||||||
|
podman image pull ghcr.io/open-webui/open-webui:main
|
||||||
|
|
||||||
|
systemctl --user restart ai-pod
|
||||||
@@ -1,15 +1,51 @@
|
|||||||
# Home Assistant
|
# Home Assistant
|
||||||
|
|
||||||
- [Home Assistant](#home-assistant)
|
- [Home Assistant](#home-assistant)
|
||||||
|
- [Certificates](#certificates)
|
||||||
- [Setup and Configuration](#setup-and-configuration)
|
- [Setup and Configuration](#setup-and-configuration)
|
||||||
- [Schlage Door Lock](#schlage-door-lock)
|
- [Schlage Door Lock](#schlage-door-lock)
|
||||||
- [Philips Hue Lights](#philips-hue-lights)
|
- [Philips Hue Lights](#philips-hue-lights)
|
||||||
- [Shelly](#shelly)
|
- [Shelly](#shelly)
|
||||||
|
- [Barometer](#barometer)
|
||||||
- [Relative Humidity Calculator](#relative-humidity-calculator)
|
- [Relative Humidity Calculator](#relative-humidity-calculator)
|
||||||
- [Font Colors](#font-colors)
|
- [Font Colors](#font-colors)
|
||||||
- [Light Indicator for Voice Assistant](#light-indicator-for-voice-assistant)
|
- [Light Indicator for Voice Assistant](#light-indicator-for-voice-assistant)
|
||||||
- [Blank Button (Spacer)](#blank-button-spacer)
|
- [Blank Button (Spacer)](#blank-button-spacer)
|
||||||
- [Roku Remote](#roku-remote)
|
- [Roku Remote](#roku-remote)
|
||||||
|
- [Flair Vent Battery](#flair-vent-battery)
|
||||||
|
- [Voice](#voice)
|
||||||
|
- [Changing the Voice of TTS](#changing-the-voice-of-tts)
|
||||||
|
- [Custom Sentences](#custom-sentences)
|
||||||
|
- [Overriding Default Sentences](#overriding-default-sentences)
|
||||||
|
- [Notifications](#notifications)
|
||||||
|
- [Unifi Cameras](#unifi-cameras)
|
||||||
|
- [Multiple Entity Triggers with Custom Names](#multiple-entity-triggers-with-custom-names)
|
||||||
|
- [Philips Hue Switches](#philips-hue-switches)
|
||||||
|
- [Datetimes](#datetimes)
|
||||||
|
- [LG TV Switch](#lg-tv-switch)
|
||||||
|
|
||||||
|
## Certificates
|
||||||
|
|
||||||
|
Note, self signed certs won't work on the hass android app.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate the key/cert
|
||||||
|
# Note, 36159 days == 99 years
|
||||||
|
openssl req \
|
||||||
|
-sha256 \
|
||||||
|
-addext "subjectAltName = IP:10.2.0.230" \
|
||||||
|
-newkey rsa:4096 \
|
||||||
|
-nodes \
|
||||||
|
-keyout privkey.pem \
|
||||||
|
-x509 \
|
||||||
|
-days 36159 \
|
||||||
|
-out fullchain.pem
|
||||||
|
|
||||||
|
http:
|
||||||
|
server_port: 8123
|
||||||
|
ssl_certificate: /ssl/fullchain.pem
|
||||||
|
ssl_key: /ssl/privkey.pem
|
||||||
|
```
|
||||||
|
|
||||||
## Setup and Configuration
|
## Setup and Configuration
|
||||||
|
|
||||||
@@ -48,7 +84,51 @@ is quicker to pick up and transmit device information. Note that "gateway mode"
|
|||||||
just enable bluetooth and rpc or select "active" from the configuration menu for the shelly
|
just enable bluetooth and rpc or select "active" from the configuration menu for the shelly
|
||||||
device.
|
device.
|
||||||
|
|
||||||
### Relative Humidity Calculator
|
#### Barometer
|
||||||
|
|
||||||
|
<https://www.thoughtco.com/how-to-read-a-barometer-3444043>
|
||||||
|
|
||||||
|
A barometric reading over 30.20 inHg is generally considered high, and high pressure is associated with clear skies and calm weather.
|
||||||
|
|
||||||
|
If the reading is over 30.20 inHg (102268.9 Pa or 1022.689 mb):
|
||||||
|
|
||||||
|
- Rising or steady pressure means continued fair weather.
|
||||||
|
- Slowly falling pressure means fair weather.
|
||||||
|
- Rapidly falling pressure means cloudy and warmer conditions.
|
||||||
|
|
||||||
|
A barometric reading in the range of 29.80 and 30.20 inHg can be considered normal, and normal pressure is associated with steady weather.
|
||||||
|
|
||||||
|
If the reading falls between 29.80 and 30.20 inHg (100914.4–102268.9 Pa or 1022.689–1009.144 mb):
|
||||||
|
|
||||||
|
- Rising or steady pressure means present conditions will continue.
|
||||||
|
- Slowly falling pressure means little change in the weather.
|
||||||
|
- Rapidly falling pressure means that rain is likely, or snow if it is cold enough.
|
||||||
|
|
||||||
|
A barometric reading below 29.80 inHg is generally considered low, and low pressure is associated with warm air and rainstorms.
|
||||||
|
|
||||||
|
If the reading is under 29.80 inHg (100914.4 Pa or 1009.144 mb):
|
||||||
|
|
||||||
|
- Rising or steady pressure indicates clearing and cooler weather.
|
||||||
|
- Slowly falling pressure indicates rain.
|
||||||
|
- Rapidly falling pressure indicates a storm is coming.
|
||||||
|
|
||||||
|
A basic automation would look like
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
It's {{ int(states("sensor.grouse_temp")) }} degrees and {{ states("weather.grouse_weather") }}. The relative humidity is {{ states("sensor.grouse_humidity") }}%. I'm seeing {{ int(states("sensor.grouse_wind_speed")) }}mph wind with gusts up to {{ int(states("sensor.grouse_wind_gust")) }}mph.
|
||||||
|
|
||||||
|
{% set pressure = float(states("sensor.grouse_rel_pressure")) %}
|
||||||
|
The barometer reads {{ pressure }}inHg.
|
||||||
|
{% if pressure > 30.20 %}
|
||||||
|
Fair weather is expected
|
||||||
|
{% elif pressure > 29.80 %}
|
||||||
|
Rain is possible
|
||||||
|
{% else %}
|
||||||
|
Rain is coming.
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Relative Humidity Calculator
|
||||||
|
|
||||||
<https://www.wikihow.com/Calculate-Humidity>
|
<https://www.wikihow.com/Calculate-Humidity>
|
||||||
|
|
||||||
@@ -336,3 +416,295 @@ cards:
|
|||||||
action: none
|
action: none
|
||||||
title: Left Living Room TV
|
title: Left Living Room TV
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Flair Vent Battery
|
||||||
|
|
||||||
|
Flair vents report low battery at 2.4v. 3v is nominal/full.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{% set volt_min=2.4 %}
|
||||||
|
{% set volt_max=3.0 %}
|
||||||
|
{% set volt_diff_max=0.6 %}
|
||||||
|
|
||||||
|
{{ (min(float(states("sensor.main_bedroom_29bf_voltage")) - volt_min, volt_diff_max) / volt_diff_max) * 100 }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Voice
|
||||||
|
|
||||||
|
### Changing the Voice of TTS
|
||||||
|
|
||||||
|
Select a media player -> play TTS -> select voice -> copy voice ID.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
options:
|
||||||
|
voice: DavisNeural||chat
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Sentences
|
||||||
|
|
||||||
|
<https://developers.home-assistant.io/docs/voice/intent-recognition/template-sentence-syntax/#sentence-templates-syntax>
|
||||||
|
|
||||||
|
### Overriding Default Sentences
|
||||||
|
|
||||||
|
1. Identify if your sentence conflicts with [Home Assistant's default
|
||||||
|
sentences](https://github.com/OHF-Voice/intents/tree/main/sentences/en)
|
||||||
|
2. Create a new file at `/config/custom_sentences/en/overrides.yaml`
|
||||||
|
3. As an example, to override the `HassGetWeather` sentence:
|
||||||
|
1. Copy the contents of `weather_HassGetWeather.yaml` into `overrides.yaml`
|
||||||
|
2. Rename `HassGetWeather` to `HassGetWeather_Custom`
|
||||||
|
3. Delete the required context `weather`
|
||||||
|
4. Now in `configuration.yaml`, under a section called `intent_script`, add the following
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
HassGetWeather_Custom:
|
||||||
|
speech:
|
||||||
|
text: >-
|
||||||
|
It's {{ int(states("sensor.backyard_weather_station_temp")) }} degrees
|
||||||
|
with {{ states("sensor.backyard_weather_station_humidity") }}% humidity.
|
||||||
|
I'm seeing {{ int(states("sensor.backyard_weather_station_wind_speed"))
|
||||||
|
}}mph wind. It's rained {{
|
||||||
|
int(states("sensor.backyard_weather_station_hourly_rain_rate")) }} inches
|
||||||
|
in the last hour.
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Restart Home Assistant
|
||||||
|
6. Navigate to Settings -> Voice Assistants -> Click the 3 dots next to
|
||||||
|
your voice assistant -> Debug -> Click the icon in the top right -> Run
|
||||||
|
text pipeline -> "What's the weather"
|
||||||
|
|
||||||
|
## Notifications
|
||||||
|
|
||||||
|
Notification Information:
|
||||||
|
|
||||||
|
<https://www.home-assistant.io/docs/automation/templating/>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Triggered by {{ trigger.entity_id }}, Date: {{ now().strftime('%Y-%m-%d') }}, Time: {{ now().strftime('%H:%M') }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unifi Cameras
|
||||||
|
|
||||||
|
Create image/video previews of events with the following automation:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: Vehicle Driveway Notification
|
||||||
|
description: Sends a notification with video upon motion detection.
|
||||||
|
triggers:
|
||||||
|
- entity_id:
|
||||||
|
- binary_sensor.driveway_camera_vehicle_detected
|
||||||
|
trigger: state
|
||||||
|
from: "on"
|
||||||
|
to: "off"
|
||||||
|
actions:
|
||||||
|
- data:
|
||||||
|
message: Vehicle detected on Driveway Camera
|
||||||
|
data:
|
||||||
|
image: >-
|
||||||
|
/api/unifiprotect/thumbnail/{{ config_entry_id(trigger.entity_id)
|
||||||
|
}}/{{ trigger.from_state.attributes.event_id }}
|
||||||
|
video: >-
|
||||||
|
/api/unifiprotect/video/{{ config_entry_id(trigger.entity_id) }}/{{
|
||||||
|
trigger.from_state.attributes.event_id }}
|
||||||
|
action: notify.notify
|
||||||
|
mode: single
|
||||||
|
max_exceeded: silent
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple Entity Triggers with Custom Names
|
||||||
|
|
||||||
|
You can set an "id" for a trigger that can be used as a human readable name.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: Notify when a Door Opened
|
||||||
|
description: ""
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- binary_sensor.my_front_door
|
||||||
|
from: "off"
|
||||||
|
to: "on"
|
||||||
|
id: Front Door
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- binary_sensor.my_back_door
|
||||||
|
from: "off"
|
||||||
|
to: "on"
|
||||||
|
id: Back Door
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- binary_sensor.super_secret_door
|
||||||
|
from: "off"
|
||||||
|
to: "on"
|
||||||
|
id: Trap Door
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: notify.notify
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
message: "{{ trigger.id }} Opened"
|
||||||
|
mode: single
|
||||||
|
```
|
||||||
|
|
||||||
|
## Philips Hue Switches
|
||||||
|
|
||||||
|
Philips Hue Switches don't expose entities, but rather trigger "zha_event" events.
|
||||||
|
|
||||||
|
To see events fired by these devices: Developer tools -> Events -> Listen to events `zha_event`
|
||||||
|
|
||||||
|
You can use this in automations like so:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: Some Switch
|
||||||
|
description: ""
|
||||||
|
triggers:
|
||||||
|
- device_id: bb54b111ec77fb7d5356bb600789098f
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: turn_on
|
||||||
|
trigger: device
|
||||||
|
id: "on"
|
||||||
|
- device_id: bb54b111ec77fb7d5356bb600789098f
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_long_press
|
||||||
|
subtype: turn_on
|
||||||
|
trigger: device
|
||||||
|
id: on-con
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.some_scene
|
||||||
|
mode: single
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datetimes
|
||||||
|
|
||||||
|
Stolen from Reddit
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
## Set placeholder templates for reference in this template
|
||||||
|
## 'dt' substitutes 'now()'
|
||||||
|
## eg. if currently 5 March 2024 at 09:08:07 (AM)
|
||||||
|
eg_now = {% set eg_now = "2024-03-05 09:08:07.123456+00:00" %}{{ eg_now }}
|
||||||
|
dt = {% set dt = eg_now | as_datetime %}{{ dt }}
|
||||||
|
ts = {% set ts = eg_now | as_timestamp %}{{ ts }}
|
||||||
|
|
||||||
|
## Basic Time & Date Functions
|
||||||
|
time_now: {{ now() }}
|
||||||
|
time_local: {{ now() | as_local }}
|
||||||
|
time_timestamp: {{ now() | as_timestamp }}
|
||||||
|
|
||||||
|
## Time Conversions
|
||||||
|
seconds_per_min : {% set spm = 60 | int %}{{ spm }}
|
||||||
|
seconds_per_hour: {% set sph = ( spm * 60 ) | int %}{{ sph }}
|
||||||
|
seconds_per_day : {% set spd = 86400 | int %}{{ spd }}
|
||||||
|
seconds_per_week: {% set spw = ( spd * 7 ) | int %}{{ spw }}
|
||||||
|
minutes_per_day : {% set mpd = ( spd / 60 ) | int %}{{ mpd }}
|
||||||
|
minutes_per_week: {% set mpw = ( mpd * 7 ) | int %}{{ mpw }}
|
||||||
|
hours_per_week : {% set hpw = ( 24 * 7 ) | int %}{{ hpw }}
|
||||||
|
|
||||||
|
## Time Calculations
|
||||||
|
## with DATETIME use timedelta:
|
||||||
|
* CURRENT TIME : {{ dt }}
|
||||||
|
+ 1 YEAR : {{ dt + timedelta(days=365) }}
|
||||||
|
- 1 DAY (24H) : {{ dt - timedelta(days=1) }}
|
||||||
|
+ 3 DAYS (72H) : {{ dt + timedelta(days=3) }}
|
||||||
|
- 3 HOURS : {{ dt - timedelta(hours=3) }}
|
||||||
|
+ 1 HR 26 MIN : {{ dt + timedelta(hours=1, minutes=26) }}
|
||||||
|
+ 1D 2H 3M 4S : {{ dt + timedelta(days=1, hours=2, minutes=3, seconds=4) }}
|
||||||
|
|
||||||
|
## with TIMESTAMP use maths and then convert:
|
||||||
|
## Referencing earlier calculations for ease
|
||||||
|
* TIMESTAMP : {{ ts }}
|
||||||
|
* CURRENT TIME : {{ ts | as_datetime }}
|
||||||
|
+ 1 YEAR : {{ ( ts + (spd * 365) ) | as_datetime }}
|
||||||
|
- 1 DAY (24H) : {{ ( ts - spd ) | as_datetime }}
|
||||||
|
+ 3 DAYS (72H) : {{ ( ts + (spd * 3) ) | as_datetime }}
|
||||||
|
- 3 HOURS : {{ ( ts - (sph * 3) ) | as_datetime }}
|
||||||
|
+ 1 HR 26 MIN : {{ ( ts + sph + (spm * 26) ) | as_datetime }}
|
||||||
|
+ 1D 2H 3M 4S : {{ ( ts + spd + (sph * 2) + (spm * 3) + 4 ) | as_datetime }}
|
||||||
|
|
||||||
|
## Adjusting Time & Date For Calculations
|
||||||
|
Start Of Today: {% set start_today = dt.replace(hour=0, minute=0, second=0, microsecond=0) %}{{ start_today }}
|
||||||
|
End Of Today : {% set start_tomorrow = start_today + timedelta(days=1) %}{{ start_tomorrow }}
|
||||||
|
|
||||||
|
## Use Relative Time For DATETIME in the PAST
|
||||||
|
relative_time: {{ relative_time( start_today ) }} ago
|
||||||
|
|
||||||
|
## For time in the FUTURE you can use:
|
||||||
|
{% set current_time = dt %}{% set future_time = as_local(dt) %}{% set time_distance = future_time - current_time %}
|
||||||
|
relative_future: In {{ relative_time(current_time - time_distance) }}
|
||||||
|
|
||||||
|
## Use Time Templates combined with History Stats Sensor:
|
||||||
|
sensor:
|
||||||
|
- platform: history_stats
|
||||||
|
name: Lamp ON today
|
||||||
|
entity_id: light.my_lamp
|
||||||
|
state: "on"
|
||||||
|
```
|
||||||
|
|
||||||
|
Stolen from <https://www.fabriziomusacchio.com/blog/2021-08-15-strftime_Cheat_Sheet/>
|
||||||
|
|
||||||
|
| Format | Example | Description |
|
||||||
|
| ------ | ------------------------ | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| %c | Thu Jan 28 12:32:01 2014 | locale’s appropriate date and time representation |
|
||||||
|
| %D | 23/05/12 | formats the date |
|
||||||
|
| %F | 2002-01-30 | date in ISO 8601 format YYYY-MM-DD |
|
||||||
|
| %x | 02/10/11 | locale’s appropriate date representation |
|
||||||
|
| %X | 14:22:01 | locale’s appropriate time representation |
|
||||||
|
| %r | 3:44:12 AM | 12-hour time |
|
||||||
|
| %R | 15:21 | 24-hour time HH:MM |
|
||||||
|
| %T | 15:21:59 | time in ISO 8601 format HH:MM:SS |
|
||||||
|
| %A | Monday | full weekday name |
|
||||||
|
| %a | Mon | abbreviated weekday name |
|
||||||
|
| %w | 0-6 | day of the week with Sunday as 0 |
|
||||||
|
| %d | 01-31 | day of the month (with a leading zero) |
|
||||||
|
| %e | 1-31 | day of the month (without a leading zero) |
|
||||||
|
| %B | April | full month name |
|
||||||
|
| %b | Apr | abbreviated month name |
|
||||||
|
| %m | 01-12 | month of the year (with a leading zero) |
|
||||||
|
| %-m | 1-12 | month of the year (without a leading zero) |
|
||||||
|
| %Y | 2003 | year |
|
||||||
|
| %y | 00-99 | year without a century (last two digits, with a leading zero) |
|
||||||
|
| %-y | 0-99 | year without a century (last two digits, without a leading zero) |
|
||||||
|
| %H | 00-23 | hour of the day, 24-hour time (with a leading zero) |
|
||||||
|
| %k | 0-23 | hour of the day, 24-hour time (without a leading zero) |
|
||||||
|
| %I | 01-11 | hour of the day, 12-hour time (with a leading zero) |
|
||||||
|
| %-I | 1-11 | hour of the day, 12-hour time (without a leading zero) |
|
||||||
|
| %P | am, pm | am or pm designation |
|
||||||
|
| %p | AM, PM | AM or PM designation |
|
||||||
|
| %M | 00-59 | minute of the hour (with a leading zero) |
|
||||||
|
| %-M | 0-59 | minute of the hour (without a leading zero) |
|
||||||
|
| %S | 00-60 | second of the minute (with a leading zero) |
|
||||||
|
| %-S | 0-60 | second of the minute (without a leading zero) |
|
||||||
|
| %f | 000000-999999 | microsecond of the second (with a leading zero) |
|
||||||
|
| %Z | UTC | timezone name or abbreviation |
|
||||||
|
| %z | +0000 | UTC offset in the form +HHMM or -HHMM |
|
||||||
|
| %s | | amount of seconds since 1970-01-01 00:00:00 UTC |
|
||||||
|
| %% | | % sign |
|
||||||
|
| %j | 001-366 | day of the year (with a leading zeroes) |
|
||||||
|
| %U | 00-53 | week number with the first Sunday as the first day of week one |
|
||||||
|
| %W | 00-53 | week number of the current year, starting with the first Monday as the first day of the first week |
|
||||||
|
| %V | 01-53 | week number in ISO 8601 format |
|
||||||
|
|
||||||
|
## LG TV Switch
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- platform: wake_on_lan
|
||||||
|
mac: b4:b2:91:8e:ce:20
|
||||||
|
name: loft_lg_tv_wol
|
||||||
|
turn_off:
|
||||||
|
service: media_player.turn_off
|
||||||
|
target:
|
||||||
|
device_id: "{{device_id('media_player.loft_lg_tv')}}"
|
||||||
|
|
||||||
|
- platform: wake_on_lan
|
||||||
|
mac: 60:8d:26:2c:4d:45
|
||||||
|
name: living_room_lg_tv_wol
|
||||||
|
turn_off:
|
||||||
|
service: media_player.turn_off
|
||||||
|
target:
|
||||||
|
device_id: "{{device_id('media_player.living_room_lg_tv')}}"
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,5 +1,47 @@
|
|||||||
# Shelly Devices
|
# Shelly Devices
|
||||||
|
|
||||||
|
- [Shelly Devices](#shelly-devices)
|
||||||
|
- [1PM Mini Gen4](#1pm-mini-gen4)
|
||||||
|
- [Setup 1PM Mini Gen4](#setup-1pm-mini-gen4)
|
||||||
|
- [Install 1PM Mini Gen4](#install-1pm-mini-gen4)
|
||||||
|
- [Shelly Plug US](#shelly-plug-us)
|
||||||
|
- [Shelly BLU Motion](#shelly-blu-motion)
|
||||||
|
- [Shelly BLU Door/Window](#shelly-blu-doorwindow)
|
||||||
|
- [Reset](#reset)
|
||||||
|
- [Shelly Flood](#shelly-flood)
|
||||||
|
|
||||||
|
## 1PM Mini Gen4
|
||||||
|
|
||||||
|
### Setup 1PM Mini Gen4
|
||||||
|
|
||||||
|
1. Cut 1 white and 3 black pieces of 14 gauge wire to 3" long.
|
||||||
|
2. Strip 1/4" from one side of each wire.
|
||||||
|
3. Strip 1/2" from the other side of each wire.
|
||||||
|
4. Connect the 1/4" side to the shelly. Tighten the screws until you can't turn them.
|
||||||
|
5. Push line and neutral into a standard outlet. The wider receptacle is neutral.
|
||||||
|
6. Press and hold the button for 10 seconds to factory reset. Light will flash on/off every 1/4 second.
|
||||||
|
7. Press and hold the button for 5 seconds to turn on AP mode. Light will flash on/off every 1/2 second.
|
||||||
|
8. Connect to shelly network.
|
||||||
|
9. Navigate to <http://192.168.33.1>.
|
||||||
|
10. Connect to wifi. The light should turn solid.
|
||||||
|
11. Update firmware.
|
||||||
|
12. Set a password for AP mode.
|
||||||
|
13. Turn off the AP.
|
||||||
|
14. In Unifi: Name the device, give it a fixed IP, set the icon.
|
||||||
|
15. Navigate to the Shelly website via its IP address.
|
||||||
|
16. Set a password for http access: Settings -> Authentication.
|
||||||
|
17. Name the device: Settings -> Device name.
|
||||||
|
18. Set Restore last known state of output/relay: Home -> Output -> Input/Output Settings.
|
||||||
|
19. Enable Zigbee: Zigbee -> Enable.
|
||||||
|
20. Connect Shelly to Home Assistant via Zigbee.
|
||||||
|
21. Change switch type: Click on switch control -> Settings -> Show as.
|
||||||
|
|
||||||
|
### Install 1PM Mini Gen4
|
||||||
|
|
||||||
|
1. Cut 1 3" white wire for neutral bridge.
|
||||||
|
2. Cut 2 3" black wires for line bridge and light switch input.
|
||||||
|
3. Prepare 4 14 gauge wire connectors.
|
||||||
|
|
||||||
## Shelly Plug US
|
## Shelly Plug US
|
||||||
|
|
||||||
1. Connect to WiFi
|
1. Connect to WiFi
|
||||||
@@ -11,8 +53,36 @@
|
|||||||
7. Enable Bluetooth Gateway
|
7. Enable Bluetooth Gateway
|
||||||
8. Update Firmware
|
8. Update Firmware
|
||||||
|
|
||||||
|
## Shelly BLU Motion
|
||||||
|
|
||||||
|
1. Download and install the Shelly Debug app
|
||||||
|
2. Follow the instructions in the app to connect the device
|
||||||
|
3. Update the firmware
|
||||||
|
4. Enable encryption (generate a 6 digit code)
|
||||||
|
5. "Read" from the device and copy the encryption key for home assistant
|
||||||
|
|
||||||
|
## Shelly BLU Door/Window
|
||||||
|
|
||||||
|
1. Download and install the Shelly Debug app
|
||||||
|
2. Follow the instructions in the app to connect the device
|
||||||
|
3. Update the firmware
|
||||||
|
4. Create a new "login" in Bitwarden called "Shelly BLU DW " + name of device
|
||||||
|
1. Password will be the encryption key
|
||||||
|
2. Website should be the MAC address of the Shelly
|
||||||
|
5. Generate a 6 digit code, send it to your phone then throw it away
|
||||||
|
6. In the Shelly Debug app, enable encryption using the 6 digit code
|
||||||
|
7. Copy the encryption and store in the password field
|
||||||
|
8. Add to Home Assistant
|
||||||
|
9. Unpair from Phone
|
||||||
|
|
||||||
### Reset
|
### Reset
|
||||||
|
|
||||||
Resetting is super finnicky. You'll need to plug it in, press and hold the power button until the
|
Resetting is super finnicky. You'll need to plug it in, press and hold the power button until the
|
||||||
red light flashes quickly (not slowly, that's a reboot). You'll probably have to do it multiple
|
red light flashes quickly (not slowly, that's a reboot). You'll probably have to do it multiple
|
||||||
times because they seem to reboot halfway through the reset process.
|
times because they seem to reboot halfway through the reset process.
|
||||||
|
|
||||||
|
## Shelly Flood
|
||||||
|
|
||||||
|
1. In the web interface, ensure "CoIoT" is enabled and pointing to `<home assistant ip>:5683`.
|
||||||
|
Allow 5683/udp from shelly flood to home assistant. If you don't do this Shelly Flood will
|
||||||
|
not report its status correctly!
|
||||||
@@ -1,7 +1,99 @@
|
|||||||
# Yubikey
|
# Yubikey
|
||||||
|
|
||||||
|
- [Yubikey](#yubikey)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [Software](#software)
|
||||||
|
- [GPG](#gpg)
|
||||||
|
- [Saving GPG key to card](#saving-gpg-key-to-card)
|
||||||
|
- [Using the GPG key on a Yubikey](#using-the-gpg-key-on-a-yubikey)
|
||||||
|
- [Factory Reset](#factory-reset)
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
1. You will likely need the [udev
|
1. You will likely need the [udev
|
||||||
rules](https://support.yubico.com/hc/en-us/articles/360013708900-Using-Your-YubiKey-with-Linux)
|
rules](https://support.yubico.com/hc/en-us/articles/360013708900-Using-Your-YubiKey-with-Linux)
|
||||||
to use the AppImage configuration tool on linux even if your udev version is above 244.
|
to use the AppImage configuration tool on linux even if your udev version is above 244.
|
||||||
|
|
||||||
|
## Software
|
||||||
|
|
||||||
|
The [Yubikey Manager](https://www.yubico.com/support/download/yubikey-manager/) is deprecated.
|
||||||
|
|
||||||
|
Use the [Yubikey Authenticator](https://www.yubico.com/products/yubico-authenticator/) for GUI.
|
||||||
|
|
||||||
|
## GPG
|
||||||
|
|
||||||
|
### Saving GPG key to card
|
||||||
|
|
||||||
|
<https://support.yubico.com/hc/en-us/articles/360013790259-Using-Your-YubiKey-with-OpenPGP>
|
||||||
|
|
||||||
|
On Fedora you'll need to add the following polkit rules to access your smart card.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export MY_USER=ducoterra
|
||||||
|
echo <<EOF > /etc/polkit-1/rules.d/10-pcsc-custom.rules
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if (action.id == "org.debian.pcsc-lite.access_pcsc" &&
|
||||||
|
subject.user == "${MY_USER}") {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if (action.id == "org.debian.pcsc-lite.access_card" &&
|
||||||
|
action.lookup("reader") == 'Yubico YubiKey OTP+FIDO+CCID 00 00' &&
|
||||||
|
subject.user == "${MY_USER}") {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can add your key to your card.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gpg --edit-key 1234ABC
|
||||||
|
|
||||||
|
# Save both the signature and authentication keys
|
||||||
|
> keytocard
|
||||||
|
|
||||||
|
# Do not save or your key will be deleted locally
|
||||||
|
> quit
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the keys on the yubikey with
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gpg --card-status
|
||||||
|
```
|
||||||
|
|
||||||
|
Once your keys have been loaded, change the pin.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gpg --change-pin
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the GPG key on a Yubikey
|
||||||
|
|
||||||
|
<https://github.com/drduh/YubiKey-Guide?tab=readme-ov-file#notes>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GPG_EMAIL='myemail@example.com'
|
||||||
|
|
||||||
|
# Import the public key. Without this the key won't show up.
|
||||||
|
gpg --auto-key-locate hkps://keys.openpgp.org --locate-keys ${GPG_EMAIL}
|
||||||
|
|
||||||
|
# Trust the key
|
||||||
|
gpg --quick-set-ownertrust ${GPG_EMAIL} full
|
||||||
|
|
||||||
|
# Yubikey should now show up
|
||||||
|
gpg --list-secret-keys
|
||||||
|
```
|
||||||
|
|
||||||
|
### Factory Reset
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gpg --edit-card
|
||||||
|
|
||||||
|
> admin
|
||||||
|
> factory-reset
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
# Kubernetes
|
# Kubernetes
|
||||||
|
|
||||||
See [k3s](/active/systemd_k3s/k3s.md)
|
## CLI Tools
|
||||||
|
|
||||||
|
kubectl: <https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/>
|
||||||
|
|
||||||
|
helm: <https://helm.sh/docs/intro/install/>
|
||||||
|
|
||||||
|
## Install a Kubernetes Server
|
||||||
|
|
||||||
|
For k3s, see [k3s](/active/systemd_k3s/k3s.md)
|
||||||
|
|
||||||
|
For k0s, see [k0s](/active/systemd_k0s/k0s.md)
|
||||||
11
active/kubernetes_gitea/gitea-demo-values.yaml
Normal file
11
active/kubernetes_gitea/gitea-demo-values.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
service:
|
||||||
|
http:
|
||||||
|
type: LoadBalancer
|
||||||
|
externalTrafficPolicy: Cluster
|
||||||
|
annotations:
|
||||||
|
metallb.io/allow-shared-ip: gitea
|
||||||
|
ssh:
|
||||||
|
type: LoadBalancer
|
||||||
|
externalTrafficPolicy: Cluster
|
||||||
|
annotations:
|
||||||
|
metallb.io/allow-shared-ip: gitea
|
||||||
@@ -21,14 +21,10 @@ ingress:
|
|||||||
persistence:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
create: true
|
create: true
|
||||||
storageClass: zfs-iscsi-enc0
|
|
||||||
claimName: data-gitea-staging-0
|
claimName: data-gitea-staging-0
|
||||||
annotations:
|
annotations:
|
||||||
"helm.sh/resource-policy": keep
|
"helm.sh/resource-policy": keep
|
||||||
|
|
||||||
global:
|
|
||||||
storageClass: zfs-iscsi-enc1
|
|
||||||
|
|
||||||
postgresql:
|
postgresql:
|
||||||
enabled: true
|
enabled: true
|
||||||
image:
|
image:
|
||||||
@@ -36,7 +32,6 @@ postgresql:
|
|||||||
primary:
|
primary:
|
||||||
persistence:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
storageClass: zfs-iscsi-enc1
|
|
||||||
annotations:
|
annotations:
|
||||||
"helm.sh/resource-policy": keep
|
"helm.sh/resource-policy": keep
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Gitea
|
# Gitea
|
||||||
|
|
||||||
- [Gitea](#gitea)
|
- [Gitea](#gitea)
|
||||||
|
- [Demo](#demo)
|
||||||
- [Staging](#staging)
|
- [Staging](#staging)
|
||||||
- [Install](#install)
|
- [Install](#install)
|
||||||
- [Backup and Restore](#backup-and-restore)
|
- [Backup and Restore](#backup-and-restore)
|
||||||
@@ -14,6 +15,17 @@ they decide to change things. This is the first chart (besides ingress-nginx) wh
|
|||||||
we need to pay attention to the MetalLB annotation. This has been set in the values.yaml
|
we need to pay attention to the MetalLB annotation. This has been set in the values.yaml
|
||||||
file.
|
file.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade --install \
|
||||||
|
gitea \
|
||||||
|
gitea-charts/gitea \
|
||||||
|
--values active/kubernetes_gitea/gitea-demo-values.yaml \
|
||||||
|
--namespace gitea \
|
||||||
|
--create-namespace
|
||||||
|
```
|
||||||
|
|
||||||
## Staging
|
## Staging
|
||||||
|
|
||||||
There is a `gitea-staging.yaml` file with staging values. This should be installed in
|
There is a `gitea-staging.yaml` file with staging values. This should be installed in
|
||||||
|
|||||||
@@ -10,8 +10,14 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Download the updated template from github
|
# Download the updated template from github
|
||||||
kubectl kustomize "github.com/rancher/local-path-provisioner/deploy?ref=v0.0.31" > active/kubernetes_local-path-provisioner/local-path-storage.yaml
|
kubectl kustomize "github.com/rancher/local-path-provisioner/deploy?ref=v0.0.32" > active/kubernetes_local-path-provisioner/local-path-storage.yaml
|
||||||
|
|
||||||
# Apply customizations (ssd/hdd storage, read write many support)
|
# Apply customizations (ssd/hdd storage, read write many support)
|
||||||
kubectl kustomize active/kubernetes_local-path-provisioner | kubectl apply -f -
|
kubectl kustomize active/kubernetes_local-path-provisioner | kubectl apply -f -
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Mark the class as default
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
|
||||||
|
```
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ spec:
|
|||||||
fieldPath: metadata.namespace
|
fieldPath: metadata.namespace
|
||||||
- name: CONFIG_MOUNT_PATH
|
- name: CONFIG_MOUNT_PATH
|
||||||
value: /etc/config/
|
value: /etc/config/
|
||||||
image: rancher/local-path-provisioner:v0.0.31
|
image: rancher/local-path-provisioner:v0.0.32
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
name: local-path-provisioner
|
name: local-path-provisioner
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ below installs nimcraft. For each installation you'll want to create your own va
|
|||||||
with a new port. The server-downloader is called "minecraft_get_server" and is available on
|
with a new port. The server-downloader is called "minecraft_get_server" and is available on
|
||||||
[Github](https://github.com/ducoterra/minecraft_get_server).
|
[Github](https://github.com/ducoterra/minecraft_get_server).
|
||||||
|
|
||||||
|
After installing, you can run admin commands (like whitelisting players) by
|
||||||
|
attaching to the container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl attach -it <pod>
|
||||||
|
|
||||||
|
> /whitelist add ducoterra
|
||||||
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ spec:
|
|||||||
value: "1"
|
value: "1"
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: {{ div .Values.max_ram 2 }}Gi
|
memory: "{{ div .Values.max_ram 2 }}Gi"
|
||||||
cpu: 1m
|
cpu: 1m
|
||||||
limits:
|
limits:
|
||||||
memory: {{ add 1 .Values.max_ram }}Gi
|
memory: "{{ add 1 .Values.max_ram }}Gi"
|
||||||
cpu: {{ .Values.max_cpu | quote }}
|
cpu: {{ .Values.max_cpu | quote }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: data
|
- name: data
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
"helm.sh/resource-policy": keep
|
"helm.sh/resource-policy": keep
|
||||||
spec:
|
spec:
|
||||||
storageClassName: ssd
|
|
||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteOnce
|
- ReadWriteOnce
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}
|
name: {{ .Release.Name }}
|
||||||
annotations:
|
|
||||||
metallb.universe.tf/address-pool: "external"
|
|
||||||
external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}.reeseapps.com
|
|
||||||
spec:
|
spec:
|
||||||
ipFamilies: ["IPv6"]
|
|
||||||
externalTrafficPolicy: Cluster
|
externalTrafficPolicy: Cluster
|
||||||
selector:
|
selector:
|
||||||
app: {{ .Release.Name }}
|
app: {{ .Release.Name }}
|
||||||
|
|||||||
21
active/notes_freedesktop/freedesktop.md
Normal file
21
active/notes_freedesktop/freedesktop.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Freedesktop Notes
|
||||||
|
|
||||||
|
Notes pertaining to the `.desktop` extension and desktop entries.
|
||||||
|
|
||||||
|
## Keys
|
||||||
|
|
||||||
|
<https://specifications.freedesktop.org/desktop-entry-spec/1.5/recognized-keys.html>
|
||||||
|
|
||||||
|
## Icons
|
||||||
|
|
||||||
|
<https://freedesktop.org/wiki/Specifications/icon-theme-spec/>
|
||||||
|
|
||||||
|
By default, apps should look in $HOME/.icons (for backwards compatibility), in $XDG_DATA_DIRS/icons and in /usr/share/pixmaps (in that order).
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Run a `.desktop` file from terminal with this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gtk-launch gnome-system-monitor.desktop
|
||||||
|
```
|
||||||
@@ -11,9 +11,9 @@ instructions for building a:
|
|||||||
|
|
||||||
- [Arch Base](#arch-base)
|
- [Arch Base](#arch-base)
|
||||||
- [Table of Contents](#table-of-contents)
|
- [Table of Contents](#table-of-contents)
|
||||||
- [Installation](#installation)
|
- [Installing Arch](#installing-arch)
|
||||||
- [Preparation](#preparation)
|
- [Preparation](#preparation)
|
||||||
- [Installation](#installation-1)
|
- [Installation](#installation)
|
||||||
- [Post Install](#post-install)
|
- [Post Install](#post-install)
|
||||||
- [Backup (or restore)](#backup-or-restore)
|
- [Backup (or restore)](#backup-or-restore)
|
||||||
- [Base Tools](#base-tools)
|
- [Base Tools](#base-tools)
|
||||||
@@ -58,7 +58,7 @@ instructions for building a:
|
|||||||
- [VLAN Setup](#vlan-setup)
|
- [VLAN Setup](#vlan-setup)
|
||||||
- [Date and Time](#date-and-time)
|
- [Date and Time](#date-and-time)
|
||||||
|
|
||||||
## Installation
|
## Installing Arch
|
||||||
|
|
||||||
### Preparation
|
### Preparation
|
||||||
|
|
||||||
@@ -70,10 +70,15 @@ Follow most of the instructions here: <https://wiki.archlinux.org/title/Installa
|
|||||||
```bash
|
```bash
|
||||||
gpg --auto-key-locate clear,wkd -v --locate-external-key pierre@archlinux.org
|
gpg --auto-key-locate clear,wkd -v --locate-external-key pierre@archlinux.org
|
||||||
gpg --keyserver-options auto-key-retrieve --verify archlinux-...
|
gpg --keyserver-options auto-key-retrieve --verify archlinux-...
|
||||||
|
gpg --verify signature_file.sig archlinux.iso
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Create a bootable ISO <https://wiki.archlinux.org/title/USB_flash_installation_medium>
|
3. Create a bootable ISO <https://wiki.archlinux.org/title/USB_flash_installation_medium>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dd bs=4M if=path/to/archlinux-version-x86_64.iso of=/dev/disk/by-id/usb-My_flash_drive conv=fsync oflag=direct status=progress
|
||||||
|
```
|
||||||
|
|
||||||
1. If you are booting into a VM, create an ISO with installation files so you don't have to
|
1. If you are booting into a VM, create an ISO with installation files so you don't have to
|
||||||
copy-paste:
|
copy-paste:
|
||||||
|
|
||||||
|
|||||||
@@ -210,35 +210,7 @@ Now the status should be correct even after connecting/disconnecting when the co
|
|||||||
|
|
||||||
## SSH
|
## SSH
|
||||||
|
|
||||||
Generate a key with password protection:
|
See [README](/README.md#ssh-setup)
|
||||||
|
|
||||||
```bash
|
|
||||||
# Omit "-N 'password'" to have it prompt you
|
|
||||||
ssh-keygen -f ~/.ssh/test-key -N 'PASSWORD'
|
|
||||||
```
|
|
||||||
|
|
||||||
Change the password for an ssh key:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Use "-N ''" to remove the password
|
|
||||||
ssh-keygen -p -N 'PASSWORD' -f ~/.ssh/test-key
|
|
||||||
```
|
|
||||||
|
|
||||||
This is an example config entry in `~/.ssh/config`:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
Host my-host
|
|
||||||
Hostname my-host.reeselink.com
|
|
||||||
User root
|
|
||||||
ProxyCommand none
|
|
||||||
ForwardAgent no
|
|
||||||
ForwardX11 no
|
|
||||||
Port 22
|
|
||||||
KeepAlive yes
|
|
||||||
IdentityFile ~/.ssh/id_my-host_rsa
|
|
||||||
```
|
|
||||||
|
|
||||||
You can ssh to that host with `ssh my-host` after adding a config entry.
|
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
@@ -652,7 +624,6 @@ Custom profiles are located at
|
|||||||
|
|
||||||
Sync this with something like Nextcloud.
|
Sync this with something like Nextcloud.
|
||||||
|
|
||||||
|
|
||||||
## Orca Slicer
|
## Orca Slicer
|
||||||
|
|
||||||
<https://github.com/SoftFever/OrcaSlicer>
|
<https://github.com/SoftFever/OrcaSlicer>
|
||||||
|
|||||||
@@ -14,55 +14,7 @@ and the operator will store information about each server.
|
|||||||
|
|
||||||
## Setup SSH
|
## Setup SSH
|
||||||
|
|
||||||
On the operator:
|
See [README](/README.md#ssh-setup)
|
||||||
|
|
||||||
```bash
|
|
||||||
export SSH_HOST=kube
|
|
||||||
ssh-keygen -t rsa -b 4096 -C ${USER}@${HOSTNAME} -f ~/.ssh/id_${SSH_HOST}_rsa
|
|
||||||
|
|
||||||
# Note: If you get "too many authentication failures" it's likely because you have too many private
|
|
||||||
# keys in your ~/.ssh directory. Use `-o PubkeyAuthentication` to fix it.
|
|
||||||
ssh-copy-id -o PubkeyAuthentication=no -i ~/.ssh/id_${SSH_HOST}_rsa.pub ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
ssh -i ~/.ssh/id_${SSH_HOST}_rsa -o 'PubkeyAuthentication=yes' ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
```
|
|
||||||
|
|
||||||
On the server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy authorized_keys to root
|
|
||||||
sudo cp ~/.ssh/authorized_keys /root/.ssh/authorized_keys
|
|
||||||
|
|
||||||
# Change your password
|
|
||||||
passwd
|
|
||||||
|
|
||||||
sudo su -
|
|
||||||
echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/01-prohibit-password.conf
|
|
||||||
echo '%sudo ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/01-nopasswd-sudo
|
|
||||||
systemctl restart ssh
|
|
||||||
```
|
|
||||||
|
|
||||||
On the operator:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat <<EOF >> ~/.ssh/config
|
|
||||||
|
|
||||||
Host $SSH_HOST
|
|
||||||
Hostname ${SSH_HOST}.reeselink.com
|
|
||||||
User root
|
|
||||||
ProxyCommand none
|
|
||||||
ForwardAgent no
|
|
||||||
ForwardX11 no
|
|
||||||
Port 22
|
|
||||||
KeepAlive yes
|
|
||||||
IdentityFile ~/.ssh/id_${SSH_HOST}_rsa
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Test if you can SSH with a password
|
|
||||||
ssh -o PubkeyAuthentication=no ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
|
|
||||||
# Test that you can log into the server with ssh config
|
|
||||||
ssh $SSH_HOST
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fail2Ban
|
## Fail2Ban
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
- [Fedora Kinoite](#fedora-kinoite)
|
- [Fedora Kinoite](#fedora-kinoite)
|
||||||
- [TPM2 Luks Decryption](#tpm2-luks-decryption)
|
- [TPM2 Luks Decryption](#tpm2-luks-decryption)
|
||||||
- [Podman](#podman)
|
- [Podman](#podman)
|
||||||
- [Autostarting services with quadlets](#autostarting-services-with-quadlets)
|
- [Docker Compose and Docker Buildkit with Rootless Podman](#docker-compose-and-docker-buildkit-with-rootless-podman)
|
||||||
- [rpm-ostree](#rpm-ostree)
|
- [rpm-ostree](#rpm-ostree)
|
||||||
- [Git, Vim, etc](#git-vim-etc)
|
- [Git, Vim, etc](#git-vim-etc)
|
||||||
- [Libvirt, Qemu, KVM](#libvirt-qemu-kvm)
|
- [Libvirt, Qemu, KVM](#libvirt-qemu-kvm)
|
||||||
@@ -71,6 +71,19 @@ export REGISTRY_AUTH_FILE=$HOME/.podman-auth.json
|
|||||||
|
|
||||||
Source that and then run `podman login` to create the file.
|
Source that and then run `podman login` to create the file.
|
||||||
|
|
||||||
|
### Docker Compose and Docker Buildkit with Rootless Podman
|
||||||
|
|
||||||
|
Allows you to use podman with full docker-compose compatibility.
|
||||||
|
|
||||||
|
<https://emersion.fr/blog/2025/using-podman-compose-and-buildkit/>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rpm-ostree install docker-compose docker-buildx
|
||||||
|
reboot
|
||||||
|
systemctl --user enable --now podman.socket
|
||||||
|
docker context create podman --docker host=unix://$XDG_RUNTIME_DIR/podman/podman.sock
|
||||||
|
docker context use podman
|
||||||
|
|
||||||
### Autostarting services with quadlets
|
### Autostarting services with quadlets
|
||||||
|
|
||||||
If you want to run something as your user at boot (like a systemd process, think ollama) you can
|
If you want to run something as your user at boot (like a systemd process, think ollama) you can
|
||||||
|
|||||||
@@ -2,22 +2,28 @@
|
|||||||
|
|
||||||
- [Fedora Server](#fedora-server)
|
- [Fedora Server](#fedora-server)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
|
- [Power Profiles with Tuned](#power-profiles-with-tuned)
|
||||||
- [Setup SSH](#setup-ssh)
|
- [Setup SSH](#setup-ssh)
|
||||||
- [DNF](#dnf)
|
- [DNF](#dnf)
|
||||||
- [Fail2Ban](#fail2ban)
|
- [Fail2Ban](#fail2ban)
|
||||||
- [BTRFS Parent Volumes](#btrfs-parent-volumes)
|
- [BTRFS Parent Volumes](#btrfs-parent-volumes)
|
||||||
- [BTRFS Snapshots](#btrfs-snapshots)
|
- [BTRFS Snapshots](#btrfs-snapshots)
|
||||||
|
- [Snapper Installation](#snapper-installation)
|
||||||
|
- [Snapper Cleanup](#snapper-cleanup)
|
||||||
|
- [BTRFS Maintenance](#btrfs-maintenance)
|
||||||
- [TPM2 Luks Decryption](#tpm2-luks-decryption)
|
- [TPM2 Luks Decryption](#tpm2-luks-decryption)
|
||||||
- [Change your password](#change-your-password)
|
- [Change your password](#change-your-password)
|
||||||
- [Automatic Updates](#automatic-updates)
|
- [Automatic Updates](#automatic-updates)
|
||||||
- [Monitoring](#monitoring)
|
- [Monitoring](#monitoring)
|
||||||
|
- [Glances](#glances)
|
||||||
- [Disk Usage](#disk-usage)
|
- [Disk Usage](#disk-usage)
|
||||||
- [Disk Wear](#disk-wear)
|
- [Disk Wear](#disk-wear)
|
||||||
- [Common Storage Mounts](#common-storage-mounts)
|
- [Common Storage Mounts](#common-storage-mounts)
|
||||||
- [Network Bridge](#network-bridge)
|
- [Network Bridge](#network-bridge)
|
||||||
- [Virtualization](#virtualization)
|
- [Virtualization](#virtualization)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Virtualization Troubleshooting](#virtualization-troubleshooting)
|
||||||
- [QEMU Images](#qemu-images)
|
- [QEMU Images](#qemu-images)
|
||||||
|
- [Shared directory with VM Guest](#shared-directory-with-vm-guest)
|
||||||
- [Firewalld](#firewalld)
|
- [Firewalld](#firewalld)
|
||||||
- [Backups](#backups)
|
- [Backups](#backups)
|
||||||
- [Connect to the ISCSI Backup Target](#connect-to-the-iscsi-backup-target)
|
- [Connect to the ISCSI Backup Target](#connect-to-the-iscsi-backup-target)
|
||||||
@@ -27,10 +33,8 @@
|
|||||||
- [Troubleshooting Backup ISCSI Connection](#troubleshooting-backup-iscsi-connection)
|
- [Troubleshooting Backup ISCSI Connection](#troubleshooting-backup-iscsi-connection)
|
||||||
- [Quick Backup](#quick-backup)
|
- [Quick Backup](#quick-backup)
|
||||||
- [Regular Backups with Borg](#regular-backups-with-borg)
|
- [Regular Backups with Borg](#regular-backups-with-borg)
|
||||||
|
- [Version Upgrades](#version-upgrades)
|
||||||
- [Optional Steps](#optional-steps)
|
- [Optional Steps](#optional-steps)
|
||||||
- [Docker with Podman as Runtime](#docker-with-podman-as-runtime)
|
|
||||||
- [Vanilla Docker](#vanilla-docker)
|
|
||||||
- [Extra Software](#extra-software)
|
|
||||||
- [Disable Swap](#disable-swap)
|
- [Disable Swap](#disable-swap)
|
||||||
- [Disable Selinux](#disable-selinux)
|
- [Disable Selinux](#disable-selinux)
|
||||||
- [Downgrading Kernel](#downgrading-kernel)
|
- [Downgrading Kernel](#downgrading-kernel)
|
||||||
@@ -39,7 +43,7 @@
|
|||||||
- [LVM Thin Provisioning](#lvm-thin-provisioning)
|
- [LVM Thin Provisioning](#lvm-thin-provisioning)
|
||||||
- [Set eui64 on network interface](#set-eui64-on-network-interface)
|
- [Set eui64 on network interface](#set-eui64-on-network-interface)
|
||||||
- [Install and Enable Cockpit](#install-and-enable-cockpit)
|
- [Install and Enable Cockpit](#install-and-enable-cockpit)
|
||||||
- [Troubleshooting](#troubleshooting-1)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Cockpit Terminal Unusable or Weird Colors](#cockpit-terminal-unusable-or-weird-colors)
|
- [Cockpit Terminal Unusable or Weird Colors](#cockpit-terminal-unusable-or-weird-colors)
|
||||||
- [Chroot into a mounted disk](#chroot-into-a-mounted-disk)
|
- [Chroot into a mounted disk](#chroot-into-a-mounted-disk)
|
||||||
- [Resize Last Partition to Fill Available Space](#resize-last-partition-to-fill-available-space)
|
- [Resize Last Partition to Fill Available Space](#resize-last-partition-to-fill-available-space)
|
||||||
@@ -71,50 +75,15 @@ and the operator will store information about each server.
|
|||||||
5. Take note of the ipv4 and ipv6 address. Update any DNS records at this time.
|
5. Take note of the ipv4 and ipv6 address. Update any DNS records at this time.
|
||||||
6. Install and reboot
|
6. Install and reboot
|
||||||
|
|
||||||
|
## Power Profiles with Tuned
|
||||||
|
|
||||||
|
1. `dnf install tuned`
|
||||||
|
2. `systemctl enable --now tuned`
|
||||||
|
3. `tuned-adm profile virtual-host`
|
||||||
|
|
||||||
## Setup SSH
|
## Setup SSH
|
||||||
|
|
||||||
In this setup we'll allow ssh to the root user via key and keep the admin user for cockpit.
|
See [README](/README.md#ssh-setup)
|
||||||
|
|
||||||
On the operator:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export SSH_HOST=kube
|
|
||||||
ssh-keygen -C ${USER}@${HOSTNAME} -f ~/.ssh/id_${SSH_HOST}_rsa
|
|
||||||
|
|
||||||
# Note: If you get "too many authentication failures" it's likely because you have too many private
|
|
||||||
# keys in your ~/.ssh directory. Use `-o PubkeyAuthentication` to fix it.
|
|
||||||
ssh-copy-id -o PubkeyAuthentication=no -i ~/.ssh/id_${SSH_HOST}_rsa.pub ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
ssh -i ~/.ssh/id_${SSH_HOST}_rsa ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
# Copy authorized_keys to root
|
|
||||||
sudo cp ~/.ssh/authorized_keys /root/.ssh/authorized_keys
|
|
||||||
exit
|
|
||||||
|
|
||||||
cat <<EOF >> ~/.ssh/config
|
|
||||||
|
|
||||||
Host ${SSH_HOST}
|
|
||||||
Hostname ${SSH_HOST}.reeselink.com
|
|
||||||
User root
|
|
||||||
Port 22
|
|
||||||
KeepAlive yes
|
|
||||||
IdentityFile ~/.ssh/id_${SSH_HOST}_rsa
|
|
||||||
EOF
|
|
||||||
|
|
||||||
ssh ${SSH_HOST}
|
|
||||||
# Disable password auth
|
|
||||||
echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/01-prohibit-password.conf
|
|
||||||
systemctl restart sshd
|
|
||||||
|
|
||||||
# OPTIONAL: Disable sudo password
|
|
||||||
echo '%wheel ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/01-nopasswd-wheel
|
|
||||||
|
|
||||||
exit
|
|
||||||
|
|
||||||
# Test if you can SSH with a password
|
|
||||||
ssh -o PubkeyAuthentication=no ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
|
|
||||||
# Test that you can log into the server with ssh config
|
|
||||||
ssh $SSH_HOST
|
|
||||||
```
|
|
||||||
|
|
||||||
## DNF
|
## DNF
|
||||||
|
|
||||||
@@ -193,6 +162,8 @@ mount -a --mkdir
|
|||||||
|
|
||||||
<http://snapper.io/manpages/snapper-configs.html>
|
<http://snapper.io/manpages/snapper-configs.html>
|
||||||
|
|
||||||
|
### Snapper Installation
|
||||||
|
|
||||||
We'll be using snapper, a tool for automating and controlling snapshot behavior.
|
We'll be using snapper, a tool for automating and controlling snapshot behavior.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -212,18 +183,37 @@ systemctl enable --now snapper-timeline.timer
|
|||||||
systemctl enable --now snapper-cleanup.timer
|
systemctl enable --now snapper-cleanup.timer
|
||||||
# Enable snapshots on boot
|
# Enable snapshots on boot
|
||||||
systemctl enable --now snapper-boot.timer
|
systemctl enable --now snapper-boot.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Snapper Cleanup
|
||||||
|
|
||||||
|
```bash
|
||||||
# List snapshots
|
# List snapshots
|
||||||
snapper -c root list
|
snapper -c root list
|
||||||
# Create snapshot manually
|
# Create snapshot manually
|
||||||
snapper -c root create --description "test snapshot"
|
snapper -c root create --description "test snapshot"
|
||||||
# Delete first snapshot
|
# Delete first snapshot
|
||||||
snapper -c root delete 1
|
snapper -c root delete 1
|
||||||
|
# Delete snapshots between 655-857
|
||||||
|
snapper -c root delete 655-857
|
||||||
```
|
```
|
||||||
|
|
||||||
Note - you probably don't want to keep yearly snapshots.
|
Note - you probably don't want to keep yearly snapshots.
|
||||||
Edit `/etc/snapper/configs/root` and change `TIMELINE_LIMIT_YEARLY=` to `0`.
|
Edit `/etc/snapper/configs/root` and change `TIMELINE_LIMIT_YEARLY=` to `0`.
|
||||||
|
|
||||||
|
## BTRFS Maintenance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start a scrub with low impact/priority at / (good for servers)
|
||||||
|
btrfs scrub start -c idle /
|
||||||
|
|
||||||
|
# Start a scrub in the foreground and monitor
|
||||||
|
btrfs scrub start -c idle -B -d /
|
||||||
|
|
||||||
|
# Check for errors
|
||||||
|
dmesg -T | grep btrfs
|
||||||
|
```
|
||||||
|
|
||||||
## TPM2 Luks Decryption
|
## TPM2 Luks Decryption
|
||||||
|
|
||||||
Mostly taken from here:
|
Mostly taken from here:
|
||||||
@@ -314,9 +304,30 @@ In Cockpit navigate to software updates -> automatic updates -> install -> secur
|
|||||||
|
|
||||||
In Cockpit: Overview -> View metrics and history -> Install PCP Support -> Metrics settings -> Turn on Collect Metrics
|
In Cockpit: Overview -> View metrics and history -> Install PCP Support -> Metrics settings -> Turn on Collect Metrics
|
||||||
|
|
||||||
|
### Glances
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dnf install -y glances python3-jinja2
|
||||||
|
systemctl enable --now glances
|
||||||
|
firewall-cmd --permanent --zone=FedoraServer --add-port=61208/tcp
|
||||||
|
firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
### Disk Usage
|
### Disk Usage
|
||||||
|
|
||||||
TODO
|
```bash
|
||||||
|
# Show size of folder exclude snapshots
|
||||||
|
du --exclude .snapshots -sh .
|
||||||
|
|
||||||
|
# Show size of all files in your current dir
|
||||||
|
for folder in $(ls); do du --exclude .snapshots -sh $folder; done
|
||||||
|
|
||||||
|
# Calculate all folder sizes in current dir
|
||||||
|
alias {dudir,dud}='du -h --max-depth 1 | sort -h'
|
||||||
|
|
||||||
|
# Calculate all file sizes in current dir
|
||||||
|
alias {dufile,duf}='ls -lhSr'
|
||||||
|
```
|
||||||
|
|
||||||
### Disk Wear
|
### Disk Wear
|
||||||
|
|
||||||
@@ -324,7 +335,7 @@ TODO
|
|||||||
|
|
||||||
## Common Storage Mounts
|
## Common Storage Mounts
|
||||||
|
|
||||||
Note: mount these before you install the relavant package!
|
Note: mount these before you install the relevant package!
|
||||||
|
|
||||||
1. For virtual machines: `/var/lib/libvirt`
|
1. For virtual machines: `/var/lib/libvirt`
|
||||||
2. For podman: `/var/lib/containers`
|
2. For podman: `/var/lib/containers`
|
||||||
@@ -379,7 +390,7 @@ systemctl enable --now libvirtd
|
|||||||
|
|
||||||
Install the cockpit machines application.
|
Install the cockpit machines application.
|
||||||
|
|
||||||
### Troubleshooting
|
### Virtualization Troubleshooting
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Oops, I did this after I installed virtualization
|
# Oops, I did this after I installed virtualization
|
||||||
@@ -405,6 +416,12 @@ qemu-img convert -f vmdk -O raw in.vmdk out.img
|
|||||||
qemu-img convert -f qcow2 -O raw in.raw out.img
|
qemu-img convert -f qcow2 -O raw in.raw out.img
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Shared directory with VM Guest
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mount -t virtiofs [mount tag] [mount point]
|
||||||
|
```
|
||||||
|
|
||||||
## Firewalld
|
## Firewalld
|
||||||
|
|
||||||
Set the default firewalld zone to `public`
|
Set the default firewalld zone to `public`
|
||||||
@@ -417,6 +434,8 @@ Set the default firewalld zone to `public`
|
|||||||
Firewalld will be on and blocking by default. You can check the zone and allowed ports with:
|
Firewalld will be on and blocking by default. You can check the zone and allowed ports with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
firewall-cmd --get-active-zones
|
||||||
|
firewall-cmd --get-default-zone
|
||||||
firewall-cmd --zone=public --list-ports
|
firewall-cmd --zone=public --list-ports
|
||||||
firewall-cmd --zone=public --list-services
|
firewall-cmd --zone=public --list-services
|
||||||
```
|
```
|
||||||
@@ -428,6 +447,21 @@ firewall-cmd --permanent --zone=public --add-port=9090/tcp
|
|||||||
firewall-cmd --reload
|
firewall-cmd --reload
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Remove cockpit with
|
||||||
|
|
||||||
|
```bash
|
||||||
|
firewall-cmd --permanent --zone=public --remove-port=9090/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a custom source for a service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo firewall-cmd --new-zone=home --permanent
|
||||||
|
sudo firewall-cmd --zone=home --add-source=10.2.0.0/24 --permanent
|
||||||
|
sudo firewall-cmd --zone=home --add-port=10700/tcp --permanent
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
## Backups
|
## Backups
|
||||||
|
|
||||||
Note: this assumes you've set up [an iscsi backup disk](/active/os_truenas/truenas.md#iscsi-backup-volumes)
|
Note: this assumes you've set up [an iscsi backup disk](/active/os_truenas/truenas.md#iscsi-backup-volumes)
|
||||||
@@ -459,7 +493,7 @@ iscsiadm -m node \
|
|||||||
systemctl restart iscsid
|
systemctl restart iscsid
|
||||||
|
|
||||||
# Discover targets
|
# Discover targets
|
||||||
iscsiadm -m discovery -t st -p driveripper.reeselink.com
|
iscsiadm -m discovery -t st -p drivework.reeselink.com
|
||||||
|
|
||||||
# Login to all nodes
|
# Login to all nodes
|
||||||
iscsiadm -m node -l
|
iscsiadm -m node -l
|
||||||
@@ -519,46 +553,20 @@ rsync -av --progress --exclude '.snapshots' /btrfs/yellow/root /btrfs/backup-yel
|
|||||||
|
|
||||||
See [borg.md](/active/systemd_borg/borg.md)
|
See [borg.md](/active/systemd_borg/borg.md)
|
||||||
|
|
||||||
|
## Version Upgrades
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Make sure to be fully up to date first
|
||||||
|
dnf upgrade --refresh
|
||||||
|
reboot
|
||||||
|
|
||||||
|
# Set the releasever to the version you want to upgrade to
|
||||||
|
dnf system-upgrade download --releasever=43
|
||||||
|
dnf system-upgrade reboot
|
||||||
|
```
|
||||||
|
|
||||||
## Optional Steps
|
## Optional Steps
|
||||||
|
|
||||||
### Docker with Podman as Runtime
|
|
||||||
|
|
||||||
Note, you'll need to ssh into the server as the user in order to start the user's systemd session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf install podman docker docker-compose
|
|
||||||
sudo loginctl enable-linger 1000 # Or whatever user
|
|
||||||
|
|
||||||
systemctl --user enable --now podman.socket
|
|
||||||
docker context create podman --docker host=unix://$XDG_RUNTIME_DIR/podman/podman.sock
|
|
||||||
docker context use podman
|
|
||||||
```
|
|
||||||
|
|
||||||
### Vanilla Docker
|
|
||||||
|
|
||||||
<https://docs.docker.com/engine/install/fedora/>
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dnf -y install dnf-plugins-core
|
|
||||||
dnf-3 config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
|
||||||
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
||||||
systemctl enable --now docker
|
|
||||||
```
|
|
||||||
|
|
||||||
### Extra Software
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Set vim as the default editor
|
|
||||||
dnf install -y vim-default-editor --allowerasing
|
|
||||||
|
|
||||||
# Install glances for system monitoring
|
|
||||||
dnf install -y glances
|
|
||||||
|
|
||||||
# ZSH
|
|
||||||
dnf install -y zsh
|
|
||||||
chsh -s $(which zsh) && chsh -s $(which zsh) ducoterra
|
|
||||||
```
|
|
||||||
|
|
||||||
### Disable Swap
|
### Disable Swap
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -658,6 +666,8 @@ mkfs.xfs /dev/mapper/vg0-docker--data
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
nmcli connection modify Wired\ connection\ 1 ipv6.addr-gen-mode eui64
|
nmcli connection modify Wired\ connection\ 1 ipv6.addr-gen-mode eui64
|
||||||
|
nmcli connection modify Wired\ connection\ 1 ipv6.ip6-privacy disabled
|
||||||
|
systemctl restart NetworkManager
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install and Enable Cockpit
|
### Install and Enable Cockpit
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# Apps
|
# Fedora Software
|
||||||
|
|
||||||
- [Apps](#apps)
|
- [Fedora Software](#fedora-software)
|
||||||
|
- [Common CLI Apps](#common-cli-apps)
|
||||||
|
- [Podman](#podman)
|
||||||
- [Gear Lever](#gear-lever)
|
- [Gear Lever](#gear-lever)
|
||||||
- [VSCode](#vscode)
|
- [VSCode](#vscode)
|
||||||
- [DNF](#dnf)
|
- [DNF](#dnf)
|
||||||
@@ -47,6 +49,17 @@
|
|||||||
- [KDiskMark](#kdiskmark)
|
- [KDiskMark](#kdiskmark)
|
||||||
- [Local Send](#local-send)
|
- [Local Send](#local-send)
|
||||||
- [Evolution](#evolution)
|
- [Evolution](#evolution)
|
||||||
|
- [Virtualization](#virtualization)
|
||||||
|
- [NVM](#nvm)
|
||||||
|
- [Ollama](#ollama)
|
||||||
|
- [UV](#uv)
|
||||||
|
- [Pipenv](#pipenv)
|
||||||
|
- [Docker](#docker)
|
||||||
|
- [Boxes](#boxes)
|
||||||
|
- [ffmpeg](#ffmpeg)
|
||||||
|
- [AMD GPU VAAPI ffmpeg Acceleration](#amd-gpu-vaapi-ffmpeg-acceleration)
|
||||||
|
- [Containers](#containers)
|
||||||
|
- [XSane](#xsane)
|
||||||
|
|
||||||
Flatpak installs are from Flathub unless otherwise noted.
|
Flatpak installs are from Flathub unless otherwise noted.
|
||||||
|
|
||||||
@@ -58,6 +71,70 @@ flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.f
|
|||||||
|
|
||||||
When prompted, prefer flathub.
|
When prompted, prefer flathub.
|
||||||
|
|
||||||
|
## Common CLI Apps
|
||||||
|
|
||||||
|
Some common apps you'll probably want available.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install \
|
||||||
|
# Advanced text editor for code editing and other tasks.
|
||||||
|
vim \
|
||||||
|
# Network bandwidth measuring tool.
|
||||||
|
iperf3 \
|
||||||
|
# Command-line interface for managing Kubernetes clusters.
|
||||||
|
kubectl \
|
||||||
|
# Package manager and deployment tool for Kubernetes.
|
||||||
|
helm \
|
||||||
|
# Utility to monitor real-time network usage of processes.
|
||||||
|
nethogs \
|
||||||
|
# DevOps configuration management tool.
|
||||||
|
ansible \
|
||||||
|
# Terminal multiplexer.
|
||||||
|
tmux \
|
||||||
|
# Microsoft Windows compatibility layer.
|
||||||
|
wine \
|
||||||
|
# Archive utility similar to GNU tar, used to package files into single archive files.
|
||||||
|
unzip \
|
||||||
|
# A terminal activity monitor (top clone).
|
||||||
|
btop \
|
||||||
|
# Command-line JSON processor.
|
||||||
|
jq \
|
||||||
|
# YAML-based configuration-as-code tool for command-line interfaces written in Go, Rust, Python, and more.
|
||||||
|
yq \
|
||||||
|
# An image manipulation software suite based on ImageMagick.
|
||||||
|
ImageMagick \
|
||||||
|
# The Go programming language environment including a toolchain (gc) and libraries.
|
||||||
|
go \
|
||||||
|
# Rust package manager and compiler installation utility.
|
||||||
|
rust rustup \
|
||||||
|
# Distributed version control system, Git extension that adds support for large files like multimedia assets.
|
||||||
|
git git-lfs \
|
||||||
|
# Provides traditional network tools such as ifconfig, netstat, hostname, etc., in a single package.
|
||||||
|
net-tools \
|
||||||
|
# Document conversion tool and markup language converter.
|
||||||
|
pandoc \
|
||||||
|
# Comprehensive LaTeX distribution for high-quality typesetting of documents.
|
||||||
|
texlive-latex texlive-scheme-full \
|
||||||
|
# Generate strong passwords.
|
||||||
|
pwgen \
|
||||||
|
# Reattach to running processes
|
||||||
|
reptyr \
|
||||||
|
# Netcat, for basic tcp/udp operations
|
||||||
|
netcat \
|
||||||
|
# 7zip support
|
||||||
|
p7zip \
|
||||||
|
# Make
|
||||||
|
make \
|
||||||
|
# GCC for compile
|
||||||
|
gcc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Podman
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dns install -y podman
|
||||||
|
```
|
||||||
|
|
||||||
## Gear Lever
|
## Gear Lever
|
||||||
|
|
||||||
I would recommend you install Gear Lever to manage App Images:
|
I would recommend you install Gear Lever to manage App Images:
|
||||||
@@ -132,6 +209,10 @@ flatpak install com.bitwarden.desktop
|
|||||||
Video player (like VLC but can frame-by-frame in reverse).
|
Video player (like VLC but can frame-by-frame in reverse).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# DNF
|
||||||
|
dnf install mpv
|
||||||
|
|
||||||
|
# Flatpak
|
||||||
flatpak install io.mpv.Mpv
|
flatpak install io.mpv.Mpv
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -296,6 +377,12 @@ At the very top of the config you can add a pin for a printer permanently with:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You'll need to open port 2021 udp
|
||||||
|
|
||||||
|
```bash
|
||||||
|
firewall-cmd --add-port=2021/udp --permanent
|
||||||
|
```
|
||||||
|
|
||||||
## Freecad
|
## Freecad
|
||||||
|
|
||||||
Benchy benchy benchy oh no, I can't do that, this is hard.
|
Benchy benchy benchy oh no, I can't do that, this is hard.
|
||||||
@@ -402,10 +489,11 @@ flatpak install io.github.thetumultuousunicornofdarkness.cpu-x
|
|||||||
|
|
||||||
## Ungoogled Chromium
|
## Ungoogled Chromium
|
||||||
|
|
||||||
Chrom
|
<https://github.com/ungoogled-software/ungoogled-chromium?tab=readme-ov-file#automated-or-maintained-builds>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
flatpak install io.github.ungoogled_software.ungoogled_chromium
|
sudo dnf copr enable wojnilowicz/ungoogled-chromium
|
||||||
|
sudo dnf install ungoogled-chromium
|
||||||
```
|
```
|
||||||
|
|
||||||
## Signal
|
## Signal
|
||||||
@@ -508,3 +596,125 @@ You still use email? I still use email.
|
|||||||
```bash
|
```bash
|
||||||
flatpak install org.gnome.Evolution
|
flatpak install org.gnome.Evolution
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Virtualization
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Virtualization
|
||||||
|
sudo dnf group install --with-optional virtualization
|
||||||
|
|
||||||
|
sudo systemctl enable --now libvirtd virtnetworkd.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## NVM
|
||||||
|
|
||||||
|
<https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating>
|
||||||
|
|
||||||
|
## Ollama
|
||||||
|
|
||||||
|
<https://ollama.com/download>
|
||||||
|
|
||||||
|
Run the installation script as normal. Make sure you have the [ROCM](#rocm)
|
||||||
|
drivers installed for GPU acceleration. The script *should* automatically pull
|
||||||
|
the ROCM drivers after installing the base packages. If not, you should install
|
||||||
|
them manually.
|
||||||
|
|
||||||
|
For starting ollama as a service, follow the link below:
|
||||||
|
|
||||||
|
<https://github.com/ollama/ollama/blob/main/docs/linux.md#adding-ollama-as-a-startup-service-recommended>
|
||||||
|
|
||||||
|
## UV
|
||||||
|
|
||||||
|
<https://docs.astral.sh/uv/getting-started/installation/>
|
||||||
|
|
||||||
|
## Pipenv
|
||||||
|
|
||||||
|
<https://pipenv.pypa.io/en/latest/installation.html#installing-pipenv>
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
<https://docs.docker.com/engine/install/fedora/>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dnf -y install dnf-plugins-core
|
||||||
|
dnf-3 config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
||||||
|
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
systemctl enable --now docker
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use docker with podman with full docker-compose compatibility.
|
||||||
|
|
||||||
|
<https://emersion.fr/blog/2025/using-podman-compose-and-buildkit/>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dnf install -y docker-compose docker-buildx
|
||||||
|
systemctl --user enable --now podman.socket
|
||||||
|
docker context create podman --docker host=unix://$XDG_RUNTIME_DIR/podman/podman.sock
|
||||||
|
docker context use podman
|
||||||
|
```
|
||||||
|
|
||||||
|
## Boxes
|
||||||
|
|
||||||
|
Virtualization at its boxiest.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flatpak install org.gnome.Boxes
|
||||||
|
```
|
||||||
|
|
||||||
|
## ffmpeg
|
||||||
|
|
||||||
|
- 1080p h264 at 10M is good quality
|
||||||
|
|
||||||
|
### AMD GPU VAAPI ffmpeg Acceleration
|
||||||
|
|
||||||
|
1. Enable [RPM Fusion](https://docs.fedoraproject.org/en-US/quick-docs/rpmfusion-setup/)
|
||||||
|
2. Install [ffmpeg non-free](https://rpmfusion.org/Howto/Multimedia)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable RPM Fusion
|
||||||
|
sudo dnf install \
|
||||||
|
https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
|
||||||
|
sudo dnf install \
|
||||||
|
https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
|
||||||
|
|
||||||
|
# Install ffmpeg non-free
|
||||||
|
sudo dnf swap ffmpeg-free ffmpeg --allowerasing
|
||||||
|
sudo dnf update @multimedia --setopt="install_weak_deps=False" --exclude=PackageKit-gstreamer-plugin
|
||||||
|
sudo dnf swap mesa-va-drivers mesa-va-drivers-freeworld
|
||||||
|
sudo dnf swap mesa-vdpau-drivers mesa-vdpau-drivers-freeworld
|
||||||
|
sudo dnf swap mesa-va-drivers.i686 mesa-va-drivers-freeworld.i686
|
||||||
|
sudo dnf swap mesa-vdpau-drivers.i686 mesa-vdpau-drivers-freeworld.i686
|
||||||
|
```
|
||||||
|
|
||||||
|
ffmpeg with vaapi
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ffmpeg \
|
||||||
|
-hwaccel vaapi \
|
||||||
|
-hwaccel_output_format vaapi \
|
||||||
|
-i VID_20250804_120159.mp4 \
|
||||||
|
-vf 'format=nv12,hwupload' \
|
||||||
|
-vf scale_vaapi=1080:1920 \
|
||||||
|
-c:v h264_vaapi \
|
||||||
|
-c:a copy \
|
||||||
|
-qp 18 \
|
||||||
|
VID_20250804_120159_1.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Containers
|
||||||
|
|
||||||
|
In order to enter a shell with systemd-user access via `machinectl`, install systemd-container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dnf install -y systemd-container
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can run `machinectl shell myuser@` to enter a shell which can execute `systemctl --user` commands.
|
||||||
|
|
||||||
|
## XSane
|
||||||
|
|
||||||
|
Scan stuff
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dnf install xsane
|
||||||
|
```
|
||||||
@@ -4,37 +4,29 @@
|
|||||||
- [Framework 16 Fixes](#framework-16-fixes)
|
- [Framework 16 Fixes](#framework-16-fixes)
|
||||||
- [Wake from Sleep](#wake-from-sleep)
|
- [Wake from Sleep](#wake-from-sleep)
|
||||||
- [Wrong keys pressed in the browser](#wrong-keys-pressed-in-the-browser)
|
- [Wrong keys pressed in the browser](#wrong-keys-pressed-in-the-browser)
|
||||||
- [Fix wifi disconnecting and reconnecting repeatedly on reboot/resume](#fix-wifi-disconnecting-and-reconnecting-repeatedly-on-rebootresume)
|
|
||||||
- [Wifi Powersave](#wifi-powersave)
|
|
||||||
- [Podman](#podman)
|
- [Podman](#podman)
|
||||||
- [Autostarting services with quadlets](#autostarting-services-with-quadlets)
|
- [Autostarting services with quadlets](#autostarting-services-with-quadlets)
|
||||||
- [Toolbox](#toolbox)
|
- [Toolbox](#toolbox)
|
||||||
- [Network](#network)
|
- [Network](#network)
|
||||||
|
- [Firewall](#firewall)
|
||||||
- [VLAN Setup with nmcli](#vlan-setup-with-nmcli)
|
- [VLAN Setup with nmcli](#vlan-setup-with-nmcli)
|
||||||
- [ZRAM](#zram)
|
- [ZRAM](#zram)
|
||||||
- [Libraries](#libraries)
|
- [Libraries](#libraries)
|
||||||
- [Common Libraries](#common-libraries)
|
- [Common Libraries](#common-libraries)
|
||||||
- [Apps](#apps)
|
|
||||||
- [Common CLI Apps](#common-cli-apps)
|
|
||||||
- [Ungoogled Chromium](#ungoogled-chromium)
|
|
||||||
- [VSCode](#vscode)
|
|
||||||
- [Virtualization](#virtualization)
|
|
||||||
- [NVM](#nvm)
|
|
||||||
- [Ollama](#ollama)
|
|
||||||
- [UV](#uv)
|
|
||||||
- [Pipenv](#pipenv)
|
|
||||||
- [Backups](#backups)
|
- [Backups](#backups)
|
||||||
- [BTRFS Snapshots](#btrfs-snapshots)
|
- [BTRFS Snapshots](#btrfs-snapshots)
|
||||||
- [ROCM](#rocm)
|
- [ROCM](#rocm)
|
||||||
- [Display](#display)
|
- [Display](#display)
|
||||||
- [Scripted Display Modes](#scripted-display-modes)
|
- [Scripted Display Modes](#scripted-display-modes)
|
||||||
|
- [Fixing generic Wayland icons on task alt tab](#fixing-generic-wayland-icons-on-task-alt-tab)
|
||||||
|
- [Tuned Power Profiles](#tuned-power-profiles)
|
||||||
|
|
||||||
## Framework 16 Fixes
|
## Framework 16 Fixes
|
||||||
|
|
||||||
### Wake from Sleep
|
### Wake from Sleep
|
||||||
|
|
||||||
The keyboard/mouse can be pressed through the lid while in a backpack. Disable them to
|
The keyboard/mouse can be pressed through the lid while in a backpack. Disable
|
||||||
prevent wake from sleep.
|
them to prevent wake from sleep.
|
||||||
|
|
||||||
`/etc/udev/rules.d/69-suspend.rules`
|
`/etc/udev/rules.d/69-suspend.rules`
|
||||||
|
|
||||||
@@ -55,61 +47,15 @@ sudo udevadm control --reload-rules && sudo udevadm trigger
|
|||||||
|
|
||||||
### Wrong keys pressed in the browser
|
### Wrong keys pressed in the browser
|
||||||
|
|
||||||
Sometimes keys will stop working when using search bars or do strange things like move the page around. This seems to be caused by some "alt" keypress combination. Pressing "alt" twice fixes it.
|
Sometimes keys will stop working when using search bars or do strange things
|
||||||
|
like move the page around. This seems to be caused by some "alt" keypress
|
||||||
### Fix wifi disconnecting and reconnecting repeatedly on reboot/resume
|
combination. Pressing "alt" twice fixes it.
|
||||||
|
|
||||||
Create a file in `/etc/systemd/system/reset-iwlwifi.service` with the following content:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
[Unit]
|
|
||||||
Description=Reload iwlwifi on wake-up
|
|
||||||
After=suspend.target
|
|
||||||
After=multi-user.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStartPre=rmmod iwlmvm iwlwifi
|
|
||||||
ExecStart=modprobe iwlwifi
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=suspend.target
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable reset-iwlwifi.service
|
|
||||||
```
|
|
||||||
|
|
||||||
### Wifi Powersave
|
|
||||||
|
|
||||||
**NOTE: THIS DOESN'T WORK. IT CAUSES WIFI DISCONNECT AND RECONNECT ISSUES.**
|
|
||||||
|
|
||||||
<https://www.networkmanager.dev/docs/api/latest/settings-802-11-wireless.html>
|
|
||||||
|
|
||||||
<https://gist.github.com/jcberthon/ea8cfe278998968ba7c5a95344bc8b55>
|
|
||||||
|
|
||||||
<https://askubuntu.com/questions/1230525/ubuntu-20-04-network-performance-extremely-slow>
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vim /etc/NetworkManager/conf.d/wifi-powersave-off.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
```conf
|
|
||||||
[connection]
|
|
||||||
# Values are 0 (use default), 1 (ignore/don't touch), 2 (disable) or 3 (enable).
|
|
||||||
wifi.powersave = 2
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
systemctl restart NetworkManager
|
|
||||||
```
|
|
||||||
|
|
||||||
## Podman
|
## Podman
|
||||||
|
|
||||||
Since you'll be using podman for most container-based services, you'll want to set the
|
Since you'll be using podman for most container-based services, you'll want to
|
||||||
the podman auth file to somewhere persistent, otherwise it'll get deleted every time you
|
set the the podman auth file to somewhere persistent, otherwise it'll get
|
||||||
reboot.
|
deleted every time you reboot.
|
||||||
|
|
||||||
Add this to your `.bashrc`:
|
Add this to your `.bashrc`:
|
||||||
|
|
||||||
@@ -122,8 +68,8 @@ Source that and then run `podman login` to create the file.
|
|||||||
|
|
||||||
### Autostarting services with quadlets
|
### Autostarting services with quadlets
|
||||||
|
|
||||||
If you want to run something as your user at boot (like a systemd process, think ollama) you can
|
If you want to run something as your user at boot (like a systemd process,
|
||||||
create a user quadlets like so:
|
think ollama) you can create a user quadlets like so:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate the .container file
|
# Generate the .container file
|
||||||
@@ -153,6 +99,72 @@ toolbox enter
|
|||||||
|
|
||||||
## Network
|
## Network
|
||||||
|
|
||||||
|
### Firewall
|
||||||
|
|
||||||
|
Set the default firewall to `drop`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo firewall-cmd --set-default-zone=drop
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
Allow KDE Connect via 1714-1764 tcp/udp
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set source address to allow connections
|
||||||
|
sudo firewall-cmd \
|
||||||
|
--zone=drop \
|
||||||
|
--permanent \
|
||||||
|
--add-port=1714-1764/udp \
|
||||||
|
--add-port=1714-1764/tcp
|
||||||
|
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check if the firewall is working via `nmap` from another machine
|
||||||
|
|
||||||
|
Note, add `-r` to scan ports in order.
|
||||||
|
|
||||||
|
Note, add `-vv` to increase verbosity.
|
||||||
|
|
||||||
|
Note, add `-A` to perform OS detection, host lookup, etc.
|
||||||
|
|
||||||
|
Note, use `-F` to perform a quick scan against common ports.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export NMAP_HOST=10.2.0.49
|
||||||
|
|
||||||
|
# Scan for common ports on TCP
|
||||||
|
nmap -sT $NMAP_HOST
|
||||||
|
|
||||||
|
# Scan all ports on TCP
|
||||||
|
nmap -sT -p- $NMAP_HOST
|
||||||
|
|
||||||
|
# Scan specific port on TCP
|
||||||
|
nmap -sT -p5432 $NMAP_HOST
|
||||||
|
|
||||||
|
# Scan range of ports on TCP
|
||||||
|
nmap -sT -p1024-9999 $NMAP_HOST
|
||||||
|
|
||||||
|
# Scan for common ports on UDP
|
||||||
|
nmap -sU $NMAP_HOST
|
||||||
|
|
||||||
|
# Skip host up checking
|
||||||
|
nmap -Pn -sT $NMAP_HOST
|
||||||
|
|
||||||
|
# Scan all ports for everything (takes a really really long time)
|
||||||
|
nmap -Pn -sT -sU -p- $NMAP_HOST
|
||||||
|
|
||||||
|
# Scan using TCP ACK Ping (More serious check that attempts to bypass firewall, See nmap man page)
|
||||||
|
nmap -PA -p- $NMAP_HOST
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, while running a scan:
|
||||||
|
|
||||||
|
v / V: Increase / decrease the verbosity level d / D: Increase / decrease the
|
||||||
|
debugging Level p / P: Turn on / off packet tracing ?: Print a runtime
|
||||||
|
interaction help screen
|
||||||
|
|
||||||
### VLAN Setup with nmcli
|
### VLAN Setup with nmcli
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -199,106 +211,6 @@ xz-devel \
|
|||||||
libgle-devel
|
libgle-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
## Apps
|
|
||||||
|
|
||||||
### Common CLI Apps
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf install \
|
|
||||||
# Advanced text editor for code editing and other tasks.
|
|
||||||
vim \
|
|
||||||
# Network bandwidth measuring tool.
|
|
||||||
iperf3 \
|
|
||||||
# Command-line interface for managing Kubernetes clusters.
|
|
||||||
kubectl \
|
|
||||||
# Package manager and deployment tool for Kubernetes.
|
|
||||||
helm \
|
|
||||||
# Utility to monitor real-time network usage of processes.
|
|
||||||
nethogs \
|
|
||||||
# DevOps configuration management tool.
|
|
||||||
ansible \
|
|
||||||
# Terminal multiplexer.
|
|
||||||
tmux \
|
|
||||||
# Multimedia player with support for a wide range of codecs and file formats.
|
|
||||||
ffmpeg \
|
|
||||||
# Microsoft Windows compatibility layer.
|
|
||||||
wine \
|
|
||||||
# Archive utility similar to GNU tar, used to package files into single archive files.
|
|
||||||
unzip \
|
|
||||||
# A terminal activity monitor (top clone).
|
|
||||||
btop \
|
|
||||||
# Command-line JSON processor.
|
|
||||||
jq \
|
|
||||||
# YAML-based configuration-as-code tool for command-line interfaces written in Go, Rust, Python, and more.
|
|
||||||
yq \
|
|
||||||
# An image manipulation software suite based on ImageMagick.
|
|
||||||
ImageMagick \
|
|
||||||
# The Go programming language environment including a toolchain (gc) and libraries.
|
|
||||||
go \
|
|
||||||
# Rust package manager and compiler installation utility.
|
|
||||||
rust rustup \
|
|
||||||
# Distributed version control system, Git extension that adds support for large files like multimedia assets.
|
|
||||||
git git-lfs \
|
|
||||||
# Provides traditional network tools such as ifconfig, netstat, hostname, etc., in a single package.
|
|
||||||
net-tools \
|
|
||||||
# Document conversion tool and markup language converter.
|
|
||||||
pandoc \
|
|
||||||
# Comprehensive LaTeX distribution for high-quality typesetting of documents.
|
|
||||||
texlive-latex \
|
|
||||||
# Generate strong passwords.
|
|
||||||
pwgen \
|
|
||||||
# Reattach to running processes
|
|
||||||
reptyr \
|
|
||||||
# Netcat, for basic tcp/udp operations
|
|
||||||
netcat \
|
|
||||||
# 7zip support
|
|
||||||
p7zip
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ungoogled Chromium
|
|
||||||
|
|
||||||
<https://github.com/ungoogled-software/ungoogled-chromium?tab=readme-ov-file#automated-or-maintained-builds>
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf copr enable wojnilowicz/ungoogled-chromium
|
|
||||||
sudo dnf install ungoogled-chromium
|
|
||||||
```
|
|
||||||
|
|
||||||
### VSCode
|
|
||||||
|
|
||||||
<https://code.visualstudio.com/docs/setup/linux#_rhel-fedora-and-centos-based-distributions>
|
|
||||||
|
|
||||||
### Virtualization
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Virtualization
|
|
||||||
sudo dnf group install --with-optional virtualization
|
|
||||||
```
|
|
||||||
|
|
||||||
### NVM
|
|
||||||
|
|
||||||
<https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating>
|
|
||||||
|
|
||||||
### Ollama
|
|
||||||
|
|
||||||
<https://ollama.com/download>
|
|
||||||
|
|
||||||
Run the installation script as normal. Make sure you have the [ROCM](#rocm) drivers installed
|
|
||||||
for GPU acceleration. The script *should* automatically pull the ROCM drivers after installing
|
|
||||||
the base packages. If not, you should install them manually.
|
|
||||||
|
|
||||||
For starting ollama as a service, follow the link below:
|
|
||||||
|
|
||||||
<https://github.com/ollama/ollama/blob/main/docs/linux.md#adding-ollama-as-a-startup-service-recommended>
|
|
||||||
|
|
||||||
### UV
|
|
||||||
|
|
||||||
<https://docs.astral.sh/uv/getting-started/installation/>
|
|
||||||
|
|
||||||
### Pipenv
|
|
||||||
|
|
||||||
<https://pipenv.pypa.io/en/latest/installation.html#installing-pipenv>
|
|
||||||
|
|
||||||
## Backups
|
## Backups
|
||||||
|
|
||||||
### BTRFS Snapshots
|
### BTRFS Snapshots
|
||||||
@@ -307,7 +219,8 @@ For starting ollama as a service, follow the link below:
|
|||||||
|
|
||||||
<http://snapper.io/manpages/snapper-configs.html>
|
<http://snapper.io/manpages/snapper-configs.html>
|
||||||
|
|
||||||
We'll be using snapper, a tool for automating and controlling snapshot behavior.
|
We'll be using snapper, a tool for automating and controlling snapshot
|
||||||
|
behavior.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dnf install snapper dnf-plugin-snapper
|
dnf install snapper dnf-plugin-snapper
|
||||||
@@ -335,8 +248,8 @@ snapper -c root create --description "test snapshot"
|
|||||||
snapper -c root delete 1
|
snapper -c root delete 1
|
||||||
```
|
```
|
||||||
|
|
||||||
Note - you probably don't want to keep yearly snapshots.
|
Note - you probably don't want to keep yearly snapshots. Edit
|
||||||
Edit `/etc/snapper/configs/root` and change `TIMELINE_LIMIT_YEARLY=` to `0`.
|
`/etc/snapper/configs/root` and change `TIMELINE_LIMIT_YEARLY=` to `0`.
|
||||||
|
|
||||||
## ROCM
|
## ROCM
|
||||||
|
|
||||||
@@ -401,3 +314,48 @@ output.eDP-2.primary \
|
|||||||
output.eDP-2.mode.1920x1080@165 \
|
output.eDP-2.mode.1920x1080@165 \
|
||||||
output.eDP-2.scale.1'
|
output.eDP-2.scale.1'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Fixing generic Wayland icons on task alt tab
|
||||||
|
|
||||||
|
<https://epergo.com/posts/fix-generic-wayland-icon/>
|
||||||
|
|
||||||
|
1. Access Window Rules
|
||||||
|
|
||||||
|
Go to “System Settings > Window Management > Window Rules”.
|
||||||
|
|
||||||
|
2. Create a New Rule (If None Exist)
|
||||||
|
|
||||||
|
If the application does not have any rules already, create a new one:
|
||||||
|
|
||||||
|
1. Click on “Add New…”
|
||||||
|
2. Add a description (e.g., “Application settings for sublime_merge”)
|
||||||
|
3. Specify the “Window class (application)”
|
||||||
|
|
||||||
|
If you’re unsure of the value for the window class, click “Detect Window
|
||||||
|
Properties”, then click on the application window. A pop-up with the
|
||||||
|
detected properties will be shown, and you can select the correct value.
|
||||||
|
|
||||||
|
3. Add Property
|
||||||
|
|
||||||
|
1. Click on “Add Property” and select “Desktop File Name”.
|
||||||
|
|
||||||
|
4. Find the Correct Desktop File Name
|
||||||
|
|
||||||
|
Standard Applications: If the application is installed using your distro’s
|
||||||
|
repositories, check the name in /usr/share/applications/. Flatpak
|
||||||
|
Applications: If it’s a Flatpak package, check the name in
|
||||||
|
/var/lib/flatpak/exports/share/applications/. For example, for Obsidian, it
|
||||||
|
will be md.obsidian.Obsidian (do not include the .desktop suffix).
|
||||||
|
|
||||||
|
5. Apply Settings
|
||||||
|
|
||||||
|
Apply the new settings and close the application if it was open. The next
|
||||||
|
time you open the application, it should show the correct icon.
|
||||||
|
|
||||||
|
## Tuned Power Profiles
|
||||||
|
|
||||||
|
Default profiles are in `/usr/lib/tuned/profiles`.
|
||||||
|
|
||||||
|
Configuration file is in `/etc/tuned/ppd.conf`.
|
||||||
|
|
||||||
|
Used `tuned-adm` CLI to interface with tuned.
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
kscreen-doctor \
|
|
||||||
output.eDP-2.enable \
|
|
||||||
output.eDP-2.position.0,0 \
|
|
||||||
output.eDP-2.primary \
|
|
||||||
output.eDP-2.mode.1920x1080@60 \
|
|
||||||
output.eDP-2.scale.1
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
kscreen-doctor \
|
|
||||||
output.eDP-2.enable \
|
|
||||||
output.eDP-2.position.0,0 \
|
|
||||||
output.eDP-2.primary \
|
|
||||||
output.eDP-2.mode.2560x1600@165 \
|
|
||||||
output.eDP-2.scale.1.25
|
|
||||||
14
active/os_fedora/selinux_policies/clamav-notifysend.te
Normal file
14
active/os_fedora/selinux_policies/clamav-notifysend.te
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
module clamav-notifysend 1.0;
|
||||||
|
|
||||||
|
require {
|
||||||
|
type session_dbusd_tmp_t;
|
||||||
|
type antivirus_t;
|
||||||
|
type unconfined_dbusd_t;
|
||||||
|
class sock_file write;
|
||||||
|
class unix_stream_socket connectto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#============= antivirus_t ==============
|
||||||
|
allow antivirus_t session_dbusd_tmp_t:sock_file write;
|
||||||
|
allow antivirus_t unconfined_dbusd_t:unix_stream_socket connectto;
|
||||||
29
active/os_fedora/selinux_policies/clamav-sudo.te
Normal file
29
active/os_fedora/selinux_policies/clamav-sudo.te
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
module clamav-sudo 1.0;
|
||||||
|
|
||||||
|
require {
|
||||||
|
type antivirus_t;
|
||||||
|
type sudo_exec_t;
|
||||||
|
type systemd_logind_var_run_t;
|
||||||
|
type pidfs_t;
|
||||||
|
type chkpwd_exec_t;
|
||||||
|
type systemd_logind_t;
|
||||||
|
class file { execute execute_no_trans map };
|
||||||
|
class netlink_audit_socket { create nlmsg_relay read write };
|
||||||
|
class capability { audit_write sys_resource };
|
||||||
|
class process { setrlimit setsched };
|
||||||
|
class sock_file write;
|
||||||
|
class unix_stream_socket connectto;
|
||||||
|
class filesystem getattr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#============= antivirus_t ==============
|
||||||
|
allow antivirus_t chkpwd_exec_t:file { execute execute_no_trans };
|
||||||
|
allow antivirus_t pidfs_t:filesystem getattr;
|
||||||
|
allow antivirus_t self:capability { audit_write sys_resource };
|
||||||
|
allow antivirus_t self:netlink_audit_socket { create nlmsg_relay write };
|
||||||
|
allow antivirus_t self:netlink_audit_socket read;
|
||||||
|
allow antivirus_t self:process { setrlimit setsched };
|
||||||
|
allow antivirus_t sudo_exec_t:file map;
|
||||||
|
allow antivirus_t systemd_logind_t:unix_stream_socket connectto;
|
||||||
|
allow antivirus_t systemd_logind_var_run_t:sock_file write;
|
||||||
23
active/os_fedora/selinux_policies/clamav-unixchkpwd.te
Normal file
23
active/os_fedora/selinux_policies/clamav-unixchkpwd.te
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
module clamav-unixchkpwd 1.0;
|
||||||
|
|
||||||
|
require {
|
||||||
|
type chkpwd_t;
|
||||||
|
type user_devpts_t;
|
||||||
|
type antivirus_t;
|
||||||
|
type shadow_t;
|
||||||
|
type init_t;
|
||||||
|
class chr_file { read write };
|
||||||
|
class file { getattr open read };
|
||||||
|
class process siginh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#============= antivirus_t ==============
|
||||||
|
allow antivirus_t shadow_t:file { open read };
|
||||||
|
allow antivirus_t shadow_t:file getattr;
|
||||||
|
|
||||||
|
#============= chkpwd_t ==============
|
||||||
|
allow chkpwd_t user_devpts_t:chr_file { read write };
|
||||||
|
|
||||||
|
#============= init_t ==============
|
||||||
|
allow init_t chkpwd_t:process siginh;
|
||||||
16
active/os_fedora/selinux_policies/my-rpcvirtstorage.te
Normal file
16
active/os_fedora/selinux_policies/my-rpcvirtstorage.te
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
module my-rpcvirtstorage 1.0;
|
||||||
|
|
||||||
|
require {
|
||||||
|
type user_home_t;
|
||||||
|
type virtstoraged_t;
|
||||||
|
type qemu_var_run_t;
|
||||||
|
class dir setattr;
|
||||||
|
class capability fowner;
|
||||||
|
class file setattr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#============= virtstoraged_t ==============
|
||||||
|
allow virtstoraged_t qemu_var_run_t:file setattr;
|
||||||
|
allow virtstoraged_t self:capability fowner;
|
||||||
|
allow virtstoraged_t user_home_t:dir setattr;
|
||||||
703
active/os_rhel/p11child.patch
Normal file
703
active/os_rhel/p11child.patch
Normal file
@@ -0,0 +1,703 @@
|
|||||||
|
From d4022a63f388f9cd0f537a4eb371e111c44b9c9c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Georgij Krajnyukov <nrk63@yandex.ru>
|
||||||
|
Date: Wed, 19 Feb 2025 11:59:14 +0300
|
||||||
|
Subject: [PATCH 1/4] P11_CHILD: Invert if statement to reduce code nesting
|
||||||
|
|
||||||
|
Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
|
||||||
|
Reviewed-by: Sumit Bose <sbose@redhat.com>
|
||||||
|
---
|
||||||
|
src/p11_child/p11_child_openssl.c | 33 +++++++++++++++----------------
|
||||||
|
1 file changed, 16 insertions(+), 17 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
|
||||||
|
index 45a4930ba..8aa73c035 100644
|
||||||
|
--- a/src/p11_child/p11_child_openssl.c
|
||||||
|
+++ b/src/p11_child/p11_child_openssl.c
|
||||||
|
@@ -1928,26 +1928,25 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
|
||||||
|
if (slot_id == (CK_SLOT_ID)-1) {
|
||||||
|
DEBUG(SSSDBG_TRACE_ALL, "Token not present.\n");
|
||||||
|
- if (p11_ctx->wait_for_card) {
|
||||||
|
- /* After obtaining the module's slot list (in the loop above), this
|
||||||
|
- * call is needed to let any changes in slots take effect. */
|
||||||
|
- rv = module->C_GetSlotList(CK_FALSE, NULL, &num_slots);
|
||||||
|
- if (rv != CKR_OK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotList failed [%lu][%s].\n",
|
||||||
|
- rv, p11_kit_strerror(rv));
|
||||||
|
- ret = EIO;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = wait_for_card(module, &slot_id, &info, &token_info, uri);
|
||||||
|
- if (ret != EOK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "wait_for_card failed.\n");
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
+ if (!p11_ctx->wait_for_card) {
|
||||||
|
ret = EIO;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
+ /* After obtaining the module's slot list (in the loop above), this
|
||||||
|
+ * call is needed to let any changes in slots take effect. */
|
||||||
|
+ rv = module->C_GetSlotList(CK_FALSE, NULL, &num_slots);
|
||||||
|
+ if (rv != CKR_OK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotList failed [%lu][%s].\n",
|
||||||
|
+ rv, p11_kit_strerror(rv));
|
||||||
|
+ ret = EIO;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = wait_for_card(module, &slot_id, &info, &token_info, uri);
|
||||||
|
+ if (ret != EOK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "wait_for_card failed.\n");
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
module_id = c;
|
||||||
|
--
|
||||||
|
2.51.0
|
||||||
|
|
||||||
|
|
||||||
|
From 06ee7f46937b40da85628a03c59865fffea48e90 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Georgij Krajnyukov <nrk63@yandex.ru>
|
||||||
|
Date: Tue, 4 Mar 2025 12:28:33 +0300
|
||||||
|
Subject: [PATCH 2/4] P11_CHILD: Implement passing const args to get_pkcs11_uri
|
||||||
|
|
||||||
|
Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
|
||||||
|
Reviewed-by: Sumit Bose <sbose@redhat.com>
|
||||||
|
---
|
||||||
|
src/p11_child/p11_child_openssl.c | 6 +++---
|
||||||
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
|
||||||
|
index 8aa73c035..30782d955 100644
|
||||||
|
--- a/src/p11_child/p11_child_openssl.c
|
||||||
|
+++ b/src/p11_child/p11_child_openssl.c
|
||||||
|
@@ -505,9 +505,9 @@ done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, CK_INFO *module_info,
|
||||||
|
- CK_SLOT_INFO *slot_info, CK_SLOT_ID slot_id,
|
||||||
|
- CK_TOKEN_INFO *token_info, CK_ATTRIBUTE *label,
|
||||||
|
+static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, const CK_INFO *module_info,
|
||||||
|
+ const CK_SLOT_INFO *slot_info, CK_SLOT_ID slot_id,
|
||||||
|
+ const CK_TOKEN_INFO *token_info, CK_ATTRIBUTE *label,
|
||||||
|
CK_ATTRIBUTE *id)
|
||||||
|
{
|
||||||
|
P11KitUri *uri;
|
||||||
|
--
|
||||||
|
2.51.0
|
||||||
|
|
||||||
|
|
||||||
|
From 54d3110be5146ec1d6575389bc60ad7585493984 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Georgij Krajnyukov <nrk63@yandex.ru>
|
||||||
|
Date: Wed, 19 Feb 2025 12:22:30 +0300
|
||||||
|
Subject: [PATCH 3/4] P11_CHILD: Extract slot processing into separate function
|
||||||
|
|
||||||
|
Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
|
||||||
|
Reviewed-by: Sumit Bose <sbose@redhat.com>
|
||||||
|
---
|
||||||
|
src/p11_child/p11_child_openssl.c | 399 ++++++++++++++++--------------
|
||||||
|
1 file changed, 212 insertions(+), 187 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
|
||||||
|
index 30782d955..b96d19f88 100644
|
||||||
|
--- a/src/p11_child/p11_child_openssl.c
|
||||||
|
+++ b/src/p11_child/p11_child_openssl.c
|
||||||
|
@@ -1702,6 +1702,215 @@ static errno_t wait_for_card(CK_FUNCTION_LIST *module, CK_SLOT_ID *slot_id,
|
||||||
|
|
||||||
|
#define MAX_SLOTS 64
|
||||||
|
|
||||||
|
+errno_t do_slot(CK_FUNCTION_LIST *module, size_t module_id, CK_SLOT_ID slot_id,
|
||||||
|
+ const CK_SLOT_INFO *info, const CK_TOKEN_INFO *token_info,
|
||||||
|
+ const CK_INFO *module_info, TALLOC_CTX *mem_ctx,
|
||||||
|
+ struct p11_ctx *p11_ctx, enum op_mode mode, const char *pin,
|
||||||
|
+ const char *module_name_in, const char *token_name_in,
|
||||||
|
+ const char *key_id_in, const char *label_in,
|
||||||
|
+ const char *uri_str, char **_multi) {
|
||||||
|
+ int ret;
|
||||||
|
+ CK_RV rv;
|
||||||
|
+ char *module_file_name = NULL;
|
||||||
|
+ char *slot_name = NULL;
|
||||||
|
+ char *token_name = NULL;
|
||||||
|
+ CK_SESSION_HANDLE session = 0;
|
||||||
|
+ struct cert_list *cert_list = NULL;
|
||||||
|
+ struct cert_list *item = NULL;
|
||||||
|
+ struct cert_list *next_item = NULL;
|
||||||
|
+ bool pkcs11_session = false;
|
||||||
|
+ bool pkcs11_login = false;
|
||||||
|
+
|
||||||
|
+ slot_name = p11_kit_space_strdup(info->slotDescription,
|
||||||
|
+ sizeof(info->slotDescription));
|
||||||
|
+ if (slot_name == NULL) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "p11_kit_space_strdup failed.\n");
|
||||||
|
+ ret = ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ token_name = p11_kit_space_strdup(token_info->label,
|
||||||
|
+ sizeof(token_info->label));
|
||||||
|
+ if (token_name == NULL) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "p11_kit_space_strdup failed.\n");
|
||||||
|
+ ret = ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ module_file_name = p11_kit_module_get_filename(module);
|
||||||
|
+ if (module_file_name == NULL) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "p11_kit_module_get_filename failed.\n");
|
||||||
|
+ ret = ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in slot [%s][%d] of module [%d][%s].\n",
|
||||||
|
+ token_name, slot_name, (int) slot_id, (int) module_id,
|
||||||
|
+ module_file_name);
|
||||||
|
+
|
||||||
|
+ rv = module->C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL, NULL,
|
||||||
|
+ &session);
|
||||||
|
+ if (rv != CKR_OK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "C_OpenSession failed [%lu][%s].\n",
|
||||||
|
+ rv, p11_kit_strerror(rv));
|
||||||
|
+ ret = EIO;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+ pkcs11_session = true;
|
||||||
|
+
|
||||||
|
+ /* login: do we need to check for Login Required? */
|
||||||
|
+ if (mode == OP_AUTH) {
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Login required.\n");
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Token flags [%lu].\n", token_info->flags);
|
||||||
|
+ if ((pin != NULL)
|
||||||
|
+ || (token_info->flags & CKF_PROTECTED_AUTHENTICATION_PATH)) {
|
||||||
|
+
|
||||||
|
+ if (token_info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Protected authentication path.\n");
|
||||||
|
+ pin = NULL;
|
||||||
|
+ }
|
||||||
|
+ rv = module->C_Login(session, CKU_USER, discard_const(pin),
|
||||||
|
+ (pin != NULL) ? strlen(pin) : 0);
|
||||||
|
+ if (rv == CKR_PIN_LOCKED) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "C_Login failed: PIN locked\n");
|
||||||
|
+ ret = ERR_P11_PIN_LOCKED;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+ else if (rv != CKR_OK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "C_Login failed [%lu][%s].\n",
|
||||||
|
+ rv, p11_kit_strerror(rv));
|
||||||
|
+ ret = EIO;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+ pkcs11_login = true;
|
||||||
|
+ } else {
|
||||||
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
||||||
|
+ "Login required but no PIN available, continue.\n");
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Login NOT required.\n");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = read_certs(mem_ctx, module, session, p11_ctx, &cert_list);
|
||||||
|
+ if (ret != EOK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "read_certs failed.\n");
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ DLIST_FOR_EACH_SAFE(item, next_item, cert_list) {
|
||||||
|
+ /* Check if we found the certificates we needed for authentication or
|
||||||
|
+ * the requested ones for pre-auth. For authentication all attributes
|
||||||
|
+ * except the label must be given and match. The label is optional for
|
||||||
|
+ * authentication but if given it must match as well. For pre-auth
|
||||||
|
+ * only the given ones must match. */
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "%s %s %s %s %s %s %s.\n",
|
||||||
|
+ module_name_in, module_file_name, token_name_in, token_name,
|
||||||
|
+ key_id_in, label_in == NULL ? "- no label given-" : label_in,
|
||||||
|
+ item->id);
|
||||||
|
+
|
||||||
|
+ if ((mode == OP_AUTH
|
||||||
|
+ && module_name_in != NULL
|
||||||
|
+ && token_name_in != NULL
|
||||||
|
+ && key_id_in != NULL
|
||||||
|
+ && item->id != NULL
|
||||||
|
+ && strcmp(key_id_in, item->id) == 0
|
||||||
|
+ && (label_in == NULL
|
||||||
|
+ || (label_in != NULL && item->label != NULL
|
||||||
|
+ && strcmp(label_in, item->label) == 0))
|
||||||
|
+ && strcmp(token_name_in, token_name) == 0
|
||||||
|
+ && strcmp(module_name_in, module_file_name) == 0)
|
||||||
|
+ || (mode == OP_PREAUTH
|
||||||
|
+ && (module_name_in == NULL
|
||||||
|
+ || (module_name_in != NULL
|
||||||
|
+ && strcmp(module_name_in, module_file_name) == 0))
|
||||||
|
+ && (token_name_in == NULL
|
||||||
|
+ || (token_name_in != NULL
|
||||||
|
+ && strcmp(token_name_in, token_name) == 0))
|
||||||
|
+ && (key_id_in == NULL
|
||||||
|
+ || (key_id_in != NULL && item->id != NULL
|
||||||
|
+ && strcmp(key_id_in, item->id) == 0)))) {
|
||||||
|
+
|
||||||
|
+ item->uri = get_pkcs11_uri(mem_ctx, module_info, info, slot_id,
|
||||||
|
+ token_info,
|
||||||
|
+ &item->attributes[1] /* label */,
|
||||||
|
+ &item->attributes[0] /* id */);
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "uri: %s.\n", item->uri);
|
||||||
|
+
|
||||||
|
+ } else {
|
||||||
|
+ DLIST_REMOVE(cert_list, item);
|
||||||
|
+ talloc_free(item);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* TODO: check module_name_in, token_name_in, key_id_in */
|
||||||
|
+
|
||||||
|
+ if (cert_list == NULL) {
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n");
|
||||||
|
+ *_multi = NULL;
|
||||||
|
+ ret = EOK;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (mode == OP_AUTH) {
|
||||||
|
+ if (cert_list->next != NULL || cert_list->prev != NULL) {
|
||||||
|
+ DEBUG(SSSDBG_FATAL_FAILURE,
|
||||||
|
+ "More than one certificate found for authentication, "
|
||||||
|
+ "aborting!\n");
|
||||||
|
+ ret = EINVAL;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = sign_data(module, session, slot_id, cert_list);
|
||||||
|
+ if (ret != EOK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "sign_data failed.\n");
|
||||||
|
+ ret = EACCES;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL,
|
||||||
|
+ "Certificate verified and validated.\n");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ *_multi = talloc_strdup(mem_ctx, "");
|
||||||
|
+ if (*_multi == NULL) {
|
||||||
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create output string.\n");
|
||||||
|
+ ret = ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ DLIST_FOR_EACH(item, cert_list) {
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n",
|
||||||
|
+ item->id);
|
||||||
|
+
|
||||||
|
+ *_multi = talloc_asprintf_append(*_multi, "%s\n%s\n%s\n%s\n%s\n",
|
||||||
|
+ token_name, module_file_name, item->id,
|
||||||
|
+ item->label, item->cert_b64);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = EOK;
|
||||||
|
+done:
|
||||||
|
+ if (module != NULL) {
|
||||||
|
+ if (pkcs11_login) {
|
||||||
|
+ rv = module->C_Logout(session);
|
||||||
|
+ if (rv != CKR_OK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "C_Logout failed [%lu][%s].\n",
|
||||||
|
+ rv, p11_kit_strerror(rv));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (pkcs11_session) {
|
||||||
|
+ rv = module->C_CloseSession(session);
|
||||||
|
+ if (rv != CKR_OK) {
|
||||||
|
+ DEBUG(SSSDBG_OP_FAILURE, "C_CloseSession failed [%lu][%s].\n",
|
||||||
|
+ rv, p11_kit_strerror(rv));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ free(slot_name);
|
||||||
|
+ free(token_name);
|
||||||
|
+ free(module_file_name);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
enum op_mode mode, const char *pin,
|
||||||
|
const char *module_name_in, const char *token_name_in,
|
||||||
|
@@ -1724,16 +1933,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
CK_INFO module_info;
|
||||||
|
CK_RV rv;
|
||||||
|
size_t module_id;
|
||||||
|
- char *module_file_name = NULL;
|
||||||
|
- char *slot_name = NULL;
|
||||||
|
- char *token_name = NULL;
|
||||||
|
- CK_SESSION_HANDLE session = 0;
|
||||||
|
- struct cert_list *cert_list = NULL;
|
||||||
|
- struct cert_list *item = NULL;
|
||||||
|
- struct cert_list *next_item = NULL;
|
||||||
|
char *multi = NULL;
|
||||||
|
- bool pkcs11_session = false;
|
||||||
|
- bool pkcs11_login = false;
|
||||||
|
P11KitUri *uri = NULL;
|
||||||
|
|
||||||
|
if (uri_str != NULL) {
|
||||||
|
@@ -1950,188 +2150,13 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
module_id = c;
|
||||||
|
- slot_name = p11_kit_space_strdup(info.slotDescription,
|
||||||
|
- sizeof(info.slotDescription));
|
||||||
|
- if (slot_name == NULL) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "p11_kit_space_strdup failed.\n");
|
||||||
|
- ret = ENOMEM;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- token_name = p11_kit_space_strdup(token_info.label,
|
||||||
|
- sizeof(token_info.label));
|
||||||
|
- if (token_name == NULL) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "p11_kit_space_strdup failed.\n");
|
||||||
|
- ret = ENOMEM;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- module_file_name = p11_kit_module_get_filename(module);
|
||||||
|
- if (module_file_name == NULL) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "p11_kit_module_get_filename failed.\n");
|
||||||
|
- ret = ENOMEM;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in slot [%s][%d] of module [%d][%s].\n",
|
||||||
|
- token_name, slot_name, (int) slot_id, (int) module_id,
|
||||||
|
- module_file_name);
|
||||||
|
-
|
||||||
|
- rv = module->C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL, NULL,
|
||||||
|
- &session);
|
||||||
|
- if (rv != CKR_OK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "C_OpenSession failed [%lu][%s].\n",
|
||||||
|
- rv, p11_kit_strerror(rv));
|
||||||
|
- ret = EIO;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
- pkcs11_session = true;
|
||||||
|
-
|
||||||
|
- /* login: do we need to check for Login Required? */
|
||||||
|
- if (mode == OP_AUTH) {
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "Login required.\n");
|
||||||
|
- if (pin != NULL) {
|
||||||
|
- rv = module->C_Login(session, CKU_USER, discard_const(pin),
|
||||||
|
- strlen(pin));
|
||||||
|
- if (rv == CKR_PIN_LOCKED) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "C_Login failed: PIN locked\n");
|
||||||
|
- ret = ERR_P11_PIN_LOCKED;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
- else if (rv != CKR_OK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "C_Login failed [%lu][%s].\n",
|
||||||
|
- rv, p11_kit_strerror(rv));
|
||||||
|
- ret = EIO;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
- pkcs11_login = true;
|
||||||
|
- } else {
|
||||||
|
- DEBUG(SSSDBG_CRIT_FAILURE,
|
||||||
|
- "Login required but no PIN available, continue.\n");
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "Login NOT required.\n");
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = read_certs(mem_ctx, module, session, p11_ctx, &cert_list);
|
||||||
|
- if (ret != EOK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "read_certs failed.\n");
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- DLIST_FOR_EACH_SAFE(item, next_item, cert_list) {
|
||||||
|
- /* Check if we found the certificates we needed for authentication or
|
||||||
|
- * the requested ones for pre-auth. For authentication all attributes
|
||||||
|
- * except the label must be given and match. The label is optional for
|
||||||
|
- * authentication but if given it must match as well. For pre-auth
|
||||||
|
- * only the given ones must match. */
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "%s %s %s %s %s %s %s.\n",
|
||||||
|
- module_name_in, module_file_name, token_name_in, token_name,
|
||||||
|
- key_id_in, label_in == NULL ? "- no label given-" : label_in,
|
||||||
|
- item->id);
|
||||||
|
-
|
||||||
|
- if ((mode == OP_AUTH
|
||||||
|
- && module_name_in != NULL
|
||||||
|
- && token_name_in != NULL
|
||||||
|
- && key_id_in != NULL
|
||||||
|
- && item->id != NULL
|
||||||
|
- && strcmp(key_id_in, item->id) == 0
|
||||||
|
- && (label_in == NULL
|
||||||
|
- || (label_in != NULL && item->label != NULL
|
||||||
|
- && strcmp(label_in, item->label) == 0))
|
||||||
|
- && strcmp(token_name_in, token_name) == 0
|
||||||
|
- && strcmp(module_name_in, module_file_name) == 0)
|
||||||
|
- || (mode == OP_PREAUTH
|
||||||
|
- && (module_name_in == NULL
|
||||||
|
- || (module_name_in != NULL
|
||||||
|
- && strcmp(module_name_in, module_file_name) == 0))
|
||||||
|
- && (token_name_in == NULL
|
||||||
|
- || (token_name_in != NULL
|
||||||
|
- && strcmp(token_name_in, token_name) == 0))
|
||||||
|
- && (key_id_in == NULL
|
||||||
|
- || (key_id_in != NULL && item->id != NULL
|
||||||
|
- && strcmp(key_id_in, item->id) == 0)))) {
|
||||||
|
-
|
||||||
|
- item->uri = get_pkcs11_uri(mem_ctx, &module_info, &info, slot_id,
|
||||||
|
- &token_info,
|
||||||
|
- &item->attributes[1] /* label */,
|
||||||
|
- &item->attributes[0] /* id */);
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "uri: %s.\n", item->uri);
|
||||||
|
-
|
||||||
|
- } else {
|
||||||
|
- DLIST_REMOVE(cert_list, item);
|
||||||
|
- talloc_free(item);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* TODO: check module_name_in, token_name_in, key_id_in */
|
||||||
|
-
|
||||||
|
- if (cert_list == NULL) {
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n");
|
||||||
|
- *_multi = NULL;
|
||||||
|
- ret = EOK;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (mode == OP_AUTH) {
|
||||||
|
- if (cert_list->next != NULL || cert_list->prev != NULL) {
|
||||||
|
- DEBUG(SSSDBG_FATAL_FAILURE,
|
||||||
|
- "More than one certificate found for authentication, "
|
||||||
|
- "aborting!\n");
|
||||||
|
- ret = EINVAL;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = sign_data(module, session, slot_id, cert_list);
|
||||||
|
- if (ret != EOK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "sign_data failed.\n");
|
||||||
|
- ret = EACCES;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL,
|
||||||
|
- "Certificate verified and validated.\n");
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- multi = talloc_strdup(mem_ctx, "");
|
||||||
|
- if (multi == NULL) {
|
||||||
|
- DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create output string.\n");
|
||||||
|
- ret = ENOMEM;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- DLIST_FOR_EACH(item, cert_list) {
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n",
|
||||||
|
- item->id);
|
||||||
|
-
|
||||||
|
- multi = talloc_asprintf_append(multi, "%s\n%s\n%s\n%s\n%s\n",
|
||||||
|
- token_name, module_file_name, item->id,
|
||||||
|
- item->label, item->cert_b64);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ ret = do_slot(module, module_id, slot_id, &info, &token_info, &module_info,
|
||||||
|
+ mem_ctx, p11_ctx, mode, pin, module_name_in, token_name_in,
|
||||||
|
+ key_id_in, label_in, uri_str, &multi);
|
||||||
|
*_multi = multi;
|
||||||
|
|
||||||
|
ret = EOK;
|
||||||
|
done:
|
||||||
|
- if (module != NULL) {
|
||||||
|
- if (pkcs11_login) {
|
||||||
|
- rv = module->C_Logout(session);
|
||||||
|
- if (rv != CKR_OK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "C_Logout failed [%lu][%s].\n",
|
||||||
|
- rv, p11_kit_strerror(rv));
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- if (pkcs11_session) {
|
||||||
|
- rv = module->C_CloseSession(session);
|
||||||
|
- if (rv != CKR_OK) {
|
||||||
|
- DEBUG(SSSDBG_OP_FAILURE, "C_CloseSession failed [%lu][%s].\n",
|
||||||
|
- rv, p11_kit_strerror(rv));
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- free(slot_name);
|
||||||
|
- free(token_name);
|
||||||
|
- free(module_file_name);
|
||||||
|
p11_kit_modules_finalize_and_release(modules);
|
||||||
|
p11_kit_uri_free(uri);
|
||||||
|
|
||||||
|
--
|
||||||
|
2.51.0
|
||||||
|
|
||||||
|
|
||||||
|
From 218c418e1ad34efe3922937673716c0cc657597c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Georgij Krajnyukov <nrk63@yandex.ru>
|
||||||
|
Date: Fri, 14 Feb 2025 15:00:50 +0300
|
||||||
|
Subject: [PATCH 4/4] P11_CHILD: Make p11_child iterate over all slots
|
||||||
|
|
||||||
|
Resolves: https://github.com/SSSD/sssd/issues/5905
|
||||||
|
|
||||||
|
Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
|
||||||
|
Reviewed-by: Sumit Bose <sbose@redhat.com>
|
||||||
|
---
|
||||||
|
src/p11_child/p11_child_openssl.c | 74 +++++++++++++++++++++----------
|
||||||
|
1 file changed, 51 insertions(+), 23 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
|
||||||
|
index b96d19f88..154a3052b 100644
|
||||||
|
--- a/src/p11_child/p11_child_openssl.c
|
||||||
|
+++ b/src/p11_child/p11_child_openssl.c
|
||||||
|
@@ -1748,6 +1748,15 @@ errno_t do_slot(CK_FUNCTION_LIST *module, size_t module_id, CK_SLOT_ID slot_id,
|
||||||
|
token_name, slot_name, (int) slot_id, (int) module_id,
|
||||||
|
module_file_name);
|
||||||
|
|
||||||
|
+ if (mode == OP_AUTH && strcmp(token_name, token_name_in) != 0) {
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Token name [%s] does not match "
|
||||||
|
+ "token_name_in [%s]. "
|
||||||
|
+ "Skipping this token...\n",
|
||||||
|
+ token_name, token_name_in);
|
||||||
|
+ ret = EOK;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
rv = module->C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL, NULL,
|
||||||
|
&session);
|
||||||
|
if (rv != CKR_OK) {
|
||||||
|
@@ -1846,7 +1855,6 @@ errno_t do_slot(CK_FUNCTION_LIST *module, size_t module_id, CK_SLOT_ID slot_id,
|
||||||
|
|
||||||
|
if (cert_list == NULL) {
|
||||||
|
DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n");
|
||||||
|
- *_multi = NULL;
|
||||||
|
ret = EOK;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
@@ -1871,13 +1879,6 @@ errno_t do_slot(CK_FUNCTION_LIST *module, size_t module_id, CK_SLOT_ID slot_id,
|
||||||
|
"Certificate verified and validated.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
- *_multi = talloc_strdup(mem_ctx, "");
|
||||||
|
- if (*_multi == NULL) {
|
||||||
|
- DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create output string.\n");
|
||||||
|
- ret = ENOMEM;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
DLIST_FOR_EACH(item, cert_list) {
|
||||||
|
DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n",
|
||||||
|
item->id);
|
||||||
|
@@ -1885,6 +1886,12 @@ errno_t do_slot(CK_FUNCTION_LIST *module, size_t module_id, CK_SLOT_ID slot_id,
|
||||||
|
*_multi = talloc_asprintf_append(*_multi, "%s\n%s\n%s\n%s\n%s\n",
|
||||||
|
token_name, module_file_name, item->id,
|
||||||
|
item->label, item->cert_b64);
|
||||||
|
+ if (*_multi == NULL) {
|
||||||
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
||||||
|
+ "Failed to append certiticate to the output string.\n");
|
||||||
|
+ ret = ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = EOK;
|
||||||
|
@@ -1933,9 +1940,14 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
CK_INFO module_info;
|
||||||
|
CK_RV rv;
|
||||||
|
size_t module_id;
|
||||||
|
- char *multi = NULL;
|
||||||
|
P11KitUri *uri = NULL;
|
||||||
|
|
||||||
|
+ *_multi = talloc_strdup(mem_ctx, "");
|
||||||
|
+ if (*_multi == NULL) {
|
||||||
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create output string.\n");
|
||||||
|
+ return ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (uri_str != NULL) {
|
||||||
|
uri = p11_kit_uri_new();
|
||||||
|
if (uri == NULL) {
|
||||||
|
@@ -1986,9 +1998,17 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG(SSSDBG_TRACE_ALL, "common name: [%s].\n", mod_name);
|
||||||
|
- DEBUG(SSSDBG_TRACE_ALL, "dll name: [%s].\n", mod_file_name);
|
||||||
|
-
|
||||||
|
free(mod_name);
|
||||||
|
+
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "dll name: [%s].\n", mod_file_name);
|
||||||
|
+ if (mode == OP_AUTH && strcmp(mod_file_name, module_name_in) != 0) {
|
||||||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Module name [%s] does not match "
|
||||||
|
+ "module_name_in [%s]. "
|
||||||
|
+ "Skipping this module...\n",
|
||||||
|
+ mod_file_name, module_name_in);
|
||||||
|
+ free(mod_file_name);
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
free(mod_file_name);
|
||||||
|
|
||||||
|
rv = modules[c]->C_GetInfo(&module_info);
|
||||||
|
@@ -2104,10 +2124,13 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_id = slots[s];
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- if (slot_id != (CK_SLOT_ID)-1) {
|
||||||
|
- break;
|
||||||
|
+ module_id = c;
|
||||||
|
+ ret = do_slot(module, module_id, slot_id, &info, &token_info, &module_info,
|
||||||
|
+ mem_ctx, p11_ctx, mode, pin, module_name_in, token_name_in,
|
||||||
|
+ key_id_in, label_in, uri_str, _multi);
|
||||||
|
+ if (ret != EOK) {
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2126,7 +2149,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (slot_id == (CK_SLOT_ID)-1) {
|
||||||
|
+ if (slot_id == (CK_SLOT_ID)-1 || (mode == OP_AUTH && *_multi[0] == '\0')) {
|
||||||
|
DEBUG(SSSDBG_TRACE_ALL, "Token not present.\n");
|
||||||
|
if (!p11_ctx->wait_for_card) {
|
||||||
|
ret = EIO;
|
||||||
|
@@ -2147,18 +2170,23 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx,
|
||||||
|
DEBUG(SSSDBG_OP_FAILURE, "wait_for_card failed.\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
- }
|
||||||
|
|
||||||
|
- module_id = c;
|
||||||
|
- ret = do_slot(module, module_id, slot_id, &info, &token_info, &module_info,
|
||||||
|
- mem_ctx, p11_ctx, mode, pin, module_name_in, token_name_in,
|
||||||
|
- key_id_in, label_in, uri_str, &multi);
|
||||||
|
- *_multi = multi;
|
||||||
|
+ ret = do_slot(module, module_id, slot_id, &info, &token_info, &module_info,
|
||||||
|
+ mem_ctx, p11_ctx, mode, pin, module_name_in, token_name_in,
|
||||||
|
+ key_id_in, label_in, uri_str, _multi);
|
||||||
|
+ if (mode == OP_AUTH && *_multi[0] == '\0') {
|
||||||
|
+ ret = EIO;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- ret = EOK;
|
||||||
|
done:
|
||||||
|
p11_kit_modules_finalize_and_release(modules);
|
||||||
|
p11_kit_uri_free(uri);
|
||||||
|
|
||||||
|
+ if (ret != EOK) {
|
||||||
|
+ talloc_free(*_multi);
|
||||||
|
+ *_multi = NULL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.51.0
|
||||||
|
|
||||||
85
active/os_rhel/rhel8.md
Normal file
85
active/os_rhel/rhel8.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# RHEL 8
|
||||||
|
|
||||||
|
## Subscriptions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Re-up subscription
|
||||||
|
subscription-manager register
|
||||||
|
subscription-manager release --show
|
||||||
|
subscription-manager release --set=8_10
|
||||||
|
```
|
||||||
|
|
||||||
|
`dnf update` will sometimes throw an error like:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Updating Subscription Management repositories.
|
||||||
|
Red Hat Enterprise Linux 8 for x86_64 - BaseOS (RPMs) 0.0 B/s | 0 B 00:00
|
||||||
|
Errors during downloading metadata for repository 'rhel-8-for-x86_64-baseos-rpms':
|
||||||
|
- Curl error (77): Problem with the SSL CA cert (path? access rights?) for https://cdn.redhat.com/content/dist/rhel8/8/x86_64/baseos/os/repodata/repomd.xml [error setting certificate verify locations:
|
||||||
|
CAfile: %(ca_cert_dir)sredhat-uep.pem
|
||||||
|
CApath: none]
|
||||||
|
Error: Failed to download metadata for repo 'rhel-8-for-x86_64-baseos-rpms': Cannot download repomd.xml: Curl error (77): Problem with the SSL CA cert (path? access rights?) for https://cdn.redhat.com/content/dist/rhel8/8/x86_64/baseos/os/repodata/repomd.xml [error setting certificate verify locations:
|
||||||
|
CAfile: %(ca_cert_dir)sredhat-uep.pem
|
||||||
|
CApath: none]
|
||||||
|
```
|
||||||
|
|
||||||
|
It can be fixed by adding the correct details to /etc/rhsm/rhsm.conf
|
||||||
|
|
||||||
|
<https://access.redhat.com/solutions/7098119>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# /etc/rhsm/rhsm.conf
|
||||||
|
|
||||||
|
[rhsm]
|
||||||
|
# Content base URL:
|
||||||
|
baseurl = https://cdn.redhat.com
|
||||||
|
|
||||||
|
# Repository metadata GPG key URL:
|
||||||
|
repomd_gpg_url =
|
||||||
|
|
||||||
|
# Server CA certificate location:
|
||||||
|
ca_cert_dir = /etc/rhsm/ca/
|
||||||
|
|
||||||
|
# Default CA cert to use when generating yum repo configs:
|
||||||
|
repo_ca_cert = %(ca_cert_dir)sredhat-uep.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
## DNF Repo Hotfixes
|
||||||
|
|
||||||
|
Fixes an issue where dnf would fail to install a package due to multiple
|
||||||
|
modules or "conflicting requests" or "filtered out by modular filtering".
|
||||||
|
|
||||||
|
<https://dnf.readthedocs.io/en/latest/modularity.html#hotfix-repositories>
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Add this to all conflicting rpm sources in /etc/yum.repos.d
|
||||||
|
module_hotfixes=1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Patching an RPM
|
||||||
|
|
||||||
|
### Creating the patch
|
||||||
|
|
||||||
|
1. Create your patch by checking out the tag matching the rpm version of the software you want to patch
|
||||||
|
2. Cherry pick the commits you want to patch in: `git cherry-pick 3392a857c^..782a6dd54 --strategy-option=theirs`
|
||||||
|
3. Create a diff: `git format-patch 20ddeab85^..0cf92b3d4 --stdout > p11child.patch`
|
||||||
|
4. Copy the patch to your rhel instance
|
||||||
|
|
||||||
|
### Applying the patch
|
||||||
|
|
||||||
|
1. Enable the `codeready-builder-for-...-source-rpms` rpm source in `/etc/yum.repos.d/redhat.repo`
|
||||||
|
2. For RHEL 8: Add `module_hotfixes=1` to appstream and codebuild in `/etc/yum.repos.d/redhat.repo`
|
||||||
|
3. `dnf download --source sssd`
|
||||||
|
4. `rpm -i sssd-2.9.4-5.el8_10.2.src.rpm`
|
||||||
|
5. `cd rpmbuild/SPECS`
|
||||||
|
6. Edit `sssd.spec` and change `Release: 5%{?dist}.2` to match your release: e.g. `Release: 5%{?dist}_10.2`
|
||||||
|
7. `dnf builddep sssd.spec`
|
||||||
|
8. `rpmbuild -bb sssd.spec --nocheck`
|
||||||
|
9. `cd ~/rpmbuild/RPMS/x86_64`
|
||||||
|
10. For RHEL 8 `rpm -ivh ./sssd-2.9.4-5.el8_10.2.x86_64.rpm --force`
|
||||||
|
11. For RHEL 9 `dnf install ./sssd-2.9.4-5.el8_10.2.x86_64.rpm`
|
||||||
|
|
||||||
|
## VM Passthrough
|
||||||
|
|
||||||
|
If you get "device or resource busy" while trying to pass through a smart card
|
||||||
|
to a VM, you'll probably need to `systemctl stop pcscd` on the host.
|
||||||
BIN
active/os_rhel/sssd-rpm.tar
Normal file
BIN
active/os_rhel/sssd-rpm.tar
Normal file
Binary file not shown.
18
active/os_rhel/sssd.spec.patch
Normal file
18
active/os_rhel/sssd.spec.patch
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
--- sssd.spec 2025-10-07 16:20:35.377452600 -0400
|
||||||
|
+++ sssd.spec.bk 2025-10-07 16:20:23.322575768 -0400
|
||||||
|
@@ -19,7 +19,7 @@
|
||||||
|
|
||||||
|
Name: sssd
|
||||||
|
Version: 2.9.4
|
||||||
|
-Release: 5%{?dist}.2
|
||||||
|
+Release: 5%{?dist}_10.2
|
||||||
|
Group: Applications/System
|
||||||
|
Summary: System Security Services Daemon
|
||||||
|
License: GPLv3+
|
||||||
|
@@ -46,6 +46,7 @@
|
||||||
|
Patch0017: 0017-KCM-another-memory-leak-fixed.patch
|
||||||
|
Patch0018: 0018-SYSDB-don-t-add-group-members-if-ignore_group_member.patch
|
||||||
|
Patch0019: 0019-SYSDB-Use-SYSDB_NAME-from-cached-entry-when-updating.patch
|
||||||
|
+Patch0020: 0020-p11child.patch
|
||||||
|
|
||||||
|
### Downstream Patches ###
|
||||||
@@ -17,9 +17,12 @@
|
|||||||
- [Cleaning up old snapshots](#cleaning-up-old-snapshots)
|
- [Cleaning up old snapshots](#cleaning-up-old-snapshots)
|
||||||
- [Creating and restoring snapshots](#creating-and-restoring-snapshots)
|
- [Creating and restoring snapshots](#creating-and-restoring-snapshots)
|
||||||
- [Filesystem ACLs](#filesystem-acls)
|
- [Filesystem ACLs](#filesystem-acls)
|
||||||
- [ISCSI Backup Volumes](#iscsi-backup-volumes)
|
- [Decrypting Pools](#decrypting-pools)
|
||||||
- [Create Backup ZVOL](#create-backup-zvol)
|
- [ZPool Scrubbing](#zpool-scrubbing)
|
||||||
- [Create Backup ISCSI Target](#create-backup-iscsi-target)
|
- [ISCSI](#iscsi)
|
||||||
|
- [Create ZVOL](#create-zvol)
|
||||||
|
- [Create ISCSI Target](#create-iscsi-target)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [VMs](#vms)
|
- [VMs](#vms)
|
||||||
- [Converting zvol to qcow2](#converting-zvol-to-qcow2)
|
- [Converting zvol to qcow2](#converting-zvol-to-qcow2)
|
||||||
- [Converting qcow2 to zvol](#converting-qcow2-to-zvol)
|
- [Converting qcow2 to zvol](#converting-qcow2-to-zvol)
|
||||||
@@ -41,7 +44,7 @@
|
|||||||
- [UPS Monitoring](#ups-monitoring)
|
- [UPS Monitoring](#ups-monitoring)
|
||||||
- [ZFS Size Data](#zfs-size-data)
|
- [ZFS Size Data](#zfs-size-data)
|
||||||
- [ZFS Rename](#zfs-rename)
|
- [ZFS Rename](#zfs-rename)
|
||||||
- [ISCSI](#iscsi)
|
- [ISCSI](#iscsi-1)
|
||||||
- [ISCSI Base Name](#iscsi-base-name)
|
- [ISCSI Base Name](#iscsi-base-name)
|
||||||
- [Archiving](#archiving)
|
- [Archiving](#archiving)
|
||||||
- [Deleting snapshots](#deleting-snapshots)
|
- [Deleting snapshots](#deleting-snapshots)
|
||||||
@@ -247,9 +250,45 @@ Dataset -> Dataset details (edit) -> Advanced Options -> ACL Type (inherit)
|
|||||||
setfacl -b -R /mnt/enc0/smb/media
|
setfacl -b -R /mnt/enc0/smb/media
|
||||||
```
|
```
|
||||||
|
|
||||||
## ISCSI Backup Volumes
|
### Decrypting Pools
|
||||||
|
|
||||||
### Create Backup ZVOL
|
Unlocking through the UI.
|
||||||
|
|
||||||
|
We'll need to recreate the key manifest json. This is a little tedious, but
|
||||||
|
your keys will be correct after this process.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all datasets and format them for json keys
|
||||||
|
export LIST_DATASET=pool0/dcsi
|
||||||
|
echo "{" && \
|
||||||
|
for DATASET_PATH in $(sudo zfs list -r $LIST_DATASET -H -o name); do echo " \"$DATASET_PATH\": \"key_here\","; done && \
|
||||||
|
echo "}"
|
||||||
|
|
||||||
|
# If the dataset's children have all the encryption keys
|
||||||
|
# Note this generates the cat EOF commands to create the json files needed to unlock.
|
||||||
|
export TL_DATASET=pool0
|
||||||
|
for TL_DATASET_PATH in $(zfs list -r $TL_DATASET -H -o name -d 1); do \
|
||||||
|
echo "cat <<EOF > dataset_${TL_DATASET_PATH}_key.json" && \
|
||||||
|
echo "{" && \
|
||||||
|
for DATASET_PATH in $(zfs list -r $TL_DATASET_PATH -H -o name); do echo " \"$DATASET_PATH\": \"key_here\","; done && \
|
||||||
|
echo "}" && \
|
||||||
|
echo "EOF";
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### ZPool Scrubbing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start a scrub
|
||||||
|
zpool scrub pool0
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
zpool status pool0
|
||||||
|
```
|
||||||
|
|
||||||
|
## ISCSI
|
||||||
|
|
||||||
|
### Create ZVOL
|
||||||
|
|
||||||
1. Create a new dataset called "iscsi" and then a dataset under that called "backups"
|
1. Create a new dataset called "iscsi" and then a dataset under that called "backups"
|
||||||
1. Set sync to always
|
1. Set sync to always
|
||||||
@@ -258,24 +297,35 @@ setfacl -b -R /mnt/enc0/smb/media
|
|||||||
2. Create a new dataset under backups with the same name as your server hostname
|
2. Create a new dataset under backups with the same name as your server hostname
|
||||||
3. Set the size to something reasonable (Note you may need to "force size")
|
3. Set the size to something reasonable (Note you may need to "force size")
|
||||||
|
|
||||||
### Create Backup ISCSI Target
|
### Create ISCSI Target
|
||||||
|
|
||||||
1. In System -> Services -> ISCSI set the Base Name following [these rules](#iscsi-base-name)
|
In Shared -> Block (iSCSI) Shares Targets
|
||||||
2. In Shared -> ISCSI -> Authorized Access -> Create a new authorized access
|
|
||||||
|
1. Global Target Configuration -> Base Name
|
||||||
|
1. set the Base Name following [these rules](#iscsi-base-name)
|
||||||
|
2. Authorized Access -> Add
|
||||||
1. Group ID arbitrary - just pick a number you haven't used
|
1. Group ID arbitrary - just pick a number you haven't used
|
||||||
2. User: The connecting machine's ISCSI Base Name
|
2. Discovery Authentication: Chap
|
||||||
3. Secret: A 16 character password with no special characters
|
3. User: The connecting machine's ISCSI Base Name
|
||||||
3. Wizard -> Create New
|
4. Secret: A 16 character password with no special characters
|
||||||
1. Extent Name: `backup-<hostname>`
|
3. Extents -> Add
|
||||||
2. Extent Type: `Device`
|
1. Name: `some-name`
|
||||||
3. Extent Device: The ZVOL you just created
|
2. Type: `Device`
|
||||||
4. Extent Sharing Platform: `Modern OS`
|
3. Device: The ZVOL you just created
|
||||||
|
4. Sharing Platform: `Modern OS`
|
||||||
5. Protocol Options Portal: Either create new (0.0.0.0 and ::) or select your existing portal
|
5. Protocol Options Portal: Either create new (0.0.0.0 and ::) or select your existing portal
|
||||||
6. Protocol Options Initiators: The base name of the connecting machine following [these rules](#iscsi-base-name)
|
6. Protocol Options Initiators: The base name of the connecting machine following [these rules](#iscsi-base-name)
|
||||||
4. Targets -> Select the backup-<hostname> target -> Edit
|
4. Targets -> Select the backup-<hostname> target -> Edit
|
||||||
1. Authentication Method: `CHAP`
|
1. Authentication Method: `CHAP`
|
||||||
2. Authentication Group Number: The group number you created above
|
2. Authentication Group Number: The group number you created above
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ISCSI connection logs
|
||||||
|
tail -f /var/log/scst.log
|
||||||
|
```
|
||||||
|
|
||||||
## VMs
|
## VMs
|
||||||
|
|
||||||
1. Force UEFI installation
|
1. Force UEFI installation
|
||||||
|
|||||||
@@ -16,55 +16,7 @@ and the operator will store information about each server.
|
|||||||
|
|
||||||
## Setup SSH
|
## Setup SSH
|
||||||
|
|
||||||
On the operator:
|
See [README](/README.md#ssh-setup)
|
||||||
|
|
||||||
```bash
|
|
||||||
export SSH_HOST=kube
|
|
||||||
ssh-keygen -t rsa -b 4096 -C ducoterra@${SSH_HOST}.reeselink.com -f ~/.ssh/id_${SSH_HOST}_rsa
|
|
||||||
|
|
||||||
# Note: If you get "too many authentication failures" it's likely because you have too many private
|
|
||||||
# keys in your ~/.ssh directory. Use `-o PubkeyAuthentication` to fix it.
|
|
||||||
ssh-copy-id -o PubkeyAuthentication=no -i ~/.ssh/id_${SSH_HOST}_rsa.pub ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
ssh -i ~/.ssh/id_${SSH_HOST}_rsa -o 'PubkeyAuthentication=yes' ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
```
|
|
||||||
|
|
||||||
On the server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy authorized_keys to root
|
|
||||||
sudo cp ~/.ssh/authorized_keys /root/.ssh/authorized_keys
|
|
||||||
|
|
||||||
# Change your password
|
|
||||||
passwd
|
|
||||||
|
|
||||||
sudo su -
|
|
||||||
echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/01-prohibit-password.conf
|
|
||||||
echo '%sudo ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/01-nopasswd-sudo
|
|
||||||
systemctl restart ssh
|
|
||||||
```
|
|
||||||
|
|
||||||
On the operator:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat <<EOF >> ~/.ssh/config
|
|
||||||
|
|
||||||
Host $SSH_HOST
|
|
||||||
Hostname ${SSH_HOST}.reeselink.com
|
|
||||||
User root
|
|
||||||
ProxyCommand none
|
|
||||||
ForwardAgent no
|
|
||||||
ForwardX11 no
|
|
||||||
Port 22
|
|
||||||
KeepAlive yes
|
|
||||||
IdentityFile ~/.ssh/id_${SSH_HOST}_rsa
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Test if you can SSH with a password
|
|
||||||
ssh -o PubkeyAuthentication=no ducoterra@${SSH_HOST}.reeselink.com
|
|
||||||
|
|
||||||
# Test that you can log into the server with ssh config
|
|
||||||
ssh $SSH_HOST
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fail2Ban
|
## Fail2Ban
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# Podman Server
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- create a btrfs subvolume for each user
|
|
||||||
81
active/podman_bricktracker/bricktracker.md
Normal file
81
active/podman_bricktracker/bricktracker.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Brick Tracker
|
||||||
|
|
||||||
|
<https://gitea.baerentsen.space/FrederikBaerentsen/BrickTracker/src/branch/master/docs/quickstart.md>
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Create the bricktracker user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH into your podman server as root
|
||||||
|
useradd bricktracker
|
||||||
|
loginctl enable-linger $(id -u bricktracker)
|
||||||
|
systemctl --user --machine=bricktracker@.host enable podman-restart
|
||||||
|
systemctl --user --machine=bricktracker@.host enable --now podman.socket
|
||||||
|
mkdir -p /home/bricktracker/.config/containers/systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure App
|
||||||
|
|
||||||
|
1. Copy the `.env.sample` from <https://gitea.baerentsen.space/FrederikBaerentsen/BrickTracker/src/branch/master/.env.sample> to `.env`
|
||||||
|
2. Set the following:
|
||||||
|
1. `BK_AUTHENTICATION_PASSWORD`
|
||||||
|
2. `BK_AUTHENTICATION_KEY`
|
||||||
|
3. `BK_DATABASE_PATH`
|
||||||
|
4. `BK_INSTRUCTIONS_FOLDER`
|
||||||
|
5. `BK_MINIFIGURES_FOLDER`
|
||||||
|
6. `BK_PARTS_FOLDER`
|
||||||
|
7. `BK_REBRICKABLE_API_KEY`
|
||||||
|
8. `BK_SETS_FOLDER`
|
||||||
|
3. Create the docker compose yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
bricktracker:
|
||||||
|
container_name: BrickTracker
|
||||||
|
restart: unless-stopped
|
||||||
|
image: gitea.baerentsen.space/frederikbaerentsen/bricktracker:1.2.2
|
||||||
|
ports:
|
||||||
|
- "3333:3333"
|
||||||
|
volumes:
|
||||||
|
- ./data:/var/lib/bricktracker
|
||||||
|
- ./static/instructions:/app/static/instructions
|
||||||
|
- ./static/minifigures:/app/static/minifigures
|
||||||
|
- ./static/parts:/app/static/parts
|
||||||
|
- ./static/sets:/app/static/sets
|
||||||
|
env_file: ".env"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Start the service: `docker compose up -d`
|
||||||
|
|
||||||
|
## Caddy
|
||||||
|
|
||||||
|
1. Create the new DNS record for your website
|
||||||
|
2. Create the Caddyfile at `./Caddyfile`
|
||||||
|
|
||||||
|
```conf
|
||||||
|
https://connors-legos.reeseapps.com:443 {
|
||||||
|
reverse_proxy 127.0.0.1:3333
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create the Caddy compose.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
image: caddy:<version>
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
- "443:443/udp"
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
|
```
|
||||||
16
active/podman_bricktracker/compose.yaml
Normal file
16
active/podman_bricktracker/compose.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
services:
|
||||||
|
bricktracker:
|
||||||
|
container_name: BrickTracker
|
||||||
|
restart: unless-stopped
|
||||||
|
image: gitea.baerentsen.space/frederikbaerentsen/bricktracker:1.2.2
|
||||||
|
ports:
|
||||||
|
- "3333:3333"
|
||||||
|
volumes:
|
||||||
|
- /home/connorbricktracker/data:/var/lib/bricktracker
|
||||||
|
- /home/connorbricktracker/static/instructions:/app/static/instructions
|
||||||
|
- /home/connorbricktracker/static/minifigures:/app/static/minifigures
|
||||||
|
- /home/connorbricktracker/static/parts:/app/static/parts
|
||||||
|
- /home/connorbricktracker/static/sets:/app/static/sets
|
||||||
|
env_file: "/home/connorbricktracker/.env"
|
||||||
|
security_opt:
|
||||||
|
- label=disable
|
||||||
17
active/podman_bricktracker/quadlets/bricktracker.container
Normal file
17
active/podman_bricktracker/quadlets/bricktracker.container
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[Container]
|
||||||
|
ContainerName=BrickTracker
|
||||||
|
EnvironmentFile=/home/connorbricktracker/.env
|
||||||
|
Image=gitea.baerentsen.space/frederikbaerentsen/bricktracker:1.2.2
|
||||||
|
PublishPort=3333:3333
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
Volume=/home/connorbricktracker/data:/var/lib/bricktracker
|
||||||
|
Volume=/home/connorbricktracker/static/instructions:/app/static/instructions
|
||||||
|
Volume=/home/connorbricktracker/static/minifigures:/app/static/minifigures
|
||||||
|
Volume=/home/connorbricktracker/static/parts:/app/static/parts
|
||||||
|
Volume=/home/connorbricktracker/static/sets:/app/static/sets
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
8
active/podman_caddy/Containerfile
Normal file
8
active/podman_caddy/Containerfile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
FROM docker.io/caddy:2-builder AS builder
|
||||||
|
|
||||||
|
RUN xcaddy build \
|
||||||
|
--with github.com/caddy-dns/route53@v1.6.0
|
||||||
|
|
||||||
|
FROM docker.io/caddy:2
|
||||||
|
|
||||||
|
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||||
@@ -4,7 +4,7 @@ Description=Caddy
|
|||||||
[Container]
|
[Container]
|
||||||
AddCapability=NET_ADMIN
|
AddCapability=NET_ADMIN
|
||||||
ContainerName=caddy
|
ContainerName=caddy
|
||||||
Image=docker.io/caddy:2
|
Image=gitea.reeseapps.com/services/caddy:latest
|
||||||
Network=host
|
Network=host
|
||||||
SecurityLabelDisable=true
|
SecurityLabelDisable=true
|
||||||
Volume=/etc/caddy:/etc/caddy
|
Volume=/etc/caddy:/etc/caddy
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
# Caddy Reverse Proxy
|
# Caddy Reverse Proxy
|
||||||
|
|
||||||
- [Caddy Reverse Proxy](#caddy-reverse-proxy)
|
- [Caddy Reverse Proxy](#caddy-reverse-proxy)
|
||||||
- [DNS Records](#dns-records)
|
- [Custom Caddy Image](#custom-caddy-image)
|
||||||
- [Install Caddy](#install-caddy)
|
- [Install Caddy](#install-caddy)
|
||||||
- [Ansible](#ansible)
|
- [Ansible](#ansible)
|
||||||
- [Manual](#manual)
|
- [Manual](#manual)
|
||||||
|
- [Adding a new Caddy Record](#adding-a-new-caddy-record)
|
||||||
|
|
||||||
## DNS Records
|
## Custom Caddy Image
|
||||||
|
|
||||||
Before you can create a Caddyfile you need records that point to your server.
|
This repo builds a custom caddy image with route53 DNS certbot support.
|
||||||
|
|
||||||
You can either create them manually in your DNS provider of choice or use the provided
|
```bash
|
||||||
ddns service:
|
podman image pull gitea.reeseapps.com/services/caddy:latest
|
||||||
|
```
|
||||||
|
|
||||||
1. Update the [ddns caddy records](/active/podman_ddns/secrets/caddy_records.yaml)
|
To upgrade the image, check [the caddy-dns route53
|
||||||
2. Run the [caddy ansible playbook](/active/podman_ddns/ddns.md#ansible-caddy-records)
|
project](https://github.com/caddy-dns/route53/tags) releases and update the
|
||||||
|
`Containerfile` with the new version.
|
||||||
|
|
||||||
## Install Caddy
|
## Install Caddy
|
||||||
|
|
||||||
@@ -102,3 +105,14 @@ WantedBy=default.target
|
|||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl restart caddy
|
systemctl restart caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Adding a new Caddy Record
|
||||||
|
|
||||||
|
Before you can create a Caddyfile you need records that point to your server.
|
||||||
|
|
||||||
|
You can either create them manually in your DNS provider of choice or use the provided
|
||||||
|
ddns service:
|
||||||
|
|
||||||
|
1. Update the [ddns caddy records](/active/podman_ddns/secrets/caddy_records.yaml)
|
||||||
|
2. (Optional) Update the Caddyfile at `active/podman_caddy/secrets/Caddyfile`
|
||||||
|
3. Run the [caddy ansible playbook](/active/podman_caddy/caddy.md#install-caddy)
|
||||||
@@ -1,26 +1,31 @@
|
|||||||
# DDNS for Route53
|
# DDNS for Route53
|
||||||
|
|
||||||
- [DDNS for Route53](#ddns-for-route53)
|
- [DDNS for Route53](#ddns-for-route53)
|
||||||
- [Install](#install)
|
- [Quickly Update DDNS Records](#quickly-update-ddns-records)
|
||||||
- [As a Systemd Service](#as-a-systemd-service)
|
- [Install a New DDNS Service](#install-a-new-ddns-service)
|
||||||
- [Ansible Caddy Records](#ansible-caddy-records)
|
- [Ansible 3D Server Records](#ansible-3d-server-records)
|
||||||
- [Ansible Git Record](#ansible-git-record)
|
|
||||||
- [Ansible Unifi External Records](#ansible-unifi-external-records)
|
- [Ansible Unifi External Records](#ansible-unifi-external-records)
|
||||||
- [Ansible Hostname reeselink records](#ansible-hostname-reeselink-records)
|
- [Ansible Hostname reeselink records](#ansible-hostname-reeselink-records)
|
||||||
- [Development](#development)
|
- [Development](#development)
|
||||||
- [Testing](#testing)
|
- [Testing](#testing)
|
||||||
- [Building Container Image](#building-container-image)
|
- [Building Container Image](#building-container-image)
|
||||||
|
|
||||||
This service will automatically keep ipv4 and ipv6 records updated in AWS Route53.
|
This service will automatically keep ipv4 and ipv6 records updated in AWS
|
||||||
|
Route53.
|
||||||
|
|
||||||
**NOTE**: This requires the aws cli to be installed on each node with
|
**NOTE**: This requires the aws cli to be installed on each node with
|
||||||
credentials that can modify records in route53. See
|
credentials that can modify records in route53. See
|
||||||
[aws_iam](/active/aws_iam/aws_iam.md) and
|
[aws_iam](/active/aws_iam/aws_iam.md) and [aws_cli](/active/aws_cli/aws_cli.md)
|
||||||
[aws_cli](/active/aws_cli/aws_cli.md)
|
|
||||||
|
|
||||||
## Install
|
## Quickly Update DDNS Records
|
||||||
|
|
||||||
### As a Systemd Service
|
In the event of a record change you can quickly trigger the ddns services with
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl start --all ddns*.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install a New DDNS Service
|
||||||
|
|
||||||
You need two files:
|
You need two files:
|
||||||
|
|
||||||
@@ -45,7 +50,12 @@ records:
|
|||||||
hosted_zone_id: ABC123456789
|
hosted_zone_id: ABC123456789
|
||||||
```
|
```
|
||||||
|
|
||||||
Then you can install the ddns service with something like
|
Then you'll need to pick a server responsible for keeping those records
|
||||||
|
updated. Whichever host you run the service on will also be the host which
|
||||||
|
provides the public IP. Choose the host accordingly if it will be updating a
|
||||||
|
public IP on behalf of another server, as the IPv6 address will not be correct.
|
||||||
|
|
||||||
|
Now you can install the DDNS service with something like:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ansible-playbook \
|
ansible-playbook \
|
||||||
@@ -60,27 +70,22 @@ active/podman_ddns/install_ddns.yaml \
|
|||||||
|
|
||||||
See ansible playbook [install_ddns.yaml](/install_ddns.yaml)
|
See ansible playbook [install_ddns.yaml](/install_ddns.yaml)
|
||||||
|
|
||||||
#### Ansible Caddy Records
|
It's recommended that you have multiple secret `foobar-records.yaml` files for
|
||||||
|
multiple servers. If you have a podman server, it'll have its own
|
||||||
|
`podman-records.yaml`. If you have a docker server, it'll have its own
|
||||||
|
`docker-records.yaml`. Etc. etc.
|
||||||
|
|
||||||
|
### Ansible 3D Server Records
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ansible-playbook \
|
ansible-playbook \
|
||||||
-i ansible/inventory.yaml \
|
-i ansible/inventory.yaml \
|
||||||
-l 3dserver \
|
-l 3dserver \
|
||||||
active/podman_ddns/install_ddns.yaml \
|
active/podman_ddns/install_ddns.yaml \
|
||||||
-e "@active/podman_ddns/secrets/caddy_records.yaml"
|
-e "@active/podman_ddns/secrets/3dserver_records.yaml"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Ansible Git Record
|
### Ansible Unifi External Records
|
||||||
|
|
||||||
```bash
|
|
||||||
ansible-playbook \
|
|
||||||
-i ansible/inventory.yaml \
|
|
||||||
-l podman \
|
|
||||||
active/podman_ddns/install_ddns.yaml \
|
|
||||||
-e "@active/podman_ddns/secrets/git_record.yaml"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Ansible Unifi External Records
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ansible-playbook \
|
ansible-playbook \
|
||||||
@@ -90,7 +95,7 @@ active/podman_ddns/install_ddns.yaml \
|
|||||||
-e "@active/podman_ddns/secrets/unifi_external_record.yaml"
|
-e "@active/podman_ddns/secrets/unifi_external_record.yaml"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Ansible Hostname reeselink records
|
### Ansible Hostname reeselink records
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export PLAYBOOK_PATH=active/podman_ddns
|
export PLAYBOOK_PATH=active/podman_ddns
|
||||||
|
|||||||
@@ -1,39 +1,36 @@
|
|||||||
version: "3"
|
|
||||||
|
|
||||||
networks:
|
|
||||||
gitea:
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
gitea:
|
gitea:
|
||||||
image: docker.gitea.com/gitea:1.23.7
|
image: docker.gitea.com/gitea:1.25-rootless
|
||||||
container_name: gitea
|
container_name: gitea
|
||||||
environment:
|
environment:
|
||||||
- USER_UID=1000
|
|
||||||
- USER_GID=1000
|
|
||||||
- GITEA__database__DB_TYPE=postgres
|
- GITEA__database__DB_TYPE=postgres
|
||||||
- GITEA__database__HOST=postgres:5432
|
- GITEA__database__HOST=postgres:5432
|
||||||
- GITEA__database__NAME=gitea
|
- GITEA__database__NAME=gitea
|
||||||
- GITEA__database__USER=gitea
|
- GITEA__database__USER=gitea
|
||||||
- GITEA__database__PASSWD=gitea
|
- GITEA__database__PASSWD=gitea
|
||||||
|
security_opt:
|
||||||
|
- "label=disable"
|
||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
- gitea
|
- gitea
|
||||||
volumes:
|
volumes:
|
||||||
- /home/gitea/gitea_data:/data
|
- /home/gitea/gitea_data:/data:Z
|
||||||
|
- /home/gitea/gitea_etc:/etc/gitea:Z
|
||||||
|
- /home/gitea/gitea_custom:/var/lib/gitea/custom:Z
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
- "2222:22"
|
- "2222:2222"
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
security_opt:
|
|
||||||
- label=disable
|
|
||||||
labels:
|
labels:
|
||||||
- "io.containers.autoupdate=registry"
|
- "io.containers.autoupdate=registry"
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: docker.io/library/postgres:15
|
image: docker.io/library/postgres:15
|
||||||
container_name: postgres
|
container_name: postgres
|
||||||
|
security_opt:
|
||||||
|
- "label=disable"
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=gitea
|
- POSTGRES_USER=gitea
|
||||||
@@ -42,6 +39,10 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- gitea
|
- gitea
|
||||||
volumes:
|
volumes:
|
||||||
- /home/gitea/gitea_postgres:/var/lib/postgresql/data
|
- /home/gitea/gitea_postgres:/var/lib/postgresql/data:Z
|
||||||
security_opt:
|
labels:
|
||||||
- label=disable
|
- "io.containers.autoupdate=registry"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
gitea:
|
||||||
|
enable_ipv6: true
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
- [Gitea](#gitea)
|
- [Gitea](#gitea)
|
||||||
- [Gitea on Rootless Podman](#gitea-on-rootless-podman)
|
- [Gitea on Rootless Podman](#gitea-on-rootless-podman)
|
||||||
|
- [A note on directories](#a-note-on-directories)
|
||||||
- [Create the gitea user](#create-the-gitea-user)
|
- [Create the gitea user](#create-the-gitea-user)
|
||||||
- [Convert Compose to Quadlet](#convert-compose-to-quadlet)
|
- [Convert Compose to Quadlet](#convert-compose-to-quadlet)
|
||||||
- [Install Quadlets](#install-quadlets)
|
- [Install Quadlets](#install-quadlets)
|
||||||
- [Upgrade Quadlets](#upgrade-quadlets)
|
- [Upgrade](#upgrade)
|
||||||
- [Editing Configs within Container](#editing-configs-within-container)
|
- [Editing Gitea Config](#editing-gitea-config)
|
||||||
- [Gitea Runners](#gitea-runners)
|
- [Gitea Runners](#gitea-runners)
|
||||||
- [Firewall Rules](#firewall-rules)
|
- [Firewall Rules](#firewall-rules)
|
||||||
- [Install](#install)
|
- [Install](#install)
|
||||||
@@ -15,73 +16,116 @@
|
|||||||
|
|
||||||
## Gitea on Rootless Podman
|
## Gitea on Rootless Podman
|
||||||
|
|
||||||
|
### A note on directories
|
||||||
|
|
||||||
|
```bash
|
||||||
|
2025/07/30 16:49:12 cmd/web.go:116:showWebStartupMessage() [I] * AppPath: /usr/local/bin/gitea
|
||||||
|
2025/07/30 16:49:12 cmd/web.go:117:showWebStartupMessage() [I] * WorkPath: /var/lib/gitea
|
||||||
|
2025/07/30 16:49:12 cmd/web.go:118:showWebStartupMessage() [I] * CustomPath: /var/lib/gitea/custom
|
||||||
|
2025/07/30 16:49:12 cmd/web.go:119:showWebStartupMessage() [I] * ConfigFile: /etc/gitea/app.ini
|
||||||
|
|
||||||
|
2025/07/30 16:49:12 modules/storage/storage.go:176:initAttachments() [I] Initialising Attachment storage with type: local
|
||||||
|
2025/07/30 16:49:12 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /var/lib/gitea/data/attachments
|
||||||
|
2025/07/30 16:49:12 modules/storage/storage.go:166:initAvatars() [I] Initialising Avatar storage with type: local
|
||||||
|
2025/07/30 16:49:12 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /var/lib/gitea/data/avatars
|
||||||
|
2025/07/30 16:49:12 modules/storage/storage.go:192:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local
|
||||||
|
2025/07/30 16:49:12 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /var/lib/gitea/data/repo-avatars
|
||||||
|
2025/07/30 16:49:12 modules/storage/storage.go:198:initRepoArchives() [I] Initialising Repository Archive storage with type: local
|
||||||
|
2025/07/30 16:49:12 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /var/lib/gitea/data/repo-archive
|
||||||
|
2025/07/30 16:49:12 modules/storage/storage.go:208:initPackages() [I] Initialising Packages storage with type: local
|
||||||
|
2025/07/30 16:49:12 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /var/lib/gitea/data/packages
|
||||||
|
2025/07/30 16:49:12 modules/storage/storage.go:219:initActions() [I] Initialising Actions storage with type: local
|
||||||
|
2025/07/30 16:49:12 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /var/lib/gitea/data/actions_log
|
||||||
|
2025/07/30 16:49:12 modules/storage/storage.go:223:initActions() [I] Initialising ActionsArtifacts storage with type: local
|
||||||
|
2025/07/30 16:49:12 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /var/lib/gitea/data/actions_artifacts
|
||||||
|
```
|
||||||
|
|
||||||
### Create the gitea user
|
### Create the gitea user
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
useradd gitea
|
useradd gitea
|
||||||
|
|
||||||
su - gitea
|
|
||||||
ssh-keygen
|
|
||||||
exit
|
|
||||||
cp ~/.ssh/authorized_keys /home/gitea/.ssh/authorized_keys
|
|
||||||
chown gitea:gitea /home/gitea/.ssh/authorized_keys
|
|
||||||
loginctl enable-linger $(id -u gitea)
|
loginctl enable-linger $(id -u gitea)
|
||||||
```
|
systemctl --user --machine=gitea@.host enable podman-restart
|
||||||
|
systemctl --user --machine=gitea@.host enable --now podman.socket
|
||||||
SSH into the server as gitea
|
su -l gitea
|
||||||
|
mkdir -p .config/containers/systemd
|
||||||
```bash
|
|
||||||
systemctl --user enable podman-restart
|
|
||||||
systemctl --user enable --now podman.socket
|
|
||||||
mkdir -p ~/.config/containers/systemd
|
|
||||||
mkdir data config postgres
|
mkdir data config postgres
|
||||||
|
exit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Convert Compose to Quadlet
|
### Convert Compose to Quadlet
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run this in Homelab, not on the serrver.
|
# Run this in Homelab, not on the server.
|
||||||
mkdir quadlets
|
mkdir $(pwd)/active/podman_gitea/quadlets
|
||||||
|
|
||||||
# Generate the systemd service
|
# Generate the systemd service
|
||||||
podman run \
|
podman run \
|
||||||
--security-opt label=disable \
|
--network none \
|
||||||
--rm \
|
--rm \
|
||||||
-v $(pwd):/compose \
|
-v $(pwd)/active/podman_gitea/compose:$(pwd)/active/podman_gitea/compose:z \
|
||||||
-v $(pwd)/quadlets:/quadlets \
|
-v $(pwd)/active/podman_gitea/quadlets:$(pwd)/active/podman_gitea/quadlets:z \
|
||||||
quay.io/k9withabone/podlet \
|
quay.io/k9withabone/podlet \
|
||||||
-f /quadlets \
|
-f $(pwd)/active/podman_gitea/quadlets \
|
||||||
-i \
|
-i \
|
||||||
--overwrite \
|
--overwrite \
|
||||||
compose /compose/compose.yaml
|
compose $(pwd)/active/podman_gitea/compose/compose.yaml
|
||||||
|
|
||||||
# Copy the files to the server
|
# Copy the files to the server
|
||||||
scp -r quadlets/. gitea:~/.config/containers/systemd/
|
scp -r $(pwd)/active/podman_gitea/quadlets/. 3dserver:/home/gitea/.config/containers/systemd/
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install Quadlets
|
### Install Quadlets
|
||||||
|
|
||||||
The first user you register will be the admin
|
First, set up the volumes needed by the container.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh gitea systemctl --user daemon-reload
|
# Enter the container namespace
|
||||||
ssh gitea systemctl --user restart gitea postgres
|
podman unshare
|
||||||
|
|
||||||
|
# Create the volumes
|
||||||
|
mkdir gitea_data
|
||||||
|
chown -R 1000:1000 gitea_data
|
||||||
|
mkdir gitea_etc
|
||||||
|
chown -R 1000:1000 gitea_etc
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
|
||||||
|
Now launch the service. The first user you register will be the admin.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create a systemctl viable shell
|
||||||
|
machinectl shell gitea@
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user restart gitea postgres
|
||||||
# Enables auto-update service which will pull new container images automatically every day
|
# Enables auto-update service which will pull new container images automatically every day
|
||||||
ssh gitea systemctl --user enable --now podman-auto-update.timer
|
systemctl --user enable --now podman-auto-update.timer
|
||||||
```
|
```
|
||||||
|
|
||||||
### Upgrade Quadlets
|
### Upgrade
|
||||||
|
|
||||||
|
1. Check [the blog](https://blog.gitea.com/) for any breaking changes.
|
||||||
|
2. Update the `compose.yaml` with any needed changes
|
||||||
|
3. [Regenerate the quadlets](#convert-compose-to-quadlet)
|
||||||
|
4. Upload the new quadlets and restart the service
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scp -r quadlets/. gitea:~/.config/containers/systemd/
|
# Upload quadlets and restart
|
||||||
ssh gitea systemctl --user daemon-reload
|
export PODMAN_SERVER=3dserver
|
||||||
ssh gitea systemctl --user restart gitea postgres
|
scp -r active/podman_gitea/quadlets/. $PODMAN_SERVER:/home/gitea/.config/containers/systemd/
|
||||||
|
ssh $PODMAN_SERVER chown -R gitea:gitea /home/gitea/.config/containers/systemd/
|
||||||
|
|
||||||
|
ssh $PODMAN_SERVER
|
||||||
|
machinectl shell gitea@
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user restart gitea postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
### Editing Configs within Container
|
### Editing Gitea Config
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
apk add vim
|
# Use podman unshare to work within the container's namespace
|
||||||
|
podman unshare vim ~/gitea_data/gitea/conf/app.ini
|
||||||
```
|
```
|
||||||
|
|
||||||
## Gitea Runners
|
## Gitea Runners
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ Requires=postgres.service
|
|||||||
[Container]
|
[Container]
|
||||||
AutoUpdate=registry
|
AutoUpdate=registry
|
||||||
ContainerName=gitea
|
ContainerName=gitea
|
||||||
Environment=USER_UID=1000 USER_GID=1000 GITEA__database__DB_TYPE=postgres GITEA__database__HOST=postgres:5432 GITEA__database__NAME=gitea GITEA__database__USER=gitea GITEA__database__PASSWD=gitea
|
Environment=GITEA__database__DB_TYPE=postgres GITEA__database__HOST=postgres:5432 GITEA__database__NAME=gitea GITEA__database__USER=gitea GITEA__database__PASSWD=gitea
|
||||||
Image=docker.gitea.com/gitea:1.23.7
|
Image=docker.gitea.com/gitea:1.25-rootless
|
||||||
Network=gitea.network
|
Network=gitea.network
|
||||||
PublishPort=3000:3000
|
PublishPort=3000:3000
|
||||||
PublishPort=2222:22
|
PublishPort=2222:2222
|
||||||
SecurityLabelDisable=true
|
SecurityLabelDisable=true
|
||||||
Volume=/home/gitea/gitea_data:/data
|
Volume=/home/gitea/gitea_data:/data:Z
|
||||||
|
Volume=/home/gitea/gitea_etc:/etc/gitea:Z
|
||||||
|
Volume=/home/gitea/gitea_custom:/var/lib/gitea/custom:Z
|
||||||
Volume=/etc/localtime:/etc/localtime:ro
|
Volume=/etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
[Network]
|
[Network]
|
||||||
|
IPv6=true
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
[Container]
|
[Container]
|
||||||
|
AutoUpdate=registry
|
||||||
ContainerName=postgres
|
ContainerName=postgres
|
||||||
Environment=POSTGRES_USER=gitea POSTGRES_PASSWORD=gitea POSTGRES_DB=gitea
|
Environment=POSTGRES_USER=gitea POSTGRES_PASSWORD=gitea POSTGRES_DB=gitea
|
||||||
Image=docker.io/library/postgres:15
|
Image=docker.io/library/postgres:15
|
||||||
Network=gitea.network
|
Network=gitea.network
|
||||||
SecurityLabelDisable=true
|
SecurityLabelDisable=true
|
||||||
Volume=/home/gitea/gitea_postgres:/var/lib/postgresql/data
|
Volume=/home/gitea/gitea_postgres:/var/lib/postgresql/data:Z
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|||||||
3
active/podman_immich/compose/README.md
Normal file
3
active/podman_immich/compose/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Compose
|
||||||
|
|
||||||
|
Put your compose.yaml here.
|
||||||
79
active/podman_immich/compose/compose.yaml
Normal file
79
active/podman_immich/compose/compose.yaml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#
|
||||||
|
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
|
||||||
|
#
|
||||||
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
|
#
|
||||||
|
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
#
|
||||||
|
# The compose file on main may not be compatible with the latest release.
|
||||||
|
|
||||||
|
services:
|
||||||
|
immich-server:
|
||||||
|
container_name: immich_server
|
||||||
|
image: ghcr.io/immich-app/immich-server:v2.3.1
|
||||||
|
# extends:
|
||||||
|
# file: hwaccel.transcoding.yml
|
||||||
|
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
|
||||||
|
volumes:
|
||||||
|
- /home/immich/library:/data:Z
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
- '2283:2283'
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- database
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
disable: false
|
||||||
|
networks:
|
||||||
|
- immich
|
||||||
|
|
||||||
|
immich-machine-learning:
|
||||||
|
container_name: immich_machine_learning
|
||||||
|
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
||||||
|
# Example tag: release-cuda
|
||||||
|
image: ghcr.io/immich-app/immich-machine-learning:release
|
||||||
|
# extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
|
||||||
|
# file: hwaccel.ml.yml
|
||||||
|
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||||
|
volumes:
|
||||||
|
- /home/immich/model-cache:/cache:Z
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
disable: false
|
||||||
|
networks:
|
||||||
|
- immich
|
||||||
|
|
||||||
|
redis:
|
||||||
|
container_name: immich_redis
|
||||||
|
image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571
|
||||||
|
healthcheck:
|
||||||
|
test: redis-cli ping || exit 1
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- immich
|
||||||
|
|
||||||
|
database:
|
||||||
|
container_name: immich_postgres
|
||||||
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_DB: immich
|
||||||
|
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||||
|
# Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
|
||||||
|
# DB_STORAGE_TYPE: 'HDD'
|
||||||
|
volumes:
|
||||||
|
- /home/immich/postgres:/var/lib/postgresql/data:Z
|
||||||
|
shm_size: 128mb
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- immich
|
||||||
|
|
||||||
|
networks:
|
||||||
|
immich:
|
||||||
|
enable_ipv6: true
|
||||||
193
active/podman_immich/immich.md
Normal file
193
active/podman_immich/immich.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# Podman immich
|
||||||
|
|
||||||
|
- [Podman immich](#podman-immich)
|
||||||
|
- [Setup immich Project](#setup-immich-project)
|
||||||
|
- [Install immich](#install-immich)
|
||||||
|
- [Create the immich user](#create-the-immich-user)
|
||||||
|
- [Write the immich compose spec](#write-the-immich-compose-spec)
|
||||||
|
- [A Note on Volumes](#a-note-on-volumes)
|
||||||
|
- [Convert immich compose spec to quadlets](#convert-immich-compose-spec-to-quadlets)
|
||||||
|
- [Create any container-mounted directories](#create-any-container-mounted-directories)
|
||||||
|
- [Start and enable your systemd quadlet](#start-and-enable-your-systemd-quadlet)
|
||||||
|
- [Expose immich](#expose-immich)
|
||||||
|
- [firewalld](#firewalld)
|
||||||
|
- [Backup immich](#backup-immich)
|
||||||
|
- [Upgrade immich](#upgrade-immich)
|
||||||
|
- [Upgrade Quadlets](#upgrade-quadlets)
|
||||||
|
- [Upload Images in Bulk](#upload-images-in-bulk)
|
||||||
|
- [Uninstall](#uninstall)
|
||||||
|
- [Notes](#notes)
|
||||||
|
- [SELinux](#selinux)
|
||||||
|
|
||||||
|
## Setup immich Project
|
||||||
|
|
||||||
|
- [x] Copy and rename this folder to active/podman_immich
|
||||||
|
- [x] Find and replace immich with the name of the service.
|
||||||
|
- [x] Create the rootless user to run the podman containers
|
||||||
|
- [ ] Write the compose.yaml spec for your service
|
||||||
|
- [ ] Convert the compose.yaml spec to a quadlet
|
||||||
|
- [ ] Install the quadlet on the podman server
|
||||||
|
- [ ] Expose the quadlet service
|
||||||
|
- [ ] Install a backup service and timer
|
||||||
|
|
||||||
|
## Install immich
|
||||||
|
|
||||||
|
### Create the immich user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH into your podman server as root
|
||||||
|
useradd immich
|
||||||
|
loginctl enable-linger $(id -u immich)
|
||||||
|
systemctl --user --machine=immich@.host enable podman-restart
|
||||||
|
systemctl --user --machine=immich@.host enable --now podman.socket
|
||||||
|
mkdir -p /home/immich/.config/containers/systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write the immich compose spec
|
||||||
|
|
||||||
|
1. Pull down the immich files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull the compose file
|
||||||
|
wget -O active/podman_immich/release-compose.yaml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
|
||||||
|
# Pull the .env file
|
||||||
|
wget -O active/podman_immich/release-env https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit the compose.yaml. Replace all environment variables with their correct values.
|
||||||
|
3. Edit the .env file. Make sure to match exactly what is in the compose file.
|
||||||
|
|
||||||
|
#### A Note on Volumes
|
||||||
|
|
||||||
|
Named volumes are stored at `/home/immich/.local/share/containers/storage/volumes/`.
|
||||||
|
|
||||||
|
### Convert immich compose spec to quadlets
|
||||||
|
|
||||||
|
Run the following to convert a compose.yaml into the various `.container` files for systemd:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate the systemd service
|
||||||
|
podman run \
|
||||||
|
--security-opt label=disable \
|
||||||
|
--rm \
|
||||||
|
-v $(pwd)/active/podman_immich/compose:/compose \
|
||||||
|
-v $(pwd)/active/podman_immich/quadlets:/quadlets \
|
||||||
|
quay.io/k9withabone/podlet \
|
||||||
|
-f /quadlets \
|
||||||
|
-i \
|
||||||
|
--overwrite \
|
||||||
|
compose /compose/compose.yaml
|
||||||
|
|
||||||
|
# Copy the files to the server
|
||||||
|
export PODMAN_SERVER=3dserver
|
||||||
|
scp -r active/podman_immich/quadlets/. $PODMAN_SERVER:/home/immich/.config/containers/systemd/
|
||||||
|
ssh $PODMAN_SERVER chown -R immich:immich /home/immich/.config/containers/systemd/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create any container-mounted directories
|
||||||
|
|
||||||
|
SSH into your podman server as root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
machinectl shell immich@
|
||||||
|
podman unshare
|
||||||
|
mkdir library postgres model-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start and enable your systemd quadlet
|
||||||
|
|
||||||
|
SSH into your podman server as root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
machinectl shell immich@
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user restart immich-server.service immich-machine-learning.service
|
||||||
|
# Enable auto-update service which will pull new container images automatically every day
|
||||||
|
systemctl --user enable --now podman-auto-update.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Expose immich
|
||||||
|
|
||||||
|
1. If you need a domain, follow the [DDNS instructions](/active/podman_ddns/ddns.md#install-a-new-ddns-service)
|
||||||
|
2. For a web service, follow the [Caddy instructions](/active/podman_caddy/caddy.md#adding-a-new-caddy-record)
|
||||||
|
3. Finally, follow your OS's guide for opening ports via its firewall service.
|
||||||
|
|
||||||
|
#### firewalld
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# command to get current active zone and default zone
|
||||||
|
firewall-cmd --get-active-zones
|
||||||
|
firewall-cmd --get-default-zone
|
||||||
|
|
||||||
|
# command to open 443 on tcp
|
||||||
|
firewall-cmd --permanent --zone=<zone> --add-port=443/tcp
|
||||||
|
|
||||||
|
# command to open 80 and 443 on tcp and udp
|
||||||
|
firewall-cmd --permanent --zone=<zone> --add-port={80,443}/{tcp,udp}
|
||||||
|
|
||||||
|
# command to list available services and then open http and https
|
||||||
|
firewall-cmd --get-services
|
||||||
|
firewall-cmd --permanent --zone=<zone> --add-service={http,https}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup immich
|
||||||
|
|
||||||
|
Follow the [Borg Backup instructions](/active/systemd_borg/borg.md#set-up-a-client-for-backup)
|
||||||
|
|
||||||
|
## Upgrade immich
|
||||||
|
|
||||||
|
### Upgrade Quadlets
|
||||||
|
|
||||||
|
Upgrades should be a repeat of [writing the compose spec](#convert-immich-compose-spec-to-quadlets) and [installing the quadlets](#start-and-enable-your-systemd-quadlet)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PODMAN_SERVER=
|
||||||
|
scp -r quadlets/. $PODMAN_SERVER$:/home/immich/.config/containers/systemd/
|
||||||
|
ssh immich systemctl --user daemon-reload
|
||||||
|
ssh immich systemctl --user restart immich
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upload Images in Bulk
|
||||||
|
|
||||||
|
<https://docs.immich.app/features/command-line-interface/>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install the CLI
|
||||||
|
npm i -g @immich/cli
|
||||||
|
|
||||||
|
# immich login [url] [key]
|
||||||
|
immich login http://192.168.1.216:2283/api HFEJ38DNSDUEG
|
||||||
|
|
||||||
|
# Check the upload
|
||||||
|
immich upload --dry-run --recursive directory/
|
||||||
|
|
||||||
|
# Upload
|
||||||
|
immich upload --recursive directory/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstall
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop the user's services
|
||||||
|
systemctl --user disable podman-restart
|
||||||
|
podman container stop --all
|
||||||
|
systemctl --user disable --now podman.socket
|
||||||
|
systemctl --user disable --now podman-auto-update.timer
|
||||||
|
|
||||||
|
# Delete the user (this won't delete their home directory)
|
||||||
|
# userdel might spit out an error like:
|
||||||
|
# userdel: user immich is currently used by process 591255
|
||||||
|
# kill those processes and try again
|
||||||
|
userdel immich
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### SELinux
|
||||||
|
|
||||||
|
<https://blog.christophersmart.com/2021/01/31/podman-volumes-and-selinux/>
|
||||||
|
|
||||||
|
:z allows a container to share a mounted volume with all other containers.
|
||||||
|
|
||||||
|
:Z allows a container to reserve a mounted volume and prevents any other container from accessing.
|
||||||
26
active/podman_immich/quadlets/.env
Normal file
26
active/podman_immich/quadlets/.env
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables
|
||||||
|
|
||||||
|
# The location where your uploaded files are stored
|
||||||
|
UPLOAD_LOCATION=/home/immich/library
|
||||||
|
|
||||||
|
# The location where your database files are stored. Network shares are not supported for the database
|
||||||
|
DB_DATA_LOCATION=/home/immich/postgres
|
||||||
|
|
||||||
|
# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||||
|
TZ=Etc/EST
|
||||||
|
|
||||||
|
# The Immich version to use. You can pin this to a specific version like "v1.71.0"
|
||||||
|
IMMICH_VERSION=release
|
||||||
|
|
||||||
|
# Connection secret for postgres. You should change it to a random password
|
||||||
|
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
|
DB_PASSWORD=postgres
|
||||||
|
|
||||||
|
# The values below this line do not need to be changed
|
||||||
|
###################################################################################
|
||||||
|
DB_USERNAME=postgres
|
||||||
|
DB_DATABASE_NAME=immich
|
||||||
|
|
||||||
|
# Should match the container_name fields in the compose.yaml
|
||||||
|
REDIS_HOSTNAME=immich_redis
|
||||||
|
DB_HOSTNAME=immich_postgres
|
||||||
3
active/podman_immich/quadlets/README.md
Normal file
3
active/podman_immich/quadlets/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Quadlets
|
||||||
|
|
||||||
|
Put your quadlets here.
|
||||||
13
active/podman_immich/quadlets/database.container
Normal file
13
active/podman_immich/quadlets/database.container
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Container]
|
||||||
|
ContainerName=immich_postgres
|
||||||
|
Environment=POSTGRES_PASSWORD=postgres POSTGRES_USER=postgres POSTGRES_DB=immich POSTGRES_INITDB_ARGS=--data-checksums
|
||||||
|
Image=ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
|
||||||
|
Network=immich.network
|
||||||
|
ShmSize=128mb
|
||||||
|
Volume=/home/immich/postgres:/var/lib/postgresql/data:Z
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[Container]
|
||||||
|
ContainerName=immich_machine_learning
|
||||||
|
EnvironmentFile=.env
|
||||||
|
Image=ghcr.io/immich-app/immich-machine-learning:release
|
||||||
|
Network=immich.network
|
||||||
|
Volume=/home/immich/model-cache:/cache:Z
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
17
active/podman_immich/quadlets/immich-server.container
Normal file
17
active/podman_immich/quadlets/immich-server.container
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
Requires=redis.service database.service
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=immich_server
|
||||||
|
EnvironmentFile=.env
|
||||||
|
Image=ghcr.io/immich-app/immich-server:v2.3.1
|
||||||
|
Network=immich.network
|
||||||
|
PublishPort=2283:2283
|
||||||
|
Volume=/home/immich/library:/data:Z
|
||||||
|
Volume=/etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
5
active/podman_immich/quadlets/immich.network
Normal file
5
active/podman_immich/quadlets/immich.network
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[Network]
|
||||||
|
IPv6=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
11
active/podman_immich/quadlets/redis.container
Normal file
11
active/podman_immich/quadlets/redis.container
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[Container]
|
||||||
|
ContainerName=immich_redis
|
||||||
|
HealthCmd=redis-cli ping || exit 1
|
||||||
|
Image=docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571
|
||||||
|
Network=immich.network
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
74
active/podman_immich/release-compose.yaml
Normal file
74
active/podman_immich/release-compose.yaml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#
|
||||||
|
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
|
||||||
|
#
|
||||||
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
|
#
|
||||||
|
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
#
|
||||||
|
# The compose file on main may not be compatible with the latest release.
|
||||||
|
|
||||||
|
name: immich
|
||||||
|
|
||||||
|
services:
|
||||||
|
immich-server:
|
||||||
|
container_name: immich_server
|
||||||
|
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||||
|
# extends:
|
||||||
|
# file: hwaccel.transcoding.yml
|
||||||
|
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
|
||||||
|
volumes:
|
||||||
|
# Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
|
||||||
|
- ${UPLOAD_LOCATION}:/data
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
- '2283:2283'
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- database
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
disable: false
|
||||||
|
|
||||||
|
immich-machine-learning:
|
||||||
|
container_name: immich_machine_learning
|
||||||
|
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
||||||
|
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
||||||
|
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||||
|
# extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
|
||||||
|
# file: hwaccel.ml.yml
|
||||||
|
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||||
|
volumes:
|
||||||
|
- model-cache:/cache
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
disable: false
|
||||||
|
|
||||||
|
redis:
|
||||||
|
container_name: immich_redis
|
||||||
|
image: docker.io/valkey/valkey:8@sha256:81db6d39e1bba3b3ff32bd3a1b19a6d69690f94a3954ec131277b9a26b95b3aa
|
||||||
|
healthcheck:
|
||||||
|
test: redis-cli ping || exit 1
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
database:
|
||||||
|
container_name: immich_postgres
|
||||||
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
|
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||||
|
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||||
|
# Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
|
||||||
|
# DB_STORAGE_TYPE: 'HDD'
|
||||||
|
volumes:
|
||||||
|
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
|
||||||
|
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
|
||||||
|
shm_size: 128mb
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
model-cache:
|
||||||
22
active/podman_immich/release-env
Normal file
22
active/podman_immich/release-env
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables
|
||||||
|
|
||||||
|
# The location where your uploaded files are stored
|
||||||
|
UPLOAD_LOCATION=./library
|
||||||
|
|
||||||
|
# The location where your database files are stored. Network shares are not supported for the database
|
||||||
|
DB_DATA_LOCATION=./postgres
|
||||||
|
|
||||||
|
# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||||
|
# TZ=Etc/UTC
|
||||||
|
|
||||||
|
# The Immich version to use. You can pin this to a specific version like "v2.1.0"
|
||||||
|
IMMICH_VERSION=v2
|
||||||
|
|
||||||
|
# Connection secret for postgres. You should change it to a random password
|
||||||
|
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
|
DB_PASSWORD=postgres
|
||||||
|
|
||||||
|
# The values below this line do not need to be changed
|
||||||
|
###################################################################################
|
||||||
|
DB_USERNAME=postgres
|
||||||
|
DB_DATABASE_NAME=immich
|
||||||
@@ -1,64 +1,59 @@
|
|||||||
# Jellyfin
|
# Jellyfin
|
||||||
|
|
||||||
|
- [Jellyfin](#jellyfin)
|
||||||
|
- [Install](#install)
|
||||||
|
- [Upgrade](#upgrade)
|
||||||
|
- [Mounting Media Directory](#mounting-media-directory)
|
||||||
|
|
||||||
They have podman rootless instructions!
|
They have podman rootless instructions!
|
||||||
|
|
||||||
<https://jellyfin.org/docs/general/installation/container/#managing-via-systemd>
|
<https://jellyfin.org/docs/general/installation/container/#managing-via-systemd>
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
### Create jellyfin btrfs volume
|
1. Create the jellyfin user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
useradd jellyfin
|
||||||
|
loginctl enable-linger $(id -u jellyfin)
|
||||||
|
systemctl --user --machine=jellyfin@.host enable podman-restart
|
||||||
|
systemctl --user --machine=jellyfin@.host enable --now podman.socket
|
||||||
|
mkdir -p /home/jellyfin/.config/containers/systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update the jellyfin record in Caddy.
|
||||||
|
3. Open port 8096 in the firewall.
|
||||||
|
4. Copy the files to the server and start the service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PODMAN_SERVER=3dserver
|
||||||
|
scp -r active/podman_jellyfin/quadlets/. $PODMAN_SERVER:/home/jellyfin/.config/containers/systemd/
|
||||||
|
ssh $PODMAN_SERVER chown -R jellyfin:jellyfin /home/jellyfin/.config/containers/systemd/
|
||||||
|
|
||||||
|
ssh $PODMAN_SERVER
|
||||||
|
machinectl shell jellyfin@
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user restart jellyfin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrade
|
||||||
|
|
||||||
|
1. Check [the blog](https://jellyfin.org/posts) for breaking changes
|
||||||
|
2. Update the `jellyfin.container` with the new image version
|
||||||
|
3. Update quadlets and restart the service
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
btrfs sub create /btrfs/jellyfin
|
# Upload quadlets and restart
|
||||||
|
export PODMAN_SERVER=3dserver
|
||||||
|
scp -r active/podman_jellyfin/quadlets/. $PODMAN_SERVER:/home/jellyfin/.config/containers/systemd/
|
||||||
|
ssh $PODMAN_SERVER chown -R jellyfin:jellyfin /home/jellyfin/.config/containers/systemd/
|
||||||
|
|
||||||
|
ssh $PODMAN_SERVER
|
||||||
|
machinectl shell jellyfin@
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user restart jellyfin
|
||||||
```
|
```
|
||||||
|
|
||||||
Add /home/jellyfin mount to /etc/fstab
|
|
||||||
|
|
||||||
```bash
|
|
||||||
systemctl daemon-reload
|
|
||||||
mount -a --mkdir
|
|
||||||
```
|
|
||||||
|
|
||||||
### Create the jellyfin user
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export JF_USER=jellyfin
|
|
||||||
useradd $JF_USER
|
|
||||||
|
|
||||||
su -l $JF_USER
|
|
||||||
ssh-keygen
|
|
||||||
exit
|
|
||||||
cp ~/.ssh/authorized_keys /home/$JF_USER/.ssh/authorized_keys
|
|
||||||
chown $JF_USER:$JF_USER /home/$JF_USER/.ssh/authorized_keys
|
|
||||||
loginctl enable-linger $(id -u $JF_USER)
|
|
||||||
```
|
|
||||||
|
|
||||||
SSH into the server as jellyfin
|
|
||||||
|
|
||||||
```bash
|
|
||||||
systemctl --user enable podman-restart
|
|
||||||
systemctl --user enable --now podman.socket
|
|
||||||
mkdir -p ~/.config/containers/systemd
|
|
||||||
mkdir jellyfin-config jellyfin-cache jellyfin-media
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install jellyfin
|
|
||||||
|
|
||||||
~/.config/containers/systemd/jellyfin.container
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy the files to the server
|
|
||||||
scp -r active/podman_jellyfin/quadlets/. jellyfin:~/.config/containers/systemd/
|
|
||||||
ssh jellyfin systemctl --user daemon-reload
|
|
||||||
ssh jellyfin systemctl --user start jellyfin
|
|
||||||
ssh jellyfin journalctl --user -xeu jellyfin
|
|
||||||
ssh jellyfin systemctl --user enable --now podman-auto-update.timer
|
|
||||||
```
|
|
||||||
|
|
||||||
Update the jellyfin record in Caddy.
|
|
||||||
|
|
||||||
Open port 8096 in the firewall.
|
|
||||||
|
|
||||||
## Mounting Media Directory
|
## Mounting Media Directory
|
||||||
|
|
||||||
Update /etc/fstab with the smb disk details.
|
Update /etc/fstab with the smb disk details.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[Container]
|
[Container]
|
||||||
Image=docker.io/jellyfin/jellyfin:latest
|
Image=docker.io/jellyfin/jellyfin:10.11.3
|
||||||
AutoUpdate=registry
|
AutoUpdate=registry
|
||||||
PublishPort=8096:8096/tcp
|
PublishPort=8096:8096/tcp
|
||||||
UserNS=keep-id
|
UserNS=keep-id
|
||||||
SecurityLabelDisable=true
|
SecurityLabelDisable=true
|
||||||
Volume=/home/jellyfin/jellyfin-config:/config:Z
|
Volume=/home/jellyfin/jellyfin-config:/config:Z
|
||||||
Volume=/home/jellyfin/jellyfin-cache:/cache:Z
|
Volume=/home/jellyfin/jellyfin-cache:/cache:Z
|
||||||
Volume=/home/jellyfin/jellyfin-media:/media:Z
|
Volume=/var/media:/media:Z
|
||||||
Network=jellyfin.network
|
Network=jellyfin.network
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|||||||
3
active/podman_matrix/compose/README.md
Normal file
3
active/podman_matrix/compose/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Compose
|
||||||
|
|
||||||
|
Put your compose.yaml here.
|
||||||
28
active/podman_matrix/compose/compose.yaml
Normal file
28
active/podman_matrix/compose/compose.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# tuwunel
|
||||||
|
|
||||||
|
services:
|
||||||
|
matrix:
|
||||||
|
image: ghcr.io/matrix-construct/tuwunel:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 8448:6167
|
||||||
|
volumes:
|
||||||
|
- /home/matrix/tuwunel-db:/var/lib/tuwunel
|
||||||
|
#- ./tuwunel.toml:/etc/tuwunel.toml
|
||||||
|
security_opt:
|
||||||
|
- "label=disable"
|
||||||
|
userns_mode: keep-id
|
||||||
|
environment:
|
||||||
|
TUWUNEL_SERVER_NAME: matrix.reeseapps.com # EDIT THIS
|
||||||
|
TUWUNEL_DATABASE_PATH: /var/lib/tuwunel
|
||||||
|
TUWUNEL_PORT: 6167
|
||||||
|
TUWUNEL_MAX_REQUEST_SIZE: 200000000 # in bytes, ~200 MB
|
||||||
|
TUWUNEL_ALLOW_REGISTRATION: 'false'
|
||||||
|
# TUWUNEL_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||||
|
#TUWUNEL_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||||
|
TUWUNEL_ALLOW_FEDERATION: 'true'
|
||||||
|
TUWUNEL_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||||
|
TUWUNEL_TRUSTED_SERVERS: '["matrix.org"]'
|
||||||
|
#TUWUNEL_LOG: warn,state_res=warn
|
||||||
|
TUWUNEL_ADDRESS: 0.0.0.0
|
||||||
|
#TUWUNEL_CONFIG: '/etc/tuwunel.toml' # Uncomment if you mapped config toml above
|
||||||
145
active/podman_matrix/matrix.md
Normal file
145
active/podman_matrix/matrix.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# Podman matrix
|
||||||
|
|
||||||
|
- [Podman matrix](#podman-matrix)
|
||||||
|
- [Setup matrix Project](#setup-matrix-project)
|
||||||
|
- [Install matrix](#install-matrix)
|
||||||
|
- [Create the matrix user](#create-the-matrix-user)
|
||||||
|
- [Write the matrix compose spec](#write-the-matrix-compose-spec)
|
||||||
|
- [A Note on Volumes](#a-note-on-volumes)
|
||||||
|
- [Convert matrix compose spec to quadlets](#convert-matrix-compose-spec-to-quadlets)
|
||||||
|
- [Setup matrix users](#setup-matrix-users)
|
||||||
|
- [Expose matrix](#expose-matrix)
|
||||||
|
- [firewalld](#firewalld)
|
||||||
|
- [Backup matrix](#backup-matrix)
|
||||||
|
- [Upgrade matrix](#upgrade-matrix)
|
||||||
|
- [Upgrade Quadlets](#upgrade-quadlets)
|
||||||
|
- [Notes](#notes)
|
||||||
|
- [SELinux](#selinux)
|
||||||
|
|
||||||
|
## Setup matrix Project
|
||||||
|
|
||||||
|
- [x] Copy and rename this folder to active/podman_matrix
|
||||||
|
- [x] Find and replace matrix with the name of the service.
|
||||||
|
- [x] Create the rootless user to run the podman containers
|
||||||
|
- [x] Write the compose.yaml spec for your service
|
||||||
|
- [x] Convert the compose.yaml spec to a quadlet
|
||||||
|
- [x] Install the quadlet on the podman server
|
||||||
|
- [ ] Expose the quadlet service
|
||||||
|
- [ ] Install a backup service and timer
|
||||||
|
|
||||||
|
## Install matrix
|
||||||
|
|
||||||
|
### Create the matrix user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH into your podman server as root
|
||||||
|
useradd matrix
|
||||||
|
loginctl enable-linger $(id -u matrix)
|
||||||
|
systemctl --user --machine=matrix@.host enable podman-restart
|
||||||
|
systemctl --user --machine=matrix@.host enable --now podman.socket
|
||||||
|
mkdir -p /home/matrix/.config/containers/systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write the matrix compose spec
|
||||||
|
|
||||||
|
Edit the compose.yaml at active/matrix/compose/compose.yaml
|
||||||
|
|
||||||
|
#### A Note on Volumes
|
||||||
|
|
||||||
|
Named volumes are stored at `~/.local/share/containers/storage/volumes/`.
|
||||||
|
|
||||||
|
### Convert matrix compose spec to quadlets
|
||||||
|
|
||||||
|
On your local machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate the systemd service
|
||||||
|
podman run \
|
||||||
|
--security-opt label=disable \
|
||||||
|
--rm \
|
||||||
|
-v $(pwd)/active/podman_matrix/compose:/compose \
|
||||||
|
-v $(pwd)/active/podman_matrix/quadlets:/quadlets \
|
||||||
|
quay.io/k9withabone/podlet \
|
||||||
|
-f /quadlets \
|
||||||
|
-i \
|
||||||
|
--overwrite \
|
||||||
|
compose /compose/compose.yaml
|
||||||
|
|
||||||
|
# Copy the files to the server
|
||||||
|
scp -r active/podman_matrix/quadlets/. matrix:~/.config/containers/systemd/
|
||||||
|
|
||||||
|
# Copy the compose files to the server
|
||||||
|
scp -r active/podman_matrix/compose/. matrix:~/.config//
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh matrix systemctl --user daemon-reload
|
||||||
|
ssh matrix systemctl --user restart matrix
|
||||||
|
# Enables auto-update service which will pull new container images automatically every day
|
||||||
|
ssh matrix systemctl --user enable --now podman-auto-update.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup matrix users
|
||||||
|
|
||||||
|
```bash
|
||||||
|
podman run \
|
||||||
|
-v /home/matrix/tuwunel-db:/var/lib/tuwunel:Z \
|
||||||
|
-e TUWUNEL_SERVER_NAME=matrix.reeseapps.com \
|
||||||
|
-e TUWUNEL_DATABASE_PATH=/var/lib/tuwunel \
|
||||||
|
--userns=keep-id \
|
||||||
|
--
|
||||||
|
-it \
|
||||||
|
--rm \
|
||||||
|
ghcr.io/matrix-construct/tuwunel:latest \
|
||||||
|
--execute "users create_user ducoterra"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Expose matrix
|
||||||
|
|
||||||
|
1. If you need a domain, follow the [DDNS instructions](/active/podman_ddns/ddns.md#install-a-new-ddns-service)
|
||||||
|
2. For a web service, follow the [Caddy instructions](/active/podman_caddy/caddy.md#adding-a-new-caddy-record)
|
||||||
|
3. Finally, follow your OS's guide for opening ports via its firewall service.
|
||||||
|
|
||||||
|
#### firewalld
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# command to get current active zone and default zone
|
||||||
|
firewall-cmd --get-active-zones
|
||||||
|
firewall-cmd --get-default-zone
|
||||||
|
|
||||||
|
# command to open 443 on tcp
|
||||||
|
firewall-cmd --permanent --zone=<zone> --add-port=443/tcp
|
||||||
|
|
||||||
|
# command to open 80 and 443 on tcp and udp
|
||||||
|
firewall-cmd --permanent --zone=<zone> --add-port={80,443}/{tcp,udp}
|
||||||
|
|
||||||
|
# command to list available services and then open http and https
|
||||||
|
firewall-cmd --get-services
|
||||||
|
firewall-cmd --permanent --zone=<zone> --add-service={http,https}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup matrix
|
||||||
|
|
||||||
|
Follow the [Borg Backup instructions](/active/systemd_borg/borg.md#set-up-a-client-for-backup)
|
||||||
|
|
||||||
|
## Upgrade matrix
|
||||||
|
|
||||||
|
### Upgrade Quadlets
|
||||||
|
|
||||||
|
Upgrades should be a repeat of [writing the compose spec](#convert-compose-to-quadlet) and [installing the quadlets](#convert-compose-to-quadlet)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp -r quadlets/. matrix:~/.config/containers/systemd/
|
||||||
|
ssh matrix systemctl --user daemon-reload
|
||||||
|
ssh matrix systemctl --user restart matrix
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### SELinux
|
||||||
|
|
||||||
|
<https://blog.christophersmart.com/2021/01/31/podman-volumes-and-selinux/>
|
||||||
|
|
||||||
|
:z allows a container to share a mounted volume with all other containers.
|
||||||
|
|
||||||
|
:Z allows a container to reserve a mounted volume and prevents any other container from accessing.
|
||||||
3
active/podman_matrix/quadlets/README.md
Normal file
3
active/podman_matrix/quadlets/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Quadlets
|
||||||
|
|
||||||
|
Put your quadlets here.
|
||||||
13
active/podman_matrix/quadlets/matrix.container
Normal file
13
active/podman_matrix/quadlets/matrix.container
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Container]
|
||||||
|
Environment=TUWUNEL_SERVER_NAME=matrix.reeseapps.com TUWUNEL_DATABASE_PATH=/var/lib/tuwunel TUWUNEL_PORT=6167 TUWUNEL_MAX_REQUEST_SIZE=200000000 TUWUNEL_ALLOW_REGISTRATION=false TUWUNEL_ALLOW_FEDERATION=true TUWUNEL_ALLOW_CHECK_FOR_UPDATES=true TUWUNEL_TRUSTED_SERVERS=["matrix.org"] TUWUNEL_ADDRESS=0.0.0.0
|
||||||
|
Image=ghcr.io/matrix-construct/tuwunel:latest
|
||||||
|
PublishPort=8448:6167
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
UserNS=keep-id
|
||||||
|
Volume=/home/matrix/tuwunel-db:/var/lib/tuwunel
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
31
active/podman_minecraft/compose.yaml
Normal file
31
active/podman_minecraft/compose.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
services:
|
||||||
|
testcraft:
|
||||||
|
image: gitea.reeseapps.com/services/minecraft:c1ca80b09b4645888e550efb0a2700b2ec1f1645
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
volumes:
|
||||||
|
- /home/minecraft/testcraft:/mc_data
|
||||||
|
ports:
|
||||||
|
- 25565:25565
|
||||||
|
environment:
|
||||||
|
- MAX_RAM=4
|
||||||
|
- MIN_RAM=1
|
||||||
|
security_opt:
|
||||||
|
- "label=disable"
|
||||||
|
userns_mode: keep-id
|
||||||
|
restart: always
|
||||||
|
nimcraft:
|
||||||
|
image: gitea.reeseapps.com/services/minecraft:c1ca80b09b4645888e550efb0a2700b2ec1f1645
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
volumes:
|
||||||
|
- /home/minecraft/nimcraft:/mc_data
|
||||||
|
ports:
|
||||||
|
- 25566:25565
|
||||||
|
environment:
|
||||||
|
- MAX_RAM=4
|
||||||
|
- MIN_RAM=1
|
||||||
|
security_opt:
|
||||||
|
- "label=disable"
|
||||||
|
userns_mode: keep-id
|
||||||
|
restart: always
|
||||||
121
active/podman_minecraft/minecraft.md
Normal file
121
active/podman_minecraft/minecraft.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Podman Template
|
||||||
|
|
||||||
|
- [Podman Template](#podman-template)
|
||||||
|
- [Install minecraft](#install-minecraft)
|
||||||
|
- [Create the minecraft user](#create-the-minecraft-user)
|
||||||
|
- [Convert Compose to Quadlet](#convert-compose-to-quadlet)
|
||||||
|
- [Install Quadlets](#install-quadlets)
|
||||||
|
- [Upgrade Quadlets](#upgrade-quadlets)
|
||||||
|
- [Expose minecraft](#expose-minecraft)
|
||||||
|
- [Backup minecraft](#backup-minecraft)
|
||||||
|
|
||||||
|
## Install minecraft
|
||||||
|
|
||||||
|
Find and replace minecraft with the name of the service.
|
||||||
|
|
||||||
|
### Create the minecraft user
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH into your podman server as root
|
||||||
|
useradd minecraft
|
||||||
|
loginctl enable-linger $(id -u minecraft)
|
||||||
|
systemctl --user --machine=minecraft@.host enable podman-restart
|
||||||
|
systemctl --user --machine=minecraft@.host enable --now podman.socket
|
||||||
|
mkdir -p /home/minecraft/.config/containers/systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Convert Compose to Quadlet
|
||||||
|
|
||||||
|
Create a folder called `quadlets` in your podman_minecraft project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate the systemd service
|
||||||
|
podman run \
|
||||||
|
--security-opt label=disable \
|
||||||
|
--userns keep-id \
|
||||||
|
--rm \
|
||||||
|
-v $(pwd)/active/podman_minecraft:/compose \
|
||||||
|
-v $(pwd)/active/podman_minecraft/quadlets:/quadlets \
|
||||||
|
quay.io/k9withabone/podlet \
|
||||||
|
-f /quadlets \
|
||||||
|
-i \
|
||||||
|
--overwrite \
|
||||||
|
compose /compose/compose.yaml
|
||||||
|
|
||||||
|
# Copy the files to the server
|
||||||
|
scp -r active/podman_minecraft/quadlets/. minecraft:~/.config/containers/systemd/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Quadlets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh minecraft
|
||||||
|
|
||||||
|
export GAME_SERVER_NAME=testcraft
|
||||||
|
mkdir $GAME_SERVER_NAME
|
||||||
|
|
||||||
|
# Download the server jar (only needed once)
|
||||||
|
podman run \
|
||||||
|
-it \
|
||||||
|
--rm \
|
||||||
|
-e SERVER_VERSION=1.21.8 \
|
||||||
|
-v $(pwd)/$GAME_SERVER_NAME:/downloads \
|
||||||
|
--security-opt label=disable \
|
||||||
|
--userns keep-id \
|
||||||
|
docker.io/ducoterra/get-minecraft:latest
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user restart $GAME_SERVER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Upgrade Quadlets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp -r quadlets/. minecraft:~/.config/containers/systemd/
|
||||||
|
ssh minecraft systemctl --user daemon-reload
|
||||||
|
ssh minecraft systemctl --user restart minecraft
|
||||||
|
```
|
||||||
|
|
||||||
|
## Expose minecraft
|
||||||
|
|
||||||
|
1. Create your minecraft ddns record first [following these docs](/active/podman_ddns/ddns.md#)
|
||||||
|
2. Create a SRV record in your DNS provider like the following:
|
||||||
|
|
||||||
|
active/podman_minecraft/secrets/reeseapps_records.json:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Comment": "CREATE/UPSERT/DELETE a record ",
|
||||||
|
"Changes": [
|
||||||
|
{
|
||||||
|
"Action": "UPSERT",
|
||||||
|
"ResourceRecordSet": {
|
||||||
|
"Name": "_minecraft._tcp.testcraft.reeseapps.com",
|
||||||
|
"Type": "SRV",
|
||||||
|
"TTL": 300,
|
||||||
|
"ResourceRecords": [
|
||||||
|
{
|
||||||
|
"Value": "0 5 25566 minecraft.reeseapps.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aws route53 change-resource-record-sets --hosted-zone-id $(cat active/aws_route53/secrets/reeseapps-zoneid) --change-batch file://active/podman_minecraft/secrets/reeseapps_records.json
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Test your record with `nslookup`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nslookup -q=srv _minecraft._tcp.testcraft.reeseapps.com
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Access your server at your domain "testcraft.reeseapps.com"
|
||||||
|
|
||||||
|
## Backup minecraft
|
||||||
|
|
||||||
|
Follow the Borg [Create a Backup Service Docs](/active/systemd_borg/borg.md#create-a-backup-service)
|
||||||
14
active/podman_minecraft/quadlets/nimcraft.container
Normal file
14
active/podman_minecraft/quadlets/nimcraft.container
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[Container]
|
||||||
|
Environment=MAX_RAM=4 MIN_RAM=1
|
||||||
|
Image=gitea.reeseapps.com/services/minecraft:c1ca80b09b4645888e550efb0a2700b2ec1f1645
|
||||||
|
PodmanArgs=--interactive --tty
|
||||||
|
PublishPort=25566:25565
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
UserNS=keep-id
|
||||||
|
Volume=/home/minecraft/nimcraft:/mc_data
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
14
active/podman_minecraft/quadlets/testcraft.container
Normal file
14
active/podman_minecraft/quadlets/testcraft.container
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[Container]
|
||||||
|
Environment=MAX_RAM=4 MIN_RAM=1
|
||||||
|
Image=gitea.reeseapps.com/services/minecraft:c1ca80b09b4645888e550efb0a2700b2ec1f1645
|
||||||
|
PodmanArgs=--interactive --tty
|
||||||
|
PublishPort=25565:25565
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
UserNS=keep-id
|
||||||
|
Volume=/home/minecraft/testcraft:/mc_data
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Nextcloud AIO Master Container
|
||||||
|
Documentation=https://github.com/nextcloud/all-in-one/blob/main/docker-rootless.md
|
||||||
|
After=local-fs.target
|
||||||
|
Requires=podman.socket
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=nextcloud-aio-mastercontainer
|
||||||
|
Image=docker.io/nextcloud/all-in-one:latest
|
||||||
|
PublishPort=0.0.0.0:11001:8080
|
||||||
|
Volume=nextcloud_aio_mastercontainer:/mnt/docker-aio-config
|
||||||
|
Volume=/run/user/1002/podman/podman.sock:/var/run/docker.sock:Z
|
||||||
|
Network=bridge
|
||||||
|
SecurityLabelDisable=true
|
||||||
|
|
||||||
|
Environment=APACHE_PORT=11000
|
||||||
|
Environment=APACHE_IP_BINDING=0.0.0.0
|
||||||
|
Environment=WATCHTOWER_DOCKER_SOCKET_PATH=/run/user/1002/podman/podman.sock
|
||||||
|
Environment=NEXTCLOUD_DATADIR="/home/nextcloud/nextcloud_data"
|
||||||
|
Environment=SKIP_DOMAIN_VALIDATION=true
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
@@ -3,13 +3,13 @@
|
|||||||
- [Nextcloud AIO](#nextcloud-aio)
|
- [Nextcloud AIO](#nextcloud-aio)
|
||||||
- [Install with Rootless Podman](#install-with-rootless-podman)
|
- [Install with Rootless Podman](#install-with-rootless-podman)
|
||||||
- [Create the nextcloud user](#create-the-nextcloud-user)
|
- [Create the nextcloud user](#create-the-nextcloud-user)
|
||||||
- [Install Podman](#install-podman)
|
|
||||||
- [Create the container autostart service](#create-the-container-autostart-service)
|
- [Create the container autostart service](#create-the-container-autostart-service)
|
||||||
- [Install Nextcloud](#install-nextcloud)
|
- [Install Nextcloud](#install-nextcloud)
|
||||||
- [Install Caddy](#install-caddy)
|
- [Install Caddy](#install-caddy)
|
||||||
- [Firewall](#firewall)
|
- [Firewall](#firewall)
|
||||||
- [Backups](#backups)
|
- [Backups](#backups)
|
||||||
- [Maintenace Mode](#maintenace-mode)
|
- [Manual Backups](#manual-backups)
|
||||||
|
- [Maintenance Mode](#maintenance-mode)
|
||||||
- [Trusted Proxy](#trusted-proxy)
|
- [Trusted Proxy](#trusted-proxy)
|
||||||
- [Default phone region](#default-phone-region)
|
- [Default phone region](#default-phone-region)
|
||||||
- [Adding existing files](#adding-existing-files)
|
- [Adding existing files](#adding-existing-files)
|
||||||
@@ -17,10 +17,13 @@
|
|||||||
- [Changing the domain](#changing-the-domain)
|
- [Changing the domain](#changing-the-domain)
|
||||||
- [Uninstall](#uninstall)
|
- [Uninstall](#uninstall)
|
||||||
- [Edit QCOW](#edit-qcow)
|
- [Edit QCOW](#edit-qcow)
|
||||||
|
- [Exclude Lists](#exclude-lists)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Stuck in login screen](#stuck-in-login-screen)
|
- [Stuck in login screen](#stuck-in-login-screen)
|
||||||
- [Freezing after working for a bit](#freezing-after-working-for-a-bit)
|
- [Freezing after working for a bit](#freezing-after-working-for-a-bit)
|
||||||
- [Out of disk space](#out-of-disk-space)
|
- [Out of disk space](#out-of-disk-space)
|
||||||
- [Redis can't dump its DB](#redis-cant-dump-its-db)
|
- [Redis can't dump its DB](#redis-cant-dump-its-db)
|
||||||
|
- [Error connecting to server](#error-connecting-to-server)
|
||||||
|
|
||||||
<https://github.com/nextcloud/all-in-one>
|
<https://github.com/nextcloud/all-in-one>
|
||||||
|
|
||||||
@@ -34,36 +37,27 @@ This has been tested working on Fedora 41 with selinux and firewalld enabled.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
useradd nextcloud
|
useradd nextcloud
|
||||||
su - nextcloud
|
|
||||||
ssh-keygen
|
|
||||||
exit
|
|
||||||
cp ~/.ssh/authorized_keys /home/nextcloud/.ssh/authorized_keys
|
|
||||||
chown nextcloud:nextcloud /home/nextcloud/.ssh/authorized_keys
|
|
||||||
loginctl enable-linger $(id -u nextcloud)
|
loginctl enable-linger $(id -u nextcloud)
|
||||||
```
|
systemctl --user --machine=nextcloud@.host enable podman-restart
|
||||||
|
systemctl --user --machine=nextcloud@.host enable --now podman.socket
|
||||||
### Install Podman
|
su -l nextcloud
|
||||||
|
mkdir -p /home/nextcloud/.config/containers/systemd
|
||||||
```bash
|
exit
|
||||||
# As root user
|
|
||||||
dnf install podman
|
|
||||||
|
|
||||||
# Now SSH into the server as the nextcloud user
|
|
||||||
systemctl --user enable podman-restart
|
|
||||||
systemctl --user enable --now podman.socket
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create the container autostart service
|
### Create the container autostart service
|
||||||
|
|
||||||
As the nextcloud user.
|
Edit the autostart service to include "unless-stopped" containers.
|
||||||
|
|
||||||
`systemctl --user edit podman-restart.service`
|
```bash
|
||||||
|
machinectl shell nextcloud@
|
||||||
|
systemctl --user edit podman-restart.service
|
||||||
|
```
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=
|
ExecStart=
|
||||||
ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always --filter restart-policy=unless-stopped
|
ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always --filter restart-policy=unless-stopped
|
||||||
ExecStop=
|
|
||||||
ExecStop=/bin/sh -c '/usr/bin/podman $LOGGING stop $(/usr/bin/podman container ls --filter restart-policy=always --filter restart-policy=unless-stopped -q)'
|
ExecStop=/bin/sh -c '/usr/bin/podman $LOGGING stop $(/usr/bin/podman container ls --filter restart-policy=always --filter restart-policy=unless-stopped -q)'
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -73,42 +67,35 @@ systemctl --user daemon-reload
|
|||||||
|
|
||||||
### Install Nextcloud
|
### Install Nextcloud
|
||||||
|
|
||||||
`mkdir -p ~/.config/containers/systemd`
|
On the operator
|
||||||
|
|
||||||
`vim ~/.config/containers/systemd/nextcloud-aio-mastercontainer.container`
|
1. Edit `nextcloud-aio-mastercontainer.container` to include the correct username and UID where relevant.
|
||||||
|
2. Copy the files to the server:
|
||||||
```conf
|
|
||||||
[Unit]
|
|
||||||
Description=Nextcloud AIO Master Container
|
|
||||||
Documentation=https://github.com/nextcloud/all-in-one/blob/main/docker-rootless.md
|
|
||||||
After=local-fs.target
|
|
||||||
Requires=podman.socket
|
|
||||||
|
|
||||||
[Container]
|
|
||||||
ContainerName=nextcloud-aio-mastercontainer
|
|
||||||
Image=docker.io/nextcloud/all-in-one:latest
|
|
||||||
PublishPort=0.0.0.0:11001:8080
|
|
||||||
Volume=nextcloud_aio_mastercontainer:/mnt/docker-aio-config
|
|
||||||
Volume=/run/user/1001/podman/podman.sock:/var/run/docker.sock:Z
|
|
||||||
Network=bridge
|
|
||||||
SecurityLabelDisable=true
|
|
||||||
|
|
||||||
Environment=APACHE_PORT=11000
|
|
||||||
Environment=APACHE_IP_BINDING=0.0.0.0
|
|
||||||
Environment=WATCHTOWER_DOCKER_SOCKET_PATH=/run/user/1001/podman/podman.sock
|
|
||||||
Environment=NEXTCLOUD_DATADIR="/home/nextcloud/nextcloud_data"
|
|
||||||
Environment=SKIP_DOMAIN_VALIDATION=true
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Restart=always
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target default.target
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Copy the quadlet files
|
||||||
|
scp \
|
||||||
|
active/podman_nextcloud/nextcloud-aio-mastercontainer.container \
|
||||||
|
3dserver:/home/nextcloud/.config/containers/systemd/
|
||||||
|
|
||||||
|
ssh chown -R nextcloud:nextcloud /home/nextcloud/.config/containers/systemd/
|
||||||
|
```
|
||||||
|
|
||||||
|
On the server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
machinectl shell nextcloud@
|
||||||
|
|
||||||
|
# Create volumes
|
||||||
|
mkdir nextcloud_data
|
||||||
|
mkdir nextcloud_aio_mastercontainer
|
||||||
|
|
||||||
|
# Create the nextcloud network with ipv6
|
||||||
|
podman network create --ipv6 nextcloud-aio
|
||||||
|
|
||||||
|
# Reload and restart the service
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
systemctl --user start nextcloud-aio-mastercontainer
|
systemctl --user restart nextcloud-aio-mastercontainer
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install Caddy
|
### Install Caddy
|
||||||
@@ -167,7 +154,6 @@ systemctl daemon-reload
|
|||||||
systemctl start caddy
|
systemctl start caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
|
|
||||||
Allow traffic to 11000 from your reverse proxy
|
Allow traffic to 11000 from your reverse proxy
|
||||||
@@ -189,7 +175,15 @@ If you need to reset the borg backup repo:
|
|||||||
docker exec nextcloud-aio-borgbackup rm /mnt/docker-aio-config/data/borg.config
|
docker exec nextcloud-aio-borgbackup rm /mnt/docker-aio-config/data/borg.config
|
||||||
```
|
```
|
||||||
|
|
||||||
## Maintenace Mode
|
### Manual Backups
|
||||||
|
|
||||||
|
1. Backup `nextcloud_data`
|
||||||
|
2. Backup all nextcloud volumes at `/home/nextcloud/.local/share/containers/storage/volumes/`
|
||||||
|
3. Backup `.config/containers/systemd/`
|
||||||
|
|
||||||
|
Copy these back to where they came to restore
|
||||||
|
|
||||||
|
## Maintenance Mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker stop nextcloud-aio-apache
|
docker stop nextcloud-aio-apache
|
||||||
@@ -251,7 +245,127 @@ sudo qemu-nbd -c /dev/nbd0 --read-only /path/to/image.qcow2
|
|||||||
udisksctl mount -b /dev/nbd0p1
|
udisksctl mount -b /dev/nbd0p1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Stuck in login screen
|
## Exclude Lists
|
||||||
|
|
||||||
|
Exclude lists take effect only if the folders/files haven't been synced yet. Here's a basic one
|
||||||
|
I stole that works well for development resources.
|
||||||
|
|
||||||
|
Put this in `~/.config/Nextcloud/sync-exclude.list`
|
||||||
|
|
||||||
|
```text
|
||||||
|
]*~
|
||||||
|
]~$*
|
||||||
|
].~lock.*
|
||||||
|
]~*.tmp
|
||||||
|
]*.~*
|
||||||
|
]Icon\r*
|
||||||
|
].DS_Store
|
||||||
|
].ds_store
|
||||||
|
]*.textClipping
|
||||||
|
]._*
|
||||||
|
]Thumbs.db
|
||||||
|
]photothumb.db
|
||||||
|
]System Volume Information
|
||||||
|
].*.sw?
|
||||||
|
].*.*sw?
|
||||||
|
].TemporaryItems
|
||||||
|
].Trashes
|
||||||
|
].DocumentRevisions-V100
|
||||||
|
].Trash-*
|
||||||
|
].fseventd
|
||||||
|
].apdisk
|
||||||
|
].Spotlight-V100
|
||||||
|
].directory
|
||||||
|
]*.part
|
||||||
|
]*.filepart
|
||||||
|
]*.crdownload
|
||||||
|
]*.kate-swp
|
||||||
|
]*.gnucash.tmp-*
|
||||||
|
].synkron.*
|
||||||
|
].sync.ffs_db
|
||||||
|
].symform
|
||||||
|
].symform-store
|
||||||
|
].fuse_hidden*
|
||||||
|
]*.unison
|
||||||
|
].nfs*
|
||||||
|
]My Saved Places.
|
||||||
|
]*.sb-*
|
||||||
|
]*.dll
|
||||||
|
]*.exe
|
||||||
|
].git/
|
||||||
|
].lock
|
||||||
|
]*.bin
|
||||||
|
].bin
|
||||||
|
]bin/
|
||||||
|
]*.lock
|
||||||
|
]node_modules/
|
||||||
|
].cache/
|
||||||
|
].vscode/
|
||||||
|
].pytest_cache/
|
||||||
|
].github/
|
||||||
|
].ipynb_checkpoints/
|
||||||
|
]*.exe
|
||||||
|
]*.dll
|
||||||
|
]*.class
|
||||||
|
]*.com
|
||||||
|
]*.so
|
||||||
|
]*.o
|
||||||
|
]@*/
|
||||||
|
]__pycache__/
|
||||||
|
].Python/
|
||||||
|
]build/
|
||||||
|
]dist/
|
||||||
|
]eggs/
|
||||||
|
].eggs/
|
||||||
|
]wheels/
|
||||||
|
]sdist/
|
||||||
|
]var/
|
||||||
|
]*.egg/
|
||||||
|
]*.egg-info/
|
||||||
|
]lib64/
|
||||||
|
]lib/
|
||||||
|
].tox/
|
||||||
|
].nox/
|
||||||
|
]env/
|
||||||
|
]venv/
|
||||||
|
]ENV/
|
||||||
|
]env.bak/
|
||||||
|
]venv.bak/
|
||||||
|
]site/
|
||||||
|
]cython_debug/
|
||||||
|
]vendor/
|
||||||
|
]tmp/
|
||||||
|
].libs/
|
||||||
|
].debs/
|
||||||
|
]src/
|
||||||
|
]Debug/
|
||||||
|
]debug/
|
||||||
|
]*.pdb
|
||||||
|
]*.enc
|
||||||
|
].enc
|
||||||
|
].sass-cache/
|
||||||
|
]_site/
|
||||||
|
].info
|
||||||
|
]*.info
|
||||||
|
].jekyll-cache
|
||||||
|
].jekyll-cache/
|
||||||
|
].zotero-ft-cache
|
||||||
|
].zotero-ft-info
|
||||||
|
]*.idlk
|
||||||
|
]zotero.sqlite.bak
|
||||||
|
]*.dwl
|
||||||
|
]*.dwl2
|
||||||
|
]*.bkp
|
||||||
|
]*.dtmp
|
||||||
|
].$*
|
||||||
|
]*.tmp
|
||||||
|
]_build/
|
||||||
|
].venv/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Stuck in login screen
|
||||||
|
|
||||||
Check logs at `/var/www/html/data/nextcloud.log` in `nextcloud-aio-nextcloud` container.
|
Check logs at `/var/www/html/data/nextcloud.log` in `nextcloud-aio-nextcloud` container.
|
||||||
|
|
||||||
@@ -268,9 +382,9 @@ Sometimes this is caused by a broken app or twofactor. try:
|
|||||||
./occ app:disable integration_openai
|
./occ app:disable integration_openai
|
||||||
```
|
```
|
||||||
|
|
||||||
## Freezing after working for a bit
|
### Freezing after working for a bit
|
||||||
|
|
||||||
### Out of disk space
|
#### Out of disk space
|
||||||
|
|
||||||
This can happen when nextcloud tries to write logs to its volume and doesn't have enough space
|
This can happen when nextcloud tries to write logs to its volume and doesn't have enough space
|
||||||
|
|
||||||
@@ -279,7 +393,7 @@ podman exec -it nextcloud-aio-nextcloud bash
|
|||||||
df -h .
|
df -h .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Redis can't dump its DB
|
#### Redis can't dump its DB
|
||||||
|
|
||||||
This can happen when the redis volume doesn't have the correct permissions
|
This can happen when the redis volume doesn't have the correct permissions
|
||||||
|
|
||||||
@@ -288,3 +402,9 @@ podman exec -it --user root nextcloud-aio-redis bash
|
|||||||
ls -lah /data
|
ls -lah /data
|
||||||
chown redis:redis /data
|
chown redis:redis /data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Error connecting to server
|
||||||
|
|
||||||
|
Your nextcloud instance won't be able to use host loopback with rootless containers. If you have
|
||||||
|
a local DNS record pointing to your server's IP address you'll need to delete that until this is
|
||||||
|
fixed.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Ollama
|
# Ollama
|
||||||
|
|
||||||
- [Ollama](#ollama)
|
- [Ollama](#ollama)
|
||||||
|
- [Firewall for Ollama](#firewall-for-ollama)
|
||||||
- [Install and run Ollama](#install-and-run-ollama)
|
- [Install and run Ollama](#install-and-run-ollama)
|
||||||
- [Install and run Ollama with Podman](#install-and-run-ollama-with-podman)
|
- [Install and run Ollama with Podman](#install-and-run-ollama-with-podman)
|
||||||
- [Unsticking models stuck in "Stopping"](#unsticking-models-stuck-in-stopping)
|
- [Unsticking models stuck in "Stopping"](#unsticking-models-stuck-in-stopping)
|
||||||
@@ -15,6 +16,19 @@
|
|||||||
|
|
||||||
<https://github.com/ollama/ollama>
|
<https://github.com/ollama/ollama>
|
||||||
|
|
||||||
|
## Firewall for Ollama
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add home zone if you don't have one
|
||||||
|
sudo firewall-cmd --get-active-zones
|
||||||
|
sudo firewall-cmd --new-zone=home --permanent
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
# Set source address to allow connections
|
||||||
|
sudo firewall-cmd --zone=ollama --add-source=10.2.0.1/24 --permanent
|
||||||
|
sudo firewall-cmd --zone=ollama --add-port=11434/tcp --permanent
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
## Install and run Ollama
|
## Install and run Ollama
|
||||||
|
|
||||||
<https://ollama.com/download/linux>
|
<https://ollama.com/download/linux>
|
||||||
|
|||||||
12
active/software_borg/backup.service
Normal file
12
active/software_borg/backup.service
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Runs backup script for {{ repo_name }}
|
||||||
|
After=network.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=no
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/script/backup-{{ repo_name }}.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user