ããã«ã¡ã¯ãX ã€ãããŒã·ã§ã³ æ¬éšã®æŽç°ã§ãã ãã®ãã¹ã㯠é»éåœéæ
å ±ãµãŒãã¹ Advent Calendar 2021 ã®5æ¥ç®ã®ãã¹ãã§ãã 4æ¥ç®ã®ãã¹ãã¯å çŽããã®ããªã¢ã«ã¿ã€ã ã¬ã³ãã©ãŒP3Dã®ã玹ä»ãã§ããã ããŠããã®ãã¹ãã§ã¯Open Policy Agentãšããªã·ãŒèšèªRegoã®ç޹ä»ãããããšæããŸãã ååã§ã¯Regoã®ææ³ãç°¡åã«èª¬æããŸãã åŸåã§ã¯ç§ãOpen Policy AgentãšRegoãå®éã«äœ¿ã£ãŠããŠããã£ãç¹ãããã€ãã玹ä»ããŸãã ãã®ãã¹ããèªãã§ãã ããæ¹ã®åœ¹ã«ç«ãŠã°å¹žãã§ãã Open Policy Agentãšã¯ Regoãšã¯ ãŸãã¯åãããŠã¿ã èšå æ§é åããŒã¿ïŒinput.jsonïŒ ããªã·ãŒïŒexample.regoïŒ æ€èšŒ Regoã®ææ³ æ¬ç« ã§æ±ãæ§é åããŒã¿ 倿° 倿°ã®æçž é
åãéåããªããžã§ã¯ããžã®ã¢ã¯ã»ã¹ ã«ãŒã« éåãçæãã ãªããžã§ã¯ãïŒèŸæžïŒãçæãã å€ãçæãã è€æ°ã®å®çŸ©ãæžã 颿° å
å
è¡šèš ãªã¹ãå
å
è¡šèš éåå
å
è¡šèš ãªããžã§ã¯ãå
å
è¡šèš ããŒã¯ãŒããšæŒç®å not some default in with ã¢ãžã¥ãŒã«ãããã±ãŒãž ãã¹ããæžã OPA/Regoãå©çšããããŒã« Conftest Gatekeeper Kubernetesã«ãããConftestãšGatekeeperã®äœ¿ãåã ãã以å€ã®ããŒã« ããã£ããšãã ççŸããã«ãŒã«ãå®çŸ©ããªã é
åãéåãããæ¡ä»¶ãæºããå€ãå«ãã§ããªããæ€èšŒããã«ãŒã«ã®æžãæ¹ ãªããžã§ã¯ãã®ãã£ãŒã«ããç¹å®ã®å€ã§ãããæ€èšŒããã«ãŒã«ã®æžãæ¹ ç°åžžç³»ãã¹ãã®ã¢ãµãŒã·ã§ã³ã®æžãæ¹ ãªããžã§ã¯ãã®ååšããªãããŒãåç
§ããéã®æå ããªã·ãŒã®ãããã°æ¹æ³ = ãš := ãš == ã®éã ãã©ãŒããã¿ã䜿ã ãããã« åè Open Policy Agentãšã¯ Open Policy AgentïŒOPAïŒã¯æ±çšããªã·ãŒãšã³ãžã³ã§ãã äžããããæ§é åããŒã¿ãRegoãšåŒã°ããããªã·ãŒèšèªã§èšè¿°ãããããªã·ãŒãæºãããŠãããå€å®ããŸãã OPAã䜿ã£ãŠãµãŒãã¹æ¬äœãšããªã·ãŒãšã³ãžã³ã ççµå ã«ããããšã§ããªã·ãŒã®æŽæ°ããããã€ãããŒãžã§ã³ç®¡çãªã©ããµãŒãã¹æ¬äœããåé¢ã§ããŸãã â»å³ã¯ Open Policy Agent | Documentation ããåŒçšããŠããŸãã OPAã«ã¯æ§ã
㪠ãŠãŒã¹ã±ãŒã¹ ããããŸããããã€ãäŸãæããŸãã Infrastructure as Code ïŒ Infrastructure as Codeã§ã¯ã€ã³ãã©ã®çæ³çãªç¶æ
ã yaml ãHCL2ãªã©ã®æ§é åããŒã¿ãšããŠå®£èšçã«èšè¿°ããŸãã ãããŠãããTerraformãªã©ã®ããŒã«ãžæž¡ãããšã§å®£èšã«åŸã£ãŠå®éã®ã€ã³ãã©ãæ§ç¯ããŸãã OPAã䜿ã£ãŠ yaml ãHCL2ãæºããã¹ãã«ãŒã«ïŒïŒã€ã³ãã©ã®èšå®ïŒãããªã·ãŒãšããŠå®çŸ©ããããšã§Infrastructure as Codeã®å質ãæ
ä¿ã§ããŸãã ããã¯ç¹ã«è€æ°ã®ããŒã ãç¬èªã«ãµãŒãã¹ãæ§ç¯ã»éçšããŠããå Žåã«ããŒã 暪æã§å質ãåäžããæ¹æ³ãšããŠæå¹ã§ãã ãã°åæ ïŒ OPAã䜿ã£ãŠã»ãã¥ãªãã£ãç£æ»ã®æ§é åãã°ãåæããæå®ããããªã·ãŒãæºãããªãç°åžžãªãã°ãåºåãããŠããªãããã§ãã¯ã§ããŸãã ããšãã°ã¡ã«ã«ãªã§ã¯ GCP ã®ã»ãã¥ãªãã£ãã°ãOPAã䜿ã£ãŠåæããŠããŸãã åèïŒ Achieving Security Compliance Monitoring with Open Policy Agent and Rego - Speaker Deck ããŒã¿ãã€ãã©ã€ã³äžã®ããªããŒã·ã§ã³ ïŒ OPAã䜿ã£ãŠããŒã¿ãã€ãã©ã€ã³äžãæµããæ§é åããŒã¿ãæå®ããããªã·ãŒãæºãããŠããããã§ãã¯ã§ããŸãã ããšãã° æ¥æ¬çµæžæ°è瀟 ã§ã¯ããŒã¿ãã€ãã©ã€ã³äžã®ã ã©ããã³ã° ããŒã¿ãæ£åžžã«å å·¥ãããŠãããOPAã§ãã§ãã¯ããŠããŸãã åèïŒ ãéå¬å ±åãæ°è瀟ã«ãã AWS ãæŽ»çšãã DX äºäŸã»ãã㌠| Amazon Web Services ããã° OPAã®ãœãŒã¹ã³ãŒã 㯠Apache License 2.0 ã§å
¬éãããŠããŸãã Regoãšã¯ Regoã¯OPAã䜿çšãã宣èšåã¯ãšãªèšèªã§ãã DatalogïŒ Prolog ã®ãµãã»ããã§ãã宣èšåè«ç ããã°ã©ãã³ã°èšèª ïŒã«ã€ã³ã¹ãã€ã¢ãããŠããŸãã ãŸãã¯åãããŠã¿ã å
¬åŒã® Example ã«æ²èŒãããŠããäŸé¡ãæå
ã§å®è¡ããŠã¿ãŸãã èšå äžããããæ§é åããŒã¿ã以äžã®ããªã·ãŒãæºãããŠãããæ€èšŒããŸãã ã€ã³ã¿ãŒãããããå°éå¯èœãªãµãŒãã®ãã¡ãå®å
šã§ãªã http ãããã³ã« ã䜿çšããŠãããã®ã¯ãªããã telnet ãããã³ã« ã䜿çšããŠãããµãŒãã¯ãªããã æ§é åããŒã¿ïŒ input.json ïŒ { " servers ": [ { " id ": " app ", " protocols ": [ " https ", " ssh " ] , " ports ": [ " p1 ", " p2 ", " p3 " ]} , { " id ": " db ", " protocols ": [ " mysql " ] , " ports ": [ " p3 " ]} , { " id ": " cache ", " protocols ": [ " memcache " ] , " ports ": [ " p3 " ]} , { " id ": " ci ", " protocols ": [ " http " ] , " ports ": [ " p1 ", " p2 " ]} , { " id ": " busybox ", " protocols ": [ " telnet " ] , " ports ": [ " p1 " ]} ] , " networks ": [ { " id ": " net1 ", " public ": " false " } , { " id ": " net2 ", " public ": " false " } , { " id ": " net3 ", " public ": " true " } , { " id ": " net4 ", " public ": " true " } ] , " ports ": [ { " id ": " p1 ", " network ": " net1 " } , { " id ": " p2 ", " network ": " net3 " } , { " id ": " p3 ", " network ": " net2 " } ] } ããªã·ãŒïŒ example.rego ïŒ äžè¿°ã®èšåãæ€èšŒããããã«Regoã§èšè¿°ãããããªã·ãŒã䜿çšããŸãã â»Regoã®ææ³ã¯åŸã»ã©èª¬æããŸããä»ã¯ã€ã¡ãŒãžã ãææ¡ããŠããã ããã°çµæ§ã§ãã package example # allow ã®ããã©ã«ãå€ã false ã«èšå®ããŸã default allow = false # violation ã®èŠçŽ æ°ã 0 ã®å Žåã allow ã true ã«èšå®ããŸã allow = true { count(violation) == 0 } # çæããéå public_server ã®èŠçŽ server ã®ãã¡ protocols ã "http" ã§ããèŠçŽ ã® id ãéå violation ã®èŠçŽ ã«è¿œå ããŸã violation[server.id] { some server public_server[server] server.protocols[_] == "http" } # é
å input.servers ã®èŠçŽ server ã®ãã¡ protocols ã "telnet" ã§ããèŠçŽ ã® id ãéå violation ã®èŠçŽ ã«è¿œå ããŸã violation[server.id] { server := input.servers[_] server.protocols[_] == "telnet" } # é
å input.servers ã®èŠçŽ server ã®ãã¡ä»¥äžã®æ¡ä»¶ãæºããèŠçŽ ãéå public_server ã®èŠçŽ ã«è¿œå ããŸã # 1. é
å input.ports ã®ãã¡ server.ports ã®ãããããš id ãåèŽããèŠçŽ ã®æ·»åã i ãšããŸã # 2. é
å input.networks ã®ãã¡ input.ports[i].network ãš id ãåèŽããèŠçŽ ã®æ·»åã j ãšããŸã # 3. input.networks[j].public ã true ã§ããå Žåã server ãéå public_server ã®èŠçŽ ã«è¿œå ããŸã public_server[server] { some i, j server := input.servers[_] server.ports[_] == input.ports[i].id input.ports[i].network == input.networks[j].id input.networks[j].public } æ€èšŒ ããŒã«ã«ç°å¢ã§å®è¡ããå Žåã¯ä»¥äžãè¡ã£ãŠãã ããã open-policy-agent/opa ããææ°ã®ãªãªãŒã¹ãã€ããªãããŠã³ããŒãããŠãå®è¡ãã¹ã®éã£ã ãã£ã¬ã¯ã ãªã«é
眮ããŸãã opa eval ã³ãã³ãã§ããªã·ãŒãè©äŸ¡ããŸãã # èšåãæºãããŠãããæ€èšŒããŸããçµæã¯ "false" ïŒæºãããŠããªãïŒã§ããã $ opa eval -i input.json -d policy.rego --format pretty data.example.allow false # èšåã®ããªã·ãŒãæºãããŠããªããµãŒã㯠"ci","busybox" ã§ããããšã確èªã§ããŸãã $ opa eval -i input.json -d policy.rego --format pretty data.example.violation [ "ci", "busybox" ] ãŸã㯠The Rego Playground ãããå®è¡ã§ããŸãã Regoã®ææ³ Regoã®ææ³ãç°¡åã«èª¬æããŸãã æ¬ç« ã§æ±ãæ§é åããŒã¿ æ¬ç« ïŒ Regoã®ææ³ ïŒã§ã¯ä»¥äžã®æ§é åããŒã¿ããµã³ãã«ããŒã¿ãšããŠäœ¿çšããŸãã { " persons ": [ { " name ": " alice ", " age ": 20 , " height ": 170 , " weight ": 60 } , { " name ": " bob ", " age ": 22 , " height ": 180 , " weight ": 80 } , { " name ": " carol ", " age ": 17 , " height ": 175 , " weight ": 66 } , { " name ": " dave ", " age ": 18 , " height ": 155 , " weight ": 50 } ] } 倿° 倿°ã®æçž Regoã®å€æ°ã¯ãæå®ãããæ¡ä»¶ãæºããããæçžãããå€ãã§ãã äŸãã°ä»¥äžã®å Žåã倿° i 㯠é
å persons ã®ãã¡ã20æä»¥äžãã€èº«é·170cm以äžã®äººããæãä»»æã®æ·»å ã®å€ãã€ãŸã 0 ãš 2 ã«æçžãããŸãã some i persons[i].age <= 20 persons[i].height >= 170 æç¶ãå ããã°ã©ãã³ã°èšèª ã®ããã«å€ãåã³ä»£å
¥ããããšã¯ã§ããŸããã i := 1 i := 2 # i ã¯æ¢ã« 1 ã«æçžã«ãããŠãããããšã©ãŒã«ãªã ãŸãç¹æ®ãªå€æ°ãšã㊠_ ããããŸãã _ ã¯ç»å Žãã床ã«ä»ã®å€æ°ãšç«¶åããªãäžæãªå€æ°ãžå€æãããŸããåã³åç
§ããå¿
èŠã®ãªã倿°ã¯ _ ã䜿ãããšãæšå¥šãããŠããŸãã _ ã䜿ã£ãŠå
çšã®äŸãæžãæãããšä»¥äžã®ããã«ãªããŸãã person := persons[_] person.age <= 20 person.height >= 160 é
åãéåããªããžã§ã¯ããžã®ã¢ã¯ã»ã¹ Regoã«ã¯ä»¥äžã®ãããªåããããŸãã æ°å€ æåå ããŒã«å€ é
å éå ãªããžã§ã¯ãïŒèŸæžïŒ é
å arr ããèŠçŽ val ãåãã ãéã¯ä»¥äžã®ããã«æžããŸãã # 倿°ã䜿ã£ãæžãæ¹ãiã¯é
åã®æ·»åãvalã¯é
åã®å€ã«æçžãããã some i val := arr[i] # ãŸã㯠`_` ã䜿ã£ãæžãæ¹ã val := arr[_] éå a_set ããèŠçŽ val ãåãã ãéã¯ä»¥äžã®ããã«æžããŸãã # valã¯éåã®åèŠçŽ ã«æçžããã a_set[val] ãªããžã§ã¯ã obj ã®ã㌠key ã«ã¢ã¯ã»ã¹ããéã¯ä»¥äžã®ããã«æžããŸãã # `.` ã䜿ã£ãæžãæ¹ value := obj.key # ãŸã㯠`[]` ã䜿ã£ãæžãæ¹ value := obj["key"] ã«ãŒã« OPAã§ã¯ãæž¡ãããæ§é åããŒã¿ïŒããŒã¹ããã¥ã¡ã³ããšãèšããŸãïŒãå
ã«ãæ°ããæ§é åããŒã¿ïŒä»®æ³ããã¥ã¡ã³ããšãèšããŸãïŒãç®åºããŸãã ãã®ä»®æ³ããã¥ã¡ã³ãã®å®çŸ©ã ã«ãŒã« ãšèšããŸããOPAã§ã¯æ§ã
ãªã«ãŒã«ãå®çŸ©ããããšã§ããªã·ãŒãå®è£
ããŸãã éåãçæãã éåãçæããã«ãŒã«ã¯ä»¥äžã®ããã«èšè¿°ããŸãã <name>[<value>] { <body> } ãã®ãšãã«ãŒã« <name> ã¯ã <body> ã®åŒãå
šãŠçã«ãªããšãã® <value> ããèŠçŽ ãšããŠæã€éåã«ãªããŸãã <body> ã®ååŒã¯ è«çç© ïŒANDïŒã§çµåãããŸãã 以äžã¯ ã幎霢20æä»¥äžã身é·170cm以äžã®äººãã®ååã®éå ãçæããã«ãŒã«ã®äŸã§ãã young_and_tall_persons[person.name] { person := input.persons[_] person.age <= 20 person.height >= 170 } ãããGoã®ã³ãŒãã§æžããšä»¥äžã®ããã«ãªããŸãïŒâ»ãããŸã§ã€ã¡ãŒãžã§ãïŒã func youngAndTallPersons(input Input) [] string { result := [] string {} for _, person := range input.persons { if person.age <= 20 && person.height >= 170 { result = append (result, person.name) } } return result } ãªããžã§ã¯ãïŒèŸæžïŒãçæãã ãªããžã§ã¯ããçæããã«ãŒã«ã¯ä»¥äžã®ããã«èšè¿°ããŸãã <name>[<key>] = <value> { <body> } ãã®ãšãã«ãŒã« <name> ã¯ã <body> ã®åŒãå
šãŠçã«ãªããšãã® <key> ãš <value> ã®ãã¢ããèŠçŽ ãšããŠæã€ãªããžã§ã¯ãã«ãªããŸãã <body> ã®ååŒã¯ è«çç© ïŒANDïŒã§çµåãããŸãã 以äžã¯ ã幎霢20æä»¥äžã身é·170cm以äžã®äººãã«ã€ããŠãååãšäœéã®ãã¢ãèŠçŽ ãšããŠæã€ã®ãªããžã§ã¯ã ãçæããã«ãŒã«ã®äŸã§ãã young_and_tall_persons_weight[person.name] = person.weight { person := input.persons[_] person.age <= 20 person.height >= 170 } ãããGoã®ã³ãŒãã§æžããšä»¥äžã®ããã«ãªããŸãïŒâ»ãããŸã§ã€ã¡ãŒãžã§ãïŒã func youngAndTallPersonsWeight(input Input) map [ string ] int { result := map [ string ] int {} for _, person := range input.persons { if person.age <= 20 && person.height >= 170 { result[person.name] = person.weight } } return result } å€ãçæãã å€ãçæããã«ãŒã«ã¯ä»¥äžã®ããã«èšè¿°ããŸãã <name> = <value> { <body> } ãã®ãšãã«ãŒã« <name> ã¯ã <body> ã®åŒãå
šãŠçã«ãªããšãã® <value> ã®å€ãã«ãªããŸãã <body> ã®ååŒã¯ è«çç© ïŒANDïŒã§çµåãããŸãã 以äžã¯ãAliceã®èº«é·ããè¿ãã«ãŒã«ã®äŸã§ãã alice_height = person.height { person := input.persons[_] person.name == "alice" } ãããGoã®ã³ãŒãã§æžããšä»¥äžã®ããã«ãªããŸãïŒâ»ãããŸã§ã€ã¡ãŒãžã§ãïŒã func aliceHeight(input Input) int { result := 0 for _, person := range input.persons { if person.name == "alice" { result = person.height } } return result } <vaule> ã«ã¯æ°å€ãæååãçåœå€ã ãã§ãªããéåãé
åããªããžã§ã¯ããªã©ãæå®ã§ããŸãã = <value> éšãçç¥ãããš = true ãšããŠæ±ãããŸãã äŸãã°ä»¥äžã®ã«ãŒã« alice_exists 㯠name ããŒã®å€ã alice ãªèŠçŽ ãååšããå Žåã®ã¿ true ã«ãªããŸãã alice_exists { person := input.persons[_] person.name == "alice" } { <body> } éšãçç¥ãããš { true } ãšããŠæ±ãããŸãã宿°ã¯ãã®æžãæ¹ã䜿ã£ãŠå®çŸ©ããŸãã pi = 3.14 è€æ°ã®å®çŸ©ãæžã ã«ãŒã«ã¯è€æ°ã«åå²ããŠå®çŸ©ã§ããŸãã åå²ããŠèšè¿°ãããã«ãŒã«ã¯ è«çå ïŒORïŒã®ããã«è©äŸ¡ãããŸãã 以äžã¯ ã BMI ã18.5æªæºãŸãã¯25以äžã®äººãã®ååã®éå ãè¿ãã«ãŒã«ã®äŸã§ãã unhealthy_persons[person.name] { person := input.persons[_] bmi := (person.weight / (person.height * person.height)) * 10000 bmi < 18.5 } unhealthy_persons[person.name] { person := input.persons[_] bmi := (person.weight / (person.height * person.height)) * 10000 bmi >= 25 } ãããGoã®ã³ãŒãã§æžããšä»¥äžã®ããã«ãªããŸãïŒâ»ãããŸã§ã€ã¡ãŒãžã§ãïŒã func unhealthyPersons(input Input) [] string { result := [] string {} for _, person := range input.persons { bmi := float64 (person.weight) / ( float64 (person.height) * float64 (person.height)) * 10000.0 if bmi < 18.5 || bmi >= 25 { result = append (result, person.name) } } return result } 颿° 颿°ã¯ä»¥äžã®ããã«èšè¿°ããŸããåŒæ°ãåãããšä»¥å€ã¯ å€ãçæããã«ãŒã« ãšæŠãåãã§ãã <name> (<args>) = <value> { <body> } 以äžã¯ BMI ãèšç®ãã颿°ãšã BMI ã®åé¡ãè¿ã颿°ã®äŸã§ãã bmi(height, weight) = (weight / (height * height)) * 10000 bmi_class(person) = "underweight" { bmi(person.height, person.weight) < 18.5 } bmi_class(person) = "normal range" { bmi(person.height, person.weight) >= 18.5 bmi(person.height, person.weight) < 25 } bmi_class(person) = "overweight" { bmi(person.height, person.weight) >= 25 } ãŸãOPA/Regoã«ã¯çµèŸŒã¿é¢æ°ããããŸãããã䜿ãçµèŸŒã¿é¢æ°ãããã€ã玹ä»ããŸãã # æžåŒä»ãæååãè©äŸ¡ããŠçµæã®æååãè¿ã sprintf("%s's weight is %d", [person.name, person.weight]) # é
åãéåããªããžã§ã¯ãã®èŠçŽ æ°ãè¿ã count(young_and_tall_persons) == 0 # ãªããžã§ã¯ããæååïŒjson圢åŒïŒã«å€æãããã®ãè¿ã json.marshal(obj) # --explainãªãã·ã§ã³ãã€ããŠå®è¡ããéã«æååãåºåããïŒè©³çްã¯åŸè¿°ïŒ trace("...") äžã§ç޹ä»ãã以å€ã«ãæ§ã
ãªçµèŸŒã¿é¢æ°ããããŸãã詳现㯠Built-in Functions ãåç
§ããŠãã ããã å
å
è¡šèš Regoã§ã¯å
å
衚èšã䜿çšã§ããŸãã ãªã¹ãå
å
è¡šèš ä»¥äžã¯ ã幎霢20æä»¥äžã身é·170cm以äžã®äººãã®ååã®é
å ãçæããå
å
衚èšã®äŸã§ãã array := [person.name | person := input.persons[_]; person.age <= 20; person.height >= 170] ãããGoã®ã³ãŒãã§æžããšä»¥äžã®ããã«ãªããŸãïŒâ»ãããŸã§ã€ã¡ãŒãžã§ãïŒã array := func (input Input) [] string { result := [] string {} for _, person := range input.persons { if person.age <= 20 && person.height >= 170 { result = append (result, person.name) } } return result }(input) éåå
å
è¡šèš ä»¥äžã¯ éåãçæãã ã§äŸç€ºãã ã幎霢20æä»¥äžã身é·170cm以äžã®äººãã®ååã®éå ãå
å
衚èšã§èšè¿°ããäŸã§ãã a_set := {person.name | person := input.persons[_]; person.age <= 20; person.height >= 170} ãããGoã§æžããå Žåã®ã³ãŒã㯠ãªã¹ãå
å
è¡šèš ãšåããã岿ããŸãïŒâ»ãããŸã§ã€ã¡ãŒãžã§ãïŒã ãªããžã§ã¯ãå
å
è¡šèš ä»¥äžã¯ ãªããžã§ã¯ãïŒèŸæžïŒãçæãã ã§äŸç€ºãã ã幎霢20æä»¥äžã身é·170cm以äžã®äººãã«ã€ããŠãååãšäœéã®ãã¢ãèŠçŽ ãšããŠæã€ã®ãªããžã§ã¯ã ãå
å
衚èšã§èšè¿°ããäŸã§ãã obj := {person.name: person.weight | person := input.persons[_]; person.age <= 20; person.height >= 170} ãããGoã®ã³ãŒãã§æžããšä»¥äžã®ããã«ãªããŸãïŒâ»ãããŸã§ã€ã¡ãŒãžã§ãïŒã obj := func (input Input) map [ string ] int { result := map [ string ] int {} for _, person := range input.persons { if person.age <= 20 && person.height >= 170 { result[person.name] = person.weight } } return result }(input) ããŒã¯ãŒããš æŒç®å not åŒãåŠå®ããã«ã¯ not ã䜿çšããŸãã not person.age <= 20 ã«ãŒã«ãä»®æ³ããã¥ã¡ã³ããçæããã é
åãéåããªããžã§ã¯ãã®äžã«æå®ããèŠçŽ ãååšããã ãå€å®ããéã«ã䜿çšããŸãã not input.persons[4] not person.birthday some some ã䜿ãããšã§ã«ãŒã«å
ã®ããŒã«ã«å€æ°ãæç€ºçã«å®£èšã§ããŸãã young_and_tall_persons[person.name] { some i person := input.persons[i] person.age <= 20 person.height >= 170 } ããŒã«ã«å€æ°ã®å®£èšãå¿
ãè¡ãå¿
èŠã¯ãããŸãããã倿°åãšåãã«ãŒã«ãååšããå Žåã§ãæåŸ
éãããŒã«ã«å€æ°ãšããŠæ±ããããããæç€ºçã«å®£èšããããšãæšå¥šãããŠããŸãã äŸãã°ä»¥äžã®ã«ãŒã« young_and_tall_persons 㯠some i ã®æç¡ã§çµæãå€ãããŸãã i = 1 young_and_tall_persons[person.name] { some i person := input.persons[i] person.age <= 20 person.height >= 170 } default default ã䜿ãããšã§ å€ãçæããã«ãŒã« ã®ããã©ã«ãå€ãæå®ã§ããŸãã 以äžã®äŸã§ã¯ãã«ãŒã« alice_exists 㯠input.persons ã®äžã«Aliceã®èŠçŽ ãååšããã° true ãããã§ãªããã° false ã«ãªããŸãã default ããŒã¯ãŒããçç¥ãããšãã«ãŒã« alice_exists 㯠input.persons ã®äžã«Aliceã®èŠçŽ ãååšããªãå Žå false ã§ã¯ãªãæªå®çŸ©ã«ãªããŸãã default alice_exists = false alice_exists { person := input.persons[_] person.name == "alice" } in in ã¯OPAã® v0.34.0 ã§å°å
¥ãããããŒã¯ãŒãã§ãã é
åãéåããªããžã§ã¯ããããå€ãèŠçŽ ã«å«ãã§ãããïŒãŸãã¯å«ãã§ããªããïŒå€å®ãã é
åãéåããªããžã§ã¯ãã®èŠçŽ ã«å€æ°ãæçžãã ãšãã£ãããšãã§ããŸãã OPA v0.34.2 æç¹ã§ã¯ã in ããŒã¯ãŒãã䜿ãã«ã¯ import future.keywords.in ã宣èšããå¿
èŠããããŸãã 以äžã¯é
åãéåããªããžã§ã¯ããããå€ãèŠçŽ ã«å«ãã§ãããå€å®ããã«ãŒã«ã®äŸã§ãã import future.keywords.in # ååã®éåãçµæã¯ ["alice", "bob", "carol", "dave"] ã person_names[person.name] { person := input.persons[_] } # ååãšäœéã®ãã¢ãããªããªããžã§ã¯ããçµæã¯ {"alice": 60, "bob": 80, "carol": 66, "dave": 50} ã person_weights[person.name] = person.weight { person := input.persons[_] } # éå person_names ã®äžã« "alice" ãååšããããçµæã¯ true ã alice_exists { "alice" in person_names } # ãªããžã§ã¯ã person_weights ã®äžã«å€ã 60 ã§ããèŠçŽ ïŒäŸïŒ {"alice": 60} ïŒãååšããããçµæã¯ true ã sixty_kg_person_exists { 60 in person_weights } not ãšçµã¿åãããŠãé
åãéåããªããžã§ã¯ããããå€ãèŠçŽ ã«å«ãã§ããªãããå€å®ã§ããŸãã # éå person_names ã®äžã« "ellen" ãååšããªãããçµæã¯ true ã ellen_does_not_exist { not "ellen" in person_names } # ãªããžã§ã¯ã person_weights ã®äžã«å€ã 70 ã§ããèŠçŽ ïŒäŸïŒ {"alice": 70} ïŒãååšããªãããçµæã¯ true ã seventy_kg_person_does_not_exist { not 70 in person_weights } 巊蟺ã«åŒæ°ã2ã€æž¡ãããšã§ãé
åããªããžã§ã¯ãããæ·»åãããŒããšãå€ãã®ãã¢ãå«ãã§ããããå€å®ã§ããŸãã # ãªããžã§ã¯ã person_weights ã®äžã«èŠçŽ {"alice": 60} ãååšããããçµæã¯ true ã is_alice_sixty_kg { "alice", 60 in person_weights } é
åãéåã®å®çŸ© 颿°ã®åŒæ° ãªã©ã§ in ããŒã¯ãŒãã®å·ŠèŸºã«åŒæ°ã2ã€æž¡ãå Žåã¯ãæ£ããè©äŸ¡ããããã () ã§å²ãå¿
èŠããããŸãã # 誀ã£ãæžãæ¹ãã`0`ãš `2 in [2]` ã®2ã€ã®åŒæ°ãåãåã颿°fããšããŠè§£éãããã f(0, 2 in [2]) # æ£ããæžãæ¹ãã`0, 2 in [2]` ã®çµæã1ã€ã®åŒæ°ãšããŠåãåã颿°fããšããŠè§£éãããã f((0, 2 in [2])) some ãšçµã¿åãããŠãé
åãéåããªããžã§ã¯ãã®èŠçŽ ã«å€æ°ãæçžã§ããŸãã # äœéã60kgã®äººã®ååã®éåãè¿ããçµæã¯ {"alice"} ã sixty_kg_persons[name] { some name, 60 in person_weights } # çµæã¯ ["a", "r", "y"] unique[x] { some x in ["a", "r", "r", "a", "y"] } with ã«ãŒã«ã®è©äŸ¡æã« with ã䜿ã£ãŠ input data.<path> ã®æ§é åããŒã¿ãæå®ããå€ã«çœ®ãæããããšãã§ããŸãã allow with input as {"user": "charlie", "method": "GET"} with data.roles as {"dev": ["charlie"]} ããã¯äž»ã« åäœãã¹ã ãæžãéã«äœ¿çšããŸãããã¹ãã®æžãæ¹ã¯ ãã¹ããæžã ã§èª¬æããŸãã ã¢ãžã¥ãŒã«ãããã±ãŒãž 0å以äžã®ã«ãŒã«ã®éåãã¢ãžã¥ãŒã«ãšããããããç¹å®ã® åå空é ã«ã°ã«ãŒãåãããã®ãããã±ãŒãžãšãããŸãã åãããã±ãŒãžã®ããªã·ãŒãåã ãã£ã¬ã¯ã ãªã«é
眮ããå¿
èŠã¯ãããŸããã ããã±ãŒãžå㯠package ã§æå®ããŸãã package example ä»ã®ããã±ãŒãžã®ã«ãŒã«ã颿°ãåç
§ããå Žå㯠import data.<ããã±ãŒãžå> ã®ããã«å®£èšããŸãã import data.other_example example_rule[value] { value := other_example.other_rule } ãã¹ããæžã åäœãã¹ã ãæžãããšã§ããªã·ãŒãæåŸ
éãåäœããã確èªã§ããŸãã ãã¹ãã³ãŒãã¯ãã¡ã€ã«åã®æ«å°Ÿã _test.rego ãšããŠãã ããã äŸãã° example.rego ã®ãã¹ãã³ãŒãã®ãã¡ã€ã«å㯠example_test.rego ã§ãã ãã¹ãã±ãŒã¹ã¯ååã test_ ããå§ãŸãã«ãŒã«ãšããŠèšè¿°ããŸããã«ãŒã«ã®bodyéšã®åŒãå
šãŠçãªãã°æåãããã§ãªããã°å€±æã§ãã package example test_young_and_tall_persons { input := {"persons": [ {"name": "alice", "age": 20, "height": 170, "weight": 60}, {"name": "bob", "age": 22, "height": 180, "weight": 80}, {"name": "carol", "age": 17, "height": 175, "weight": 66}, {"name": "dave", "age": 18, "height": 155, "weight": 50}, ]} young_and_tall_persons == {"alice", "carol"} with input as input } test_no_young_and_tall_persons { input := {"persons": [ {"name": "alice", "age": 30, "height": 170, "weight": 60}, {"name": "bob", "age": 22, "height": 180, "weight": 80}, {"name": "carol", "age": 27, "height": 175, "weight": 66}, {"name": "dave", "age": 18, "height": 155, "weight": 50}, ]} count(young_and_tall_persons) == 0 with input as input } ãã¹ãã¯ä»¥äžã®ããã«å®è¡ããŸãã $ opa test . PASS: 2/2 詊ãã«ãã¹ãã±ãŒã¹ test_young_and_tall_persons ã® ã¢ãµãŒã·ã§ã³ ã®å³èŸºã {"alice", "carol"} ãã {"alice", "bob"} ãžæžãæããŠãã¹ãã倱æãããŸãã ãã¹ãã倱æããå Žåã¯æ¬¡ã®ãããªçµæã«ãªããŸãã $ opa test . data.example.test_young_and_tall_persons: FAIL (1.466812ms) -------------------------------------------------------------------------------- PASS: 1/2 FAIL: 1/2 opa test ã³ãã³ãã«ã¯ä»¥äžã®ãããªãªãã·ã§ã³ããããŸãã -v, --verbose ïŒãã¹ãçµæã®è©³çްã衚瀺ããŸãã -r, --run ïŒæå®ãããã¹ãã±ãŒã¹ã®ã¿ãå®è¡ã§ããŸãã -c, --coverate ïŒãã¹ã ã«ãã¬ããž ãåºåã§ããŸãã OPA/Regoãå©çšããããŒã« OPA/Regoãå©çšããããŒã«ãç°¡åã«ã玹ä»ããŸãã Conftest Conftest 㯠Kubernetes ã® ãããã§ã¹ã ãã¡ã€ã«ãTerraformã³ãŒããªã©ã®æ§æãã¡ã€ã«ãRegoã§èšè¿°ãããããªã·ãŒã«åŸã£ãŠãããæ€èšŒããããã® CLI ããŒã«ã§ãã 以äžã¯ Kubernetes ã®Podã« Readiness Probe ãèšå®ãããŠãããæ€èšŒããããªã·ãŒã®äŸã§ãïŒâ»ãã®ããªã·ãŒã¯èª¬æã®ããã«ç°¡ç¥åããŠããå®éçšã«ã¯é©ããŠããŸããïŒã package deny_container_without_readiness_probe violation[msg] { input.kind == "Pod" container := input.spec.containers[_] not container.readinessProbe msg := "Readiness Probe must be set" } å
çšã®ããªã·ãŒãçšããŠä»¥äžã® ãããã§ã¹ã ãã¡ã€ã«ãæ€èšŒããŠã¿ãŸãã apiVersion : v1 kind : Pod metadata : name : myapp spec : containers : - name : myapp image : myapp:1.0.0 ports : - containerPort : 8080 $ conftest test --policy . --all-namespaces manifests.yaml FAIL - manifests.yaml - deny_container_without_readiness_probe - Readiness Probe must be set 1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions ãããã§ã¹ã ãããªã·ãŒã«éåããŠããããšãæ€åºã§ããŸããã Conftest㯠Kubernetes ã® ãããã§ã¹ã ãã¡ã€ã«ä»¥å€ã®æ§æãã¡ã€ã«ã«å¯ŸããŠãå©çšã§ããŸããå
¬åŒããã¥ã¡ã³ãã® Examples ã«ã¯ä»¥äžã®æ§æãã¡ã€ã«ãæ€èšŒãããµã³ãã«ããªã·ãŒãæ²èŒãããŠããŸãã AWS SAM Framework CUE Docker compose Dockerfile EDN Ignore HCL HCL 2 HOCON INI Jsonnet Kubernetes Kustomize Serverless Framework Traefik Typescript VCL XML Gatekeeper Gatekeeper 㯠Kubernetes ã® Validating Admission Webhook ãšããŠåäœããŸãã kube-apiserverã«å¯ŸãããªãœãŒã¹äœæã®èŠæ±ãæ€èšŒããããªã·ãŒã«éåãããªãœãŒã¹ã®äœæãçŠæ¢ã§ããŸãã Kubernetes ã«ãããConftestãš Gatekeeper ã®äœ¿ãåã Conftestã¯äž»ã«éçºè
ã®ããŒã«ã«ç°å¢ãCIãµãŒããªã©ã§å®è¡ãããŸãã git ãªããžã㪠äžã® ãããã§ã¹ã ãã¡ã€ã«ã倿Žãããéã«ããã®å€æŽãããªã·ãŒã«åŸã£ãŠãããæ€èšŒããå³åº§ã«éçºè
ãžãã£ãŒãããã¯ã§ããŸãã ãã ã以äžã®ãããªã±ãŒã¹ã¯Conftestã§æ€ç¥ã§ããŸããã ãªãã¬ãŒã¿ãŒã kubectl create ãªã©ã§ Kubernetes ã¯ã©ã¹ã¿ ãŒã«ãªãœãŒã¹ãçŽæ¥äœæããå Žå Kubernetes ã¯ã©ã¹ã¿ ãŒäžã®CustomControllerã«ãã£ãŠãªãœãŒã¹ãäœæãããå Žå Gatekeeper 㯠Kubernetes ã®Validating Admission WebhookãšããŠåäœããŸãã kube-apiserverã«å¯Ÿããå
šãŠã®ãªãœãŒã¹äœæã®èŠæ±ãæ€èšŒããããªã·ãŒã«éåãããªãœãŒã¹ã®äœæãçŠæ¢ã§ããŸãã ãã ã kubectl create ã kubectl apply ãå®è¡ããŠå®éã«kube-apiserverãžãªã¯ ãšã¹ ããéä¿¡ãããŸã§ãããªã·ãŒã«éåããŠãããæ€èšŒã§ããŸããã Conftestãš Gatekeeper ã¯ã©ã¡ããçæ¹ã ã䜿ãã®ã§ã¯ãªããäž¡æ¹çµã¿åãããŠäœ¿ãã®ãããã§ãããã äž»ãªå®è¡å Žæ äž»ãªå®è¡ã¿ã€ãã³ã° æ€èšŒå¯Ÿè±¡ Conftest éçºè
ã®ããŒã«ã«ç°å¢ãCIãµãŒã ãããã§ã¹ã ãã¡ã€ã«ã®æŽæ°æ ãããã§ã¹ã ãã¡ã€ã« Gatekeeper Kubernetes ã®Validating Admission Webhook kube-apiserverãžã®ãªãœãŒã¹äœæã®èŠæ±æ kube-apiserverã«å¯Ÿããå
šãŠã®ãªãœãŒã¹äœæã®èŠæ± Conftestã Gatekeeper ãRegoã§èšè¿°ãããããªã·ãŒã䜿çšããŸãã ãããæ§é åããŒã¿ïŒïŒ ãããã§ã¹ã ïŒã®æž¡ãæ¹ãªã©ã«å·®ç°ããããŸãã Conftestãš Gatekeeper ã§åãããªã·ãŒã䜿çšããå Žåãäœããã®æ¹æ³ã§ãã®å·®åãåžåããå¿
èŠããããŸãã ãããã§ã¹ã ã®æ ŒçŽå Žæ ConftestïŒ --combine ãªãã·ã§ã³ãªãïŒ(*1) input ConftestïŒ --combine ãªãã·ã§ã³ããïŒ(*1) input[_].contents Gatekeeper input.review.object (*1)ïŒConftestã¯éåžž ãããã§ã¹ã ãã¡ã€ã«ã«å«ãŸãããªãœãŒã¹ã1ã€ãã€åå¥ã«è©äŸ¡ããŸãã ãã®ãããäŸãã°Deploymentã«å¯ŸããŠPodDisruptionBudgetãå®çŸ©ãããŠããããšãã£ããããªãè€æ°ã®ãªãœãŒã¹ã«ãŸããã£ãããªã·ãŒãè©äŸ¡ã§ããŸããã --combine ãªãã·ã§ã³ãèšå®ããããšã§ã ãããã§ã¹ã ãã¡ã€ã«ã«å«ãŸããå
šãŠã®ãªãœãŒã¹ãåæã«Regoãžæž¡ããè€æ°ã®ãªãœãŒã¹ã«ãŸããã£ãããªã·ãŒãè©äŸ¡ã§ããŸãã ãŸãConftestã䜿çšãã *.rego ãã¡ã€ã«ãã Gatekeeper ãå¿
èŠãšãããªãœãŒã¹ïŒ ConstraintTemplate ãªã©ïŒãçæããå¿
èŠããããŸãã ãããè¡ãããŒã«ã« konstraint ããããŸãã ãã以å€ã®ããŒã« ãããŸã§ç޹ä»ããããŒã«ã®ä»ã«ã以äžã®ããŒã«ãRegoã§èšè¿°ãããããªã·ãŒãå©çšããŠããŸãã Trivy Terrascan ããã£ããšãã ç§ãå®éã«OPA/Regoã䜿ã£ãŠããŠããã£ãç¹ãããã€ãã玹ä»ããŸãã ççŸããã«ãŒã«ãå®çŸ©ããªã ã«ãŒã«ã®å®çŸ©ãççŸããŠã¯ãããŸããããã®ãããªã«ãŒã«ã«ççŸãçºçãããæ§é åããŒã¿ãæž¡ããšå®è¡æãšã©ãŒãçºçããŸãã ããã€ãæªãäŸã玹ä»ããŸãã 以äžã¯å€ãçæããã«ãŒã«ã§ãããã ãå€ã1ã€ã«å®ãŸããŸããã height = person.height { person = input.persons[_] } 以äžã®äŸã§ã¯ã«ãŒã«ã®å®çŸ©ãè€æ°ã«åãããŠããŸãã 1ã€ç®ã®å®çŸ©ã§ã¯ã name ãš weight ã®ãã¢ãèŠçŽ ãšããŠæã€ãªããžã§ã¯ãããçæããŸãã äžæ¹ã2ã€ç®ã®å®çŸ©ã§ã¯ã name ãš height ã®ãã¢ãèŠçŽ ãšããŠæã€ãªããžã§ã¯ãããçæããŸãã çµæãçæããããªããžã§ã¯ãã® name ããŒã®å€ã¯1ã€ã«å®ãŸããŸããã personal_health_info[person.name] = person.height { person := input.persons[_] } personal_health_info[person.name] = person.weight { person := input.persons[_] } é
åãéåãããæ¡ä»¶ãæºããå€ãå«ãã§ããªããæ€èšŒããã«ãŒã«ã®æžãæ¹ é
å array ãéå a_set ã颿° f ãæºããå€ãå«ãŸãªããå€å®ããã«ãŒã«ã¯ä»¥äžã®ããã«èšè¿°ã§ããŸãã # é
åã颿°fãæºããå€ãå«ãã§ããªãã none_in_array_match { count({x | x := array[_]; f(x)}) == 0 } # éåã颿°fãæºããå€ãå«ãã§ããªãã none_in_set_match { count({x | a_set[x]; f(x)}) == 0 } ç¹ã«é
å array ãéå a_set ãããå€ x ãèŠçŽ ã«å«ãã§ããªããå€å®ãããå Žå㯠in ã䜿ã£ãŠä»¥äžã®ããã«èšè¿°ã§ããŸãã â»OPA v0.34.2 æç¹ã§ã¯ã in ããŒã¯ãŒãã䜿ãã«ã¯ import future.keywords.in ã宣èšããå¿
èŠããããŸãã import future.keywords.in # é
åãããå€ãå«ãã§ããªãã none_in_array_match { not x in array } # éåãããå€ãå«ãã§ããªãã none_in_set_match { not x in a_set } 以äžã®æžãæ¹ã¯èª€ãã§ãã 以äžã®ã«ãŒã«ã¯é
åãéåã«é¢æ° f ãæºãããªãå€ã1ã€ã§ãååšããã° true ã«ãªããŸãã é
åãéåã®å
šãŠã®èŠçŽ ã颿° f ãæºãããªãããšã¯æ€èšŒã§ããŸããã # é
åã颿°fãæºããå€ãå«ãã§ããªããïŒééã£ãæžãæ¹ïŒ none_in_array_match { x := array[_] not f(x) } # éåã颿°fãæºããå€ãå«ãã§ããªããïŒééã£ãæžãæ¹ïŒ none_in_set_match { a_set[x] not f(x) } ãªããžã§ã¯ãã®ãã£ãŒã«ããç¹å®ã®å€ã§ãããæ€èšŒããã«ãŒã«ã®æžãæ¹ ãªããžã§ã¯ã obj ã®ã㌠key ã®å€ã value ã§ããããšãå€å®ããã«ãŒã«ã¯ä»¥äžã®ããã«èšè¿°ã§ããŸãã violation[msg] { not has_specific_value(obj) msg := "'obj.key' must be 'value'" } has_specific_value(obj) { obj.key == value } 以äžã®æžãæ¹ã¯èª€ãã§ãã ããªããžã§ã¯ã obj ã®ã㌠key ã®å€ã value ã§ããå Žåãã«å ããŠããªããžã§ã¯ã obj ã«ã㌠key ãååšããªãå Žåãã«ãã«ãŒã«ã®bodyéšã false ã«ãªãããã§ãã violation[msg] { obj.key != value msg := "'obj.key' must be 'value'" } ç°åžžç³»ãã¹ãã® ã¢ãµãŒã·ã§ã³ ã®æžãæ¹ ä»¥äžã¯å
¥åããŒã¿ã®å€ãå¶æ°ãã©ããå€å®ããããªã·ãŒã®äŸã§ãã violation[msg] { input.value % 2 != 0 msg := "Value must be even" } äžè¿°ã®ããªã·ãŒã«å¯Ÿããç°åžžç³»ãã¹ããšããŠãæåç§ã¯ä»¥äžã®ãããªãã¹ãã±ãŒã¹ãèšè¿°ããŠããŸããã test_odd { input := {"value": 1} violation with input as input } ã§ããäžè¿°ã®ãã¹ãã±ãŒã¹ã¯äžè¡šã®éãåžžã«æåããŠããŸããŸãã ãã㯠{} ïŒ ç©ºéå ïŒãçãšããŠæ±ãããããã§ãã ãã£ãŠãã®ãã¹ãã±ãŒã¹ã®æžãæ¹ã¯èª€ãã§ãã value ã®å€ violation ã®çµæ test_odd ã®çµæ 奿°ïŒäŸïŒ1, 3, ...ïŒ {"Value must be even"} true å¶æ°ïŒäŸïŒ2, 4, ...ïŒ {} ïŒ ç©ºéå ïŒ true åèïŒ open policy agent - Rego testing: how to test "not deny"? - Stack Overflow æ£ããã¯ä»¥äžã®ããããã®ããã«èšè¿°ãããšããã§ããããäžã®æ¹ãããå³å¯ã«æ€èšŒãè¡ããŸãã # äŸ1ïŒviolationãæå®ããå€ã«å®å
šã«äžèŽããã確èªããã test_odd { input := {"value": 1} violation == {"Value must be even"} with input as input } # äŸ2ïŒviolationã«æå®ããèŠçŽ ãå«ãŸããŠããã確èªãããä»ã®èŠçŽ ãå«ãŸããŠããå¯èœæ§ãããã test_odd { input := {"value": 1} violation["Value must be even"] with input as input } # äŸ3ïŒviolationã空ã§ãªãããšã確èªãããå
·äœçãªèŠçŽ ãŸã§ã¯ç¢ºèªããªãã test_odd { input := {"value": 1} count(violation) > 0 with input as input } ãªããžã§ã¯ãã®ååšããªãããŒãåç
§ããéã®æå ãªããžã§ã¯ãã®ååšããªãããŒãåç
§ãããšã«ãŒã«ãæå³ããªãçµæãè¿ãããæ³šæãå¿
èŠã§ãã å
çšã®å¶æ°ãã©ããå€å®ããããªã·ãŒã誀ã£ãŠä»¥äžã®ããã«èšè¿°ãããšããŸãã violation[msg] { input.number % 2 != 0 # å€ãæ ŒçŽãããŠããã®ã¯ input.value ã ã誀ã£ãŠ input.number ãåç
§ããŠãã msg := "Value must be even" } ãã®ãšã input.value ãã©ã®ãããªå€ã§ã violation 㯠空éå ãè¿ããŸãã ããã¯ã«ãŒã« violation ã®bodyéšã«ããã1çªç®ã®åŒãã input.value ã®å€ã«ããããããåžžã«æªå®çŸ©ïŒã€ãŸãçã§ã¯ãªãïŒã«ãªãããã§ãã $ cat input_odd.json { "value": 1 } $ opa eval -i input_odd.json -d policy.rego --format pretty data.example.violation [] $ cat input_even.json { "value": 2 } $ opa eval -i input_even.json -d policy.rego --format pretty data.example.violation [] ããã§åé¡ãªã®ã¯æªå®çŸ©ã false ãšåæ§ã«æ±ãããŠããããšã§ãã ã«ãŒã«ã®äžã§ãªããžã§ã¯ãã®ååšããªãããŒãåç
§ããŠæªå®çŸ©ãçºçããŠãç¹ã«äŸå€ãªã©ã¯çºçããŸããã ãã®ããæ£ããããªã·ãŒãè©äŸ¡ãããŠã«ãŒã«ã®bodyéšã false ã«ãªã£ãã®ãããªããžã§ã¯ãã®ååšããªãããŒãåç
§ããŠæªå®çŸ©ã«ãªã£ãã®ããåºå¥ããã®ã¯å°é£ã§ãã çŸæç¹ã§ã¯ã åäœãã¹ã ã® ã«ãã¬ããž ãäžãã以å€ã«ããã®åé¡ã解決ããããæ¹æ³ã¯èŠã€ããããŠããŸããã ãŸãããã¯ãOPAã®ããªã·ãŒãšã³ãžã³ããšããããåŒã³ã ãå€éšãµãŒãã¹ãã®ã€ã³ã¿ãŒãã§ãŒã¹éšåã®ä»æ§ã倿Žãããå Žåãåé¡ã«ãªããŸãã äŸãã°Conftestã§ã¯ v0.22.0 ã§ --combine ãªãã·ã§ã³ãã€ããéã«Regoãžæž¡ãããŒã¿ã®æ§é ã倿ŽãããŸããïŒ #388 ïŒã çµæãããã€ãã®ã«ãŒã«ããªããžã§ã¯ãã®ååšããªãããŒãåç
§ããŠå¿
ãæªå®çŸ©ã«ãªãã ãããã§ã¹ã ãããªã·ãŒã«éåããŠããŠãConftestã®æ€èšŒãåžžã«æåãç¶ãããšããåé¡ãèµ·ãããŸããã ãããé²ãæ¹æ³ãšããŠãRegoã® åäœãã¹ã ã«å ããŠãOPAã®ããªã·ãŒãšã³ãžã³ããšããããåŒã³ã ãå€éšãµãŒãã¹ãã®éã® çµåãã¹ã ãå®è£
ããããšãæããããŸãã äŸãã°ç§ã¯Conftestã®ãã¹ãçµæã conftest test -o json ã§åºåããŠãããConftestãããŒãžã§ã³ã¢ããããéã¯ãã¹ãçµæã«æå³ããªã倿ŽãçºçããŠããªãã確èªããã¹ãããã·ã§ãããã¹ããè¡ãããã«ããŠããŸãã ããªã·ãŒã® ãããã° æ¹æ³ ããªã·ãŒã®çµæãæåŸ
éãã§ãªãå Žåãåå ã調æ»ããå¿
èŠããããŸããã§ããRegoã®åŠçã®æµãã¯è€éã§è¿œãã®ãå°é£ã§ãã ããªã·ãŒã®æåãææ¡ããæ¹æ³ãšã㊠--explain ãªãã·ã§ã³ã掻çšã§ããŸãã äŸãšããŠä»¥äžã®ããªã·ãŒãšæ§é åããŒã¿ã --explain ãªãã·ã§ã³ãã€ããŠè©äŸ¡ããŸãã violation[msg] { trace(sprintf("input.value is %d", [input.value])) input.value % 2 != 0 msg := "Value must be even" } { " value ": 2 } --explain ãªãã·ã§ã³ã®å€ãšããã«å¯Ÿããåºåã¯ä»¥äžã®ãšããã§ãã --explain=full ïŒã«ãŒã«ãã©ã®ããã«è©äŸ¡ããããå
šãŠè¡šç€ºããã $ opa eval -i input.json -d policy.rego --explain full --format pretty data.example.violation query:1 Enter data.example.violation = _ query:1 | Eval data.example.violation = _ query:1 | Index data.example.violation (matched 1 rule) policy.rego:3 | Enter data.example.violation policy.rego:4 | | Eval __local3__ = input.value policy.rego:4 | | Eval sprintf("input.value is %d", [__local3__], __local1__) policy.rego:4 | | Eval trace(__local1__) policy.rego:4 | | Note "input.value is 2" policy.rego:5 | | Eval __local4__ = input.value policy.rego:5 | | Eval rem(__local4__, 2, __local2__) policy.rego:5 | | Eval neq(__local2__, 0) policy.rego:5 | | Fail neq(__local2__, 0) policy.rego:5 | | Redo rem(__local4__, 2, __local2__) policy.rego:5 | | Redo __local4__ = input.value policy.rego:4 | | Redo trace(__local1__) policy.rego:4 | | Redo sprintf("input.value is %d", [__local3__], __local1__) policy.rego:4 | | Redo __local3__ = input.value query:1 | Exit data.example.violation = _ query:1 Redo data.example.violation = _ query:1 | Redo data.example.violation = _ [] --explain=fails ïŒåŒã false ã«ãªã£ãéšåã®ã¿è¡šç€ºããã $ opa eval -i input.json -d policy.rego --explain fails --format pretty data.example.violation query:1 Enter data.example.violation = _ policy.rego:3 | Enter data.example.violation policy.rego:5 | | Fail neq(__local2__, 0) [] --explain=notes ïŒ trace 颿°ïŒåŸè¿°ïŒã®å
容ã®ã¿è¡šç€ºããã $ opa eval -i input.json -d policy.rego --explain notes --format pretty data.example.violation query:1 Enter data.example.violation = _ policy.rego:3 | Enter data.example.violation policy.rego:4 | | Note "input.value is 2" [] trace 颿°ã䜿ããš --explain ãªãã·ã§ã³ãã€ããŠå®è¡ããéã«æååãåºåã§ããŸãã trace 颿°ã®åŒæ°ã¯æååã§ãã sprintf 颿°ã json.marshal 颿°ãšçµã¿åãããããšã§æåå以å€ã®å€ïŒæ°å€ããªããžã§ã¯ããªã©ïŒãåºåã§ããŸãã ããã䜿ã£ãŠprint ãããã° ã§ããŸãã --explain=full ã®åºåãèªã¿è§£ãã®ã¯ãªããªã倧å€ãªã®ã§ãæå㯠trace 颿°ãš --explain=notes ãªãã·ã§ã³ãçµã¿åãããŠprint ãããã° ããã®ãããã§ãããã Conftestã§ã --trace ãªãã·ã§ã³ãã€ããããšã§ã«ãŒã«ãã©ã®ããã«è©äŸ¡ãããã衚瀺ã§ããŸãããã㯠opa ã³ãã³ãã® --explain=full ã«çžåœããŸãã = ãš := ãš == ã®éã Regoã«ã¯ = ã := ã == ãšãã䌌ããã㪠æŒç®å ããããŸããããããã®éãã¯ä»¥äžã®ãšããã§ãã èšæ³ èšè¿°ã§ããå Žæ ã³ã³ãã€ã« ãšã©ãŒ ãŠãŒã¹ã±ãŒã¹ := ã«ãŒã«å
倿°ãæ¢ã«å®çŸ©ãããŠããå Žåã«çºç ããŒã«ã«å€æ°ãå®çŸ©ãã == ã«ãŒã«å
倿°ããŸã å®çŸ©ãããŠããªãå Žåã«çºç å€ãæ¯èŒãã = ã©ãã§ã 倿°ãæ£ããåç
§ã§ããªãå Žåã«çºç ã¯ãšãªã衚çŸãã := ãš == ã®ãããã« = ã䜿ãããšãã§ããŸãã ãã ã := ãš == 㯠ã³ã³ãã€ã« æã«è¿œå ã®ãã§ãã¯ãåãããã䜿ããå Žé¢ã§ã¯ãªãã¹ã = ããã := ãš == ã䜿ããšããã§ãããã åèïŒ Equality: Assignment, Comparison, and Unification ãã©ãŒããã¿ã䜿ã opa fmt --write . ã§çŸåšã® ãã£ã¬ã¯ã ãªé
äžã«ããããªã·ãŒã®ãã©ãŒããããæŽããããšãã§ããŸãã è€æ°äººã§ããªã·ãŒãæŽæ°ããéã¯ãã®ã³ãã³ãã䜿ã£ãŠã€ã³ãã³ããæ¹è¡ã®æç¡ãªã©ãçµ±äžãããšããã§ãããã ãŸã opa fmt --fail . ã§ããªã·ãŒã®ãã©ãŒããããæŽã£ãŠããªãã£ãå Žåã«0以å€ã®çµäºã³ãŒããè¿ãããšãã§ããŸãã CIã«çµã¿èŸŒãã§ããªã·ãŒã®ãã©ãŒããããæŽã£ãŠãããæ€èšŒãããšããã§ãããã ãããã« ãã®ãã¹ãã§ã¯Open Policy Agentãšããªã·ãŒèšèªRegoã®ç޹ä»ãããŸããã ååã§ã¯Regoã®ææ³ãç°¡åã«èª¬æããŸããã åŸåã§ã¯ç§ãOpen Policy AgentãšRegoãå®éã«äœ¿ã£ãŠããŠããã£ãç¹ãããã€ãã玹ä»ããŸããã ãã®ãã¹ããèªãã§ãã ãã£ãæ¹ã®åœ¹ã«ç«ãŠã°å¹žãã§ãã ããŠãææ¥ã¯æ©è©°ããã®ãä»å¹Žç€Ÿå
ã®æè¡æžèªæžäŒã§èªãã æ¬ïŒãã§ãã ã©ããªæè¡æžã玹ä»ãããã®ã楜ãã¿ã§ãïŒ æåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åè Open Policy Agent | Documentation ç¹ã« Policy Reference ã¯Regoã®è²ã
ãªæžãæ¹ã玹ä»ãããŠããŠåèã«ãªããŸãã Open Policy Agent Rego Knowledge Sharing Meetup - connpass Achieving Security Compliance Monitoring with Open Policy Agent and Rego - Speaker Deck ãéå¬å ±åãæ°è瀟ã«ãã AWS ãæŽ»çšãã DX äºäŸã»ãã㌠| Amazon Web Services ããã° Gatekeeper/conftestã®RegoãDRYã«ç®¡çãã â HACK The Nikkei å·çïŒ @shibata.takao ãã¬ãã¥ãŒïŒ @ueba.yuki ïŒ Shodo ã§å·çãããŸãã ïŒ