CTO室SREã® @kenzo0107 ã§ãã
2021å¹´6æ24æ¥ã«ã kakari for Clinic ãã¼ã ãã¼ã¸å¶ä½ ãããªãªã¼ã¹ããã¾ããã
ä»åã¯ä¸è¨ãµã¼ãã¹ã§æ¡ç¨ããã
AWS + ngx_mruby ã§æ§ç¯ãã SSL 証ææ¸ã®åçèªã¿è¾¼ã¿ã·ã¹ãã ã«ã¤ãã¦ã§ãã
SSL 証ææ¸ãåçã«èªã¿è¾¼ã¿ããçç±
kakari for Clinic ãã¼ã ãã¼ã¸å¶ä½ã®1æ©è½ã§ãå¶ä½ãããã¼ã ãã¼ã¸ã«ç¬èªãã¡ã¤ã³ãè¨å®ããæ©è½ãããçºã§ãã*1
è¤æ°ãã¡ã¤ã³ã§ã¢ã¯ã»ã¹ã§ãã
ï¼è¤æ°ãã¡ã¤ã³ã® SSL 証ææ¸ãèªã¿è¾¼ã
ãå®ç¾ããå¿
è¦ãããã¾ãã
åçã« SSL 証ææ¸ãèªã¿è¾¼ãã«ã¯ï¼
以ä¸ããããã®ã¢ã¸ã¥ã¼ã«ãçµã¿è¾¼ããã¨ã§ SSL 証ææ¸ã®åçèªã¿è¾¼ã¿ãå¯è½ã«ãªãã¾ãã
以ä¸çç±ãã ngx_mruby ãæ¡ç¨ãã¾ããã
- å¼ç¤¾ã¯ Ruby ã¨ã³ã¸ãã¢ã®å²åãé«ãï¼
- æè¡é¡§å Matz ããã«ç¸è«ã§ããï¼*2
ngx_mruby ã§ã® SSL 証ææ¸åçèªã¿è¾¼ã¿ å®è£ åèè³æ
è«æãé«éç©ãã«ãããã³ãWebãµã¼ãã®å¤§è¦æ¨¡è¨¼ææ¸ç®¡çããåèã«ããã¦ããã ãã¾ããã
p4 ã®ãå³3 åçãªãµã¼ã証ææ¸èªã¿è¾¼ã¿ã®è¨å®ä¾ (KVS ãã¼ã¹)ããè¦ãã¨å®è£ æ¦è¦ãããããããã§ãã
server { listen 443 ssl; server_name _; ssl_certificate /path/to/dummy.crt; ssl_certificate_key /path/to/dummy.key; mruby_ssl_handshake_handler_code â ssl = Nginx::SSL.new host = ssl.servername redis = Redis.new "127.0.0.1", 6379 crt, key = redis.hmget host, "crt", "key" ssl.certificate_data = redis["#{host}.crt"] ssl.certificate_key_data = redis["#{host}.key"] â; }
é常ã Nginx ã® ssl_certificate, ssl_certificate_key ã«å¤æ°ãå©ç¨ã§ãã¾ããã ngx_mruby ãå©ç¨ãã㨠Redis or ãã®ä»ãã証ææ¸æ å ± crt, key ãåå¾ãã è¨å®ãããã¨ãã§ãã¾ãã
ã·ã¹ãã æ§æ
å³å´ã®ã·ã¹ãã 管çè
ã»éå¶è
ã管çç»é¢ããéçã³ã³ãã³ãã S3 ã«çæãã¦ãã¾ãã
ä»å㯠ngx_mruby ã§ã®è¨¼ææ¸ã®åçé
ä¿¡ã«ã¤ãã¦ãã©ã¼ã«ã¹ãã¦ç´¹ä»ãã¾ãã*3
ã¦ã¼ã¶ã¢ã¯ã»ã¹ããã®ãµã¤ãã®ã³ã³ãã³ãé ä¿¡ãã大ã¾ããªæµãã¯ä»¥ä¸ã®éãã§ãã
- æ£è æ§ ãã¯ãªããã¯ãµã¤ãã«ã¢ã¯ã»ã¹
- ngx_mruby 㧠SSL/TLS ãã³ãã·ã§ã¤ã¯æã«ãã¡ã¤ã³ãå
ã« Redis ãã証ææ¸(crt), ç§å¯éµ(key) ãåå¾
- Redis ã«åå¨ããªãå ´å㯠DynamoDB ããåå¾ãã Redis ã«ãã£ãã·ã¥ç»é²
- åå¾ãã crt, key ãå ã« SSL/TLS ãã³ãã·ã§ã¤ã¯
- éçã¦ã§ããµã¤ãã¨ãã¦ãã¹ãã£ã³ã°ããã S3 㸠proxy ã HTML ã表示
- HTML å ã®å種 css, js, img 㯠CDN ã§é ä¿¡
ã·ã¹ãã ã®è©³ç´°ã»å·¥å¤«ç¹ã以ä¸ã«è¨è¼ãã¦åãã¾ãã
Nginx ã Fargate ã§èµ·åããã
ngx_mruby ãçµã¿è¾¼ãã Nginx 㯠Fargate ä¸ã§èµ·åããã¾ããã
ãµã¼ã管çã»ãããã¤ãã¹ã±ã¼ãªã³ã°ã®å®¹æãã®ã¡ãªããã大ããçºãFargate ãæ¡ç¨ãã¾ããã
Fargate ã§ã¯ net.core.somaxconn ãå¤æ´ã§ãã¾ãã ãã ãªã¯ã¨ã¹ãè©°ã¾ãããªãæ§ãã¿ã¹ã¯æ°ã«ã¯ä½è£ãæããã¦ãã¾ãã
Docker ã¤ã¡ã¼ã¸ã¯ https://github.com/matsumotory/ngx_mruby/blob/master/Dockerfile ãåèã« alpine ã§ãã«ãã¹ãã¼ã¸ãã«ãã軽éå (850 MB â 26 MB) ãã¾ããã
ã¤ã¡ã¼ã¸ãã«ãã ECS ã¸ã®ãããã¤ã¯ GitHub Actions ã§å®æ½ãã¦ãã¾ãã
SSL çµç«¯ã Nginx ã§å®æ½ãã¹ã NLB ãæ¡ç¨
ALB, CLB ã§ã¯ HTTPS (443) éä¿¡ããå ´åã¯ã証ææ¸ã®è¨å®ãå¿
é ã§ãã
NLB 㯠TCP (443) ãæå®ã SSL çµç«¯ã Target ã§å®æ½ã§ããFargate ã¨ã®è¦ªåæ§ãé«ãçºãæ¡ç¨ãã¾ããã
ALB 㯠ãã¼ããã©ã³ãµã¼ãããã®è¨¼ææ¸ (ããã©ã«ã証ææ¸ã¯å«ã¾ãªã): 25
ã§ãããã¨çãã¯ã©ã¼ã¿å¶é ãããçºãAWS LB ã·ãªã¼ãºã§ã® SSL çµç«¯ã¯ãµã¼ãã¹ãã¹ã±ã¼ã«ãããã¨ãèæ
®ããã¨æ¡ç¨ã§ãã¾ããã§ããã
証ææ¸çºè¡ã¯ ACM ã§ãªã Let's Encrypt ãæ¡ç¨
ACM 証ææ¸æ° ã¯ã©ã¼ã¿ å¶éãããçºããµã¼ãã¹ãã¹ã±ã¼ã«ãããã¨ãèæ ®ãã¦è¨¼ææ¸ã®çºè¡ã¯ Let's Encrypt ã§å®æ½ãããã¨ã¨ãã¾ããã*4
éå»ã«æ¥åã§å©ç¨çµé¨ããããã¾ãæ¬ä»¶ã§åèã«ããã¦ããã ããã¯ã¦ãªããã°ããã§ãæ¡ç¨ãã¦ãããã¨ãã¾ããããã¸ã§ã¯ããéå§ãããé ã« Software Design 2021å¹´4æå· ã§ç¹éããã¦ãããçºè¡ã®æ軽ãã¨ä¿¡é ¼æ§ããæ¡ç¨ãã¾ããã
NLB å©ç¨æã®æ³¨æç¹
NLB 㯠ALB ã¨ç°ãªãã以ä¸ã注æããå¿ è¦ãããã¾ããã*5
- ã»ãã¥ãªãã£ã°ã«ã¼ããã¢ã¿ããã§ããªã
- WAFãã¢ã¿ããã§ããªã
- 4xx, 5xx çã®ã¡ããªã¯ã¹ããªã
対ç: ã»ãã¥ãªãã£ã°ã«ã¼ããã¢ã¿ããã§ããªã
ã»ãã¥ãªãã£ã°ã«ã¼ãã§å®æ½ãã¦ãã IP å¶é㯠ngx_mruby ã§å®è£ ãã¾ããã
- allow_request.rb
# frozen_string_literal: true # ãªã¯ã¨ã¹ã許å¯å¦çã¯ã©ã¹ class AllowRequest def initialize(request, connection) @r = request @c = connection end def allowed_ip_addresses ENV['ALLOW_IPS'].split(',') end def allowed? return true unless (allowed_ip_addresses & [ @c.remote_ip, @r.headers_in['X-Real-IP'], @r.headers_in['X-Forwarded-For'] ].compact).empty? false end AllowRequest.new(Nginx::Request.new, Nginx::Connection.new).allowed? end
nginx.conf
env ALLOW_IPS; ... # è¨±å¯ IP ã§ãªãå ´åã 404 ãè¿ã mruby_set $allow_request /etc/nginx/hook/allow_request.rb cache; if ($allow_request = 'false') { return 404; }
ç°å¢å¤æ° ALLOW_IPS ã«è¨±å¯ããã IP ã渡ã㨠ngx_mruby ã§è¨±å¯ IP 以å¤ã¯ 404 ãè¿ãã¾ãã
NLB + Nginx on Fargate ã§ã¯ã©ã¤ã¢ã³ã IP ã渡ãæ¹æ³
NLB 㯠Target Group ã®ãããã³ã«ã TCP or TLS ã®å ´åã ã¯ã©ã¤ã¢ã³ã IP ä¿æã¯ããã©ã«ãã§ç¡å¹åããã¦ãã¾ãã*6
ãã®çºãæ示çã«ã¯ã©ã¤ã¢ã³ã IP ã®ä¿æãæå¹åããå¿
è¦ãããã¾ãã
Proxy protocol v2 ãæå¹åããNginx 㧠proxy_protocol
ãè¨å®ãããã¨ã§ãNginx ã§ã¯ã©ã¤ã¢ã³ã IP ã解éã§ããæ§ã«ãªãã¾ãã
server { listen 443 ssl proxy_protocol; server_name _;
対ç: WAF ãã¢ã¿ããã§ããªã
NLB ã«ã¯ WAF ãã¢ã¿ããã§ãã¾ããã
XSS, SQLi çã® WAF 㯠Nginx ã« NAXSI *7 ãå°å
¥ãããã¨ã§å¯¾å¿ãã¾ããã*8
location / { # NAXSI ã«ãã SQLi, XSS çæ¤ç¥ããããã¯ããå ´åã403 ãè¿ã SecRulesEnabled; DeniedUrl /request_denied; CheckRule "$SQL >= 8" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$EVADE >= 4" BLOCK; # whitelist: XSS double encoding ã誤æ¤ç¥ãããçºã許容ãã BasicRule wl:1315; ... } # WAF ã§ãããã¯ããéã« 403 ãè¿ã location = /request_denied { return 403; }
誤æ¤ç¥ããéã«ã¯ç¹å®ã«ã¼ã«ããã¯ã¤ããªã¹ãã¨ãã¦ç»é²ã許容ãããã¨ãå¯è½ã§ãã*9
ãããã¯æã«ã¯ Nginx ã¨ã©ã¼ãã°ã«åºåããã¾ãã*10
2021/06/11 17:53:32 [error] 7#0: *53 NAXSI_FMT: ip=172.21.0.1&server=example.com&uri=/%25U&vers=1.3&total_processed=13&total_blocked=11&config=block&cscore0=$EVADE&score0=4&zone0=URL&id0=1401&var_name0=
対ç: 4xx, 5xx ã¡ããªã¯ã¹ããªã
NLB 㯠ALB ã¨ã¯ç°ãªã 4xx, 5xx ã¡ããªã¯ã¹ããªããã¨ã©ã¼æ¤ç¥ãã§ãã¾ããã
以ä¸ã®æ§ã«å¯¾å¿ãã¾ããã
- fluentbit 㧠Nginx ã®ãã°ã CloudWatch Logs ã¸é ä¿¡
- CloudWatch Metric Filter 㧠4xx, 5xx ã¨ã©ã¼ããã£ã«ã¿ãªã³ã°*11
- CloudWatch Alarm 㧠4xx, 5xx ã®æ°ãé¾å¤ãè¶ ãã㨠SNS çµç±ã§ Chatbot ã¸éç¥*12
- Chatbot ã¨é£æºãã Slack ã¸éç¥
CloudWatch Logs ã¯éç¥ç¨ã«å©ç¨ã
Kinesis Firehose + S3 㯠Athena ã§ãã°ææ»æã«å©ç¨ãã¾ãã
RDS ã§ãªã DynamoDB ã§ãã¼ã¿æ°¸ç¶å
ngx_mruby ã®ãµã³ãã«ã³ã¼ãã§ã¯ã証ææ¸æ å ±ã Redis ã§ãã£ãã·ã¥ãã RDS ã§æ°¸ç¶åãããã¿ã¼ã³ãããè¦ããã¾ããã
ã§ãããä»å㯠DynamoDB ãæ¡ç¨ãã¦ãã¾ãã
çç±ã¯ããã¡ã¤ã³åããã¼ã«è¨¼ææ¸æ å ±ãåå¾ããä»åã®ã±ã¼ã¹ã§ã¯è¤éãªã¯ã¨ãªãå®è¡ããå¿ è¦ããªãããªã¬ã¼ã·ã§ãã« DB ã¨æ¯è¼ã㦠NoSQL ã®ç¹å¾´ã§ãã以ä¸ã¡ãªããã享åã§ããçºã§ãã
- æè»ã§ã¹ãã¼ãã¬ã¹ãªãã¼ã¿ã¢ãã«
- æ°´å¹³ã¹ã±ã¼ã©ããªãã£
- åæ£ã¢ã¼ããã¯ãã£
- é«éãªå¦ç
åè: 何が違う?DynamoDBとRDS - サーバーワークスエンジニアブログ
DynamoDB ã¸ã®ã¢ã¯ã»ã¹ã¯ API Gateway + Lambda
ngx_mruby 㯠https://rubygems.org/ ã® gem ãå©ç¨ã§ãã¾ããã *13
ä½ã¬ãã« API ã mattn/mruby-curl ã§å®ç¾ã§ããªããã¨ããªãããã§ãããé£æ度ãé«ãæ¤è¨¼å·¥æ°ã確ä¿ã§ãããã«ãªãç¹ããè¦éãã¾ããã
ãã®ä»£ããã«
Lambda 㧠aws-sdk ãå©ç¨ã DynamoDB ã¸ã¢ã¯ã»ã¹ããæ§ã«ãã¾ããã
API Gateway 㧠Lambda ã®ã¨ã³ããã¤ã³ããè¨å®ã ngx_mruby ãã mattn/mruby-curl ã§ã¨ã³ããã¤ã³ããå©ã Lambda ãå®è¡ããæ§ã«ãã¾ããã
ä¸è¨æ§æã§æ°åããªç§ç¨åº¦ã§ã¬ã¹ãã³ã¹ãè¿ãåç¨ç°å¢ã®å©ç¨ã¯åé¡ããã¾ããã§ããã
ã¡ãªã¿ã«ã æ°¸ç¶åãã¼ã¿ãæ ä¿ãã DynamoDB ã¸ã®ã¢ã¯ã»ã¹ã¯ä»¥ä¸ã®å ´åã¨ãªããåºæ¬çã«é »åº¦ã¯ä½ãã§ãã
- ElastiCache Redis ã«ã¢ã¯ã»ã¹ã§ããªã
- ElastiCache Redis ã®ãã¼ã¿ãæ®çºãã*14
証ææ¸ã®èªåæ´æ° ã·ã¹ãã æ§æ
æ¦è¦ã¯ä»¥ä¸ã®éãã§ãã
- EventBridge (cron) 㧠Lambda
cert-lifecycle-store
ãå®æå®è¡ cert-lifecycle-store
ã§è¨¼ææ¸ã®æå¹æ¥æ°ã 30æ¥ä»¥ä¸ã®è¨¼ææ¸ã®ãã¡ã¤ã³ãªã¹ããåå¾*15cert-lifecycle-store
ããcert-updater
ã«ãã¡ã¤ã³åã渡ã証ææ¸ã®æ´æ°ãå®è¡cert-updater
㧠go-acme/lego ãå©ç¨ã Let's Encrypt ã§è¨¼ææ¸ãçºè¡- SSL 証ææ¸ (crt) 㨠ç§å¯éµ (key) ã DynamoDB, ElastiCache Redis ã«ä¿åããã¼ã¸ã§ã³ç®¡çã¨ã㦠S3 ã«è¨¼ææ¸çºè¡æã®ã¬ã¹ãã³ã¹ã JSON ãã¡ã¤ã«ã«ä¿å
証ææ¸ã®æ°è¦çºè¡ã¯ç®¡çç»é¢ãã cert-updater
ãå®æ½ã§ããæ§ã«ãã¦ãããéç¨è
ã証ææ¸ãçºè¡ã§ããæ§ã«ãã¦ãã¾ãã
åè
- AWSã§ã¯ã¦ãªããã°ã®å¸¸æHTTPSé ä¿¡ããã¼ã³ã¨ãã話
- Let's Encrypt ã®è¨¼ææ¸åå¾ã AWS Lambda ã§ãã£ã¦ã¿ã
ãã¾ã
mruby 仲éãå¢ããããæ°æã¡ããä»åã® ngx_mruby ãç¨ãã証ææ¸ã®åçèªã¿è¾¼ã¿ãç°¡æçã«ä½é¨ã§ãããªãã¸ããªãç¨æãã¾ããã
ngx_mruby åãã¾ãã¦ã®æ¹ãããã§ãªãæ¹ãéãã§ããã ããã¨å¹¸ãã§ãã
以ä¸ã§ãã
æ¡ç¨ã®ãªã³ã¯
ã¡ããã¢ã§ã¯ä¸ç·ã«åã仲éãåéãã¦ãã¾ãã ãå¿åããå¾ ã¡ãã¦ããã¾ãï¼
â åéãã¸ã·ã§ã³ã¯ãã¡ã
https://medpeer.co.jp/recruit/entry/
â éçºç°å¢ã¯ãã¡ã
https://medpeer.co.jp/recruit/workplace/development.html
*1:å¼ç¤¾ããã¯ããã°ã§ãå©ç¨ãã¦ããã¾ããã¯ã¦ãªããã°ã®ãç¬èªãã¡ã¤ã³ãã®è¨å®ã¨åæ§ã®æ©è½ã§ãã
*2:å¼ç¤¾ã§ã¯å®æçã« Matz ããã¸èããããã¨ï¼ä¼ãéå¬é ãã¦ããã¾ãã
*3:Ruby on Rails ã§æ§æããã管çç»é¢ã§éçã³ã³ãã³ããS3ã«ã¢ãããã¼ãããä»çµã¿ã«ã¤ãã¦ã¯å¥éæ¬ããã°ã§ç´¹ä»äºå®ã§ããã楽ãã¿ã«â¨
*4:ããã¸ã§ã¯ãéå§åã«å¼ç¤¾æ å½ã® AWS ã½ãªã¥ã¼ã·ã§ã³ã¢ã¼ãããã«ç¸è«ããã¨ããããµã¼ãã¹ãã¹ã±ã¼ã«ãããã¨ãèæ ®ãã㨠ACM ã§ãªãå¥é証ææ¸çºè¡ã·ã¹ãã ãæ¡ç¨ãããã¨ãæ¨å¥¨ããã¾ããã
*5:å¼ç¤¾ã§ã¯ NLB ã¯æ¬ããã¸ã§ã¯ããåæ¡ç¨ã§ããã
*6: NLB Client IP preservation ã«ã¦ãIf the target group protocol is TCP or TLS, client IP preservation is disabled by default. ãã¨è¨è¼ãããéãã§ãã
*7:NAXSI 㯠Nginx Anti XSS & SQL Injection ã®ç¥ã§ Nginx ç¹åã® WAF ã¢ã¸ã¥ã¼ã«ã§ãã
*8:Nemesida WAF Free㯠alpineãã¼ã¹ã ã¨å°å ¥æ¹æ³ãããããªãã£ãï¼ã§ããªãã£ãï¼ãNginx Plus ModSecurityã¯å¹´é40ä¸å以ä¸ã®æåãµã¼ãã¹ã§æ¤è¨¼å·¥æ°ã確ä¿ã§ãããæ念ãã¾ããã
*9:w:1315 ã® 1315 㯠ã«ã¼ã«ã«æ¡çªããã¦ããID㧠https://github.com/nbs-system/naxsi/blob/master/naxsi_config/naxsi_core.rules ã«è¨è¼ããã¦ãã¾ãã
*10:LOG ãè¨å®ããã¨ãããã¯ãããã°ã«åºåããã¢ã¼ããããæ§ã§ãããLearningMode ï¼å¦ç¿ã¢ã¼ãï¼ãè¨å®ããªãã¨ãAssertion failed: strlen(fmt_config) != 0 (/usr/local/src/naxsi/naxsi_src//naxsi_runtime.c: ngx_http_nx_log: 1076)ãã¨ããã¨ã©ã¼ãçºçãããã¨ã確èªãã¦ãã¾ããAWS WAF ã® count ã®æ§ãªæ©è½ãæå¾ ãã¦ãã¾ãããéãã¾ããã
*11:CloudWatch Metric Filter ã®ã¢ã¤ã³ã³ãè¦ã¤ãããªãã£ã
*12:SNS é£æºå ã Lambda ã§ãªã Chatbot ã«ããå ´åãéç¥å 容ã
*13:ãã®ä»£ãã https://github.com/mruby/mgem-list ã«ãã gem ãå©ç¨ã§ãã¾ã
*14:よくある質問 - Amazon ElastiCache | AWS ã«ã¦ãã¨ã³ã¸ã³ã®ã¢ããã°ã¬ã¼ãããã»ã¹ã¯ãæ¢åã®ãã¼ã¿ããã¹ãã¨ãã©ã¼ãã§ä¿æããããã«è¨è¨ããã¦ãããRedis ã¬ããªã±ã¼ã·ã§ã³ã«æåããå¿ è¦ãããã¾ãããã¨ããããã¼ã¿ã¯æ®çºããå¯è½æ§ããããã¨ãåæã«è¨è¨ãã¦ãã¾ãã
*15:Let's Encrypt ã®è¨¼ææ¸ã®æå¹æé㯠90 æ¥é㧠60æ¥æ¯ã®æ´æ°ãæ¨å¥¨ãã¦ããçºã§ã