Add lastpass, bitwarden adapters

This commit is contained in:
Donal McBreen
2024-08-29 15:29:39 +01:00
parent b2e1a4d4c1
commit a726a86a17
3 changed files with 105 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
class Kamal::Secrets::Adapters::Base
delegate :optionize, to: Kamal::Utils
def fetch(secrets, account:, location: nil)
session = login(account)
full_secrets = secrets.map { |secret| [ location, secret ].compact.join("/") }
fetch_from_vault(full_secrets, account: account, session: session)
rescue => e
$stderr.puts " \e[31mERROR (#{e.class}): #{e.message}\e[0m"
$stderr.puts e.backtrace if ENV["VERBOSE"]
Process.kill("INT", Process.ppid) if ENV["KAMAL_SECRETS_KILL_PARENT"]
exit 1
end
private
def login(...)
raise NotImplementedError
end
def fetch_from_vault(...)
raise NotImplementedError
end
end

View File

@@ -0,0 +1,52 @@
class Kamal::Secrets::Adapters::Bitwarden < Kamal::Secrets::Adapters::Base
private
def login(account)
status = run_command("status")
if status["status"] == "unauthenticated"
run_command("login #{account}")
status = run_command("status")
end
if status["status"] == "locked"
session = run_command("unlock --raw", raw: true)
status = run_command("status", session: session)
end
raise RuntimeError, "Failed to login to and unlock Bitwarden" unless status["status"] == "unlocked"
run_command("sync", raw: true)
raise RuntimeError, "Failed to sync Bitwarden" unless $?.success?
session
end
def fetch_from_vault(secrets, account:, session:)
{}.tap do |results|
secrets.each do |secret|
item, field = secret.split("/")
item = run_command("get item #{item}", session: session)
raise RuntimeError, "Could not read #{item} from Bitwarden" unless $?.success?
if field
item_field = item["fields"].find { |f| f["name"] == field }
raise RuntimeError, "Could not find field #{field} in item #{item} in Bitwarden" unless item_field
value = item_field["value"]
results[secret] = value
results[field] = value
else
results[secret] = item["login"]["password"]
end
end
end
end
def signedin?(account)
JSON.parse(`bw status`.strip)["status"] != "unauthenticated"
end
def run_command(command, session: nil, raw: false)
full_command = [ *("BW_SESSION=#{session}" if session), "bw", command ].join(" ")
result = `#{full_command}`.strip
raw ? result : JSON.parse(result)
end
end

View File

@@ -0,0 +1,29 @@
class Kamal::Secrets::Adapters::LastPass < Kamal::Secrets::Adapters::Base
private
def login(account)
unless loggedin?(account)
`lpass login #{account}`
raise RuntimeError, "Failed to login to 1Password" unless $?.success?
end
end
def loggedin?(account)
`lpass status --color never`.strip == "Logged in as #{account}."
end
def fetch_from_vault(secrets, account:, session:)
items = JSON.parse(`lpass show #{secrets.join(" ")} --json`
raise RuntimeError, "Could not read #{fields} from 1Password" unless $?.success?
{}.tap do |results|
items.each do |item|
results[item["name"]] = item["password"]
results[item["fullname"]] = item["password"]
end
if (missing_items = secrets - results.keys).any?
raise RuntimeError, "Could not find #{missing_items.join(", ")} in LassPass"
end
end
end
end