Skip to content

Commit

Permalink
Update all policies to Rego v1 (#260)
Browse files Browse the repository at this point in the history
And fix all Regal violations in order to add Regal to CI for this project.

Found a couple of issues in both Regal and OPA while working on this, so it
feels like it was worth the time spent.

Signed-off-by: Anders Eknert <[email protected]>
  • Loading branch information
anderseknert authored Jul 29, 2024
1 parent a813272 commit b791362
Show file tree
Hide file tree
Showing 54 changed files with 1,264 additions and 1,025 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,11 @@ jobs:

- name: Make build
run: PATH=$PATH:$PWD:/home/runner/.local/bin make build

- name: Setup Regal
uses: StyraInc/setup-regal@v1
with:
version: v0.24.0

- name: Regal Lint
run: regal lint --format github .
2 changes: 1 addition & 1 deletion api_authz/docker/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
opa:
image: openpolicyagent/opa:0.40.0-rootless
image: openpolicyagent/opa:0.67.0
ports:
- "8181:8181"
command:
Expand Down
16 changes: 8 additions & 8 deletions api_authz/docker/policy/example-hr.rego
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package httpapi.authz
package httpapi.authz.hr

import rego.v1

# Allow HR members to get anyone's salary.
allow {
input.method == "GET"
input.path = ["finance", "salary", _]
input.user == hr[_]
allow if {
input.method == "GET"
input.path = ["finance", "salary", _]
input.user in members
}

# David is the only member of HR.
hr = [
"david",
]
members := ["david"]
44 changes: 21 additions & 23 deletions api_authz/docker/policy/example-jwt.rego
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
package httpapi.authz
package httpapi.authz.jwt

default allow = false
import rego.v1

default allow := false

# Allow users to get their own salaries.
allow {
some username
input.method == "GET"
input.path = ["finance", "salary", username]
token.payload.user == username
user_owns_token
allow if {
input.method == "GET"
input.path == ["finance", "salary", token.payload.user]
user_owns_token
}

# Allow managers to get their subordinate' salaries.
allow {
some username
input.method == "GET"
input.path = ["finance", "salary", username]
token.payload.subordinates[_] == username
user_owns_token
allow if {
some username
input.method == "GET"
input.path = ["finance", "salary", username]
username in token.payload.subordinates
user_owns_token
}

# Allow HR members to get anyone's salary.
allow {
input.method == "GET"
input.path = ["finance", "salary", _]
token.payload.hr == true
user_owns_token
allow if {
input.method == "GET"
input.path = ["finance", "salary", _]
token.payload.hr == true
user_owns_token
}

# Ensure that the token was issued to the user supplying it.
user_owns_token { input.user == token.payload.azp }
user_owns_token if input.user == token.payload.azp

# Helper to get the token payload.
token = {"payload": payload} {
[_, payload, _] := io.jwt.decode(input.token)
}
token := {"payload": io.jwt.decode(input.token)[1]}
24 changes: 12 additions & 12 deletions api_authz/docker/policy/example.rego
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package httpapi.authz

import rego.v1

# bob is alice's manager, and betty is charlie's.
subordinates = {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]}
subordinates := {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]}

# HTTP API request
# input = {
Expand All @@ -10,20 +12,18 @@ subordinates = {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie
# "method": "GET"
# }

default allow = false
default allow := false

# Allow users to get their own salaries.
allow {
some username
input.method == "GET"
input.path = ["finance", "salary", username]
input.user == username
allow if {
input.method == "GET"
input.path == ["finance", "salary", input.user]
}

# Allow managers to get their subordinates' salaries.
allow {
some username
input.method == "GET"
input.path = ["finance", "salary", username]
subordinates[input.user][_] == username
allow if {
some username
input.method == "GET"
input.path = ["finance", "salary", username]
username in subordinates[input.user]
}
28 changes: 19 additions & 9 deletions cloud-foundry/polices/cipher/cipher_test.rego
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
package main

test_if_ciphers_match {
deny_if_ciphers_missing[_] with input as {
".properties.gorouter_ssl_ciphers": {
"value": "ECDHE-RSA-AES128-GCM-SHA256:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
}
}
}
package main_test

import rego.v1

import data.main

test_if_ciphers_match if {
val := "ECDHE-RSA-AES128-GCM-SHA256:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
obj := {".properties.gorouter_ssl_ciphers": {"value": val}}

expect := concat("\n", [
"expected cipher configuration of: ECDHE-RSA-AES128-GCM-SHA256:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
" please update the value following this json path: [\".properties.gorouter_ssl_ciphers\", \"value\"]",
])

result := main.deny_if_ciphers_missing with input as obj

expect in result
}
91 changes: 46 additions & 45 deletions cloud-foundry/polices/pas/cert/certificate.rego
Original file line number Diff line number Diff line change
@@ -1,65 +1,66 @@
package cert

parse_certificate(cert) = parsedCertificate {
strippedCert := replace(replace(cert, "-----END CERTIFICATE-----", ""), "-----BEGIN CERTIFICATE-----", "")
parsedCertificate := crypto.x509.parse_certificates(strippedCert)
import rego.v1

parse_certificate(cert) := parsed_certificate if {
stripped_cert := replace(replace(cert, "-----END CERTIFICATE-----", ""), "-----BEGIN CERTIFICATE-----", "")
parsed_certificate := crypto.x509.parse_certificates(stripped_cert)
}

separate_certs(certChain) = cleanedCerts {
addDelimeter := replace(certChain, "-----END CERTIFICATE-----\n", "-----END CERTIFICATE-----\n&&&&")
splitCerts := split(addDelimeter, "&&&&")
count(splitCerts) > 0
separate_certs(cert_chain) := cleaned_certs if {
add_delimeter := replace(cert_chain, "-----END CERTIFICATE-----\n", "-----END CERTIFICATE-----\n&&&&")
split_certs := split(add_delimeter, "&&&&")
count(split_certs) > 0

cleanedCerts := array.slice(splitCerts, 0, count(splitCerts) - 1)
cleaned_certs := array.slice(split_certs, 0, count(split_certs) - 1)
}

get_certificate_expiry(rawCertChain) = expiryDate {
certArray := separate_certs(rawCertChain)
parsedCerts := [parsedCert |
cert := certArray[_]
parsedCert := parse_certificate(cert)
]
expiry(raw_cert_chain) := expiry_date if {
cert_array := separate_certs(raw_cert_chain)
parsed_certs := [parsed_cert |
some cert in cert_array
parsed_cert := parse_certificate(cert)
]

expiryDate := [expiryDate |
expiryDate := parsedCerts[_][_].NotAfter
]
expiry_date := [expiry_date |
expiry_date := parsed_certs[_][_].NotAfter
]
}

determine_if_expired(dates) = certsForRenewal {
thirty_days_in_nanoseconds := 2.592e+15
determine_if_expired(dates) := certs_for_renewal if {
thirty_days_in_nanoseconds := 2.592e+15

currentTime_nano := time.now_ns()
certsForRenewal := [expired |
date := dates[_]
certExpiryDate_nano := time.parse_rfc3339_ns(date)
timeDelta := certExpiryDate_nano - currentTime_nano
timeDelta <= thirty_days_in_nanoseconds
certs_for_renewal := [expired |
some date in dates
cert_expiry_date_nano := time.parse_rfc3339_ns(date)
time_delta := cert_expiry_date_nano - time.now_ns()
time_delta <= thirty_days_in_nanoseconds

expired := {
"date": date,
"expired": timeDelta <= thirty_days_in_nanoseconds,
}
]
expired := {
"date": date,
"expired": time_delta <= thirty_days_in_nanoseconds,
}
]
}

deny_certs_not_present[msg] {
exists := [certs |
certs := input.certs
] #you will need to provide a path to a cert
deny_certs_not_present contains msg if {
exists := [certs |
certs := input.certs
] # you will need to provide a path to a cert

count(exists) == 0
count(exists) == 0

msg = sprintf("No certs in provided, either in path or input object: %v", [exists])
msg = sprintf("No certs in provided, either in path or input object: %v", [exists])
}

deny_thirty_days[msg] {
# must manually define path to cert. JSON input
# key values are accessed using bracket notation rather than dot "." notation
certs := input.certs #you will need to provide a path to a cert
expirys := get_certificate_expiry(certs)
isExpired := determine_if_expired(expirys)
deny_thirty_days contains msg if {
# must manually define path to cert. JSON input
# key values are accessed using bracket notation rather than dot "." notation
certs := input.certs # you will need to provide a path to a cert
expirys := expiry(certs)
is_expired := determine_if_expired(expirys)

count(isExpired) > 0
count(is_expired) > 0

msg = sprintf("Your certificate expires on this date %v please update cert", [isExpired])
}
msg = sprintf("Your certificate expires on this date %v please update cert", [is_expired])
}
Loading

0 comments on commit b791362

Please sign in to comment.