Quantcast
Channel: ablog
Viewing all 1154 articles
Browse latest View live

[AWS]VPCエンドポイントポリシーを変更すると CWE で検知して Lambda でリセットするようにしてみた

$
0
0

VPCエンドポイントポリシーを変更すると CloudWatch Events で検知して Lambda でリセットするようにしてみた。


設定

IAMロールを作成する
  • 名前: LambdaVpcEndpoint
  • インラインポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeVpcEndpoints",
                "ec2:ModifyVpcEndpoint"
            ],
            "Resource": "*"
        }
    ]
}

Lambda 関数を作成する
  • 名前: rollbackEndpointPolicy
  • ランタイム: Python 2.7
  • ロール: LambdaVpcEndpoint
  • 関数コード
import json
import boto3
ec2 = boto3.client('ec2')

def lambda_handler(event, context):
    print( 'event: ', event )
    vpcendpoint_id=event['detail']['requestParameters']['ModifyVpcEndpointRequest']['VpcEndpointId']
#    vpcendpoint_id=event['requestParameters']['ModifyVpcEndpointRequest']['VpcEndpointId']
    res_endpoint_desc = ec2.describe_vpc_endpoints(VpcEndpointIds=[vpcendpoint_id])
    service_name=res_endpoint_desc['VpcEndpoints'][0]['ServiceName']
    policy_doc=res_endpoint_desc['VpcEndpoints'][0]['PolicyDocument']
    if service_name.endswith(".s3") and policy_doc!='{"Version":"2008-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"*","Resource":"*"}]}':
        res_mod_endpoint = ec2.modify_vpc_endpoint(
            DryRun=False,
            VpcEndpointId=vpcendpoint_id,
            ResetPolicy=True
        )
    return 'rollbackEndpointPolicy finished!'

CloudWatch Events
  • [CloudWatch]-[イベント]-[ルール]を選択し、[ルールの作成]をクリックしてルールを作成する。
  • [イベントパターン]を選択する。
  • サービス名: EC2
  • イベントタイプ: AWS API Call via CloudTrail
  • 特定のオペレーション: ModifyVpcEndpoint
  • Lambda関数の機能で " rollbackEndpointPolicy" を指定する。
  • 名前: rollbackEndpointPolicy
{
  "source": [
    "aws.ec2"
  ],
  "detail-type": [
    "AWS API Call via CloudTrail"
  ],
  "detail": {
    "eventSource": [
      "ec2.amazonaws.com"
    ],
    "eventName": [
      "ModifyVpcEndpoint"
    ]
  }
}


実行してみる

VPCエンドポイントポリシーを変更する 
  • [VPC]-[エンドポイント]で、エンドポイントを選択して[ポリシー]タブを選択して[ポリシーの編集]をクリックする。
  • [カスタム]を選択して以下の通り変更して、[保存]をクリックする。

f:id:yohei-a:20181002120801p:image:w640

{
  "Id": "Policy1538445504510",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1538445501263",
      "Action": [
        "s3:*"
      ],
      "Effect": "Allow",
      "Resource": "*",
      "Principal": "*"
    }
  ]
}
CloudWatch Logs
  • [CloudWatc]-[ログ]で "/aws/lambda/rollbackEndpointPolicy" をクリックし、最新のログ(一番上)をクリックする。
02:44:47 START RequestId: 1fab2160-c5ed-11e8-aa66-db2cfffe3c67 Version: $LATEST
02:44:47 ('event: ...
02:44:47 END RequestId: 1fab2160-c5ed-11e8-aa66-db2cfffe3c67
02:44:47 REPORT RequestId: 1fab2160-c5ed-11e8-aa66-db2cfffe3c67Duration: 406.19 msBilled Duration: 500 ms Memory Size: 128 MBMax Memory Used: 46 MB
02:44:54 START RequestId: 2495fb78-c5ed-11e8-868c-1debbeb114d2 Version: $LATEST
02:44:54 ('event: ', ...
02:44:54 END RequestId: 2495fb78-c5ed-11e8-868c-1debbeb114d2
02:44:54 REPORT RequestId: 2495fb78-c5ed-11e8-868c-1debbeb114d2Duration: 228.92 msBilled Duration: 300 ms Memory Size: 128 MBMax Memory Used: 46 MB
  • [VPC]-[エンドポイント]で、エンドポイントを選択して[ポリシー]タブを選択し、ポリシーが元に戻っていることを確認する。

f:id:yohei-a:20181002121857p:image

CloudTrail
  • マネジメントコンソールでVPCエンドポイントポリシーを変更した際のイベント

f:id:yohei-a:20181002122801p:image

{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "...:yoheia",
        "arn": "arn:aws:sts::...:assumed-role/AdminRole/yoheia",
        "accountId": "...",
        "accessKeyId": "...",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2018-10-02T02:06:37Z"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "...",
                "arn": "arn:aws:iam::...:role/AdminRole",
                "accountId": "...",
                "userName": "AdminRole"
            }
        }
    },
    "eventTime": "2018-10-02T02:43:45Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "ModifyVpcEndpoint",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "**.*.*.145",
    "userAgent": "console.ec2.amazonaws.com",
    "requestParameters": {
        "ModifyVpcEndpointRequest": {
            "PolicyDocument": "{\n    \"Id\": \"Policy1538445504510\",\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"Stmt1538445501263\",\n            \"Action\": [\n                \"s3:*\"\n            ],\n            \"Effect\": \"Allow\",\n            \"Resource\": \"*\",\n            \"Principal\": \"*\"\n        }\n    ]\n}",
            "VpcEndpointId": "vpce-..."
        }
    },
    "responseElements": {
        "ModifyVpcEndpointResponse": {
            "xmlns": "http://ec2.amazonaws.com/doc/2016-11-15/",
            "requestId": "22bc133d-997b-4686-b5d7-1c6189a79006",
            "return": true
        }
    },
    "requestID": "22bc133d-997b-4686-b5d7-1c6189a79006",
    "eventID": "cccfde87-22b8-4862-858d-00954013e814",
    "eventType": "AwsApiCall",
    "recipientAccountId": "..."
}
  • Lambda で自動修正された際のイベント

f:id:yohei-a:20181002122758p:image



{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "...:rollbackEndpointPolicy",
        "arn": "arn:aws:sts::...:assumed-role/LambdaVpcEndpoint/rollbackEndpointPolicy",
        "accountId": "...",
        "accessKeyId": "...",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2018-10-02T02:44:46Z"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "...",
                "arn": "arn:aws:iam::...:role/LambdaVpcEndpoint",
                "accountId": "...",
                "userName": "LambdaVpcEndpoint"
            }
        }
    },
    "eventTime": "2018-10-02T02:44:47Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "ModifyVpcEndpoint",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "13.230.96.217",
    "userAgent": "Boto3/1.7.74 Python/2.7.12 Linux/4.14.67-66.56.amzn1.x86_64 exec-env/AWS_Lambda_python2.7 Botocore/1.10.74",
    "requestParameters": {
        "ModifyVpcEndpointRequest": {
            "ResetPolicy": true,
            "VpcEndpointId": "vpce-..."
        }
    },
    "responseElements": {
        "ModifyVpcEndpointResponse": {
            "xmlns": "http://ec2.amazonaws.com/doc/2016-11-15/",
            "requestId": "86bc748b-e09c-4ac2-acb1-081be51434cc",
            "return": true
        }
    },
    "requestID": "86bc748b-e09c-4ac2-acb1-081be51434cc",
    "eventID": "527fc03f-39f1-47a8-906a-273d36bfcd51",
    "eventType": "AwsApiCall",
    "recipientAccountId": "..."
}

参考


[Oracle][PostgreSQL]Oracle->PostgreSQL移行関連

$
0
0

[AWS]特定のLambda関数の編集・実行を制限する

$
0
0

アプリケーションをデプロイする人に特定のLambda関数だけにはアクセスできないようにするには、ResourceでLambda関数を指定してDenyするIAMポリシーを作成してIAMロールにアタッチし、このLambda関数の編集や実行を許可する以外のIAMユーザーにIAMロールをアタッチすると実現できる。面倒だが。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Deny",
            "Action": "lambda:*", ★アクションを絞るとさらにきめ細かい制御が可能
            "Resource": [
                "arn:aws:lambda:ap-northeast-1:123456789012:function:lambdaFunction1",
                "arn:aws:lambda:ap-northeast-1:123456789012:function:lambdaFunction2"
            ]
        }
    ]
}

[AWS]EC2に Netflix Vector をインストールしてみた

$
0
0

EC2 に Netflix Vector をインストールしてみた。


インストール・設定

  • pcp のインストール
$ sudo yum -y install git flex bison libmicrohttpd libmicrohttpd-devel http-parser
$ git clone https://github.com/performancecopilot/pcp.git
$ cd pcp
$ ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --with-webapi
$ make
$ sudo groupadd -r pcp
$ sudo useradd -c "Performance Co-Pilot" -g pcp -d /var/lib/pcp -M -r -s /usr/sbin/nologin pcp
$ sudo  make install
  • pcpの起動
$ sudo service pmcd start
$ sudo service pmwebd start
  • Apache のインストール・起動
$ sudo yum -y install httpd
$ sudo service httpd start
  • Vector のインストール・実行
$ cd /var/www/html
$ sudo wget https://dl.bintray.com/netflixoss/downloads/1.3.1/vector.tar.gz
$ sudo tar xvzf vector.tar.gz
$ sudo chown -R apache:apache dist

性能情報を確認する

f:id:yohei-a:20181008231740p:image


参考

*1:セキュリティグループでアクセス許可しておく

[Mac]ssh で Amazon Linux on EC2 に接続しようとすると ”Permission denied (publickey)” エラー

[Java]jar を解凍してクラスをデコンパイルして Java コードを調査する

$
0
0
jar xf foo.jar
cd com
find . -type f -name '*.class'|xargs -n 1 jad
find . -name '*.jad'|xargs -n 1 perl -nle '/md5/i and print qq/$ARGV:$_/'

[Java]perf-map-agent 実行時に ”AttachNotSupportedException: Unable to open socket file” と怒られる

$
0
0

事象

perf-map-agent をビルドして

$ sudo yum -y install cmake
$ git clone --depth=1 https://github.com/jvm-profiling-tools/perf-map-agent
$ cd perf-map-agent
$ cmake .
$ make
$ cd out

/etc/hadoop/conf/ 以下の hadoop-env.sh, yarn-env.sh にexport HADOOP_OPTS="... -XX:+PreserveFramePointer" export YARN_OPTS="... -XX:+PreserveFramePointer" を追加して、hdfs の jvm を kill して再起動。ps で -XX:+PreserveFramePointer が入っていることを確認してから、hdfs のプロセスにアタッチしようとすると "AttachNotSupportedException: Unable to open socket file" と怒られる

$ ps -elf|grep hdfs
$ sudo -u hdfs java -cp attach-main.jar:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce 6362
Exception in thread "main" http://com.sun.tools .attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded

調査

スモールケースで調べてみた。

カレントディレクトリを perf-map-agent/out にして、

$ cd /home/hadoop/perf-map-agent/out

