目次

まえがき

想定する技術レベル
ゴール
ソースコード
連絡先
免責事項
表記関係

第1章 基本的な準備をする

1.1 Go言語
1.2 AWS

第2章 開発周りの準備をする

2.1 ローカル環境の準備
2.2 AWSの準備

第3章 ウォーターマークを追加する

3.1 概要
3.2 コード
3.3 Lambdaの設定変更
3.4 テスト

第4章 文字列入りページを追加する

4.1 概要
4.2 コード
4.3 Lambdaの設定変更
4.4 テスト

第5章 カスタムプロパティーを追加する

5.1 概要
5.2 コード
5.3 Lambdaの設定変更
5.4 テスト

第6章 暗号化する

6.1 概要
6.2 QPDFの環境作成
6.3 コード
6.4 Lambdaの設定変更
6.5 テスト

第7章 API化する

7.1 概要
7.2 オブジェクト移動用関数を用意する
7.3 S3のライフサイクルルールを設定する
7.4 CloudFrontを設定する
7.5 Step Functionsを設定する
7.6 API Gatewayを設定する
7.7 CloudFront経由でPDFを確認する

第8章 Webサイトに組み込む

8.1 概要
8.2 HTMLファイル
8.3 使ってみる
8.4 S3で公開してみる

付録A Serverless Frameworkを利用してLambdaを管理する

A.1 概要
A.2 導入と設定
A.3 テンプレート作成
A.4 ビルド
A.5 デプロイ
A.6 テスト実行
A.7 テンプレートにCloudFrontの設定を追記
A.8 削除

付録B CloudFormationを利用してAWSリソースを管理する

B.1 概要
B.2 導入と設定
B.3 テンプレート作成
B.4 デプロイ
B.5 削除

あとがき

まえがき

 この本をお手に取っていただき、ありがとうございます。

 近年の電子書籍の普及や、技術同人誌の盛り上がりにより、以前よりPDFを手にする機会はもちろん、配布する機会も多くなりました。ですが、その中で再配布や加工の防止・保護をされている書類は、多くありません。1プレスリリースのPDFを保護していないことで編集ができてしまい、その中にある個人情報のマスク(塗りつぶした四角で上書きしただけ)が外せてしまう、という事例2もありました。

 保護の方法としては色々ありますが、その中でもメジャーなのは、透かしや個人を特定できる情報を埋め込むことです。再配布を完全に防止できるわけではありませんが、ある程度抑制することができます。また、パスワードで保護しておくことで、PDFの加工や編集、印刷も防止することができます。

 そこで本書では、PDFの保護フローを、サーバレスで実現する手段について解説します。PDFの加工自体は、無料のツールやサイトでも行えますが、サーバサイドで処理させることで、メールアドレスやシリアル番号の埋め込みなど、動的に加工することができるようになります。また、サーバレスで実装することで、サーバの構築や細かい設定をせずに利用できるというメリットもあります。

 今回は、AWS(Amazon Web Services)のLambdaを利用して実装します。最後にStep FunctionsやAPI Gatewayを利用して一連の処理をAPI化し、簡易的なWebサービスを作成して、埋め込むところまで紹介します。また、付録として、今回構築するAWSの環境をコードで管理する方法についても紹介しています。

想定する技術レベル

 この本は次の読者を想定しています。

 ・PDFの保護に興味がある

 ・サーバレスに興味がある

 ・AWSを利用、もしくは検討している

 ・何らかのプログラミング言語が読み書きできる

ゴール

 この本の内容に沿って進めることでたどり着く、最終的なゴールは次のとおりです。

 ・PDFをサーバレスで動的に加工・保護できるようになる

 ・Lambda同士を組み合わせてAPIとして公開できるようになる

 ・APIをWebサービスとして組み込み、公開できるようになる

ソースコード

 この本の中で使用している各種コードは、次のリポジトリーに公開しています。

 https://github.com/taiko19xx/GenerateSecurePDF-With-AWS

連絡先

 質問や誤字脱字、誤った内容などがありましたら、次の連絡先へご連絡ください。

 ・メール: hello@taiko19xx.net

 ・Twitter: @taiko19xx

 いただいた情報は確認の上、著者のブログにて適時公開します。

 ・ブログ: https://tech.taiko19xx.net/

