Private endpoints

Vespa Cloud lets you set up private endpoint services on your application clusters, for exclusive access from your own, co-located VPCs with the same cloud provider. This is supported for AWS deployments through AWS's PrivateLink, and for GCP deployments through GCP's Private Service Connect. This guide takes you through the necessary configuration steps, for either AWS PrivateLink, or for GCP Private Service Connect.

Required information:

Item Description
Your IAM account number The numeric identifier for your AWS account.
VPC ID The identifier of your AWS VPC where you wish to connect to the service endpoints from.
AWS region name The name of the AWS region to connect from. Note that you can only connect to a service in the same region, or, if public endpoints are disabled, in the same AWS availability zone.

Procedure:

  1. Configure a private endpoint service on your Vespa Cloud Container cluster

    Add <endpoint type="private" /> to deployment.xml, allowing access to the container cluster using the designated ARN from your account. The example allows all roles and users under the 123123123123 account to connect to the endpoint service on the my-container cluster, in each region listed under the <prod> tag.

    See endpoint service configuration for details on valid ARNs, and more fine-grained access control.

    The example also shows how to disable the public zone endpoint, by adding the "zone" type endpoint declaration—this is an optional step, and not required to set up the private service

    <deployment version="1.0">
        <prod>
            <region>region-1</region>
            <region>region-2</region>
        </prod>
        <endpoints>
            <endpoint type="zone" container-id="my-container" enabled="false" />
            <endpoint type="private" container-id="my-container">
                <allow with="aws-private-link" arn="arn:aws:iam::123123123123:root" />
            </endpoint>
        </endpoints>
    </deployment>

    Build and deploy the application package, and wait for it to deploy to the indicated regions.

  2. Find the service ID of your endpoint services

    Navigate to the endpoints tab for your application in the Console, and find the service ID for the deployment to wish to connect to. While there, verify that access to connect to the endpoint was granted to the correct ARNs.

    Service ID for VPC endpoint
  3. Create the VPC interface endpoint

    Create a VPC endpoint in your VPC. This is your entry point, which forwards connections to your Vespa application through the private network of AWS. For this example, assume your VPC has id vpc-123 and resides in the AWS region us-east-1, and that the service ID of your endpoint service, found in the Console, is com.amazonaws.vpce.us-east-1.vpce-svc-321:

    $ aws ec2 create-vpc-endpoint \
      --region us-east-1 \
      --vpc-id vpc-123 \
      --service-name com.amazonaws.vpce.us-east-1.vpce-svc-321 \
      --vpc-endpoint-type Interface \
      --private-dns-enabled | jq .
    

    Note the value of the VpcEndpointId field, for verification in the below item. This is also where you specify optional security group and subnet IDs; these are omitted here for brevity. If creating the VPC endpoint through the AWS console instead, be sure to check "Enable DNS names"!

  4. Verify the VPC endpoint is connected to the Vespa cluster

    Navigate back to the endpoints tab in the Console, and refresh the page. You should now see a new entry representing the connection between your newly created interface endpoint, and the endpoint service on your container cluster. This is the "CONNECTED ENDPOINTS" in the image above. Verify the ID matches the value of the VpcEndpointId field above. The connection is ready when the state is open.

  5. Verify your Vespa cluster is reachable from within your VPC

    The zone endpoint of the designated container cluster should now resolve through private DNS, for any AWS resource that is allowed to connect to your VPC endpoint. The easiest way to verify this is to run the following Python 3.9 lambda, using your own zone endpoint, from within your VPC:

    from socket import gethostbyname
    from urllib.request import urlopen
    
    def lambda_handler(event, context):
        return {
            'statusCode': 200,
            'body': urlopen('https://badc0ffee.deadbeef.z.vespa-app.cloud/status.html').read(),
            'ip': gethostbyname('badc0ffee.deadbeef.z.vespa-app.cloud')
        }
    

    Alternatively, run a couple of commands from a host inside the VPC:

    $ host my-container.my-app.my-tenant.region-1.z.vespa-app.cloud
    $ curl https://my-container.my-app.my-tenant.region-1.z.vespa-app.cloud/status.html
    

    In both cases, the IP should be in one of the private IP ranges, and the HTTP response from the Vespa container endpoint should be OK.

GCP Private Service Connect

Prerequisites:

Item Description
Enabled GCP APIs The Compute Engine, Service Directory and Cloud DNS APIs must all be enabled in your GCP account:
$ gcloud services enable compute.googleapis.com
$ gcloud services enable dns.googleapis.com
$ gcloud services enable servicedirectory.googleapis.com
Your GCP project name The string identifier for your GCP account, like resonant-diode-123456
VPC network and subnetwork names The name of the network and subnetwork to create your consumer endpoint in.