DoWhileTrueJava.java を作成して

public class DoWhileTrueJava {
public static void main(String[] args) throws InterruptedException {
System.out.println("Start Processing inside do while loop");
do {
Thread.sleep(5 * 1000);
} while (true);
}
}

コンパイルして

$ javac DoWhileTrueJava.java

DoWhileTrueJava を実行して

$ java  DoWhileTrueJava &
[2] 3995
Start Processing inside do while loop

perf-map-agent で DoWhileTrueJava のJavaプロセスにアタッチしてみると、NoClassDefFoundError で怒られる。

$ java -cp attach-main.jar:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce 3995
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/attach/AgentInitializationException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: com.sun.tools.attach.AgentInitializationException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more

あら、$JAVA_HOME/lib/tools.jar がない

$ ls -l $JAVA_HOME/lib/tools.jar
ls: cannot access /etc/alternatives/jre/lib/tools.jar: No such file or directory

探すと見つかったので、

$ sudo find /usr -name tools.jar
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/lib/tools.jar

お、成功した

$ java -cp /home/hadoop/perf-map-agent/out/attach-main.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/lib/tools.jar net.virtualvoid.perf.AttachOnce 3995

うまくいってますね

$ head /tmp/perf-3995.map
7fb5ad000060 1d flush_icache_stub
7fb5ad000160 21b get_cpu_info_stub
7fb5ad000420 3f forward exception
7fb5ad00045f e8 call_stub
7fb5ad000547 1f catch_exception
7fb5ad000566 5 atomic_xchg
7fb5ad00056b 7 atomic_xchg_ptr
7fb5ad000572 7 atomic_cmpxchg
7fb5ad000579 9 atomic_cmpxchg_long
7fb5ad000582 9 atomic_add

hdfs のプロセスに対しても成功

$ ps -elf|grep hdfs
$ sudo -u hdfs java -cp /home/hadoop/perf-map-agent/out/attach-main.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/lib/tools.jar net.virtualvoid.perf.AttachOnce 8837

情報とれてますね

$ head /tmp/perf-8837.map
7f1989000060 1d flush_icache_stub
7f1989000160 21b get_cpu_info_stub
7f1989000420 3f forward exception
7f198900045f e8 call_stub
7f1989000547 1f catch_exception
7f1989000566 5 atomic_xchg
7f198900056b 7 atomic_xchg_ptr
7f1989000572 7 atomic_cmpxchg
7f1989000579 9 atomic_cmpxchg_long
7f1989000582 9 atomic_add

関連


参考

[Presto][Parquet]Presto で Parquet にクエリするとファイル中の必要な Column chunk のみを読んでいるか

$
0
0

f:id:yohei-a:20181021053908j:image:w360 f:id:yohei-a:20181021053904j:image:w360 f:id:yohei-a:20181021053900j:image:w360

Column chunks

Column chunks are composed of pages written back to back. The pages share a common header and readers can skip over page they are not interested in. The data for the page follows the header and can be compressed and/or encoded. The compression and encoding is specified in the page metadata.

https://parquet.apache.org/documentation/latest/

f:id:yohei-a:20181021052338p:image:w640

f:id:yohei-a:20181021052335p:image:w640

https://events.static.linuxfound.org/sites/events/files/slides/Presto.pdf

(1) read only required columns in Parquet and build columnar blocks on the fly, saving CPU and memory to transform row-based Parquet records into columnar blocks, and (2) evaluate the predicate using columnar blocks in the Presto engine.

no title

New Hive Parquet Reader

We have added a new Parquet reader implementation. The new reader supports vectorized reads, lazy loading, and predicate push down, all of which make the reader more efficient and typically reduces wall clock time for a query. Although the new reader has been heavily tested, it is an extensive rewrite of the Apache Hive Parquet reader, and may have some latent issues, so it is not enabled by default. If you are using Parquet we suggest you test out the new reader on a per-query basis by setting the <hive-catalog>.parquet_optimized_reader_enabled session property, or you can enable the reader by default by setting the Hive catalog property hive.parquet-optimized-reader.enabled=true. To enable Parquet predicate push down there is a separate session property <hive-catalog>.parquet_predicate_pushdown_enabled and configuration property hive.parquet-predicate-pushdown.enabled=true.

https://prestodb.io/docs/current/release/release-0.138.html

f:id:yohei-a:20181021051335p:image

Hadoop Internals for Oracle Developers and DBAs: Strata Conference + Hadoop World 2013 - O'Reilly Conferences, October 28 - 30, 2013, New York, NY
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.facebook.presto.parquet.reader;

import com.facebook.presto.parquet.RichColumnDescriptor;
import com.facebook.presto.spi.block.BlockBuilder;
import com.facebook.presto.spi.type.Type;
import io.airlift.slice.Slice;
import parquet.io.api.Binary;

import static com.facebook.presto.spi.type.Chars.isCharType;
import static com.facebook.presto.spi.type.Chars.truncateToLengthAndTrimSpaces;
import static com.facebook.presto.spi.type.Varchars.isVarcharType;
import static com.facebook.presto.spi.type.Varchars.truncateToLength;
import static io.airlift.slice.Slices.EMPTY_SLICE;
import static io.airlift.slice.Slices.wrappedBuffer;

public class BinaryColumnReader
        extends PrimitiveColumnReader
{
    public BinaryColumnReader(RichColumnDescriptor descriptor)
    {
        super(descriptor);
    }

    @Override
    protected void readValue(BlockBuilder blockBuilder, Type type)
    {
        if (definitionLevel == columnDescriptor.getMaxDefinitionLevel()) {
            Binary binary = valuesReader.readBytes();
            Slice value;
            if (binary.length() == 0) {
                value = EMPTY_SLICE;
            }
            else {
                value = wrappedBuffer(binary.getBytes());
            }
            if (isVarcharType(type)) {
                value = truncateToLength(value, type);
            }
            if (isCharType(type)) {
                value = truncateToLengthAndTrimSpaces(value, type);
            }
            type.writeSlice(blockBuilder, value);
        }
        else if (isValueNull()) {
            blockBuilder.appendNull();
        }
    }

    @Override
    protected void skipValue()
    {
        if (definitionLevel == columnDescriptor.getMaxDefinitionLevel()) {
            valuesReader.readBytes();
        }
    }
}
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.facebook.presto.parquet.reader;

import com.facebook.presto.parquet.DataPage;
import com.facebook.presto.parquet.DataPageV1;
import com.facebook.presto.parquet.DataPageV2;
import com.facebook.presto.parquet.DictionaryPage;
import com.facebook.presto.parquet.Field;
import com.facebook.presto.parquet.ParquetEncoding;
import com.facebook.presto.parquet.ParquetTypeUtils;
import com.facebook.presto.parquet.RichColumnDescriptor;
import com.facebook.presto.parquet.dictionary.Dictionary;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.block.BlockBuilder;
import com.facebook.presto.spi.type.DecimalType;
import com.facebook.presto.spi.type.Type;
import io.airlift.slice.Slice;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import parquet.bytes.BytesUtils;
import parquet.column.ColumnDescriptor;
import parquet.column.values.ValuesReader;
import parquet.column.values.rle.RunLengthBitPackingHybridDecoder;
import parquet.io.ParquetDecodingException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Consumer;

import static com.facebook.presto.parquet.ParquetTypeUtils.createDecimalType;
import static com.facebook.presto.parquet.ValuesType.DEFINITION_LEVEL;
import static com.facebook.presto.parquet.ValuesType.REPETITION_LEVEL;
import static com.facebook.presto.parquet.ValuesType.VALUES;
import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;

public abstract class PrimitiveColumnReader
{
    private static final int EMPTY_LEVEL_VALUE = -1;
    protected final RichColumnDescriptor columnDescriptor;

    protected int definitionLevel = EMPTY_LEVEL_VALUE;
    protected int repetitionLevel = EMPTY_LEVEL_VALUE;
    protected ValuesReader valuesReader;

    private int nextBatchSize;
    private LevelReader repetitionReader;
    private LevelReader definitionReader;
    private long totalValueCount;
    private PageReader pageReader;
    private Dictionary dictionary;
    private int currentValueCount;
    private DataPage page;
    private int remainingValueCountInPage;
    private int readOffset;

    protected abstract void readValue(BlockBuilder blockBuilder, Type type);

    protected abstract void skipValue();

    protected boolean isValueNull()
    {
        return ParquetTypeUtils.isValueNull(columnDescriptor.isRequired(), definitionLevel, columnDescriptor.getMaxDefinitionLevel());
    }

    public static PrimitiveColumnReader createReader(RichColumnDescriptor descriptor)
    {
        switch (descriptor.getType()) {
            case BOOLEAN:
                return new BooleanColumnReader(descriptor);
            case INT32:
                return createDecimalColumnReader(descriptor).orElse(new IntColumnReader(descriptor));
            case INT64:
                return createDecimalColumnReader(descriptor).orElse(new LongColumnReader(descriptor));
            case INT96:
                return new TimestampColumnReader(descriptor);
            case FLOAT:
                return new FloatColumnReader(descriptor);
            case DOUBLE:
                return new DoubleColumnReader(descriptor);
            case BINARY:
                return createDecimalColumnReader(descriptor).orElse(new BinaryColumnReader(descriptor));
            case FIXED_LEN_BYTE_ARRAY:
                return createDecimalColumnReader(descriptor)
                        .orElseThrow(() -> new PrestoException(NOT_SUPPORTED, " type FIXED_LEN_BYTE_ARRAY supported as DECIMAL; got " + descriptor.getPrimitiveType().getOriginalType()));
            default:
                throw new PrestoException(NOT_SUPPORTED, "Unsupported parquet type: " + descriptor.getType());
        }
    }

    private static Optional<PrimitiveColumnReader> createDecimalColumnReader(RichColumnDescriptor descriptor)
    {
        Optional<Type> type = createDecimalType(descriptor);
        if (type.isPresent()) {
            DecimalType decimalType = (DecimalType) type.get();
            return Optional.of(DecimalColumnReaderFactory.createReader(descriptor, decimalType.getPrecision(), decimalType.getScale()));
        }
        return Optional.empty();
    }

    public PrimitiveColumnReader(RichColumnDescriptor columnDescriptor)
    {
        this.columnDescriptor = requireNonNull(columnDescriptor, "columnDescriptor");
        pageReader = null;
    }

    public PageReader getPageReader()
    {
        return pageReader;
    }

    public void setPageReader(PageReader pageReader)
    {
        this.pageReader = requireNonNull(pageReader, "pageReader");
        DictionaryPage dictionaryPage = pageReader.readDictionaryPage();

        if (dictionaryPage != null) {
            try {
                dictionary = dictionaryPage.getEncoding().initDictionary(columnDescriptor, dictionaryPage);
            }
            catch (IOException e) {
                throw new ParquetDecodingException("could not decode the dictionary for " + columnDescriptor, e);
            }
        }
        else {
            dictionary = null;
        }
        checkArgument(pageReader.getTotalValueCount() > 0, "page is empty");
        totalValueCount = pageReader.getTotalValueCount();
    }