免責事項

 本書に記載された内容は情報の提供のみを目的としています。したがって、本書を用いた開発および運用は、必ずご自身の責任と判断によって行ってください。これらの情報による開発および運用の結果について、著者はいかなる責任も負いません。

表記関係

 本書に記載された会社名や製品名などは、各社の登録商標または商標、商品名です。本文中では©、®、™マークなどは表示していません。

1. 人のことは言えませんが...

2. 廃棄されたはずのハードディスクを転売した事件

第1章 基本的な準備をする

本書の作業を進めるにあたり、環境や知識の準備が必要です。本章では、その設定や中身について紹介します。

1.1 Go言語

 Go言語は2009年に、Googleによって開発された言語です。数ある言語の中で、まだ歴史は若いのですが、採用例は少しずつ増えています。本書では今回、Lambdaのランタイムとして、Go言語を利用します。

 Go言語のインストールは簡単で、公式サイトからインストーラーを落としてインストールするだけです。詳細はドキュメントを参照してください。HomeBrewやLinuxBrew、各種パッケージマネージャーで導入できる場合もありますが、バージョンが古いこともあるので注意してください。1.13以上であれば問題ありません。また、gvm1goenv2といったバージョンマネージャーを利用するのも有効です。

 導入後、go versionを実行して、1.13以上であることが確認できればOKです。

$ go version

go version go1.14.2 linux/amd64


図1.1: Go言語
図1.2: ダウンロードページ

1.2 AWS

 カスタムランタイムはLambda上で動作しますので、AWSのアカウントが必要です。持っていない場合は、公式サイト3から新規作成します。

図1.3: Amazon Web Services

LambdaにおけるGo言語について

 Lambdaでは、ブラウザー上のエディター(Cloud94ベース)で、関数内のコードを編集できます。しかし、Go言語の場合は、Lambdaへデプロイするものがコードではなく、ビルドされたバイナリのため、編集できません。また、Lambda上ではベースのOSとして、Amazon Linuxが動作しています。そのため、バイナリは、Linux向けにビルドされたものを利用する必要があります。

 通常、Go言語でビルドする場合は、次のコマンドを実行します。-oオプションでは、出力するファイル名を指定しています。

$ go build -o main main.go

 このコマンドをLinux以外で実行してしまうと、その環境に合わせたバイナリ(Windowsであれば.exeファイル)となってしまいます。そのため、Linux環境向けにビルドすることを明示的に指定する必要があります。指定するには、GOOS環境変数に対してlinuxという値をビルド時に指定します。

 macOS上では次のコマンドを実行すると、mainというファイル名のLinux向けバイナリファイルが生成されます。それをzipで圧縮し、Lambdaへデプロイします。

$ GOOS=linux go build -o main main.go

 PowerShellの場合は次のコマンドで、同様の処理が実行されます。Windowsのコマンドプロンプトの場合は、環境変数の設定はset GOOS=linuxです。

PS> $env:GOOS = "linux"

PS> go build -o main main.go

 本節以降でビルドするタイミングがいくつかありますので、Linux環境以外ではGOOS環境変数の指定を忘れずに行ってください。

試しに実行する

 それでは、動作確認も兼ねて、簡単なスクリプトを作成して、ローカルとLambdaで実行します。

作成、実行、ビルド

 まずはローカルで実行します。実行するスクリプトは次のスクリプトです。作業用ディレクトリーを作成し、その中にmain.goという名前で保存します。

リスト1.1: main.go

package main

  import (
    "fmt"
  )

  func main() {
    fmt.Println("Hello, Lambda!")
  }

 ローカルで実行する場合はgo runを実行します。次のコマンドを実行し、Hello, Lambda!と表示されればOKです。

$ go run main.go

Hello, Lambda!

 次に、go buildでビルドします。Linux以外の場合はGOOS環境変数を忘れずに設定します。ビルドするとmainというバイナリファイルができているので、これをzipファイルにし、パッケージファイルを作成します。今回はバイナリファイルのみですが、他に依存するファイルがある場合は、一緒にzipファイルの中に入れます。

