Skip to main content
Version: 2.11

Add Service Accounts

Prerequisites

  • A cluster admin role on your Kubernetes cluster

  • Stork deployed or upgraded on all application clusters

note

The service account created by this procedure is assigned the cluster-admin ClusterRoleBinding. This level of access is required because Portworx Backup must discover and back up resources across all namespaces in the cluster, including custom resources, storage objects, and RBAC configurations.

If your security policy does not allow cluster-admin access, work with your Kubernetes administrator to create a custom ClusterRole that includes only the API groups and permissions required by Portworx Backup.

To add a cluster in Portworx Backup, you must first create a service account. This account enables you to retrieve the kubeconfig and token necessary to add a cluster in Portworx Backup.

Perform the following steps to add a service account:

  1. Create a yaml spec called pxbackup-sa.yaml with the following content:

    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: pxbackup-sa
    namespace: kube-system
    ---
    apiVersion: v1
    kind: Secret
    type: kubernetes.io/service-account-token
    metadata:
    name: pxbackup-sa
    namespace: kube-system
    annotations:
    kubernetes.io/service-account.name: pxbackup-sa
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: pxbackup-sa-clusterrolebinding
    subjects:
    - kind: ServiceAccount
    name: pxbackup-sa
    namespace: kube-system
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: cluster-admin
  2. Apply the spec file:

    kubectl apply -f pxbackup-sa.yaml
  3. Retrieve the cluster API server URL:

    kubectl cluster-info

    Note the Kubernetes control plane URL from the output (for example, https://192.168.1.10:6443). You will use this value in the next step.

  4. Create a shell script kubeconfig-sa.sh with the following content. If you have a valid certificate, follow step a, otherwise go to step b.

    a. If you have a valid certificate, use the below content. This creates a certificate-based service account:

    #!/bin/bash
    SERVICE_ACCOUNT=pxbackup-sa
    NAMESPACE=kube-system
    SERVER=https://<SERVER-ADDRESS:PORT> # Replace with the Kubernetes control plane URL from Step 3

    SERVICE_ACCOUNT_TOKEN_COUNT=$(kubectl -n ${NAMESPACE} get secret -o=jsonpath='{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="'${SERVICE_ACCOUNT}'")].metadata.name}' | wc -w)
    if [ ${SERVICE_ACCOUNT_TOKEN_COUNT} -gt 1 ]
    then
    SERVICE_ACCOUNT_TOKEN_NAME=$(kubectl -n ${NAMESPACE} get secret -o=jsonpath='{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="'${SERVICE_ACCOUNT}'")].metadata.name}' | awk '{for(i=1;i<=NF;i++){ if($i ~ /-token/){print $i} } }' | head -n 1)
    else
    SERVICE_ACCOUNT_TOKEN_NAME=$(kubectl -n ${NAMESPACE} get secret -o=jsonpath='{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="'${SERVICE_ACCOUNT}'")].metadata.name}')
    fi

    SERVICE_ACCOUNT_TOKEN=$(kubectl -n ${NAMESPACE} get secret ${SERVICE_ACCOUNT_TOKEN_NAME} -o "jsonpath={.data.token}" | base64 --decode)
    SERVICE_ACCOUNT_CERTIFICATE=$(kubectl -n ${NAMESPACE} get secret ${SERVICE_ACCOUNT_TOKEN_NAME} -o "jsonpath={.data['ca\.crt']}")

    cat <<END
    apiVersion: v1
    kind: Config
    clusters:
    - name: default-cluster
    cluster:
    certificate-authority-data: ${SERVICE_ACCOUNT_CERTIFICATE}
    server: ${SERVER}
    contexts:
    - name: default-context
    context:
    cluster: default-cluster
    namespace: ${NAMESPACE}
    user: ${SERVICE_ACCOUNT}
    current-context: default-context
    users:
    - name: ${SERVICE_ACCOUNT}
    user:
    token: ${SERVICE_ACCOUNT_TOKEN}
    END

    b. If you do not have valid certificate, use the following content:

    #!/bin/bash
    SERVICE_ACCOUNT=pxbackup-sa
    NAMESPACE=kube-system
    SERVER=https://<SERVER-ADDRESS:PORT>

    SERVICE_ACCOUNT_TOKEN_COUNT=$(kubectl -n ${NAMESPACE} get secret -o=jsonpath='{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="'${SERVICE_ACCOUNT}'")].metadata.name}' | wc -w)
    if [ ${SERVICE_ACCOUNT_TOKEN_COUNT} -gt 1 ]
    then
    SERVICE_ACCOUNT_TOKEN_NAME=$(kubectl -n ${NAMESPACE} get secret -o=jsonpath='{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="'${SERVICE_ACCOUNT}'")].metadata.name}' | awk '{for(i=1;i<=NF;i++){ if($i ~ /-token/){print $i} } }' | head -n 1)
    else
    SERVICE_ACCOUNT_TOKEN_NAME=$(kubectl -n ${NAMESPACE} get secret -o=jsonpath='{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="'${SERVICE_ACCOUNT}'")].metadata.name}')
    fi

    SERVICE_ACCOUNT_TOKEN=$(kubectl -n ${NAMESPACE} get secret ${SERVICE_ACCOUNT_TOKEN_NAME} -o "jsonpath={.data.token}" | base64 --decode)
    SERVICE_ACCOUNT_CERTIFICATE=$(kubectl -n ${NAMESPACE} get secret ${SERVICE_ACCOUNT_TOKEN_NAME} -o "jsonpath={.data['ca\.crt']}")

    cat <<END
    apiVersion: v1
    kind: Config
    clusters:
    - name: default-cluster
    cluster:
    insecure-skip-tls-verify: true
    server: ${SERVER}
    contexts:
    - name: default-context
    context:
    cluster: default-cluster
    namespace: ${NAMESPACE}
    user: ${SERVICE_ACCOUNT}
    current-context: default-context
    users:
    - name: ${SERVICE_ACCOUNT}
    user:
    token: ${SERVICE_ACCOUNT_TOKEN}
    END
  5. Run the shell script and save the kubeconfig file you obtain. You need this file later to add a cluster in the Portworx Backup web console.

    chmod 755 kubeconfig-sa.sh
    ./kubeconfig-sa.sh

    Portworx Backup utilizes the service account token as a long-lived connection to the target cluster, therefore the script does not set the expiry field on the service account token. If expiry is set on this token, cluster owners will need to ensure that the token is refreshed before expiry timeline to avoid cluster authentication issues.