Procedure:

  1. Configure a private endpoint service on your Vespa Cloud Container cluster

    Add <endpoint type="private"/> to deployment.xml, allowing access to the container cluster from the GCP account with the designated project ID. The example below allows consumer endpoints created under the private-test account to connect to the endpoint service on the my-container cluster, in each region listed under the <prod> tag.

    The example also shows how to disable the public zone endpoint, by adding the "zone" type endpoint declaration—this is an optional step, and not required to set up the private service

    <deployment version="1.0">
        <prod>
            <region>region-1</region>
            <region>region-2</region>
        </prod>
        <endpoints>
            <endpoint type="zone" container-id="my-container" enabled="false" />
            <endpoint type="private" container-id="my-container">
                <allow with="gcp-service-connect" project="private-test" />
            </endpoint>
        </endpoints>
    </deployment>

    Build and deploy the application package, and wait for it to deploy to the indicated regions.

  2. Find the service ID of your endpoint services

    Navigate to the endpoints tab for your application in the Console, and find the service ID for the deployment to wish to connect to. While there, verify that access to connect to the endpoint was granted to the correct projects.

    Service ID for VPC endpoint
  3. Create the service consumer endpoint

    Create a service consumer endpoint in your VPC. This is your entry point, which forwards connections to your Vespa application through the private network of GCP. In this example the project is named test-project, has a VPC network named test-network that resides in the GCP region us-central1, with a subnet test-subnet to hold the endpoint, behind an address to be named test-address, and the service ID of the endpoint service (found in the Console) is projects/vespa-external/regions/us-central1/serviceAttachments/scsa-xxxxxx. Finally, the endpoint is named badc0ffee, and the service directory namespace is my-tenant-my-app. See the discussion on generated endpoint names in the last item in this guide.

    Create network (if it does not already exist):

    $ gcloud compute networks create test-network
    

    Create subnet (if it does not already exist):

    $ gcloud compute networks subnets create test-subnet \
        --region=us-central1 \
        --network=test-network \
        --range=10.10.0.0/24
    

    Create the IP address which will be used for the endpoint, for clients inside your VPC:

    $ gcloud compute addresses create test-address \
        --region=us-central1 \
        --subnet=test-subnet
    

    Create a forwarding rule for traffic to the above IP, to the service endpoint in Vespa Cloud:

    $ gcloud compute forwarding-rules create badc0ffee \
        --region=us-central1 \
        --network=test-network \
        --address=test-address \
        --target-service-attachment=projects/vespa-external/regions/us-central1/serviceAttachments/scsa-xxxxxx \
        --service-directory-registration=projects/test-project/locations/us-central1/namespaces/my-tenant-my-app
    

    Note the ID of the created resource, for the verification step below.

  4. Verify the VPC endpoint is connected to the Vespa cluster

    Navigate back to the endpoints tab in the Console, and refresh the page. You should now see a new entry representing the connection between your newly created interface endpoint, and the endpoint service on your container cluster. This is the "CONNECTED ENDPOINTS" in the image above. Verify the ID matches the resource ID of the forwarding rule created above. The connection is ready when the state is open.

  5. Verify your Vespa cluster is reachable from within your VPC

    The generated endpoint name (see last items) of the designated container cluster should now resolve through private DNS inside your VPC. The easiest way to verify this is to launch an instance in your VPC, inside the designated subnet, and run a couple of commands from it:

    $ host badc0ffee.deadbeef.z.vespa-app.cloud
    $ curl https://badc0ffee.deadbeef.z.vespa-app.cloud/status.html
    

    The resolved IP address should be that of the address created earlier, and the curl command should simply output OK.

    If the endpoint fails to resolve, refer to GCP's troubleshooting documentation.

  6. Generated endpoints with Private Service Connect

    When a consumer endpoint is created with a Service Directory namespace, GCP automatically creates a private DNS record for that endpoint, which must be used instead of the IP address (created above) of the endpoint, as Vespa application containers have web certificates matching specific domain names. Unfortunately, we are unable to set the final endpoint names for the consumer endpoint; for a private endpoint service, we can only set a domain name suffix, and GCP then generates private DNS records matching your endpoint resource name prepended to this suffix. The Service Directory namespace of these endpoints must also be one-to-one with their domain name suffixes, lest the automatic setup fail.

    The domain name suffix used by Vespa Cloud is [<tenant-and-app-anonymous-id>.].z.vespa-app.cloud. We therefore encourage using the <tenant>-<application> pair as the service directory namespace, as this ensures a one-to-one mapping between suffixes and namespaces, as required by GCP (see above).

    The Vespa Cloud web certificates (see above) match any direct descendant of the domain suffix we set for your services; thus, any endpoint resource name yields a private DNS record that matches the web certificate, with Service Directory. Moreover, the zone endpoints generated by Vespa Cloud consist of a random, unique cluster-instance-region id. Using this same ID as the GCP endpoint resource name (as in the example) results in identical domain names for the private DNS set up by GCP, and the endpoint names generated by Vespa Cloud, visible in our console.