NetScaler ingress controller

Using NetScaler credentials stored in a Vault server for the NetScaler Ingress Controller

In most organizations, tier 1 NetScaler Ingress devices and Kubernetes clusters are managed by separate teams. Usually, network administrators manage tier 1 NetScaler Ingress devices, while developers manage Kubernetes clusters. The NetScaler Ingress Controller requires NetScaler credentials such as NetScaler user name and password to configure the NetScaler. You can specify NetScaler credentials as part of the NetScaler Ingress Controller specification and store the ADC credentials as Kubernetes secrets. However, you can also store NetScaler credentials in a Vault server and pass credentials to the NetScaler Ingress Controller to minimize any security risk. This topic provides information on how to use NetScaler credentials stored in a Vault server for the NetScaler Ingress Controller.

The following diagram explains the steps for using NetScaler credentials which are stored in a Vault server with the NetScaler Ingress Controller.

vault-auto-authentication

Prerequisites

Ensure that you have setup a Vault server and enabled key-value (KV) secret store. For more information, see Vault documentation.

Using NetScaler credentials from a Vault server for the NetScaler Ingress Controller

Perform the following tasks to use NetScaler credentials from a Vault server for the NetScaler Ingress Controller.

  1. Create a service account for Kubernetes authentication.

  2. Create a Key Vault secret and setup Kubernetes authentication on Vault server.

  3. Leverage Vault Auto-Auth functionality to fetch NetScaler credentials for the NetScaler Ingress Controller.

Create a service account for Kubernetes authentication

Create a service account for Kubernetes authentication by using the following steps:

  1. Create a service account cic-k8s-role and provide the service account necessary permissions to access the Kubernetes TokenReview API by using the following command.

    $ kubectl apply -f cic-k8s-role-service-account.yml
         
            
    serviceaccount/cic-k8s-role created
    clusterrole.rbac.authorization.k8s.io/cic-k8s-role configured
    clusterrolebinding.rbac.authorization.k8s.io/cic-k8s-role configured
    clusterrolebinding.rbac.authorization.k8s.io/role-tokenreview-binding configured
    

    Following is a part of the sample cic-k8s-role-service-account.yml file.

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: role-tokenreview-binding
    namespace: default
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: system:auth-delegator
    subjects:
    - kind: ServiceAccount
    name: cic-k8s-role
    namespace: default
    
  2. Set the VAULT_SA_NAME environment variable to the name of the service account you have already created.

         export VAULT_SA_NAME=$(kubectl get sa cic-k8s-role -o jsonpath="{.secrets[*]['name']}")
    <!--NeedCopy-->
    
  3. Set the SA_JWT_TOKEN environment variable to the JWT of the service account that you used to access the TokenReview API.

         export SA_JWT_TOKEN=$(kubectl get secret $VAULT_SA_NAME -o jsonpath="{.data.token}" | base64 --decode; echo)
    <!--NeedCopy-->
    
  4. Get a Kubernetes CA signed certificate to communicate with Kubernetes API.

         export SA_CA_CRT=$(kubectl get secret $VAULT_SA_NAME -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)
    <!--NeedCopy-->
    

Create a key vault secret and setup Kubernetes authentication on the Vault server

Log in to the Vault server and perform the following steps to create a Key Vault secret and setup Kubernetes authentication.

  1. Review the sample vault policy file citrix-adc-kv-ro.hcl and create a read-only policy, citrix-adc-kv-ro in Vault.

    $ tee citrix-adc-kv-ro.hcl <<EOF 
    # If working with K/V v1
    path "secret/citrix-adc/*"
    {
        capabilities = ["read", "list"]
    }
    # If working with K/V v2
    path "secret/data/citrix-adc/*" 
    {
        capabilities = ["read", "list"]
    }
    EOF
    
    #  Create a policy named citrix-adc-kv-ro
    $  vault policy write citrix-adc-kv-ro citrix-adc-kv-ro.hcl
    
  2. Create a KV secret with NetScaler credentials at the secret/citrix-adc/ path.

    vault kv put secret/citrix-adc/credential   username='<ADC username>' \
    password='<ADC password>' \
    ttl='30m'
    
  3. Enable Kubernetes authentication at the default path (auth/kubernetes).

    # $ vault auth enable kubernetes
    
  4. Specify how to communicate with the Kubernetes cluster.

    $ vault write auth/kubernetes/config \
    token_reviewer_jwt="$SA_JWT_TOKEN" \
    kubernetes_host="https://<K8S_CLUSTER_URL>:<API_SERVER_PORT>" \
    kubernetes_ca_cert="$SA_CA_CRT"      
    
  5. Create a role to map the Kubernetes service account to Vault policies and the default token TTL. This role authorizes the cic-k8s-role service account in the default namespace and maps the service account to the citrix-adc-kv-ro policy.

    $ vault write auth/kubernetes/role/cic-vault-example\
    bound_service_account_names=cic-k8s-role \
    bound_service_account_namespaces=default \
    policies=citrix-adc-kv-ro \
    ttl=24h
    

    Note:

    Authorization with Kubernetes authentication back-end is role based. Before a token is used for login, it must be configured as part of a role.

