talked at builderscon tokyo 2018
ϒϩάαʔϏεͷ HTTPSԽΛࢧ͑ͨ AWSͰ࡞ΔϐλΰϥεΠονid:aereal
View Slide
staff.hatenablog.com/entry/2018/06/13/160000ಠࣗυϝΠϯͰӡ༻͞Ε͍ͯΔϒϩά͕ɺHTTPSͰ৴Ͱ͖ΔΑ͏ʹͳΓ·ͨ͠
͢͜ͱ• ͯͳϒϩάͷৗ࣌HTTPS৴ͷղઆ• എܠͱཁٻ• ࣮ͷհ• ্هࣄྫΛݩʹෳࡶͳόον = ϐλΰϥεΠονߏஙͷ ΤοηϯεΛߟ͑ͯΈΔ
ࣗݾհ• id:aereal• GitHub: aereal• Twitter: aereal• ϒϩά౷߹νʔϜ ΞϓϦέʔγϣϯΤϯδχΞ ςοΫϦʔυ
എܠͯͳϒϩάͷৗ࣌HTTPS৴ͷղઆ
• ͯͳϒϩάPro (༗ྉϓϥϯ) ʹਃ͠ࠐΉͱ ಠࣗυϝΠϯͰࣗͷϒϩάΛ৴Ͱ͖Δ• ݱࡏɺສ୯ҐͷಠࣗυϝΠϯ͕ొɾར༻͞Ε͍ͯΔ• ͜ΕΒͷಠࣗυϝΠϯͰৗ࣌HTTPS৴͍ͨ͠
Let's Encrypt• ISRG = Internet Security Research Group͕ఏڙ͢Δ ϓϩάϥϚϒϧʹΞΫηεՄೳͳೝূہ (CA)• ͜Ε·ͰTLSূ໌ॻΛൃߦ͢Δʹ ͦͦ͜͜ͷֹۚͱख͕ؒඞཁ͕ͩͬͨɺͦΕΛม͑ͨCA• LEͷొʹΑΓTLSূ໌ॻͷେྔൃߦ͕ݱ࣮తʹͳͬͨ
developer.hatenastaff.com/entry/2018/06/04/140000ͯͳϒϩάͷHTTPSԽ࣮ࢪʹ͍, Let'sEncryptͷدΛ࣮ࢪ͠·ͨ͠ - Hatena Developer Blog
• LEͷొ࿕ใ͕ͩ͜Ε͚ͩͰΓͳ͍• ສ୯ҐͷTLSূ໌ॻΛཧ͢Δઓज़ɾઓུ͕͚͍ܽͯΔ• ৴ͱൃߦʹେ͖͚ͯ͘ΈΔ
ཁ݅ͷݕ౼: ৴ͯͳϒϩάͷৗ࣌HTTPS৴ͷղઆ
HTTPS৴: ͓͞Β͍• ͯͳϒϩάͰສ୯ҐͷಠࣗυϝΠϯ͕ར༻͞Ε͍ͯΔ• ҰൠతͳWebαΠτӡ༻ͷײ֮ͩͱφʔόε͗͢Δ• ສ୯Ґͷূ໌ॻΛҰʹಡΈࠐΉͱ proxyͷϝϞϦ༻ྔ͕ஶ͘͠૿Ճ͢Δ• proxyͷ࠶ىಈʹ͕͔͔࣌ؒΔ
SAN?• = Subject Alternative Names 1ͭͷূ໌ॻʹෳυϝΠϯΛඥ͚Δ֦ு• ͔݁Βݴ͏ͱͯͳϒϩάͷέʔεͰ͍͠• LEͰSANΛར༻͢Δ߹ɺACME challengedns-01ͷΈར༻Ͱ͖Δ (ݱࡏ)• DNSઃఆ֤ϢʔβʔʹҕͶΒΕΔͷͰࣗಈԽͰ͖ͳ͍
ACME?• ACME: Automated Certificate Management Environment• ূ໌ॻൃߦͳͲͷ࡞ۀΛ ࣗಈԽ͢ΔϓϩτίϧΛ·ͱΊ༷ͨ• ACME challenge: υϝΠϯͷॴ༗ݖݶΛ֬ೝ͢Δํ๏• Google AnalyticsͷΞϨΈ͍ͨͳͭ• LE͕ࡦఆɾ࠾༻͍ͯ͠Δ
ACME challenge?• dns-01: υϝΠϯͷTXTϨίʔυʹϫϯλΠϜτʔΫϯΛॻ͖ࠐΉ• http-01: CAͷϦΫΤετʹର͠ॴఆͷϨεϙϯεΛฦ͢• ྫ: /.well-known/TOKEN• (ଞʹ͍Ζ͍Ζ)
HTTPS৴: ͓͞Β͍ (࠶)• ͯͳϒϩάͰສ୯ҐͷಠࣗυϝΠϯ͕ར༻͞Ε͍ͯΔ• ҰൠతͳWebαΠτӡ༻ͷײ֮ͩͱφʔόε͗͢Δ• ສ୯Ґͷূ໌ॻΛҰʹಡΈࠐΉͱ proxyͷϝϞϦ༻ྔ͕ஶ͘͠૿Ճ͢Δ• proxyͷ࠶ىಈʹ͕͔͔࣌ؒΔ
HTTPS৴: ํ• ϦΫΤετຖʹূ໌ॻΛબɾಡΈࠐΉ• ϝϞϦ༻ྔͷ૿Ճ࠶ىಈ࣌ؒͷѱԽΛ͑Δ• ෳproxyʹରԠ͢ΔͨΊσʔλετΞʹূ໌ॻΛอଘ• ͔͠ϨΠςϯγΛѱԽͤͣ͞ʹ࣮ݱ͢Δ• ϩʔΧϧΩϟογϡ
ཁ݅ͷݕ౼:ൃߦͯͳϒϩάͷৗ࣌HTTPS৴ͷղઆ
ূ໌ॻൃߦ• Ұ؏ੑɾཏੑ͕ٻΊΒΕΔ• ൃߦʹࣦഊ͠ଓ͚Δͱϒϩά͕ӾཡͰ͖ͳ͘ͳΔ• ແޮͳυϝΠϯΛ์ஔ͍͚ͯ͠ͳ͍• ཁٻߴ͍͕ෆ࣮֬ੑߴ͍• ূ໌ॻΛߋ৽͢ΔࡍɺυϝΠϯʹର͠εέʔϧ͢Δ͜ͱ
ແޮͳυϝΠϯͷআ• ແޮͳυϝΠϯ = ඞͣACME challengeʹࣦഊ͢Δ• LEʹΞΧϯτ * time window͝ͱʹࣦഊͷ্ݶ͕͋Δ• ์ஔ͢ΔͱඞͣAPI limitʹ͋ͨͬͯ͠·͏• ࣦഊͨ͠υϝΠϯඞͣআ
ূ໌ॻൃߦ: ෆ࣮֬ੑ• υϝΠϯͷ༗ޮੑมΘΓ͏Δ• ՝ۚऴྃ• DNSϨίʔυҟৗ• ֎෦API = LEͱͷ౷߹• API Limit• దͳϦτϥΠͱΤϥʔϦΧόϦ͕ඞਢ
ূ໌ॻൃߦ: εέʔϥϏϦςΟ• ରυϝΠϯͷ૿Ճʹର͠εέʔϧ͢ΔΈʹ͍ͨ͠• SELECT * FROM custom_domain WHERE id > ? Έ͍ͨͳΫΤϦආ͚͍ͨ• υϝΠϯ͕૿͑Δͱϖʔδϯά͕ඞཁ• ࣮ߦ్தͰࣦഊͨ͠ΒɺϦτϥΠΩϡʔʹೖΕ͢Α͏ͳΛڽΒ͞ͳ͍ͱ͍͚ͳ͘ͳΔ
γεςϜͷཁ݅: ·ͱΊ• ϦΫΤετຖʹূ໌ॻΛऔಘɾ༻• Ͱ͖Δ͚ͩϨΠςϯγͰ• Τϥʔੑ͕ߴ͍• ࣦഊͨ͠ΒऔಘରͷυϝΠϯ͔Β֎͢• ֎෦API௨৴ͷΤϥʔΛదʹॲཧͰ͖Δ• υϝΠϯͷ૿Ճʹεέʔϧ͢Δ
γεςϜͷհͯͳϒϩάͷৗ࣌HTTPS৴ͷղઆ
cert-dispatchercert-cache-gwcert-storecert-cacheUser BlogHTTP ssl_handshake_handlerHTTPGet/Set Get৴
৴γεςϜ• ngx_mruby: ূ໌ॻಡΈࠐΈ࣌ʹmrubyͷίʔυΛ࣮ߦ• cache gatewayHTTP GET͢Δ͚ͩ• https://github.com/matsumotory/ngx_mruby• cache gateway (Go): HTTP GET͢Δͱূ໌ॻΛฦ͢• DynamoDB: ূ໌ॻΛอଘ͢ΔσʔλετΞ
cache gateway• AWS (DynamoDB) APIݺͼग़͠ΛHTTP APIʹม͑Δ• mrubyʹAWS SDK͕ͳ͍• ಉډ͢ΔmemcachedʹಡΈॻ͖͠ɺ DynamoDBͷΞΫηεΛͰ͖Δ͚ͩݮΒ͢
৴γεςϜ• ngx_mrubyΛͬͯϦΫΤετຖʹূ໌ॻΛऔಘͰ͖ͨ• proxyʹಉډͨ͠memcachedΛ͏͜ͱͰ DynamoDBͷϦΫΤετΛݮΒ͠ϨΠςϯγΛԼ͛ͨ
cert-updater-state cert-updater-functioncert-update-notifierLet's Encryptcert-store cert-lifecycle-storeBlogHTTPHTTP࣮ߦ࣮ߦUpdateItemUpdateItemূ໌ॻൃߦ࣮ߦূ໌ॻൃߦ
ূ໌ॻൃߦγεςϜ• cert-updater-state: AWS StepFunctions; ֤LambdaΛىಈ• Τϥʔ༰ʹԠͨ͡ϦΧόϦɾϦτϥΠ (ޙड़)• cert-updater-function: AWS Lambda; ূ໌ॻΛൃߦɺDynamoDBॻ͖ࠐΈ• cert-update-notifier: Lambda; ൱Λͯͳϒϩά௨
AWS SFn: ϦτϥΠ"Retry": [{"ErrorEquals": ["ErrMaybeRecoverable"],"IntervalSeconds": 1,"MaxAttempts": 3,"BackoffRate": 2.0}],"Catch": [{"ErrorEquals": ["States.TaskFailed"],"Next": "Notify result to Hatena-Epic"}],
ূ໌ॻൃߦγεςϜ• AWS StepFunctionsΛͬͯదͳΤϥʔॲཧΛ࣮ݱ• Ϧιʔε্ݶʹୡ͢ΔͳͲ ҟৗऴྃͨ࣌͠ଈ࠲ʹ݁ՌΛ௨• APIݺͼग़ࣦ͠ഊͳͲϦτϥΠՄೳͳ࣌ϦτϥΠ
cert-reissue-state cert-reissue-confirmercert-updater-state-callercert-cleanup-functionBlogcert-lifecycle-storecert-update-triggercert-updater-statecert-store࣮ߦ ࣮ߦ࣮ߦ࣮ߦ࣮ߦHTTPTTL TriggerDeleteItemূ໌ॻൃߦ (ߋ৽)
ূ໌ॻൃߦ: ߋ৽• DynamoDBͷTTL Trigger͕Lambdaܦ༝ͰSFnΛىಈ• cert-reissue-confirmer: ͯͳϒϩάʹυϝΠϯ༗ޮੑΛ͍߹Θͤͯɺߋ৽͢Δඞཁ͕͋Δ͔Λޙଓʹ͑Δ• cert-cleanup-function: ແޮͳυϝΠϯΛDynamoDB͔Βফ͢
cert-lifecycle-store (DynamoDB)Domain: ex1.example.comExpiresAt: 2018-05-23T02:00:00Domain: ex2.example.comExpiresAt: 2018-05-23T03:00:00Domain: ex2.example.comExpiresAt: 2018-05-23T04:00:00Domain: ex2.example.comExpiresAt: 2018-05-23T05:00:00
cert-lifecycle-store (DynamoDB)Domain: ex2.example.comExpiresAt: 2018-05-23T03:00:00Domain: ex2.example.comExpiresAt: 2018-05-23T04:00:00Domain: ex2.example.comExpiresAt: 2018-05-23T05:00:00
cert-lifecycle-store (DynamoDB) Domain: ex2.example.comExpiresAt: 2018-05-23T04:00:00Domain: ex2.example.comExpiresAt: 2018-05-23T05:00:00
cert-lifecycle-store (DynamoDB)Domain: ex2.example.comExpiresAt: 2018-05-23T05:00:00
cert-lifecycle-store (DynamoDB)
publishSELECT * FROM ...࣮ߦ
Τϥʔॲཧ͕؆ܿʹ• όονॲཧͩͱ: औಘͨ͠ෳͷυϝΠϯΛϧʔϓͰॲཧ• = ॲཧ୯Ґ͕ෳυϝΠϯʹͳΔ• Ұ෦ͷυϝΠϯ͕ࣦഊͨ࣌͠ɺόονॲཧશମͷ εςʔλεͲ͏͢Δ? ޭ? ࣦഊ? • pub/subͩͱ: Ҿͱͯͬͨ͠υϝΠϯ1ͭΛॲཧ͢Δ• = ॲཧ୯Ґ͕υϝΠϯ1ͭʹͳΔ
cert-reissue-state"Determine next state": {"Comment": "࣍ͷঢ়ଶΛܾఆ͠·͢","Type": "Choice","Choices": [{"Variable": "$.UpdateRequired","BooleanEquals": true,"Next": "Call reissue of certificate"},{"Variable": "$.UpdateRequired","BooleanEquals": false,"Next": "Clean up of certificate"}]},
ূ໌ॻߋ৽γεςϜ• σʔλϑϩʔΛpub/subͰγϯϓϧʹ• ॳճൃߦߋ৽࣌DynamoDBͷI/O͚͕ͩൃੜ͢Δ• DynamoDB TTL TriggerΛ׆༻• ঢ়ଶ = σʔλΛதԝʹू
࠶ܝ: ৴γεςϜ• ngx_mrubyΛͬͯϦΫΤετຖʹূ໌ॻΛऔಘͰ͖ͨ• proxyʹಉډͨ͠memcachedΛ͏͜ͱͰ DynamoDBͷϦΫΤετΛݮΒ͠ϨΠςϯγΛԼ͛ͨ
࠶ܝ: ূ໌ॻൃߦγεςϜ• AWS StepFunctionsΛͬͯదͳΤϥʔॲཧΛͰ͖ͨ• Ϧιʔε্ݶʹୡ͢ΔͳͲ ҟৗऴྃͨ࣌͠ଈ࠲ʹ݁ՌΛ௨• APIݺͼग़ࣦ͠ഊͳͲϦτϥΠՄೳͳ࣌ϦτϥΠ
ߟϐλΰϥεΠονͷ࡞Γํ
ڊେͳόονͷ͠͞• ࣮ߦεςοϓશ༰ΛѲ͢Δ͜ͱͷ͠͞• શମͰεςοϓ͕͜Ε͚ͩ͋Δ• Ͳ͜ͷεςοϓͰࣦഊͨ͠ͷ͔• ॲཧ୯Ґ͕େ͖͘ͳΓ͕ͪ• ඞવͱ࣮ߦ࣌ؒҾ͖͕ͪ• Ұ෦͚ࣦͩഊͨ࣌͠ɺ࣮ߦͷঢ়ଶޭ? ࣦഊ?
΅͘ͷ͔Μ͕͍͖͑ͨ͞ΐ͏ͷϐλΰϥεΠον• ϫʔΫϑϩʔΤϯδϯͷಋೖ• ࣮ߦεςοϓશ༰ΛѲ͘͢͠• ͦΕͱߴʹ౷߹͞Εͨόον࣮ߦڥ͕͋Δͱͳ͓Α͍• pub/subϞσϧͰରσʔλͷ૿Ճʹର͠εέʔϧͤ͞Δ• ॲཧ͢Δσʔλ୯ҐΛෳˠ1ͭ• ͍ͭͰʹσʔλετΞঢ়ଶ͕ڽू͞ΕΔ
ׂ౷࣏• খ͞ͳؔΫϥεΛ࡞ΓɺͦΕΒΛΈ߹ΘͤΔ͜ͱΛීஈ͔Βҙ͍ࣝͯͬͯ͠Δͣ• ʹؔΘΒͣόον͕ڊେʹͳΓ͕ͪͳͷͳͥͳͷ͔?• ύϑΥʔϚϯε• ߹Մೳ (composable) Ͱͳ͍
߹ՄೳΛࢧ͑Δٕज़• 2ͭͷεςοϓͷྻ࣮ߦΛೋ߲ԋࢉͱΈͳͯ͠ΈΔ• operand: ൣғ͕খ͍͜͞ͱ• operator: ༷ʑͳ๏ଇΛຬͨ͢͜ͱ• ݁߹ଇɺଇ
ہॴঢ়ଶΛ࣋ͨͳ͍• ঢ়ଶ = มߋՄೳͳσʔλ• άϩʔόϧʹͨͩ1ͭͷঢ়ଶΛ࣋ͭ͜ͱ͕େࣄ• Ճ͑ͯঢ়ଶΛมߋ͢Δཁૉ͕୯ҰͰ͋Δ͜ͱ
άϩʔόϧม?• άϩʔόϧมѱͱ͍͏ߟ͑ํͱ͠ͳ͍͔? → ͠ͳ͍• ঢ়ଶΛมߋ͢Δཁૉ͕୯ҰͳΒɺ ֤࣮ߦεςοϓঢ়ଶΛड͚औͬͯ৽ͨͳσʔλΛฦ͢ ؔͱΈͳͤΔ
// ϫʔΫϑϩʔΤϯδϯͷঢ়ଶ{"domain":"www.example.com","endpoint": "https://...."}// ͋Δόονͷೖྗ{"domain":"www.example.com"}
// ϫʔΫϑϩʔΤϯδϯͷঢ়ଶ{"domain":"www.example.com","endpoint": "https://...."}// ͋Δόονͷೖྗ{"domain":"www.example.com"}άϩʔόϧঢ়ଶΛҾม͢Δ (όον͔ΒͷมߋෆՄ)
// ϫʔΫϑϩʔΤϯδϯͷঢ়ଶ{"updateRequired": true,"domain":"www.example.com","endpoint": "https://...."}// ͋Δόονͷग़ྗ{"updateRequired": true}
// ϫʔΫϑϩʔΤϯδϯͷঢ়ଶ{"updateRequired": true,"domain":"www.example.com","endpoint": "https://...."}// ͋Δόονͷग़ྗ{"updateRequired": true}όονͷग़ྗΛάϩʔόϧͳঢ়ଶม (વɺग़ྗޙ͔ΒมߋෆՄ)
όονॲཧͷ߹• operand: ֤εςοϓ• operator: ϫʔΫϑϩʔΤϯδϯ
όονॲཧͷ߹• operand: ֤εςοϓ; AWS Lambda• operator: ϫʔΫϑϩʔΤϯδϯ; AWS StepFunctions
΅͘ͷ͔Μ͕͍͖͑ͨ͞ΐ͏ͷϐλΰϥεΠον@ͯͳϒϩά• ϫʔΫϑϩʔΤϯδϯ: AWS StepFunctions• ……ͱͦΕΒ͔Β࣮ߦ͞ΕΔAWS Lambda• pub/sub: DynamoDB TTL Trigger
࠶: ΅͘ͷ͔Μ͕͍͖͑ͨ͞ΐ͏ͷϐλΰϥεΠον• ϫʔΫϑϩʔΤϯδϯͷಋೖ• ࣮ߦεςοϓશ༰ΛѲ͘͢͠• ͦΕͱߴʹ౷߹͞Εͨόον࣮ߦڥ͕͋Δͱͳ͓Α͍• pub/subϞσϧͰରσʔλͷ૿Ճʹର͠εέʔϧͤ͞Δ• ॲཧ͢Δσʔλ୯ҐΛෳˠ1ͭ• ͍ͭͰʹσʔλετΞঢ়ଶ͕ڽू͞ΕΔ
·ͱΊ
·ͱΊ• ιϑτΣΞߏஙҰൠͷݪଇ͕͑Δ• άϩʔόϧͳঢ়ଶΛ࣋ͨͳ͍ɾม͑ͳ͍ɾ࣋ͪࠐ·ͤͳ͍• ॲཧ୯ҐΛͰ͖Δ͚ͩখ͘͞ɺࣦഊΛѲ͘͢͠• ͜ΕΒΛ࣮ݱ͢ΔͨΊͷҰྫͱͯ͠• ϫʔΫϑϩʔΤϯδϯ: AWS StepFunctions• pub/subΛαϙʔτ͢ΔσʔλετΞ: DynamoDB