まえがき
第1章 基本的な準備をする
第2章 開発周りの準備をする
第3章 ウォーターマークを追加する
第4章 文字列入りページを追加する
第5章 カスタムプロパティーを追加する
第6章 暗号化する
第7章 API化する
第8章 Webサイトに組み込む
付録A Serverless Frameworkを利用してLambdaを管理する
付録B CloudFormationを利用してAWSリソースを管理する
あとがき
この本をお手に取っていただき、ありがとうございます。
近年の電子書籍の普及や、技術同人誌の盛り上がりにより、以前より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/
本書に記載された内容は情報の提供のみを目的としています。したがって、本書を用いた開発および運用は、必ずご自身の責任と判断によって行ってください。これらの情報による開発および運用の結果について、著者はいかなる責任も負いません。
本書に記載された会社名や製品名などは、各社の登録商標または商標、商品名です。本文中では©、®、™マークなどは表示していません。
本書の作業を進めるにあたり、環境や知識の準備が必要です。本章では、その設定や中身について紹介します。
Go言語は2009年に、Googleによって開発された言語です。数ある言語の中で、まだ歴史は若いのですが、採用例は少しずつ増えています。本書では今回、Lambdaのランタイムとして、Go言語を利用します。
Go言語のインストールは簡単で、公式サイトからインストーラーを落としてインストールするだけです。詳細はドキュメントを参照してください。HomeBrewやLinuxBrew、各種パッケージマネージャーで導入できる場合もありますが、バージョンが古いこともあるので注意してください。1.13以上であれば問題ありません。また、gvm1やgoenv2といったバージョンマネージャーを利用するのも有効です。
導入後、go versionを実行して、1.13以上であることが確認できればOKです。
$ go version
go version go1.14.2 linux/amd64
カスタムランタイムはLambda上で動作しますので、AWSのアカウントが必要です。持っていない場合は、公式サイト3から新規作成します。
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という名前で保存します。
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のダッシュボードに移動し、「関数の作成」を選択します。右上の「サポート」の左側が「東京」以外になっている場合は東京リージョンではないため、クリックして「東京」に切り替えます。6
関数名を任意で設定し、ランタイムを「Go 1.x」にして「関数の作成」を選択します。
少し待つと「関数(関数名)を正常に作成しました。」と表示されます。
スクロールすると「関数コード」という部分があるので、ここで設定します。「ハンドラー」の項目を「main」にし、「アップロード」で先ほど作成したzipファイルを選択します。設定後、右上の「保存」でアップロードおよび設定を保存します。
画面上部に「関数(関数名)が正常に更新されました。」と表示されれば、正常に保存されています。
続けて実行します。手動で実行させるため、ページ上部のメニューにある「テスト」をクリックします。すると、テストイベントの設定が表示されるので、「イベント名」に「testEvent」と入力します。その下にJSONを入力できる部分がありますが、今回はJSON処理しないため、設定そのままで「作成」をクリックします。
そして、再度「テスト」をクリックすると実行しますが、一見失敗したように見えます。しかし、詳細をクリックすると出てくる「ログ出力」の項目にはHello, Lambdaと表示されているので、実行はできています。失敗しているように見えてしまうのは、コード内にLambdaで必要なコード(ハンドラー)が実装されていない(作法に従っていない)ためです。
それでは、main.goの中身を次のファイルに変更します。Lambda用のハンドラーと処理を追加し、返り値としてHello, Lambdaの文字列を返しています。
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)
GOROOTやGOPATHといった環境変数を定義して作業しても解決しますが、今回はGo 1.11から追加されたGo Modules7を利用します。これは外部パッケージを管理する仕組みで、npmやpipのようなものです。これを利用するため、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上で関数として実行できるところまでを確認できました。