Leverage Vault agent auto-authentication for the NetScaler Ingress Controller

Perform the following steps to leverage Vault auto-authentication.

  1. Review the provided Vault Agent configuration file, vault-agent-config.hcl.

        exit_after_auth = true
        pid_file = "/home/vault/pidfile"
    
        auto_auth {
            method "kubernetes" {
                mount_path = "auth/kubernetes"
                config = {
                    role = "cic-vault-example"
                }
            }
    
            sink "file" {
                config = {
                    path = "/home/vault/.vault-token"
                }
            }
        }
    

    Note:

    The Vault agent Auto-Auth is configured to use the Kubernetes authentication method enabled at the auth/kubernetes path on the Vault server. The Vault Agent uses the cic-vault-example role to authenticate. The sink block specifies the location on disk where to write tokens. Vault Agent Auto-Auth sink can be configured multiple times if you want Vault Agent to place the token into multiple locations. In this example, the sink is set to /home/vault/.vault-token.

  2. Review the Consul template consul-template-config.hcl file.

        vault {
            renew_token = false
            vault_agent_token_file = "/home/vault/.vault-token"
            retry {
                backoff = "1s"
            }
        }
    
        template {
            destination = "/etc/citrix/.env"
            contents = <<EOH
            NS_USER=
            NS_PASSWORD=
                
            EOH
        }
    

    This template reads secrets at the secret/citrix-adc/credential path and sets the user name and password values. If you are using KV store version 1, use the following template.

        template {
            destination = "/etc/citrix/.env"
            contents = <<EOH
            NS_USER=
            NS_PASSWORD=
                
            EOH
                }
    
  3. Create a Kubernetes config-map from vault-agent-config.hcl and consul-template-config.hcl.

        kubectl create configmap example-vault-agent-config --from-file=./vault-agent-config.hcl --form-file=./consul-template-config.hcl
    
  4. Create a NetScaler Ingress Controller pod with Vault and consul template as init container citrix-k8s-ingress-controller-vault.yaml. Vault fetches the token using the Kubernetes authentication method and pass it on to a consul template which creates the .env file on shared volume. This token is used by the NetScaler Ingress Controller for authentication with tier 1 NetScaler.

        kubectl apply citrix-k8s-ingress-controller-vault.yaml
    

    The citrix-k8s-ingress-controller-vault.yaml file is as follows:

            apiVersion: v1
            kind: Pod
            metadata:
            annotations:
            name: cic-vault
            namespace: default
            spec:
            containers:
            - args:
                - --ingress-classes tier-1-vpx
                - --feature-node-watch true
                env:
                - name: NS_IP
                value: <Tier 1 ADC IP-ADDRESS>
                - name: EULA
                value: "yes"
                image: in-docker-reg.eng.citrite.net/cpx-dev/kumar-cic:latest
                imagePullPolicy: Always
                name: cic-k8s-ingress-controller
                volumeMounts:
                - mountPath: /etc/citrix
                name: shared-data
            initContainers:
            - args:
                - agent
                - -config=/etc/vault/vault-agent-config.hcl
                - -log-level=debug
                env:
                - name: VAULT_ADDR
                value: <VAULT URL>
                image: vault
                imagePullPolicy: Always
                name: vault-agent-auth
                volumeMounts:
                - mountPath: /etc/vault
                name: config
                - mountPath: /home/vault
                name: vault-token
            - args:
                - -config=/etc/consul-template/consul-template-config.hcl
                - -log-level=debug
                - -once
                env:
                - name: HOME
                value: /home/vault
                - name: VAULT_ADDR
                value: <VAULT_URL>
                image: hashicorp/consul-template:alpine
                imagePullPolicy: Always
                name: consul-template
                volumeMounts:
                - mountPath: /home/vault
                name: vault-token
                - mountPath: /etc/consul-template
                name: config
                - mountPath: /etc/citrix
                name: shared-data
            serviceAccountName: vault-auth
            volumes:
            - emptyDir:
                medium: Memory
                name: vault-token
            - configMap:
                defaultMode: 420
                items:
                - key: vault-agent-config.hcl
                    path: vault-agent-config.hcl
                - key: consul-template-config.hcl
            name: example-vault-agent-config
                name: config
            - emptyDir:
                medium: Memory
                name: shared-data        
    

If the configuration is successful, the Vault server fetches a token and passes it on to a Consul template container. The Consul template uses the token to read NetScaler credentials and write it as an environment variable in the path /etc/citrix/.env. The NetScaler Ingress Controller uses these credentials for communicating with the tier 1 NetScaler.

Verify that the NetScaler Ingress Controller is running successfully using credentials fetched from the Vault server.

Using NetScaler credentials stored in a Vault server for the NetScaler Ingress Controller