    public void prepareNextRead(int batchSize)
    {
        readOffset = readOffset + nextBatchSize;
        nextBatchSize = batchSize;
    }

    public ColumnDescriptor getDescriptor()
    {
        return columnDescriptor;
    }

    public ColumnChunk readPrimitive(Field field)
            throws IOException
    {
        IntList definitionLevels = new IntArrayList();
        IntList repetitionLevels = new IntArrayList();
        seek();
        BlockBuilder blockBuilder = field.getType().createBlockBuilder(null, nextBatchSize);
        int valueCount = 0;
        while (valueCount < nextBatchSize) {
            if (page == null) {
                readNextPage();
            }
            int valuesToRead = Math.min(remainingValueCountInPage, nextBatchSize - valueCount);
            readValues(blockBuilder, valuesToRead, field.getType(), definitionLevels, repetitionLevels);
            valueCount += valuesToRead;
        }
        checkArgument(valueCount == nextBatchSize, "valueCount %s not equals to batchSize %s", valueCount, nextBatchSize);

        readOffset = 0;
        nextBatchSize = 0;
        return new ColumnChunk(blockBuilder.build(), definitionLevels.toIntArray(), repetitionLevels.toIntArray());
    }

    private void readValues(BlockBuilder blockBuilder, int valuesToRead, Type type, IntList definitionLevels, IntList repetitionLevels)
    {
        processValues(valuesToRead, ignored -> {
            readValue(blockBuilder, type);
            definitionLevels.add(definitionLevel);
            repetitionLevels.add(repetitionLevel);
        });
    }

    private void skipValues(int valuesToRead)
    {
        processValues(valuesToRead, ignored -> skipValue());
    }

    private void processValues(int valuesToRead, Consumer<Void> valueConsumer)
    {
        if (definitionLevel == EMPTY_LEVEL_VALUE && repetitionLevel == EMPTY_LEVEL_VALUE) {
            definitionLevel = definitionReader.readLevel();
            repetitionLevel = repetitionReader.readLevel();
        }
        int valueCount = 0;
        for (int i = 0; i < valuesToRead; i++) {
            do {
                valueConsumer.accept(null);
                valueCount++;
                if (valueCount == remainingValueCountInPage) {
                    updateValueCounts(valueCount);
                    if (!readNextPage()) {
                        return;
                    }
                    valueCount = 0;
                }
                repetitionLevel = repetitionReader.readLevel();
                definitionLevel = definitionReader.readLevel();
            }
            while (repetitionLevel != 0);
        }
        updateValueCounts(valueCount);
    }

    private void seek()
    {
        checkArgument(currentValueCount <= totalValueCount, "Already read all values in column chunk");
        if (readOffset == 0) {
            return;
        }
        int valuePosition = 0;
        while (valuePosition < readOffset) {
            if (page == null) {
                readNextPage();
            }
            int offset = Math.min(remainingValueCountInPage, readOffset - valuePosition);
            skipValues(offset);
            valuePosition = valuePosition + offset;
        }
        checkArgument(valuePosition == readOffset, "valuePosition %s must be equal to readOffset %s", valuePosition, readOffset);
    }

    private boolean readNextPage()
    {
        verify(page == null, "readNextPage has to be called when page is null");
        page = pageReader.readPage();
        if (page == null) {
            // we have read all pages
            return false;
        }
        remainingValueCountInPage = page.getValueCount();
        if (page instanceof DataPageV1) {
            valuesReader = readPageV1((DataPageV1) page);
        }
        else {
            valuesReader = readPageV2((DataPageV2) page);
        }
        return true;
    }

    private void updateValueCounts(int valuesRead)
    {
        if (valuesRead == remainingValueCountInPage) {
            page = null;
            valuesReader = null;
        }
        remainingValueCountInPage -= valuesRead;
        currentValueCount += valuesRead;
    }

    private ValuesReader readPageV1(DataPageV1 page)
    {
        ValuesReader rlReader = page.getRepetitionLevelEncoding().getValuesReader(columnDescriptor, REPETITION_LEVEL);
        ValuesReader dlReader = page.getDefinitionLevelEncoding().getValuesReader(columnDescriptor, DEFINITION_LEVEL);
        repetitionReader = new LevelValuesReader(rlReader);
        definitionReader = new LevelValuesReader(dlReader);
        try {
            byte[] bytes = page.getSlice().getBytes();
            rlReader.initFromPage(page.getValueCount(), bytes, 0);
            int offset = rlReader.getNextOffset();
            dlReader.initFromPage(page.getValueCount(), bytes, offset);
            offset = dlReader.getNextOffset();
            return initDataReader(page.getValueEncoding(), bytes, offset, page.getValueCount());
        }
        catch (IOException e) {
            throw new ParquetDecodingException("Error reading parquet page " + page + " in column " + columnDescriptor, e);
        }
    }

    private ValuesReader readPageV2(DataPageV2 page)
    {
        repetitionReader = buildLevelRLEReader(columnDescriptor.getMaxRepetitionLevel(), page.getRepetitionLevels());
        definitionReader = buildLevelRLEReader(columnDescriptor.getMaxDefinitionLevel(), page.getDefinitionLevels());
        return initDataReader(page.getDataEncoding(), page.getSlice().getBytes(), 0, page.getValueCount());
    }

    private LevelReader buildLevelRLEReader(int maxLevel, Slice slice)
    {
        if (maxLevel == 0) {
            return new LevelNullReader();
        }
        return new LevelRLEReader(new RunLengthBitPackingHybridDecoder(BytesUtils.getWidthFromMaxInt(maxLevel), new ByteArrayInputStream(slice.getBytes())));
    }

    private ValuesReader initDataReader(ParquetEncoding dataEncoding, byte[] bytes, int offset, int valueCount)
    {
        ValuesReader valuesReader;
        if (dataEncoding.usesDictionary()) {
            if (dictionary == null) {
                throw new ParquetDecodingException("Dictionary is missing for Page");
            }
            valuesReader = dataEncoding.getDictionaryBasedValuesReader(columnDescriptor, VALUES, dictionary);
        }
        else {
            valuesReader = dataEncoding.getValuesReader(columnDescriptor, VALUES);
        }

        try {
            valuesReader.initFromPage(valueCount, bytes, offset);
            return valuesReader;
        }
        catch (IOException e) {
            throw new ParquetDecodingException("Error reading parquet page in column " + columnDescriptor, e);
        }
    }
}
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.facebook.presto.hive.parquet;

import com.facebook.presto.hive.HiveColumnHandle;
import com.facebook.presto.parquet.Field;
import com.facebook.presto.parquet.ParquetCorruptionException;
import com.facebook.presto.parquet.reader.ParquetReader;
import com.facebook.presto.spi.ConnectorPageSource;
import com.facebook.presto.spi.Page;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.block.Block;
import com.facebook.presto.spi.block.LazyBlock;
import com.facebook.presto.spi.block.LazyBlockLoader;
import com.facebook.presto.spi.block.RunLengthEncodedBlock;
import com.facebook.presto.spi.predicate.TupleDomain;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.spi.type.TypeManager;
import com.google.common.collect.ImmutableList;
import parquet.io.MessageColumnIO;
import parquet.schema.MessageType;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.Optional;
import java.util.Properties;

import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR;
import static com.facebook.presto.hive.HiveErrorCode.HIVE_BAD_DATA;
import static com.facebook.presto.hive.HiveErrorCode.HIVE_CURSOR_ERROR;
import static com.facebook.presto.hive.parquet.ParquetPageSourceFactory.getParquetType;
import static com.facebook.presto.parquet.ParquetTypeUtils.getFieldIndex;
import static com.facebook.presto.parquet.ParquetTypeUtils.lookupColumnByName;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import static parquet.io.ColumnIOConverter.constructField;

