updates...

This commit is contained in:
Amar Saljic
2025-11-15 23:02:28 +01:00
parent 4ccd00db2d
commit cd2b304a5c
16 changed files with 338 additions and 39 deletions

View File

@ -1,9 +1,22 @@
# NAxS Homelab # NAxS Homelab
Prerequisites: ## Prerequisites
- Create a default network called homelab 0. Make sure that your user is part of the docker group
- `cat /etc/group | grep docker` - if the entry looks like `docker:x:<Group ID>:<username>`, you're good to go
- Otherwise please run `sudo usermod -aG docker <username>`, followed by logging out & in again for these changes to take into effect
1. Create a default network called homelab
``` ```
docker network create homelab docker network create homelab
``` ```
2. Set up 1Password for access to secrets
- Install `pass` & `gpg`
- Generate key with `gpg --full-generate-key`
- stick to defaults
- as password, use `GPG cert password` stored inside the `NAxS Homelab` vault in 1Password
- Initialize password storage with `pass init "GPG key ID"`
- You can check out the ID by using `gpg --list-secret-keys --keyid-format LONG` - you should see a line with `sec`, containing the following information `<encryption technology>/ID`
- Store the 1Password service account token in `pass` as `op-sa_token` by executing `pass insert op-sa_token`
- Make sure your .zshrc file loads the token into the `OP_SERVICE_ACCOUNT_TOKEN` (this is needed by the 1Password CLI for authentication purposes when loading the secrets) environment variable by executing `export OP_SERVICE_ACCOUNT_TOKEN="$(pass op-sa_token)"`
// TODO: Create template script // TODO: Create template script
Template script which helps with setting up new applications (asks for potential secrets needs, adds default network to compose file, creates new users/groups to run containers rootless) Template script which helps with setting up new applications (asks for potential secrets needs, adds default network to compose file, creates new users/groups to run containers rootless)

View File

