Slide 1

Slide 1 text

ϢʔεέʔεͰֶͿʂ "1*(BUFXBZ-BNCEB"VUIPSJ[FS࣮૷ೖ໳ʢ$%,ʣ "84ࣄۀຊ෦ίϯαϧςΟϯά෦നੴ੒Ұ

Slide 2

Slide 2 text

ࣗݾ঺հ w"84ࣄۀຊ෦ίϯαϧς Οϯά෦ w"84ΤϯδχΞ ϑϩϯτ ΤϯυΤϯδχΞ w8FCडୗ4&4$. w"84 )5.- $44 +BWB4DSJQU 5ZQF4DSJQU 1)1 1FSM $ 1ZUIPO നੴ੒ҰʢShiraishi Seiichiʣ

Slide 3

Slide 3 text

ΞδΣϯμ ຊηογϣϯʹ͍ͭͯ ࣮૷֓ཁʢ8FCIPPL#BTJDೝূར༻ʣ "1*(BUFXBZ-BNCEB"VUIPSJ[FS࣮૷ ·ͱΊ

Slide 4

Slide 4 text

ΞδΣϯμ ຊηογϣϯʹ͍ͭͯ ࣮૷֓ཁʢ8FCIPPL#BTJDೝূར༻ʣ "1*(BUFXBZ-BNCEB"VUIPSJ[FS࣮૷ ·ͱΊ

Slide 5

Slide 5 text

ຊηογϣϯʹ͍ͭͯ ର৅ࢹௌऀ w"1*։ൃॳֶऀ w"1*(BUFXBZ-BNCEB"VUIPSJ[FSॳֶऀ ΰʔϧ w8FCIPPLΛड͚෇͚ΔͨΊͷ"1*(BUFXBZΛ࣮૷͠·͢ɻ"1* (BUFXBZʹ͸#BTJDೝূΛ࣮૷͠·͢ɻ w͜ͷྫΛ௨ͯ͠ɺԿ͔ಘΔ΋ͷ͕͋Γ·͢ͱ޾͍Ͱ͢ɻ

Slide 6

Slide 6 text

ຊηογϣϯʹೖΔલʹ લఏ৚݅ wຊηογϣϯͰ͸ɺ;FOEFTLʹͯ8FCIPPLͷઃఆΛߦ͍ɺ 8FCIPPLΛड͚෇͚Δ"1*Λ࡞੒͢Δํ๏Λ͝঺հ͠·͢ɻ w;FOEFTLଆͷઃఆ͸औΓѻΘͣɺࢀߟϒϩάͷ͝঺հͷΈͱͤͯ͞ ͍͖ͨͩ·͢ɻ ࢖༻͢Δٕज़ w"1*(BUFXBZ-BNCEB"VUIPSJ[FS w"84$%,W

Slide 7

Slide 7 text

ΞδΣϯμ ຊηογϣϯʹ͍ͭͯ ࣮૷֓ཁʢ8FCIPPL#BTJDೝূར༻ʣ "1*(BUFXBZ-BNCEB"VUIPSJ[FS࣮૷ ·ͱΊ

Slide 8

Slide 8 text

࣮૷֓ཁ w;FOEFTLͷ8FCIPPLઃఆ w;FOEFTLͷ#BTJDೝূઃఆ w8FCIPPLΛड͚෇͚Δ"1*ͷ࡞੒ w"1*(BUFXBZͷ࡞੒ w-BNCEB"VUIPSJ[FSͷ࡞੒

Slide 9

Slide 9 text

࣮૷֓ཁ w;FOEFTLͱ͸ w;FOEFTL͸ɺʮ͓٬༷͕৺஍Α͍ͱײ͡ΔΤΫεϖϦΤϯεʯΛ࣮ ݱ͢ΔΧελϚʔαʔϏεϓϥοτϑΥʔϜΛఏڙ͍ͯ͠·͢ɻ 
 ϝʔϧ͔Βͷ͓໰͍߹Θͤ΋ɺνϟοτ͔Βͷ͓໰͍߹Θͤ΋Ұݩ ؅ཧͰ͖ΔͨΊɺΧελϚʔαʔϏεۀ຿͕ܶతʹεϜʔζʹͳΔ ͱͱ΋ʹɺސ٬ͱͷΑΓྑ͍ؔ܎Λங͘͜ͱ͕ՄೳʹͳΓ·͢ɻ wҾ༻ɿIUUQTXXX[FOEFTLDPKQBCPVU

Slide 10

Slide 10 text

࣮૷֓ཁ w"1*(BUFXBZͱ͸ w"NB[PO"1*(BUFXBZ͸ɺ͋ΒΏΔن໛ͷ3&45ɺ)551ɺ͓Α ͼ8FC4PDLFU"1*Λ࡞੒ɺެ։ɺҡ࣋ɺϞχλϦϯάɺ͓Αͼη ΩϡΞԽ͢ΔͨΊͷ"84ͷαʔϏεͰ͢ɻ wҾ༻ɿIUUQTEPDTBXTBNB[PODPNKB@KQBQJHBUFXBZ MBUFTUEFWFMPQFSHVJEFXFMDPNFIUNM

Slide 11

Slide 11 text

࣮૷֓ཁ w-BNCEB"VUIPSJ[FSͱ͸ w-BNCEBؔ਺Λ࢖༻ͯ͠)551"1*΁ͷΞΫηεΛ੍ޚ͢Δ࢓૊ ΈͰ͢ɻ wҾ༻ɿIUUQTEPDTBXTBNB[PODPNKB@KQBQJHBUFXBZ MBUFTUEFWFMPQFSHVJEFIUUQBQJMBNCEBBVUIPSJ[FSIUNM

Slide 12

Slide 12 text

ΞʔΩςΫνϟ

Slide 13

Slide 13 text

ΞδΣϯμ ຊηογϣϯʹ͍ͭͯ ࣮૷֓ཁʢ8FCIPPL#BTJDೝূར༻ʣ "1*(BUFXBZ-BNCEB"VUIPSJ[FS࣮૷ ·ͱΊ

Slide 14

Slide 14 text

;FOEFTLଆઃఆ https://dev.classmethod.jp/ articles/zendesk-attachments- to-s3/ wʮ;FOEFTLͷ8FCIPPLͱ τϦΨʔͷઃఆʯͷষΛࢀߟ

Slide 15

Slide 15 text

"1*(BUFXBZ࣮૷ import { IdentitySource, LambdaIntegration, RequestAuthorizer, RestApi } from ‘aws-cdk-lib/aws-apigateway'; const api = new RestApi(this, 'zendesk-webhook-api', { deployOptions: { stageName: ‘v1’ // default Ͱ͸ prod ͕ੜ੒͞ΕΔ } }) const auth = new RequestAuthorizer(this, 'zendeskWebhookAuthorizer', { handler: authorizerFunction, // LambdaAuthorizer ࣮૷ identitySources: [ IdentitySource.header(‘Authorization'), IdentitySource.header(‘X-Request-Id’) ] })

Slide 16

Slide 16 text

"1*(BUFXBZ࣮૷ const webhookApi = api.root.addResource('resource'); webhookApi.addMethod( 'POST', new LambdaIntegration(postResourceFunction), { authorizer: auth } ) webhookApi.addCorsPreflight({ statusCode: 200, allowOrigins: Cors.ALL_ORIGINS, allowMethods: Cors.ALL_METHODS, allowHeaders: Cors.DEFAULT_HEADERS, })

Slide 17

Slide 17 text

-BNCEB"VUIPSJ[FS࣮૷ import { ManagedPolicy, Effect, PolicyStatement, Role, ServicePrincipal } from ‘aws-cdk-lib/aws-iam'; // policy ͷ࣮૷ const authorizerFunctionPolicy = new ManagedPolicy(this, "authorizer-function-policy", { managedPolicyName: "authorizer-function-policy", statements: [ new PolicyStatement({ effect: Effect.ALLOW, actions: [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources: ["arn:aws:logs:*:*:*"], }), ] }); const authorizerFunctionRole = new Role(this, "authorizer-function-role", { roleName: "authorizer-function-role", assumedBy: new ServicePrincipal("lambda.amazonaws.com"), managedPolicies: [authorizerFunctionPolicy] });

Slide 18

Slide 18 text

-BNCEB"VUIPSJ[FS࣮૷ // policy ͷ࣮૷ const authorizerInvokePolicy = new ManagedPolicy(this, "authorizer-invoke-policy", { managedPolicyName: "authorizer-invoke-policy", statements: [ new PolicyStatement({ effect: Effect.ALLOW, actions: [ "sts:AssumeRole", ], resources: ['*'], }) ] }) const authorizerInvokeRole = new Role(this, 'authorizer-invoke-role', { roleName: "authorizer-invoke-role", assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), managedPolicies: [authorizerInvokePolicy] });

Slide 19

Slide 19 text

-BNCEB"VUIPSJ[FS࣮૷ const authorizerFunction = new Function(this, ‘authorizer-function', { functionName: 'authorizer-function', description: ‘Zendesk WebhookͷBasicೝূΛݕূ͢Δ', code: new AssetCode(“resources"), // ೚ҙͷύεΛઃఆ handler: “auth/authorizer.handler", // ೚ҙͷύεΛઃఆ runtime: Runtime.PYTHON_3_9, role: authorizerFunctionRole, environment: { // parameter store Λར༻ SSM_ZENDESK_USERNAME: "/zendesk/username", SSM_ZENDESK_PASSWORD: "/zendesk/password", } }); // API Gateway ͕ Lambda Λ࣮ߦ͢ΔͨΊͷݖݶ authorizerFunction.grantInvoke(authorizerInvokeRole);

Slide 20

Slide 20 text

-BNCEB"VUIPSJ[FS࣮૷ import base64 import json import logging import os from hooks.utils import common TOKYO = "ap-northeast-1" logger = logging.getLogger() logger.setLevel(logging.INFO) zendesk_username = os.environ['SSM_ZENDESK_USERNAME'] zendesk_password = os.environ['SSM_ZENDESK_PASSWORD'] def handler(event, context): try: basic_header = event[“headers"]["Authorization"] req_id = event["headers"]["X-Request-Id"] result = is_valid(basic_header, common.get_parameter_value(zendesk_username), common.get_parameter_value(zendesk_password)) if result is True: return gen_policy(req_id, "Allow") return gen_policy(req_id, "Deny") except Exception as e: logger.error(e) raise Exception("Unauthorized") authorizer.py

Slide 21

Slide 21 text

-BNCEB"VUIPSJ[FS࣮૷ def is_valid(basic_header, username, password): basic_username_and_pasword = base64.b64encode( f"{username}:{password}".encode("utf-8") ) sig = f"Basic {basic_username_and_pasword.decode('utf-8')}" if basic_header == sig: return True return False def gen_policy(principal_id, effect: str): return { "principalId": principal_id, "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": effect, "Resource": '*', } ], }, } authorizer.py

Slide 22

Slide 22 text

-BNCEB"VUIPSJ[FS࣮૷ import boto3 def get_parameter_value(param_key): ssm = boto3.client('ssm') return ssm.get_parameter(Name=param_key, WithDecryption=True)['Parameter']['Value'] common.py

Slide 23

Slide 23 text

ʢ͓·͚ʣ8FCIPPLॺ໊ݕূ common.py https://dev.classmethod.jp/ articles/zendesk-webhook- signature-veri fi cation-in-python/ wηΩϡϦςΟରࡦͱͯ͠ 8FCIPPL͕ຊ౰ʹ;FOEFTL ͔Βͷ΋ͷͰ͋Δ͔Ͳ͏͔Λ֬ ೝɾݕূ͢Δ

Slide 24

Slide 24 text

ΞδΣϯμ ຊηογϣϯʹ͍ͭͯ ࣮૷֓ཁʢ8FCIPPL#BTJDೝূར༻ʣ "1*(BUFXBZ-BNCEB"VUIPSJ[FS࣮૷ ·ͱΊ

Slide 25

Slide 25 text

·ͱΊ w$%, -BNCEB"VUIPSJ[FS͸ϦϑΝϨϯεΛಡΈ࣮૷͠·͠ΐ͏ wIUUQTEPDTBXTBNB[PODPNKB@KQBQJHBUFXBZMBUFTU EFWFMPQFSHVJEFBQJHBUFXBZVTFMBNCEBBVUIPSJ[FSIUNM w8FCIPPLΛར༻࣮ͨ͠૷͸ɺ;FOEFTLʹݶΒͣଞͷαʔϏεͰ΋Α ͋͘ΔͨΊྲྀ༻Ͱ͖Δʢ4MBDL %JTDPSE -*/&FUDʣ wૉৼΓͱͯ͠ɺ͓खܰͳϢʔεέʔε

Slide 26

Slide 26 text

͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

Slide 27

Slide 27 text

No content