public class ParquetPageSource
        implements ConnectorPageSource
{
    private static final int MAX_VECTOR_LENGTH = 1024;

    private final ParquetReader parquetReader;
    private final MessageType fileSchema;
    // for debugging heap dump
    private final List<String> columnNames;
    private final List<Type> types;
    private final List<Optional<Field>> fields;

    private final Block[] constantBlocks;
    private final int[] hiveColumnIndexes;

    private int batchId;
    private boolean closed;
    private long readTimeNanos;
    private final boolean useParquetColumnNames;

    public ParquetPageSource(
            ParquetReader parquetReader,
            MessageType fileSchema,
            MessageColumnIO messageColumnIO,
            TypeManager typeManager,
            Properties splitSchema,
            List<HiveColumnHandle> columns,
            TupleDomain<HiveColumnHandle> effectivePredicate,
            boolean useParquetColumnNames)
    {
        requireNonNull(splitSchema, "splitSchema is null");
        requireNonNull(columns, "columns is null");
        requireNonNull(effectivePredicate, "effectivePredicate is null");
        this.parquetReader = requireNonNull(parquetReader, "parquetReader is null");
        this.fileSchema = requireNonNull(fileSchema, "fileSchema is null");
        this.useParquetColumnNames = useParquetColumnNames;

        int size = columns.size();
        this.constantBlocks = new Block[size];
        this.hiveColumnIndexes = new int[size];

        ImmutableList.Builder<String> namesBuilder = ImmutableList.builder();
        ImmutableList.Builder<Type> typesBuilder = ImmutableList.builder();
        ImmutableList.Builder<Optional<Field>> fieldsBuilder = ImmutableList.builder();
        for (int columnIndex = 0; columnIndex < size; columnIndex++) {
            HiveColumnHandle column = columns.get(columnIndex);
            checkState(column.getColumnType() == REGULAR, "column type must be regular");

            String name = column.getName();
            Type type = typeManager.getType(column.getTypeSignature());

            namesBuilder.add(name);
            typesBuilder.add(type);
            hiveColumnIndexes[columnIndex] = column.getHiveColumnIndex();

            if (getParquetType(column, fileSchema, useParquetColumnNames) == null) {
                constantBlocks[columnIndex] = RunLengthEncodedBlock.create(type, null, MAX_VECTOR_LENGTH);
                fieldsBuilder.add(Optional.empty());
            }
            else {
                String columnName = useParquetColumnNames ? name : fileSchema.getFields().get(column.getHiveColumnIndex()).getName();
                fieldsBuilder.add(constructField(type, lookupColumnByName(messageColumnIO, columnName)));
            }
        }
        types = typesBuilder.build();
        fields = fieldsBuilder.build();
        columnNames = namesBuilder.build();
    }

    @Override
    public long getCompletedBytes()
    {
        return parquetReader.getDataSource().getReadBytes();
    }

    @Override
    public long getReadTimeNanos()
    {
        return readTimeNanos;
    }

    @Override
    public boolean isFinished()
    {
        return closed;
    }

    @Override
    public long getSystemMemoryUsage()
    {
        return parquetReader.getSystemMemoryContext().getBytes();
    }

    @Override
    public Page getNextPage()
    {
        try {
            batchId++;
            long start = System.nanoTime();

            int batchSize = parquetReader.nextBatch();

            readTimeNanos += System.nanoTime() - start;

            if (closed || batchSize <= 0) {
                close();
                return null;
            }

            Block[] blocks = new Block[hiveColumnIndexes.length];
            for (int fieldId = 0; fieldId < blocks.length; fieldId++) {
                if (constantBlocks[fieldId] != null) {
                    blocks[fieldId] = constantBlocks[fieldId].getRegion(0, batchSize);
                }
                else {
                    Type type = types.get(fieldId);
                    Optional<Field> field = fields.get(fieldId);
                    int fieldIndex;
                    if (useParquetColumnNames) {
                        fieldIndex = getFieldIndex(fileSchema, columnNames.get(fieldId));
                    }
                    else {
                        fieldIndex = hiveColumnIndexes[fieldId];
                    }
                    if (fieldIndex != -1 && field.isPresent()) {
                        blocks[fieldId] = new LazyBlock(batchSize, new ParquetBlockLoader(field.get()));
                    }
                    else {
                        blocks[fieldId] = RunLengthEncodedBlock.create(type, null, batchSize);
                    }
                }
            }
            return new Page(batchSize, blocks);
        }
        catch (PrestoException e) {
            closeWithSuppression(e);
            throw e;
        }
        catch (RuntimeException e) {
            closeWithSuppression(e);
            throw new PrestoException(HIVE_CURSOR_ERROR, e);
        }
    }

    private void closeWithSuppression(Throwable throwable)
    {
        requireNonNull(throwable, "throwable is null");
        try {
            close();
        }
        catch (RuntimeException e) {
            // Self-suppression not permitted
            if (e != throwable) {
                throwable.addSuppressed(e);
            }
        }
    }

    @Override
    public void close()
    {
        if (closed) {
            return;
        }
        closed = true;

        try {
            parquetReader.close();
        }
        catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    private final class ParquetBlockLoader
            implements LazyBlockLoader<LazyBlock>
    {
        private final int expectedBatchId = batchId;
        private final Field field;
        private boolean loaded;

        public ParquetBlockLoader(Field field)
        {
            this.field = requireNonNull(field, "field is null");
        }

        @Override
        public final void load(LazyBlock lazyBlock)
        {
            if (loaded) {
                return;
            }

            checkState(batchId == expectedBatchId);

            try {
                Block block = parquetReader.readBlock(field);
                lazyBlock.setBlock(block);
            }
            catch (ParquetCorruptionException e) {
                throw new PrestoException(HIVE_BAD_DATA, e);
            }
            catch (IOException e) {
                throw new PrestoException(HIVE_CURSOR_ERROR, e);
            }
            loaded = true;
        }
    }
}

[AWS]SCT でレポートを PDF で保存しようとすると ”No glyph for U+5348 in font AmazonEmber-Regular” と怒られる

$
0
0

事象

  • AWS Schema Conversion Tool で Assessment Report Viewを表示して PDF で保存しようとすると "No glyph for U+5348 in font AmazonEmber-Regular" と怒られる。

回避策

  • 言語を英語、地域をUSにしてOS再起動して、SCTを再実行する。

f:id:yohei-a:20181022004404p:image:w640


環境

  • macOS Sierra 10.12.6
  • AWS Schema Conversion Tool 1.0 Build 619

[AWS]Aurora MySQL でスロークエリログを取得する

$
0
0
  • パラメータグループを作成し以下の通り設定する
    • long_query_time=1
    • slow_query_log=1

[AWS]Aurora PostgreSQL互換でクエリログを取得する

$
0
0

Aurora PostgreSQL 9.6 互換でクエリログを取得してみた。


設定

  • DB Parameter Group をパラメータグループファミリー"aurora-postgresql9.6" で作成する
    • log_statement=1
    • log_min_duration_statement=0
    • log_destination=csvlog
    • log_duration=1
    • log_error_verbosity=verbose
  • DBインスタンスの DB Parameter Group を作成したものに変えて再起動する。

クエリログを確認する

  • クエリを実行する
> select * from (select *  from pg_tables a, pg_tables b, pg_tables c) d;
  • AWSマネジメントコンソールで[ログ]の error/postgresql.log.*.csv をクリックする
2018-10-24 17:15:00.599 UTC,"awsuser","mydb",26380,"**.*.*.145:54214",5bd0a876.670c,5,"idle",2018-10-24 17:14:30 UTC,6/26561,0,LOG,00000,"statement: select count(*) from (select * from pg_tables a, pg_tables b) d;",,,,,,,,"exec_simple_query, postgres.c:942","psql"
2018-10-24 17:15:00.600 UTC,"awsuser","mydb",26380,"**.*.*.145:54214",5bd0a876.670c,6,"SELECT",2018-10-24 17:14:30 UTC,6/0,0,LOG,00000,"duration: 1.014 ms",,,,,,,,"exec_simple_query, postgres.c:1171","psql"

18.8.4. CSV書式のログ出力の利用

log_destinationリストにcsvlogを含めることは、ログファイルをデータベーステーブルにインポートする簡便な方法を提供します。このオプションはカンマ区切り値書式(CSV)で以下の列を含むログ行を生成します。 ミリ秒単位のtimestamp、 ユーザ名、 データベース名、 プロセス識別子、 クライアントホスト:ポート番号、 セッション識別子、 セッション前行番号、 コマンドタグ、 セッション開始時間、 仮想トランザクション識別子、 通常トランザクション識別子、 エラーの深刻度、 SQL状態コード、 エラーメッセージ、 詳細エラーメッセージ、 ヒント、 エラーとなった内部的な問い合わせ(もしあれば)、 内部問い合わせにおけるエラー位置の文字数、 エラーの文脈、 PostgreSQLソースコード上のエラー発生場所(log_error_verbosityがverboseに設定されているならば) アプリケーション名。 以下にcsvlog出力を格納するためのテーブル定義のサンプルを示します。

CREATE TABLE postgres_log
(
  log_time timestamp(3) with time zone,
  user_name text,
  database_name text,
  process_id integer,
  connection_from text,
  session_id text,
  session_line_num bigint,
  command_tag text,
  session_start_time timestamp with time zone,
  virtual_transaction_id text,
  transaction_id bigint,
  error_severity text,
  sql_state_code text,
  message text,
  detail text,
  hint text,
  internal_query text,
  internal_query_pos integer,
  context text,
  query text,
  query_pos integer,
  location text,
  application_name text,
  PRIMARY KEY (session_id, session_line_num)
);
エラー報告とログ取得

参考

[AWS]S3 のデフォルト暗号化キーに使っている BYOK した CMK を入れ替える

$
0
0

KMS に BYOK(Bring Your Own Key) した CMK(Customer Master Key) で S3 バケットを SSE-KMS(Server-Side Encryption with AWS KMS) でデフォルト暗号化し、ファイルを S3 に Put した後、S3 のデフォルト暗号化キーを BYOK した別の CMK に変更して、S3 のオブジェクトを Get/Put して新しいキーで暗号化後に古い CMK を無効化してみた。


手順

KMS にCMKをインポートする
  • KMS に CMK を空で作成する。
$ aws kms create-key --origin EXTERNAL --description imported_key
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "d07a6b28-a314-44c0-899f-4c0b4fa18f23",
        "Description": "imported_key",
        "KeyManager": "CUSTOMER",
        "Enabled": false,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "PendingImport",
        "CreationDate": 1540532700.471,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/d07a6b28-a314-44c0-899f-4c0b4fa18f23",
        "AWSAccountId": "123456789012"
    }
}
  • 作成した CMK にエイリアス名をつける。
$ aws kms create-alias --alias-name alias/rotation-test-key --target-key-id d07a6b28-a314-44c0-899f-4c0b4fa18f23
  • PublicKey と ImportToken をダウンロードする。
$ aws kms get-parameters-for-import \
--key-id d07a6b28-a314-44c0-899f-4c0b4fa18f23 \
--wrapping-algorithm RSAES_PKCS1_V1_5 \
--wrapping-key-spec RSA_2048
{
    "ParametersValidTo": 1540625003.173,
    "PublicKey": "...",
    "ImportToken": "..."
}
  • 上記のPublicKey を PublicKey1.b64、ImportToken を ImportToken1.b64 というファイル名で保存する。
  • PublicKey と ImportToken をそれぞれbase64デコードして、新しいファイルで保存する。
$ openssl enc -d -a -A -in PublicKey1.b64 -out PublicKey1.bin
$ openssl enc -d -a -A -in ImportToken1.b64 -out ImportToken1.bin
  • KMS にインポートする CMK を作成する
$ openssl rand -out PlaintextKeyMaterial1.bin 32
  • 生成した CMK を、デコードした PublicKey を使って暗号化する。
$ openssl rsautl -encrypt \
-in PlaintextKeyMaterial1.bin \
-pkcs \
-inkey PublicKey1.bin \
-keyform DER \
-pubin \
-out EncryptedKeyMaterial1.bin
  • 暗号化した CMK を KMS にインポートする。
$ aws kms import-key-material --key-id d07a6b28-a314-44c0-899f-4c0b4fa18f23 \
--encrypted-key-material fileb://EncryptedKeyMaterial1.bin \
--import-token fileb://ImportToken1.bin \
--expiration-model KEY_MATERIAL_EXPIRES \
--valid-to 2018-11-01T00:00:00-00:00
S3バケットを作成し BYOK した CMK でデフォルト暗号化設定する
  • S3 にバケットを作成する
$ aws s3 mb s3://kms-key-rotation-test-bucket
make_bucket: kms-key-rotation-test-bucket
  • 作成した S3 バケットのデフォルト暗号化キーに作成した CMK を指定する。
$ aws s3api put-bucket-encryption --bucket kms-key-rotation-test-bucket --server-side-encryption-configuration '{
   "Rules": [
     {
       "ApplyServerSideEncryptionByDefault": {
         "SSEAlgorithm": "aws:kms",
         "KMSMasterKeyID": "d07a6b28-a314-44c0-899f-4c0b4fa18f23"
       }
     }
   ]
}'
S3 にファイルを Put する
  • ファイルを S3 に Put する
$ echo 'kms key rotation test 1' > kms-key-rotation-test1.txt
$ aws s3 cp kms-key-rotation-test1.txt s3://kms-key-rotation-test-bucket/
$ echo 'kms key rotation test 2' > kms-key-rotation-test2.txt
$ aws s3 cp kms-key-rotation-test2.txt s3://kms-key-rotation-test-bucket/
  • Put したオブジェクトの暗号化に使われている CMK を確認する
$ aws s3api head-object --bucket kms-key-rotation-test-bucket --key kms-key-rotation-test1.txt
{
    "AcceptRanges": "bytes",
    "ContentType": "text/plain",
    "LastModified": "Fri, 26 Oct 2018 07:47:48 GMT",
    "ContentLength": 24,
    "ETag": "\"97138b7a0ea21fd4607973bd6cbd83b5\"",
    "ServerSideEncryption": "aws:kms",
    "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/d07a6b28-a314-44c0-899f-4c0b4fa18f23",
    "Metadata": {}
}
新しい CMK をインポートする
  • KMS に CMK を空で作成する。
