Select your cookie preferences

We use essential cookies and similar tools that are necessary to provide our site and services. We use performance cookies to collect anonymous statistics, so we can understand how customers use our site and make improvements. Essential cookies cannot be deactivated, but you can choose “Customize” or “Decline” to decline performance cookies.

If you agree, AWS and approved third parties will also use cookies to provide useful site features, remember your preferences, and display relevant content, including relevant advertising. To accept or decline all non-essential cookies, choose “Accept” or “Decline.” To make more detailed choices, choose “Customize.”

SIGv4 authenticated requests for Amazon VPC Lattice - Amazon VPC Lattice

SIGv4 authenticated requests for Amazon VPC Lattice

VPC Lattice uses Signature Version 4 (SIGv4) or Signature Version 4A (SIGv4A) for client authentication. For more information, see AWS Signature Version 4 for API requests in the IAM User Guide.

Considerations
  • VPC Lattice attempts to authenticate any request that is signed with SIGv4 or SIGv4A. The request fails without authentication.

  • VPC Lattice does not support payload signing. You must send an x-amz-content-sha256 header with the value set to "UNSIGNED-PAYLOAD".

Python

This example sends the signed requests over a secure connection to a service registered in the network. If you prefer to use requests, the botocore package simplifies the authentication process, but is not strictly required. For more information, see Credentials in the Boto3 documentation.

To install the botocore and awscrt packages, use the following command. For more information, see AWS CRT Python.

pip install botocore awscrt

If you run the client application on Lambda, install the required modules using Lambda layers, or include them in your deployment package.

In the following example, replace the placeholder values with your own values.