@ -0,0 +1,180 @@
#!/bin/zsh
while [[ "$#" -gt 0 ]]
do
case $1 in
--app_name) app_name="$2"
shift;;
*) echo "Unknown parameter passed: $1"
exit 1;;
esac
shift
done
# Make sure app_name exists and is not empty
if [[ -z "$app_name" ]]; then
echo "app_name is unset or empty."
exit 1;
fi
# Define the base directory for the new application structure
# This will be one level up from where this script is executed.
app_base_dir="../${app_name}"
app_data_dir="${app_base_dir}/data"
compose_file="${app_base_dir}/compose.yaml"
readme_file="${app_base_dir}/README.md"
start_script="${app_base_dir}/start.sh"
echo "--- Setting up files and directories for application: ${app_name} ---"
echo "Base directory: ${app_base_dir}"
# 1. Create the application base directory and data directory
echo "Creating directory: ${app_data_dir}"
mkdir -p "${app_data_dir}" || { echo "Error: Failed to create directory ${app_data_dir}"; exit 1; }
# 2. Create compose.yaml and README.md
echo "Creating file: ${compose_file}"
touch "${compose_file}" || { echo "Error: Failed to create file ${compose_file}"; exit 1; }
echo "Creating file: ${readme_file}"
touch "${readme_file}" || { echo "Error: Failed to create file ${readme_file}"; exit 1; }
# 3. Prepopulate README.md with title case app name
# Convert app_name to title case (first letter of each word capitalized)
# This is a simple conversion; for more robust title casing, a function would be better.
app_name_title_case=$(echo "$app_name" | awk '{for(i=1;i<=NF;i++){ $i=toupper(substr($i,1,1)) tolower(substr($i,2)) }}1')
echo "Prepopulating ${readme_file}..."
cat > "${readme_file}" << EOF
# ${app_name_title_case}
This is the README file for the **${app_name}** application.
## Overview
Provide a brief description of your application here.
## Setup
Instructions for setting up the application.
EOF
# 4. Ask the user if secrets will be required
read "requires_secrets?Will this application require secrets (yes/no)? "
requires_secrets=$(echo "$requires_secrets" | tr '[:upper:]' '[:lower:]') # Convert to lowercase
# 5. Prepopulate compose.yaml based on user selection re. secrets
echo "Prepopulating ${compose_file} based on secrets requirement..."
if [[ "$requires_secrets" == "yes" || "$requires_secrets" == "y" ]]; then
cat > "${compose_file}" << EOF
version: '3.8'
services:
${app_name}:
image: your-app-image:latest
container_name: ${app_name}-container
ports:
- "8080:8080"
volumes:
- ./data:/app/data
environment:
# Example of how to pass secrets as environment variables
# These would typically be loaded from a .env file or a secrets management system
- DATABASE_URL=\${DATABASE_URL}
- API_KEY=\${API_KEY}
# Example of secrets usage with Docker Compose secrets (requires Docker Swarm or specific setup)
# secrets:
# - my_database_password
# - my_api_key
# deploy:
# resources:
# limits:
# cpus: '0.5'
# memory: 512M
secrets:
my_database_password:
external: true # Assumes secret is created externally (e.g., docker secret create)
my_api_key:
external: true
EOF
else
cat > "${compose_file}" << EOF
services:
${app_name}:
image: your-app-image:latest
container_name: ${app_name}-container
ports:
- "8080:8080"
volumes:
- ./data:/app/data
# No secrets configuration needed for this version
# deploy:
# resources:
# limits:
# cpus: '0.5'
# memory: 512M
EOF
fi
# 6. If secrets will be used - touch ../"app_name"/start.sh and prepopulate it
if [[ "$requires_secrets" == "yes" || "$requires_secrets" == "y" ]]; then
echo "Creating and prepopulating ${start_script}..."
cat > "${start_script}" << EOF
#!/bin/zsh
# Example start script for an application using secrets
# This script assumes you have a .env file or similar mechanism
# for loading secrets into the environment before starting Docker Compose.
# --- IMPORTANT: Replace with your actual secret loading mechanism ---
# Example 1: Load from a .env file
# if [[ -f ".env" ]]; then
# echo "Loading environment variables from .env"
# source .env
# else
# echo "Warning: .env file not found. Secrets might be missing."
# fi
# Example 2: Using a secrets management tool (e.g., HashiCorp Vault, AWS Secrets Manager)
# echo "Fetching secrets from Vault..."
# export DATABASE_URL=\$(vault read -field=value secret/myapp/database_url)
# export API_KEY=\$(vault read -field=value secret/myapp/api_key)
# -------------------------------------------------------------------
echo "Starting Docker Compose services for ${app_name}..."
docker compose -f "${compose_file}" up -d
echo "Application ${app_name} started. Check logs with: docker compose logs -f ${app_name}"
EOF
chmod +x "${start_script}" # Make the start script executable
fi
# 7. If secrets will be used, append another chapter at the end of the README.md file
if [[ "$requires_secrets" == "yes" || "$requires_secrets" == "y" ]]; then
echo "Appending 'Secrets Management' chapter to ${readme_file}..."
cat >> "${readme_file}" << EOF
## Secrets Management
This application utilizes secrets for sensitive configurations (e.g., database credentials, API keys).
### Configuration
Secrets are expected to be provided via environment variables or Docker Compose secrets.
Refer to the \`start.sh\` script for an example of how to load these secrets before application startup.
**Example Environment Variables (to be set in your environment or a \`.env\` file):**
\`\`\`
DATABASE_URL="postgres://user:password@host:port/database"
API_KEY="your_super_secret_api_key"
\`\`\`
**For Docker Compose secrets:**
Ensure the necessary Docker secrets are created on your Docker host or Swarm cluster.
\`\`\`bash
docker secret create my_database_password <(echo "your_db_password")
docker secret create my_api_key <(echo "your_api_key")
\`\`\`
Then, update the \`compose.yaml\` to reference these secrets.
EOF
fi
echo "--- Setup complete for ${app_name} ---"
echo "Check the '${app_base_dir}' directory for your new files."

View File

@ -0,0 +1,80 @@
#!/bin/zsh
while [[ "$#" -gt 0 ]]
do
case $1 in
--app_name) app_name="$2"
shift;;
--id) desired_id="$2"
shift;;
*) echo "Unknown parameter passed: $1"
exit 1;;
esac
shift
done
# Validate desired_id is a number
if ! [[ "$desired_id" =~ ^[0-9]+$ ]]; then
echo "Error: Invalid UID/GID. Please enter a numeric value."
exit 1
fi
user_name="${app_name}-user"
group_name="${app_name}-group"
echo "--- Checking/Creating User and Group for ${app_name} ---"
# --- Handle Group ---
echo "Checking group: ${group_name}"
existing_gid=$(getent group "${group_name}" | cut -d: -f3)
if [[ -n "$existing_gid" ]]; then
if [[ "$existing_gid" -eq "$desired_id" ]]; then
echo "Group '${group_name}' already exists with the correct GID (${desired_id})."
else
echo "Group '${group_name}' exists with GID ${existing_gid}, but desired GID is ${desired_id}."
echo "Attempting to modify group GID..."
if sudo groupmod -g "$desired_id" "${group_name}"; then
echo "Successfully adjusted group '${group_name}' to GID ${desired_id}."
else
echo "Failed to adjust group '${group_name}' GID. Please check permissions or try manually."
exit 1
fi
fi
else
echo "Group '${group_name}' does not exist. Creating..."
if sudo groupadd -g "$desired_id" "${group_name}"; then
echo "Successfully created group '${group_name}' with GID ${desired_id}."
else
echo "Failed to create group '${group_name}'. Please check permissions or try manually."
exit 1
fi
fi
# --- Handle User ---
echo "Checking user: ${user_name}"
existing_uid=$(getent passwd "${user_name}" | cut -d: -f3)
if [[ -n "$existing_uid" ]]; then
if [[ "$existing_uid" -eq "$desired_id" ]]; then
echo "User '${user_name}' already exists with the correct UID (${desired_id})."
else
echo "User '${user_name}' exists with UID ${existing_uid}, but desired UID is ${desired_id}."
echo "Attempting to modify user UID..."
if sudo usermod -u "$desired_id" -g "$desired_id" "${user_name}"; then
echo "Successfully adjusted user '${user_name}' to UID ${desired_id} and primary GID ${desired_id}."
else
echo "Failed to adjust user '${user_name}' UID/GID. Please check permissions or try manually."
exit 1
fi
fi
else
echo "User '${user_name}' does not exist. Creating..."
if sudo useradd -u "$desired_id" -g "$desired_id" -s /sbin/nologin -c "Application User for ${app_name}" "${user_name}"; then
echo "Successfully created user '${user_name}' with UID ${desired_id} and primary GID ${desired_id}."
else
echo "Failed to create user '${user_name}'. Please check permissions or try manually."
exit 1
fi
fi
echo "--- Operation complete for ${app_name} ---"

View File

@ -0,0 +1,8 @@
#!/bin/zsh
# Ask for the application name
read "app_name?Enter the name of the application: "
# Ask for the desired UID/GID
read "desired_id?Enter the desired UID/GID for the application (e.g., 1001): "
./create-user.sh --app_name ${app_name} --id ${desired_id}
./create-files+directories.sh --app_name ${app_name}

View File

@ -25,13 +25,4 @@ In case user/group doesn't exist at all, please create the user & group incl. th
``` ```
sudo groupadd -g 1003 gitea-group sudo groupadd -g 1003 gitea-group
sudo useradd -g gitea-group -u 1003 gitea-user sudo useradd -g gitea-group -u 1003 gitea-user
``` ```
## About secrets
In order to manage secrets centrally in 1Password and due to the need for secrets in Gitea, using `docker compose` directly in the terminal does not work.
## Bring up/tear down container
Please use the `start.sh` to spin up the container
### Prerequisites start.sh
- User executing the script is part of the `docker` group
- Env variable `OP_SERVICE_ACCOUNT_TOKEN` is set up \[check out top-level README.md for more information on how to set this up\]

View File

@ -1,6 +1,3 @@
secrets:
gitea_postgres_password:
environment: GITEA_POSTGRES_PASSWORD
services: services:
gitea: gitea:
image: docker.gitea.com/gitea:1-rootless image: docker.gitea.com/gitea:1-rootless
@ -14,11 +11,9 @@ services:
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_FILE: /run/secrets/gitea_postgres_password
restart: always restart: always
volumes: ['./data:/var/lib/gitea', './config:/etc/gitea'] volumes: ['./data:/var/lib/gitea', './config:/etc/gitea']
ports: ['8030:3000', '2222:2222'] ports: ['8030:3000', '2222:2222']
secrets: ['gitea_postgres_password']
networks: ['homelab'] networks: ['homelab']
networks: networks:

View File

@ -1,12 +0,0 @@
#!/bin/zsh
# Exit immediately if a command exits with a non-zero status.
set -e
echo "--- Starting Docker Secret Management ---"
# Mount secrets
export GITEA_POSTGRES_PASSWORD="$(op read 'op://NAxS Homelab/Gitea Postgres credentials/password')"
# Bring up container
docker compose up -d
echo "--- Docker Secret Management Complete ---"

View File

@ -2,20 +2,26 @@
## Set up non-root user for container ## Set up non-root user for container
We are providing a non-root user to the container to limit the attack surface for privilege escalations. In order for this to work in our setup, please make sure to check if you have a user called `postgres` set up. We are providing a non-root user to the container to limit the attack surface for privilege escalations. In order for this to work in our setup, please make sure to check if you have a user called `postgres` set up.
1. Check if user `postgres` exists and if the UID is 1002 1. Check if user `postgres` exists
``` ```
cat /etc/passwd | grep postgres cat /etc/passwd | grep postgres
``` ```
In case the `postgres` user doesn't exist, please create the user by running
In case the `postgres` user exists but the UID is not 1002, please adjust it via
``` ```
sudo usermod -u 1002 postgres sudo useradd postgres
```
2. `data` folder ownership
Also you need to make sure that the `postgres` owner owns the volumes mounted for docker
```
sudo chown -R postgres:postgres data
sudo chmod 770 data
``` ```
In case the `postgres` user doesn't exist at all, please create the user incl. the right UID by running 3. Adjust compose.yml
Within `services > postgres > user`, make sure to replare `postgres` with the UID of the user on your machine
``` ```
sudo useradd -u 1002 postgres cat /etc/passwd | grep postgres
``` ```
## About secrets ## About secrets
@ -25,4 +31,4 @@ In order to manage secrets centrally in 1Password and due to the need for secret
Please use the `start.sh` to spin up the container Please use the `start.sh` to spin up the container
### Prerequisites start.sh ### Prerequisites start.sh
- User executing the script is part of the `docker` group - User executing the script is part of the `docker` group
- Env variable `OP_SERVICE_ACCOUNT_TOKEN` is set up \[check out top-level README.md for more information on how to set this up\] - Environment variable `OP_SERVICE_ACCOUNT_TOKEN` is set up \[check out top-level README.md for more information on how to set this up\]

View File

@ -101,6 +101,4 @@ op --version
apt install pass gnupg2 apt install pass gnupg2
echo "Done. Recommended: log out and back in (or reboot) to start using zsh and ensure all services are active." echo "Done. Recommended: log out and back in (or reboot) to start using zsh and ensure all services are active."

View File

@ -0,0 +1,29 @@
function run(argv) {
const title = $.NSProcessInfo.processInfo.environment.objectForKey("title").js;
const notes = $.NSProcessInfo.processInfo.environment.objectForKey("notes").js
function addToOmnifocus(transportText) {
newTasks = Task.byParsingTransportText(transportText, true);
taskID = newTasks[0].id.primaryKey;
URL.fromString("omnifocus:///task/" + taskID).open();
}
function generateTransportText(title, notes) {
const tag = 'work';
let transportText = `${title} @${tag}`;
if (notes) {
transportText = `${transportText} // ${notes}`
}
return transportText;
}
const transportText = generateTransportText(title, notes);
const encodedFunctionAndInput = `%28${encodeURIComponent(addToOmnifocus.toString())}%29%28argument%29&arg=%22${encodeURIComponent(transportText)}%22`;
const omnifocusUrl = `omnifocus://localhost/omnijs-run?script=${encodedFunctionAndInput}`;
console.log(omnifocusUrl);
let app = Application.currentApplication();
app.includeStandardAdditions = true;
app.openLocation(omnifocusUrl);
}

Binary file not shown.

View File

@ -0,0 +1,5 @@
# ZSH setup
1. Place .zshrc file in home directory
2. Create folder called zshrc
3. Include all needed .sh files inside the zshcrc folder
4. To activate the changes right away, make sure to execute `source .zshrc` from within the home directory in your terminal

View File

@ -0,0 +1 @@
export OP_SERVICE_ACCOUNT_TOKEN="$(pass op-sa_token)"

View File

@ -0,0 +1,2 @@
bindkey -v
PS1='%n@%m %~ $ '

View File

@ -9,4 +9,4 @@ export PATH="/Users/mucas/.local/share/gem/ruby/3.2.0/bin:$PATH"
export PYENV_ROOT="$HOME/.pyenv" export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH" [[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init - zsh)" eval "$(pyenv init - zsh)"

View File

@ -0,0 +1,3 @@
for FILE in ~/zshrc/*; do
source $FILE
done