Use vault-client-go instead
Signed-off-by: Richard Wall <richard.wall@venafi.com>
This commit is contained in:
parent
88adf38221
commit
3f75290e04
@ -26,7 +26,8 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
vault "github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault-client-go"
|
||||
"github.com/hashicorp/vault-client-go/schema"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -184,21 +185,23 @@ func NewVaultKubernetesSecret(secretName, serviceAccountName string) *corev1.Sec
|
||||
|
||||
// Set up a new Vault client, port-forward to the Vault instance.
|
||||
func (v *VaultInitializer) Init() error {
|
||||
cfg := vault.DefaultConfig()
|
||||
cfg := vault.DefaultConfiguration()
|
||||
cfg.Address = v.details.ProxyURL
|
||||
|
||||
caCertPool := x509.NewCertPool()
|
||||
if ok := caCertPool.AppendCertsFromPEM(v.details.VaultCA); !ok {
|
||||
return fmt.Errorf("error loading Vault CA bundle: %s", v.details.VaultCA)
|
||||
}
|
||||
cfg.HttpClient.Transport.(*http.Transport).TLSClientConfig.RootCAs = caCertPool
|
||||
cfg.HTTPClient.Transport.(*http.Transport).TLSClientConfig.RootCAs = caCertPool
|
||||
|
||||
client, err := vault.NewClient(cfg)
|
||||
client, err := vault.New(vault.WithConfiguration(cfg))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize vault client: %s", err)
|
||||
}
|
||||
|
||||
client.SetToken(vaultToken)
|
||||
if err := client.SetToken(vaultToken); err != nil {
|
||||
return err
|
||||
}
|
||||
v.client = client
|
||||
|
||||
// Wait for port-forward to be ready
|
||||
@ -227,7 +230,7 @@ func (v *VaultInitializer) Init() error {
|
||||
{
|
||||
var lastError error
|
||||
err = wait.PollUntilContextTimeout(context.TODO(), time.Second, 20*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||
_, err := v.client.Sys().Health()
|
||||
_, err := v.client.System.ReadHealthStatus(context.TODO())
|
||||
if err != nil {
|
||||
lastError = err
|
||||
return false, nil
|
||||
@ -243,33 +246,6 @@ func (v *VaultInitializer) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) callVault(method, url, field string, params map[string]string) (string, error) {
|
||||
req := v.client.NewRequest(method, url)
|
||||
|
||||
err := req.SetJSONBody(params)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error encoding Vault parameters: %s", err.Error())
|
||||
|
||||
}
|
||||
|
||||
resp, err := v.client.RawRequest(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error calling Vault server: %s", err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
result := map[string]interface{}{}
|
||||
resp.DecodeJSON(&result)
|
||||
|
||||
fieldData := ""
|
||||
if field != "" {
|
||||
data := result["data"].(map[string]interface{})
|
||||
fieldData = data[field].(string)
|
||||
}
|
||||
|
||||
return fieldData, err
|
||||
}
|
||||
|
||||
// Set up a Vault PKI.
|
||||
func (v *VaultInitializer) Setup() error {
|
||||
// Enable a new Vault secrets engine at v.RootMount
|
||||
@ -343,10 +319,12 @@ func (v *VaultInitializer) Setup() error {
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) Clean() error {
|
||||
if err := v.client.Sys().Unmount("/" + v.intermediateMount); err != nil {
|
||||
ctx := context.Background()
|
||||
|
||||
if _, err := v.client.System.MountsDisableSecretsEngine(ctx, "/"+v.intermediateMount); err != nil {
|
||||
return fmt.Errorf("unable to unmount %v: %v", v.intermediateMount, err)
|
||||
}
|
||||
if err := v.client.Sys().Unmount("/" + v.rootMount); err != nil {
|
||||
if _, err := v.client.System.MountsDisableSecretsEngine(ctx, "/"+v.rootMount); err != nil {
|
||||
return fmt.Errorf("unable to unmount %v: %v", v.rootMount, err)
|
||||
}
|
||||
|
||||
@ -354,50 +332,68 @@ func (v *VaultInitializer) Clean() error {
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) CreateAppRole() (string, string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
// create policy
|
||||
policy := fmt.Sprintf(`path "%s" { capabilities = [ "create", "update" ] }`, v.IntermediateSignPath())
|
||||
err := v.client.Sys().PutPolicy(v.role, policy)
|
||||
_, err := v.client.System.PoliciesWriteAclPolicy(
|
||||
ctx,
|
||||
v.role,
|
||||
schema.PoliciesWriteAclPolicyRequest{
|
||||
Policy: policy,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("error creating policy: %s", err.Error())
|
||||
}
|
||||
|
||||
// # create approle
|
||||
params := map[string]string{
|
||||
"period": "24h",
|
||||
"policies": v.role,
|
||||
}
|
||||
|
||||
baseUrl := path.Join("/v1", "auth", v.appRoleAuthPath, "role", v.role)
|
||||
_, err = v.callVault("POST", baseUrl, "", params)
|
||||
_, err = v.client.Auth.AppRoleWriteRole(
|
||||
ctx,
|
||||
v.role,
|
||||
schema.AppRoleWriteRoleRequest{
|
||||
Period: "24h",
|
||||
Policies: []string{v.role},
|
||||
},
|
||||
vault.WithMountPath(v.appRoleAuthPath),
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("error creating approle: %s", err.Error())
|
||||
}
|
||||
|
||||
// # read the role-id
|
||||
url := path.Join(baseUrl, "role-id")
|
||||
roleId, err := v.callVault("GET", url, "role_id", map[string]string{})
|
||||
respRoleId, err := v.client.Auth.AppRoleReadRoleId(
|
||||
ctx,
|
||||
v.role,
|
||||
vault.WithMountPath(v.appRoleAuthPath),
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("error reading role_id: %s", err.Error())
|
||||
}
|
||||
|
||||
// # read the secret-id
|
||||
url = path.Join(baseUrl, "secret-id")
|
||||
secretId, err := v.callVault("POST", url, "secret_id", map[string]string{})
|
||||
// TODO: Should use Auth.AppRoleWriteSecretId instead of raw write here,
|
||||
// but it's currently broken. See:
|
||||
// https://github.com/hashicorp/vault-client-go/issues/249
|
||||
resp, err := v.client.Write(ctx, "/v1/auth/"+v.appRoleAuthPath+"/role/"+v.role+"/secret-id", nil)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("error reading secret_id: %s", err.Error())
|
||||
}
|
||||
|
||||
return roleId, secretId, nil
|
||||
return respRoleId.Data.RoleId, resp.Data["secret_id"].(string), nil
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) CleanAppRole() error {
|
||||
url := path.Join("/v1", "auth", v.appRoleAuthPath, "role", v.role)
|
||||
_, err := v.callVault("DELETE", url, "", nil)
|
||||
ctx := context.Background()
|
||||
_, err := v.client.Auth.AppRoleDeleteRole(
|
||||
ctx,
|
||||
v.role,
|
||||
vault.WithMountPath(v.appRoleAuthPath),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting AppRole: %s", err.Error())
|
||||
}
|
||||
|
||||
err = v.client.Sys().DeletePolicy(v.role)
|
||||
_, err = v.client.System.PoliciesDeleteAclPolicy(ctx, v.role)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting policy: %s", err.Error())
|
||||
}
|
||||
@ -406,13 +402,18 @@ func (v *VaultInitializer) CleanAppRole() error {
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) mountPKI(mount, ttl string) error {
|
||||
opts := &vault.MountInput{
|
||||
Type: "pki",
|
||||
Config: vault.MountConfigInput{
|
||||
MaxLeaseTTL: "87600h",
|
||||
ctx := context.Background()
|
||||
_, err := v.client.System.MountsEnableSecretsEngine(
|
||||
ctx,
|
||||
"/"+mount,
|
||||
schema.MountsEnableSecretsEngineRequest{
|
||||
Type: "pki",
|
||||
Config: map[string]interface{}{
|
||||
"max_lease_ttl": ttl,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := v.client.Sys().Mount("/"+mount, opts); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error mounting %s: %s", mount, err.Error())
|
||||
}
|
||||
|
||||
@ -420,65 +421,74 @@ func (v *VaultInitializer) mountPKI(mount, ttl string) error {
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) generateRootCert() (string, error) {
|
||||
params := map[string]string{
|
||||
"common_name": "Root CA",
|
||||
"ttl": "87600h",
|
||||
"exclude_cn_from_sans": "true",
|
||||
"key_type": "ec",
|
||||
"key_bits": "256",
|
||||
}
|
||||
url := path.Join("/v1", v.rootMount, "root", "generate", "internal")
|
||||
|
||||
cert, err := v.callVault("POST", url, "certificate", params)
|
||||
ctx := context.Background()
|
||||
resp, err := v.client.Secrets.PkiGenerateRoot(
|
||||
ctx,
|
||||
"internal",
|
||||
schema.PkiGenerateRootRequest{
|
||||
CommonName: "Root CA",
|
||||
Ttl: "87600h",
|
||||
ExcludeCnFromSans: true,
|
||||
KeyType: "ec",
|
||||
KeyBits: 256,
|
||||
},
|
||||
vault.WithMountPath(v.rootMount),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error generating CA root certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
return resp.Data.Certificate, nil
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) generateIntermediateSigningReq() (string, error) {
|
||||
params := map[string]string{
|
||||
"common_name": "Intermediate CA",
|
||||
"ttl": "43800h",
|
||||
"exclude_cn_from_sans": "true",
|
||||
"key_type": "ec",
|
||||
"key_bits": "256",
|
||||
}
|
||||
url := path.Join("/v1", v.intermediateMount, "intermediate", "generate", "internal")
|
||||
|
||||
csr, err := v.callVault("POST", url, "csr", params)
|
||||
ctx := context.Background()
|
||||
resp, err := v.client.Secrets.PkiGenerateIntermediate(
|
||||
ctx,
|
||||
"internal",
|
||||
schema.PkiGenerateIntermediateRequest{
|
||||
CommonName: "Intermediate CA",
|
||||
Ttl: "43800h",
|
||||
ExcludeCnFromSans: true,
|
||||
KeyType: "ec",
|
||||
KeyBits: 256,
|
||||
},
|
||||
vault.WithMountPath(v.intermediateMount),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error generating CA intermediate certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
return csr, nil
|
||||
return resp.Data.Csr, nil
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) signCertificate(csr string) (string, error) {
|
||||
params := map[string]string{
|
||||
"use_csr_values": "true",
|
||||
"ttl": "43800h",
|
||||
"exclude_cn_from_sans": "true",
|
||||
"csr": csr,
|
||||
}
|
||||
url := path.Join("/v1", v.rootMount, "root", "sign-intermediate")
|
||||
|
||||
cert, err := v.callVault("POST", url, "certificate", params)
|
||||
ctx := context.Background()
|
||||
resp, err := v.client.Secrets.PkiRootSignIntermediate(
|
||||
ctx,
|
||||
schema.PkiRootSignIntermediateRequest{
|
||||
UseCsrValues: true,
|
||||
Ttl: "43800h",
|
||||
ExcludeCnFromSans: true,
|
||||
Csr: csr,
|
||||
},
|
||||
vault.WithMountPath(v.rootMount),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error signing intermediate Vault certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
return resp.Data.Certificate, nil
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) importSignIntermediate(caChain, intermediateMount string) error {
|
||||
params := map[string]string{
|
||||
"certificate": caChain,
|
||||
}
|
||||
url := path.Join("/v1", intermediateMount, "intermediate", "set-signed")
|
||||
|
||||
_, err := v.callVault("POST", url, "", params)
|
||||
ctx := context.Background()
|
||||
_, err := v.client.Secrets.PkiSetSignedIntermediate(
|
||||
ctx,
|
||||
schema.PkiSetSignedIntermediateRequest{
|
||||
Certificate: caChain,
|
||||
},
|
||||
vault.WithMountPath(intermediateMount),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error importing intermediate Vault certificate: %s", err.Error())
|
||||
}
|
||||
@ -487,13 +497,19 @@ func (v *VaultInitializer) importSignIntermediate(caChain, intermediateMount str
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) configureCert(mount string) error {
|
||||
params := map[string]string{
|
||||
"issuing_certificates": fmt.Sprintf("https://vault.vault:8200/v1/%s/ca", mount),
|
||||
"crl_distribution_points": fmt.Sprintf("https://vault.vault:8200/v1/%s/crl", mount),
|
||||
}
|
||||
url := path.Join("/v1", mount, "config", "urls")
|
||||
|
||||
_, err := v.callVault("POST", url, "", params)
|
||||
ctx := context.Background()
|
||||
_, err := v.client.Secrets.PkiConfigureUrls(
|
||||
ctx,
|
||||
schema.PkiConfigureUrlsRequest{
|
||||
IssuingCertificates: []string{
|
||||
fmt.Sprintf("https://vault.vault:8200/v1/%s/ca", mount),
|
||||
},
|
||||
CrlDistributionPoints: []string{
|
||||
fmt.Sprintf("https://vault.vault:8200/v1/%s/crl", mount),
|
||||
},
|
||||
},
|
||||
vault.WithMountPath(mount),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error configuring Vault certificate: %s", err.Error())
|
||||
}
|
||||
@ -502,20 +518,23 @@ func (v *VaultInitializer) configureCert(mount string) error {
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) configureIntermediateRoles() error {
|
||||
params := map[string]string{
|
||||
"allow_any_name": "true",
|
||||
"max_ttl": "2160h",
|
||||
"key_type": "any",
|
||||
"require_cn": "false",
|
||||
"allowed_other_sans": "*",
|
||||
"use_csr_sans": "true",
|
||||
"allowed_uri_sans": "spiffe://cluster.local/*",
|
||||
"enforce_hostnames": "false",
|
||||
"allow_bare_domains": "true",
|
||||
}
|
||||
url := path.Join("/v1", v.intermediateMount, "roles", v.role)
|
||||
|
||||
_, err := v.callVault("POST", url, "", params)
|
||||
ctx := context.Background()
|
||||
_, err := v.client.Secrets.PkiWriteRole(
|
||||
ctx,
|
||||
v.role,
|
||||
schema.PkiWriteRoleRequest{
|
||||
AllowAnyName: true,
|
||||
MaxTtl: "2160h",
|
||||
KeyType: "any",
|
||||
RequireCn: false,
|
||||
AllowedOtherSans: []string{"*"},
|
||||
UseCsrSans: true,
|
||||
AllowedUriSans: []string{"spiffe://cluster.local/*"},
|
||||
EnforceHostnames: false,
|
||||
AllowBareDomains: true,
|
||||
},
|
||||
vault.WithMountPath(v.intermediateMount),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating role %s: %s", v.role, err.Error())
|
||||
}
|
||||
@ -524,60 +543,72 @@ func (v *VaultInitializer) configureIntermediateRoles() error {
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) setupAppRoleAuth() error {
|
||||
ctx := context.Background()
|
||||
// vault auth-enable approle
|
||||
auths, err := v.client.Sys().ListAuth()
|
||||
resp, err := v.client.System.AuthListEnabledMethods(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error fetching auth mounts: %s", err.Error())
|
||||
}
|
||||
|
||||
if _, ok := auths[v.appRoleAuthPath]; ok {
|
||||
if _, ok := resp.Data[v.appRoleAuthPath]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
options := &vault.EnableAuthOptions{
|
||||
Type: "approle",
|
||||
}
|
||||
if err := v.client.Sys().EnableAuthWithOptions(v.appRoleAuthPath, options); err != nil {
|
||||
return fmt.Errorf("error enabling approle: %s", err.Error())
|
||||
_, err = v.client.System.AuthEnableMethod(
|
||||
ctx,
|
||||
v.appRoleAuthPath,
|
||||
schema.AuthEnableMethodRequest{
|
||||
Type: "approle",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error enabling approle auth: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VaultInitializer) setupKubernetesBasedAuth() error {
|
||||
ctx := context.Background()
|
||||
// vault auth-enable kubernetes
|
||||
auths, err := v.client.Sys().ListAuth()
|
||||
resp, err := v.client.System.AuthListEnabledMethods(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error fetching auth mounts: %s", err.Error())
|
||||
}
|
||||
|
||||
if _, ok := auths[v.kubernetesAuthPath]; ok {
|
||||
if _, ok := resp.Data[v.kubernetesAuthPath]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
options := &vault.EnableAuthOptions{
|
||||
Type: "kubernetes",
|
||||
}
|
||||
if err := v.client.Sys().EnableAuthWithOptions(v.kubernetesAuthPath, options); err != nil {
|
||||
return fmt.Errorf("error enabling approle: %s", err.Error())
|
||||
_, err = v.client.System.AuthEnableMethod(
|
||||
ctx,
|
||||
v.kubernetesAuthPath,
|
||||
schema.AuthEnableMethodRequest{
|
||||
Type: "kubernetes",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error enabling kubernetes auth: %s", err.Error())
|
||||
}
|
||||
|
||||
// vault write auth/kubernetes/config
|
||||
params := map[string]string{
|
||||
"kubernetes_host": v.kubernetesAPIServerURL,
|
||||
// Since Vault 1.9, HashiCorp recommends disabling the iss validation.
|
||||
// If we don't disable the iss validation, we can't use the same
|
||||
// Kubernetes auth config for both testing the "secretRef" Kubernetes
|
||||
// auth and the "serviceAccountRef" Kubernetes auth because the former
|
||||
// relies on static tokens for which "iss" is
|
||||
// "kubernetes/serviceaccount", and the later relies on bound tokens for
|
||||
// which "iss" is "https://kubernetes.default.svc.cluster.local".
|
||||
// https://www.vaultproject.io/docs/auth/kubernetes#kubernetes-1-21
|
||||
"disable_iss_validation": "true",
|
||||
}
|
||||
|
||||
url := path.Join("/v1", "auth", v.kubernetesAuthPath, "config")
|
||||
if _, err = v.callVault("POST", url, "", params); err != nil {
|
||||
_, err = v.client.Auth.KubernetesConfigureAuth(
|
||||
ctx,
|
||||
schema.KubernetesConfigureAuthRequest{
|
||||
KubernetesHost: v.kubernetesAPIServerURL,
|
||||
// Since Vault 1.9, HashiCorp recommends disabling the iss validation.
|
||||
// If we don't disable the iss validation, we can't use the same
|
||||
// Kubernetes auth config for both testing the "secretRef" Kubernetes
|
||||
// auth and the "serviceAccountRef" Kubernetes auth because the former
|
||||
// relies on static tokens for which "iss" is
|
||||
// "kubernetes/serviceaccount", and the later relies on bound tokens for
|
||||
// which "iss" is "https://kubernetes.default.svc.cluster.local".
|
||||
// https://www.vaultproject.io/docs/auth/kubernetes#kubernetes-1-21
|
||||
DisableIssValidation: true,
|
||||
},
|
||||
vault.WithMountPath(v.kubernetesAuthPath),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error configuring kubernetes auth backend: %s", err.Error())
|
||||
}
|
||||
|
||||
@ -588,33 +619,42 @@ func (v *VaultInitializer) setupKubernetesBasedAuth() error {
|
||||
// Kubernetes auth delegation. The name "boundSA" refers to the Vault param
|
||||
// "bound_service_account_names".
|
||||
func (v *VaultInitializer) CreateKubernetesRole(client kubernetes.Interface, boundNS, boundSA string) error {
|
||||
ctx := context.Background()
|
||||
serviceAccount := &corev1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: boundSA,
|
||||
},
|
||||
}
|
||||
_, err := client.CoreV1().ServiceAccounts(boundNS).Create(context.TODO(), serviceAccount, metav1.CreateOptions{})
|
||||
_, err := client.CoreV1().ServiceAccounts(boundNS).Create(ctx, serviceAccount, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating ServiceAccount for Kubernetes auth: %s", err.Error())
|
||||
}
|
||||
|
||||
// create policy
|
||||
policy := fmt.Sprintf(`path "%s" { capabilities = [ "create", "update" ] }`, v.IntermediateSignPath())
|
||||
err = v.client.Sys().PutPolicy(v.role, policy)
|
||||
_, err = v.client.System.PoliciesWriteAclPolicy(
|
||||
ctx,
|
||||
v.role,
|
||||
schema.PoliciesWriteAclPolicyRequest{
|
||||
Policy: policy,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating policy: %s", err.Error())
|
||||
}
|
||||
|
||||
// # create approle
|
||||
params := map[string]string{
|
||||
"period": "24h",
|
||||
"policies": v.role,
|
||||
"bound_service_account_names": boundSA,
|
||||
"bound_service_account_namespaces": boundNS,
|
||||
}
|
||||
|
||||
baseUrl := path.Join("/v1", "auth", v.kubernetesAuthPath, "role", v.role)
|
||||
_, err = v.callVault("POST", baseUrl, "", params)
|
||||
_, err = v.client.Auth.KubernetesWriteAuthRole(
|
||||
ctx,
|
||||
v.role,
|
||||
schema.KubernetesWriteAuthRoleRequest{
|
||||
Period: "24h",
|
||||
Policies: []string{v.role},
|
||||
BoundServiceAccountNames: []string{boundSA},
|
||||
BoundServiceAccountNamespaces: []string{boundNS},
|
||||
},
|
||||
vault.WithMountPath(v.kubernetesAuthPath),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating kubernetes role: %s", err.Error())
|
||||
}
|
||||
@ -628,18 +668,18 @@ func (v *VaultInitializer) IntermediateSignPath() string {
|
||||
|
||||
// CleanKubernetesRole cleans up the ClusterRoleBinding and ServiceAccount for Kubernetes auth delegation
|
||||
func (v *VaultInitializer) CleanKubernetesRole(client kubernetes.Interface, boundNS, boundSA string) error {
|
||||
if err := client.CoreV1().ServiceAccounts(boundNS).Delete(context.TODO(), boundSA, metav1.DeleteOptions{}); err != nil {
|
||||
ctx := context.Background()
|
||||
if err := client.CoreV1().ServiceAccounts(boundNS).Delete(ctx, boundSA, metav1.DeleteOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// vault delete auth/kubernetes/role/<roleName>
|
||||
url := path.Join("/v1", "auth", v.kubernetesAuthPath, "role", v.role)
|
||||
_, err := v.callVault("DELETE", url, "", nil)
|
||||
_, err := v.client.Auth.KubernetesDeleteAuthRole(ctx, v.role, vault.WithMountPath(v.kubernetesAuthPath))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error cleaning up kubernetes auth role: %s", err.Error())
|
||||
}
|
||||
|
||||
err = v.client.Sys().DeletePolicy(v.role)
|
||||
_, err = v.client.System.PoliciesDeleteAclPolicy(ctx, v.role)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting policy: %s", err.Error())
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ replace github.com/cert-manager/cert-manager => ../../
|
||||
require (
|
||||
github.com/cert-manager/cert-manager v0.0.0-00010101000000-000000000000
|
||||
github.com/cloudflare/cloudflare-go v0.58.1
|
||||
github.com/hashicorp/vault/api v1.10.0
|
||||
github.com/hashicorp/vault-client-go v0.4.3
|
||||
github.com/kr/pretty v0.3.1
|
||||
github.com/onsi/ginkgo/v2 v2.13.0
|
||||
github.com/onsi/gomega v1.29.0
|
||||
@ -32,13 +32,11 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.6 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
@ -46,7 +44,6 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.22.7 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/go-test/deep v1.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
@ -55,16 +52,11 @@ require (
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@ -73,7 +65,6 @@ require (
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
@ -92,7 +83,7 @@ require (
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
|
||||
@ -8,8 +8,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@ -34,8 +32,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
@ -51,8 +47,6 @@ github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR
|
||||
github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@ -61,7 +55,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@ -78,30 +71,19 @@ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I=
|
||||
github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
|
||||
github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ=
|
||||
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/hashicorp/vault-client-go v0.4.3 h1:zG7STGVgn/VK6rnZc0k8PGbfv2x/sJExRKHSUg3ljWc=
|
||||
github.com/hashicorp/vault-client-go v0.4.3/go.mod h1:4tDw7Uhq5XOxS1fO+oMtotHL7j4sB9cp0T7U6m4FzDY=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
@ -131,8 +113,6 @@ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvls
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -192,7 +172,6 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@ -238,8 +217,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
||||
Loading…
Reference in New Issue
Block a user