diff --git a/.gitignore b/.gitignore
index 43bb5f2..aba70c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,12 @@
# Config files
-build-config.yaml
-deploy-config.yaml
-config.ign
+config.yaml
# Python
__pycache__/
*.py[cod]
*$py.class
-
-# Downloaded images
-fedora-coreos-*-hetzner.x86_64.raw.xz
+# Images
+*.iso
# Environment
.env
\ No newline at end of file
diff --git a/README.md b/README.md
index 24afe94..81938b6 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,12 @@
+# nullpoint
+
nullpoint
-Secure Fedora Server setup with LUKS encryption, TPM, and BTRFS RAID1 with focus on Hetzner Infra.
+Secure Fedora Server setup with LUKS encryption, TPM, and BTRFS RAID1 for Hetzner Dedicated Servers.
## Features
@@ -14,7 +16,8 @@ Secure Fedora Server setup with LUKS encryption, TPM, and BTRFS RAID1 with focus
- TPM-based boot verification
- BTRFS RAID1 for data redundancy
- Dedicated database subvolume with `nodatacow` and `noatime`
-- Automated deployment to Hetzner
+- Enhanced shell environment with zsh, Oh My Zsh, Powerlevel10k, and an amazing custom theme
+- SSH key-only access with early boot SSH via dropbear
If you need a dead man's switch to go along with it check out [raven](https://git.dominik-roth.eu/dodox/raven).
@@ -34,55 +37,76 @@ The system uses multiple methods to unlock the LUKS volumes:
### TPM Updates
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`
+1. Use the provided script: `sudo /root/update-tpm-bindings.py`
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
-
-```bash
-# Install tools
-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
-1. **Configure Build Settings**
+1. **Configure Installer**
```bash
- cp build-config.yaml.example build-config.yaml
- vim build-config.yaml # Edit LUKS, storage, and image settings
+ # Edit the variables at the top of install.sh:
+ vim install.sh
```
+ Set your:
+ - Tang server URLs and thumbprints
+ - TPM PCR settings
+ - Fedora version
+ - SSH public key for the default user
-2. **Build Base Image** (one-time setup)
- ```bash
- python3 build.py # Creates and uploads Fedora Server image to Hetzner
- ```
+2. **Install on Hetzner Server**
+ - Log into Hetzner Robot
+ - Select your server
+ - Go to "Rescue" tab
+ - Choose "Linux" and "64 bit"
+ - Activate Rescue System
+ - Upload the installer:
+ ```bash
+ scp install.sh root@your-server:/root/
+ ```
+ - SSH into Rescue System:
+ ```bash
+ ssh root@your-server
+ ```
+ - Make it executable and run:
+ ```bash
+ chmod +x install.sh
+ ./install.sh
+ ```
+ - Wait for installation to complete
+ - Reboot the server
-3. **Configure Deployment Settings**
+3. **Verify Installation**
```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
+ ssh root@your-server
systemctl status clevis-luks-askpass
lsblk
btrfs filesystem show # Check RAID1 status
clevis-luks-list -d /dev/sda2
- ```
\ No newline at end of file
+ ```
+
+## Recovery
+
+If you need to recover the system:
+
+1. **Using Rescue System**
+ - Boot into Rescue System
+ - Mount the encrypted volumes:
+ ```bash
+ cryptsetup luksOpen /dev/sda2 root_a
+ cryptsetup luksOpen /dev/sdb2 root_b
+ mount /dev/mapper/root_a /mnt
+ ```
+ - Access your data at `/mnt`
+
+2. **Using Tang Server**
+ - Ensure your Tang server is accessible
+ - The system should automatically unlock if TPM measurements match
+
+3. **Using Manual Passphrase**
+ - Connect via SSH during early boot (dropbear)
+ - Enter the LUKS passphrase when prompted
+ - The passphrase is stored in `/root/luks-passphrase.txt` on the installed system
\ No newline at end of file
diff --git a/build-config.yaml.example b/build-config.yaml.example
deleted file mode 100644
index fe443d6..0000000
--- a/build-config.yaml.example
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/build.py b/build.py
deleted file mode 100644
index b179ff7..0000000
--- a/build.py
+++ /dev/null
@@ -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()
\ No newline at end of file
diff --git a/deploy-config.yaml.example b/deploy-config.yaml.example
deleted file mode 100644
index 3960bfc..0000000
--- a/deploy-config.yaml.example
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/deploy.py b/deploy.py
deleted file mode 100644
index 2004daa..0000000
--- a/deploy.py
+++ /dev/null
@@ -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@")
-
-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()
\ No newline at end of file
diff --git a/install.sh b/install.sh
new file mode 100644
index 0000000..85725e9
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,838 @@
+#!/bin/bash
+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_PCR_BANK="sha256"
+TPM_PCR_IDS="0,1,2,3,4,5,6,7,8"
+FEDORA_VERSION="42"
+FEDORA_USER="null"
+ENABLE_MOTD=true
+SSH_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOkoTn2NreAXMriOUqzyj3YoFW6jMo9B5B+3R5k8yrMi dodox@dodox-ProArt"
+
+########################################################
+# Config End
+########################################################
+
+set -euo pipefail
+echo -e "\nStarting installation...\n"
+
+# Check for TPM
+echo "Checking for TPM..."
+if [ ! -d "/sys/class/tpm/tpm0" ]; then
+ echo "WARNING: No TPM detected!"
+ echo "This system will not be able to use TPM-based boot verification."
+ read -p "Continue without TPM? [y/N] " -n 1 -r
+ echo
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ echo "Installation aborted."
+ exit 1
+ fi
+ echo "Proceeding without TPM..."
+ TPM_ENABLED=false
+else
+ echo "TPM detected."
+ TPM_ENABLED=true
+fi
+
+# Check for SSH key
+if [ -z "${SSH_KEY:-}" ]; then
+ echo "No SSH key provided. Please enter your public SSH key:"
+ read -r SSH_KEY
+ if [ -z "$SSH_KEY" ]; then
+ echo "Error: SSH key is required for installation"
+ exit 1
+ fi
+fi
+
+# Generate secure LUKS passphrase
+echo "Generating secure LUKS passphrase..."
+LUKS_PASSPHRASE=$(openssl rand -base64 32)
+USER_PASSWORD=$(openssl rand -base64 12) # Shorter for easier typing
+echo "Generated LUKS passphrase: ${LUKS_PASSPHRASE}"
+echo "Generated user password: ${USER_PASSWORD}"
+echo "Please save these credentials securely. You will need them for recovery."
+echo "Press Enter to continue..."
+read
+
+# Install required packages in Rescue System (Debian)
+echo "Installing required packages..."
+apt-get update
+apt-get install -y cryptsetup btrfs-progs mdadm
+
+# Wipe disks
+echo "Wiping disks..."
+dd if=/dev/zero of=/dev/sda bs=1M count=100
+dd if=/dev/zero of=/dev/sdb bs=1M count=100
+sync
+
+# Create partitions
+echo "Creating partitions..."
+# First disk
+parted /dev/sda mklabel gpt
+parted /dev/sda mkpart primary 1MiB 1000MiB
+parted /dev/sda mkpart primary 1000MiB 100%
+
+# Second disk
+parted /dev/sdb mklabel gpt
+parted /dev/sdb mkpart primary 1MiB 1000MiB
+parted /dev/sdb mkpart primary 1000MiB 100%
+
+# Create boot RAID1
+echo "Creating boot RAID1 array..."
+mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sda1 /dev/sdb1
+mkfs.ext4 /dev/md0
+
+# Create LUKS volumes
+echo "Setting up LUKS encryption..."
+echo "${LUKS_PASSPHRASE}" | cryptsetup luksFormat /dev/sda2 --type luks2
+echo "${LUKS_PASSPHRASE}" | cryptsetup luksFormat /dev/sdb2 --type luks2
+
+# Open LUKS volumes
+echo "Opening LUKS volumes..."
+echo "${LUKS_PASSPHRASE}" | cryptsetup luksOpen /dev/sda2 root_a
+echo "${LUKS_PASSPHRASE}" | cryptsetup luksOpen /dev/sdb2 root_b
+
+# Create BTRFS RAID1
+echo "Creating BTRFS RAID1 filesystem..."
+mkfs.btrfs -f -d raid1 -m raid1 /dev/mapper/root_a /dev/mapper/root_b
+
+# Create subvolumes
+echo "Creating BTRFS subvolumes..."
+mount /dev/mapper/root_a /mnt
+btrfs subvolume create /mnt/@root
+btrfs subvolume create /mnt/@home
+btrfs subvolume create /mnt/@db
+chattr +C /mnt/@db
+
+# Download Fedora installer
+echo "Downloading Fedora installer..."
+wget "https://download.fedoraproject.org/pub/fedora/linux/releases/${FEDORA_VERSION}/Server/x86_64/iso/Fedora-Server-dvd-x86_64-${FEDORA_VERSION}-1.5.iso"
+
+# Mount Fedora ISO
+echo "Mounting Fedora installer..."
+mkdir -p /mnt/iso
+mount -o loop "Fedora-Server-dvd-x86_64-${FEDORA_VERSION}-1.5.iso" /mnt/iso
+
+# Create kickstart file
+echo "Creating kickstart configuration..."
+cat > /mnt/ks.cfg << EOF
+# Fedora Server installation with our secure setup
+text
+lang en_US.UTF-8
+keyboard us
+timezone --utc Etc/UTC
+
+# Security settings
+selinux --enforcing
+rootpw --lock
+user --name=${FEDORA_USER} --groups=wheel --shell=/bin/bash --password="${USER_PASSWORD}" --lock
+
+# SSH setup
+sshkey --username=${FEDORA_USER} "${SSH_KEY}"
+
+# Network
+network --bootproto=dhcp --device=link --activate
+
+# Bootloader
+bootloader --timeout=1 --location=mbr --append="no_timer_check console=tty1 console=ttyS0,115200n8"
+
+# Services
+services --enabled=sshd,clevis-luks-askpass,dropbear
+
+# Partitioning
+clearpart --all --initlabel --disklabel=gpt
+
+# Boot RAID1
+part /boot --size=1000 --fstype=ext4 --label=boot
+raid /boot --level=1 --device=md0 --fstype=ext4 --label=boot /dev/sda1 /dev/sdb1
+
+# LUKS + BTRFS RAID1
+part /dev/sda2 --size=2000 --grow --fstype=btrfs --label=root_a
+part /dev/sdb2 --size=2000 --grow --fstype=btrfs --label=root_b
+
+# Packages
+%packages
+@^server-product-environment
+clevis
+clevis-luks
+clevis-tang
+clevis-tpm2
+tpm2-tools
+tpm2-tss
+cryptsetup
+btrfs-progs
+mdadm
+dropbear
+%end
+
+# Post-installation
+%post
+# Create BTRFS subvolumes
+btrfs subvolume create /@root
+btrfs subvolume create /@home
+btrfs subvolume create /@db
+chattr +C /@db
+
+# Update fstab
+cat > /etc/fstab << EOF
+/dev/md0 /boot ext4 defaults 1 2
+/dev/mapper/root_a / btrfs subvol=@root,defaults,noatime 0 0
+/dev/mapper/root_a /home btrfs subvol=@home,defaults,noatime 0 0
+/dev/mapper/root_a /db btrfs subvol=@db,defaults,noatime,nodatacow 0 0
+EOF
+
+# Save LUKS passphrase
+echo "${LUKS_PASSPHRASE}" > /root/luks-passphrase.txt
+chmod 600 /root/luks-passphrase.txt
+
+# Configure sudoers
+echo "%wheel ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel
+
+# Configure SSH
+cat > /etc/ssh/sshd_config.d/99-custom.conf << EOF
+PasswordAuthentication no
+PermitRootLogin no
+PubkeyAuthentication yes
+EOF
+
+# Configure dropbear for early SSH access
+cat > /etc/dropbear-initramfs/config << EOF
+DROPBEAR_OPTIONS="-I 60 -j -k -p 2222 -s"
+EOF
+
+# Add SSH key to dropbear for early boot access
+mkdir -p /etc/dropbear-initramfs/authorized_keys
+echo "${SSH_KEY}" > /etc/dropbear-initramfs/authorized_keys
+chmod 600 /etc/dropbear-initramfs/authorized_keys
+
+# Set up MOTD
+if [ "$ENABLE_MOTD" = true ]; then
+ cat > /etc/motd << EOF
+ ${BANNER}
+ EOF
+fi
+
+# Configure Clevis
+if [ ${#TANG_SERVERS[@]} -gt 0 ] || [ "$TPM_ENABLED" = true ]; then
+ mkdir -p /etc/clevis
+
+ # Build Tang servers JSON array if we have any
+ if [ ${#TANG_SERVERS[@]} -gt 0 ]; then
+ TANG_JSON="["
+ for server in "${TANG_SERVERS[@]}"; do
+ read -r url thumbprint <<< "$server"
+ TANG_JSON+="{\"url\":\"$url\",\"thumbprint\":\"$thumbprint\"},"
+ done
+ TANG_JSON="${TANG_JSON%,}]" # Remove trailing comma and close array
+ fi
+
+ # Create Clevis config
+ cat > /etc/clevis/clevis.conf << EOF
+{
+ "t": 2,
+ "pins": {
+ $([ "$TPM_ENABLED" = true ] && echo "\"tpm2\": {
+ \"pcr_bank\": \"${TPM_PCR_BANK}\",
+ \"pcr_ids\": \"${TPM_PCR_IDS}\"
+ },")
+ $([ ${#TANG_SERVERS[@]} -gt 0 ] && echo "\"tang\": {
+ \"t\": 1,
+ \"tang\": ${TANG_JSON}
+ }")
+ }
+}
+EOF
+
+ # Bind LUKS volumes
+ clevis luks bind -d /dev/sda2 sss -c /etc/clevis/clevis.conf
+ clevis luks bind -d /dev/sdb2 sss -c /etc/clevis/clevis.conf
+else
+ echo "No Tang servers or TPM available, skipping Clevis setup"
+fi
+
+# Enable required services
+systemctl enable clevis-luks-askpass
+systemctl enable dropbear-initramfs
+
+# Create TPM update script
+cat > /root/update-tpm-bindings.py << 'TPMSCRIPT'
+#!/usr/bin/env python3
+import subprocess
+import sys
+import os
+
+def run_command(cmd):
+ try:
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
+ return result.stdout.strip()
+ except subprocess.CalledProcessError as e:
+ print(f"Error running command: {' '.join(cmd)}")
+ print(f"Error: {e.stderr}")
+ sys.exit(1)
+
+def get_pcr_values():
+ print("Current PCR values:")
+ for i in range(0, 9):
+ pcr = run_command(['tpm2_pcrread', f'sha256:{i}'])
+ print(f"PCR {i}: {pcr.split('=')[1].strip()}")
+
+def update_bindings():
+ print("\nUpdating TPM bindings...")
+ devices = ['/dev/sda2', '/dev/sdb2']
+
+ for device in devices:
+ print(f"\nUpdating bindings for {device}")
+ try:
+ # Remove existing bindings
+ run_command(['clevis', 'luks', 'unbind', '-d', device, '-s', '1'])
+ run_command(['clevis', 'luks', 'unbind', '-d', device, '-s', '2'])
+
+ # Create new bindings
+ run_command(['clevis', 'luks', 'bind', '-d', device, 'sss', '-c', '/etc/clevis/clevis.conf'])
+
+ print(f"Successfully updated bindings for {device}")
+ except subprocess.CalledProcessError as e:
+ print(f"Error updating bindings for {device}: {e.stderr}")
+ continue
+
+def verify_bindings():
+ print("\nVerifying bindings...")
+ devices = ['/dev/sda2', '/dev/sdb2']
+
+ for device in devices:
+ print(f"\nBindings for {device}:")
+ try:
+ bindings = run_command(['clevis', 'luks', 'list', '-d', device])
+ print(bindings)
+ except subprocess.CalledProcessError as e:
+ print(f"Error verifying bindings for {device}: {e.stderr}")
+ continue
+
+def main():
+ if os.geteuid() != 0:
+ print("This script must be run as root")
+ sys.exit(1)
+
+ print("TPM Binding Update Script")
+ print("========================")
+
+ get_pcr_values()
+ update_bindings()
+ verify_bindings()
+
+ print("\nUpdate complete. Please reboot to test the new bindings.")
+
+if __name__ == "__main__":
+ main()
+TPMSCRIPT
+
+chmod +x /root/update-tpm-bindings.py
+
+# Install additional packages
+dnf install -y git zsh lsd bat tmux neovim fortune-mod cowsay lolcat xclip python3-pip --skip-unavailable
+
+# Set zsh as default shell for root and default user
+chsh -s /bin/zsh root
+chsh -s /bin/zsh ${FEDORA_USER}
+
+# Install Oh My Zsh
+su - ${FEDORA_USER} -c 'sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended'
+
+# Install Powerlevel10k
+su - ${FEDORA_USER} -c 'git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k"'
+
+# Create .zshrc for default user
+cat > /home/${FEDORA_USER}/.zshrc << 'ZSHRC'
+USE_OH_MY_ZSH=true
+
+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
+
+# Path to your oh-my-zsh installation.
+if [[ $USE_OH_MY_ZSH == "true" ]]; then
+ export ZSH="$HOME/.oh-my-zsh"
+fi
+
+ZSH_THEME="powerlevel10k/powerlevel10k"
+HIST_STAMPS="dd.mm.yyyy"
+
+if [[ $USE_OH_MY_ZSH == "true" ]]; then
+ plugins=(git)
+ source $ZSH/oh-my-zsh.sh
+fi
+
+if [[ -n $SSH_CONNECTION ]]; then
+ export EDITOR='vim'
+else
+ export EDITOR='nvim'
+fi
+
+if [ $TILIX_ID ] || [ $VTE_VERSION ]; then
+ source /etc/profile.d/vte.sh
+fi
+
+# 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"
+notify() {
+ $@ && echo "[DONE] $@" || echo "[ERROR] $@"
+}
+
+# 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"
+alias upf="sudo flatpak update"
+alias upp="pip install --upgrade pip setuptools wheel && pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U"
+
+#clipboard
+alias setclip="xclip -selection c"
+alias getclip="xclip -selection c -o"
+alias pwdclip="pwd | xargs echo -n | xclip -selection c"
+
+# chars
+alias c="clear"
+alias t="bpytop"
+alias p="ptpy"
+alias h="history | grep"
+alias v="nvim"
+alias n="nano"
+alias f="nautilus ."
+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'
+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'
+alias gpa='gaa && gc -m "." && gp -4'
+
+# 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"
+
+# Powerlevel10k initialization
+[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
+ZSHRC
+
+# Create .p10k.zsh for default user
+cat > /home/${FEDORA_USER}/.p10k.zsh << 'P10K'
+# Temporarily change options.
+'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 all configuration options. This allows you to apply configiguration changes without
+ # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`.
+ 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
+
+ # The list of segments shown on the left. Fill it with the most important segments.
+ typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
+ context
+ dir # current directory
+ vcs # git status
+ )
+
+ typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
+ background_jobs # presence of background jobs
+ direnv # direnv status (https://direnv.net/)
+ virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html)
+ kubecontext # current kubernetes context (https://kubernetes.io/)
+ vim_shell # vim shell indicator (:sh)
+ vi_mode # vi mode (you don't need this if you've enabled prompt_char)
+ disk_usage # 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=
+
+ #################################[ os_icon: os identifier ]##################################
+ typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND=232
+ typeset -g POWERLEVEL9K_OS_ICON_BACKGROUND=7
+ typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='%B${P9K_CONTENT// }'
+
+ ################################[ prompt_char: prompt symbol ]################################
+ 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=
+
+ ##################################[ dir: current directory ]##################################
+ 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=()
+
+ #####################################[ vcs: git status ]######################################
+ # Version control system colors.
+ typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=10
+ typeset -g POWERLEVEL9K_VCS_CLEAN_BACKGROUND=235 #2
+ typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=10
+ typeset -g POWERLEVEL9K_VCS_MODIFIED_BACKGROUND=235 #3
+ typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=10
+ typeset -g POWERLEVEL9K_VCS_UNTRACKED_BACKGROUND=235 #2
+ typeset -g POWERLEVEL9K_VCS_CONFLICTED_FOREGROUND=9
+ typeset -g POWERLEVEL9K_VCS_CONFLICTED_BACKGROUND=235 #3
+ typeset -g POWERLEVEL9K_VCS_LOADING_FOREGROUND=15
+ typeset -g POWERLEVEL9K_VCS_LOADING_BACKGROUND=255
+
+ # Branch icon. Set this parameter to '\uF126 ' for the popular Powerline branch icon. Disabled because bug
+ typeset -g POWERLEVEL9K_VCS_BRANCH_ICON='\uF126 '
+ POWERLEVEL9K_VCS_BRANCH_ICON=${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}
+
+ # Untracked files icon. It's really a question mark, your font isn't broken.
+ # Change the value of this parameter to show a different icon.
+ typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?'
+ POWERLEVEL9K_VCS_UNTRACKED_ICON=${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}
+
+ # Formatter for Git status.
+ #
+ # Example output: master ⇣42⇡42 *42 merge ~42 +42 !42 ?42.
+ #
+ # You can edit the function to customize how Git status looks.
+ #
+ # VCS_STATUS_* parameters are set by gitstatus plugin. See reference:
+ # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh.
+ function my_git_formatter() {
+ emulate -L zsh
+
+ if [[ -n $P9K_CONTENT ]]; then
+ # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from
+ # gitstatus plugin). VCS_STATUS_* parameters are not available in this case.
+ typeset -g my_git_format=$P9K_CONTENT
+ return
+ fi
+
+ # Styling for different parts of Git status.
+ local meta='' #'%7F' # white foreground
+ local clean='' #'%0F' # black foreground
+ local modified='' #'%0F' # black foreground
+ local untracked='' #'%0F' # black foreground
+ local conflicted='' #'%1F' # red foreground
+
+ local res
+ local where # branch or tag
+ 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
+
+ # If local branch name or tag is at most 32 characters long, show it in full.
+ # Otherwise show the first 12 … the last 12.
+ (( $#where > 32 )) && where[13,-13]="…"
+ res+="${clean}${where//\%/%%}" # escape %
+
+ # Display the current Git commit if there is no branch or tag.
+ # Tip: To always display current Git commit, remove `[[ -z $where ]] &&` from the next line.
+ [[ -z $where ]] && res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}"
+
+ # Show tracking branch name if it differs from local branch.
+ if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then
+ res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" # escape %
+ fi
+
+ # ⇣42 if behind the remote.
+ (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}"
+ # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42.
+ (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" "
+ (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}"
+ # *42 if have stashes.
+ (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}"
+ # 'merge' if the repo is in an unusual state.
+ [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}"
+ # ~42 if have merge conflicts.
+ (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}"
+ # +42 if have staged changes.
+ (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}"
+ # !42 if have unstaged changes.
+ (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}"
+ # ?42 if have untracked files. It's really a question mark, your font isn't broken.
+ # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon.
+ # Remove the next line if you don't want to see untracked files at all.
+ (( 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
+
+ # Disable the default Git status formatting.
+ typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true
+ # Install our own Git status formatter.
+ typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter()))+${my_git_format}}'
+ # Enable counters for staged, unstaged, etc.
+ typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1
+
+ # Custom icon.
+ # typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION='⭐'
+ # Custom prefix.
+ # typeset -g POWERLEVEL9K_VCS_PREFIX='on '
+
+ # Show status of repositories of these types. You can add svn and/or hg if you are
+ # using them. If you do, your prompt may become slow even when your current directory
+ # isn't in an svn or hg reposotiry.
+ typeset -g POWERLEVEL9K_VCS_BACKENDS=(git)
+
+ ##################################[ disk_usgae: disk usage ]##################################
+ typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=95
+ typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=98
+ typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=true
+
+ ###########[ vi_mode: vi mode (you don't need this if you've enabled prompt_char) ]###########
+ 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
+
+ ######################################[ ram: free RAM ]#######################################
+ # RAM color.
+ # typeset -g POWERLEVEL9K_RAM_FOREGROUND=0
+ # typeset -g POWERLEVEL9K_RAM_BACKGROUND=3
+ # Custom icon.
+ # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐'
+
+ #####################################[ swap: used swap ]######################################
+ # Swap color.
+ # typeset -g POWERLEVEL9K_SWAP_FOREGROUND=0
+ # typeset -g POWERLEVEL9K_SWAP_BACKGROUND=3
+ # Custom icon.
+ # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐'
+
+ ######################################[ load: CPU load ]######################################
+ typeset -g POWERLEVEL9K_LOAD_WHICH=5
+
+ ##################################[ context: user@hostname ]##################################
+ 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'
+
+ ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]###
+ typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
+ typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
+
+ #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]#############
+ typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc'
+ typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=(
+ # '*prod*' PROD # These values are examples that are unlikely
+ # '*test*' TEST # to match your needs. Customize them as needed.
+ '*' 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}'
+
+ ####################################[ time: current time ]####################################
+ typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
+ typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
+
+ 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'
+P10K
+
+# Set correct permissions
+chown -R ${FEDORA_USER}:${FEDORA_USER} /home/${FEDORA_USER}/.oh-my-zsh
+chown ${FEDORA_USER}:${FEDORA_USER} /home/${FEDORA_USER}/.zshrc
+chown ${FEDORA_USER}:${FEDORA_USER} /home/${FEDORA_USER}/.p10k.zsh
+
+# Install Python packages
+pip3 install --user ptpython bpytop
+
+%end
+
+reboot
+EOF
+
+# Start Fedora installation
+echo "Starting Fedora installation..."
+echo "This will take some time. Please wait..."
+/mnt/iso/isolinux/vmlinuz initrd=/mnt/iso/isolinux/initrd.img inst.ks=file:///mnt/ks.cfg inst.stage2=hd:LABEL=Fedora-S-42-1-5-x86_64
+echo -e "\nInstallation started! Please wait for completion and reboot."
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 1321e71..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-pyyaml>=6.0.1
\ No newline at end of file