$ aws kms create-key --origin EXTERNAL --description imported_key
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "1d3829b7-a35d-44fb-8f1d-41fc5ed229d8",
        "Description": "imported_key",
        "KeyManager": "CUSTOMER",
        "Enabled": false,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "PendingImport",
        "CreationDate": 1540540340.387,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/1d3829b7-a35d-44fb-8f1d-41fc5ed229d8",
        "AWSAccountId": "123456789012"
    }
}
  • PublicKey と ImportToken をダウンロードする。
$ aws kms get-parameters-for-import \
--key-id 1d3829b7-a35d-44fb-8f1d-41fc5ed229d8 \
--wrapping-algorithm RSAES_PKCS1_V1_5 \
--wrapping-key-spec RSA_2048
{
    "ParametersValidTo": 1540626803.766,
    "PublicKey": "...",
    "ImportToken": "..."
}
  • 上記のPublicKey を PublicKey2.b64、ImportToken を ImportToken2.b64 というファイル名で保存する。
  • PublicKey と ImportToken をそれぞれbase64デコードして、新しいファイルで保存する。
$ openssl enc -d -a -A -in PublicKey2.b64 -out PublicKey2.bin
$ openssl enc -d -a -A -in ImportToken2.b64 -out ImportToken2.bin
  • KMSにインポートする CMK を作成する
$ openssl rand -out PlaintextKeyMaterial2.bin 32
  • 生成した CMK を、デコードした PublicKey を使って暗号化する。
$ openssl rsautl -encrypt \
-in PlaintextKeyMaterial2.bin \
-pkcs \
-inkey PublicKey2.bin \
-keyform DER \
-pubin \
-out EncryptedKeyMaterial2.bin
  • 暗号化した CMK を KMS にインポートする。
$ aws kms import-key-material --key-id 1d3829b7-a35d-44fb-8f1d-41fc5ed229d8 \
--encrypted-key-material fileb://EncryptedKeyMaterial2.bin \
--import-token fileb://ImportToken2.bin \
--expiration-model KEY_MATERIAL_EXPIRES \
--valid-to 2018-11-01T00:00:00-00:00
S3 のデフォルト暗号化キーを変更する
  • S3 のデフォルト暗号化キーを新しく作成した CMK に変更する。
$ aws s3api put-bucket-encryption --bucket kms-key-rotation-test-bucket --server-side-encryption-configuration '{
   "Rules": [
     {
       "ApplyServerSideEncryptionByDefault": {
         "SSEAlgorithm": "aws:kms",
         "KMSMasterKeyID": "1d3829b7-a35d-44fb-8f1d-41fc5ed229d8"
       }
     }
   ]
}'
  • エイリアス"rotation-test-key"のキーIDを確認する。
$ aws kms list-aliases | jq '.Aliases[] | select(.AliasName=="alias/rotation-test-key")'
{
  "AliasArn": "arn:aws:kms:ap-northeast-1:123456789012:alias/rotation-test-key",
  "AliasName": "alias/rotation-test-key",
  "TargetKeyId": "d07a6b28-a314-44c0-899f-4c0b4fa18f23"
}
  • エイリアスに紐づく CMK を変更する。
$ aws kms update-alias --alias-name alias/rotation-test-key --target-key-id 1d3829b7-a35d-44fb-8f1d-41fc5ed229d8
  • エイリアス"rotation-test-key"のキーIDが変わったことを確認する
$ aws kms list-aliases | jq '.Aliases[] | select(.AliasName=="alias/rotation-test-key")'
{
  "AliasArn": "arn:aws:kms:ap-northeast-1:123456789012:alias/rotation-test-key",
  "AliasName": "alias/rotation-test-key",
  "TargetKeyId": "1d3829b7-a35d-44fb-8f1d-41fc5ed229d8"
}
新しい CMK でオブジェクトを暗号化し直す
  • オブジェクト暗号化に使われているキーを確認する
$ aws s3api head-object --bucket kms-key-rotation-test-bucket --key kms-key-rotation-test1.txt
{
    "AcceptRanges": "bytes",
    "ContentType": "text/plain",
    "LastModified": "Fri, 26 Oct 2018 07:47:48 GMT",
    "ContentLength": 24,
    "ETag": "\"97138b7a0ea21fd4607973bd6cbd83b5\"",
    "ServerSideEncryption": "aws:kms",
    "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/d07a6b28-a314-44c0-899f-4c0b4fa18f23",
    "Metadata": {}
}
  • オブジェクトを Get する
$ aws s3 cp s3://kms-key-rotation-test-bucket/kms-key-rotation-test1.txt ./
download: s3://kms-key-rotation-test-bucket/kms-key-rotation-test1.txt to ./kms-key-rotation-test1.txt
  • オブジェクトを Put する
$ aws s3 cp kms-key-rotation-test1.txt s3://kms-key-rotation-test-bucket/
upload: ./kms-key-rotation-test1.txt to s3://kms-key-rotation-test-bucket/kms-key-rotation-test1.txt
  • Put し直したオブジェクトが新しいキーで暗号化されていることを確認する
$ aws s3api head-object --bucket kms-key-rotation-test-bucket --key kms-key-rotation-test1.txt
{
    "AcceptRanges": "bytes",
    "ContentType": "text/plain",
    "LastModified": "Fri, 26 Oct 2018 08:43:18 GMT",
    "ContentLength": 24,
    "ETag": "\"a66d75d4b8183e6d8475ec2da993e553\"",
    "ServerSideEncryption": "aws:kms",
    "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/1d3829b7-a35d-44fb-8f1d-41fc5ed229d8",
    "Metadata": {}
}
古いキーを無効化する
  • 古いキーを無効化する
$ aws kms  disable-key --key-id d07a6b28-a314-44c0-899f-4c0b4fa18f23
  • 新しいキーで暗号化し直したオブジェクトはダウンロードできる。
$ aws s3 cp s3://kms-key-rotation-test-bucket/kms-key-rotation-test1.txt ./
download: s3://kms-key-rotation-test-bucket/kms-key-rotation-test1.txt to ./kms-key-rotation-test1.txt
  • 古い CMK で Put したオブジェクトはダウンロードできない。
$ aws s3 cp s3://kms-key-rotation-test-bucket/kms-key-rotation-test2.txt ./
download failed: s3://kms-key-rotation-test-bucket/kms-key-rotation-test2.txt to ./kms-key-rotation-test2.txt An error occurred (KMS.DisabledException) when calling the GetObject operation: arn:aws:kms:ap-northeast-1:123456789012:key/d07a6b28-a314-44c0-899f-4c0b4fa18f23 is disabled.

参考

[AWS]EBS ボリュームの CMK を KMS で生成したものから BYOK に変更する

$
0
0

EBS ボリュームの CMK(Customer Master Key) を KMS で生成したものから BYOK(Bring Your Own Key) したものに変更してみた。EBS のスナップショットを取得して、スナップショットをコピーする際に CMK を変更し、EBS ボリュームを作成して EC2 にアタッチすることで CMK を変更できた。


手順

KMSにキーを作成する
  • CMK を作成する。
$ aws kms create-key
{
    "KeyMetadata": {
        "Origin": "AWS_KMS",
        "KeyId": "2ae86c99-8b8f-4bba-9998-005b2dd768ac",
        "Description": "",
        "KeyManager": "CUSTOMER",
        "Enabled": true,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Enabled",
        "CreationDate": 1540638331.395,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/2ae86c99-8b8f-4bba-9998-005b2dd768ac",
        "AWSAccountId": "123456789012"
    }
}
  • エイリアス名をつける。
$ aws kms create-alias --alias-name alias/ebs-kms-key --target-key-id 2ae86c99-8b8f-4bba-9998-005b2dd768ac
EC2インスタンスを作成する
  • EC2インスタンスを作成する
    • EBSボリュームを ebs-kms-key(2ae86c99-8b8f-4bba-9998-005b2dd768ac) で暗号化する。

f:id:yohei-a:20181027201557p:image

  • EBSのボリュームを初期化してマウントする
$ sudo mkfs -t ext4 /dev/sdb
$ sudo mkdir /ebs-enc
$ sudo chmod o+w /ebs-enc
$ sudo vi /etc/fstab
/dev/sdb    /ebs-enc    ext4    defaults        1   1 # 追記
$ sudo mount -a
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        3.9G   68K  3.9G   1% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
/dev/xvda1      7.8G  1.1G  6.7G  14% /
/dev/xvdb        59G   52M   56G   1% /ebs-enc
EBSボリュームにデータを置く
$ aws s3 ls --recursive --human-readable --summarize s3://amazon-reviews-pds/tsv
(中略)
Total Objects: 55
   Total Size: 32.2 GiB
$ aws s3 cp --recursive s3://amazon-reviews-pds/tsv /ebs-enc/
$ ls -lh /ebs-enc|head -5
total 33G
-rw-rw-r-- 1 ec2-user ec2-user 231M Nov 24  2017 amazon_reviews_multilingual_DE_v1_00.tsv.gz
-rw-rw-r-- 1 ec2-user ec2-user  68M Nov 24  2017 amazon_reviews_multilingual_FR_v1_00.tsv.gz
-rw-rw-r-- 1 ec2-user ec2-user  91M Nov 24  2017 amazon_reviews_multilingual_JP_v1_00.tsv.gz
-rw-rw-r-- 1 ec2-user ec2-user 334M Nov 24  2017 amazon_reviews_multilingual_UK_v1_00.tsv.gz
KMSにCMKをBYOKする
  • KMS に CMK を空で作成する。
$ aws kms create-key --origin EXTERNAL --description byok_key
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "35e6c88a-0cd9-4614-9e7a-ec5782d7c1da",
        "Description": "byok_key",
        "KeyManager": "CUSTOMER",
        "Enabled": false,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "PendingImport",
        "CreationDate": 1540639989.29,
        "Arn": "arn:aws:kms:ap-northeast-1:269419664770:key/35e6c88a-0cd9-4614-9e7a-ec5782d7c1da",
        "AWSAccountId": "269419664770"
    }
}
  • 作成した CMK にエイリアス名をつける。
$ aws kms create-alias --alias-name alias/ebs-byok-key --target-key-id 35e6c88a-0cd9-4614-9e7a-ec5782d7c1da
  • PublicKey と ImportToken をダウンロードする。
$ aws kms get-parameters-for-import \
--key-id 35e6c88a-0cd9-4614-9e7a-ec5782d7c1da \
--wrapping-algorithm RSAES_PKCS1_V1_5 \
--wrapping-key-spec RSA_2048
  • 上記のPublicKey を PublicKey.b64、ImportToken を ImportToken.b64 というファイル名で保存する。
  • PublicKey と ImportToken をそれぞれbase64デコードして、新しいファイルで保存する。
$ openssl enc -d -a -A -in PublicKey.b64 -out PublicKey.bin
$ openssl enc -d -a -A -in ImportToken.b64 -out ImportToken.bin
  • KMS にインポートする CMK を作成する
$ openssl rand -out PlaintextKeyMaterial.bin 32
  • 生成した CMK を、デコードした PublicKey を使って暗号化する。
$ openssl rsautl -encrypt \
-in PlaintextKeyMaterial.bin \
-pkcs \
-inkey PublicKey.bin \
-keyform DER \
-pubin \
-out EncryptedKeyMaterial.bin
  • 暗号化した CMK を KMS にインポートする。
