diff --git a/hosts/amzebs-01/EMAIL_SETUP.md b/hosts/amzebs-01/EMAIL_SETUP.md deleted file mode 100644 index 72aadfb..0000000 --- a/hosts/amzebs-01/EMAIL_SETUP.md +++ /dev/null @@ -1,471 +0,0 @@ -# Email Setup for amzebs-01 (amz.at) - -This host is configured to send emails via Laravel with DKIM signing. - -## Configuration Overview - -- **Postfix**: Localhost-only SMTP server (no external access) -- **Rspamd**: DKIM signing with host-specific key -- **Domain**: amz.at -- **DKIM Selector**: amzebs-01 -- **Secret Management**: DKIM private key stored in sops - -## Initial Setup (Before First Deployment) - -### 1. Generate DKIM Key Pair - -You need to generate a DKIM key pair locally first. You'll need `rspamd` package installed. - -#### Option A: Using rspamd (if installed locally) - -```bash -# Create a temporary directory -mkdir -p /tmp/dkim-gen - -# Generate the key pair -rspamadm dkim_keygen -s amzebs-01 -d amz.at -k /tmp/dkim-gen/amz.at.amzebs-01.key -``` - -This will output: -- **Private key** saved to `/tmp/dkim-gen/amz.at.amzebs-01.key` -- **Public key** printed to stdout (starts with `v=DKIM1; k=rsa; p=...`) - -#### Option B: Using OpenSSL (alternative) - -```bash -# Create temporary directory -mkdir -p /tmp/dkim-gen - -# Generate private key (2048-bit RSA) -openssl genrsa -out /tmp/dkim-gen/amz.at.amzebs-01.key 2048 - -# Extract public key in the correct format for DNS -openssl rsa -in /tmp/dkim-gen/amz.at.amzebs-01.key -pubout -outform PEM | \ - grep -v '^-----' | tr -d '\n' > /tmp/dkim-gen/public.txt - -# Display the DNS record value -echo "v=DKIM1; k=rsa; p=$(cat /tmp/dkim-gen/public.txt)" -``` - -**Save the public key output!** You'll need it for DNS configuration later. - -### 2. Add DKIM Private Key to Sops Secrets - -Now you need to encrypt and add the private key to your secrets file. - -#### Step 1: View the private key - -```bash -cat /tmp/dkim-gen/amz.at.amzebs-01.key -``` - -#### Step 2: Edit the secrets file - -```bash -cd /home/dominik/projects/cloonar/cloonar-nixos/hosts/amzebs-01 -sops secrets.yaml -``` - -#### Step 3: Add the key to secrets.yaml - -In the sops editor, add a new key called `rspamd-dkim-key` with the **entire private key content** including the BEGIN/END markers: - -```yaml -rspamd-dkim-key: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC... - (paste the entire key content here) - ... - -----END PRIVATE KEY----- -``` - -**Important:** -- Make sure to use the pipe `|` character for multiline content -- Keep the proper indentation (2 spaces before each line of the key) -- Include the full BEGIN/END markers - -#### Step 4: Save and exit - -Save the file in sops (it will be encrypted automatically). - -#### Step 5: Clean up temporary files - -```bash -rm -rf /tmp/dkim-gen -``` - -### 3. Verify Secret is Encrypted - -Check that the secret is properly encrypted: - -```bash -cat hosts/amzebs-01/secrets.yaml -``` - -You should see encrypted content, not the plain private key. - -### 4. Extract Public Key for DNS (if needed later) - -If you didn't save the public key earlier, you can extract it after deployment: - -```bash -# On the server after deployment -sudo cat /var/lib/rspamd/dkim/amz.at.amzebs-01.key | \ - openssl rsa -pubout -outform PEM 2>/dev/null | \ - grep -v '^-----' | tr -d '\n' -``` - -Then format it as: -``` -v=DKIM1; k=rsa; p= -``` - -## Deployment - -### 1. Deploy Configuration - -After adding the DKIM private key to sops, deploy the configuration: - -```bash -# Build and switch on the remote host -nixos-rebuild switch --flake .#amzebs-01 --target-host amzebs-01 --use-remote-sudo -``` - -Or if deploying locally on the server: - -```bash -sudo nixos-rebuild switch -``` - -### 2. Verify Deployment - -Check that the services are running: - -```bash -# Check rspamd-dkim-setup service -systemctl status rspamd-dkim-setup - -# Check that rspamd is running -systemctl status rspamd - -# Check that postfix is running -systemctl status postfix - -# Verify DKIM key was deployed -ls -la /var/lib/rspamd/dkim/amz.at.amzebs-01.key -``` - -## DNS Configuration - -Add the following DNS records to ensure proper email delivery and avoid spam classification. - -### Critical: PTR Record (Reverse DNS) - -**This is CRITICAL for email deliverability!** Without a proper PTR record, most mail servers will reject or spam your emails. - -#### What is a PTR Record? -A PTR (pointer) record is a reverse DNS entry that maps your IP address back to your hostname. Mail servers use this to verify you're a legitimate mail server. - -#### Required PTR Record -``` -IP Address: 23.88.38.1 -Points to: amzebs-01.amz.at -``` - -#### How to Configure PTR Record - -**Step 1: Contact Your Hosting Provider** - -PTR records MUST be configured through your hosting provider (e.g., Hetzner, OVH, AWS, etc.). You cannot set PTR records through your domain registrar. - -1. Log into your hosting provider's control panel -2. Find the "Reverse DNS" or "PTR Record" section -3. Set the PTR record for IP `23.88.38.1` to point to `amzebs-01.amz.at` - -**Common Provider Links:** -- **Hetzner**: Robot panel → IPs → Edit reverse DNS -- **OVH**: Network → IP → ... → Modify reverse -- **AWS EC2**: Select instance → Networking → Request reverse DNS - -**Step 2: Verify Forward DNS First** - -Before setting the PTR record, ensure your forward DNS is correct: - -```bash -# This should return 23.88.38.1 -dig +short amzebs-01.amz.at A -host amzebs-01.amz.at -``` - -**Step 3: Verify PTR Record** - -After configuring, verify the PTR record is working: - -```bash -# Method 1: Using dig -dig +short -x 23.88.38.1 - -# Method 2: Using host -host 23.88.38.1 - -# Method 3: Using nslookup -nslookup 23.88.38.1 -``` - -All commands should return: `amzebs-01.amz.at` - -**Step 4: Verify FCrDNS (Forward-Confirmed Reverse DNS)** - -This ensures forward and reverse DNS match properly: - -```bash -# Forward lookup -dig +short amzebs-01.amz.at -# Should output: 23.88.38.1 - -# Reverse lookup -dig +short -x 23.88.38.1 -# Should output: amzebs-01.amz.at. -``` - -If both work correctly, FCrDNS passes! ✓ - -**Why PTR Records Matter:** -- Gmail, Microsoft, Yahoo require valid PTR records -- Missing PTR = automatic spam classification or rejection -- Can add 5-10 points to spam score alone -- Required for professional email delivery - -### Domain DNS Records (amz.at) - -Add these records through your domain registrar's DNS management: - -#### SPF Record -``` -Type: TXT -Name: @ -Value: v=spf1 mx a:amzebs-01.amz.at ~all -``` - -#### DKIM Record -``` -Type: TXT -Name: amzebs-01._domainkey -Value: [Your public key from step 1 above] -``` - -The DKIM record will look something like: -``` -v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -``` - -#### DMARC Record -``` -Type: TXT -Name: _dmarc -Value: v=DMARC1; p=quarantine; rua=mailto:postmaster@amz.at; ruf=mailto:postmaster@amz.at; fo=1 -``` - -**Explanation:** -- `p=quarantine`: Failed messages should be quarantined (you can change to `p=reject` after testing) -- `rua=mailto:...`: Aggregate reports sent to this address -- `ruf=mailto:...`: Forensic reports sent to this address -- `fo=1`: Generate forensic reports for any failure - -## Laravel Configuration - -Update your Laravel application's `.env` file: - -#### Option A: Using sendmail (Recommended) -```env -MAIL_MAILER=sendmail -MAIL_FROM_ADDRESS=noreply@amz.at -MAIL_FROM_NAME="${APP_NAME}" -``` - -#### Option B: Using SMTP -```env -MAIL_MAILER=smtp -MAIL_HOST=127.0.0.1 -MAIL_PORT=25 -MAIL_USERNAME=null -MAIL_PASSWORD=null -MAIL_ENCRYPTION=null -MAIL_FROM_ADDRESS=noreply@amz.at -MAIL_FROM_NAME="${APP_NAME}" -``` - -**Note**: Laravel can use ANY email address with @amz.at domain. All will be DKIM signed automatically. - -## Testing Email - -### Test from Command Line - -```bash -# Send a test email -echo "Test email body" | mail -s "Test Subject" test@example.com -aFrom:test@amz.at -``` - -### Check Postfix Queue - -```bash -# View mail queue -mailq - -# View logs -journalctl -u postfix -f -``` - -### Check Rspamd Logs - -```bash -# View rspamd logs -journalctl -u rspamd -f -``` - -### Test DKIM Signature and Deliverability - -Send an email to test your complete email configuration: - -#### Email Testing Services -1. **Mail Tester** (https://www.mail-tester.com/) - - Provides a temporary email address - - Shows comprehensive spam score (0-10, higher is better) - - Checks DKIM, SPF, DMARC, PTR, blacklists, content - - **Target: 9/10 or higher** - -2. **MXToolbox Email Health** (https://mxtoolbox.com/emailhealth/) - - Comprehensive deliverability check - - Checks DNS records, blacklists, configuration - -3. **Google Admin Toolbox** (https://toolbox.googleapps.com/apps/messageheader/) - - Paste email headers to see how Gmail scored your email - - Shows SPF, DKIM, DMARC results - -#### What to Check -- ✓ DKIM signature is valid -- ✓ SPF passes -- ✓ DMARC passes -- ✓ PTR record (reverse DNS) matches -- ✓ Not on any blacklists -- ✓ Spam score < 2.0 (lower is better) - -#### Common Issues & Fixes - -**High Spam Score (> 5.0)** -- Check: PTR record configured correctly? (Critical!) -- Check: HELO name matches hostname? -- Check: All headers present (To:, From:, Subject:)? -- Check: IP not blacklisted? - -**Missing "To:" Header** -Your Laravel app must set a recipient. In your code: -```php -Mail::to('recipient@example.com') - ->send(new YourMailable()); -``` - -**HELO/EHLO Mismatch** -After applying this configuration, HELO should be `amzebs-01.amz.at`, not `localhost` - -**Check Current HELO Name** -```bash -# On the server -echo "HELO test" | nc localhost 25 -# Should see: 250 amzebs-01.amz.at -``` - -## Verification Commands - -```bash -# Check if Postfix is running -systemctl status postfix - -# Check if Rspamd is running -systemctl status rspamd - -# Check if Postfix is listening on localhost only -ss -tlnp | grep master - -# View DKIM public key again -systemctl start rspamd-show-dkim -journalctl -u rspamd-show-dkim - -# Check if DKIM key exists -ls -la /var/lib/rspamd/dkim/ -``` - -## Security Notes - -1. **Localhost-only**: Postfix is configured to listen ONLY on 127.0.0.1 -2. **No authentication**: Not needed since only local processes can connect -3. **No firewall changes**: No external ports opened for email -4. **DKIM signing**: All outgoing emails are automatically signed with DKIM -5. **Host-specific key**: Using selector "amzebs-01" allows multiple hosts to send for amz.at - -## Troubleshooting - -### Email not being sent - -1. Check Postfix status: `systemctl status postfix` -2. Check queue: `mailq` -3. Check logs: `journalctl -u postfix -n 100` - -### DKIM not signing - -1. Check Rspamd status: `systemctl status rspamd` -2. Check if key exists: `ls -la /var/lib/rspamd/dkim/amz.at.amzebs-01.key` -3. Check Rspamd logs: `journalctl -u rspamd -n 100` - -### Permission errors - -```bash -# Ensure proper ownership -chown -R rspamd:rspamd /var/lib/rspamd/dkim/ -chmod 600 /var/lib/rspamd/dkim/*.key -``` - -### Rotate DKIM key - -```bash -# 1. Generate new key pair locally (follow "Initial Setup" steps) -# 2. Update the rspamd-dkim-key in secrets.yaml with new key -# 3. Deploy the configuration -nixos-rebuild switch - -# 4. Restart the setup service to copy new key -systemctl restart rspamd-dkim-setup - -# 5. Restart rspamd to use new key -systemctl restart rspamd - -# 6. Update DNS with new public key -# 7. Wait for DNS propagation before removing old DNS record -``` - -## Related Files - -- Postfix config: `hosts/amzebs-01/modules/postfix.nix` -- Rspamd config: `hosts/amzebs-01/modules/rspamd.nix` -- Main config: `hosts/amzebs-01/configuration.nix` -- Secrets file: `hosts/amzebs-01/secrets.yaml` (encrypted) - -## Sops Secret Configuration - -The DKIM private key is stored as a sops secret with the following configuration: - -```nix -sops.secrets.rspamd-dkim-key = { - owner = "rspamd"; - group = "rspamd"; - mode = "0400"; -}; -``` - -This ensures: -- Only the rspamd user can read the key -- The key is decrypted at boot time by sops-nix -- The key is encrypted in version control -- The key persists across rebuilds - -The key is automatically copied from the sops secret path to `/var/lib/rspamd/dkim/amz.at.amzebs-01.key` by the `rspamd-dkim-setup.service` on every boot. diff --git a/hosts/amzebs-01/configuration.nix b/hosts/amzebs-01/configuration.nix index 11336ed..9ccb1e1 100644 --- a/hosts/amzebs-01/configuration.nix +++ b/hosts/amzebs-01/configuration.nix @@ -8,8 +8,6 @@ ./modules/web/stack.nix ./modules/laravel-storage.nix ./modules/blackbox-exporter.nix - ./modules/postfix.nix - ./modules/rspamd.nix ./utils/modules/autoupgrade.nix ./utils/modules/promtail diff --git a/hosts/amzebs-01/modules/laravel-storage.nix b/hosts/amzebs-01/modules/laravel-storage.nix index 513e8d4..92e8884 100644 --- a/hosts/amzebs-01/modules/laravel-storage.nix +++ b/hosts/amzebs-01/modules/laravel-storage.nix @@ -5,7 +5,6 @@ systemd.tmpfiles.rules = [ # api.ebs.cloonar.dev "d /var/www/api.ebs.cloonar.dev/storage/framework/cache 0775 api_ebs_cloonar_dev nginx -" - "d /var/www/api.ebs.cloonar.dev/storage/framework/testing 0775 api_ebs_cloonar_dev nginx -" "d /var/www/api.ebs.cloonar.dev/storage/framework/sessions 0775 api_ebs_cloonar_dev nginx -" "d /var/www/api.ebs.cloonar.dev/storage/framework/views 0775 api_ebs_cloonar_dev nginx -" "d /var/www/api.ebs.cloonar.dev/storage/logs 0775 api_ebs_cloonar_dev nginx -" @@ -13,7 +12,6 @@ # api.ebs.amz.at "d /var/www/api.ebs.amz.at/storage/framework/cache 0775 api_ebs_amz_at nginx -" - "d /var/www/api.ebs.amz.at/storage/framework/testing 0775 api_ebs_amz_at nginx -" "d /var/www/api.ebs.amz.at/storage/framework/sessions 0775 api_ebs_amz_at nginx -" "d /var/www/api.ebs.amz.at/storage/framework/views 0775 api_ebs_amz_at nginx -" "d /var/www/api.ebs.amz.at/storage/logs 0775 api_ebs_amz_at nginx -" @@ -21,7 +19,6 @@ # api.stage.ebs.amz.at "d /var/www/api.stage.ebs.amz.at/storage/framework/cache 0775 api_stage_ebs_amz_at nginx -" - "d /var/www/api.stage.ebs.amz.at/storage/framework/testing 0775 api_stage_ebs_amz_at nginx -" "d /var/www/api.stage.ebs.amz.at/storage/framework/sessions 0775 api_stage_ebs_amz_at nginx -" "d /var/www/api.stage.ebs.amz.at/storage/framework/views 0775 api_stage_ebs_amz_at nginx -" "d /var/www/api.stage.ebs.amz.at/storage/logs 0775 api_stage_ebs_amz_at nginx -" diff --git a/hosts/amzebs-01/modules/postfix.nix b/hosts/amzebs-01/modules/postfix.nix deleted file mode 100644 index 6c21698..0000000 --- a/hosts/amzebs-01/modules/postfix.nix +++ /dev/null @@ -1,55 +0,0 @@ -{ pkgs -, lib -, config -, ... -}: -{ - # Header checks file for validating email headers - environment.etc."postfix/header_checks".text = '' - # Warn about missing critical headers (but don't reject from localhost) - # These help identify misconfigured applications - /^$/ WARN Missing headers detected - ''; - - services.postfix = { - enable = true; - hostname = "amzebs-01.amz.at"; - domain = "amz.at"; - - config = { - # Explicitly set hostname to prevent "localhost" HELO issues - myhostname = "amzebs-01.amz.at"; - - # Set proper HELO name for outgoing SMTP connections - smtp_helo_name = "amzebs-01.amz.at"; - - # Professional SMTP banner (prevents appearing as default/misconfigured) - smtpd_banner = "$myhostname ESMTP"; - - # Listen only on localhost for security - # Laravel will send via localhost, no external access needed - inet_interfaces = "loopback-only"; - - # Compatibility - compatibility_level = "2"; - - # Only accept mail from localhost - mynetworks = "127.0.0.0/8 [::1]/128"; - - # Larger message size limits for attachments - mailbox_size_limit = "202400000"; # ~200MB - message_size_limit = "51200000"; # ~50MB - - # Ensure proper header handling - # Reject mail that's missing critical headers - header_checks = "regexp:/etc/postfix/header_checks"; - - # Rate limiting to prevent spam-like behavior - # Allow reasonable sending rates for applications - smtpd_client_message_rate_limit = "100"; - smtpd_client_recipient_rate_limit = "200"; - - # Milter configuration is handled automatically by rspamd.postfix.enable - }; - }; -} diff --git a/hosts/amzebs-01/modules/rspamd.nix b/hosts/amzebs-01/modules/rspamd.nix deleted file mode 100644 index fa3dd80..0000000 --- a/hosts/amzebs-01/modules/rspamd.nix +++ /dev/null @@ -1,84 +0,0 @@ -{ pkgs -, config -, ... -}: -let - domain = "amz.at"; - selector = "amzebs-01"; - - localConfig = pkgs.writeText "local.conf" '' - logging { - level = "notice"; - } - - # DKIM signing configuration with host-specific selector - dkim_signing { - path = "/var/lib/rspamd/dkim/${domain}.${selector}.key"; - selector = "${selector}"; - allow_username_mismatch = true; - } - - # ARC signing (Authenticated Received Chain) - arc { - path = "/var/lib/rspamd/dkim/${domain}.${selector}.key"; - selector = "${selector}"; - allow_username_mismatch = true; - } - - # Add authentication results to headers - milter_headers { - use = ["authentication-results"]; - authenticated_headers = ["authentication-results"]; - } - ''; -in -{ - services.rspamd = { - enable = true; - extraConfig = '' - .include(priority=1,duplicate=merge) "${localConfig}" - ''; - - # Enable Postfix milter integration - postfix.enable = true; - }; - - # Copy DKIM key from sops secret to rspamd directory - systemd.services.rspamd-dkim-setup = { - description = "Setup DKIM key from sops secret for ${domain}"; - wantedBy = [ "multi-user.target" ]; - before = [ "rspamd.service" ]; - after = [ "sops-nix.service" ]; - - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - - script = '' - DKIM_DIR="/var/lib/rspamd/dkim" - DKIM_KEY="$DKIM_DIR/${domain}.${selector}.key" - - # Create directory if it doesn't exist - mkdir -p "$DKIM_DIR" - - # Copy key from sops secret - if [ -f "${config.sops.secrets.rspamd-dkim-key.path}" ]; then - cp "${config.sops.secrets.rspamd-dkim-key.path}" "$DKIM_KEY" - chown rspamd:rspamd "$DKIM_KEY" - chmod 600 "$DKIM_KEY" - echo "DKIM key deployed successfully from sops secret" - else - echo "ERROR: DKIM key not found in sops secrets!" - echo "Please ensure rspamd-dkim-key is defined in secrets.yaml" - exit 1 - fi - ''; - }; - - sops.secrets.rspamd-dkim-key = { - owner = "rspamd"; - group = "rspamd"; - mode = "0400"; - }; -} diff --git a/hosts/amzebs-01/secrets.yaml b/hosts/amzebs-01/secrets.yaml index 2407a9e..36c2241 100644 --- a/hosts/amzebs-01/secrets.yaml +++ b/hosts/amzebs-01/secrets.yaml @@ -1,46 +1,45 @@ -borg-passphrase: ENC[AES256_GCM,data:Q2GvEat5EHmshFiya3yNqFTVS+oJv0al+bYMRwysb0yu7F2gCJd000Y3ibA+tUPSL9iSlMSy0cTkesGVEGBt9w==,iv:/kUJXgibF1cyaCPB55/0nKYq9sSva6psxu2P/l7iRN4=,tag:velr9LTfoj7gEWhUmvPtQg==,type:str] -borg-ssh-key: ENC[AES256_GCM,data:0YEvv7QDmGsur0PFMmz5HqDgDCEk0kRaOu1n7GGWAwmmr0K0bVbpKzHw5wMiMnXEBrvj0izo4P4LYGAlAAYn22Bhgi2eN/vdvSO5V5uDV1ep2dV/TN2m3oYgTIgot47YgwBhcNunIUtEsbZuhAsTGL6LFBPJ3OCLKvhXTNTDaajgH/e4CvyxHHl63MBzr0i1ajigl1IKCk2hhZF4Kd1YGBCVZRoNyyNXywihlcFeskNfldW/sd5Qn2nowVf1MEV9n6Il6Zc1FX69WUVy1k+kOT7HJZGq3uDmgwXQgQhqKm1wh5uOlLkGUX6fz/nz+YFzLFMuUVvs34CzbbEFuWmGU+aNQrfCfI1hqwB5s6wVNdpUmigX9AQMQklu85tHFJg1AaRvhA24Cp/GrptggrTThcjwVFoe9NSQouNYn+ImTvlsE4HuDRRFE6YUounGd2lpRd40LsEjwKiLtwBwqG94u4ZOI91+LG6ZqHftRehE9r/CtedLyqtluNyyQyUNKPraUOm9Rrapewsj0ZCZgGQU,iv:xdRUBQlZlwVIog5KgZRmGNxdmhFE9HgnK3Ahfo+zT9k=,tag:McsJKUEGnKXxiv8Tg5zA4A==,type:str] -mysql-readonly-password: ENC[AES256_GCM,data:k2RplkUZPGZlh29KXXdtwe+MCqKzTI/bLdyuEeicdkbGlBk1SGyLF8vW4t8=,iv:a14IrXYVCDqPKGfJSEPP8g19sPvRTx5NT8IVJJeL48s=,tag:GWgU3oa/+u21/L2y3+vOsw==,type:str] -rspamd-dkim-key: ENC[AES256_GCM,data:maOnsx8AQUIjXqHEzHLxtSvAkr9+YCZid9xWaflkffS0gHd/hoHrozHy+rHSjU7Mz7QHYhjUjFY7Hp7wdKQnHpQLJRV96iNPXTXXYtBr7oDL51cq8ozd094FuMeLNSPitV89OHDcM+9h1F4dsdDWPUiw7eoijQeZ8vx1/VCVAp4FVxTFX3qhoMhXlFabiyM85eKMwJG4BdSwqS624f2Z4tvECRp0pBGtd/3r4/EVRDV1qNsiFvH8mi8eyg9xiWDLDrePq4TuWSu1Xc7z0qpDy0o8iAwGhPu9egIyzHEPk07j9U7PpK56C2UCSY0JBm0hkBGbqLXyRklSMytxoKgw4GJykMwNPNXmA9yuLPanxagJB/z8b7X4HTuYhExzQcC6ke/y8xKcxU4qGt8Ayy5v+QoNpdqXIPsZkIuw9uWm6RIgDt2dCaOdI06lesZKjqU/T6EhDfGoGZX7DwQ7uV9xNDM4NW2jsKpUdFKnzPCCe7/jO/ck4P4i8V+6NWDjj4+/BXDNnKJbMcHIHoSvckCGZiginJsbGvSWd0HfbpR7GQAnL3uKB5/HuFAaUkx+dPHmmP2tOBv6vNt+tq+V9i4kQmwAdl8a9KI456tw9vLwXcBDZOO7n4X5H0jc4afoYCnvLxahvbIXm2QNcBYVKxkqBCvoYEMrBjrnujwbQdEfDKQf3g5p8LQwAfCQ3ng+XH/BDF3qMBdsN1u5Di0FQpCDaGKX8pJ1gg+il76fJgSU8ftoaT32hJnLAjal4cgNIxbta2UYQLixUqaWZ8xqvxrSopkWYrlBBUyQh9jMEoTzpxwCsEPQ72qgVcQfJYlMl4WUBwcasfJnySR+qZ22g3fhStpAQ2HuTGhLjTG1QOewYdwDXDhNmcbqZ478Sp1t7qbBx0R7vWFSyYCMlbmLmvzPm6Z3ET7lkfCrMjMNXaQ8cWSF19QaHAfqRwQooLL93yx7U0KHCilEg4bUjsw8MLQNa4A0ohpq6CG4s5O1+di7W4/h71/moggIebFb2eGLJ8BvbkwiVozXI9L77IGd9RswlEjZed18u7fqetS7dyDthhVG2pvya4zZI/cxIq6oJkNr2RIt3NgYChOh0I/17DuSJJ1jAmPB0Evj8QtCCo49ENnyO5cGWn12DZWybwYkg2jQC4aDFA/u5ajTo3wOKdwHj5hgMz/z05Bn2vAdhCGl6uWWNzcNnDiu3/rjqsjOkfkp0hCP7Q==,iv:FORxJ8htcoLIEJihUN7im3dN4jhnigB70InTohtpWwU=,tag:e2DHBd2dn3piCkEdkbHdoA==,type:str] +borg-passphrase: ENC[AES256_GCM,data:6T00Em+a5TcrmQNvtoCoij5aks6KIZkCAAaPXLirkQlZ6x1p1bX9KXU2ZvBAtVPrUuTeZLPTKqT/iL5Io+WKGw==,iv:gB9cktzKa8khmZZ8xwLS6oEX+Ag3APmf2jIQNLa1g/Y=,tag:sWfVbKQHgaaSRWuqYdpKTQ==,type:str] +borg-ssh-key: ENC[AES256_GCM,data:TrfaVOWlk8NXMEm6xr5+9pv2j8qPQ2dd6jAhHw8uw25ijhiA+eNtQh9YuQj40zw7hj7cQKctZ6pptdGS1OvS0Zoq1r6IJWLJ2UZcYLXDhOX2/TJQbRcooawG5+JiYCMBe6+T1bzgQaDGWKM7l09lq/saycci/ICe6KKQE8d8i0RaKCsCTf6auNiSMgjYBUMezYP0tTMTIZT6lqeHCUqiEkQ32aE7fdFCAF4hU9JkzhUad3BYasJfDKWksPGc2/GrCpx5JHAD4Tp6GbUpTFVCFf0JVFWaAIyYfKsIShO+yks2TIEOkcQbg8VZADbTtAhhEBcvOCS6mVcgpqQRiEfJI3OvG07KhujJa66EKHlaOM2K71RDDVG+KrlPTW2/1Zaz03FDo7QxiOae9u6KkF+GCIMXAiOL6XYAyDMkxssUA6JodFXImWWwqDJe5Af5jIUAIAAGJTDBSl6S+TWP3pJSM+Pfq6OxRJYFeCVrIE2P4aC37x2vi8V7iTPk0EJSm9TmrLbw+Ia5OvrarwzwtZ2u,iv:IVyeqEGhWUamXw8HPwqyvrHcmTcyEOZmm2NRaTdK+qw=,tag:hQb7wk0YeeQxrPFWuMlfGg==,type:str] +mysql-readonly-password: ENC[AES256_GCM,data:KQiL0ZJGkJEqX7wADmY2YucT79Grt+tCQA/aER7llHgqUIvjJHO8C2yw+VI=,iv:M3QchAeKXp7BjP2FfaWgUNiGPs0qQHe9P5lttxO5+Fg=,tag:TPGUWXYQsf40hlVu7PGEEg==,type:str] sops: age: - recipient: age14grjcxaq4h55yfnjxvnqhtswxhj9sfdcvyas4lwvpa8py27pjy2sv3g6v7 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhQUpWNUgxVnhuTXd2TkF0 - SVVHemFKRWlYczZ0TnBESVNRczhuRUNnUG1BCmJKQ2JZbHhFcXJidHJzci9OaFBm - ZTd0MGhsaVBic3dMb3psUHRCRnR3ODQKLS0tIERrSG1GVTRHdkJpVWpqdTZ4Yytq - OHhlZjV6MjRVbXFsWjlQSU03ZDNwYm8KAswHRSdV0BW/oJyZx63iZRHsF7SZ6PO+ - hajQqmEyfcVfEu39zZzxQ2mtWlOr69I++irOhE3NeiFeJ1yIRQDJEQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvWjd4K3BueGVxNGs2OC9t + YURtcTIzNytCMDdYd29KenZOdm4vMk9mWEZFCkJrZlVZdkVJeW4rNzc0N3NBY3hM + dE1ORzRHRHlONEQ2dW83R051aE45QVEKLS0tIE0zOVpVbWphNitPaUg0cmxUbW5m + ZHViVHJrOWREb0pYR3hOTm0zSHZ3N0EKeNcZOM+H0XZN3Ji1ubBoHMgycuJFX3+C + YvJ795wSwtXMU+mCDB04tcYPSAI0RC82wGT9r3XLNZgbF/xP0Er3nw== -----END AGE ENCRYPTED FILE----- - recipient: age1exny8unxynaw03yu8ppahu5z28uermghr8ag34e7kdqnaduq9stsyettzz enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUaUNnY0hpdDAzMTNIUS9D - RmdKbmplUk9DRXlLRXEvSnVjT05sQjcvTnpJCkd6bGRINm5yYUZOUTVzWEdjRmtG - Mmx0ci93N2wvTWV5MzlRVnlYdUxoUWsKLS0tIEVHUlNWYStWTG01RzRrVnNXc3BW - VkRkUXROU3plNmwvTUVhYmhCS2syQkEKKgC0EmUu1u2vZ/SZTnam+h846gZSyY4V - JyMzkws8O5TY9juWdDzXJIU67mIgc4qrWWN3uh8k28JBZGc078b5bg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWTnpIdHBmMGdBQzk2N0xW + SGlNMUxjYU5SZHJLK0wvWnZyYkEzK04vdWd3CmVGWW50RUQ2K0lLRC90WW9KY1hj + NGVpMEdzaHUyTUVBaDcxb2w5MC9BUjAKLS0tIFdSSjlFTHl1Z3NYNVhxaSs0WkJE + eGVWZHdnMkhaNzlDNlhCa24wZzlvNmsK7pLzsxtlMevP2o9nJOjVgDAjrYdEgRUu + NlJHfO0m9U7fJfeu6XSWQgGYRJm7tSmTZKvsJgTS+pKcynHz8B9rkQ== -----END AGE ENCRYPTED FILE----- - recipient: age1v6p8dan2t3w9h94fz4flldl32082j3s9x6zqq7u5j66keth9aphsd6pvch enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoK2JUNVdYTzkvM1BBWXRm - citCNlE4Z1NLdEZ2R0tNZTVSMlFSeGxGOURnClJnYURYa0JZaVprQWdBcmVnOWVj - TGVCK1JWMVlueHJUaTZZYmROM0E5aDAKLS0tIEJxYkdadGtZM250d2d6Ujl2UU9C - YUpkVll2S2RpT0I1UVZiZFRKS1prMEEKp/bGImanJ/58vTQG/gUun/Y2QdmOEi3h - hVS0V2QcfuGgi0/YofLOM3+M6k6ViXw07XfXmR+puvLIHKr2y11x1Q== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNMk9VL2s0ZmoxSzY3NmFQ + MTZEaml5Q0kxRDFwSEN5dG90SzZmcXZLUjJBClpVNUJEZEdaa09hM1BQUU1jVVQz + M2w4QXdmZnJya2pCTEV1QW9keXgyTWMKLS0tIFA1VWl3RzF5V2FMUE1mZ2NYRnBU + OURWSFZnM0lEMXJEcjVPL3hnZ0pIQ1kKVvoCVQuayH/XRfddMKq2d8TssXOS5e1o + bIL6F+tRBle2UgVuXSMkyggCnvLePA8OxfAdMMg5npSFkPgTZrAYYQ== -----END AGE ENCRYPTED FILE----- - recipient: age1xcgc6u7fmc2trgxtdtf5nhrd7axzweuxlg0ya9jre3sdrg6c6easecue9w enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDSGdEZnZEaDRpWUJVcnds - VGFSQklvczBZdEdEbXhodW8vME9wMUpVRENjClFZcnVqYkJxdlBiZFhma0tmZjgz - YXlIdlRDTDU4MHg1dzhGVDRJb2FGYVUKLS0tIDBXSWZ2NkxzdEk0ZlFRM00ybFNy - M0doaWl5R2cwU2RxQm5DbWxXeTZ5S2MKwrB3SysmgzCThQOhEVx18dxIfko0+oZY - 9BSZOoFbfuwiLbtpL4J8bzxDvxn6sXxB8EBJH1hbpID53AquWDsxSw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3QmR3T3FYN2RpR1JrV0w0 + aVMycUJUN1NKME1UKzVsZmpNNTAzTmxUUUZNClhmdUF5N0Q4K09IeVhNOWhNNEc2 + UTNzeGJ4NlpxMUtEaHZDWDZOeHdvSU0KLS0tIFNTaThWbklXeE85c3hSMWZwNTNN + RWFUVXVXWjdsSHM5ZGljd3YyQW5ja2MKvAhwHL5PcLFxuU7MfV/cWtNfzTb9yoqR + 3iD4UJsDDagCIkpvjKods4ydlzh3agOyLHswDSX/WmUur9J5pd4PAg== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-11-19T11:16:25Z" - mac: ENC[AES256_GCM,data:x4yor9G+QirceSYSX1K9GdfyGellT4JCkE09Tl9/mOX8HMOKFAQGknuwwU6SNGg+ciBFk4TdjQnDmVai4T8JQo9W/DLiZ+GKnWO3s+ZLDX30sEF0aMjKa43R5CCPO/Fl2XH96TaPC+8itTJQ6TpBSg51QLPcpqrMljiBNWvEoTU=,iv:Zi9rglAwgsejUmIpLN/1QlL80BSp3HP32k1xkWt2b+o=,tag:2ADk8d2G4OezkQjcV3CZuA==,type:str] + lastmodified: "2025-11-14T11:33:59Z" + mac: ENC[AES256_GCM,data:AnEs3yzpOJ5/wyCL/sHV6U5V7FBhZZlBQeA+mCGfZ25JZAL3Yb6yD6xJhmGC8AqIFS6PIFSWa2r0suDRQAoVO2AwFVwd9Y/TEwjPGnXvWfwB82+mnyLIakyzM/pcLjiMePUqr5nnJ8tWoKzuqs/jQHuMOGkItqwjkVDr9/hx3lc=,iv:kc08z63phDfs7gzruHjnQA9bXAvWMGkE14/0Kyfhuds=,tag:9wedcRCTAv0HMtSap55JOw==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0 diff --git a/hosts/amzebs-01/sites/ebs-mobile.cloonar.dev.nix b/hosts/amzebs-01/sites/ebs-mobile.cloonar.dev.nix index 2e2edfd..4d2dd25 100644 --- a/hosts/amzebs-01/sites/ebs-mobile.cloonar.dev.nix +++ b/hosts/amzebs-01/sites/ebs-mobile.cloonar.dev.nix @@ -18,7 +18,7 @@ in { # React client-side routing support locations."/".extraConfig = '' index index.html; - try_files $uri $uri/ /index.html$is_args$args; + try_files $uri $uri/ /index.html; ''; # Cache static assets diff --git a/hosts/amzebs-01/sites/ebs.amz.at.nix b/hosts/amzebs-01/sites/ebs.amz.at.nix index 1caa1a3..e6ec6a4 100644 --- a/hosts/amzebs-01/sites/ebs.amz.at.nix +++ b/hosts/amzebs-01/sites/ebs.amz.at.nix @@ -18,7 +18,7 @@ in { # React client-side routing support locations."/".extraConfig = '' index index.html; - try_files $uri $uri/ /index.html$is_args$args; + try_files $uri $uri/ /index.html; ''; # Cache static assets diff --git a/hosts/amzebs-01/sites/ebs.cloonar.dev.nix b/hosts/amzebs-01/sites/ebs.cloonar.dev.nix index 218b9e2..ef4aa78 100644 --- a/hosts/amzebs-01/sites/ebs.cloonar.dev.nix +++ b/hosts/amzebs-01/sites/ebs.cloonar.dev.nix @@ -18,7 +18,7 @@ in { # React client-side routing support locations."/".extraConfig = '' index index.html; - try_files $uri $uri/ /index.html$is_args$args; + try_files $uri $uri/ /index.html; ''; # Cache static assets diff --git a/hosts/amzebs-01/sites/stage.ebs.amz.at.nix b/hosts/amzebs-01/sites/stage.ebs.amz.at.nix index ee258d6..60dc9bc 100644 --- a/hosts/amzebs-01/sites/stage.ebs.amz.at.nix +++ b/hosts/amzebs-01/sites/stage.ebs.amz.at.nix @@ -18,7 +18,7 @@ in { # React client-side routing support locations."/".extraConfig = '' index index.html; - try_files $uri $uri/ /index.html$is_args$args; + try_files $uri $uri/ /index.html; ''; # Cache static assets diff --git a/hosts/fw/configuration.nix b/hosts/fw/configuration.nix index ecfb518..87c6312 100644 --- a/hosts/fw/configuration.nix +++ b/hosts/fw/configuration.nix @@ -49,8 +49,7 @@ ./modules/firefox-sync.nix ./modules/fivefilters.nix - ./modules/pyload.nix - + # home assistant ./modules/home-assistant ./modules/deconz.nix @@ -88,18 +87,6 @@ "ai-mailer" ]; - # Intel N100 Graphics Support for hardware transcoding - hardware.graphics = { - enable = true; - extraPackages = with pkgs; [ - intel-media-driver # VAAPI driver (iHD) for modern Intel GPUs - vpl-gpu-rt # Intel VPL/QSV runtime for Gen 12+ (N100) - intel-compute-runtime # OpenCL support for tone-mapping - ]; - }; - - hardware.enableRedistributableFirmware = true; - time.timeZone = "Europe/Vienna"; services.logind.extraConfig = "RuntimeDirectorySize=2G"; diff --git a/hosts/fw/modules/ai-mailer.nix b/hosts/fw/modules/ai-mailer.nix index 35d81d7..cf1f77a 100644 --- a/hosts/fw/modules/ai-mailer.nix +++ b/hosts/fw/modules/ai-mailer.nix @@ -33,7 +33,6 @@ context: urls: - - "https://paraclub.cloonar.dev/de/tandemfallschirmspringen/faq/" - "https://paraclub.at/de/" - "https://paraclub.at/de/tandemfallschirmspringen/alle-infos/" - "https://paraclub.at/de/tandemfallschirmspringen/kosten-tandemsprung/" diff --git a/hosts/fw/modules/deconz.nix b/hosts/fw/modules/deconz.nix index eea6b2a..bfbf632 100644 --- a/hosts/fw/modules/deconz.nix +++ b/hosts/fw/modules/deconz.nix @@ -2,33 +2,21 @@ virtualisation = { oci-containers.containers = { deconz = { - autoStart = true; + autoStart = false; image = "marthoc/deconz"; volumes = [ "/etc/localtime:/etc/localtime:ro" "/var/lib/deconz:/root/.local/share/dresden-elektronik/deCONZ" - "/dev/bus/usb:/dev/bus/usb:ro" - "/run/udev:/run/udev:ro" ]; environment = { DECONZ_DEVICE = "/dev/ttyACM0"; TZ = "Europe/Vienna"; - DECONZ_UID = "0"; - DECONZ_GID = "0"; - DECONZ_START_VERBOSE = "1"; }; extraOptions = [ "--network=server" "--ip=${config.networkPrefix}.97.22" "--device=/dev/ttyACM0" "--hostname=deconz" - "--mac-address=1a:c4:04:6e:29:bd" - "--cap-add=CAP_MKNOD" - "--cap-add=CAP_NET_RAW" - "--cap-add=CAP_NET_ADMIN" - "--device-cgroup-rule=c 166:* rmw" - "--device-cgroup-rule=c 188:* rmw" - "--security-opt=label=disable" ]; }; }; diff --git a/hosts/fw/modules/dnsmasq.nix b/hosts/fw/modules/dnsmasq.nix index c323f0b..70d5ef9 100644 --- a/hosts/fw/modules/dnsmasq.nix +++ b/hosts/fw/modules/dnsmasq.nix @@ -133,10 +133,6 @@ "/foundry-vtt.cloonar.com/${config.networkPrefix}.97.5" "/sync.cloonar.com/${config.networkPrefix}.97.5" - # multimedia - "/dl.cloonar.com/${config.networkPrefix}.97.5" - "/jellyfin.cloonar.com/${config.networkPrefix}.97.5" - "/deconz.cloonar.multimedia/${config.networkPrefix}.97.22" "/ddl-warez.to/172.67.184.30" diff --git a/hosts/fw/modules/home-assistant/light.nix b/hosts/fw/modules/home-assistant/light.nix index 26579e7..03e218f 100644 --- a/hosts/fw/modules/home-assistant/light.nix +++ b/hosts/fw/modules/home-assistant/light.nix @@ -396,7 +396,12 @@ all = true; entities = [ "light.livingroom_switch" - "light.living_room" + "light.livingroom_bulb_1_rgbcw_bulb" + "light.livingroom_bulb_2_rgbcw_bulb" + "light.livingroom_bulb_3_rgbcw_bulb" + "light.livingroom_bulb_4_rgbcw_bulb" + "light.livingroom_bulb_5_rgbcw_bulb" + "light.livingroom_bulb_6_rgbcw_bulb" ]; } { diff --git a/hosts/fw/modules/pyload.nix b/hosts/fw/modules/pyload.nix deleted file mode 100644 index 9a0b1b0..0000000 --- a/hosts/fw/modules/pyload.nix +++ /dev/null @@ -1,191 +0,0 @@ -{ config, pkgs, ... }: -let - cids = import ./staticids.nix; - networkPrefix = config.networkPrefix; - - pyloadUser = { - isSystemUser = true; - uid = cids.uids.pyload; - group = "pyload"; - home = "/var/lib/pyload"; - createHome = true; - }; - pyloadGroup = { - gid = cids.gids.pyload; - }; - - jellyfinUser = { - isSystemUser = true; - uid = cids.uids.jellyfin; - group = "jellyfin"; - home = "/var/lib/jellyfin"; - createHome = true; - extraGroups = [ "render" "video" ]; - }; - jellyfinGroup = { - gid = cids.gids.jellyfin; - }; -in -{ - users.users.pyload = pyloadUser; - users.groups.pyload = pyloadGroup; - users.users.jellyfin = jellyfinUser; - users.groups.jellyfin = jellyfinGroup; - - # Create the multimedia directory structure on the host - systemd.tmpfiles.rules = [ - "d /var/lib/multimedia 0755 root root - -" - "d /var/lib/multimedia/downloads 0755 pyload pyload - -" - "d /var/lib/multimedia/movies 0755 jellyfin jellyfin - -" - "d /var/lib/multimedia/tv-shows 0755 jellyfin jellyfin - -" - "d /var/lib/multimedia/music 0755 jellyfin jellyfin - -" - "d /var/lib/jellyfin 0755 jellyfin jellyfin - -" - ]; - - containers.pyload = { - autoStart = true; - ephemeral = false; - privateNetwork = true; - hostBridge = "server"; - hostAddress = "${networkPrefix}.97.1"; - localAddress = "${networkPrefix}.97.11/24"; - - # GPU device passthrough for hardware transcoding - allowedDevices = [ - { - modifier = "rwm"; - node = "/dev/dri/card0"; - } - { - modifier = "rwm"; - node = "/dev/dri/renderD128"; - } - ]; - - bindMounts = { - "/dev/dri" = { - hostPath = "/dev/dri"; - isReadOnly = false; - }; - "/run/opengl-driver" = { - hostPath = "/run/opengl-driver"; - isReadOnly = true; - }; - "/nix/store" = { - hostPath = "/nix/store"; - isReadOnly = true; - }; - "/var/lib/pyload" = { - hostPath = "/var/lib/pyload"; - isReadOnly = false; - }; - "/var/lib/jellyfin" = { - hostPath = "/var/lib/jellyfin"; - isReadOnly = false; - }; - "/multimedia" = { - hostPath = "/var/lib/multimedia"; - isReadOnly = false; - }; - }; - - config = { lib, config, pkgs, ... }: { - nixpkgs.overlays = [ - (import ../utils/overlays/packages.nix) - ]; - - - nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ - "unrar" - ]; - - environment.systemPackages = with pkgs; [ - unrar # Required for RAR archive extraction - ]; - - # Intel graphics support for hardware transcoding - hardware.graphics = { - enable = true; - extraPackages = with pkgs; [ - intel-media-driver - vpl-gpu-rt - intel-compute-runtime - ]; - }; - - # Set VA-API driver to iHD (modern Intel driver for N100) - environment.sessionVariables = { - LIBVA_DRIVER_NAME = "iHD"; - }; - - networking = { - hostName = "pyload"; - useHostResolvConf = false; - defaultGateway = { - address = "${networkPrefix}.97.1"; - interface = "eth0"; - }; - nameservers = [ "${networkPrefix}.97.1" ]; - firewall.enable = false; - }; - - services.pyload = { - enable = true; - downloadDirectory = "/multimedia/downloads"; - listenAddress = "0.0.0.0"; - port = 8000; - }; - - services.jellyfin = { - enable = true; - openFirewall = true; - }; - - # Override systemd hardening for GPU access - systemd.services.jellyfin = { - serviceConfig = { - PrivateUsers = lib.mkForce false; # Disable user namespacing - breaks GPU device access - DeviceAllow = [ - "/dev/dri/card0 rw" - "/dev/dri/renderD128 rw" - ]; - SupplementaryGroups = [ "render" "video" ]; # Critical: Explicit group membership for GPU access - }; - environment = { - LIBVA_DRIVER_NAME = "iHD"; # Ensure service sees this variable - }; - }; - - # Disable SSL certificate verification - systemd.services.pyload = { - environment = { - PYLOAD__GENERAL__SSL_VERIFY = "0"; - }; - - # Bind-mount DNS configuration files and system tools into the chroot - serviceConfig = { - BindReadOnlyPaths = [ - "/etc/resolv.conf" - "/etc/nsswitch.conf" - "/etc/hosts" - "/etc/ssl" - "/etc/static/ssl" - # Make all system packages (including unrar) accessible - "/run/current-system/sw/bin" - ]; - }; - }; - - # Ensure render/video groups exist with consistent GIDs for GPU access - users.groups.render = { gid = 303; }; - users.groups.video = { gid = 26; }; - - users.users.pyload = pyloadUser; - users.groups.pyload = pyloadGroup; - users.users.jellyfin = jellyfinUser; - users.groups.jellyfin = jellyfinGroup; - - system.stateVersion = "24.05"; - }; - }; -} diff --git a/hosts/fw/modules/staticids.nix b/hosts/fw/modules/staticids.nix index ae57ad5..498d9db 100644 --- a/hosts/fw/modules/staticids.nix +++ b/hosts/fw/modules/staticids.nix @@ -5,8 +5,6 @@ gitea-runner = 10003; podman = 10004; foundry-vtt = 10005; - pyload = 10006; - jellyfin = 10007; }; gids = { unbound = 10001; @@ -14,7 +12,5 @@ gitea-runner = 10003; podman = 10004; foundry-vtt = 10005; - pyload = 10006; - jellyfin = 10007; }; } diff --git a/hosts/fw/modules/web/proxies.nix b/hosts/fw/modules/web/proxies.nix index 5991ea5..87878cd 100644 --- a/hosts/fw/modules/web/proxies.nix +++ b/hosts/fw/modules/web/proxies.nix @@ -33,51 +33,4 @@ proxyPass = "http://${config.networkPrefix}.97.10"; }; }; - services.nginx.virtualHosts."dl.cloonar.com" = { - forceSSL = true; - enableACME = true; - acmeRoot = null; - - # Restrict to internal LAN only - extraConfig = '' - allow ${config.networkPrefix}.96.0/24; - allow ${config.networkPrefix}.98.0/24; - deny all; - ''; - - locations."/" = { - proxyPass = "http://${config.networkPrefix}.97.11:8000"; - proxyWebsockets = true; - }; - }; - - services.nginx.virtualHosts."jellyfin.cloonar.com" = { - forceSSL = true; - enableACME = true; - acmeRoot = null; - - # Restrict to internal LAN only - extraConfig = '' - allow ${config.networkPrefix}.96.0/24; - allow ${config.networkPrefix}.98.0/24; - allow ${config.networkPrefix}.99.0/24; - deny all; - ''; - - locations."/" = { - proxyPass = "http://${config.networkPrefix}.97.11:8096"; - proxyWebsockets = true; - - extraConfig = '' - # Jellyfin-specific headers for proper streaming - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $http_host; - - # Disable buffering for better streaming performance - proxy_buffering off; - ''; - }; - }; } diff --git a/hosts/nb/configuration.nix b/hosts/nb/configuration.nix index f23c92c..e1b6db5 100644 --- a/hosts/nb/configuration.nix +++ b/hosts/nb/configuration.nix @@ -189,8 +189,6 @@ in { networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. networking.extraHosts = '' 77.119.230.30 vpn.cloonar.com - 23.88.38.1 api.ebs.amz.at - 23.88.38.1 ebs.amz.at ''; # Set your time zone. @@ -289,9 +287,8 @@ in { settings = { auto-optimise-store = true; experimental-features = [ "nix-command" "flakes" ]; - max-jobs = 4; - cores = 4; - max-substitution-jobs = 16; + max-jobs = 12; + cores = 2; }; gc = { automatic = true; diff --git a/hosts/nb/modules/development/mcp-chromium.nix b/hosts/nb/modules/development/mcp-chromium.nix index f357393..0ab6c52 100644 --- a/hosts/nb/modules/development/mcp-chromium.nix +++ b/hosts/nb/modules/development/mcp-chromium.nix @@ -3,7 +3,7 @@ let # Wrapper to launch Chromium on Wayland, scale=1, DevTools debugging on 127.0.0.1:9222 chromiumWaylandWrapper = pkgs.writeShellScriptBin "chromium-mcp" '' - exec ${pkgs.ungoogled-chromium}/bin/chromium \ + exec ${pkgs.chromium}/bin/chromium \ --ozone-platform=wayland \ --enable-features=UseOzonePlatform \ --force-device-scale-factor=1 \ @@ -11,13 +11,32 @@ let --remote-debugging-port=9222 \ "$@" ''; + + # Desktop entry that uses our wrapper. The filename will be chromium.desktop + chromiumDesktopOverride = pkgs.makeDesktopItem { + name = "chromium"; # ← important: must match stock filename to override + desktopName = "Chromium"; + genericName = "Web Browser"; + comment = "Chromium on Wayland (scale=1) with DevTools remote debugging for MCP"; + icon = "chromium"; + exec = "${chromiumWaylandWrapper}/bin/chromium-mcp %U"; + terminal = false; + categories = [ "Network" "WebBrowser" ]; + mimeTypes = [ + "text/html" "text/xml" "application/xhtml+xml" + "x-scheme-handler/http" "x-scheme-handler/https" + "x-scheme-handler/ftp" "x-scheme-handler/chrome" + ]; + # If you want extra desktop keys, you can add them as a raw block: + }; in { - # Tools: Chromium, Node (for MCP server), our wrapper + # Tools: Chromium, Node (for MCP server), our wrapper, and the desktop override environment.systemPackages = [ - pkgs.ungoogled-chromium + pkgs.chromium pkgs.nodejs_22 # 25.05 ships Node 22 LTS; works great for MCP servers chromiumWaylandWrapper + chromiumDesktopOverride # ← keep AFTER pkgs.chromium so our .desktop wins ]; # Where Codex CLI reads config; we make it system-wide diff --git a/hosts/nb/modules/puppeteer.nix b/hosts/nb/modules/puppeteer.nix index 4207f30..c84529b 100644 --- a/hosts/nb/modules/puppeteer.nix +++ b/hosts/nb/modules/puppeteer.nix @@ -1,6 +1,6 @@ { config, pkgs, ... }: { environment.systemPackages = with pkgs; [ - ungoogled-chromium + chromium nodejs # Graphics and font dependencies freetype @@ -30,6 +30,6 @@ ]; environment.variables = { - PUPPETEER_EXECUTABLE_PATH = "${pkgs.ungoogled-chromium}/bin/chromium"; + PUPPETEER_EXECUTABLE_PATH = "${pkgs.chromium}/bin/chromium"; }; } diff --git a/hosts/nb/users/dominik.nix b/hosts/nb/users/dominik.nix index cee91e4..90035c4 100644 --- a/hosts/nb/users/dominik.nix +++ b/hosts/nb/users/dominik.nix @@ -228,21 +228,6 @@ in Restart = "always"; }; }; - pyload-tunnel = { - Unit = { - Description = "SSH tunnel for pyLoad Click'n'Load"; - After = [ "graphical-session-pre.target" ]; - PartOf = [ "graphical-session.target" ]; - }; - Install = { - WantedBy = [ "graphical-session.target" ]; - }; - Service = { - ExecStart = "${pkgs.openssh}/bin/ssh -N -L 9666:10.42.97.11:9666 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 root@fw.cloonar.com"; - Restart = "always"; - RestartSec = "10s"; - }; - }; }; programs.chromium = { diff --git a/scripts/update-pyload-hash b/scripts/update-pyload-hash deleted file mode 100755 index 100ee8b..0000000 --- a/scripts/update-pyload-hash +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - -# Check if commit SHA is provided -if [ $# -ne 1 ]; then - echo -e "${RED}Error: Git commit SHA required${NC}" - echo "Usage: $0 " - echo "Example: $0 e5fe7038f3e116d878c58059323b682e426f9c84" - echo "" - echo "To find the latest commit:" - echo " Visit: https://github.com/pyload/pyload/commits/main" - exit 1 -fi - -COMMIT_SHA="$1" - -# Validate commit SHA format (40 character hex string) -if ! [[ "$COMMIT_SHA" =~ ^[0-9a-f]{40}$ ]]; then - echo -e "${RED}Error: Invalid commit SHA format${NC}" - echo "Commit SHA must be a 40-character hexadecimal string" - exit 1 -fi - -echo -e "${GREEN}==> Updating pyload-ng to commit: ${COMMIT_SHA}${NC}" - -# File to update -PKG_FILE="$REPO_ROOT/utils/pkgs/pyload-ng-updated.nix" - -if [ ! -f "$PKG_FILE" ]; then - echo -e "${RED}Error: Package file not found: $PKG_FILE${NC}" - exit 1 -fi - -# Step 1: Update commit SHA in package file -echo -e "${YELLOW}Step 1: Updating commit SHA in package file...${NC}" -sed -i "s/rev = \"[0-9a-f]*\";/rev = \"$COMMIT_SHA\";/" "$PKG_FILE" -echo " ✓ Updated commit SHA in $PKG_FILE" - -# Step 2: Set hash to a fake value to trigger hash discovery -echo -e "${YELLOW}Step 2: Setting hash to fake value...${NC}" -sed -i 's/hash = "sha256-[^"]*";/hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";/' "$PKG_FILE" -echo " ✓ Updated hash in $PKG_FILE" - -# Step 3: Build package to discover the correct hash -echo -e "${YELLOW}Step 3: Building package to discover hash...${NC}" -BUILD_OUTPUT=$(nix-build --impure -E "with import { overlays = [ (import $REPO_ROOT/utils/overlays/packages.nix) ]; }; callPackage $PKG_FILE { }" 2>&1 || true) - -# Extract hash from error message -HASH=$(echo "$BUILD_OUTPUT" | grep -oP '\s+got:\s+\Ksha256-[A-Za-z0-9+/=]+' | head -1) - -if [ -z "$HASH" ]; then - echo -e "${RED}Error: Failed to extract hash from build output${NC}" - echo "Build output:" - echo "$BUILD_OUTPUT" - exit 1 -fi - -echo " ✓ Discovered hash: $HASH" - -# Step 4: Update package file with the correct hash -echo -e "${YELLOW}Step 4: Updating hash in package file...${NC}" -sed -i "s|hash = \"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\";|hash = \"$HASH\";|" "$PKG_FILE" -echo " ✓ Updated hash in $PKG_FILE" - -# Step 5: Verify the build succeeds -echo -e "${YELLOW}Step 5: Verifying build with correct hash...${NC}" -if nix-build --impure -E "with import { overlays = [ (import $REPO_ROOT/utils/overlays/packages.nix) ]; }; callPackage $PKG_FILE { }" > /dev/null 2>&1; then - echo " ✓ Build verification successful" -else - echo -e "${RED}Error: Build verification failed${NC}" - exit 1 -fi - -# Step 6: Test configuration for fw host (which uses pyload) -echo -e "${YELLOW}Step 6: Testing fw configuration...${NC}" -cd "$REPO_ROOT" -if ./scripts/test-configuration fw > /dev/null 2>&1; then - echo " ✓ Configuration test passed" -else - echo -e "${RED}Warning: Configuration test failed${NC}" - echo "This may be due to missing secrets or other issues unrelated to the hash update." -fi - -# Success summary -echo -e "${GREEN}" -echo "======================================" -echo "✓ pyload-ng updated successfully!" -echo "======================================" -echo "Commit: $COMMIT_SHA" -echo "Hash: $HASH" -echo -e "${NC}" -echo "Next steps:" -echo " 1. Review changes: git diff $PKG_FILE" -echo " 2. Test locally if needed" -echo " 3. Commit changes: git add $PKG_FILE && git commit -m 'update: pyload-ng to commit ${COMMIT_SHA:0:8}'" -echo " 4. Push to trigger automatic deployment" diff --git a/utils/overlays/packages.nix b/utils/overlays/packages.nix index 811ae3b..cffe405 100644 --- a/utils/overlays/packages.nix +++ b/utils/overlays/packages.nix @@ -6,15 +6,5 @@ self: super: { openmanus = (super.callPackage ../pkgs/openmanus.nix { }); ai-mailer = self.callPackage ../pkgs/ai-mailer.nix { }; - # Python packages - python3 = super.python3.override { - packageOverrides = pself: psuper: { - mini-racer = pself.callPackage ../pkgs/mini-racer.nix { }; - }; - }; - python3Packages = self.python3.pkgs; - - pyload-ng = self.callPackage ../pkgs/pyload-ng-updated.nix { pyload-ng = super.pyload-ng; }; - # vscode-insiders = (super.callPackage ../pkgs/vscode-insiders.nix { }); } diff --git a/utils/pkgs/mini-racer.nix b/utils/pkgs/mini-racer.nix deleted file mode 100644 index 3d00cc1..0000000 --- a/utils/pkgs/mini-racer.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ lib -, buildPythonPackage -, fetchPypi -, stdenv -, autoPatchelfHook -}: - -buildPythonPackage rec { - pname = "mini-racer"; - version = "0.12.4"; - format = "wheel"; - - src = fetchPypi { - pname = "mini_racer"; - inherit version format; - dist = "py3"; - python = "py3"; - abi = "none"; - platform = "manylinux_2_31_x86_64"; - hash = "sha256-aaHETQKpBpuIFoTO8VotdH/gdD3ynq3Igf2nACquX9I="; - }; - - nativeBuildInputs = [ - autoPatchelfHook - ]; - - buildInputs = [ - stdenv.cc.cc.lib - ]; - - # Don't strip binaries, it breaks V8 - dontStrip = true; - - meta = with lib; { - description = "Minimal Python wrapper for V8 JavaScript engine"; - homepage = "https://github.com/bpcreech/PyMiniRacer"; - license = licenses.isc; - maintainers = [ ]; - platforms = [ "x86_64-linux" ]; - }; -} diff --git a/utils/pkgs/pyload-ng-updated.nix b/utils/pkgs/pyload-ng-updated.nix deleted file mode 100644 index 195eebf..0000000 --- a/utils/pkgs/pyload-ng-updated.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ lib, pyload-ng, fetchFromGitHub, python3Packages }: - -pyload-ng.overridePythonAttrs (oldAttrs: rec { - version = "0.5.0b3.dev93+git"; - - src = fetchFromGitHub { - owner = "pyload"; - repo = "pyload"; - rev = "3115740a2210fd57b5d050cd0850a0e61ec493ed"; # [DdownloadCom] fix #4537 - hash = "sha256-g1eEeNnr3Axtr+0BJzMcNQomTEX4EsUG1Jxt+huPyoc="; - }; - - # Add new dependencies required in newer versions - propagatedBuildInputs = (oldAttrs.propagatedBuildInputs or []) ++ (with python3Packages; [ - mini-racer - packaging - pydantic - flask-wtf - defusedxml - ]); -})