Monorepo for Tangled tangled.org

spindle/secrets: rework openbao manager to use bao proxy

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

authored by anirudh.fi and committed by Tangled cccf0b0b 60437654

Changed files
+133 -255
spindle
+2 -4
spindle/config/config.go
··· 28 28 } 29 29 30 30 type OpenBaoConfig struct { 31 - Addr string `env:"ADDR"` 32 - RoleID string `env:"ROLE_ID"` 33 - SecretID string `env:"SECRET_ID"` 34 - Mount string `env:"MOUNT, default=spindle"` 31 + ProxyAddr string `env:"PROXY_ADDR, default=http://127.0.0.1:8200"` 32 + Mount string `env:"MOUNT, default=spindle"` 35 33 } 36 34 37 35 type Pipelines struct {
+55 -149
spindle/secrets/openbao.go
··· 6 6 "log/slog" 7 7 "path" 8 8 "strings" 9 - "sync" 10 9 "time" 11 10 12 11 "github.com/bluesky-social/indigo/atproto/syntax" ··· 16 15 type OpenBaoManager struct { 17 16 client *vault.Client 18 17 mountPath string 19 - roleID string 20 - secretID string 21 - stopCh chan struct{} 22 - tokenMu sync.RWMutex 23 18 logger *slog.Logger 24 19 } 25 20 ··· 31 26 } 32 27 } 33 28 34 - func NewOpenBaoManager(address, roleID, secretID string, logger *slog.Logger, opts ...OpenBaoManagerOpt) (*OpenBaoManager, error) { 35 - if address == "" { 36 - return nil, fmt.Errorf("address cannot be empty") 37 - } 38 - if roleID == "" { 39 - return nil, fmt.Errorf("role_id cannot be empty") 40 - } 41 - if secretID == "" { 42 - return nil, fmt.Errorf("secret_id cannot be empty") 29 + // NewOpenBaoManager creates a new OpenBao manager that connects to a Bao Proxy 30 + // The proxyAddress should point to the local Bao Proxy (e.g., "http://127.0.0.1:8200") 31 + // The proxy handles all authentication automatically via Auto-Auth 32 + func NewOpenBaoManager(proxyAddress string, logger *slog.Logger, opts ...OpenBaoManagerOpt) (*OpenBaoManager, error) { 33 + if proxyAddress == "" { 34 + return nil, fmt.Errorf("proxy address cannot be empty") 43 35 } 44 36 45 37 config := vault.DefaultConfig() 46 - config.Address = address 38 + config.Address = proxyAddress 47 39 48 40 client, err := vault.NewClient(config) 49 41 if err != nil { 50 42 return nil, fmt.Errorf("failed to create openbao client: %w", err) 51 43 } 52 44 53 - // Authenticate using AppRole 54 - err = authenticateAppRole(client, roleID, secretID) 55 - if err != nil { 56 - return nil, fmt.Errorf("failed to authenticate with AppRole: %w", err) 57 - } 58 - 59 45 manager := &OpenBaoManager{ 60 46 client: client, 61 47 mountPath: "spindle", // default KV v2 mount path 62 - roleID: roleID, 63 - secretID: secretID, 64 - stopCh: make(chan struct{}), 65 48 logger: logger, 66 49 } 67 50 ··· 69 52 opt(manager) 70 53 } 71 54 72 - go manager.tokenRenewalLoop() 73 - 74 - return manager, nil 75 - } 76 - 77 - // authenticateAppRole authenticates the client using AppRole method 78 - func authenticateAppRole(client *vault.Client, roleID, secretID string) error { 79 - authData := map[string]interface{}{ 80 - "role_id": roleID, 81 - "secret_id": secretID, 82 - } 83 - 84 - resp, err := client.Logical().Write("auth/approle/login", authData) 85 - if err != nil { 86 - return fmt.Errorf("failed to login with AppRole: %w", err) 87 - } 88 - 89 - if resp == nil || resp.Auth == nil { 90 - return fmt.Errorf("no auth info returned from AppRole login") 91 - } 92 - 93 - client.SetToken(resp.Auth.ClientToken) 94 - return nil 95 - } 96 - 97 - // stop stops the token renewal goroutine 98 - func (v *OpenBaoManager) Stop() { 99 - close(v.stopCh) 100 - } 101 - 102 - // tokenRenewalLoop runs in a background goroutine to automatically renew or re-authenticate tokens 103 - func (v *OpenBaoManager) tokenRenewalLoop() { 104 - ticker := time.NewTicker(30 * time.Second) // Check every 30 seconds 105 - defer ticker.Stop() 106 - 107 - for { 108 - select { 109 - case <-v.stopCh: 110 - return 111 - case <-ticker.C: 112 - ctx := context.Background() 113 - if err := v.ensureValidToken(ctx); err != nil { 114 - v.logger.Error("openbao token renewal failed", "error", err) 115 - } 116 - } 117 - } 118 - } 119 - 120 - // ensureValidToken checks if the current token is valid and renews or re-authenticates if needed 121 - func (v *OpenBaoManager) ensureValidToken(ctx context.Context) error { 122 - v.tokenMu.Lock() 123 - defer v.tokenMu.Unlock() 124 - 125 - // check current token info 126 - tokenInfo, err := v.client.Auth().Token().LookupSelf() 127 - if err != nil { 128 - // token is invalid, need to re-authenticate 129 - v.logger.Warn("token lookup failed, re-authenticating", "error", err) 130 - return v.reAuthenticate() 131 - } 132 - 133 - if tokenInfo == nil || tokenInfo.Data == nil { 134 - return v.reAuthenticate() 135 - } 136 - 137 - // check TTL 138 - ttlRaw, ok := tokenInfo.Data["ttl"] 139 - if !ok { 140 - return v.reAuthenticate() 141 - } 142 - 143 - var ttl int64 144 - switch t := ttlRaw.(type) { 145 - case int64: 146 - ttl = t 147 - case float64: 148 - ttl = int64(t) 149 - case int: 150 - ttl = int64(t) 151 - default: 152 - return v.reAuthenticate() 55 + if err := manager.testConnection(); err != nil { 56 + return nil, fmt.Errorf("failed to connect to bao proxy: %w", err) 153 57 } 154 58 155 - // if TTL is less than 5 minutes, try to renew 156 - if ttl < 300 { 157 - v.logger.Info("token ttl low, attempting renewal", "ttl_seconds", ttl) 158 - 159 - renewResp, err := v.client.Auth().Token().RenewSelf(3600) // 1h 160 - if err != nil { 161 - v.logger.Warn("token renewal failed, re-authenticating", "error", err) 162 - return v.reAuthenticate() 163 - } 164 - 165 - if renewResp == nil || renewResp.Auth == nil { 166 - v.logger.Warn("token renewal returned no auth info, re-authenticating") 167 - return v.reAuthenticate() 168 - } 169 - 170 - v.logger.Info("token renewed successfully", "new_ttl_seconds", renewResp.Auth.LeaseDuration) 171 - } 172 - 173 - return nil 59 + logger.Info("successfully connected to bao proxy", "address", proxyAddress) 60 + return manager, nil 174 61 } 175 62 176 - // reAuthenticate performs a fresh authentication using AppRole 177 - func (v *OpenBaoManager) reAuthenticate() error { 178 - v.logger.Info("re-authenticating with approle") 63 + // testConnection verifies that we can connect to the proxy 64 + func (v *OpenBaoManager) testConnection() error { 65 + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 66 + defer cancel() 179 67 180 - err := authenticateAppRole(v.client, v.roleID, v.secretID) 68 + // try token self-lookup as a quick way to verify proxy works 69 + // and is authenticated 70 + _, err := v.client.Auth().Token().LookupSelfWithContext(ctx) 181 71 if err != nil { 182 - return fmt.Errorf("re-authentication failed: %w", err) 72 + return fmt.Errorf("proxy connection test failed: %w", err) 183 73 } 184 74 185 - v.logger.Info("re-authentication successful") 186 75 return nil 187 76 } 188 77 189 78 func (v *OpenBaoManager) AddSecret(ctx context.Context, secret UnlockedSecret) error { 190 - v.tokenMu.RLock() 191 - defer v.tokenMu.RUnlock() 192 79 if err := ValidateKey(secret.Key); err != nil { 193 80 return err 194 81 } 195 82 196 83 secretPath := v.buildSecretPath(secret.Repo, secret.Key) 197 - 198 - fmt.Println(v.mountPath, secretPath) 84 + v.logger.Debug("adding secret", "repo", secret.Repo, "key", secret.Key, "path", secretPath) 199 85 86 + // Check if secret already exists 200 87 existing, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath) 201 88 if err == nil && existing != nil { 89 + v.logger.Debug("secret already exists", "path", secretPath) 202 90 return ErrKeyAlreadyPresent 203 91 } 204 92 ··· 210 98 "created_by": secret.CreatedBy.String(), 211 99 } 212 100 213 - _, err = v.client.KVv2(v.mountPath).Put(ctx, secretPath, secretData) 101 + v.logger.Debug("writing secret to openbao", "path", secretPath, "mount", v.mountPath) 102 + resp, err := v.client.KVv2(v.mountPath).Put(ctx, secretPath, secretData) 214 103 if err != nil { 104 + v.logger.Error("failed to write secret", "path", secretPath, "error", err) 215 105 return fmt.Errorf("failed to store secret in openbao: %w", err) 216 106 } 217 107 108 + v.logger.Debug("secret write response", "version", resp.VersionMetadata.Version, "created_time", resp.VersionMetadata.CreatedTime) 109 + 110 + v.logger.Debug("verifying secret was written", "path", secretPath) 111 + readBack, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath) 112 + if err != nil { 113 + v.logger.Error("failed to verify secret after write", "path", secretPath, "error", err) 114 + return fmt.Errorf("secret not found after writing to %s/%s: %w", v.mountPath, secretPath, err) 115 + } 116 + 117 + if readBack == nil || readBack.Data == nil { 118 + v.logger.Error("secret verification returned empty data", "path", secretPath) 119 + return fmt.Errorf("secret verification failed: empty data returned for %s/%s", v.mountPath, secretPath) 120 + } 121 + 122 + v.logger.Info("secret added and verified successfully", "repo", secret.Repo, "key", secret.Key, "version", readBack.VersionMetadata.Version) 218 123 return nil 219 124 } 220 125 221 126 func (v *OpenBaoManager) RemoveSecret(ctx context.Context, secret Secret[any]) error { 222 - v.tokenMu.RLock() 223 - defer v.tokenMu.RUnlock() 224 127 secretPath := v.buildSecretPath(secret.Repo, secret.Key) 225 128 129 + // check if secret exists 226 130 existing, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath) 227 131 if err != nil || existing == nil { 228 132 return ErrKeyNotFound ··· 233 137 return fmt.Errorf("failed to delete secret from openbao: %w", err) 234 138 } 235 139 140 + v.logger.Debug("secret removed successfully", "repo", secret.Repo, "key", secret.Key) 236 141 return nil 237 142 } 238 143 239 144 func (v *OpenBaoManager) GetSecretsLocked(ctx context.Context, repo DidSlashRepo) ([]LockedSecret, error) { 240 - v.tokenMu.RLock() 241 - defer v.tokenMu.RUnlock() 242 145 repoPath := v.buildRepoPath(repo) 243 146 244 - secretsList, err := v.client.Logical().List(fmt.Sprintf("%s/metadata/%s", v.mountPath, repoPath)) 147 + secretsList, err := v.client.Logical().ListWithContext(ctx, fmt.Sprintf("%s/metadata/%s", v.mountPath, repoPath)) 245 148 if err != nil { 246 149 if strings.Contains(err.Error(), "no secret found") || strings.Contains(err.Error(), "no handler for route") { 247 150 return []LockedSecret{}, nil ··· 266 169 continue 267 170 } 268 171 269 - secretPath := path.Join(repoPath, key) 172 + secretPath := fmt.Sprintf("%s/%s", repoPath, key) 270 173 secretData, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath) 271 174 if err != nil { 272 - continue // Skip secrets we can't read 175 + v.logger.Warn("failed to read secret metadata", "path", secretPath, "error", err) 176 + continue 273 177 } 274 178 275 179 if secretData == nil || secretData.Data == nil { ··· 308 212 secrets = append(secrets, secret) 309 213 } 310 214 215 + v.logger.Debug("retrieved locked secrets", "repo", repo, "count", len(secrets)) 311 216 return secrets, nil 312 217 } 313 218 314 219 func (v *OpenBaoManager) GetSecretsUnlocked(ctx context.Context, repo DidSlashRepo) ([]UnlockedSecret, error) { 315 - v.tokenMu.RLock() 316 - defer v.tokenMu.RUnlock() 317 220 repoPath := v.buildRepoPath(repo) 318 221 319 - secretsList, err := v.client.Logical().List(fmt.Sprintf("%s/metadata/%s", v.mountPath, repoPath)) 222 + secretsList, err := v.client.Logical().ListWithContext(ctx, fmt.Sprintf("%s/metadata/%s", v.mountPath, repoPath)) 320 223 if err != nil { 321 224 if strings.Contains(err.Error(), "no secret found") || strings.Contains(err.Error(), "no handler for route") { 322 225 return []UnlockedSecret{}, nil ··· 341 244 continue 342 245 } 343 246 344 - secretPath := path.Join(repoPath, key) 247 + secretPath := fmt.Sprintf("%s/%s", repoPath, key) 345 248 secretData, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath) 346 249 if err != nil { 250 + v.logger.Warn("failed to read secret", "path", secretPath, "error", err) 347 251 continue 348 252 } 349 253 ··· 355 259 356 260 valueStr, ok := data["value"].(string) 357 261 if !ok { 358 - continue // skip secrets without values 262 + v.logger.Warn("secret missing value", "path", secretPath) 263 + continue 359 264 } 360 265 361 266 createdAtStr, ok := data["created_at"].(string) ··· 389 294 secrets = append(secrets, secret) 390 295 } 391 296 297 + v.logger.Debug("retrieved unlocked secrets", "repo", repo, "count", len(secrets)) 392 298 return secrets, nil 393 299 } 394 300 395 - // buildRepoPath creates an OpenBao path for a repository 301 + // buildRepoPath creates a safe path for a repository 396 302 func (v *OpenBaoManager) buildRepoPath(repo DidSlashRepo) string { 397 303 // convert DidSlashRepo to a safe path by replacing special characters 398 304 repoPath := strings.ReplaceAll(string(repo), "/", "_") ··· 401 307 return fmt.Sprintf("repos/%s", repoPath) 402 308 } 403 309 404 - // buildSecretPath creates an OpenBao path for a specific secret 310 + // buildSecretPath creates a path for a specific secret 405 311 func (v *OpenBaoManager) buildSecretPath(repo DidSlashRepo, key string) string { 406 312 return path.Join(v.buildRepoPath(repo), key) 407 313 }
+59 -84
spindle/secrets/openbao_test.go
··· 16 16 secrets map[string]UnlockedSecret // key: repo_key format 17 17 shouldError bool 18 18 errorToReturn error 19 - stopped bool 20 19 } 21 20 22 21 func NewMockOpenBaoManager() *MockOpenBaoManager { ··· 31 30 func (m *MockOpenBaoManager) ClearError() { 32 31 m.shouldError = false 33 32 m.errorToReturn = nil 34 - } 35 - 36 - func (m *MockOpenBaoManager) Stop() { 37 - m.stopped = true 38 - } 39 - 40 - func (m *MockOpenBaoManager) IsStopped() bool { 41 - return m.stopped 42 33 } 43 34 44 35 func (m *MockOpenBaoManager) buildKey(repo DidSlashRepo, key string) string { ··· 118 109 } 119 110 } 120 111 112 + // Test MockOpenBaoManager interface compliance 113 + func TestMockOpenBaoManagerInterface(t *testing.T) { 114 + var _ Manager = (*MockOpenBaoManager)(nil) 115 + } 116 + 121 117 func TestOpenBaoManagerInterface(t *testing.T) { 122 118 var _ Manager = (*OpenBaoManager)(nil) 123 119 } ··· 125 121 func TestNewOpenBaoManager(t *testing.T) { 126 122 tests := []struct { 127 123 name string 128 - address string 129 - roleID string 130 - secretID string 124 + proxyAddr string 131 125 opts []OpenBaoManagerOpt 132 126 expectError bool 133 127 errorContains string 134 128 }{ 135 129 { 136 - name: "empty address", 137 - address: "", 138 - roleID: "test-role-id", 139 - secretID: "test-secret-id", 130 + name: "empty proxy address", 131 + proxyAddr: "", 140 132 opts: nil, 141 133 expectError: true, 142 - errorContains: "address cannot be empty", 134 + errorContains: "proxy address cannot be empty", 143 135 }, 144 136 { 145 - name: "empty role_id", 146 - address: "http://localhost:8200", 147 - roleID: "", 148 - secretID: "test-secret-id", 137 + name: "valid proxy address", 138 + proxyAddr: "http://localhost:8200", 149 139 opts: nil, 150 - expectError: true, 151 - errorContains: "role_id cannot be empty", 140 + expectError: true, // Will fail because no real proxy is running 141 + errorContains: "failed to connect to bao proxy", 152 142 }, 153 143 { 154 - name: "empty secret_id", 155 - address: "http://localhost:8200", 156 - roleID: "test-role-id", 157 - secretID: "", 158 - opts: nil, 159 - expectError: true, 160 - errorContains: "secret_id cannot be empty", 144 + name: "with mount path option", 145 + proxyAddr: "http://localhost:8200", 146 + opts: []OpenBaoManagerOpt{WithMountPath("custom-mount")}, 147 + expectError: true, // Will fail because no real proxy is running 148 + errorContains: "failed to connect to bao proxy", 161 149 }, 162 150 } 163 151 164 152 for _, tt := range tests { 165 153 t.Run(tt.name, func(t *testing.T) { 166 154 logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) 167 - manager, err := NewOpenBaoManager(tt.address, tt.roleID, tt.secretID, logger, tt.opts...) 155 + manager, err := NewOpenBaoManager(tt.proxyAddr, logger, tt.opts...) 168 156 169 157 if tt.expectError { 170 158 assert.Error(t, err) 171 159 assert.Nil(t, manager) 172 160 assert.Contains(t, err.Error(), tt.errorContains) 173 161 } else { 174 - // For valid configurations, we expect an error during authentication 175 - // since we're not connecting to a real OpenBao server 176 - assert.Error(t, err) 177 - assert.Nil(t, manager) 162 + assert.NoError(t, err) 163 + assert.NotNil(t, manager) 178 164 } 179 165 }) 180 166 } ··· 253 239 assert.Equal(t, "custom-mount", manager.mountPath) 254 240 } 255 241 256 - func TestOpenBaoManager_Stop(t *testing.T) { 257 - // Create a manager with minimal setup 258 - manager := &OpenBaoManager{ 259 - mountPath: "test", 260 - stopCh: make(chan struct{}), 261 - } 262 - 263 - // Verify the manager implements Stopper interface 264 - var stopper Stopper = manager 265 - assert.NotNil(t, stopper) 266 - 267 - // Call Stop and verify it doesn't panic 268 - assert.NotPanics(t, func() { 269 - manager.Stop() 270 - }) 271 - 272 - // Verify the channel was closed 273 - select { 274 - case <-manager.stopCh: 275 - // Channel was closed as expected 276 - default: 277 - t.Error("Expected stop channel to be closed after Stop()") 278 - } 279 - } 280 - 281 - func TestOpenBaoManager_StopperInterface(t *testing.T) { 282 - manager := &OpenBaoManager{} 283 - 284 - // Verify that OpenBaoManager implements the Stopper interface 285 - _, ok := interface{}(manager).(Stopper) 286 - assert.True(t, ok, "OpenBaoManager should implement Stopper interface") 287 - } 288 - 289 - // Test MockOpenBaoManager interface compliance 290 - func TestMockOpenBaoManagerInterface(t *testing.T) { 291 - var _ Manager = (*MockOpenBaoManager)(nil) 292 - var _ Stopper = (*MockOpenBaoManager)(nil) 293 - } 294 - 295 242 func TestMockOpenBaoManager_AddSecret(t *testing.T) { 296 243 tests := []struct { 297 244 name string ··· 563 510 assert.NoError(t, err) 564 511 } 565 512 566 - func TestMockOpenBaoManager_Stop(t *testing.T) { 567 - mock := NewMockOpenBaoManager() 568 - 569 - assert.False(t, mock.IsStopped()) 570 - 571 - mock.Stop() 572 - 573 - assert.True(t, mock.IsStopped()) 574 - } 575 - 576 513 func TestMockOpenBaoManager_Integration(t *testing.T) { 577 514 tests := []struct { 578 515 name string ··· 628 565 }) 629 566 } 630 567 } 568 + 569 + func TestOpenBaoManager_ProxyConfiguration(t *testing.T) { 570 + tests := []struct { 571 + name string 572 + proxyAddr string 573 + description string 574 + }{ 575 + { 576 + name: "default_localhost", 577 + proxyAddr: "http://127.0.0.1:8200", 578 + description: "Should connect to default localhost proxy", 579 + }, 580 + { 581 + name: "custom_host", 582 + proxyAddr: "http://bao-proxy:8200", 583 + description: "Should connect to custom proxy host", 584 + }, 585 + { 586 + name: "https_proxy", 587 + proxyAddr: "https://127.0.0.1:8200", 588 + description: "Should connect to HTTPS proxy", 589 + }, 590 + } 591 + 592 + for _, tt := range tests { 593 + t.Run(tt.name, func(t *testing.T) { 594 + t.Log("Testing scenario:", tt.description) 595 + logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) 596 + 597 + // All these will fail because no real proxy is running 598 + // but we can test that the configuration is properly accepted 599 + manager, err := NewOpenBaoManager(tt.proxyAddr, logger) 600 + assert.Error(t, err) // Expected because no real proxy 601 + assert.Nil(t, manager) 602 + assert.Contains(t, err.Error(), "failed to connect to bao proxy") 603 + }) 604 + } 605 + }
+13 -6
spindle/secrets/policy.hcl
··· 1 - # KV v2 data operations 2 - path "spindle/data/*" { 1 + # Allow full access to the spindle KV mount 2 + path "spindle/*" { 3 3 capabilities = ["create", "read", "update", "delete", "list"] 4 4 } 5 5 6 - # KV v2 metadata operations (needed for listing) 6 + path "spindle/data/*" { 7 + capabilities = ["create", "read", "update", "delete"] 8 + } 9 + 7 10 path "spindle/metadata/*" { 8 11 capabilities = ["list", "read", "delete"] 9 12 } 10 13 11 - # Root path access (needed for mount-level operations) 12 - path "spindle/*" { 13 - capabilities = ["list"] 14 + # Allow listing mounts (for connection testing) 15 + path "sys/mounts" { 16 + capabilities = ["read"] 14 17 } 15 18 19 + # Allow token self-lookup (for health checks) 20 + path "auth/token/lookup-self" { 21 + capabilities = ["read"] 22 + }
+4 -12
spindle/server.go
··· 71 71 var vault secrets.Manager 72 72 switch cfg.Server.Secrets.Provider { 73 73 case "openbao": 74 - if cfg.Server.Secrets.OpenBao.Addr == "" { 75 - return fmt.Errorf("openbao address is required when using openbao secrets provider") 76 - } 77 - if cfg.Server.Secrets.OpenBao.RoleID == "" { 78 - return fmt.Errorf("openbao role_id is required when using openbao secrets provider") 79 - } 80 - if cfg.Server.Secrets.OpenBao.SecretID == "" { 81 - return fmt.Errorf("openbao secret_id is required when using openbao secrets provider") 74 + if cfg.Server.Secrets.OpenBao.ProxyAddr == "" { 75 + return fmt.Errorf("openbao proxy address is required when using openbao secrets provider") 82 76 } 83 77 vault, err = secrets.NewOpenBaoManager( 84 - cfg.Server.Secrets.OpenBao.Addr, 85 - cfg.Server.Secrets.OpenBao.RoleID, 86 - cfg.Server.Secrets.OpenBao.SecretID, 78 + cfg.Server.Secrets.OpenBao.ProxyAddr, 87 79 logger, 88 80 secrets.WithMountPath(cfg.Server.Secrets.OpenBao.Mount), 89 81 ) 90 82 if err != nil { 91 83 return fmt.Errorf("failed to setup openbao secrets provider: %w", err) 92 84 } 93 - logger.Info("using openbao secrets provider", "address", cfg.Server.Secrets.OpenBao.Addr, "mount", cfg.Server.Secrets.OpenBao.Mount) 85 + logger.Info("using openbao secrets provider", "proxy_address", cfg.Server.Secrets.OpenBao.ProxyAddr, "mount", cfg.Server.Secrets.OpenBao.Mount) 94 86 case "sqlite", "": 95 87 vault, err = secrets.NewSQLiteManager(cfg.Server.DBPath, secrets.WithTableName("secrets")) 96 88 if err != nil {