$ aws kms import-key-material --key-id 35e6c88a-0cd9-4614-9e7a-ec5782d7c1da \
--encrypted-key-material fileb://EncryptedKeyMaterial.bin \
--import-token fileb://ImportToken.bin \
--expiration-model KEY_MATERIAL_EXPIRES \
--valid-to 2018-11-01T00:00:00-00:00
  • AWSマネジメントコンソールのIAMの暗号化キーで確認する。

f:id:yohei-a:20181027204448p:image

EBSをBYOKしたCMKで暗号化し直す
  • アンマウントする。
$ sudo umount /ebs-enc
  • スナップショットを取得する。

f:id:yohei-a:20181027205349p:image

  • スナップショット取得完了を確認する。

f:id:yohei-a:20181027213441p:image

  • スナップショットを選択して、[アクション]-[コピー]を選択して[マスターキー]をBYOKしたCMKに変更して[コピー]をクリックする。

f:id:yohei-a:20181027213853p:image

  • スナップショットのコピーが完了したら、

f:id:yohei-a:20181027214959p:image

  • BYOKで暗号化したスナップショットを選択して、[アクション]-[ボリュームの作成]を選択して、スナップショットからEBSボリュームを作成する

f:id:yohei-a:20181027215058p:image

  • 作成したEBSボリュームをEC2インスタンスにアタッチする。

f:id:yohei-a:20181027215451p:image

  • 作成したEBSボリュームがBYOKしたCMKで暗号化されていることを確認する。

f:id:yohei-a:20181027220822p:image

  • EBSボリュームをマウントして確認する。
$ sudo mkdir /ebs-byok
$ sudo chmod o+w /ebs-byok
$ sudo vi /etc/fstab
/dev/sdf   /ebs-byok    ext4   defaults        1   1 #追記
$ sudo mount -a
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        3.9G   72K  3.9G   1% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
/dev/xvda1      7.8G  1.1G  6.7G  14% /
/dev/xvdb        59G   33G   24G  58% /ebs-enc
/dev/xvdf        59G   33G   24G  58% /ebs-byok
$ cd /ebs-byok
$ ls -lh|head -5
total 33G
-rw-rw-r-- 1 ec2-user ec2-user 231M Nov 24  2017 amazon_reviews_multilingual_DE_v1_00.tsv.gz
-rw-rw-r-- 1 ec2-user ec2-user  68M Nov 24  2017 amazon_reviews_multilingual_FR_v1_00.tsv.gz
-rw-rw-r-- 1 ec2-user ec2-user  91M Nov 24  2017 amazon_reviews_multilingual_JP_v1_00.tsv.gz
-rw-rw-r-- 1 ec2-user ec2-user 334M Nov 24  2017 amazon_reviews_multilingual_UK_v1_00.tsv.gz
  • ファーストタッチペナルティを回避するためブロックデバイスのデータにアクセスする。
$ sudo dd if=/dev/xvdf of=/dev/null bs=1M

参考

[AWS]Redshift の CMK を KMS で生成したものから BYOK に変更する

$
0
0

Redshift の場合、EBSのようにスナップショットのコピー時に CMK(Customer Master Key) を変更できないので、エンドポイントを変更したくない場合は、既存のクラスター名を変更して、元のクラスター名で BYOK を指定してクラスターを作成して、旧クラスターで UNLOAD したデータを COPY でロードしてやるとよい。

[Linux]grep でパターンにマッチする・しないファイルをリストする


[AWS]S3のデフォルト暗号化キーはエイリアス名ではなくキーIDと紐付いてそう

$
0
0

AWSマネジメントコンソールでS3バケットのデフォルト暗号化設定で、エイリアス名でキーを指定できるが、後で確認するとエイリアス名ではなくキーIDで表示される。


  • 設定時

f:id:yohei-a:20181029132624p:image:w360

  • 設定後に確認すると

f:id:yohei-a:20181029134347p:image:w360


AWS CLI からS3とKMSの設定を確認すると、S3バケットとエイリアス名ではなくキーIDと紐付いているように見える。

  • デフォルト暗号化キーはエイリアスではなくキーIDで指定されている。
$ aws s3api get-bucket-encryption --bucket az-test-bucket1
{
    "ServerSideEncryptionConfiguration": {
        "Rules": [
            {
                "ApplyServerSideEncryptionByDefault": {
                    "KMSMasterKeyID": "arn:aws:kms:ap-northeast-1:123456789012:key/608781a9-cf79-4e35-98dd-7efa5a109f72",
                    "SSEAlgorithm": "aws:kms"
                }
            }
        ]
    }
  • キーIDを指定してキーのメタデータを確認してもエイリアスはない。
$ aws kms describe-key --key-id 608781a9-cf79-4e35-98dd-7efa5a109f72
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "608781a9-cf79-4e35-98dd-7efa5a109f72",
        "Description": "",
        "KeyManager": "CUSTOMER",
        "ExpirationModel": "KEY_MATERIAL_EXPIRES",
        "ValidTo": 1541059200.0,
        "Enabled": true,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Enabled",
        "CreationDate": 1540447295.095,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/608781a9-cf79-4e35-98dd-7efa5a109f72",
        "AWSAccountId": "123456789012"
    }
}
  • エイリアス名とキーIDのマッピング表という形で持っていそうに見える。
$ aws kms list-aliases|jq '.Aliases[] | select(.AliasName=="alias/test-key")'
{
  "AliasArn": "arn:aws:kms:ap-northeast-1:123456789012:alias/test-key",
  "AliasName": "alias/test-key",
  "TargetKeyId": "608781a9-cf79-4e35-98dd-7efa5a109f72"
}

[AWS]S3 バケットのデフォルト暗号化に使う CMK を KMS で生成したものから BYOK に変えてみる

$
0
0

S3 バケットのデフォルト暗号化に使う CMK(Customer Master Key) を KMS(Key Management Service) で生成したものから BYOK(Bring Your Own Key) に変えてみた。


手順

KMS に CMK を作成する
  • CMK を作成する
$ aws kms create-key
{
    "KeyMetadata": {
        "Origin": "AWS_KMS",
        "KeyId": "c3c1fb86-168d-40ca-9489-90cf2bd49dfa",
        "Description": "",
        "KeyManager": "CUSTOMER",
        "Enabled": true,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Enabled",
        "CreationDate": 1540799362.916,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/c3c1fb86-168d-40ca-9489-90cf2bd49dfa",
        "AWSAccountId": "123456789012"
    }
}
  • エイリアス名をつける
$ aws kms create-alias --alias-name alias/kms-gen-key --target-key-id c3c1fb86-168d-40ca-9489-90cf2bd49dfa
S3バケットのデフォルト暗号化を設定してファイルを Put する
  • S3バケットを作成する
$ aws s3 mb s3://kms-key-change-test
make_bucket: kms-key-change-test
  • デフォルト暗号化設定を行う
$ aws s3api  put-bucket-encryption --bucket kms-key-change-test --server-side-encryption-configuration '{
  "Rules": [
    {
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms",
        "KMSMasterKeyID": "c3c1fb86-168d-40ca-9489-90cf2bd49dfa"
      }
    }
  ]
}'
  • デフォルト暗号化設定を確認する
$ aws s3api get-bucket-encryption --bucket kms-key-change-test
{
    "ServerSideEncryptionConfiguration": {
        "Rules": [
            {
                "ApplyServerSideEncryptionByDefault": {
                    "KMSMasterKeyID": "c3c1fb86-168d-40ca-9489-90cf2bd49dfa",
                    "SSEAlgorithm": "aws:kms"
                }
            }
        ]
    }
}
  • ファイルを Put する
$ echo 'Test' > test1.txt
$ cp test1.txt test2.txt
$ cp test1.txt test3.txt
$ aws s3 cp test1.txt s3://kms-key-change-test/
upload: ./test1.txt to s3://kms-key-change-test/test1.txt
$ aws s3 cp test2.txt s3://kms-key-change-test/
upload: ./test2.txt to s3://kms-key-change-test/test2.txt
$ aws s3 cp test3.txt s3://kms-key-change-test/
upload: ./test3.txt to s3://kms-key-change-test/test3.txt
  • オブジェクト暗号化に使われているキーを確認する
$ aws s3api head-object --bucket kms-key-change-test --key test1.txt
{
    "AcceptRanges": "bytes",
    "ContentType": "text/plain",
    "LastModified": "Mon, 29 Oct 2018 08:01:00 GMT",
    "ContentLength": 5,
    "ETag": "\"028422466ea4c14965264d1666728a7c\"",
    "ServerSideEncryption": "aws:kms",
    "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/c3c1fb86-168d-40ca-9489-90cf2bd49dfa",
    "Metadata": {}
}
KMS に BYOK する
  • CMK を作成する
$ aws kms create-key --origin EXTERNAL --description byok_key
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "2afa832b-d531-40fe-ac8c-4e34a578939a",
        "Description": "byok_key",
        "KeyManager": "CUSTOMER",
        "Enabled": false,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "PendingImport",
        "CreationDate": 1540801553.271,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/2afa832b-d531-40fe-ac8c-4e34a578939a",
        "AWSAccountId": "123456789012"
    }
}
  • エイリアス名をつける
$ aws kms create-alias --alias-name alias/kms-byok-key --target-key-id 2afa832b-d531-40fe-ac8c-4e34a578939a
  • PublicKey と ImportToken をダウンロードする。
$ aws kms get-parameters-for-import \
--key-id 2afa832b-d531-40fe-ac8c-4e34a578939a \
--wrapping-algorithm RSAES_PKCS1_V1_5 \
--wrapping-key-spec RSA_2048
  • 上記のPublicKey を PublicKey.b64、ImportToken を ImportToken.b64 というファイル名で保存する。
  • PublicKey と ImportToken をそれぞれbase64デコードして、新しいファイルで保存する。
$ openssl enc -d -a -A -in PublicKey.b64 -out PublicKey.bin
$ openssl enc -d -a -A -in ImportToken.b64 -out ImportToken.bin
  • KMS にインポートする CMK を作成する
$ openssl rand -out PlaintextKeyMaterial.bin 32
  • 生成した CMK を、デコードした PublicKey を使って暗号化する。
$ openssl rsautl -encrypt \
-in PlaintextKeyMaterial.bin \
-pkcs \
-inkey PublicKey.bin \
-keyform DER \
-pubin \
-out EncryptedKeyMaterial.bin
  • CMK を KMS にインポートする。
$ aws kms import-key-material --key-id 2afa832b-d531-40fe-ac8c-4e34a578939a \
--encrypted-key-material fileb://EncryptedKeyMaterial.bin \
--import-token fileb://ImportToken.bin \
--expiration-model KEY_MATERIAL_EXPIRES \
--valid-to 2018-11-01T00:00:00-00:00
S3バケットのデフォルト暗号化キーを BYOK した CMK に変更する
  • デフォルト暗号化キーを BYOK した CMK に変更する
$ aws s3api put-bucket-encryption --bucket kms-key-change-test --server-side-encryption-configuration '{
  "Rules": [
    {
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms",
        "KMSMasterKeyID": "2afa832b-d531-40fe-ac8c-4e34a578939a"
      }
    }
  ]
}'
  • デフォルト暗号化キーが変更されていることを確認する
