翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
Amazon VPC Lattice の SIGv4 認証リクエスト
VPC Lattice は、クライアント認証に Signature Version 4 (SIGv4) または Signature Version 4A (SIGv4A) を使用します。詳細については、「IAM ユーザーガイドAWS 」の「API リクエストの署名バージョン 4」を参照してください。
考慮事項
-
VPC Lattice は、SIGv4 または SIGv4A で署名されたリクエストの認証を試みます。認証なしではリクエストは失敗します。
-
VPC Lattice ではペイロード署名をサポートしていません。
x-amz-content-sha256
ヘッダーの値を"UNSIGNED-PAYLOAD"
に設定して送信する必要があります。
Python
この例では、署名付きリクエストを安全な接続経由でネットワークに登録されたサービスに送信します。requests
botocore
および awscrt
パッケージをインストールするには、次のコマンドを使用します。詳細については、AWS 「CRT Python
pip install botocore awscrt
Lambda でクライアントアプリケーションを実行する場合は、Lambda レイヤーを使用して必要なモジュールをインストールするか、デプロイパッケージに含めます。
次の例では、プレースホルダー値を独自の値に置き換えます。
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
この例は、カスタムインターセプターを使用してリクエスト署名を実行する方法を示しています。AWS SDK for Java 2.x
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
この例では、aws-crt NodeJS バインディング
aws-crt
パッケージをインストールするには、次のコマンドを使用します。
npm -i aws-crt
AWS_REGION
環境変数が存在する場合、この例では AWS_REGION
で指定されたリージョンを使用しています。デフォルトのリージョンは us-east-1
です。
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').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
この例では、Go 用の Smithy コードジェネレーター
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
この例では、AWS Go プログラミング言語の SDK
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")
}