initial commit
This commit is contained in:
192
policy-allow.go
Normal file
192
policy-allow.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
package libpolicy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func contains(slice []string, val string) bool {
|
||||||
|
for _, v := range slice {
|
||||||
|
if v == val {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Effect uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
Allow Effect = iota
|
||||||
|
Deny
|
||||||
|
NoMatch
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsIPInCIDR(ipStr, cidrStr string) bool {
|
||||||
|
// Parse the IP address
|
||||||
|
cidrStr = normalizeCIDR(cidrStr)
|
||||||
|
log.Println("Parsing IP:", ipStr, " against CIDR:", cidrStr)
|
||||||
|
ip := net.ParseIP(ipStr)
|
||||||
|
if ip == nil {
|
||||||
|
return false // Invalid IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the CIDR
|
||||||
|
_, ipnet, err := net.ParseCIDR(cidrStr)
|
||||||
|
if err != nil {
|
||||||
|
return false // Invalid CIDR
|
||||||
|
}
|
||||||
|
|
||||||
|
// net.IPNet.Contains() handles IPv4/IPv6 correctly, including IPv4-mapped IPv6
|
||||||
|
return ipnet.Contains(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeCIDR(cidrStr string) string {
|
||||||
|
cidrStr = strings.TrimSpace(cidrStr)
|
||||||
|
if cidrStr == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already has a mask suffix → use as-is
|
||||||
|
if strings.Contains(cidrStr, "/") {
|
||||||
|
return cidrStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse as IP address to determine version
|
||||||
|
addr, err := netip.ParseAddr(cidrStr)
|
||||||
|
if err != nil {
|
||||||
|
return "" // Invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.Is4() || addr.Is4In6() {
|
||||||
|
return cidrStr + "/32"
|
||||||
|
}
|
||||||
|
return cidrStr + "/128"
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyRequest(request *Request, policy *Policy) (Effect, string) {
|
||||||
|
//conditionReview = []Condition{}
|
||||||
|
var effect Effect
|
||||||
|
effect = NoMatch
|
||||||
|
statementid := "NoMatch"
|
||||||
|
|
||||||
|
for _, condition := range policy.Conditions {
|
||||||
|
|
||||||
|
log.Println("Checking Statement: ", condition.StatementID)
|
||||||
|
|
||||||
|
// Check the principal
|
||||||
|
if !contains(condition.Principal, request.Principal) {
|
||||||
|
log.Println("No match on principal")
|
||||||
|
log.Println("Checked condition principal:", condition.Principal)
|
||||||
|
log.Println("Against: request principal:", request.Principal)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !contains(condition.Actions, request.Action) {
|
||||||
|
log.Println("No match on actions")
|
||||||
|
log.Println("Checked condition actions:", condition.Actions)
|
||||||
|
log.Println("Against: request action:", request.Action)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source Check
|
||||||
|
sourceMatch := false
|
||||||
|
for _, source := range condition.Source {
|
||||||
|
if IsIPInCIDR(request.SourceIPAddress, source) {
|
||||||
|
sourceMatch = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceMatch == false {
|
||||||
|
log.Println("No match on source")
|
||||||
|
log.Println("Checked condition source:", condition.Source)
|
||||||
|
log.Println("Against: request source:", request.SourceIPAddress)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if condition.Effect == "allow" {
|
||||||
|
effect = Allow
|
||||||
|
log.Println("Allowed per ")
|
||||||
|
log.Printf("Request is allowed by condition: %s\n", condition.StatementID)
|
||||||
|
effect = Allow
|
||||||
|
statementid = condition.StatementID
|
||||||
|
} else if condition.Effect == "deny" {
|
||||||
|
// Immediately deny upon explicit deny
|
||||||
|
log.Printf("Request is denied by condition: %s\n", condition.StatementID)
|
||||||
|
return Deny, condition.StatementID
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return effect, statementid
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func loadRequestFile(filename string) (*Request, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open '%s' %w", filename, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var request Request
|
||||||
|
if err := json.NewDecoder(file).Decode(&request); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse json from '%s': %w", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Successfully loaded request from %s\n", filename)
|
||||||
|
return &request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPolicyFile(filename string) (*Policy, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file '%s': %w", filename, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var policy Policy
|
||||||
|
if err := json.NewDecoder(file).Decode(&policy); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse JSON from '%s': %w", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Successfully loaded policy from %s\n", filename)
|
||||||
|
return &policy, nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
func main() {
|
||||||
|
policy, err := loadPolicyFile("policy.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Policy structure loaded successfully:")
|
||||||
|
pretty, _ := json.MarshalIndent(policy, "", "\t")
|
||||||
|
fmt.Println(string(pretty))
|
||||||
|
|
||||||
|
request, err := loadRequestFile("request.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Loaded Request successfully\n")
|
||||||
|
pretty, _ = json.MarshalIndent(request, "", "\t")
|
||||||
|
fmt.Println(string(pretty))
|
||||||
|
|
||||||
|
effect, statementid := verifyRequest(request, policy)
|
||||||
|
|
||||||
|
fmt.Println("")
|
||||||
|
if effect == NoMatch {
|
||||||
|
fmt.Println("No matching conditions found. Defaulting to Deny.")
|
||||||
|
} else if effect == Allow {
|
||||||
|
fmt.Println("Request is allowed based on matching conditions.")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Request is denied based on matching conditions.")
|
||||||
|
}
|
||||||
|
println("statementid:", statementid)
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
25
structures.go
Normal file
25
structures.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package libpolicy
|
||||||
|
|
||||||
|
// Policy represents the top-level structure of your custom policy.json
|
||||||
|
type Policy struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Conditions []Condition `json:"conditions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condition represents each item in the "conditions" array
|
||||||
|
type Condition struct {
|
||||||
|
StatementID string `json:"statementid"`
|
||||||
|
Principal []string `json:"principals"`
|
||||||
|
Actions []string `json:"actions"`
|
||||||
|
Source []string `json:"source"`
|
||||||
|
Effect string `json:"effect"`
|
||||||
|
Operator string `json:"operator"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Principal string `json:"principal"`
|
||||||
|
SourceIPAddress string `json:"source_ipaddress"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
Target string `json:"target"`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user