$ aws s3api get-bucket-encryption --bucket kms-key-change-test
{
    "ServerSideEncryptionConfiguration": {
        "Rules": [
            {
                "ApplyServerSideEncryptionByDefault": {
                    "KMSMasterKeyID": "2afa832b-d531-40fe-ac8c-4e34a578939a",
                    "SSEAlgorithm": "aws:kms"
                }
            }
        ]
    }
}
S3のオブジェクトを Get/Put して BYOK した CMK で暗号化し直す
  • Get/Put して BYOK したキーで暗号化し直す
$ aws s3 cp s3://kms-key-change-test/test.txt ./
$ aws s3 cp test.txt s3://kms-key-change-test/
$ aws s3 cp s3://kms-key-change-test/test2.txt ./
$ aws s3 cp test2.txt s3://kms-key-change-test/
  • オブジェクト暗号化に使われているキーを確認する
$ aws s3api head-object --bucket kms-key-change-test --key test1.txt
{
    "AcceptRanges": "bytes",
    "ContentType": "text/plain",
    "LastModified": "Mon, 29 Oct 2018 08:37:32 GMT",
    "ContentLength": 5,
    "ETag": "\"f5b4a98e6bbdce444ebe6eaabbf5854b\"",
    "ServerSideEncryption": "aws:kms",
    "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/2afa832b-d531-40fe-ac8c-4e34a578939a",
    "Metadata": {}
}
$ aws s3api head-object --bucket kms-key-change-test --key test1.txt
{
    "AcceptRanges": "bytes",
    "ContentType": "text/plain",
    "LastModified": "Mon, 29 Oct 2018 08:37:32 GMT",
    "ContentLength": 5,
    "ETag": "\"f5b4a98e6bbdce444ebe6eaabbf5854b\"",
    "ServerSideEncryption": "aws:kms",
    "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/2afa832b-d531-40fe-ac8c-4e34a578939a",
    "Metadata": {}
}
古い CMK を無効化する
  • 古いキーを無効化する
$ aws kms  disable-key --key-id c3c1fb86-168d-40ca-9489-90cf2bd49dfa
  • 無効化されていることを確認する。
$ aws kms describe-key --key-id c3c1fb86-168d-40ca-9489-90cf2bd49dfa
{
    "KeyMetadata": {
        "Origin": "AWS_KMS",
        "KeyId": "c3c1fb86-168d-40ca-9489-90cf2bd49dfa",
        "Description": "",
        "KeyManager": "CUSTOMER",
        "Enabled": false,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Disabled",
        "CreationDate": 1540799362.916,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/c3c1fb86-168d-40ca-9489-90cf2bd49dfa",
        "AWSAccountId": "123456789012"
    }
}
  • 新しいキーで暗号化し直したオブジェクトはダウンロードできる。
$ aws s3 cp s3://kms-key-change-test/test1.txt ./
download: s3://kms-key-change-test/test1.txt to ./test1.txt
  • 古い CMK で Put したオブジェクトはダウンロードできない。
$ aws s3 cp s3://kms-key-change-test/test3.txt ./
download failed: s3://kms-key-change-test/test3.txt to ./test3.txt An error occurred (KMS.DisabledException) when calling the GetObject operation: arn:aws:kms:ap-northeast-1: 123456789012:key/c3c1fb86-168d-40ca-9489-90cf2bd49dfa is disabled.
  • オブジェクトが暗号化されている CMK を確認する。
$ aws s3 ls --recursive s3://kms-key-change-test|perl -lane 'print $F[3]'|xargs -n1 aws s3api head-object --bucket kms-key-change-test --key|jq '.|@text "\(.SSEKMSKeyId)"'
"arn:aws:kms:ap-northeast-1:123456789012:key/2afa832b-d531-40fe-ac8c-4e34a578939a"
"arn:aws:kms:ap-northeast-1:123456789012:key/2afa832b-d531-40fe-ac8c-4e34a578939a"
"arn:aws:kms:ap-northeast-1:123456789012:key/c3c1fb86-168d-40ca-9489-90cf2bd49dfa"

[AWS]KMSで別のキーマテリアルを CMK にインポートすることはできない

$
0
0
CMK を作成してインポートする
  • CMK を作成する。
$ aws kms create-key --origin EXTERNAL --description byok_key_test1
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "02c66c29-81b3-404b-beba-35355b4a56cc",
        "Description": "byok_key_test1",
        "KeyManager": "CUSTOMER",
        "Enabled": false,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "PendingImport",
        "CreationDate": 1540823706.373,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/02c66c29-81b3-404b-beba-35355b4a56cc",
        "AWSAccountId": "123456789012"
    }
}
  • エイリアス名をつける。
$ aws kms create-alias --alias-name alias/byok-key-test1 --target-key-id 02c66c29-81b3-404b-beba-35355b4a56cc
  • PublicKey と ImportToken をダウンロードする。
$ aws kms get-parameters-for-import \
--key-id 02c66c29-81b3-404b-beba-35355b4a56cc \
--wrapping-algorithm RSAES_PKCS1_V1_5 \
--wrapping-key-spec RSA_2048
  • 上記のPublicKey を PublicKey.b64、ImportToken を ImportToken.b64 というファイル名で保存する。
  • PublicKey と ImportToken をそれぞれbase64デコードして、新しいファイルで保存する。
$ openssl enc -d -a -A -in PublicKey.b64 -out PublicKey.bin
$ openssl enc -d -a -A -in ImportToken.b64 -out ImportToken.bin
  • KMS にインポートする CMK を作成する
$ openssl rand -out PlaintextKeyMaterial.bin 32
  • 生成した CMK を、デコードした PublicKey を使って暗号化する。
$ openssl rsautl -encrypt \
-in PlaintextKeyMaterial.bin \
-pkcs \
-inkey PublicKey.bin \
-keyform DER \
-pubin \
-out EncryptedKeyMaterial.bin
  • CMK を KMS にインポートする。
$ aws kms import-key-material --key-id 02c66c29-81b3-404b-beba-35355b4a56cc \
--encrypted-key-material fileb://EncryptedKeyMaterial.bin \
--import-token fileb://ImportToken.bin \
--expiration-model KEY_MATERIAL_EXPIRES \
--valid-to 2018-11-01T00:00:00-00:00
  • キーの定義を確認する
$ aws kms describe-key --key-id 02c66c29-81b3-404b-beba-35355b4a56cc
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "02c66c29-81b3-404b-beba-35355b4a56cc",
        "Description": "byok_key_test1",
        "KeyManager": "CUSTOMER",
        "ExpirationModel": "KEY_MATERIAL_EXPIRES",
        "ValidTo": 1541030400.0,
        "Enabled": true,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Enabled",
        "CreationDate": 1540823706.373,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/02c66c29-81b3-404b-beba-35355b4a56cc",
        "AWSAccountId": "123456789012"
    }
}
CMK を削除して再インポートする
  • キーマテリアルを削除する
$ aws kms delete-imported-key-material --key-id 02c66c29-81b3-404b-beba-35355b4a56cc
  • キーの定義を確認する
$ aws kms describe-key --key-id 02c66c29-81b3-404b-beba-35355b4a56cc
{
    "KeyMetadata": {
        "Origin": "EXTERNAL",
        "KeyId": "02c66c29-81b3-404b-beba-35355b4a56cc",
        "Description": "byok_key_test1",
        "KeyManager": "CUSTOMER",
        "Enabled": false, ★
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "PendingImport", ★
        "CreationDate": 1540823706.373,
        "Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/02c66c29-81b3-404b-beba-35355b4a56cc",
        "AWSAccountId": "123456789012"
    }
}
  • PublicKey と ImportToken をダウンロードする。
aws kms get-parameters-for-import \
--key-id 02c66c29-81b3-404b-beba-35355b4a56cc \
--wrapping-algorithm RSAES_PKCS1_V1_5 \
--wrapping-key-spec RSA_2048
  • 上記のPublicKey を PublicKey.b64、ImportToken を ImportToken.b64 というファイル名で保存する。
  • PublicKey と ImportToken をそれぞれbase64デコードして、新しいファイルで保存する。
$ openssl enc -d -a -A -in PublicKey.b64 -out PublicKey.bin
$ openssl enc -d -a -A -in ImportToken.b64 -out ImportToken.bin
  • KMS にインポートする CMK を作成する
$ openssl rand -out PlaintextKeyMaterial.bin 32
  • 生成した CMK を、デコードした PublicKey を使って暗号化する。
$ openssl rsautl -encrypt \
-in PlaintextKeyMaterial.bin \
-pkcs \
-inkey PublicKey.bin \
-keyform DER \
-pubin \
-out EncryptedKeyMaterial.bin
  • CMK を KMS にインポートする。
$ aws kms import-key-material --key-id 02c66c29-81b3-404b-beba-35355b4a56cc \
--encrypted-key-material fileb://EncryptedKeyMaterial.bin \
--import-token fileb://ImportToken.bin \
--expiration-model KEY_MATERIAL_EXPIRES \
--valid-to 2018-11-01T00:00:00-00:00

An error occurred (IncorrectKeyMaterialException) when calling the ImportKeyMaterial operation: ★同じキーマテリアルでないため失敗する

[Hadoop]Parquet ファイルに Presto でクエリ時の Column Projection について調べた

$
0
0

確認したかったこと

  • Parquet ファイルに対して Presto で select count(year) のように特定カラムのみ参照するとそのカラムのみをストレージから読んでいる。
  • select count(*) すると Parquet ファイルのフッターのメタデータ(Row group の num rows)のみをストレージから読んでいる。

検証シナリオ

  • データセットは Amazon Customer Reviews Dataset を使用。
  • Presto から Parquet on HDFS、Athena から Parquet on S3 にクエリを発行してスキャンサイズ、実行時間、ブロックI/O量を確認する。
#クエリ
1select count(*) from amazon_reviews_parquet
2select count(year) from amazon_reviews_parquet
3select count(review_body) from amazon_reviews_parquet
4select * from amazon_reviews_parquet limit 10000
5select year from amazon_reviews_parquet limit 10000
6select review_body from amazon_reviews_parquet limit 10000

検証ポイント

Presto のスキャンサイズ
  • Presto のスキャンサイズが以下の通りになること
    • count(review_body) > count(year) > count(*)
      • count(*)が小さいのはフッターのメタデータのみの参照で済むため
    • * > review_body > year
  • Athena と Presto でスキャンサイズが同じこと
  • Prestoのメトリック*1
システムコール
  • I/O発行量の多いプロセスを確認する。
$ sudo iotop
  • HDFS プロセスのシステムコールトレースを取得し、ファイルの位置と範囲を指定してランダムアクセスしていること
$ sudo csysdig
  • システムコールで読んでいるオフセットと範囲を可視化し select count(year) より select count(review_body) のほうがI/Oサイズが大きいこと
Linux カーネルのブロックレイヤーのI/O量
  • iostat で見たI/O量が select count(year) より select count(review_body) のほうが多いこと
  • blktrace + btt で見たI/O量が select count(year) より select count(review_body) のほうが多いこと
