Compare commits

...
47 Commits
Author SHA1 Message Date
dodoxandClaude aa9bac2c5b Add SHA256 fingerprint display for ED25519 host key
- Show SHA256 fingerprint for ed25519 key (modern standard)
- Keep SHA1 output from dropbearkey for other keys
- Clean up key generation output

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-18 20:55:40 +02:00
dodoxandClaude 555987af40 Fix SSH port in post-install message
- Update from port 2222 to port 22 for dropbear
- Remove redundant -p flag since 22 is default SSH port

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-18 20:46:12 +02:00
dodoxandClaude eb7a74e6fc Fix basename error in dotfiles installation
- Properly handle . and .. in dotfile copy loop
- Change directory before listing to avoid path issues
- Explicitly exclude . and .. from file copying

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-18 20:45:10 +02:00
dodoxandClaude bb642474b7 Fix initramfs SSH and Clevis compatibility issues
- Replace tinyssh with dropbear for better AlmaLinux compatibility
- Create custom dracut module for dropbear SSH access
- Fix clevis-luks-askpass.path for AlmaLinux 8.7+ (not needed)
- Fix dotfiles installation basename error
- Remove duplicate epel-release installation
- Update README to reflect dropbear on port 22

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-18 20:39:54 +02:00
dodox 6f3e1788e5 going back to dropbear 2025-08-18 20:21:45 +02:00
dodox 10ac0401af . 2025-08-18 01:37:18 +02:00
dodox 97c296da97 . 2025-08-18 01:35:27 +02:00
dodox ab251d723f . 2025-08-18 01:22:32 +02:00
dodox 0a108c460d . 2025-08-18 01:18:55 +02:00
dodox 9bc2c4bb34 change 2025-08-18 01:14:33 +02:00
dodox 8aee8ab3de ffuuuuu 2025-08-18 01:14:04 +02:00
dodox 70bd757166 tinyssh gen host keys 2025-08-18 01:02:54 +02:00
dodox 059a926da6 clevis luks askpass ehm ok 2025-08-18 00:58:00 +02:00
dodox 0fe95ab969 tinyssh over dropbear 2025-08-18 00:51:10 +02:00
dodox ca24354114 fix missing tar 2025-08-18 00:35:31 +02:00
dodox bf051cba2a FiXeS 2025-08-18 00:30:35 +02:00
dodox 1323fdabe4 post install fixes 2025-08-18 00:24:36 +02:00
dodox ac4740438b fixes (dropbear etc) 2025-08-18 00:10:29 +02:00
dodox d80a6a2ab1 fix issues with install.conf 2025-08-17 23:56:39 +02:00
dodox 25ee9c9dd6 simple ext4 boot 2025-08-17 23:49:19 +02:00
dodox f12dbf687d seperate install and get scripts... 2025-08-17 23:47:07 +02:00
dodox c4ad943223 pull earlier. 2025-08-17 23:43:21 +02:00
dodox 34bd561e84 fix: wrong os image identifier 2025-08-17 23:39:12 +02:00
dodox 1c60f71446 auto detect drive names 2025-08-17 23:36:37 +02:00
dodox 525b36dc28 fixes 2025-08-17 23:31:20 +02:00
dodox 9456753877 minor fixes 2025-08-17 23:28:13 +02:00
dodox aa9ba7ce23 save, then run, dont execuet while piping 2025-08-17 23:23:34 +02:00
dodox 7beb5bf119 fix: missing tpm enabled config from posgt install 2025-08-17 23:20:56 +02:00
dodox 5c6ddee629 fix broken sed escaping 2025-08-17 23:19:16 +02:00
dodox 6788a6e11d Upd README 2025-07-26 22:43:35 +02:00
dodox ed3a157ecf Upd README 2025-07-26 22:35:11 +02:00
dodox 1a58f63c7f tweaks 2025-07-26 22:08:49 +02:00
dodox 34761a1cdd Upd ROADMAP 2025-07-26 21:53:33 +02:00
dodox d66eaace7e Upd README 2025-07-26 21:50:58 +02:00
dodox e2309551c3 Upd README 2025-07-26 21:50:17 +02:00
dodox 35571c4c44 who even cares about fedora 2025-07-26 21:47:37 +02:00
dodox fbc558877c Upd README 2025-05-19 17:07:20 +02:00
dodox 671276478c Upd README 2025-05-19 17:02:17 +02:00
dodox e4e2cb4435 Upd README 2025-05-19 16:55:30 +02:00
dodox 2be74c8923 Upd README 2025-05-19 16:54:54 +02:00
dodox ce1736a7c1 installer does more 2025-05-19 16:54:46 +02:00
dodox 8a39511aea many fixes and additions but somethings still broken 2025-05-19 01:24:01 +02:00
dodox 31f1543fe5 make script executable 2025-05-18 18:42:44 +02:00
dodox f4795c232a better README 2025-05-18 18:39:22 +02:00
dodox 6f98289af0 dont null whole disk 2025-05-18 18:39:15 +02:00
dodox fd4e92e49b Upd README 2025-05-18 18:32:27 +02:00
dodox 92461a8ebf rewrote everything 2025-05-18 18:31:20 +02:00
15 changed files with 2827 additions and 846 deletions
+3 -6
View File
@@ -1,15 +1,12 @@
# Config files # Config files
build-config.yaml config.yaml
deploy-config.yaml
config.ign
# Python # Python
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
# Images
# Downloaded images *.iso
fedora-coreos-*-hetzner.x86_64.raw.xz
# Environment # Environment
.env .env
+23 -67
View File
@@ -4,85 +4,41 @@
<br> <br>
</div> </div>
Secure Fedora Server setup with LUKS encryption, TPM, and BTRFS RAID1 with focus on Hetzner Infra. Secure AlmaLinux (RHEL) Server setup with LUKS encryption, Tang, TPM and RAID1 for Hetzner Dedicated Servers.
## Features ## Features
- Fedora Server base - AlmaLinux Server base
- Full disk encryption with LUKS - Full disk encryption with LUKS
- Remote unlock via Tang server - Remote unlock via Tang server
- TPM-based boot verification - TPM-based boot verification
- BTRFS RAID1 for data redundancy - mdadm RAID1 + XFS (RHEL standard)
- Dedicated database subvolume with `nodatacow` and `noatime` - SSH key-only access with early boot SSH via dropbear
- Automated deployment to Hetzner - Best-in-class terminal: zsh + powerlevel10k + evil tmux
If you need a dead man's switch to go along with it check out [raven](https://git.dominik-roth.eu/dodox/raven). ## Unlock Strategy
## Security Model 1. **Automatic unlock via Tang/TPM** (default):
- Configure TPM2 and/or Tang servers in post-install.sh
- System unlocks automatically if conditions are met
- No manual intervention required
### Unlock Methods 2. **Manual unlock via SSH** (fallback):
The system uses multiple methods to unlock the LUKS volumes: - SSH to server on port 22 (dropbear in early boot)
1. **Primary Method**: TPM2 + Tang server - Enter LUKS passphrase when prompted (twice, once per disk)
- TPM2 verifies boot integrity - Used when automatic unlock fails or is not configured
- Tang server provides remote unlock capability
- Both must succeed for automatic unlock
2. **Fallback Method**: Manual passphrase
- Available via SSH before LUKS unlock
- Uses dropbear for early SSH access
- Can be used for recovery or maintenance
### TPM Updates ## Install
After firmware updates (UEFI/BIOS), the TPM bindings need to be updated:
(otherwise the system will not be able to boot without recovery phrase)
1. Use the provided script: `sudo /root/update-tpm-bindings.sh`
2. The script will:
- Show current PCR values
- Update TPM bindings to match new measurements
- Verify all bindings are correct
3. Manual passphrase is available in `/root/luks-passphrase.txt` if needed
## Prerequisites Boot your Hetzner server into rescue mode and run:
```bash ```bash
# Install tools wget -qO- https://git.dominik-roth.eu/dodox/nullpoint/raw/branch/master/get.sh | bash
curl -fsSL https://raw.githubusercontent.com/hetznercloud/cli/master/install.sh | bash
go install github.com/hetznercloud/hcloud-upload-image@latest
sudo dnf install -y jq python3-pyyaml libguestfs-tools cloud-image-utils curl
# Configure Hetzner
export HCLOUD_TOKEN="your-token-here"
hcloud ssh-key create --name "fedora-server-hetzner" --public-key "$(cat ~/.ssh/id_ed25519.pub)"
``` ```
## Setup The installer will:
- Detect your SSH key from the current session
1. **Configure Build Settings** - Ask for hostname and username
```bash - Generate a secure LUKS passphrase (SAVE IT!)
cp build-config.yaml.example build-config.yaml - Download and configure everything
vim build-config.yaml # Edit LUKS, storage, and image settings - Run Hetzner's installimage automatically
```
2. **Build Base Image** (one-time setup)
```bash
python3 build.py # Creates and uploads Fedora Server image to Hetzner
```
3. **Configure Deployment Settings**
```bash
cp deploy-config.yaml.example deploy-config.yaml
vim deploy-config.yaml # Edit server type, location, and hostname settings
```
4. **Deploy Server**
```bash
python3 deploy.py # Creates new server from base image
```
5. **Verify**
```bash
ssh admin@your-server
systemctl status clevis-luks-askpass
lsblk
btrfs filesystem show # Check RAID1 status
clevis-luks-list -d /dev/sda2
```
-43
View File
@@ -1,43 +0,0 @@
# Build Configuration
image:
name: nullpoint
version: 39
arch: x86_64
hetzner_arch: x86
# System Configuration
system:
# LUKS Configuration
luks:
tang_servers:
- url: https://tang1.example.com
thumbprint: your-tang1-thumbprint
- url: https://tang2.example.com
thumbprint: your-tang2-thumbprint
# TPM Configuration
tpm:
pcr_bank: sha256 # PCR bank to use (sha1 or sha256)
pcr_ids: [0,4,7,8,9] # PCRs to measure
# PCR descriptions:
# 0: Core System Firmware executable code (BIOS/UEFI) (RECOMMENDED)
# 1: Core System Firmware data (BIOS/UEFI settings)
# 2: Extended or pluggable executable code
# 3: Extended or pluggable firmware data
# 4: Boot Manager Code (bootloader) (RECOMMENDED)
# 5: Boot Manager Configuration and Data
# 6: Platform-specific code
# 7: Platform-specific configuration (RECOMMENDED)
# 8: UEFI driver and application code (RECOMMENDED)
# 9: UEFI driver and application configuration (RECOMMENDED)
# 10: UEFI Handoff Tables
# 11: UEFI Boot Services Code
# 12: UEFI Boot Services Data
# 13: UEFI Runtime Services Code
# 14: UEFI Runtime Services Data
# 15: UEFI Secure Boot State
# System Settings
timezone: UTC
keyboard: us
language: en_US.UTF-8
-547
View File
@@ -1,547 +0,0 @@
#!/usr/bin/env python3
import os
import sys
import yaml
import subprocess
import json
import shutil
from pathlib import Path
import tempfile
def load_config(config_file):
"""Load and parse YAML config file."""
with open(config_file, 'r') as f:
return yaml.safe_load(f)
def check_prerequisites():
"""Check if required tools are installed."""
required_tools = ['hcloud', 'hcloud-upload-image', 'jq', 'virt-customize', 'cloud-localds']
for tool in required_tools:
if not shutil.which(tool):
print(f"Error: {tool} not found. Please install it first.")
sys.exit(1)
def check_hetzner_token():
"""Check if HCLOUD_TOKEN is set."""
if not os.environ.get('HCLOUD_TOKEN'):
print("Error: HCLOUD_TOKEN environment variable not set")
sys.exit(1)
def generate_cloud_init(config):
"""Generate cloud-init configuration."""
system = config['system']
cloud_init = system['cloud_init']
tpm_config = system['luks']['tpm']
# Generate TPM config
tpm_json = {
'pcr_bank': tpm_config['pcr_bank'],
'pcr_ids': ','.join(map(str, tpm_config['pcr_ids']))
}
# Base cloud-init config
cloud_config = {
'timezone': cloud_init['timezone'],
'users': cloud_init['users'],
'package_update': True,
'package_upgrade': True,
'packages': cloud_init['packages'] + ['mdadm'], # Add mdadm package
'write_files': [
{
'path': '/etc/clevis/tang.conf',
'content': f"URL={system['luks']['tang_url']}\nThumbprint={system['luks']['tang_thumbprint']}\n",
'permissions': '0644'
},
{
'path': '/etc/clevis/tpm2.conf',
'content': json.dumps(tpm_json),
'permissions': '0644'
},
{
'path': '/root/update-tpm-bindings.sh',
'content': '''#!/bin/bash
# Script to update TPM bindings after firmware updates
# Usage: sudo ./update-tpm-bindings.sh
set -e
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit 1
fi
# Check if TPM is available
if ! tpm2_getcap properties-fixed > /dev/null; then
echo "Error: TPM not available"
exit 1
fi
# Check if at least one Tang server is accessible
TANG_AVAILABLE=0
for tang_url in $(grep -h "url" /etc/clevis/sss.conf | grep -o '"url": "[^"]*"' | cut -d'"' -f4); do
if curl -s -f "$tang_url/adv" > /dev/null; then
echo "Tang server $tang_url is accessible"
TANG_AVAILABLE=1
break
else
echo "Tang server $tang_url is not accessible"
fi
done
if [ $TANG_AVAILABLE -eq 0 ]; then
echo "Error: No Tang servers are accessible"
exit 1
fi
# Get current PCR values
echo "Current PCR values:"
PCR_IDS=$(grep -h "pcr_ids" /etc/clevis/sss.conf | grep -o '"pcr_ids": "[^"]*"' | cut -d'"' -f4)
tpm2_pcrread sha256:$(echo $PCR_IDS | tr ',' ' ')
# Ask for confirmation
read -p "Have you updated firmware? Continue with TPM binding update? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted"
exit 1
fi
# Get LUKS passphrase
if [ -f /root/luks-passphrase.txt ]; then
LUKS_PASSPHRASE=$(cat /root/luks-passphrase.txt)
else
echo "LUKS passphrase file not found. Please enter your LUKS passphrase:"
read -s LUKS_PASSPHRASE
echo
fi
# Update TPM bindings
echo "Updating TPM bindings..."
for dev in /dev/sda2 /dev/sdb2; do
echo "Processing $dev..."
# Unbind old SSS binding
SLOT=$(clevis luks list -d "$dev" | grep -n "sss" | cut -d: -f1)
if [ -n "$SLOT" ]; then
echo "Removing old SSS binding from slot $SLOT"
clevis luks unbind -d "$dev" -s "$SLOT" || true
fi
# Create new binding with SSS using the same config
echo "Adding new SSS binding"
echo -n "$LUKS_PASSPHRASE" | clevis luks bind -d "$dev" sss -c /etc/clevis/sss.conf -k-
# Verify binding was successful
if ! clevis luks list -d "$dev" | grep -q "sss"; then
echo "Error: Failed to create SSS binding for $dev"
exit 1
fi
done
echo "TPM bindings updated successfully!"
echo "Please reboot to verify the changes."
''',
'permissions': '0700'
}
],
# Use cloud-init's native disk setup for partitioning only
'disk_setup': {
'/dev/sda': {
'table_type': 'gpt',
'layout': [
[1024, 'boot'], # 1GB for boot
['auto', 'data'] # Rest for LUKS1
],
'overwrite': True
},
'/dev/sdb': {
'table_type': 'gpt',
'layout': [
[1024, 'boot'], # 1GB for boot mirror
['auto', 'data'] # Rest for LUKS2
],
'overwrite': True
}
},
'runcmd': [
# Check if passphrase is set
'[ -n "$LUKS_PASSPHRASE" ] || { echo "Error: LUKS passphrase not found"; exit 1; }',
# Verify Tang server is accessible
f'curl -s -f {system["luks"]["tang_url"]}/adv > /dev/null || {{ echo "Tang server not accessible"; exit 1; }}',
# Verify TPM is available
'tpm2_getcap properties-fixed > /dev/null || { echo "TPM not available"; exit 1; }',
# Create boot RAID1
'mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/sda1 /dev/sdb1 --metadata=1.2',
# Create filesystem on RAID array
'mkfs.ext4 -L boot /dev/md0',
# Mount boot RAID
'mkdir -p /mnt/boot',
'mount /dev/md0 /mnt/boot',
# Setup LUKS on data partitions
'echo -n "${LUKS_PASSPHRASE}" | tr -d "\n" | cryptsetup luksFormat /dev/sda2 --type luks2 --key-file -',
'echo -n "${LUKS_PASSPHRASE}" | tr -d "\n" | cryptsetup luksFormat /dev/sdb2 --type luks2 --key-file -',
# Setup Clevis with error handling
'clevis luks bind -d /dev/sda2 tpm2 -c /etc/clevis/tpm2.conf || { echo "TPM bind failed"; exit 1; }',
'clevis luks bind -d /dev/sda2 tang -c /etc/clevis/tang.conf || { echo "Tang bind failed"; exit 1; }',
'clevis luks bind -d /dev/sdb2 tpm2 -c /etc/clevis/tpm2.conf || { echo "TPM bind failed"; exit 1; }',
'clevis luks bind -d /dev/sdb2 tang -c /etc/clevis/tang.conf || { echo "Tang bind failed"; exit 1; }',
# Open LUKS volumes
'echo -n "${LUKS_PASSPHRASE}" | tr -d "\n" | cryptsetup luksOpen /dev/sda2 root_a --key-file -',
'echo -n "${LUKS_PASSPHRASE}" | tr -d "\n" | cryptsetup luksOpen /dev/sdb2 root_b --key-file -',
# Create BTRFS on decrypted devices
'mkfs.btrfs -f -d raid1 -m raid1 /dev/mapper/root_a /dev/mapper/root_b',
'mount /dev/mapper/root_a /mnt',
# Create subvolumes
'btrfs subvolume create /mnt/@home',
'btrfs subvolume create /mnt/@db',
'chattr +C /mnt/@db',
# Setup fstab
'echo "/dev/md0 /boot ext4 defaults 0 0" >> /etc/fstab',
'echo "/dev/mapper/root_a / btrfs compress=zstd 0 0" >> /etc/fstab',
'echo "/dev/mapper/root_a /home btrfs subvol=@home,compress=zstd 0 0" >> /etc/fstab',
'echo "/dev/mapper/root_a /db btrfs subvol=@db,nodatacow,noatime,compress=zstd 0 0" >> /etc/fstab',
# Save RAID configuration
'mdadm --detail --scan > /etc/mdadm.conf',
# Enable services
'systemctl enable clevis-luks-askpass.service',
'systemctl enable clevis-luks-askpass.path'
]
}
return yaml.dump(cloud_config)
def download_fedora_image(config):
"""Download Fedora Server netinstall image."""
image = config['image']
url = f"https://download.fedoraproject.org/pub/fedora/linux/releases/{image['version']}/Server/x86_64/iso/Fedora-Server-netinst-x86_64-{image['version']}-1.6.iso"
cmd = ['curl', '-L', '-o', 'fedora-server.iso', url]
subprocess.run(cmd, check=True)
def generate_kickstart(config):
"""Generate kickstart configuration for automated installation."""
system = config['system']
# TPM update script content
tpm_update_script = '''#!/bin/bash
# Script to update TPM bindings after firmware updates
# Usage: sudo ./update-tpm-bindings.sh
set -e
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit 1
fi
# Check if TPM is available
if ! tpm2_getcap properties-fixed > /dev/null; then
echo "Error: TPM not available"
exit 1
fi
# Check if at least one Tang server is accessible
TANG_AVAILABLE=0
for tang_url in $(grep -h "url" /etc/clevis/sss.conf | grep -o '"url": "[^"]*"' | cut -d'"' -f4); do
if curl -s -f "$tang_url/adv" > /dev/null; then
echo "Tang server $tang_url is accessible"
TANG_AVAILABLE=1
break
else
echo "Tang server $tang_url is not accessible"
fi
done
if [ $TANG_AVAILABLE -eq 0 ]; then
echo "Error: No Tang servers are accessible"
exit 1
fi
# Get current PCR values
echo "Current PCR values:"
PCR_IDS=$(grep -h "pcr_ids" /etc/clevis/sss.conf | grep -o '"pcr_ids": "[^"]*"' | cut -d'"' -f4)
tpm2_pcrread sha256:$(echo $PCR_IDS | tr ',' ' ')
# Ask for confirmation
read -p "Have you updated firmware? Continue with TPM binding update? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted"
exit 1
fi
# Get LUKS passphrase
if [ -f /root/luks-passphrase.txt ]; then
LUKS_PASSPHRASE=$(cat /root/luks-passphrase.txt)
else
echo "LUKS passphrase file not found. Please enter your LUKS passphrase:"
read -s LUKS_PASSPHRASE
echo
fi
# Update TPM bindings
echo "Updating TPM bindings..."
for dev in /dev/sda2 /dev/sdb2; do
echo "Processing $dev..."
# Unbind old SSS binding
SLOT=$(clevis luks list -d "$dev" | grep -n "sss" | cut -d: -f1)
if [ -n "$SLOT" ]; then
echo "Removing old SSS binding from slot $SLOT"
clevis luks unbind -d "$dev" -s "$SLOT" || true
fi
# Create new binding with SSS using the same config
echo "Adding new SSS binding"
echo -n "$LUKS_PASSPHRASE" | clevis luks bind -d "$dev" sss -c /etc/clevis/sss.conf -k-
# Verify binding was successful
if ! clevis luks list -d "$dev" | grep -q "sss"; then
echo "Error: Failed to create SSS binding for $dev"
exit 1
fi
done
echo "TPM bindings updated successfully!"
echo "Please reboot to verify the changes."
'''
# Create SSS config for TPM + any Tang server
tang_servers = []
for server in system['luks']['tang_servers']:
tang_servers.append({"url": server['url']})
# Convert Tang servers to JSON for SSS config
tang_servers_json = json.dumps(tang_servers)
# Create SSS policy: Require TPM AND at least one Tang server
sss_config = {
"t": 2, # Threshold: Both pins must succeed
"pins": {
"tpm2": {
"pcr_bank": system['luks']['tpm']['pcr_bank'],
"pcr_ids": ','.join(map(str, system['luks']['tpm']['pcr_ids']))
},
"tang": {"t": 1, "tang": tang_servers} # Only one Tang server needed from the list
}
}
# Convert config to JSON string
sss_config_json = json.dumps(sss_config)
kickstart = f"""# Kickstart configuration for Fedora Server
# Generated for Nullpoint
# System language
lang en_US.UTF-8
# Keyboard layouts
keyboard --vckeymap=us --xlayouts='us'
# Network information
network --bootproto=dhcp --device=link --activate
# Root password
rootpw --lock
# System timezone
timezone {system['timezone']} --utc
# Installation type
text
# Wipe all disk
zerombr
clearpart --all --initlabel
# Disk partitioning information
# Boot partitions (5GB each)
part /boot --fstype=btrfs --size=5120 --ondisk=sda
part /boot --fstype=btrfs --size=5120 --ondisk=sdb
# Main data partitions with LUKS
part / --fstype=btrfs --encrypted --cipher=aes-xts-plain64 --luks-version=luks2 --grow --ondisk=sda
part / --fstype=btrfs --encrypted --cipher=aes-xts-plain64 --luks-version=luks2 --grow --ondisk=sdb
# Package source
url --mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$releasever&arch=$basearch
repo --name=fedora
repo --name=updates
# Make sure initial-setup runs
firstboot --reconfig
# Package selection
%packages
@^server-product
@system-tools
btrfs-progs
clevis
clevis-luks
clevis-tang
clevis-tpm2
tpm2-tools
tpm2-tss
cryptsetup
systemd
curl
shim-x64
grub2-efi-x64
dropbear
%end
# Pre-installation script
%pre
# Create TPM and Tang config files
mkdir -p /etc/clevis
# Save SSS config for TPM + Tang servers
cat > /etc/clevis/sss.conf << EOF
{sss_config_json}
EOF
%end
# Post-installation script
%post
# https://unix.stackexchange.com/a/351755 for handling TTY in anaconda
printf "\n=== Nullpoint Installation Progress ===\r\n" > /dev/tty1
printf "Press Alt+F3 to view detailed installation logs\r\n" > /dev/tty1
printf "Press Alt+F1 to return to main installation screen\r\n" > /dev/tty1
printf "Current step: Setting up TPM and Tang...\r\n\n" > /dev/tty1
{{
# Get the LUKS passphrase that was used during installation
LUKS_PASSPHRASE=$(cat /tmp/luks-passphrase.txt)
echo "$LUKS_PASSPHRASE" > /root/luks-passphrase.txt
chmod 600 /root/luks-passphrase.txt
# Setup Clevis with SSS policy (TPM + at least one Tang server)
printf "Configuring TPM and Tang with SSS policy...\r\n" > /dev/tty1
echo "Using SSS policy: TPM verification AND (at least one Tang server)" > /dev/tty1
echo -n "$LUKS_PASSPHRASE" | clevis luks bind -d /dev/sda2 sss -c /etc/clevis/sss.conf -k-
echo -n "$LUKS_PASSPHRASE" | clevis luks bind -d /dev/sdb2 sss -c /etc/clevis/sss.conf -k-
# Get BTRFS UUID (same for all devices in the filesystem)
BTRFS_UUID=$(blkid -s UUID -o value /dev/mapper/luks-$(blkid -s UUID -o value /dev/sda2))
# Create subvolumes
printf "Creating BTRFS subvolumes...\r\n" > /dev/tty1
# Mount both devices for RAID1
mount -t btrfs -o raid1 UUID=$BTRFS_UUID /mnt
btrfs subvolume create /mnt/@root
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@db
chattr +C /mnt/@db
# Setup fstab
printf "Configuring system mount points...\r\n" > /dev/tty1
cat > /etc/fstab << EOF
UUID=$BTRFS_UUID / btrfs subvol=@root,compress=zstd,raid1 0 0
UUID=$BTRFS_UUID /home btrfs subvol=@home,compress=zstd,raid1 0 0
UUID=$BTRFS_UUID /db btrfs subvol=@db,nodatacow,noatime,compress=zstd,raid1 0 0
EOF
# Configure dropbear for early SSH access
printf "Configuring early SSH access...\r\n" > /dev/tty1
mkdir -p /etc/dropbear
echo "{config['admin_ssh_key']}" > /etc/dropbear/authorized_keys
chmod 600 /etc/dropbear/authorized_keys
# Enable dropbear for early SSH
systemctl enable dropbear
systemctl enable dropbear.socket
# Enable services
printf "Enabling system services...\r\n" > /dev/tty1
systemctl enable clevis-luks-askpass.service
systemctl enable clevis-luks-askpass.path
# Create TPM update script
printf "Creating TPM update script...\r\n" > /dev/tty1
cat > /root/update-tpm-bindings.sh << 'EOF'
{tpm_update_script}
EOF
chmod +x /root/update-tpm-bindings.sh
printf "\nInstallation complete! The system will reboot shortly.\r\n" > /dev/tty1
printf "IMPORTANT: LUKS passphrase has been saved to /root/luks-passphrase.txt\r\n" > /dev/tty1
}} 2>&1 | tee -a /root/postinstall.log > /dev/tty3
%end
"""
return kickstart
def customize_image(config):
"""Customize the Fedora Server image."""
# Generate kickstart config
kickstart = generate_kickstart(config)
# Create kickstart file
with tempfile.NamedTemporaryFile(mode='w', suffix='.ks') as f:
f.write(kickstart)
f.flush()
# Create custom ISO with kickstart
cmd = [
'mkisofs',
'-o', 'fedora-server-custom.iso',
'-b', 'isolinux/isolinux.bin',
'-c', 'isolinux/boot.cat',
'-boot-info-table',
'-no-emul-boot',
'-boot-load-size', '4',
'-R',
'-J',
'-v',
'-T',
'-V', 'Fedora-S-custom',
'-A', 'Fedora-S-custom',
'fedora-server.iso',
f.name
]
subprocess.run(cmd, check=True)
def create_snapshot(config):
"""Create new snapshot from image."""
image = config['image']
cmd = [
'hcloud-upload-image', 'upload',
'--architecture', image['hetzner_arch'],
'--compression', 'xz',
'--image-path', 'fedora-server-custom.iso',
'--name', image['name'],
'--labels', f'os=fedora-server,version={image["version"]}',
'--description', f'Fedora Server {image["version"]} for Nullpoint'
]
subprocess.run(cmd, check=True)
def main():
"""Main entry point."""
# Load config
if not os.path.exists('build-config.yaml'):
print("Error: build-config.yaml not found")
sys.exit(1)
config = load_config('build-config.yaml')
# Check prerequisites
check_prerequisites()
check_hetzner_token()
# Download Fedora Server image
print("Downloading Fedora Server image...")
download_fedora_image(config)
# Customize image
print("Customizing image...")
customize_image(config)
# Create snapshot
print("Creating snapshot...")
create_snapshot(config)
print("Image build complete!")
if __name__ == '__main__':
main()
-13
View File
@@ -1,13 +0,0 @@
# Deployment Configuration
hetzner:
datacenter: nbg1
server_type: cx31
ssh_key_name: fedora-server-hetzner
# Admin Configuration
admin_ssh_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..." # Your SSH public key here
# Hostname Configuration
hostname:
prefix: nullpoint
format: "{prefix}-{date}-{random}" # date format: YYMMDD, random: 000-999
-169
View File
@@ -1,169 +0,0 @@
#!/usr/bin/env python3
import os
import sys
import yaml
import subprocess
import json
import shutil
from datetime import datetime
import random
import tempfile
import secrets
import string
# List of adjectives for hostname generation
ADJECTIVES = [
# Scientific/Sci-fi
'quantum', 'atomic', 'plasma', 'fusion', 'ionic', 'magnetic', 'cosmic',
'stellar', 'nebular', 'pulsar', 'quasar', 'warp', 'phaser', 'hyper',
'temporal', 'spatial', 'dimensional', 'subspace', 'transwarp',
# Cool/Interesting
'abysmal', 'adamant', 'aerial', 'arcane', 'astral', 'azure', 'celestial',
'crimson', 'cryptic', 'crystalline', 'dormant', 'eerie', 'eldritch',
'ethereal', 'fractal', 'frozen', 'ghostly', 'gilded', 'singular',
'hollow', 'infernal', 'lunar', 'mystic', 'nebulous', 'obsidian',
'occult', 'prismatic', 'radiant', 'shadow', 'solar', 'spectral',
'stellar', 'sublime', 'titanic', 'twilight', 'void', 'volcanic'
]
NOUNS = [
# Star Trek
'enterprise', 'voyager', 'galaxy', 'intrepid', 'nova', 'warbird', 'falcon',
'aegis', 'nemesis', 'equinox', 'stargazer', 'challenger', 'discovery',
'prometheus', 'odyssey', 'daedalus', 'apollo', 'korolev', 'phoenix', 'orion',
# Space
'nebula', 'pulsar', 'quasar', 'nova', 'supernova', 'blackhole',
'wormhole', 'singularity', 'galaxy', 'void', 'rift', 'nexus',
# Cool Concepts
'abyss', 'aether', 'anomaly', 'artifact', 'beacon', 'cipher', 'crystal',
'echo', 'enigma', 'essence', 'fractal', 'horizon', 'infinity',
'paradox', 'phoenix', 'prism', 'spectrum', 'tesseract',
'vector', 'vertex', 'vortex', 'zenith'
]
def load_config(config_file):
"""Load and parse YAML config file."""
with open(config_file, 'r') as f:
return yaml.safe_load(f)
def check_prerequisites():
"""Check if required tools are installed."""
required_tools = ['hcloud', 'jq']
for tool in required_tools:
if not shutil.which(tool):
print(f"Error: {tool} not found. Please install it first.")
sys.exit(1)
def check_hetzner_token():
"""Check if HCLOUD_TOKEN is set."""
if not os.environ.get('HCLOUD_TOKEN'):
print("Error: HCLOUD_TOKEN environment variable not set")
sys.exit(1)
def generate_hostname():
"""Generate a unique hostname in adjective-noun-date-time format."""
adjective = random.choice(ADJECTIVES)
noun = random.choice(NOUNS)
timestamp = datetime.now().strftime('%y%m%d-%H%M')
return f"{adjective}-{noun}-{timestamp}"
def get_image_id():
"""Get the base image ID."""
cmd = [
'hcloud', 'image', 'list',
'--type=snapshot',
'--selector=name=fedora-coreos-nullpoint',
'--output', 'json'
]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
images = json.loads(result.stdout)
if not images:
print("Error: Base image not found. Run build.py first.")
sys.exit(1)
return images[0]['id']
def generate_secure_passphrase(length=32):
"""Generate a secure random passphrase."""
alphabet = string.ascii_letters + string.digits + string.punctuation
return ''.join(secrets.choice(alphabet) for _ in range(length))
def create_server(config, hostname, image_id):
"""Create a new server."""
hetzner = config['hetzner']
# Generate cloud-init config for this specific server
cloud_init = {
'hostname': hostname,
'timezone': 'UTC',
'users': [
{
'name': 'admin',
'groups': ['wheel'],
'sudo': 'ALL=(ALL) NOPASSWD:ALL',
'ssh_authorized_keys': [config['admin_ssh_key']]
}
],
'package_update': True,
'package_upgrade': True
}
# Create temporary cloud-init config
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml') as f:
yaml.dump(cloud_init, f)
f.flush()
cmd = [
'hcloud', 'server', 'create',
'--name', hostname,
'--type', hetzner['server_type'],
'--datacenter', hetzner['datacenter'],
'--image', str(image_id),
'--ssh-key', hetzner['ssh_key_name'],
'--user-data-from-file', f.name
]
subprocess.run(cmd, check=True)
print(f"\nServer '{hostname}' created successfully!")
print("The LUKS passphrase has been saved to /root/luks-passphrase.txt on the server.")
print("Please save this passphrase securely - it will be needed if TPM+Tang unlock fails.")
print("\nYou can connect using: ssh admin@<server-ip>")
def get_server_ip(hostname):
"""Get the server's IP address."""
cmd = ['hcloud', 'server', 'ip', hostname]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
return result.stdout.strip()
def main():
"""Main entry point."""
# Load config
if not os.path.exists('deploy-config.yaml'):
print("Error: deploy-config.yaml not found")
sys.exit(1)
config = load_config('deploy-config.yaml')
# Check prerequisites
check_prerequisites()
check_hetzner_token()
# Generate hostname
hostname = generate_hostname()
# Get image ID
image_id = get_image_id()
# Create server
print(f"Creating server '{hostname}'...")
create_server(config, hostname, image_id)
# Get server IP
server_ip = get_server_ip(hostname)
print(f"Server created! IP: {server_ip}")
print(f"You can connect using: ssh core@{server_ip}")
if __name__ == '__main__':
main()
+271
View File
@@ -0,0 +1,271 @@
'builtin' 'local' '-a' 'p10k_config_opts'
[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases')
[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob')
[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
() {
emulate -L zsh
setopt no_unset extended_glob
unset -m 'POWERLEVEL9K_*'
autoload -Uz is-at-least && is-at-least 5.1 || return
zmodload zsh/langinfo
if [[ ${langinfo[CODESET]:-} != (utf|UTF)(-|)8 ]]; then
local LC_ALL=${${(@M)$(locale -a):#*.(utf|UTF)(-|)8}[1]:-en_US.UTF-8}
fi
typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
context
dir
vcs
)
typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
background_jobs
direnv
virtualenv
pyenv
disk_usage
)
typeset -g POWERLEVEL9K_VISUAL_IDENTIFIER_EXPANSION='${P9K_VISUAL_IDENTIFIER// }'
typeset -g POWERLEVEL9K_MODE=nerdfont-complete
typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=
typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false
typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX='%242F╭─'
typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX='%242F├─'
typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX='%242F╰─'
typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX='%242F─╮'
typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX='%242F─┤'
typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX='%242F─╯'
typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' '
typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_BACKGROUND=
if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then
typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=242
typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}'
typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}'
fi
typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR='\uE0B1'
typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR='\uE0B3'
typeset -g POWERLEVEL9K_LEFT_SEGMENT_SEPARATOR='\uE0B0'
typeset -g POWERLEVEL9K_RIGHT_SEGMENT_SEPARATOR='\uE0B2'
typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='\uE0B0'
typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='\uE0B2'
typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL=''
typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL=''
typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=
typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND=232
typeset -g POWERLEVEL9K_OS_ICON_BACKGROUND=7
typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='%B${P9K_CONTENT// }'
typeset -g POWERLEVEL9K_PROMPT_CHAR_BACKGROUND=
typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76
typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION=' '
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION=' '
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION=' '
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION=' '
typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true
typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=
typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL=
typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_{LEFT,RIGHT}_WHITESPACE=
typeset -g POWERLEVEL9K_DIR_BACKGROUND=234
typeset -g POWERLEVEL9K_DIR_FOREGROUND=39
typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique
typeset -g POWERLEVEL9K_SHORTEN_DELIMITER=
typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103
typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39
typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=false
local anchor_files=(
.bzr
.citc
.git
.hg
.node-version
.python-version
.ruby-version
.shorten_folder_marker
.svn
.terraform
CVS
Cargo.toml
composer.json
go.mod
package.json
)
typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})"
typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1
typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80
typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40
typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50
typeset -g POWERLEVEL9K_DIR_HYPERLINK=false
typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=true
typeset -g POWERLEVEL9K_DIR_CLASSES=()
typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=10
typeset -g POWERLEVEL9K_VCS_CLEAN_BACKGROUND=235
typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=10
typeset -g POWERLEVEL9K_VCS_MODIFIED_BACKGROUND=235
typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=10
typeset -g POWERLEVEL9K_VCS_UNTRACKED_BACKGROUND=235
typeset -g POWERLEVEL9K_VCS_CONFLICTED_FOREGROUND=9
typeset -g POWERLEVEL9K_VCS_CONFLICTED_BACKGROUND=235
typeset -g POWERLEVEL9K_VCS_LOADING_FOREGROUND=15
typeset -g POWERLEVEL9K_VCS_LOADING_BACKGROUND=255
typeset -g POWERLEVEL9K_VCS_BRANCH_ICON='\uF126 '
POWERLEVEL9K_VCS_BRANCH_ICON=${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}
typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?'
POWERLEVEL9K_VCS_UNTRACKED_ICON=${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}
function my_git_formatter() {
emulate -L zsh
if [[ -n $P9K_CONTENT ]]; then
typeset -g my_git_format=$P9K_CONTENT
return
fi
local meta=''
local clean=''
local modified=''
local untracked=''
local conflicted=''
local res
local where
if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then
res+="${clean}${POWERLEVEL9K_VCS_BRANCH_ICON}"
where=${(V)VCS_STATUS_LOCAL_BRANCH}
elif [[ -n $VCS_STATUS_TAG ]]; then
res+="${meta}#"
where=${(V)VCS_STATUS_TAG}
fi
(( $#where > 32 )) && where[13,-13]="…"
res+="${clean}${where//\%/%%}"
[[ -z $where ]] && res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}"
if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then
res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}"
fi
(( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}"
(( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" "
(( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}"
(( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}"
[[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}"
(( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}"
(( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}"
(( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}"
(( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}"
typeset -g my_git_format=$res
}
functions -M my_git_formatter 2>/dev/null
typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true
typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter()))+${my_git_format}}'
typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1
typeset -g POWERLEVEL9K_VCS_BACKENDS=(git)
typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true
typeset -g POWERLEVEL9K_STATUS_OK=true
typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔'
typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true
typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔'
typeset -g POWERLEVEL9K_STATUS_ERROR=true
typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘'
typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true
typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false
typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘'
typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true
typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘'
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=0
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_BACKGROUND=3
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s'
typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false
typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION=
typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION=
typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=95
typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=98
typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=true
typeset -g POWERLEVEL9K_VI_MODE_FOREGROUND=0
typeset -g POWERLEVEL9K_VI_COMMAND_MODE_STRING=NORMAL
typeset -g POWERLEVEL9K_VI_MODE_NORMAL_BACKGROUND=2
typeset -g POWERLEVEL9K_VI_VISUAL_MODE_STRING=VISUAL
typeset -g POWERLEVEL9K_VI_MODE_VISUAL_BACKGROUND=4
typeset -g POWERLEVEL9K_VI_OVERWRITE_MODE_STRING=OVERTYPE
typeset -g POWERLEVEL9K_VI_MODE_OVERWRITE_BACKGROUND=3
typeset -g POWERLEVEL9K_VI_INSERT_MODE_STRING=
typeset -g POWERLEVEL9K_VI_MODE_INSERT_FOREGROUND=8
typeset -g POWERLEVEL9K_LOAD_WHICH=5
typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true
typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false
typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}'
typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=1
typeset -g POWERLEVEL9K_CONTEXT_ROOT_BACKGROUND=0
typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=3
typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_BACKGROUND=0
typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=3
typeset -g POWERLEVEL9K_CONTEXT_BACKGROUND=0
typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%n@%m'
typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m'
typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n'
typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
typeset -g POWERLEVEL9K_ANACONDA_SHOW_PYTHON_VERSION=false
typeset -g POWERLEVEL9K_ANACONDA_{LEFT,RIGHT}_DELIMITER=
typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global)
typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false
typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global)
typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false
typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false
typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false
typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER=
typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true
typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true
typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true
typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true
typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global)
typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false
typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false
typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false
typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global)
typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false
typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global)
typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false
typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global)
typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false
typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=(
'*' DEFAULT)
typeset -g POWERLEVEL9K_TERRAFORM_DEFAULT_FOREGROUND=4
typeset -g POWERLEVEL9K_TERRAFORM_DEFAULT_BACKGROUND=0
typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc'
typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=(
'*' DEFAULT)
typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=7
typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_BACKGROUND=5
typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION=
POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}'
POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}'
typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|terraform|pulumi'
typeset -g POWERLEVEL9K_AWS_CLASSES=(
'*' DEFAULT)
typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi'
typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs'
typeset -g POWERLEVEL9K_GCLOUD_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT//\%/%%}'
typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi'
typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=(
'*' DEFAULT)
typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}'
typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=0
typeset -g POWERLEVEL9K_VPN_IP_BACKGROUND=6
typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION=
typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(wg|(.*tun))[0-9]*'
typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20
typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=1
typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=2
typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=3
typeset -g POWERLEVEL9K_BATTERY_STAGES=$'\uf58d\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf578'
typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false
typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
function prompt_example() {
p10k segment -i "POWERLEVEL9K_LINUX_ICON" -r -f blue -t " "
}
function instant_prompt_example() {
prompt_example
}
typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=off
typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose
typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true
(( ! $+functions[p10k] )) || p10k reload
}
(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
'builtin' 'unset' 'p10k_config_opts'
+1370
View File
File diff suppressed because it is too large Load Diff
+404
View File
@@ -0,0 +1,404 @@
# : << EOF
# https://github.com/gpakosz/.tmux
# (‑●‑●)> dual licensed under the WTFPL v2 license and the MIT license,
# without any warranty.
# Copyright 2012— Gregory Pakosz (@gpakosz).
# -- navigation ----------------------------------------------------------------
# if you're running tmux within iTerm2
# - and tmux is 1.9 or 1.9a
# - and iTerm2 is configured to let option key act as +Esc
# - and iTerm2 is configured to send [1;9A -> [1;9D for option + arrow keys
# then uncomment the following line to make Meta + arrow keys mapping work
#set -ga terminal-overrides "*:kUP3=\e[1;9A,*:kDN3=\e[1;9B,*:kRIT3=\e[1;9C,*:kLFT3=\e[1;9D"
# -- windows & pane creation ---------------------------------------------------
# new window retains current path, possible values are:
# - true
# - false (default)
tmux_conf_new_window_retain_current_path=false
# new pane retains current path, possible values are:
# - true (default)
# - false
tmux_conf_new_pane_retain_current_path=true
# new pane tries to reconnect ssh sessions (experimental), possible values are:
# - true
# - false (default)
tmux_conf_new_pane_reconnect_ssh=true
# prompt for session name when creating a new session, possible values are:
# - true
# - false (default)
tmux_conf_new_session_prompt=false
# -- display -------------------------------------------------------------------
# RGB 24-bit colour support (tmux >= 2.2), possible values are:
# - true
# - false (default)
tmux_conf_24b_colour=true
# default theme
tmux_conf_theme_colour_1="#080808" # dark gray
tmux_conf_theme_colour_2="#303030" # gray
tmux_conf_theme_colour_3="#8a8a8a" # light gray
tmux_conf_theme_colour_4="#00afff" # light blue
tmux_conf_theme_colour_5="#ffff00" # yellow
tmux_conf_theme_colour_6="#080808" # dark gray
tmux_conf_theme_colour_7="#e4e4e4" # white
tmux_conf_theme_colour_8="#080808" # dark gray
tmux_conf_theme_colour_9="#ffff00" # yellow
tmux_conf_theme_colour_10="#ff00af" # pink
tmux_conf_theme_colour_11="#5fff00" # green
tmux_conf_theme_colour_12="#8a8a8a" # light gray
tmux_conf_theme_colour_13="#e4e4e4" # white
tmux_conf_theme_colour_14="#080808" # dark gray
tmux_conf_theme_colour_15="#080808" # dark gray
tmux_conf_theme_colour_16="#d70000" # red
tmux_conf_theme_colour_17="#e4e4e4" # white
# default theme (ansi)
#tmux_conf_theme_colour_1="colour0"
#tmux_conf_theme_colour_2="colour8"
#tmux_conf_theme_colour_3="colour8"
#tmux_conf_theme_colour_4="colour14"
#tmux_conf_theme_colour_5="colour11"
#tmux_conf_theme_colour_6="colour0"
#tmux_conf_theme_colour_7="colour15"
#tmux_conf_theme_colour_8="colour0"
#tmux_conf_theme_colour_9="colour11"
#tmux_conf_theme_colour_10="colour13"
#tmux_conf_theme_colour_11="colour10"
#tmux_conf_theme_colour_12="colour8"
#tmux_conf_theme_colour_13="colour15"
#tmux_conf_theme_colour_14="colour0"
#tmux_conf_theme_colour_15="colour0"
#tmux_conf_theme_colour_16="colour1"
#tmux_conf_theme_colour_17="colour15"
# window style
tmux_conf_theme_window_fg="default"
tmux_conf_theme_window_bg="default"
# highlight focused pane (tmux >= 2.1), possible values are:
# - true
# - false (default)
tmux_conf_theme_highlight_focused_pane=false
# focused pane colours:
tmux_conf_theme_focused_pane_bg="$tmux_conf_theme_colour_2"
# pane border style, possible values are:
# - thin (default)
# - fat
tmux_conf_theme_pane_border_style=thin
# pane borders colours:
tmux_conf_theme_pane_border="$tmux_conf_theme_colour_2"
tmux_conf_theme_pane_active_border="$tmux_conf_theme_colour_4"
# pane indicator colours (when you hit <prefix> + q)
tmux_conf_theme_pane_indicator="$tmux_conf_theme_colour_4"
tmux_conf_theme_pane_active_indicator="$tmux_conf_theme_colour_4"
# status line style
tmux_conf_theme_message_fg="$tmux_conf_theme_colour_1"
tmux_conf_theme_message_bg="$tmux_conf_theme_colour_5"
tmux_conf_theme_message_attr="bold"
# status line command style (<prefix> : Escape)
tmux_conf_theme_message_command_fg="$tmux_conf_theme_colour_5"
tmux_conf_theme_message_command_bg="$tmux_conf_theme_colour_1"
tmux_conf_theme_message_command_attr="bold"
# window modes style
tmux_conf_theme_mode_fg="$tmux_conf_theme_colour_1"
tmux_conf_theme_mode_bg="$tmux_conf_theme_colour_5"
tmux_conf_theme_mode_attr="bold"
# status line style
tmux_conf_theme_status_fg="$tmux_conf_theme_colour_3"
tmux_conf_theme_status_bg="$tmux_conf_theme_colour_1"
tmux_conf_theme_status_attr="none"
# terminal title
# - built-in variables are:
# - #{circled_window_index}
# - #{circled_session_name}
# - #{hostname}
# - #{hostname_ssh}
# - #{username}
# - #{username_ssh}
#tmux_conf_theme_terminal_title="#{username}@#{hostname}: #S - #I - #W"
tmux_conf_theme_terminal_title=" "
# window status style
# - built-in variables are:
# - #{circled_window_index}
# - #{circled_session_name}
# - #{hostname}
# - #{hostname_ssh}
# - #{username}
# - #{username_ssh}
tmux_conf_theme_window_status_fg="$tmux_conf_theme_colour_3"
tmux_conf_theme_window_status_bg="$tmux_conf_theme_colour_1"
tmux_conf_theme_window_status_attr="none"
tmux_conf_theme_window_status_format="#I #W"
#tmux_conf_theme_window_status_format="#{circled_window_index} #W"
#tmux_conf_theme_window_status_format="#I #W#{?window_bell_flag,🔔,}#{?window_zoomed_flag,🔍,}"
# window current status style
# - built-in variables are:
# - #{circled_window_index}
# - #{circled_session_name}
# - #{hostname}
# - #{hostname_ssh}
# - #{username}
# - #{username_ssh}
tmux_conf_theme_window_status_current_fg="$tmux_conf_theme_colour_1"
tmux_conf_theme_window_status_current_bg="$tmux_conf_theme_colour_4"
tmux_conf_theme_window_status_current_attr="bold"
tmux_conf_theme_window_status_current_format="#I #W"
#tmux_conf_theme_window_status_current_format="#{circled_window_index} #W"
#tmux_conf_theme_window_status_current_format="#I #W#{?window_zoomed_flag,🔍,}"
# window activity status style
tmux_conf_theme_window_status_activity_fg="default"
tmux_conf_theme_window_status_activity_bg="default"
tmux_conf_theme_window_status_activity_attr="underscore"
# window bell status style
tmux_conf_theme_window_status_bell_fg="$tmux_conf_theme_colour_5"
tmux_conf_theme_window_status_bell_bg="default"
tmux_conf_theme_window_status_bell_attr="blink,bold"
# window last status style
tmux_conf_theme_window_status_last_fg="$tmux_conf_theme_colour_4"
tmux_conf_theme_window_status_last_bg="$tmux_conf_theme_colour_2"
tmux_conf_theme_window_status_last_attr="none"
# status left/right sections separators
#tmux_conf_theme_left_separator_main=""
#tmux_conf_theme_left_separator_sub="|"
#tmux_conf_theme_right_separator_main=""
#tmux_conf_theme_right_separator_sub="|"
tmux_conf_theme_left_separator_main="\uE0B0" # /!\ you don't need to install Powerline
tmux_conf_theme_left_separator_sub="\uE0B1" # you only need fonts patched with
tmux_conf_theme_right_separator_main="\uE0B2" # Powerline symbols or the standalone
tmux_conf_theme_right_separator_sub="\uE0B3" # PowerlineSymbols.otf font, see README.md
# status left/right content:
# - separate main sections with "|"
# - separate subsections with ","
# - built-in variables are:
# - #{battery_bar}
# - #{battery_hbar}
# - #{battery_percentage}
# - #{battery_status}
# - #{battery_vbar}
# - #{circled_session_name}
# - #{hostname_ssh}
# - #{hostname}
# - #{loadavg}
# - #{pairing}
# - #{prefix}
# - #{root}
# - #{synchronized}
# - #{uptime_y}
# - #{uptime_d} (modulo 365 when #{uptime_y} is used)
# - #{uptime_h}
# - #{uptime_m}
# - #{uptime_s}
# - #{username}
# - #{username_ssh}
#tmux_conf_theme_status_left=" ❐ #S | ↑#{?uptime_y, #{uptime_y}y,}#{?uptime_d, #{uptime_d}d,}#{?uptime_h, #{uptime_h}h,}#{?uptime_m, #{uptime_m}m,} "
tmux_conf_theme_status_left=" [#S] | #{username}@#{hostname} ,"
#tmux_conf_theme_status_left=" [#S] "
#tmux_conf_theme_status_right=" #{prefix}#{pairing}#{synchronized}#{?battery_status,#{battery_status},}#{?battery_bar, #{battery_bar},}#{?battery_percentage, #{battery_percentage},} , %R , %d %b | #{username}#{root} | #{hostname} "
tmux_conf_theme_status_right=", %R , %d %b | #{battery_percentage} "
# status left style
tmux_conf_theme_status_left_fg="$tmux_conf_theme_colour_1,$tmux_conf_theme_colour_12"
tmux_conf_theme_status_left_bg="$tmux_conf_theme_colour_3,$tmux_conf_theme_colour_1"
tmux_conf_theme_status_left_attr="bold,none,none"
# status right style
tmux_conf_theme_status_right_fg="$tmux_conf_theme_colour_12,$tmux_conf_theme_colour_1"
tmux_conf_theme_status_right_bg="$tmux_conf_theme_colour_1,$tmux_conf_theme_colour_3"
tmux_conf_theme_status_right_attr="none,none,none"
# pairing indicator
tmux_conf_theme_pairing="⚇" # U+2687
tmux_conf_theme_pairing_fg="none"
tmux_conf_theme_pairing_bg="none"
tmux_conf_theme_pairing_attr="none"
# prefix indicator
tmux_conf_theme_prefix="⌨" # U+2328
tmux_conf_theme_prefix_fg="none"
tmux_conf_theme_prefix_bg="none"
tmux_conf_theme_prefix_attr="none"
# root indicator
tmux_conf_theme_root="!"
tmux_conf_theme_root_fg="none"
tmux_conf_theme_root_bg="none"
tmux_conf_theme_root_attr="bold,blink"
# synchronized indicator
tmux_conf_theme_synchronized="⚏" # U+268F
tmux_conf_theme_synchronized_fg="none"
tmux_conf_theme_synchronized_bg="none"
tmux_conf_theme_synchronized_attr="none"
# battery bar symbols
tmux_conf_battery_bar_symbol_full="◼"
tmux_conf_battery_bar_symbol_empty="◻"
#tmux_conf_battery_bar_symbol_full="♥"
#tmux_conf_battery_bar_symbol_empty="·"
# battery bar length (in number of symbols), possible values are:
# - auto
# - a number, e.g. 5
tmux_conf_battery_bar_length="auto"
# battery bar palette, possible values are:
# - gradient (default)
# - heat
# - "colour_full_fg,colour_empty_fg,colour_bg"
tmux_conf_battery_bar_palette="gradient"
#tmux_conf_battery_bar_palette="#d70000,#e4e4e4,#000000" # red, white, black
# battery hbar palette, possible values are:
# - gradient (default)
# - heat
# - "colour_low,colour_half,colour_full"
tmux_conf_battery_hbar_palette="gradient"
#tmux_conf_battery_hbar_palette="#d70000,#ff5f00,#5fff00" # red, orange, green
# battery vbar palette, possible values are:
# - gradient (default)
# - heat
# - "colour_low,colour_half,colour_full"
tmux_conf_battery_vbar_palette="gradient"
#tmux_conf_battery_vbar_palette="#d70000,#ff5f00,#5fff00" # red, orange, green
# symbols used to indicate whether battery is charging or discharging
tmux_conf_battery_status_charging="↑" # U+2191
tmux_conf_battery_status_discharging="↓" # U+2193
#tmux_conf_battery_status_charging="🔌" # U+1F50C
#tmux_conf_battery_status_discharging="🔋" # U+1F50B
# clock style (when you hit <prefix> + t)
# you may want to use %I:%M %p in place of %R in tmux_conf_theme_status_right
tmux_conf_theme_clock_colour="$tmux_conf_theme_colour_4"
tmux_conf_theme_clock_style="24"
# -- clipboard -----------------------------------------------------------------
# in copy mode, copying selection also copies to the OS clipboard
# - true
# - false (default)
# on macOS, this requires installing reattach-to-user-namespace, see README.md
# on Linux, this requires xsel or xclip
tmux_conf_copy_to_os_clipboard=true
# -- user customizations -------------------------------------------------------
# this is the place to override or undo settings
# increase history size
set -g history-limit 2048
# start with mouse mode enabled
set -g mouse on
# force Vi mode
# really you should export VISUAL or EDITOR environment variable, see manual
#set -g status-keys vi
#set -g mode-keys vi
# replace C-b by C-a instead of using both prefixes
#set -gu prefix2
#unbind C-a
#unbind C-b
#set -g prefix C-a
#bind C-a send-prefix
# move status line to top
#set -g status-position top
####### FULL CUSTOM ########
#bind '+' split-window -h
#bind '#' split-window -v
bind -r '+' split-window -p 50 -h -c "#{pane_current_path}"# \; set -u pane-active-border-style
bind -r '#' split-window -p 50 -v -c "#{pane_current_path}"# \; set -u pane-active-border-style
bind -n C-Left select-pane -L
bind -n C-Right select-pane -R
bind -n C-Up select-pane -U
bind -n C-Down select-pane -D
bind -r Tab next-window
#bind c new-window -c '#{pane_current_path}'
bind Z resize-pane -Z
set -g mouse on
setw -g monitor-activity on
set -g visual-activity on
set -g history-limit 2048
set -s escape-time 50
#set -g default-terminal "screen-256color"
set -g status-bg black
set -g status-fg white
set -g window-status-current-style bg=white,fg=black,bold
#set -g status-justify centre
set -g status-interval 60
set -g status-left-length 30
set -g status-left '#[fg=green][#S] #(whoami)@#(hostname) \uE0B0 #[bg=black] '
set -g status-right '#[fg=yellow]#(cut -d " " -f 1-3 /proc/loadavg)#[default] #[fg=white]%H:%M#[default]'
#set-option -ga terminal-overrides ",xterm-256color:Tc"
###bind y run-shell "tmux show-buffer | xclip -sel clip -i" \; display-message "Copied tmux buffer to system clipboard"
#bind -T copy-mode y send-keys -X copy-pipe-and-cancel "xsel -i -p && xsel -o -p | xsel -i -b"
#bind C-y run "xsel -o | tmux load-buffer - ; tmux paste-buffer"
# -- custom variables ----------------------------------------------------------
# to define a custom #{foo} variable, define a POSIX shell function between the
# '# EOF' and the '# "$@"' lines. Please note that the opening brace { character
# must be on the same line as the function name otherwise the parse won't detect
# it.
#
# then, use #{foo} in e.g. the 'tmux_conf_theme_status_left' or the
# 'tmux_conf_theme_status_right' variables.
# # /!\ do not remove the following line
# EOF
#
# weather() {
# curl -m 1 wttr.in?format=3 2>/dev/null
# sleep 900 # sleep for 15 minutes, throttle network requests whatever the value of status-interval
# }
#
# online() {
# ping -t 1 -c 1 1.1.1.1 >/dev/null 2>&1 && printf '✔' || printf '✘'
# }
#
# "$@"
# # /!\ do not remove the previous line
+131
View File
@@ -0,0 +1,131 @@
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="powerlevel10k/powerlevel10k"
HIST_STAMPS="dd.mm.yyyy"
plugins=(git)
source $ZSH/oh-my-zsh.sh
autoload -U +X bashcompinit && bashcompinit
[ -f /usr/share/bash-completion/completions/dnf ] && source /usr/share/bash-completion/completions/dnf
######################
# User configuration #
######################
export EDITOR='nvim'
if [ $TILIX_ID ] || [ $VTE_VERSION ]; then
[ -f /etc/profile.d/vte.sh ] && source /etc/profile.d/vte.sh
fi
#export LS_COLORS='rs=0:no=00:mi=00:mh=00:ln=01;36:or=01;31:di=01;34:ow=04;01;34:st=34:tw=04;34:pi=01;33:so=01;33:do=01;33:bd=01;33:cd=01;33:su=01;35:sg=01;35:ca=01;35:ex=01;32:'
##########
# lang
alias ptpython="python -m ptpython"
alias ptpy="python -m ptpython"
alias py13='python3.13'
alias py12='python3.12'
alias py10='python3.10'
alias bpytop="python -m bpytop"
# python venv
alias pvcreate="python -m venv .venv"
alias pvactivate="source .venv/bin/activate"
alias pvinstall="echo 'Creating Python-venv' && pvcreate && echo 'Entering venv' && pvactivate && echo 'Upgrading pip' && pip install pip --upgrade && echo 'Installing Dependencies' && pip install -r requirements.txt && echo 'Done.'"
# qol
alias cls="clear"
alias root="sudo su --shell /bin/zsh"
alias open="xdg-open"
alias filenum="du -a | cut -d/ -f2 | sort | uniq -c | sort -nr"
alias pg="progress"
alias hist="history"
alias untar="tar -zxvf"
alias neofetch=fastfetch
# grep
alias antigrep="grep -v"
alias grep2="grep -A 2 -B 2"
alias grep4="grep -A 4 -B 4"
alias grep8="grep -A 8 -B 8"
alias grep16="grep -A 16 -B 16"
alias grep32="grep -A 32 -B 32"
alias ag="alias | grep"
# packages
alias get="sudo dnf install"
alias upd="sudo dnf check-upgrade"
alias upg="sudo dnf upgrade"
# chars
alias c="clear"
alias t="bpytop"
alias p="ptpy"
alias h="history | grep"
alias v="nvim"
alias n="nano"
alias o="open"
alias e="\d"
alias ":q"="exit"
# git
alias gs="git status"
alias gt="git log --all --graph --decorate --oneline --abbrev-commit"
alias gnoc='git shortlog -sn | cat' # Number Of Commit by user
alias gloc='git ls-files | while read f; do git blame -w -M -C -C --line-porcelain "$f" | grep -I "^author "; done | sort -f | uniq -ic | sort -n --reverse' # Lines Of Code by user (in final version)
alias gpa='gaa && gc -m "." && gp -4'
# dirs
alias tmp="cd ~/.tmp"
# extensions
alias sudo="sudo "
alias watch="watch "
alias watch1="watch -n 1 "
# better ls via lsd
alias ls="lsd"
alias l="ls -1"
alias la="ls -A"
alias laa="ls -a"
alias lsa="ls -lA"
alias lla="ls -lA"
alias ltree="ls --tree"
alias d0="ls --tree"
alias d="ls --depth 1 --tree"
alias d1="ls --depth 1 --tree"
alias d2="ls --depth 2 --tree"
alias d3="ls --depth 3 --tree"
alias d4="ls --depth 4 --tree"
alias d5="ls --depth 5 --tree"
alias d6="ls --depth 6 --tree"
# more ls
alias lo="\ls --color=tty"
alias dtree="tree --du -h"
alias gtree="tree --du -h -F -C | grep 'G] ' --color=never"
# better cat via batcat
alias bat="bat --pager ''"
alias pat="bat --pager '' --plain"
alias lat="bat"
# more fancy batcat stuff
alias man="batman"
# tmux
alias s="tmux"
alias sa="tmux attach"
alias sn="tmux new -s"
alias san="tmux attach -t"
alias sl="tmux ls"
alias sk="tmux kill-session -t"
alias ska="tmux kill-session -a"
#####
export PATH=$PATH:$HOME/.local/bin
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# Simple installer for nullpoint - just downloads and runs
# Usage: wget -qO- https://git.dominik-roth.eu/dodox/nullpoint/raw/branch/master/get.sh | bash
set -euo pipefail
# Clean up any existing directory
[ -d "/tmp/nullpoint_installer" ] && rm -rf /tmp/nullpoint_installer
# Clone and run
echo "[+] Downloading nullpoint installer..."
git clone https://git.dominik-roth.eu/dodox/nullpoint.git /tmp/nullpoint_installer
cd /tmp/nullpoint_installer
exec bash install.sh
+13
View File
@@ -0,0 +1,13 @@
DRIVE1 /dev/nvme0n1
DRIVE2 /dev/nvme1n1
HOSTNAME nullpoint
BOOTLOADER grub
SWRAID 1
SWRAIDLEVEL 1
FILESYSTEM xfs
IMAGE /root/images/Alma-9-latest-amd64-base.tar.gz
CRYPTPASSWORD changeme
PART /boot/efi esp 256M
PART /boot ext4 2G
PART / xfs all crypt
+251
View File
@@ -0,0 +1,251 @@
#!/bin/bash
# nullpoint installer - run this from Hetzner rescue mode
# Use get.sh for wget piping: wget -qO- https://git.dominik-roth.eu/dodox/nullpoint/raw/branch/master/get.sh | bash
set -euo pipefail
BANNER=$(cat << "EOF"
:^7J5GB##&&##GPY?~:
^75B&@@@@@@&&&@@@@@@@#GJ~:
5&@@@&B5?7~^^^^^~!7YP#@@@@#!
Y##P7^ :~JB#B!
:: :
7PP?: :^~!!~^: :?PP7
:B@@B: !5B&@@@@&B5! :#@@B:
:!!: ^G@@@&BPPB@@@@G^ :!!:
:B@@@5^ ^5@@@B:
:7J7: !@@@# :&@@@~ :?J7:
J@@@5 :#@@@Y: :Y@@@B: 5@@@J
!@@@&^ ~B@@@&G55G&@@@B~ ~&@@@~
5@@@G: :7P#@@@@@@#P7: :B@@@Y
:P@@@B~ :~!77!~: ~B@@@P
Y@@@&Y^ ^5@@@@J
!G@@@&P7^ ^7P&@@@G~
!P&@@@&B? :: ?B&@@@&P!
^75#&&Y :P&&5: 5&&B57^
:^^ :P&&5: ^^:
^^
[nullpoint]
EOF
)
clear
echo -e "\n$BANNER"
echo -e "\n[+] nullpoint installer starting..."
# Check if we're in Hetzner rescue mode
if [ ! -f /etc/hetzner-build ]; then
echo "ERROR: This script must be run from Hetzner rescue mode!"
exit 1
fi
# Get SSH key from current session
echo "[+] Detecting SSH key from current session..."
SSH_KEY=$(grep "^ssh-" ~/.ssh/authorized_keys | head -1)
if [ -z "$SSH_KEY" ]; then
echo "ERROR: No SSH key found in authorized_keys!"
echo "Please enter your SSH public key:"
read -r SSH_KEY
if [ -z "$SSH_KEY" ]; then
echo "SSH key is required!"
exit 1
fi
fi
echo "Found SSH key: ${SSH_KEY:0:50}..."
# Ask for hostname
echo -e "\n[+] Server configuration"
read -r -p "Enter hostname [nullpoint]: " HOSTNAME < /dev/tty
HOSTNAME=${HOSTNAME:-nullpoint}
# Ask for username
read -r -p "Enter username for admin account [null]: " USERNAME < /dev/tty
USERNAME=${USERNAME:-null}
# Generate secure LUKS passphrase
echo -e "\n[+] Generating secure LUKS passphrase..."
LUKS_PASS=$(openssl rand -base64 30)
echo -e "\n================================================"
echo "LUKS PASSPHRASE (SAVE THIS!):"
echo "$LUKS_PASS"
echo "================================================"
echo -e "\nPress Enter when you've saved the passphrase..."
read -r < /dev/tty
# Check if we're already in the nullpoint directory or need to use it
if [ -f "install.conf" ] && [ -f "post-install.sh" ]; then
echo "[+] Using local nullpoint files..."
else
# Clean up any existing directory
if [ -d "/tmp/nullpoint" ]; then
rm -rf /tmp/nullpoint
fi
echo "[+] Downloading nullpoint configuration..."
git clone https://git.dominik-roth.eu/dodox/nullpoint.git /tmp/nullpoint
cd /tmp/nullpoint
fi
echo "[+] Configuring installation..."
# Auto-detect drives
echo "[+] Detecting drives..."
DRIVES=($(lsblk -dno NAME,TYPE | grep disk | awk '{print "/dev/"$1}' | sort))
if [ ${#DRIVES[@]} -lt 2 ]; then
echo "ERROR: Need at least 2 drives for RAID1, found ${#DRIVES[@]}"
exit 1
fi
DRIVE1="${DRIVES[0]}"
DRIVE2="${DRIVES[1]}"
echo " Found drives: $DRIVE1 and $DRIVE2"
# Update install.conf with detected drives
if ! sed -i "s|^DRIVE1 .*|DRIVE1 $DRIVE1|" install.conf; then
echo "ERROR: Failed to update DRIVE1 in install.conf"
exit 1
fi
if ! sed -i "s|^DRIVE2 .*|DRIVE2 $DRIVE2|" install.conf; then
echo "ERROR: Failed to update DRIVE2 in install.conf"
exit 1
fi
# Update hostname
if ! sed -i "s/^HOSTNAME .*/HOSTNAME $HOSTNAME/" install.conf; then
echo "ERROR: Failed to update HOSTNAME in install.conf"
exit 1
fi
# Use awk for CRYPTPASSWORD to handle special characters
if ! awk -v pass="$LUKS_PASS" '
/^CRYPTPASSWORD / { print "CRYPTPASSWORD " pass; next }
{ print }
' install.conf > install.conf.tmp; then
echo "ERROR: Failed to update CRYPTPASSWORD in install.conf"
exit 1
fi
mv install.conf.tmp install.conf
# Update post-install.sh
if ! sed -i "s/^ALMA_USER=.*/ALMA_USER=\"$USERNAME\"/" post-install.sh; then
echo "ERROR: Failed to update ALMA_USER in post-install.sh"
exit 1
fi
# Use awk to replace SSH_KEY line to avoid sed issues with special characters
if ! awk -v key="$SSH_KEY" '
/^SSH_KEY=/ { print "SSH_KEY=\"" key "\""; next }
{ print }
' post-install.sh > post-install.sh.tmp; then
echo "ERROR: Failed to update SSH_KEY in post-install.sh"
exit 1
fi
mv post-install.sh.tmp post-install.sh
# Copy to root directory where installimage expects them
if ! cp install.conf /root/; then
echo "ERROR: Failed to copy install.conf to /root/"
exit 1
fi
if ! cp post-install.sh /root/; then
echo "ERROR: Failed to copy post-install.sh to /root/"
exit 1
fi
chmod +x /root/post-install.sh
# Ask for optional features
echo -e "\n[+] Optional features:"
read -r -p "Do you have a TPM and want to use it? [y/N]: " USE_TPM < /dev/tty
if [[ "$USE_TPM" =~ ^[Yy]$ ]]; then
echo "TPM will be configured if available."
else
sed -i 's/^TPM_ENABLED=.*/TPM_ENABLED=false/' /root/post-install.sh
fi
read -r -p "Do you want to configure remote unlock Tang servers? [y/N]: " USE_TANG < /dev/tty
if [[ "$USE_TANG" =~ ^[Yy]$ ]]; then
echo "Configuring Tang servers..."
TANG_CONFIG=""
while true; do
read -r -p "Enter Tang server URL (or press Enter to finish): " TANG_URL < /dev/tty
if [ -z "$TANG_URL" ]; then
break
fi
read -r -p "Enter thumbprint for $TANG_URL: " TANG_THUMBPRINT < /dev/tty
if [ -n "$TANG_THUMBPRINT" ]; then
TANG_CONFIG+=" \"$TANG_URL $TANG_THUMBPRINT\"\n"
echo "Added Tang server: $TANG_URL"
else
echo "Skipping server (no thumbprint provided)"
fi
done
if [ -n "$TANG_CONFIG" ]; then
# Update the TANG_SERVERS array in post-install.sh
sed -i '/^TANG_SERVERS=(/,/^)/ {
/^TANG_SERVERS=(/ {
r /dev/stdin
d
}
/^)/ !d
}' /root/post-install.sh << EOF
TANG_SERVERS=(
$TANG_CONFIG)
EOF
echo "Configured Tang servers in post-install script."
else
echo "No Tang servers configured."
fi
fi
# Final confirmation
echo -e "\n[+] Ready to install with these settings:"
echo " Hostname: $HOSTNAME"
echo " Username: $USERNAME"
echo " SSH Key: ${SSH_KEY:0:50}..."
echo " LUKS Passphrase: $LUKS_PASS"
echo ""
read -r -p "Proceed with installation? [Y/n]: " CONFIRM < /dev/tty
if [[ "$CONFIRM" =~ ^[Nn]$ ]]; then
echo "Installation cancelled."
exit 1
fi
# Check if installimage exists
if ! command -v installimage &> /dev/null; then
echo "ERROR: installimage command not found!"
echo "This might not be a Hetzner rescue system, or installimage is not in PATH."
echo "On Hetzner rescue, installimage is usually at /root/.oldroot/nfs/install/installimage"
# Try common locations
if [ -x "/root/.oldroot/nfs/install/installimage" ]; then
echo "Found installimage at /root/.oldroot/nfs/install/installimage"
INSTALLIMAGE_CMD="/root/.oldroot/nfs/install/installimage"
else
echo "Could not find installimage. Please check your Hetzner rescue environment."
exit 1
fi
else
INSTALLIMAGE_CMD="installimage"
fi
# Run the installer
echo -e "\n[+] Starting Hetzner installimage..."
echo "The installer will now run. Follow any prompts if needed."
echo ""
if ! $INSTALLIMAGE_CMD -a -c /root/install.conf -x /root/post-install.sh; then
echo -e "\nERROR: Installation failed!"
exit 1
fi
echo -e "\n[+] Installation complete!"
echo ""
echo "IMPORTANT REMINDERS:"
echo "1. Save your LUKS passphrase securely!"
echo "2. After reboot, you'll need to enter it twice (once per disk)"
echo "3. SSH to the server as user '$USERNAME'"
echo ""
echo "The system is ready for use!"
+347
View File
@@ -0,0 +1,347 @@
#!/bin/bash
set -euo pipefail
BANNER=$(cat << "EOF"
:^7J5GB##&&##GPY?~:
^75B&@@@@@@&&&@@@@@@@#GJ~:
5&@@@&B5?7~^^^^^~!7YP#@@@@#!
Y##P7^ :~JB#B!
:: :
7PP?: :^~!!~^: :?PP7
:B@@B: !5B&@@@@&B5! :#@@B:
:!!: ^G@@@&BPPB@@@@G^ :!!:
:B@@@5^ ^5@@@B:
:7J7: !@@@# :&@@@~ :?J7:
J@@@5 :#@@@Y: :Y@@@B: 5@@@J
!@@@&^ ~B@@@&G55G&@@@B~ ~&@@@~
5@@@G: :7P#@@@@@@#P7: :B@@@Y
:P@@@B~ :~!77!~: ~B@@@P
Y@@@&Y^ ^5@@@@J
!G@@@&P7^ ^7P&@@@G~
!P&@@@&B? :: ?B&@@@&P!
^75#&&Y :P&&5: 5&&B57^
:^^ :P&&5: ^^:
^^
[nullpoint]
EOF
)
TANG_SERVERS=(
# "https://tang1.example.com your-thumbprint-1"
# "https://tang2.example.com your-thumbprint-2"
)
TPM_ENABLED=true
TPM_PCR_BANK="sha256"
TPM_PCR_IDS="0,1,2,3,4,5,6,7,8"
ALMA_USER="null"
ENABLE_MOTD=true
# REQUIRED: Set your SSH public key here - installation will fail without it!
SSH_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOkoTn2NreAXMriOUqzyj3YoFW6jMo9B5B+3R5k8yrMi dodox@dodox-ProArt"
########################################################
# Config End
########################################################
set -euo pipefail
echo -e "\n$BANNER"
echo -e "\n[+] Starting post-installation configuration..."
# Check for SSH key
if [ -z "${SSH_KEY:-}" ]; then
echo "ERROR: No SSH key configured!"
echo "You must set SSH_KEY variable at the top of this script."
exit 1
fi
# Check for TPM
echo "[+] Checking for TPM..."
if [ ! -d "/sys/class/tpm/tpm0" ]; then
echo "WARNING: No TPM detected!"
TPM_ENABLED=false
else
echo "TPM detected."
TPM_ENABLED=true
fi
# Install basic packages first
echo "[+] Installing basic packages..."
dnf install -y epel-release || exit 1
dnf config-manager --set-enabled crb || exit 1
dnf install -y zsh git wget curl || exit 1
# Create user and add SSH key
echo "[+] Creating user ${ALMA_USER}..."
useradd -m -G wheel -s /bin/zsh ${ALMA_USER} || exit 1
mkdir -p /home/${ALMA_USER}/.ssh
echo "${SSH_KEY}" > /home/${ALMA_USER}/.ssh/authorized_keys
chmod 700 /home/${ALMA_USER}/.ssh
chmod 600 /home/${ALMA_USER}/.ssh/authorized_keys
chown -R ${ALMA_USER}:${ALMA_USER} /home/${ALMA_USER}/.ssh
# Install oh-my-zsh and powerlevel10k
echo "[+] Installing oh-my-zsh and powerlevel10k..."
# Download and run oh-my-zsh installer as the user with proper environment
su - ${ALMA_USER} -c 'export RUNZSH=no CHSH=no KEEP_ZSHRC=yes && bash -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"' 2>/dev/null || echo "WARNING: oh-my-zsh installation failed"
# Clone powerlevel10k theme
su - ${ALMA_USER} -c 'git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/.oh-my-zsh/custom/themes/powerlevel10k' 2>/dev/null || echo "WARNING: powerlevel10k installation failed"
# Install dotfiles from git repo (cloning needed as we're in chroot)
echo "[+] Installing dotfiles..."
su - ${ALMA_USER} -c '
cd &&
git clone https://git.dominik-roth.eu/dodox/nullpoint.git /tmp/nullpoint-dotfiles &&
cd /tmp/nullpoint-dotfiles/dotfiles &&
for file in .*; do
if [ -f "$file" ] && [ "$file" != "." ] && [ "$file" != ".." ]; then
cp "$file" ~/ 2>/dev/null || true
fi
done &&
cd && rm -rf /tmp/nullpoint-dotfiles
' || echo "WARNING: dotfiles installation failed"
# Set up MOTD
if [ "$ENABLE_MOTD" = true ]; then
echo "[+] Setting up MOTD..."
cat > /etc/motd << MOTD
$BANNER
MOTD
fi
# Install additional packages
echo "[+] Installing additional packages..."
dnf install -y \
clevis clevis-luks tpm2-tools tpm2-tss \
tmux neovim python3-pip \
tree gcc make autoconf automake tar bzip2 || exit 1
# Install dropbear for early boot SSH
echo "[+] Installing dropbear for early boot SSH..."
dnf install -y dropbear dracut-network || exit 1
# Install lsd and bat
echo "[+] Installing lsd and bat..."
# Install using fixed versions that should work
LSD_VERSION="1.0.0"
BAT_VERSION="0.24.0"
# Download and install lsd
if curl -sL "https://github.com/lsd-rs/lsd/releases/download/v${LSD_VERSION}/lsd-v${LSD_VERSION}-x86_64-unknown-linux-musl.tar.gz" | tar xz -C /tmp; then
mv /tmp/lsd-*/lsd /usr/local/bin/ 2>/dev/null || true
chmod +x /usr/local/bin/lsd 2>/dev/null || true
fi
# Download and install bat
if curl -sL "https://github.com/sharkdp/bat/releases/download/v${BAT_VERSION}/bat-v${BAT_VERSION}-x86_64-unknown-linux-musl.tar.gz" | tar xz -C /tmp; then
mv /tmp/bat-*/bat /usr/local/bin/ 2>/dev/null || true
chmod +x /usr/local/bin/bat 2>/dev/null || true
fi
# Create batman script for fancy man pages
cat > /usr/local/bin/batman << 'BATMAN'
#!/bin/bash
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
export MANROFFOPT="-c"
man "$@"
BATMAN
chmod +x /usr/local/bin/batman
# Create .tmp directory for user
mkdir -p /home/${ALMA_USER}/.tmp
chown ${ALMA_USER}:${ALMA_USER} /home/${ALMA_USER}/.tmp
# Configure Clevis for automatic unlock
if [ ${#TANG_SERVERS[@]} -gt 0 ] || [ "$TPM_ENABLED" = true ]; then
echo "[+] Configuring Clevis for automatic unlock..."
# Find LUKS devices
LUKS_DEVICES=$(lsblk -o NAME,FSTYPE -nr | grep crypto_LUKS | cut -d' ' -f1)
for device in $LUKS_DEVICES; do
DEVICE_PATH="/dev/${device}"
echo "Configuring Clevis for ${DEVICE_PATH}..."
if [ "$TPM_ENABLED" = true ] && [ ${#TANG_SERVERS[@]} -eq 0 ]; then
# TPM only
clevis luks bind -d "$DEVICE_PATH" tpm2 "{\"pcr_bank\":\"$TPM_PCR_BANK\",\"pcr_ids\":\"$TPM_PCR_IDS\"}"
elif [ "$TPM_ENABLED" = false ] && [ ${#TANG_SERVERS[@]} -gt 0 ]; then
# Tang only
for server in "${TANG_SERVERS[@]}"; do
read -r url thumbprint <<< "$server"
clevis luks bind -d "$DEVICE_PATH" tang "{\"url\":\"$url\",\"thp\":\"$thumbprint\"}"
done
elif [ "$TPM_ENABLED" = true ] && [ ${#TANG_SERVERS[@]} -gt 0 ]; then
# Both TPM and Tang (require both)
CONFIG="{\"t\":2,\"pins\":{"
CONFIG+="\"tpm2\":{\"pcr_bank\":\"$TPM_PCR_BANK\",\"pcr_ids\":\"$TPM_PCR_IDS\"},"
CONFIG+="\"tang\":{\"t\":1,\"tang\":["
for server in "${TANG_SERVERS[@]}"; do
read -r url thumbprint <<< "$server"
CONFIG+="{\"url\":\"$url\",\"thp\":\"$thumbprint\"},"
done
CONFIG="${CONFIG%,}]}}}"
clevis luks bind -d "$DEVICE_PATH" sss "$CONFIG"
fi
done
fi
# Enable Clevis for early boot (only needed for AlmaLinux < 8.7)
echo "[+] Configuring Clevis for early boot..."
OS_VERSION=$(cat /etc/redhat-release | grep -oE '[0-9]+\.[0-9]+' | head -1)
if [[ "$(echo "$OS_VERSION < 8.7" | bc)" -eq 1 ]]; then
echo " - Enabling clevis-luks-askpass.path for AlmaLinux $OS_VERSION"
systemctl enable clevis-luks-askpass.path || true
else
echo " - AlmaLinux $OS_VERSION: clevis-luks-askpass.path not needed"
fi
# Configure dropbear for remote unlock
echo "[+] Configuring dropbear SSH for remote unlock..."
# Create custom dracut module for dropbear SSH
mkdir -p /usr/lib/dracut/modules.d/60dropbear-ssh
# Create the module setup script
cat > /usr/lib/dracut/modules.d/60dropbear-ssh/module-setup.sh << 'EOF'
#!/bin/bash
check() {
require_binaries dropbear dbclient dropbearkey dropbearconvert || return 1
return 0
}
depends() {
echo network
}
install() {
inst_multiple dropbear dbclient dropbearkey dropbearconvert
# Create directories
inst_dir /etc/dropbear
inst_dir /var/log
inst_dir /root/.ssh
# Copy authorized keys if they exist
if [ -f /etc/dropbear/authorized_keys ]; then
inst /etc/dropbear/authorized_keys /root/.ssh/authorized_keys
fi
# Generate host keys if they don't exist
for keytype in rsa ecdsa ed25519; do
keyfile="/etc/dropbear/dropbear_${keytype}_host_key"
if [ ! -f "$keyfile" ]; then
dropbearkey -t $keytype -f "$keyfile" 2>/dev/null
fi
[ -f "$keyfile" ] && inst "$keyfile"
done
# Install the service
inst_simple "$moddir/dropbear.service" /etc/systemd/system/dropbear.service
systemctl -q --root "$initdir" enable dropbear.service
# Install unlock helper
inst_simple "$moddir/unlock-luks.sh" /usr/bin/unlock-luks
chmod 755 "$initdir/usr/bin/unlock-luks"
}
EOF
# Create systemd service for dropbear
cat > /usr/lib/dracut/modules.d/60dropbear-ssh/dropbear.service << 'EOF'
[Unit]
Description=Dropbear SSH Server
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
ExecStart=/usr/sbin/dropbear -R -E -p 22
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=sysinit.target
EOF
# Create unlock helper script
cat > /usr/lib/dracut/modules.d/60dropbear-ssh/unlock-luks.sh << 'EOF'
#!/bin/bash
echo "=== LUKS Remote Unlock Helper ==="
echo ""
echo "Available block devices:"
lsblk -o NAME,SIZE,TYPE,FSTYPE
echo ""
echo "Encrypted devices waiting for unlock:"
systemd-ask-password --list
echo ""
echo "To unlock, run: systemd-tty-ask-password-agent"
echo ""
exec systemd-tty-ask-password-agent
EOF
chmod +x /usr/lib/dracut/modules.d/60dropbear-ssh/*.sh
# Setup dropbear authorized keys
mkdir -p /etc/dropbear
echo "${SSH_KEY}" > /etc/dropbear/authorized_keys
chmod 600 /etc/dropbear/authorized_keys
# Generate host keys and display SHA256 fingerprints
echo "[+] Generating SSH host keys..."
for keytype in rsa ecdsa ed25519; do
keyfile="/etc/dropbear/dropbear_${keytype}_host_key"
if [ ! -f "$keyfile" ]; then
echo " - Generating $keytype key..."
dropbearkey -t $keytype -f "$keyfile" | grep -v "Generating" || true
# Extract and display SHA256 fingerprint for ed25519
if [ "$keytype" = "ed25519" ] && command -v ssh-keygen >/dev/null 2>&1; then
# Convert dropbear key to OpenSSH format and get SHA256 fingerprint
dropbearkey -y -f "$keyfile" | grep "^ssh-" > "/tmp/dropbear_${keytype}.pub"
fingerprint=$(ssh-keygen -lf "/tmp/dropbear_${keytype}.pub" -E sha256 2>/dev/null | awk '{print $2}')
if [ -n "$fingerprint" ]; then
echo " - ED25519 SHA256 fingerprint: $fingerprint"
fi
rm -f "/tmp/dropbear_${keytype}.pub"
fi
fi
done
# Configure dracut
cat > /etc/dracut.conf.d/60-dropbear-ssh.conf << 'EOF'
# Enable network and dropbear SSH
add_dracutmodules+=" network dropbear-ssh "
# Network configuration
kernel_cmdline="rd.neednet=1"
EOF
# Regenerate initramfs
echo "[+] Regenerating initramfs..."
dracut -f --regenerate-all
# Enable required services
echo "[+] Enabling services..."
# systemctl enable stratisd # Not needed without Stratis
systemctl enable sshd
# Disable root login
echo "[+] Securing SSH..."
sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
# Set SELinux to enforcing
echo "[+] Setting SELinux to enforcing..."
sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
echo "✅ Post-installation complete!"
echo ""
echo "IMPORTANT: The LUKS passphrase is set in install.conf"
echo "Save it securely for recovery purposes."
echo ""
echo "After reboot:"
echo "- SSH to port 22 for remote unlock: ssh root@<server-ip>"
echo "- Run 'unlock-luks' and follow the instructions to unlock LUKS"
echo "- Once unlocked, SSH to port 22 as user '${ALMA_USER}'"
echo "- LUKS passphrase: [see installer output]"
-1
View File
@@ -1 +0,0 @@
pyyaml>=6.0.1