From 92461a8ebf6541e0956f30c61736b1d676f19a06 Mon Sep 17 00:00:00 2001 From: Dominik Roth Date: Sun, 18 May 2025 18:31:20 +0200 Subject: [PATCH] rewrote everything --- .gitignore | 9 +- README.md | 98 +++-- build-config.yaml.example | 43 -- build.py | 547 ------------------------ deploy-config.yaml.example | 13 - deploy.py | 169 -------- install.sh | 838 +++++++++++++++++++++++++++++++++++++ requirements.txt | 1 - 8 files changed, 902 insertions(+), 816 deletions(-) delete mode 100644 build-config.yaml.example delete mode 100644 build.py delete mode 100644 deploy-config.yaml.example delete mode 100644 deploy.py create mode 100644 install.sh delete mode 100644 requirements.txt 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