Compare commits

..

5 Commits

4 changed files with 142 additions and 55 deletions

View File

@@ -2,7 +2,7 @@
- install ubuntu 20.04
- get age key from SSH
```console
curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=hetznercloud NIX_CHANNEL=nixos-24.05 bash 2>&1 | tee /tmp/infect.log
curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=hetznercloud NIX_CHANNEL=nixos-25.05 bash 2>&1 | tee /tmp/infect.log
nix-shell -p ssh-to-age --run 'ssh-keyscan install.cloonar.com | ssh-to-age'
```
- fix secrets files

View File

@@ -3,8 +3,8 @@
let
signalDesktopItem = pkgs.makeDesktopItem {
name = "signal-desktop";
desktopName = "Signal";
name = "signal-private";
desktopName = "Signal private";
icon = "signal-desktop";
exec = "signal-desktop --ozone-platform=x11 --enable-features=VaapiVideoDecoder -- %u";
};
@@ -13,7 +13,7 @@ let
name = "signal-work";
desktopName = "Signal with work profile";
icon = "signal-desktop";
exec = "signal-desktop --ozone-platform=x11 --enable-features=VaapiVideoDecoder --enable-dev-tools --user-data-dir=/home/dominik/.config/Signal-work -- %u";
exec = "signal-desktop --ozone-platform=x11 --enable-features=VaapiVideoDecoder --user-data-dir=/home/dominik/.config/Signal-work -- %u";
};
in {
environment.systemPackages = [

View File

@@ -6,9 +6,19 @@ local sops_group = vim.api.nvim_create_augroup("SopsEncryption", { clear = true
-- Pattern matching for secrets files
local secrets_patterns = {
"*/secrets.yaml",
"*secrets*.yaml",
}
-- Size limits to prevent memory issues and UI freezes
-- 5MB encrypted file limit (typical secrets files are <100KB)
local MAX_SOPS_FILE_SIZE = 5 * 1024 * 1024 -- 5MB
-- 10MB decrypted content limit (allows for expansion during decryption)
local MAX_DECRYPTED_SIZE = 10 * 1024 * 1024 -- 10MB
-- Timeout for SOPS operations to prevent infinite hangs
local SOPS_TIMEOUT = 30 -- seconds
-- Guard against double-execution (patterns overlap, causing callback to fire twice)
local currently_saving = {}
-- Helper function to check if file matches secrets pattern
local function is_secrets_file(filepath)
for _, pattern in ipairs(secrets_patterns) do
@@ -38,13 +48,47 @@ vim.api.nvim_create_autocmd("BufReadPost", {
-- Only decrypt if file exists and has content
if vim.fn.filereadable(filepath) == 1 and vim.fn.getfsize(filepath) > 0 then
-- Check file size before attempting to decrypt
local filesize = vim.fn.getfsize(filepath)
if filesize > MAX_SOPS_FILE_SIZE then
local size_mb = string.format("%.1f", filesize / (1024 * 1024))
local limit_mb = string.format("%.1f", MAX_SOPS_FILE_SIZE / (1024 * 1024))
vim.notify(
string.format("SOPS: File too large (%sMB > %sMB limit). Skipping decryption to prevent freeze.", size_mb, limit_mb),
vim.log.levels.WARN
)
return
end
-- Save cursor position
local cursor_pos = vim.api.nvim_win_get_cursor(0)
-- Decrypt file content
local result = vim.fn.system("sops --decrypt " .. vim.fn.shellescape(filepath))
-- Decrypt file content with timeout to prevent hanging
local cmd = string.format("timeout %d sops --decrypt %s", SOPS_TIMEOUT, vim.fn.shellescape(filepath))
local result = vim.fn.system(cmd)
local exit_code = vim.v.shell_error
-- Check for timeout (exit code 124 from timeout command)
if exit_code == 124 then
vim.notify(
string.format("SOPS: Decryption timed out after %d seconds. Check your SOPS configuration and age keys.", SOPS_TIMEOUT),
vim.log.levels.ERROR
)
return
end
if exit_code == 0 then
-- Validate decrypted content size before loading into buffer
if #result > MAX_DECRYPTED_SIZE then
local size_mb = string.format("%.1f", #result / (1024 * 1024))
local limit_mb = string.format("%.1f", MAX_DECRYPTED_SIZE / (1024 * 1024))
vim.notify(
string.format("SOPS: Decrypted content too large (%sMB > %sMB limit). Skipping to prevent freeze.", size_mb, limit_mb),
vim.log.levels.WARN
)
return
end
if vim.v.shell_error == 0 then
-- Replace buffer content with decrypted content
vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(result, "\n"))
@@ -71,70 +115,113 @@ vim.api.nvim_create_autocmd("BufReadPost", {
end,
})
-- Encrypt file before writing
vim.api.nvim_create_autocmd("BufWritePre", {
-- Override write command for secrets files
vim.api.nvim_create_autocmd("BufWriteCmd", {
group = sops_group,
pattern = secrets_patterns,
callback = function(args)
local filepath = vim.fn.expand("%:p")
if is_secrets_file(filepath) then
-- Guard against double-execution
if currently_saving[filepath] then
return
end
currently_saving[filepath] = true
-- Get current buffer content
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local content = table.concat(lines, "\n")
-- Encrypt content using SOPS
local encrypted = vim.fn.system("sops --encrypt /dev/stdin", content)
if vim.v.shell_error == 0 then
-- Write encrypted content directly to file
local file = io.open(filepath, "w")
if file then
file:write(encrypted)
file:close()
-- Mark buffer as saved (prevent Vim from writing again)
vim.bo.modified = false
vim.notify("SOPS: File encrypted and saved successfully", vim.log.levels.INFO)
else
vim.notify("SOPS: Failed to write encrypted file", vim.log.levels.ERROR)
end
else
vim.notify("SOPS: Failed to encrypt file: " .. encrypted, vim.log.levels.ERROR)
-- Prevent write on encryption failure
return true
-- Check buffer content size before encrypting
if #content > MAX_DECRYPTED_SIZE then
local size_mb = string.format("%.1f", #content / (1024 * 1024))
local limit_mb = string.format("%.1f", MAX_DECRYPTED_SIZE / (1024 * 1024))
vim.notify(
string.format("SOPS: Buffer content too large (%sMB > %sMB limit). Cannot encrypt.", size_mb, limit_mb),
vim.log.levels.ERROR
)
-- Don't write anything, leave buffer marked as modified
currently_saving[filepath] = nil
return
end
-- Prevent default write behavior since we handled it
return true
end
end,
})
-- Encrypt content using SOPS via temporary file in same directory
-- This avoids /dev/stdin issues while keeping secrets secure (not in /tmp)
local dir = vim.fn.fnamemodify(filepath, ":h")
local filename = vim.fn.fnamemodify(filepath, ":t")
local temp_file = string.format("%s/.%s.sops_tmp_%d", dir, filename, os.time())
-- Re-decrypt after writing to show plaintext in buffer
vim.api.nvim_create_autocmd("BufWritePost", {
group = sops_group,
pattern = secrets_patterns,
callback = function(args)
local filepath = vim.fn.expand("%:p")
-- Write plaintext content to temp file
local temp_f, temp_err = io.open(temp_file, "w")
if not temp_f then
vim.notify("SOPS: Failed to create temp file: " .. (temp_err or "unknown error"), vim.log.levels.ERROR)
-- Don't write anything, leave buffer marked as modified
currently_saving[filepath] = nil
return
end
temp_f:write(content)
temp_f:close()
if is_secrets_file(filepath) and vim.fn.filereadable(filepath) == 1 then
-- Decrypt and reload buffer content
local result = vim.fn.system("sops --decrypt " .. vim.fn.shellescape(filepath))
-- Encrypt temp file with filename override so SOPS matches .sops.yaml rules
-- Uses real filepath for rule matching, temp file for content
local cmd = string.format("sops --encrypt --filename-override %s %s",
vim.fn.shellescape(filepath),
vim.fn.shellescape(temp_file))
local encrypted = vim.fn.system(cmd)
local sops_exit_code = vim.v.shell_error
if vim.v.shell_error == 0 then
-- Save cursor position
local cursor_pos = vim.api.nvim_win_get_cursor(0)
-- Always clean up temp file, even on error
os.remove(temp_file)
-- Replace buffer with decrypted content
vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(result, "\n"))
if sops_exit_code == 0 then
-- Write encrypted content directly to file
local file, err = io.open(filepath, "w")
if file then
local success, write_err = file:write(encrypted)
file:close()
-- Mark as not modified
vim.bo.modified = false
if success then
-- Mark buffer as saved
vim.bo.modified = false
vim.notify("SOPS: File encrypted and saved successfully", vim.log.levels.INFO)
-- Restore cursor position
pcall(vim.api.nvim_win_set_cursor, 0, cursor_pos)
-- Re-decrypt to show plaintext in buffer
local decrypt_cmd = string.format("timeout %d sops --decrypt %s", SOPS_TIMEOUT, vim.fn.shellescape(filepath))
local decrypted = vim.fn.system(decrypt_cmd)
local decrypt_exit = vim.v.shell_error
if decrypt_exit == 0 then
-- Save cursor position
local cursor_pos = vim.api.nvim_win_get_cursor(0)
-- Replace buffer with decrypted content
vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(decrypted, "\n"))
-- Mark as not modified since we just saved
vim.bo.modified = false
-- Restore cursor position
pcall(vim.api.nvim_win_set_cursor, 0, cursor_pos)
else
vim.notify("SOPS: Could not re-decrypt after save. Buffer may show encrypted content.", vim.log.levels.WARN)
end
-- Clear guard after successful save
currently_saving[filepath] = nil
else
vim.notify("SOPS: Failed to write encrypted content: " .. (write_err or "unknown error"), vim.log.levels.ERROR)
-- Don't mark as saved, keep buffer marked as modified
currently_saving[filepath] = nil
end
else
vim.notify("SOPS: Failed to open file for writing: " .. (err or "unknown error"), vim.log.levels.ERROR)
-- Don't mark as saved, keep buffer marked as modified
currently_saving[filepath] = nil
end
else
vim.notify("SOPS: Failed to encrypt file - NOT SAVED! Error: " .. encrypted, vim.log.levels.ERROR)
-- Don't write anything, leave buffer marked as modified
currently_saving[filepath] = nil
end
end
end,

View File

@@ -41,7 +41,7 @@ local config = {
-- { vim.o.shell, "<M-2>", "Vertical Terminal", "vertical", 0.4 },
{ vim.o.shell, "<M-1>", "Float Terminal 1", "float", nil },
{ vim.o.shell, "<M-2>", "Float Terminal 2", "float", nil },
{ vim.o.shell, "<M-3>", "Float Terminal 3", "float", nil },
{ "claude", "<M-3>", "Claude Terminal", "float", nil },
{ vim.o.shell, "<M-4>", "Float Terminal 4", "float", nil },
{ vim.o.shell, "<M-5>", "Float Terminal 5", "float", nil },
},