package azure import ( "context" "fmt" "slices" "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault" "git.kebcloud.no/keb/lazysecrets/internal/provider" ) // Details are a vault's settings, from the Key Vault management API: where // it is, how access to it works, and how it's protected. Pinned vaults, // known only by their URL, have none. func (p *Provider) Details(ctx context.Context, c provider.Container) ([]provider.Attr, error) { id, err := arm.ParseResourceID(c.ID) if err != nil { return nil, provider.ErrUnsupported } client, err := armkeyvault.NewVaultsClient(id.SubscriptionID, p.cred, p.opts.arm) if err != nil { return nil, err } resp, err := client.Get(ctx, id.ResourceGroupName, id.Name, nil) if err != nil { return nil, cleanError(err) } return vaultAttrs(resp.Vault), nil } func vaultAttrs(v armkeyvault.Vault) []provider.Attr { var out []provider.Attr add := func(label, value string) { if value != "" { out = append(out, provider.Attr{Label: label, Value: value}) } } if id, err := arm.ParseResourceID(deref(v.ID)); err == nil { add("group", id.ResourceGroupName) } add("location", deref(v.Location)) pr := v.Properties if pr == nil { return out } if pr.SKU != nil && pr.SKU.Name != nil { add("SKU", string(*pr.SKU.Name)) } // Who may read secrets is decided by Azure roles or by the vault's // own access policies, never both. if on(pr.EnableRbacAuthorization, false) { add("access", "Azure roles (RBAC)") } else { add("access", fmt.Sprintf("access policies (%d)", len(pr.AccessPolicies))) } if on(pr.EnableSoftDelete, true) { days := int32(90) if pr.SoftDeleteRetentionInDays != nil { days = *pr.SoftDeleteRetentionInDays } add("soft delete", fmt.Sprintf("on, %d days", days)) } else { add("soft delete", "off") } add("purge prot.", map[bool]string{true: "on", false: "off"}[on(pr.EnablePurgeProtection, false)]) add("network", network(pr)) if n := len(pr.PrivateEndpointConnections); n > 0 { add("private", fmt.Sprintf("%d endpoint%s", n, map[bool]string{true: "s"}[n > 1])) } keys := make([]string, 0, len(v.Tags)) for k := range v.Tags { keys = append(keys, k) } slices.Sort(keys) for i, k := range keys { label := "" if i == 0 { label = "tags" } out = append(out, provider.Attr{Label: label, Value: k + ": " + deref(v.Tags[k])}) } return out } // network is where the vault can be reached from. func network(pr *armkeyvault.VaultProperties) string { if strings.EqualFold(deref(pr.PublicNetworkAccess), "disabled") { return "private endpoints only" } acl := pr.NetworkACLs if acl == nil || acl.DefaultAction == nil || *acl.DefaultAction != armkeyvault.NetworkRuleActionDeny { return "all networks" } return fmt.Sprintf("selected networks (%d IP ranges, %d subnets)", len(acl.IPRules), len(acl.VirtualNetworkRules)) } // on is *b, or def when it isn't set. func on(b *bool, def bool) bool { if b == nil { return def } return *b }