Let's Encryptãã¯ã¾ã£ãGolangã®è½ã¨ãç©´
0. çãã¾ã¨ã
300ä¸ä»¥ä¸ã®è¨¼ææ¸ã®å¤±å¹ãè¿«ãããLet's Encryptã®ã¤ã³ã·ãã³ãã¯ãGolangã§ããããééããã¨æ¸ããã¦ãããããªãã°ãåå ã§ããã
1. ã¯ããã«ã
Let's Encryptã¯ãç¡æã§ãµã¼ã証ææ¸ãèªååãã¦çºè¡ãããµã¼ãã¹ãè¡ãéå¶å©å£ä½ã¨ãã¦2014å¹´ã«è¨ç«ããã¾ããã
2015å¹´ã«ãµã¼ãã¹éå§ãããã¨è¨¼ææ¸ã®çºè¡æ°ã¯ãããã伸ã³ãå ææ«ã®ãã¬ã¹ãªãªã¼ã¹ã§ã¯ç´¯è¨10åæã®ãµã¼ã証ææ¸ãçºè¡ãããã¨ãã¢ãã¦ã³ã¹ããã¾ãããLet's Encrypt Has Issued a Billion CertificatesããCTLogã®èª¿æ»ããã2020å¹´2ææ«ã®æç¹ã§ã¯æå¹ãªå ¨è¨¼ææ¸ã®38.4%ãLet's Encryptã®è¨¼ææ¸ã§ããã¨ã¿ããã¦ãã¾ããCertificate Validity Datesãã
ç¡æã®è¨¼ææ¸ãæä¾ãã¦ããããã®ã¯é常ã«å¬ããã®ã§ãããèªè¨¼å±ã®æ¥åãã·ã¹ãã ã®éç¨ã«ã¯å½ç¶å¤§ããªã³ã¹ããæããã¾ããç§ãæ£å¼å ¬éåã®ãã¼ã¿ãµã¼ãã¹ã®æããå人ãã¡ã¤ã³ã§Let's Encryptã®è¨¼ææ¸ãå©ç¨ãå§ãã¾ããããã¯ããã¦å¯ä»éã ããé ¼ããéå¶å©ã§ç¡æã®ã¾ã¾ãã®ãããªãµã¼ãã¹ãé·æã«ç¶ç¶ãã¦æä¾ã§ããã®ã ãããï¼
æ£ç´è¨ã£ã¦ãã®ããã¸ã§ã¯ããåãã¦èããæ Let's Encrypt ã®å è¡ãã«å°ãä¸å®ãæã£ã¦ãã¾ããããããã¯å°æ¥ãææã®EV証ææ¸ã¨ãããã£ã¨å£²ãå§ããã ããã¨ãäºæ³ãã¦ãã¾ããã
ãããããã4å¹´åãçµã¡ã¾ãããLet's Encryptã主ä½ã¨ãªã£ã¦ãã¡ã¤ã³èªè¨¼ã¨è¨¼ææ¸çºè¡ã®èªååãè¡ãACME(Automatic Certificate Management Environment)ãããã³ã«ã®ä»æ§åãå®äºããç¾å¨ã¯ACME v2ãéç¨ä¸ã§ãã ããã«ãã£ã¦DV証ææ¸éå®ã«ãªãã¾ãããå¾æ¥ã®èªè¨¼å±ãç©æ¥µçã«é²ãããã¨ããªãã£ã証ææ¸çºè¡ã·ã¹ãã ã®å®å ¨èªååã«æåããç¾å¨ããã13åã®ãã«ã¿ã¤ã ã¹ã¿ããã¨å¹´éUSD3.35M(ç´3å5åä¸å)ã®äºç®ã§Let's Encryptã®ã·ã¹ãã ãéç¨ããã大è¦æ¨¡ãªè¨¼ææ¸çºè¡ãµã¼ãã¹ãå®ç¾ã§ãã¦ãã¾ããæ¬å½ã«é©ãã§ãã
æ¥æ¬ä¼æ¥ããã¯ãæé¨å ããã»ãããã¤ã³ã¿ã¼ããããããªã©ãLet's Encryptã®ã¹ãã³ãµã¼ã¨ãã¦è²¢ç®ããã¦ãã¾ãããããã³ãæè¬ããããã¾ããã
Let's Encryptã¯ééããªãä¸çã®Webãµã¼ãã¹ã®HTTPSåã大ããé²ãããã®æãã¾ãã
ããããããã£ãé«ãè²¢ç®ã«å¯¾ããè©ä¾¡ã®åé¢ãLet's Encryptã«ãã£ã¦ç¡æ証ææ¸ã使ã£ãHTTPSã®ãã£ãã·ã³ã°ãµã¤ããå¤§å¹ ã«å¢å ãã¦ããã¨ãã£ãè² ã®å´é¢ãææããã¦ãã¾ãã
ããã¯Let's Encryptã ããæªãã¨ã¯æãã¾ãããçµå±Web PKIããããæ´å²ççµç·¯ã®ä¸ã§çã¾ããæ§ã ãªæªãLet's Encryptã«ãã£ã¦ä»ãã¶ãåºããããã®ã ã¨ç§ã¯æã£ã¦ãã¾ãã
2. Let's Encryptã®ã¤ã³ã·ãã³ã
ãããªä»ã§ã¯ä¸ç1ä½ã®ã·ã§ã¢ãæã¤Let's Encryptã§ãããå æ¥è¨¼ææ¸çºè¡ã«é¢ããã¤ã³ã·ãã³ãçºçã®ã¢ãã¦ã³ã¹ãCommunity Supportã«æ稿ããã¾ããã2020.02.29 CAA Rechecking Bugãã Mozillaã«ã¯ãLet's Encrypt: CAA Rechecking bugãã®ãã±ããã§å ±åããã¦ãã¾ãã
ãã®å ±åã«ããã¨ãLet's Encryptãgithubä¸ã§éçºãç¶ãã¦ããèªè¨¼ã·ã¹ãã boulder ã§ãã°ãè¦ã¤ãããä¸é¨ã®è¨¼ææ¸ã§ãã¡ã¤ã³ã®CAA(Certification Authority Authorization)ãåãã§ãã¯ããã«çºè¡ãã¦ãã¾ã£ãã¨ã®ãã¨ã
証ææ¸ã®çºè¡ã®éã«è¨è¼ãã¡ã¤ã³ãã¹ã¦ã®CAAã¬ã³ã¼ãããã§ãã¯ãããã¨ã¯ãCA/Browser Forum ã®BR(Baseline Requirements)ãåºã¨ããLet's Encryptã®CP(Certificate Policy)ã«è¦å®ããã¦ããã証ææ¸çºè¡æã®å¿ é è¦ä»¶ã§ãã
ãã®è¦å®è¦ä»¶ã«åãã¦è¨¼ææ¸çºè¡ãè¡ãããå ´åã以ä¸ã®CPè¦å®ã«å¾ãï¼æ¥ä»¥å ã«å¯¾è±¡ã®è¨¼ææ¸ã失å¹ãããªããã°ãªãã¾ããã
4.9.1.1 Reasons for revoking a subscriber certificate
The CA SHOULD revoke a certificate within 24 hours and MUST revoke a Certificate within 5 days if one or more of the following occurs:
7. The CA is made aware that the Certificate was not issued in accordance with these Requirements or the CA's Certificate Policy or Certification Practice Statement;4.9.1.1 å å ¥è ã®è¨¼ææ¸ãåãæ¶ãçç±
CAã¯æ¬¡ã®ã¤ã³ã·ãã³ãã1ã¤ä»¥ä¸ãçºçããå ´åã24æé以å ã«è¨¼ææ¸ãåãæ¶ãã¹ãã§ããï¼SHOULDï¼ã5æ¥ä»¥å ã«è¨¼ææ¸ãåãæ¶ããªããã°ãªãã¾ãã(MUST)ã
7. 証ææ¸ãããã«è¨è¼ããã¦ããè¦ä»¶ã¾ãã¯CAã®è¨¼ææ¸ããªã·ã¼ã»èªè¨¼å®æ½è¦å®ã«å¾ã£ã¦çºè¡ããã¦ããªããã¨ãCAãèªèããæ
証ææ¸ã®å¤±å¹æ¹éã¨ã¦ã¼ã¶ã¸è¨¼ææ¸ã®åçºè¡ãã¡ã¼ã«ã§è¦è«ãããã¨ã«ã¤ãã¦ããRevoking certain certificates on March 4ã ã®ã¢ãã¦ã³ã¹ãç´ã¡ã«æ稿ããã¾ããã
対象ã¨ãªã£ãã®ã¯ããã305ä¸ã®è¨¼ææ¸ãLet's Encryptããçºè¡æ¸ã¿ã§æå¹ãªè¨¼ææ¸ã®ããã2.8%ã«ãããå¤ãã§ãããããã¤ã³ã·ãã³ãçºè¦ãã5æ¥ä»¥å ã2020å¹´3æ5æ¥3:00(UTC)ã¾ã§ã«å ¨é¨å¤±å¹ãããªããã°ãªãã¾ããã
Let's Encryptããã®è¦è«ãåãããã®æéæ¥ã¾ã§ã«ã¦ã¼ã¶ã«ãã£ã¦170ä¸ä»¥ä¸ã®è¨¼ææ¸ãåçºè¡ããã¾ããã
ãããã¾ã 130ä¸è¨¼ææ¸è¨¼ææ¸ãæªæ´æ°ã®ã¾ã¾ã§ããã®åæ°ä»¥ä¸(ç´65%)ãç¾å¨å©ç¨ä¸ã§ãããã¨ããããã¾ããããã®ã¾ã¾ç¨¼åä¸ã®è¨¼ææ¸ãå¼·å¶çã«å¤±å¹ãããã¨ãå¤æ°ã®Webãµã¼ãã¹ã«é大ãªå½±é¿ãä¸ãããã¨ãäºæ³ããã¾ãã
çµå±ãLet's Encryptã¯ãã®å½±é¿åº¦ãèæ ®ããCPè¦å®ã®5æ¥ä»¥å ã«æªæ´æ°ã®è¨¼ææ¸ã失å¹ããããã¨ãæ¢ããæ®ã83æ¥ã®Expireãå¾ ã¤æ¹éã¨ãã¾ãããå¼ãç¶ã証ææ¸ã¢ãã¿ã¼ã¨é£çµ¡ãç¶ç¶ãä»å¾ãã®æ§ãªå¤§è¦æ¨¡ã¤ã³ã·ãã³ãã«å¯¾å¿ã§ããããã失å¹éç¥ãããããã³ã«ã®éçºãé²ããã¨ãããã¨ã§ããLet's Encrypt: Incomplete revocation for CAA rechecking bugãã
ãã®æ¹éã«å¯¾ãã¦åãã©ã¦ã¶ã¼ãã³ãã¼ãä»å¾ã©ãåå¿ããã®ããæ°ã«ãªãã¨ããã§ãã
ä»åã®ã¤ã³ã·ãã³ãã¯ãGoè¨èªã§éçºããã¦ããboulderã®ãã°ã«ãããã®ã§ãããã®ãã°ã®è©³ç´°ã«ã¤ãã¦Incident Reportã§ç´°ããè¨åããã¦ãã¾ããã
ãã®ã¬ãã¼ããèªãã§ã¿ãã¨ãé©ãããã¨ã«ãã®ãã°ã¯ãGoã®Wikiãã¼ã¸ã§ãCommonMistakes/Using reference to loop iterator variableï¼ããããééã/iteratorå¤æ°ãloopããéã«åç §ã使ãå ´å)ãã§æ¸ããã¦ããééããç´æ¥ã®åå ã§ããã
Wikiã§ã¯ãã®ééããã
func main() { var out []*int for i := 0; i < 3; i++ { out = append(out, &i) } fmt.Println("Values:", *out[0], *out[1], *out[2]) fmt.Println("Addresses:", out[0], out[1], out[2]) }
ã®ãããªã³ã¼ãã§å®ä¾ã¨ãã¦æãã¦ãã¾ãã
ããã¯ãã«ã¼ãå ã§ã«ã¼ãå¤æ°iã®åç §ãé åã«å ¥ãã¦ãã¾ããã¨ã§ãã«ã¼ãçµäºå¾ã®åºåå¤ã
$ ./test_gomistake Values: 3 3 3 Addresses: 0xc0000160a0 0xc0000160a0 0xc0000160a0
ã®ããã«é åãå ¨ã¦åãå¤ã«ãªã£ã¦ãã¾ãåé¡ã§ãã
for ã range ãªã©ã§æ±ãã«ã¼ãå¤æ°ãåãåç §ã«ãªããã¨ãç¥ã£ã¦ããªãã¨ãã£ã¦ãã¾ããããªåå¿è çãªééãã§ãã
ãããå®éã«boulderã³ã¼ããèªãã¨ãLet's Encryptã®ã¨ã³ã¸ãã¢ã¯æ±ºãã¦ãã®ééããç¥ããªãã£ãããã§ã¯ãªããããå¤æ°ã§ã¯ãã¡ãã¨å¯¾å¿ãã¦ããã®ã«ä»ã®å¤æ°ã§ã¯ãã£ãããã®ééããè¦éãã¦ãã¾ã£ããã¨ãåå ã®ããã§ããã
ããã¯ã²ãã£ã¨ãããèªåããã¤ããã®ãããªãã°ãä»è¾¼ãã§ãã¾ããããã¨èçãå¯ããªãã¾ããã
ä»åã300ä¸ä»¥ä¸ã®è¨¼ææ¸ã失å¹ãããã»ã©ã®è¦å ã¨ãªã£ãCAAã¨ã¯ã©ããããã®ãªã®ãï¼ ãªãåãã§ãã¯ãå¿ è¦ãªã®ãï¼ ãã®Goã§ã¯ããããåé¡ã¨æ¸ããã¦ããç¨ã®ãã°ã¯ããªãã©ã®ããã«çºçããã®ãï¼ ã«ã¤ãã¦ããã®ããã°ã§ã¾ã¨ãã¦ã¿ããã¨æãã¾ãã
3. CAAã¨ã¯ãªã«ãï¼
CAA(Certification Authority Authorization)ã¯ããã¡ã¤ã³ã®ç®¡çè ãDNSã¬ã³ã¼ãã«è¨¼ææ¸çºè¡ã許å¯ããèªè¨¼å±æ å ±ãè¨è¼ãã証ææ¸ã®ä¸æ£çºè¡ã誤çºè¡ãé²ãæè¡ã§ãã
èªè¨¼å±ã«ãã証ææ¸ã®èª¤çºè¡ãä¸æ£çºè¡ãåé¡ã¨ãªã£ã2013å¹´ã«RFC6844ã§CAAã®ä»æ§åãè¡ããã¾ããã2019å¹´11æã«æ¢ç´¢ã¢ã«ã´ãªãºã ã®ãã°ä¿®æ£ãããæ¹è¨çRFC8659ãçºè¡ããã¦ãã¾ãã
ãã©ã¦ã¶ãã³ããèªè¨¼å±ãåå ããCA/Browser Forumã®è¦ç´ã§2017å¹´9æããCAAããµãã¼ããããã¨ãå¿ é åããã¾ããããã®è¦å®ã«ããã証ææ¸çºè¡ããéã«ã¯å¿ ãCAAã¬ã³ã¼ãã®ãã§ãã¯ãå ¥ãã¾ãã
èªè¨¼å±ã¯ã証ææ¸çºè¡æã«è¨¼ææ¸ã® subjectAltName ãã£ã¼ã«ãã«è¨è¼ããã¦ããå ¨ã¦ã®ãã¡ã¤ã³ã«å¯¾ãã¦CAAã®ãã§ãã¯ãè¡ããªããã°ãªãã¾ããã
CAAã¬ã³ã¼ãã«è¨è¼ããã¦ãããã¡ã¤ã³ãèªç¤¾æå®ã®ãã®ã§ãããªã証ææ¸ãçºè¡ãããã¨ãã§ãã¾ãããããã§ãªããã°çºè¡ããã¨ã©ã¼ãè¿ãã¾ãã
ã ããã¨è¨ã£ã¦ç¦ã£ã¦ãã¡ã¤ã³ã«CAAã¬ã³ã¼ãã追å ããå¿
è¦ã¯ããã¾ãããCAAã¬ã³ã¼ãã®ç»é²èªä½ã¯ãªãã·ã§ã³æ±ãã§ããCAAã¬ã³ã¼ããå¼ããªãã£ãå ´åã§ã証ææ¸ã¯çºè¡ããã¾ãã ãCAAã¬ã³ã¼ããè¨å®ããã¦ããããåå¨ããªãã£ãå ´åã証ææ¸ã¯çºè¡ããã¾ããã
2020å¹´3æ9æ¥18:00æ´æ° DNSã¨ã©ã¼æã®CAAãã§ãã¯ã«ã¤ãã¦
Yasuhiro Morishita (@OrangeMorishita) | TwitterããããDNSã¨ã©ã¼æã®èª¬æãééã£ã¦ãããã¨ãææãã¦ããã ãã¾ããããææã®éãã§ãã®ã§è¨è¼å 容ãåãæ¶ãã¾ããã
ï¼ç¶ãï¼ã§ãã®ã§ãç´°ããã§ããå½è©²é¨åããCAAã¬ã³ã¼ããè¨å®ããã¦ããããåå¨ããªãã£ãå ´åã証ææ¸ã¯çºè¡ããã¾ãããã¨ãããæ¹ããããã®ã§ã¯ãªããã¨æãã¾ããã
— Yasuhiro Morishita (@OrangeMorishita) 2020å¹´3æ9æ¥
ãã®è¾ºãç§ãè¦å®ãèªã¿ééããã³ã¼ãã®ç¢ºèªãæ ã£ã¦ãã¾ããã詳細ã«ã¤ãã¦ã¯ä»¥ä¸ã®togetterãã確èªãã ããã ãæææ¬å½ã«æè¬ãããã¾ãã togetter.com
次ã®å³ã§ã¯ãæ®æ®µ example.com ãã¡ã¤ã³ãèªè¨¼å±ï¼(ca1.example.net)ãã証ææ¸çºè¡ãåãã¦ããå ´åãä¾ã«ãã¾ãã example.com ã®ãã¡ã¤ã³ç®¡çè ã¯ãDNSãµã¼ãã« ca1.example.net ã®CAAã¬ã³ã¼ããç»é²ããèªè¨¼å±ï¼ãã証ææ¸ã®çºè¡ãå¯è½ã§ãããã¨ã示ãã¦ããã¾ãã
æ»æè ã¯ãå¥ã®èªè¨¼å±ï¼ã«å¯¾ãã¦ãªãããã®ç©´ãã¤ã㦠www.example.com ã®è¨¼ææ¸ãçºè¡ãããã¨ãã¾ãããã®éèªè¨¼å±ï¼ã¯ã www.example.com ã®CAAã¬ã³ã¼ãããã§ãã¯ã«ããã¾ãã
CAAã¬ã³ã¼ãã§ã¯ www.example.com ã®è¨¼ææ¸çºè¡å¯è½ã§ããã®ã¯èªè¨¼å±ï¼ã ãã§ãããã¨ãè¨è¼ããã¦ãããããèªè¨¼å±ï¼ã§ã¯ www.example.com ã®è¨¼ææ¸çºè¡è¦æ±ãæå¦ãã¦ã¨ã©ã¼ã¨ãã¦è¿ãã¾ãã
ãã£ã¦æ»æè ã«ãã www.eample.com ã®ä¸æ£è¨¼ææ¸å ¥æã¯å¤±æã«çµããã¾ãã
CAAã®ã¬ã³ã¼ãã«è¨è¼ãããã¼ã¿ãã©ã¼ãããã¯ä»¥ä¸ã®ãããªé ç®ãå ¥ãã¾ãã ä¾ãã°å®éã®ãã¡ã¤ã³ã§ã¯ä»¥ä¸ã®ãããªCAAã¬ã³ã¼ããç»é²ããã¦ããã®ããããã¾ãã ãã®ãã¡ã¤ã³ã¯ãCyberTrustãDigiCertãGlobalSignã®èªè¨¼å±ï¼ç¤¾ãããã証ææ¸ãçºè¡ã§ããªãããæå®ããã¦ããã®ããããã¾ãã
SSL Labs ã®çµ±è¨ã§ã¯ã2020å¹´3æ3æ¥æç¹ã§7.1%ã®ãµã¤ããCAAã®è¨å®ããã¦ããã¨å ±åããã¦ãã¾ãããã¯ããªãã·ã§ã³æ±ããªã®ã§ãã¾ã ããã»ã©é«ãæ®åçã¨ã¯è¨ãã¾ããã
CAAã§è¨¼ææ¸ã®ä¸æ£çºè¡ã誤çºè¡ããã¹ã¦é²ããã¨ã¯ã§ãã¾ããããããéå®ãããã±ã¼ã¹ã§ããCAAã«ãã£ã¦å®ããã¨ã¯ã§ããªãã¨è¨ã£ã¦ããã§ãããã
ä¾ãã°ãèªè¨¼å±ã·ã¹ãã ã®CAAãã§ãã¯ãç¡å¹åããã¾ã§å®å ¨ã«ä¹ã£åããããã(DNSSECãå©ç¨ãã¦ããªãå ´åã«)DNSã¸ã®æ»æãªã©ãåããããããããªã±ã¼ã¹ã«å¯¾ãã¦ã¯CAAã¯ç¡åã§ãã
å®éã«CAAãè¨å®ãã¦ããã®ã«ãã¡ã¤ã³ã¬ã¸ã¹ãã©ã¸ã®æ»æã«ãã£ã¦è¨¼ææ¸ã®ä¸æ£çºè¡é²ããªãã£ãäºä¾ãããã¾ããã
Googleã®ãã¡ã¤ã³ã«ã¯èªç¤¾èªè¨¼å± pki.goog ã® CAA ãè¨å®ããã¦ãã¾ããã2017å¹´ã«Googleã®tg(ãã¼ã´)ãã¡ã¤ã³(google.tg)ãLet's Encryptããä¸æ£çºè¡ãçºè¦ãã¦ãã¾ãã https://crt.sh/?id=245397170 ãã®è¨¼ææ¸ã¯ç´ã¡ã«å¤±å¹ããã¾ããã
RFCã§ã¯ã¢ããªã±ã¼ã·ã§ã³ãCAAã¬ã³ã¼ããåç §ãã¦è¨¼ææ¸ãæ¤è¨¼ãããã¨ãç¦æ¢ãã¦ãã¾ãããã®ããCAAã¬ã³ã¼ããä»ä¸ãããã¨ã«ãã£ã¦ãµã¼ãã¹ãç´æ¥å½±é¿ãåããå¯è½æ§ã¯é常ã«å°ãªãã§ãã
ãµã¼ãã¹ã«ä¸ãããªã¹ã¯ãå°ãªããæ軽ã«è¨¼ææ¸ã®ä¸æ£çºè¡ã®å¯¾çãã§ãããã¨ãCAAã®ã¡ãªããã§ãããã ãã¡ã¤ã³ã«CNAMEãä»ä¸ããã¦ããå ´åãCAAã¬ã³ã¼ãã®æ¢ç´¢ãå°ãè¤éã«ãªãã¾ãã®ã§æ³¨æãã¾ãããã
4. Let's Encryptã®è¨¼ææ¸çºè¡ãæ¯ããboulder
boulder ã®ã¢ã¼ããã¯ãã£ãä¸å³ã«ç¤ºãã¾ããä»¥ä¸ github repoã®documentããå¼ç¨ããå³ãä»ãã¦ãã¾ãã boulderã®ã·ã¹ãã è¦ç´ ãå ¨ã¦è§£èª¬ããããã«ããã¾ããã®ã§ãä»åé¢é£ããé¨åã ããæ¸ãã¾ããåã·ã¹ãã è¦ç´ (Authority)㯠protocol buffer v2 ã使ã£ã gRPC ã§é£æºãã¦åä½ãã¦ãã¾ãã
ACME v2 ã§ã¯ãããã以ä¸ã®ã¹ãããã§è¨¼ææ¸çºè¡ãè¡ãã¾ãã
- Subscriber(ã¦ã¼ã¶ or ã¯ã©ã¤ã¢ã³ã)ã¯ãletsencrypt(æ§certbot)ã³ãã³ãçãéã㦠ACME v2ãããã³ã«ã使ã£ã¦WFE(Web Front End)ãµã¼ãã¨éä¿¡ãã¾ãã
- ã¦ã¼ã¶èªè¨¼ç³»ãæ´ã£ã¦ããã°ã¯ã©ã¤ã¢ã³ãã¯ã証ææ¸çºè¡ã®ããã® Order ãWFEçµç±ã§RA(Registration Authority)ã«éãã¾ãã
- http-01ã¨ãdns-01çã®æå®ããã Challenge å½¢å¼ã®æé ã«å¾ããRAçµç±ã§VA(Validation Authority)ãã¦ã¼ã¶ã¸ã®ãµã¼ãã«ã¢ã¯ã»ã¹ãã¦ãã¡ã¤ã³èªè¨¼(Challenge Response)ãè¡ãã¾ãã
ãã®æVAã¯ãçºè¡ãã¡ã¤ã³ã®CAAã¬ã³ã¼ãããã§ãã¯ãã¦è¨¼ææ¸çºè¡ã許å¯ããã¦ãããã©ããã確èªãã¾ãã ããã§ä¸åº¦èªè¨¼ããããã¡ã¤ã³èªè¨¼æ å ±ã¯ãLet's Encryptã®å ´å30æ¥éæå¹ã§ããFAQ Technical Questionã«ã¯ä»¥ä¸ã®éãè¨è¼ããã¦ãã¾ããOnce you successfully complete the challenges for a domain, the resulting authorization is cached for your account to use again later. Cached authorizations last for 30 days from the time of validation. If the certificate you requested has all of the necessary authorizations cached then validation will not happen again until the relevant cached authorizations expire
ãã¡ã¤ã³ã«å¯¾ãããã£ã¬ã³ã¸ãæ£å¸¸ã«å®äºããã¨ãèªè¨¼ãããçµæãã¢ã«ã¦ã³ãã«å¯¾ãã¦ãã£ãã·ã¥ãããå¾ã§åã³ä½¿ç¨ã§ããããã«ãªãã¾ãã èªè¨¼ã®ãã£ãã·ã¥ã¯æ¤è¨¼æãã30æ¥éç¶ç¶ãã¾ãã çºè¡è¦æ±ãã証ææ¸ãå¿ è¦ãªèªè¨¼ãå ¨ã¦ãã£ãã·ã¥ããã¦ããå ´åã«ã¯ããã®èªè¨¼ãæéåãã«ãªãã¾ã§validationã¯å度è¡ããã¾ããã - ã¯ã©ã¤ã¢ã³ãã¯WFEã¨RAãéãã¦CA(Certificate Authority)ã«è¨¼ææ¸ã®çºè¡è¦æ±(Order Final)ãè¡ãã¾ãããã®éãã¡ã¤ã³èªè¨¼ãã8æé以ä¸çµã£ã¦ããã°RAã¯å度CAAã®ãã§ãã¯ãè¡ãã¾ãã ãªã8æé以ä¸çµã£ããRAã«ããCAAã®åãã§ãã¯ãå¿ è¦ãªã®ãï¼ å®ã¯CPã®ä¸è¨è¦å®ã«ãããCAAãã§ãã¯çµæã¯æ大ã§ã8æéããæå¹ã¨ã¿ãªããªãè¦å®ãããããã§ãã
3.2.2.8. CAA Records If the CA issues, they must do so within the TTL of the CAA record, or 8 hours, whichever is greater.
CAãçºè¡ããéã¯ãCAAã¬ã³ã¼ãã®TTLã¾ãã¯8æéã®ãããã大ããæ¹ã®æéå ã§è¨¼ææ¸ãçºè¡ããªããã°ãªããªãã
ãã¡ã¤ã³èªè¨¼ã¯30æ¥éæå¹ã§ãã®ã§ããã¡ã¤ã³èªè¨¼ãã8æéå¾ãéããã¨ãã®æã®CAAãã§ãã¯çµæã¯ç¡å¹ã«ãªãã¾ãããã¡ã¤ã³èªè¨¼ãã8æéå¾ãã¤30æ¥ä»¥å ã§è¨¼ææ¸çºè¡ãè¦æ±ãããã¨ããã¡ã¤ã³èªè¨¼ã§ã®CAAãã§ãã¯ã¯ã¹ããããããã®ã§ãRAã¯å度CAAããã§ãã¯ããªãã¨ãããªãããã§ãã
5. boulderã®ãã°
ä»åãã°ã¯ãå½åã¦ã¼ã¶ããã¨ã©ã¼ã99åã®åä¸ã®ã¡ãã»ã¼ã¸ãåºãã¦ããã¨ã®ã¬ãã¼ãã«ãã£ã¦çºè¦ãã¾ãããRechecking caa fails with 99 identical subproblemsãã100ã®ãã¡ã¤ã³ãå«ãã 証ææ¸çºè¡ãè¦æ±ããéã«ãåä¸ãã¡ã¤ã³ã®CAAã®recheckã¨ã©ã¼ã99åå«ã¾ããã¡ãã»ã¼ã¸ãè¿ã£ã¦ããã®ã§ãã
ãã®issueã«åå¿ããå¥ã®ã³ãã¥ããã£ã¦ã¼ã¶ãç¾è±¡ã確èªããLet's Encryptã¹ã¿ããã«å¯¾ãã¦ä»¥ä¸ã®åãããã¾ãã
any confirmation? (Iâm wary that this might actually be possible to apply as a CAA re-checking bypass ⦠maybe I should delete and send to security@ â¦)
確èªã§ãã¾ãï¼ (ãããå®éã«CAAã®åãã§ãã¯ããã¤ãã¹ã§ãããããããªãã¨å¿é ãã¦ãã¾ã⦠ãããããããåé¤ã㦠security@ ã«é£çµ¡ãã¹ãããããã¾ãããâ¦)
ãªãã¨è¡æãªã¦ã¼ã¶ã§ããããã§ããæ®å¿µãªãã¨ã«Let's Encryptã®ã¹ã¿ããã¯å½åãããã¨ã©ã¼è¡¨ç¤ºã®åé¡ã¨æãã¦ãã¾ããããããæ°æ¥å¾ããããä½ã£ã¦ç¢ºèªãã¦ããéã«ééãã§ãããã¨ã«æ°ã¥ãã¾ããã
åé¡ã¯RAãOrder Finalã®éã«CAAã®åãã§ãã¯ãè¡ãã¨ããã§ããããã§ã¯RAã¨SA(Storage Authority)ã®éã®gRPCã§ä»¥ä¸ã®ããåããããã¦ãã¾ããã
- RAã¯ãã¯ã©ã¤ã¢ã³ãããã®è¨¼ææ¸çºè¡è¦æ±(Order Final)ãåãèªè¨¼æ å ±ã確èªãã¾ã(checkAUthorizations)ããã®éRAã¯ãCSRã®subjectAltNameã«è¨è¼ããã¦ããåãã¡ã¤ã³ã®èªè¨¼æ å ±ãSAã«å¯¾ãã¦gRPCãéãã¦å ¥æãã¾ã(GetValidAuthorizations2)ã
- SAã¯ãèªèº«ã管çãã¦ããèªè¨¼æ å ±(AuthzModel)ãProtocol Buffer v2å½¢å¼ã«ãã¦ããã¡ã¤ã³ã¨èªè¨¼æ å ±ã®Mapãé åã«å ¥ãRAã«è¿ãã¾ã(authzModelMapToPB, modelToAuthzPB)ã
- RAã¯ããã®èªè¨¼æ å ±ã«è¨è¼ããã¦ãããã¡ã¤ã³æ å ±ãè¦ã¦CAAã¬ã³ã¼ãããã§ãã¯ãã¾ã(checkAuthorizationCAA/recheckCAA)ã
ãã°ã«é¢é£ãã該å½é¨åã®ã³ã¼ã(authzModelMapToPB, modelToAuthzPB)ã示ãã¾ãã説æããããããããªããã便å®çã«è¡çªå·ããµã£ã¦ãã¾ããã¾ãä¸é¨èª¬æã«é¢ä¿ãªãé¨åã®ã³ã¼ããçç¥ãã¦ãã¾ãã
1.func modelToAuthzPB(am *authzModel) (*corepb.Authorization, error) { 2. expires := am.Expires.UTC().UnixNano() 3. id := fmt.Sprintf("%d", am.ID) 4. status := uintToStatus[am.Status] 5 pb := &corepb.Authorization{ 6. Id: &id, 7. Status: &status, 8. Identifier: &am.IdentifierValue, 9. RegistrationID: &am.RegistrationID, 10. Expires: &expires, 11. } 12. (snip) 13. return pb, nil 14.} 15. 16. // authzModelMapToPB converts a mapping of domain name to authzModels into a 17. // protobuf authorizations map 18. func authzModelMapToPB(m map[string]authzModel) (*sapb.Authorizations, error) { 19. resp := &sapb.Authorizations{} 20. for k, v := range m { 21. // Make a copy of k because it will be reassigned with each loop. 22. kCopy := k 23. authzPB, err := modelToAuthzPB(&v) 24. if err != nil { 25. return nil, err 26. } 27. resp.Authz = append(resp.Authz, &sapb.Authorizations_MapElement{Domain: &kCopy, Authz: authzPB}) 28. } 29. return resp, nil 30.}
ããã¾ãããããããééã/iteratorå¤æ°ãloopeããéã«åç §ã使ãå ´åãã§ãã20è¡ç®ãã28è¡ç®ã«æ¸¡ãfor loopã該å½ãã¾ãã
authzModelMapToPBã«æ¸¡ãããmap m ã® key k 㨠value v ãåç
§ãã¦ãã¾ãã
kã«é¢ãã¦ã¯ãã¡ããã¨ãã°ã«ãªããªããã kCopyã«ä»£å
¥ãã¦å¥ã®åç
§ã«æ¸¡ãã¦ãã¾ãã
vã«é¢ãã¦ã¯ãmodelToAuthzPBã«åç
§ã渡ãã¦ãã¾ããä¸è¦åé¡ããªãããã§ããã§ã渡ãããmodelToAuthzPBã«ããã¦Identifierã¨RegistrationIDã®ãã£ã¼ã«ãã«vã®åç
§ã渡ãã¦ãã¾ããããã¦ãã®è¿ãå¤ãé
åã«ä»£å
¥ãã¦ãã¾ãã
ãã®é¨åã ãåãåºãã¦åä½ããããã«ãã¦è©¦ãã¦ã¿ã¾ã(åä½ãå¤ãããªãç¨åº¦ã«è¥å¹²ã³ã¼ãã«å¤æ´ãããã¦ãã¾ã)ã
package main import ( "fmt" "time" apb "./proto" ) type authzModel struct { ID int64 IdentifierType uint8 IdentifierValue string RegistrationID int64 Status uint8 Expires time.Time } func modelToAuthzPB(am *authzModel) (*apb.Authorization, error) { expires := am.Expires.UTC().UnixNano() id := fmt.Sprintf("%d", am.ID) status := "valid" pb := &apb.Authorization{ Id: &id, Status: &status, Identifier: &am.IdentifierValue, RegistrationID: &am.RegistrationID, Expires: &expires, } // snip return pb, nil } func authzModelMapToPB(m map[string]authzModel) (*apb.Authorizations, error) { resp := &apb.Authorizations{} for k, v := range m { // Make a copy of k because it will be reassigned with each loop. kCopy := k authzPB, err := modelToAuthzPB(&v) if err != nil { return nil, err } resp.Authz = append(resp.Authz, &apb.Authorizations_MapElement{Domain: &kCopy, Authz: authzPB}) } return resp, nil } func main() { authzModels := [...]authzModel{ authzModel{1,1,"www.example1.com",1,1, time.Date(2020, time.January, 1, 1, 1, 1, 1, time.UTC)}, authzModel{2,2,"www.example2.com",2,2, time.Date(2020, time.February, 2, 2, 2, 2, 2, time.UTC)}, authzModel{3,3,"www.example3.com",3,3, time.Date(2020, time.March, 3, 3, 3, 3, 3, time.UTC)}, } authzModelMap := make(map[string]authzModel) for _, am := range authzModels { authzModelMap[am.IdentifierValue] = am } resp, _ := authzModelMapToPB(authzModelMap) fmt.Printf("%+v, Identifier:%p, RegistrationID:%p\n", resp.Authz[0], resp.Authz[0].Authz.Identifier, resp.Authz[0].Authz.RegistrationID) fmt.Printf("%+v, Identifier:%p, RegistrationID:%p\n", resp.Authz[1], resp.Authz[1].Authz.Identifier, resp.Authz[1].Authz.RegistrationID) fmt.Printf("%+v, Identifier:%p, RegistrationID:%p\n", resp.Authz[2], resp.Authz[2].Authz.Identifier, resp.Authz[2].Authz.RegistrationID) }
ããã§ã¯ããã¹ãç¨ã®mapãã¼ã¿www.example[1-3].comã®ï¼ã¤ã®ãã¡ã¤ã³ã渡ãããå ´åã模æ¬ãã¦ãã¾ãã
å®è¡ãã¦ã¿ã¾ãã
$ ./le_bug domain:"www.example1.com" authz:<id:"1" identifier:"www.example3.com" registrationID:3 status:"valid" expires:1577840461000000001 > , Identifier:0xc0000c8060, RegistrationID:0xc0000c8070 domain:"www.example2.com" authz:<id:"2" identifier:"www.example3.com" registrationID:3 status:"valid" expires:1580608922000000002 > , Identifier:0xc0000c8060, RegistrationID:0xc0000c8070 domain:"www.example3.com" authz:<id:"3" identifier:"www.example3.com" registrationID:3 status:"valid" expires:1583204583000000003 > , Identifier:0xc0000c8060, RegistrationID:0xc0000c8070
ãããidentifier 㨠registrationIDã¯åãåç §ã«ãªã£ã¦ããããåãå¤(www.example3.com, 3)ã«ãªã£ã¦ãã¾ããrecheckCAAã¯registrationIDãåç §ãããããããã§ã¯www.example3.comã®1ãã¡ã¤ã³ããCAAã®åãã§ãã¯ãè¡ãã¾ããããã°ãåç¾ã§ãã¾ããã
ãã®ãã°ã¯ã次ã®PR(Pass authzModel by value, not reference)ã§ä¿®æ£ããã¾ãããåç´ã«åç §æ¸¡ããå¤æ¸¡ãã«å¤ããã ãã§ããããã§ãåãä¿®æ£ããã¦ã¿ã¾ãã
diff --git a/main.go b/main.go index e4fa2a1..828401d 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,7 @@ type authzModel struct { Expires time.Time } -func modelToAuthzPB(am *authzModel) (*apb.Authorization, error) { +func modelToAuthzPB(am authzModel) (*apb.Authorization, error) { expires := am.Expires.UTC().UnixNano() id := fmt.Sprintf("%d", am.ID) status := "valid" @@ -36,7 +36,7 @@ func authzModelMapToPB(m map[string]authzModel) (*apb.Authorizations, error) { for k, v := range m { // Make a copy of k because it will be reassigned with each loop. kCopy := k - authzPB, err := modelToAuthzPB(&v) + authzPB, err := modelToAuthzPB(v) if err != nil { return nil, err }
試ãã¦ã¿ã¾ãããã
$ ./le_bug domain:"www.example1.com" authz:<id:"1" identifier:"www.example1.com" registrationID:1 status:"valid" expires:1577840461000000001 > , Identifier:0xc0000b8060, RegistrationID:0xc0000b8070 domain:"www.example2.com" authz:<id:"2" identifier:"www.example2.com" registrationID:2 status:"valid" expires:1580608922000000002 > , Identifier:0xc0000b8100, RegistrationID:0xc0000b8110 domain:"www.example3.com" authz:<id:"3" identifier:"www.example3.com" registrationID:3 status:"valid" expires:1583204583000000003 > , Identifier:0xc0000b81a0, RegistrationID:0xc0000b81b0
ç¡äºãããããã®ãã¡ã¤ã³ã«å¿ããå¤ã«ãªã£ã¦ãã¾ãã
22è¡ç®ã®kCopyå¤æ°ã®å¦çã³ã¡ã³ããè¦ãã¨ãiteratorå¤æ°ãloopããéã«åç §ã使ãåé¡ã«ã¤ãã¦ã¡ããã¨æèãã¦ã³ã¼ããæ¸ãã¦ãããã¨ããããã¾ããæ¬å½ã«æããã
ã¬ãã¼ãã§ã¯ kCopy ã¯åé¡ãåé¿ãã¦ããã®ã« v ã«ã¤ãã¦è¦éããã®ã¯ãprotocol buffer ver2 ã«ããããã£ã¼ã«ãå¤ã®ä»£å ¥ãå ¨ã¦åç §æ¸¡ãã«ãªã£ã¦ãããã¨ãä¸å ã«ããã¨åæãã¦ãã¾ãããã®ããã¤ãåç §æ¸¡ãã«ãã¦ãã¾ã£ãã®ã§ãããã
åçºé²æ¢çã¨ãã¦ããã¹ãããã°ã®å å®ãéç解æãã¬ãã¥ã¼ã®å®æ½ãprotocol buffer ver3ã®ã¢ããã°ã¬ã¼ããªã©ãæãããã¦ãã¾ãã
ãããã¨ãããã¡ããã¨ç解ãã¦åé¿ããã¤ããããã³ãã¡ãã£ã¨ã®æãè¾¼ã¿ã§ä»ã®æå½ãå¿ãã¦ãã¾ããè¸ã«æããã¦ã¦è¦ã¦ãéå»ãããªãã¨ããã£ãè¦ããããã¾ãããããããã絶対ã«èªåã«èµ·ãããªãã¨ã¯è¨ãã¾ãããæããã¨ã§ãã
ä»åã®ã¤ã³ã·ãã³ãã¯å¯¾å²¸ã®ç«äºã¨ã¯ã¨ã¦ãæãã¾ãããèªåãããããã¤ã³ã·ãã³ããå°æ¥èµ·ãããªãããæ¬å½ã«æ°ãã¤ãããã¨ã¬ãã¼ããèªãã§ãã¿ãã¿æãã®ã§ããã