Bitwarden Secrets Manager as a secret provider via an operator-installed bws client

This commit is contained in:
jarek committed 2026-08-19 14:26:45 +02:00
1 parent 2584e52852
commit 29795c074c
3 files changed
+48 -91

No files matched your search

+12 -2
View File
@@ -2,6 +2,7 @@
import { Label } from '$lib/components/ui/label';
import { Input } from '$lib/components/ui/input';
import * as Select from '$lib/components/ui/select';
import { getProviderIcon } from '$lib/components/provider-icons';
import { BULK_SELECTOR_FIELDS } from '../../routes/settings/secrets/ProviderModal.svelte';
import { SELECTOR_VARS, BULK_SELECTOR_VAR } from '$lib/utils/bulk-selector';
import type { EnvVar } from '$lib/components/StackEnvVarsEditor.svelte';
@@ -58,7 +59,12 @@
>
<Select.Trigger id="secret-provider-select{idSuffix}" class="h-7 text-xs flex-1 min-w-0 max-w-xs overflow-hidden">
{#if secretProviderId !== null}
<span class="truncate min-w-0">{providers.find((p) => p.id === secretProviderId)?.name ?? 'Unknown provider'}</span>
{@const sel = providers.find((p) => p.id === secretProviderId)}
{@const SelIcon = sel ? getProviderIcon(sel.type) : null}
<span class="flex items-center gap-2 min-w-0">
{#if SelIcon}<SelIcon class="h-4 w-4 shrink-0 text-muted-foreground" />{/if}
<span class="truncate min-w-0">{sel?.name ?? 'Unknown provider'}</span>
</span>
{:else}
<span class="text-muted-foreground truncate">None — disabled</span>
{/if}
@@ -68,8 +74,12 @@
<span class="text-muted-foreground">None — disabled</span>
</Select.Item>
{#each providers as provider (provider.id)}
{@const ProviderIcon = getProviderIcon(provider.type)}
<Select.Item value={String(provider.id)} label={provider.name}>
{provider.name}
<span class="flex items-center gap-2 min-w-0">
{#if ProviderIcon}<ProviderIcon class="h-4 w-4 shrink-0 text-muted-foreground" />{/if}
<span class="truncate">{provider.name}</span>
</span>
</Select.Item>
{/each}
</Select.Content>
@@ -5,80 +5,16 @@
<svg
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
fill-rule="evenodd"
clip-rule="evenodd"
stroke-linejoin="round"
stroke-miterlimit="2"
xml:space="preserve"
class={className}
fill="currentColor"
aria-label="Proton Pass"
>
<g transform="matrix(13.61103 0 0 13.61103 11 11)">
<clipPath id="protonpass__a"><path d="M0 0h36v36H0z" /></clipPath>
<g clip-path="url(#protonpass__a)" fill-rule="nonzero">
<path
d="M10.826 4.548C13.337 2.036 14.592.781 16.04.31a6.356 6.356 0 013.92 0c1.448.471 2.703 1.726 5.214 4.238l6.278 6.278c2.512 2.511 3.767 3.766 4.238 5.214a6.356 6.356 0 010 3.92c-.471 1.448-1.726 2.703-4.238 5.214l-6.278 6.278c-2.511 2.512-3.766 3.767-5.214 4.238a6.356 6.356 0 01-3.92 0c-1.448-.471-2.703-1.726-5.214-4.238l-1.181-1.323c-.714-.801-1.071-1.201-1.326-1.656a4.777 4.777 0 01-.492-1.291c-.113-.509-.113-1.045-.113-2.118V10.936c0-1.073 0-1.609.113-2.118.101-.452.266-.887.492-1.291.255-.455.612-.855 1.326-1.656l1.181-1.323z"
fill="url(#protonpass__radial2)"
/>
<path
d="M10.826 4.548C13.337 2.036 14.592.781 16.04.31a6.356 6.356 0 013.92 0c1.448.471 2.703 1.726 5.214 4.238l6.278 6.278c2.512 2.511 3.767 3.766 4.238 5.214a6.356 6.356 0 010 3.92c-.471 1.448-1.726 2.703-4.238 5.214l-6.278 6.278c-2.511 2.512-3.766 3.767-5.214 4.238a6.356 6.356 0 01-3.92 0c-1.448-.471-2.703-1.726-5.214-4.238l-1.181-1.323c-.714-.801-1.071-1.201-1.326-1.656a4.777 4.777 0 01-.492-1.291c-.113-.509-.113-1.045-.113-2.118V10.936c0-1.073 0-1.609.113-2.118.101-.452.266-.887.492-1.291.255-.455.612-.855 1.326-1.656l1.181-1.323z"
fill="url(#protonpass__linear3)"
/>
<path
d="M10.377 4.996c1.256-1.256 1.884-1.884 2.608-2.119a3.168 3.168 0 011.959 0c.724.235 1.352.863 2.608 2.119l9.416 9.416c1.256 1.256 1.884 1.884 2.119 2.608a3.168 3.168 0 010 1.959c-.235.724-.863 1.352-2.119 2.608l-9.416 9.416c-1.256 1.256-1.884 1.884-2.608 2.119a3.168 3.168 0 01-1.959 0c-.724-.235-1.352-.863-2.608-2.119l-5.829-5.829C2.036 22.663.781 21.407.31 19.959a6.353 6.353 0 010-3.919c.471-1.448 1.726-2.704 4.238-5.215l5.829-5.829z"
fill="url(#protonpass__radial4)"
/>
</g>
</g>
<defs>
<radialGradient
id="protonpass__radial2"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(15.8615 -25.5243 39.9147 24.8041 10.865 36.702)"
>
<stop offset="0" stop-color="#ffd580" />
<stop offset=".09" stop-color="#f6c592" />
<stop offset=".21" stop-color="#ebb6a2" />
<stop offset=".32" stop-color="#dfa5af" />
<stop offset=".43" stop-color="#d397be" />
<stop offset=".53" stop-color="#c486cb" />
<stop offset=".65" stop-color="#b578d9" />
<stop offset=".77" stop-color="#a166e5" />
<stop offset=".89" stop-color="#8b57f2" />
<stop offset="1" stop-color="#704cff" />
</radialGradient>
<radialGradient
id="protonpass__radial4"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12.7582 -20.5304 32.1053 19.9512 8.225 33.042)"
>
<stop offset="0" stop-color="#ffd580" />
<stop offset=".09" stop-color="#f6c592" />
<stop offset=".21" stop-color="#ebb6a2" />
<stop offset=".32" stop-color="#dfa5af" />
<stop offset=".43" stop-color="#d397be" />
<stop offset=".53" stop-color="#c486cb" />
<stop offset=".65" stop-color="#b578d9" />
<stop offset=".77" stop-color="#a166e5" />
<stop offset=".89" stop-color="#8b57f2" />
<stop offset="1" stop-color="#704cff" />
</radialGradient>
<linearGradient
id="protonpass__linear3"
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="scale(43.30457) rotate(80.696 .208 .048)"
>
<stop offset="0" stop-color="#6d4aff" />
<stop offset=".39" stop-color="#b39ffb" stop-opacity=".98" />
<stop offset="1" stop-color="#ffe8db" stop-opacity=".8" />
</linearGradient>
</defs>
<path
d="M154 64.7c35.7-35.7 53.6-53.6 74.2-60.3 18.1-5.9 37.6-5.9 55.7 0 20.6 6.7 38.4 24.5 74.2 60.3l89.3 89.3c35.7 35.7 53.6 53.6 60.3 74.2 5.9 18.1 5.9 37.6 0 55.7-6.7 20.6-24.6 38.4-60.3 74.2L358 447.3c-35.7 35.7-53.6 53.6-74.2 60.3-18.1 5.9-37.6 5.9-55.7 0-20.6-6.7-38.5-24.6-74.2-60.3l-16.8-18.8c-10.2-11.4-15.2-17.1-18.9-23.6-3.2-5.7-5.6-11.9-7-18.4-1.6-7.2-1.6-14.9-1.6-30.1V155.5c0-15.3 0-22.9 1.6-30.1 1.4-6.4 3.8-12.6 7-18.3 3.6-6.5 8.7-12.2 18.9-23.6z"
style="opacity:.5"
/>
<path
d="M147.6 71.1c17.9-17.9 26.8-26.8 37.1-30.1 9.1-2.9 18.8-2.9 27.9 0 10.3 3.3 19.2 12.3 37.1 30.1L383.5 205c17.9 17.9 26.8 26.8 30.1 37.1 2.9 9.1 2.9 18.8 0 27.9-3.3 10.3-12.3 19.2-30.1 37.1L249.6 440.9c-17.9 17.9-26.8 26.8-37.1 30.1-9.1 2.9-18.8 2.9-27.9 0-10.3-3.3-19.2-12.3-37.1-30.1L64.7 358C29 322.3 11.1 304.5 4.4 283.9c-5.9-18.1-5.9-37.6 0-55.7 6.7-20.6 24.5-38.5 60.3-74.2z"
/>
</svg>
@@ -117,7 +117,7 @@
import * as Select from '$lib/components/ui/select';
import { FieldLabel } from '$lib/components/ui/field-label';
import { Input } from '$lib/components/ui/input';
import { Plus, Check, RefreshCw, PlugZap, KeyRound } from 'lucide-svelte';
import { Plus, Check, RefreshCw, PlugZap, KeyRound, Info } from 'lucide-svelte';
import { scale } from 'svelte/transition';
import { backOut, cubicIn } from 'svelte/easing';
import { getProviderIcon } from '$lib/components/provider-icons';
@@ -429,20 +429,31 @@
</div>
{/each}
</div>
{#if formType === 'bitwarden'}
<p class="text-xs text-muted-foreground">
Bitwarden Secrets Manager requires an externally installed or mounted official
<code>bws</code> client at <code>/usr/local/bin/bws</code> (or an absolute
<code>DOCKHAND_BWS_PATH</code> process override).
</p>
{/if}
{#if formType === 'proton'}
<p class="text-xs text-muted-foreground">
Proton Pass requires an externally installed or mounted official
<code>pass-cli</code> client at <code>/usr/local/bin/pass-cli</code> (or an absolute
<code>DOCKHAND_PASS_CLI_PATH</code> process override). Supports both a bulk vault pull
and inline <code>pass://</code> references.
</p>
{#if formType === 'bitwarden' || formType === 'proton'}
<!-- Fixed min-height so switching between the bitwarden (shorter) and proton
(taller) external-CLI notes doesn't jump the dialog's vertical size. -->
<div class="min-h-16">
{#if formType === 'bitwarden'}
<p class="flex items-start gap-2 text-xs text-muted-foreground">
<Info class="h-3.5 w-3.5 shrink-0 mt-0.5 text-amber-500" />
<span>
Bitwarden Secrets Manager requires an externally installed or mounted official
<code>bws</code> client at <code>/usr/local/bin/bws</code> (or an absolute
<code>DOCKHAND_BWS_PATH</code> process override).
</span>
</p>
{:else}
<p class="flex items-start gap-2 text-xs text-muted-foreground">
<Info class="h-3.5 w-3.5 shrink-0 mt-0.5 text-amber-500" />
<span>
Proton Pass requires an externally installed or mounted official
<code>pass-cli</code> client at <code>/usr/local/bin/pass-cli</code> (or an absolute
<code>DOCKHAND_PASS_CLI_PATH</code> process override). Supports both a bulk vault pull
and inline <code>pass://</code> references.
</span>
</p>
{/if}
</div>
{/if}
<p class="text-xs text-muted-foreground">
Configuration is stored encrypted.{#if isEditing}