$ go build -o main main.go

$ zip function.zip main

 ちなみにWindows(WSL以外)の場合は、build-lambda-zipを利用してパッケージファイルを作成することが推奨されています。5

Lambdaで実行

 次に、Lambda関数を作成して実行します。

 最初に関数を作成するため、Lambdaのダッシュボードに移動し、「関数の作成」を選択します。右上の「サポート」の左側が「東京」以外になっている場合は東京リージョンではないため、クリックして「東京」に切り替えます。6

図1.4: Lambdaダッシュボード

 関数名を任意で設定し、ランタイムを「Go 1.x」にして「関数の作成」を選択します。

 少し待つと「関数(関数名)を正常に作成しました。」と表示されます。

図1.5: 関数作成
図1.6: 作成完了

 スクロールすると「関数コード」という部分があるので、ここで設定します。「ハンドラー」の項目を「main」にし、「アップロード」で先ほど作成したzipファイルを選択します。設定後、右上の「保存」でアップロードおよび設定を保存します。

図1.7: 設定更新&デプロイ

 画面上部に「関数(関数名)が正常に更新されました。」と表示されれば、正常に保存されています。

図1.8: デプロイ完了

 続けて実行します。手動で実行させるため、ページ上部のメニューにある「テスト」をクリックします。すると、テストイベントの設定が表示されるので、「イベント名」に「testEvent」と入力します。その下にJSONを入力できる部分がありますが、今回はJSON処理しないため、設定そのままで「作成」をクリックします。

 そして、再度「テスト」をクリックすると実行しますが、一見失敗したように見えます。しかし、詳細をクリックすると出てくる「ログ出力」の項目にはHello, Lambdaと表示されているので、実行はできています。失敗しているように見えてしまうのは、コード内にLambdaで必要なコード(ハンドラー)が実装されていない(作法に従っていない)ためです。

図1.9: テストイベント作成
図1.10: テスト失敗?

 それでは、main.goの中身を次のファイルに変更します。Lambda用のハンドラーと処理を追加し、返り値としてHello, Lambdaの文字列を返しています。

リスト1.2: main_lambda.go

package main

  import (
    "context"

    "github.com/aws/aws-lambda-go/lambda"
  )

  func handleRequest(ctx context.Context) (string, error) {
    return "Hello, Lambda!", nil
  }

  func main() {
    lambda.Start(handleRequest)
  }

 ただし、これをgo runまたはgo buildしようとすると、次のエラーが表示されます。これは、必要なモジュール(github.com/aws/aws-lambda-go/lambda)を取得していないためです。

$ go run main.go

main.go:7:2: cannot find package "github.com/aws/aws-lambda-go/lambda" in anyof:

        /usr/local/go/src/github.com/aws/aws-lambda-go/lambda (from $GOROOT)

        /home/taiko/go/src/github.com/aws/aws-lambda-go/lambda (from $GOPATH)

 GOROOTGOPATHといった環境変数を定義して作業しても解決しますが、今回はGo 1.11から追加されたGo Modules7を利用します。これは外部パッケージを管理する仕組みで、npmpipのようなものです。これを利用するため、main.goと同じディレクトリーで、次のコマンドを実行します。実行するとgo.modが生成されます。

$ go mod init main

go: creating new go.mod: module main

 この状態でgo runまたはgo buildを実行すると、パッケージの取得処理が行われます。ただし、Lambdaに特化したスクリプトのため、go runはうまく実行できず、終了されません。go buildを行い、エラーなくバイナリが生成されたのを確認し、再度zipファイルにします。

 そのzipファイルをLambdaにデプロイしてテストし、成功と表示されれば成功です。これでLambda向けのバイナリファイルの作成、およびLambda上で関数として実行できるところまでを確認できました。

図1.11: テスト成功

1. https://github.com/moovweb/gvm

2. https://github.com/syndbg/goenv

3. https://aws.amazon.com/jp/

4. https://aws.amazon.com/jp/cloud9/

5. https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-go-how-to-create-deployment-package.html

6. 本書ではリージョンを東京に統一します。

7. https://github.com/golang/go/wiki/Modules

試し読みはここまでです。
この続きは、製品版でお楽しみください。