$ sudo mount -t debugfs none /sys/kernel/debug
$ sudo blktrace -w 60 -d /dev/nvme1n1p2 -o nvme1n1p2
$ sudo btt -i nvme1n1p2.blktrace.0 -B nvme1n1p2.blktrace.0.btt
ハイパーバイザーレイヤーのI/O量
  • EC2 の I/O 量が select count(year) より select count(review_body) のほうが多いこと
観測面から見える実装
  • perf-java-top で CPU 時間の長い関数を確認する。
$ ps -u hdfs
$ sudo ~/perf-map-agent/bin/perf-java-top <PID>
  • Perf + Flame Graph で、データノードの Presto と HDFS のコールスタックを確認する。
  • PrestoとHDFSの通信
ソースコードの実装
  • Presto から HDFS に対してリクエストを出すコード
  • HDFS のランダムアクセスAPI
  • HDFSのファイルシステムからランダムアクセスするコード
  • Sさんに教えてもらったやつ

環境

  • emr 5.18.0-ami-roller-6 hvm ebs (ami-08366f64fbeb6134f)
  • マスターノード: m4.xlarge
  • コアノード: r5d.xlarge

参考

  • Parquet ファイルの定義
$ aws s3 cp s3://amazon-reviews-pds/parquet/product_category=Apparel/part-00000-495c48e6-96d6-4650-aa65-3c36a3516ddd.c000.snappy.parquet ./
$ java -jar parquet-tools-1.6.0.jar meta ~/part-00000-495c48e6-96d6-4650-aa65-3c36a3516ddd.c000.snappy.parquet
file:              file:/Users/azekyohe/part-00000-495c48e6-96d6-4650-aa65-3c36a3516ddd.c000.snappy.parquet
creator:           parquet-mr version 1.8.2 (build c6522788629e590a53eb79874b95f6c3ff11f16c)
extra:             org.apache.spark.sql.parquet.row.metadata = {"type":"struct","fields":[{"name":"marketplace","type":"string","nullable":true,"metadata":{}},{"name":"customer_id","type":"string","nullable":true,"metadata":{}},{"name":"review_id","type":"string","nullable":true,"metadata":{}},{"name":"product_id","type":"string","nullable":true,"metadata":{}},{"name":"product_parent","type":"string","nullable":true,"metadata":{}},{"name":"product_title","type":"string","nullable":true,"metadata":{}},{"name":"star_rating","type":"integer","nullable":true,"metadata":{}},{"name":"helpful_votes","type":"integer","nullable":true,"metadata":{}},{"name":"total_votes","type":"integer","nullable":true,"metadata":{}},{"name":"vine","type":"string","nullable":true,"metadata":{}},{"name":"verified_purchase","type":"string","nullable":true,"metadata":{}},{"name":"review_headline","type":"string","nullable":true,"metadata":{}},{"name":"review_body","type":"string","nullable":true,"metadata":{}},{"name":"review_date","type":"date","nullable":true,"metadata":{}},{"name":"year","type":"integer","nullable":true,"metadata":{}}]}

file schema:       spark_schema
--------------------------------------------------------------------------------
marketplace:       OPTIONAL BINARY O:UTF8 R:0 D:1
customer_id:       OPTIONAL BINARY O:UTF8 R:0 D:1
review_id:         OPTIONAL BINARY O:UTF8 R:0 D:1
product_id:        OPTIONAL BINARY O:UTF8 R:0 D:1
product_parent:    OPTIONAL BINARY O:UTF8 R:0 D:1
product_title:     OPTIONAL BINARY O:UTF8 R:0 D:1
star_rating:       OPTIONAL INT32 R:0 D:1
helpful_votes:     OPTIONAL INT32 R:0 D:1
total_votes:       OPTIONAL INT32 R:0 D:1
vine:              OPTIONAL BINARY O:UTF8 R:0 D:1
verified_purchase: OPTIONAL BINARY O:UTF8 R:0 D:1
review_headline:   OPTIONAL BINARY O:UTF8 R:0 D:1
review_body:       OPTIONAL BINARY O:UTF8 R:0 D:1 ★
review_date:       OPTIONAL INT32 O:DATE R:0 D:1
year:              OPTIONAL INT32 R:0 D:1 ★

row group 1:       RC:589900 TS:192293412 OFFSET:4
--------------------------------------------------------------------------------
marketplace:        BINARY SNAPPY DO:0 FPO:4 SZ:231/221/0.96 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY
customer_id:        BINARY SNAPPY DO:0 FPO:235 SZ:4581545/6957912/1.52 VC:589900 ENC:RLE,PLAIN,BIT_PACKED
review_id:          BINARY SNAPPY DO:0 FPO:4581780 SZ:8637455/10463253/1.21 VC:589900 ENC:RLE,PLAIN,BIT_PACKED
product_id:         BINARY SNAPPY DO:0 FPO:13219235 SZ:5034158/8259079/1.64 VC:589900 ENC:RLE,PLAIN,BIT_PACKED
product_parent:     BINARY SNAPPY DO:0 FPO:18253393 SZ:5187236/7484331/1.44 VC:589900 ENC:RLE,PLAIN,BIT_PACKED,PLAIN_DICTIONARY
product_title:      BINARY SNAPPY DO:0 FPO:23440629 SZ:21585566/34792104/1.61 VC:589900 ENC:RLE,PLAIN,BIT_PACKED,PLAIN_DICTIONARY
star_rating:        INT32 SNAPPY DO:0 FPO:45026195 SZ:221797/221763/1.00 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY
helpful_votes:      INT32 SNAPPY DO:0 FPO:45247992 SZ:276866/558457/2.02 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY
total_votes:        INT32 SNAPPY DO:0 FPO:45524858 SZ:306153/590359/1.93 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY
vine:               BINARY SNAPPY DO:0 FPO:45831011 SZ:962/1025/1.07 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY
verified_purchase:  BINARY SNAPPY DO:0 FPO:45831973 SZ:62135/74471/1.20 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY
review_headline:    BINARY SNAPPY DO:0 FPO:45894108 SZ:8309372/14205327/1.71 VC:589900 ENC:RLE,PLAIN,BIT_PACKED,PLAIN_DICTIONARY
review_body:        BINARY SNAPPY DO:0 FPO:54203480 SZ:66263112/107760057/1.63 VC:589900 ENC:RLE,PLAIN,BIT_PACKED
review_date:        INT32 SNAPPY DO:0 FPO:120466592 SZ:70391/738861/10.50 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY
year:               INT32 SNAPPY DO:0 FPO:120536983 SZ:9111/186192/20.44 VC:589900 ENC:RLE,BIT_PACKED,PLAIN_DICTIONARY

準備手順

データを S3 から HDFS にコピーする
  • マスターノードに ssh 接続する。
$ ssh -i ~/mykey.pem hadoop@ec2-**-***-***-233.compute-1.amazonaws.com
  • hdfs ユーザーにスイッチする。
$ sudo su - hdfs
  • ディレクトリを作成する。
$ hadoop fs -mkdir /amazon-reviews-pds
  • データを S3 から HDFS にコピーする。
$ nohup s3-dist-cp --src  s3://amazon-reviews-pds/ --dest /amazon-reviews-pds  &
外部テーブルを定義する。
  • hive シェルを起動する。
$ hive
  • データベースを作成する。
> create database if not exists parquet;
^H> show databases;
> use parquet;
  • テーブルを作成する。
> CREATE EXTERNAL TABLE parquet.amazon_reviews_parquet(
  marketplace string, 
  customer_id string, 
  review_id string, 
  product_id string, 
  product_parent string, 
  product_title string, 
  star_rating int, 
  helpful_votes int, 
  total_votes int, 
  vine string, 
  verified_purchase string, 
  review_headline string, 
  review_body string, 
  review_date bigint, 
  year int)
PARTITIONED BY (product_category string)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
  'hdfs:///amazon-reviews-pds/parquet';
  • パーティションを認識させる
> MSCK REPAIR TABLE parquet.amazon_reviews_parquet;
  • hive シェルを終了する。
> quit;
  • クエリを実行できることを確認する。
$ presto-cli
presto> use hive.parquet;
presto:parquet> select count(*) from amazon_reviews_parquet;
presto:parquet> select count(year) from amazon_reviews_parquet;
presto:parquet> select count(review_body) from amazon_reviews_parquet;

性能情報取得ツールのインストール
  • マスターノードとコアノードに以下をインストールする。
$ sudo yum -y install htop sysstat dstat iotop ltrace strace perf blktrace gnuplot
  • perf-map-agent をインストールする。
$ sudo yum -y install cmake
$ git clone --depth=1 https://github.com/jrudolph/perf-map-agent
$ cd perf-map-agent
$ cmake .
$ make
  • FlameGraph
$ git clone https://github.com/brendangregg/FlameGraph
$ chmod +x FlameGraph/*.pl
$ vi ~/.bashrc
export FLAMEGRAPH_DIR=~/FlameGraph

*1:VPN経由でアクセスできない

[AWS]S3オブジェクトのバージョン一覧を表示する

$
0
0

S3 で複数バージョン存在するオブジェクトをリストアップする。

$ aws s3api list-object-versions --bucket az-test-bucket|jq -r '.Versions[].Key'|sort|uniq -c|perl -lane '$F[0] > 1 and print'
      2 swingbench/data/gz/sh10/sales/test2.txt
      3 swingbench/data/gz/sh10/sales/test.txt

参考

バケット内のオブジェクトのバージョン一覧を表示するには、[Show] を選択します。各オブジェクトバージョンについて、一意のバージョン ID、そのバージョンが作成された日時、その他のプロパティがコンソールに表示されます (バージョニング状態を設定する前にバケットに格納されているオブジェクトには、バージョン ID null が付けられています)。

バージョンのないオブジェクトをリストするには、[Hide] を選択します。

f:id:yohei-a:20181107175036p:image:w640

S3 オブジェクトのバージョンを表示するには - Amazon Simple Storage Service
  • AWSマネジメントコンソールで確認する。

f:id:yohei-a:20181107175929p:image

  • AWS CLI で表示する。
$ aws s3api list-object-versions --bucket az-test-bucket|head -30
{
    "Versions": [
        {
            "LastModified": "2018-05-11T06:31:01.000Z",
            "VersionId": "null",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "StorageClass": "STANDARD",
            "Key": "bigdata-handson/",
            "Owner": {
                "DisplayName": "yoheia",
                "ID": "..."
            },
            "IsLatest": true,
            "Size": 0
        },
        {
            "LastModified": "2018-05-08T06:19:00.000Z",
            "VersionId": "null",
            "ETag": "\"1dd76d97a608b7b3ccb9e6da4f80a8cb-4\"",
            "StorageClass": "STANDARD",
            "Key": "swingbench/data/gz/sh10/sales/sh10_sales_0000_part_00.gz",
            "Owner": {
                "DisplayName": "yoheia",
                "ID": "..."
            },
            "IsLatest": true,
            "Size": 32481061
        },
        {
            "LastModified": "2018-05-08T06:19:00.000Z",
Viewing all 1154 articles
Browse latest View live