SIGv4
from botocore import crt import requests from botocore.awsrequest import AWSRequest import botocore.session if __name__ == '__main__': session = botocore.session.Session() signer = crt.auth.CrtSigV4Auth(session.get_credentials(), 'vpc-lattice-svcs', 'us-west-2') endpoint = 'https://data-svc-022f67d3a42.1234abc.vpc-lattice-svcs.us-west-2.on.aws' data = "some-data-here" headers = {'Content-Type': 'application/json', 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD'} request = AWSRequest(method='POST', url=endpoint, data=data, headers=headers) request.context["payload_signing_enabled"] = False signer.add_auth(request) prepped = request.prepare() response = requests.post(prepped.url, headers=prepped.headers, data=data) print(response.text)
SIGv4A
from botocore import crt import requests from botocore.awsrequest import AWSRequest import botocore.session if __name__ == '__main__': session = botocore.session.Session() signer = crt.auth.CrtSigV4AsymAuth(session.get_credentials(), 'vpc-lattice-svcs', '*') endpoint = 'https://data-svc-022f67d3a42.1234abc.vpc-lattice-svcs.us-west-2.on.aws' data = "some-data-here" headers = {'Content-Type': 'application/json', 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD'} request = AWSRequest(method='POST', url=endpoint, data=data, headers=headers) request.context["payload_signing_enabled"] = False signer.add_auth(request) prepped = request.prepare() response = requests.post(prepped.url, headers=prepped.headers, data=data) print(response.text)
from botocore import crt import requests from botocore.awsrequest import AWSRequest import botocore.session if __name__ == '__main__': session = botocore.session.Session() signer = crt.auth.CrtSigV4Auth(session.get_credentials(), 'vpc-lattice-svcs', 'us-west-2') endpoint = 'https://data-svc-022f67d3a42.1234abc.vpc-lattice-svcs.us-west-2.on.aws' data = "some-data-here" headers = {'Content-Type': 'application/json', 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD'} request = AWSRequest(method='POST', url=endpoint, data=data, headers=headers) request.context["payload_signing_enabled"] = False signer.add_auth(request) prepped = request.prepare() response = requests.post(prepped.url, headers=prepped.headers, data=data) print(response.text)

Java

This example shows how you can perform request signing by using custom interceptors. It uses the default credentials provider class from AWS SDK for Java 2.x, which gets the correct credentials for you. If you would prefer to use a specific credential provider, you can select one from the AWS SDK for Java 2.x. The AWS SDK for Java allows only unsigned payloads over HTTPS. However, you can extend the signer to support unsigned payloads over HTTP.

SIGv4
package com.example; import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; import software.amazon.awssdk.http.auth.spi.signer.SignedRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.http.HttpExecuteRequest; import software.amazon.awssdk.http.HttpExecuteResponse; import java.io.IOException; import java.net.URI; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; public class sigv4 { public static void main(String[] args) { AwsV4HttpSigner signer = AwsV4HttpSigner.create(); AwsCredentialsIdentity credentials = DefaultCredentialsProvider.create().resolveCredentials(); if (args.length < 2) { System.out.println("Usage: sample <url> <region>"); System.exit(1); } // Create the HTTP request to be signed var url = args[0]; SdkHttpRequest httpRequest = SdkHttpRequest.builder() .uri(URI.create(url)) .method(SdkHttpMethod.GET) .build(); SignedRequest signedRequest = signer.sign(r -> r.identity(credentials) .request(httpRequest) .putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, "vpc-lattice-svcs") .putProperty(AwsV4HttpSigner.PAYLOAD_SIGNING_ENABLED, false) .putProperty(AwsV4HttpSigner.REGION_NAME, args[1])); System.out.println("[*] Raw request headers:"); signedRequest.request().headers().forEach((key, values) -> { values.forEach(value -> System.out.println(" " + key + ": " + value)); }); try (SdkHttpClient httpClient = ApacheHttpClient.create()) { HttpExecuteRequest httpExecuteRequest = HttpExecuteRequest.builder() .request(signedRequest.request()) .contentStreamProvider(signedRequest.payload().orElse(null)) .build(); System.out.println("[*] Sending request to: " + url); HttpExecuteResponse httpResponse = httpClient.prepareRequest(httpExecuteRequest).call(); System.out.println("[*] Request sent"); System.out.println("[*] Response status code: " + httpResponse.httpResponse().statusCode()); // Read and print the response body httpResponse.responseBody().ifPresent(inputStream -> { try { String responseBody = new String(inputStream.readAllBytes()); System.out.println("[*] Response body: " + responseBody); } catch (IOException e) { System.err.println("[*] Failed to read response body"); e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { System.err.println("[*] Failed to close input stream"); e.printStackTrace(); } } }); } catch (IOException e) { System.err.println("[*] HTTP Request Failed."); e.printStackTrace(); } } }
SIGv4A

This example requires an additional dependency on software.amazon.awssdk:http-auth-aws-crt.

package com.example; import software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.signer.SignedRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.http.HttpExecuteRequest; import software.amazon.awssdk.http.HttpExecuteResponse; import java.io.IOException; import java.net.URI; import java.util.Arrays; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; public class sigv4a { public static void main(String[] args) { AwsV4aHttpSigner signer = AwsV4aHttpSigner.create(); AwsCredentialsIdentity credentials = DefaultCredentialsProvider.create().resolveCredentials(); if (args.length < 2) { System.out.println("Usage: sample <url> <regionset>"); System.exit(1); } // Create the HTTP request to be signed var url = args[0]; SdkHttpRequest httpRequest = SdkHttpRequest.builder() .uri(URI.create(url)) .method(SdkHttpMethod.GET) .build(); SignedRequest signedRequest = signer.sign(r -> r.identity(credentials) .request(httpRequest) .putProperty(AwsV4aHttpSigner.SERVICE_SIGNING_NAME, "vpc-lattice-svcs") .putProperty(AwsV4aHttpSigner.PAYLOAD_SIGNING_ENABLED, false) .putProperty(AwsV4aHttpSigner.REGION_SET, RegionSet.create(String.join(" ",Arrays.copyOfRange(args, 1, args.length))))); System.out.println("[*] Raw request headers:"); signedRequest.request().headers().forEach((key, values) -> { values.forEach(value -> System.out.println(" " + key + ": " + value)); }); try (SdkHttpClient httpClient = ApacheHttpClient.create()) { HttpExecuteRequest httpExecuteRequest = HttpExecuteRequest.builder() .request(signedRequest.request()) .contentStreamProvider(signedRequest.payload().orElse(null)) .build(); System.out.println("[*] Sending request to: " + url); HttpExecuteResponse httpResponse = httpClient.prepareRequest(httpExecuteRequest).call(); System.out.println("[*] Request sent"); System.out.println("[*] Response status code: " + httpResponse.httpResponse().statusCode()); // Read and print the response body httpResponse.responseBody().ifPresent(inputStream -> { try { String responseBody = new String(inputStream.readAllBytes()); System.out.println("[*] Response body: " + responseBody); } catch (IOException e) { System.err.println("[*] Failed to read response body"); e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { System.err.println("[*] Failed to close input stream"); e.printStackTrace(); } } }); } catch (IOException e) { System.err.println("[*] HTTP Request Failed."); e.printStackTrace(); } } }
package com.example; import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; import software.amazon.awssdk.http.auth.spi.signer.SignedRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.http.HttpExecuteRequest; import software.amazon.awssdk.http.HttpExecuteResponse; import java.io.IOException; import java.net.URI; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; public class sigv4 { public static void main(String[] args) { AwsV4HttpSigner signer = AwsV4HttpSigner.create(); AwsCredentialsIdentity credentials = DefaultCredentialsProvider.create().resolveCredentials(); if (args.length < 2) { System.out.println("Usage: sample <url> <region>"); System.exit(1); } // Create the HTTP request to be signed var url = args[0]; SdkHttpRequest httpRequest = SdkHttpRequest.builder() .uri(URI.create(url)) .method(SdkHttpMethod.GET) .build(); SignedRequest signedRequest = signer.sign(r -> r.identity(credentials) .request(httpRequest) .putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, "vpc-lattice-svcs") .putProperty(AwsV4HttpSigner.PAYLOAD_SIGNING_ENABLED, false) .putProperty(AwsV4HttpSigner.REGION_NAME, args[1])); System.out.println("[*] Raw request headers:"); signedRequest.request().headers().forEach((key, values) -> { values.forEach(value -> System.out.println(" " + key + ": " + value)); }); try (SdkHttpClient httpClient = ApacheHttpClient.create()) { HttpExecuteRequest httpExecuteRequest = HttpExecuteRequest.builder() .request(signedRequest.request()) .contentStreamProvider(signedRequest.payload().orElse(null)) .build(); System.out.println("[*] Sending request to: " + url); HttpExecuteResponse httpResponse = httpClient.prepareRequest(httpExecuteRequest).call(); System.out.println("[*] Request sent"); System.out.println("[*] Response status code: " + httpResponse.httpResponse().statusCode()); // Read and print the response body httpResponse.responseBody().ifPresent(inputStream -> { try { String responseBody = new String(inputStream.readAllBytes()); System.out.println("[*] Response body: " + responseBody); } catch (IOException e) { System.err.println("[*] Failed to read response body"); e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { System.err.println("[*] Failed to close input stream"); e.printStackTrace(); } } }); } catch (IOException e) { System.err.println("[*] HTTP Request Failed."); e.printStackTrace(); } } }

Node.js

This example uses aws-crt NodeJS bindings to send a signed request using HTTPS.

To install the aws-crt package, use the following command.

npm -i aws-crt

If the AWS_REGION environment variable exists, the example uses the Region specified by AWS_REGION. The default Region is us-east-1.

SIGv4
const https = require('https') const crt = require('aws-crt') const { HttpRequest } = require('aws-crt/dist/native/http') function sigV4Sign(method, endpoint, service, algorithm) { const host = new URL(endpoint).host const request = new HttpRequest(method, endpoint) request.headers.add('host', host) // crt.io.enable_logging(crt.io.LogLevel.INFO) const config = { service: service, region: process.env.AWS_REGION ? process.env.AWS_REGION : 'us-east-1', algorithm: algorithm, signature_type: crt.auth.AwsSignatureType.HttpRequestViaHeaders, signed_body_header: crt.auth.AwsSignedBodyHeaderType.XAmzContentSha256, signed_body_value: crt.auth.AwsSignedBodyValue.UnsignedPayload, provider: crt.auth.AwsCredentialsProvider.newDefault() } return crt.auth.aws_sign_request(request, config) } if (process.argv.length === 2) { console.error(process.argv[1] + ' <url>') process.exit(1) } const algorithm = crt.auth.AwsSigningAlgorithm.SigV4; sigV4Sign('GET', process.argv[2], 'vpc-lattice-svcs', algorithm).then( httpResponse => { var headers = {} for (const sigv4header of httpResponse.headers) { headers[sigv4header[0]] = sigv4header[1] } const options = { hostname: new URL(process.argv[2]).host, path: new URL(process.argv[2]).pathname, method: 'GET', headers: headers } req = https.request(options, res => { console.log('statusCode:', res.statusCode) console.log('headers:', res.headers) res.on('data', d => { process.stdout.write(d) }) }) req.on('error', err => { console.log('Error: ' + err) }) req.end() } )
SIGv4A
const https = require('https') const crt = require('aws-crt') const { HttpRequest } = require('aws-crt/dist/native/http') function sigV4Sign(method, endpoint, service, algorithm) { const host = new URL(endpoint).host const request = new HttpRequest(method, endpoint) request.headers.add('host', host) // crt.io.enable_logging(crt.io.LogLevel.INFO) const config = { service: service, region: process.env.AWS_REGION ? process.env.AWS_REGION : 'us-east-1', algorithm: algorithm, signature_type: crt.auth.AwsSignatureType.HttpRequestViaHeaders, signed_body_header: crt.auth.AwsSignedBodyHeaderType.XAmzContentSha256, signed_body_value: crt.auth.AwsSignedBodyValue.UnsignedPayload, provider: crt.auth.AwsCredentialsProvider.newDefault() } return crt.auth.aws_sign_request(request, config) } if (process.argv.length === 2) { console.error(process.argv[1] + ' <url>') process.exit(1) } const algorithm = crt.auth.AwsSigningAlgorithm.SigV4Asymmetric; sigV4Sign('GET', process.argv[2], 'vpc-lattice-svcs', algorithm).then( httpResponse => { var headers = {} for (const sigv4header of httpResponse.headers) { headers[sigv4header[0]] = sigv4header[1] } const options = { hostname: new URL(process.argv[2]).host, path: new URL(process.argv[2]).pathname, method: 'GET', headers: headers } req = https.request(options, res => { console.log('statusCode:', res.statusCode) console.log('headers:', res.headers) res.on('data', d => { process.stdout.write(d) }) }) req.on('error', err => { console.log('Error: ' + err) }) req.end() } )
const https = require('https') const crt = require('aws-crt') const { HttpRequest } = require('aws-crt/dist/native/http') function sigV4Sign(method, endpoint, service, algorithm) { const host = new URL(endpoint).host const request = new HttpRequest(method, endpoint) request.headers.add('host', host) // crt.io.enable_logging(crt.io.LogLevel.INFO) const config = { service: service, region: process.env.AWS_REGION ? process.env.AWS_REGION : 'us-east-1', algorithm: algorithm, signature_type: crt.auth.AwsSignatureType.HttpRequestViaHeaders, signed_body_header: crt.auth.AwsSignedBodyHeaderType.XAmzContentSha256, signed_body_value: crt.auth.AwsSignedBodyValue.UnsignedPayload, provider: crt.auth.AwsCredentialsProvider.newDefault() } return crt.auth.aws_sign_request(request, config) } if (process.argv.length === 2) { console.error(process.argv[1] + ' <url>') process.exit(1) } const algorithm = crt.auth.AwsSigningAlgorithm.SigV4; sigV4Sign('GET', process.argv[2], 'vpc-lattice-svcs', algorithm).then( httpResponse => { var headers = {} for (const sigv4header of httpResponse.headers) { headers[sigv4header[0]] = sigv4header[1] } const options = { hostname: new URL(process.argv[2]).host, path: new URL(process.argv[2]).pathname, method: 'GET', headers: headers } req = https.request(options, res => { console.log('statusCode:', res.statusCode) console.log('headers:', res.headers) res.on('data', d => { process.stdout.write(d) }) }) req.on('error', err => { console.log('Error: ' + err) }) req.end() } )

Golang

This example uses the Smithy code generators for Go and the AWS SDK for the Go programming language to handle request signing requests. The example requires a Go version of 1.21 or higher.

SIGv4
package main import ( "context" "flag" "fmt" "io" "log" "net/http" "net/http/httputil" "os" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/smithy-go/aws-http-auth/credentials" "github.com/aws/smithy-go/aws-http-auth/sigv4" v4 "github.com/aws/smithy-go/aws-http-auth/v4" ) type nopCloser struct { io.ReadSeeker } func (nopCloser) Close() error { return nil } type stringFlag struct { set bool value string } flag.PrintDefaults() os.Exit(1) } func main() { flag.Parse() if !url.set || !region.set { Usage() } cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithClientLogMode(aws.LogSigning)) if err != nil { log.Fatalf("failed to load SDK configuration, %v", err) } if len(os.Args) < 2 { log.Fatalf("Usage: go run main.go <url>") } // Retrieve credentials from an SDK source, such as the instance profile sdkCreds, err := cfg.Credentials.Retrieve(context.TODO()) if err != nil { log.Fatalf("Unable to retrieve credentials from SDK, %v", err) } creds := credentials.Credentials{ AccessKeyID: sdkCreds.AccessKeyID, SecretAccessKey: sdkCreds.SecretAccessKey, SessionToken: sdkCreds.SessionToken, } // Add a payload body, which will not be part of the signature calculation body := nopCloser{strings.NewReader(`Example payload body`)} req, _ := http.NewRequest(http.MethodPost, url.value, body) // Create a sigv4a signer with specific options signer := sigv4.New(func(o *v4.SignerOptions) { o.DisableDoublePathEscape = true // This will add the UNSIGNED-PAYLOAD sha256 header o.AddPayloadHashHeader = true o.DisableImplicitPayloadHashing = true }) // Perform the signing on req, using the credentials we retrieved from the SDK err = signer.SignRequest(&sigv4.SignRequestInput{ Request: req, Credentials: creds, Service: "vpc-lattice-svcs", Region: region.String(), }) if err != nil { log.Fatalf("%s", err) } res, err := httputil.DumpRequest(req, true) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Raw request\n%s\n", string(res)) log.Printf("[*] Sending request to %s\n", url.value) resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Request sent\n") log.Printf("[*] Response status code: %d\n", resp.StatusCode) respBody, err := io.ReadAll(resp.Body) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Response body: \n%s\n", respBody) }
SIGv4A
package main import ( "context" "flag" "fmt" "io" "log" "net/http" "net/http/httputil" "os" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/smithy-go/aws-http-auth/credentials" "github.com/aws/smithy-go/aws-http-auth/sigv4a" v4 "github.com/aws/smithy-go/aws-http-auth/v4" ) type nopCloser struct { io.ReadSeeker } func (nopCloser) Close() error { return nil } type stringFlag struct { func main() { flag.Parse() if !url.set || !regionSet.set { Usage() } cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithClientLogMode(aws.LogSigning)) if err != nil { log.Fatalf("failed to load SDK configuration, %v", err) } if len(os.Args) < 2 { log.Fatalf("Usage: go run main.go <url>") } // Retrieve credentials from an SDK source, such as the instance profile sdkCreds, err := cfg.Credentials.Retrieve(context.TODO()) if err != nil { log.Fatalf("Unable to retrieve credentials from SDK, %v", err) } creds := credentials.Credentials{ AccessKeyID: sdkCreds.AccessKeyID, SecretAccessKey: sdkCreds.SecretAccessKey, SessionToken: sdkCreds.SessionToken, } // Add a payload body, which will not be part of the signature calculation body := nopCloser{strings.NewReader(`Example payload body`)} req, _ := http.NewRequest(http.MethodPost, url.value, body) // Create a sigv4a signer with specific options signer := sigv4a.New(func(o *v4.SignerOptions) { o.DisableDoublePathEscape = true // This will add the UNSIGNED-PAYLOAD sha256 header o.AddPayloadHashHeader = true o.DisableImplicitPayloadHashing = true }) // Create a slice out of the provided regionset rs := strings.Split(regionSet.value, ",") // Perform the signing on req, using the credentials we retrieved from the SDK err = signer.SignRequest(&sigv4a.SignRequestInput{ Request: req, Credentials: creds, Service: "vpc-lattice-svcs", RegionSet: rs, }) if err != nil { log.Fatalf("%s", err) } res, err := httputil.DumpRequest(req, true) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Raw request\n%s\n", string(res)) log.Printf("[*] Sending request to %s\n", url.value) resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Request sent\n") log.Printf("[*] Response status code: %d\n", resp.StatusCode) respBody, err := io.ReadAll(resp.Body) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Response body: \n%s\n", respBody) }
package main import ( "context" "flag" "fmt" "io" "log" "net/http" "net/http/httputil" "os" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/smithy-go/aws-http-auth/credentials" "github.com/aws/smithy-go/aws-http-auth/sigv4" v4 "github.com/aws/smithy-go/aws-http-auth/v4" ) type nopCloser struct { io.ReadSeeker } func (nopCloser) Close() error { return nil } type stringFlag struct { set bool value string } flag.PrintDefaults() os.Exit(1) } func main() { flag.Parse() if !url.set || !region.set { Usage() } cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithClientLogMode(aws.LogSigning)) if err != nil { log.Fatalf("failed to load SDK configuration, %v", err) } if len(os.Args) < 2 { log.Fatalf("Usage: go run main.go <url>") } // Retrieve credentials from an SDK source, such as the instance profile sdkCreds, err := cfg.Credentials.Retrieve(context.TODO()) if err != nil { log.Fatalf("Unable to retrieve credentials from SDK, %v", err) } creds := credentials.Credentials{ AccessKeyID: sdkCreds.AccessKeyID, SecretAccessKey: sdkCreds.SecretAccessKey, SessionToken: sdkCreds.SessionToken, } // Add a payload body, which will not be part of the signature calculation body := nopCloser{strings.NewReader(`Example payload body`)} req, _ := http.NewRequest(http.MethodPost, url.value, body) // Create a sigv4a signer with specific options signer := sigv4.New(func(o *v4.SignerOptions) { o.DisableDoublePathEscape = true // This will add the UNSIGNED-PAYLOAD sha256 header o.AddPayloadHashHeader = true o.DisableImplicitPayloadHashing = true }) // Perform the signing on req, using the credentials we retrieved from the SDK err = signer.SignRequest(&sigv4.SignRequestInput{ Request: req, Credentials: creds, Service: "vpc-lattice-svcs", Region: region.String(), }) if err != nil { log.Fatalf("%s", err) } res, err := httputil.DumpRequest(req, true) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Raw request\n%s\n", string(res)) log.Printf("[*] Sending request to %s\n", url.value) resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Request sent\n") log.Printf("[*] Response status code: %d\n", resp.StatusCode) respBody, err := io.ReadAll(resp.Body) if err != nil { log.Fatalf("%s", err) } log.Printf("[*] Response body: \n%s\n", respBody) }

Golang - GRPC

This example uses the AWS SDK for the Go programming language to handle request signing for GRPC requests. This can be used with the echo server from the GRPC sample code repository.

package main import ( "context" "crypto/tls" "crypto/x509" "flag" "fmt" "log" "net/http" "net/url" "strings" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "github.com/aws/aws-sdk-go-v2/aws" v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" "github.com/aws/aws-sdk-go-v2/config" ecpb "google.golang.org/grpc/examples/features/proto/echo" ) const ( headerContentSha = "x-amz-content-sha256" headerSecurityToken = "x-amz-security-token" headerDate = "x-amz-date" headerAuthorization = "authorization" unsignedPayload = "UNSIGNED-PAYLOAD" ) type SigV4GrpcSigner struct { service string region string credProvider aws.CredentialsProvider signer *v4.Signer } func NewSigV4GrpcSigner(service string, region string, credProvider aws.CredentialsProvider) *SigV4GrpcSigner { signer := v4.NewSigner() return &SigV4GrpcSigner{ service: service, region: region, credProvider: credProvider, signer: signer, } } func (s *SigV4GrpcSigner) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { ri, _ := credentials.RequestInfoFromContext(ctx) creds, err := s.credProvider.Retrieve(ctx) if err != nil { return nil, fmt.Errorf("failed to load credentials: %w", err) } // The URI we get here is scheme://authority/service/ - for siging we want to include the RPC name // But RequestInfoFromContext only has the combined /service/rpc-name - so read the URI, and // replace the Path with what we get from RequestInfo. parsed, err := url.Parse(uri[0]) if err != nil { return nil, err } parsed.Path = ri.Method // Build a request for the signer. bodyReader := strings.NewReader("") req, err := http.NewRequest("POST", uri[0], bodyReader) if err != nil { return nil, err } date := time.Now() req.Header.Set(headerContentSha, unsignedPayload) req.Header.Set(headerDate, date.String()) if creds.SessionToken != "" { req.Header.Set(headerSecurityToken, creds.SessionToken) } // The signer wants this as //authority/path // So get this by triming off the scheme and the colon before the first slash. req.URL.Opaque = strings.TrimPrefix(parsed.String(), parsed.Scheme+":") err = s.signer.SignHTTP(context.Background(), creds, req, unsignedPayload, s.service, s.region, date) if err != nil { return nil, fmt.Errorf("failed to sign request: %w", err) } // Pull the relevant headers out of the signer, and return them to get // included in the request we make. reqHeaders := map[string]string{ headerContentSha: req.Header.Get(headerContentSha), headerDate: req.Header.Get(headerDate), headerAuthorization: req.Header.Get(headerAuthorization), } if req.Header.Get(headerSecurityToken) != "" { reqHeaders[headerSecurityToken] = req.Header.Get(headerSecurityToken) } return reqHeaders, nil } func (c *SigV4GrpcSigner) RequireTransportSecurity() bool { return true } var addr = flag.String("addr", "some-lattice-service:443", "the address to connect to") var region = flag.String("region", "us-west-2", "region") func callUnaryEcho(client ecpb.EchoClient, message string) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() resp, err := client.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message}) if err != nil { log.Fatalf("client.UnaryEcho(_) = _, %v: ", err) } fmt.Println("UnaryEcho: ", resp.Message) } func main() { flag.Parse() cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithClientLogMode(aws.LogSigning)) if err != nil { log.Fatalf("failed to load SDK configuration, %v", err) } pool, _ := x509.SystemCertPool() tlsConfig := &tls.Config{ RootCAs: pool, } authority, _, _ := strings.Cut(*addr, ":") // Remove the port from the addr opts := []grpc.DialOption{ grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), // Lattice needs both the Authority to be set (without a port), and the SigV4 signer grpc.WithAuthority(authority), grpc.WithPerRPCCredentials(NewSigV4GrpcSigner("vpc-lattice-svcs", *region, cfg.Credentials)), } conn, err := grpc.Dial(*addr, opts...) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() rgc := ecpb.NewEchoClient(conn) callUnaryEcho(rgc, "hello world") }
PrivacySite termsCookie preferences
© 2025, Amazon Web Services, Inc. or its affiliates. All rights reserved.