Skip to content

Commit

Permalink
[Feature] Update to include latest Santa rule types of TeamID and `…
Browse files Browse the repository at this point in the history
…SigningID` support (#44)
  • Loading branch information
radsec authored Mar 30, 2024
1 parent 3893706 commit 7c425c1
Show file tree
Hide file tree
Showing 39 changed files with 598 additions and 415 deletions.
2 changes: 1 addition & 1 deletion examples/sample-rules.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sha256,type,policy,custom_msg,description
identifier,type,policy,custom_msg,description
d84db96af8c2e60ac4c851a21ec460f6f84e0235beb17d24a78712b9b021ed57,CERTIFICATE,ALLOWLIST,,"Software Signing by Apple Inc."
d292f56f78effeb715382f3578b3716309da04e31589b23b68c3750edd526660,CERTIFICATE,ALLOWLIST,,"Developer ID Application: Zoom Video Communications, Inc. (BJ4HAAB9B3)"
96f18e09d65445985c7df5df74ef152a0bc42e8934175a626180d9700c343e7b,CERTIFICATE,ALLOWLIST,,"Developer ID Application: Mozilla Corporation (43AQ936H96)"
Expand Down
4 changes: 2 additions & 2 deletions examples/sample-rules2.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[
{
"sha256": "d84db96af8c2e60ac4c851a21ec460f6f84e0235beb17d24a78712b9b021ed57",
"identifier": "d84db96af8c2e60ac4c851a21ec460f6f84e0235beb17d24a78712b9b021ed57",
"type": "CERTIFICATE",
"policy": "ALLOWLIST",
"custom_msg": "",
"description": "Software Signing by Apple Inc."
},
{
"sha256": "345a8e098bd04794aaeefda8c9ef56a0bf3d3706d67d35bc0e23f11bb3bffce5",
"identifier": "345a8e098bd04794aaeefda8c9ef56a0bf3d3706d67d35bc0e23f11bb3bffce5",
"type": "CERTIFICATE",
"policy": "ALLOWLIST",
"custom_msg": "",
Expand Down
6 changes: 6 additions & 0 deletions internal/cli/flags/client_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import (
"github.com/airbnb/rudolph/pkg/types"
)

const (
monitorMode = "monitor"
lockdownMode = "lockdown"
defaultClientMode = "monitor"
)

// configMode is a custom type for use as a CLI flag representing the type of config mode being applied
type ClientMode types.ClientMode

Expand Down
20 changes: 10 additions & 10 deletions internal/cli/flags/rule_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ package flags
import "github.com/spf13/cobra"

type RuleInfoFlags struct {
RuleType *RuleType
SHA256 *string
FilePath *string
RuleType *RuleType
Identifier *string
FilePath *string
}

func (r *RuleInfoFlags) AddRuleInfoFlags(cmd *cobra.Command) {
var (
ruleTypeArg RuleType
sha256Arg string
filepathArg string
ruleTypeArg RuleType
identifierArg string
filepathArg string
)

// Flag specifying the binary
cmd.Flags().StringVarP(&filepathArg, "filepath", "f", "", `The filepath of a binary. Provide exactly one of [--filepath|--sha]`)
cmd.Flags().StringVarP(&sha256Arg, "sha", "s", "", `The sha256 of a file`)
cmd.Flags().StringVarP(&filepathArg, "filepath", "f", "", `The filepath of a binary/application. Provide exactly one of [--filepath|--sha]`)
cmd.Flags().StringVarP(&identifierArg, "identifier", "i", "", `The Identifier/SHA256 for a file, application, teamID, or signingID`)

// rule-type should be one of "binary" or "cert" ("bin" and "certificate" also work)
cmd.Flags().VarP(&ruleTypeArg, "rule-type", "t", `type of rule being applied. valid options are: "binary", "bin", "certificate", or "cert"`)
cmd.Flags().VarP(&ruleTypeArg, "rule-type", "t", `type of rule being applied. valid options are: "binary", "bin", "certificate", "cert", "teamid", "signingid"`)
_ = cmd.MarkFlagRequired("rule-type")

// If we want to make the `rule-type` flag optional with a default (say "binary"),
Expand All @@ -30,6 +30,6 @@ func (r *RuleInfoFlags) AddRuleInfoFlags(cmd *cobra.Command) {
// rule-policy is to specify the policy for edit commands

r.RuleType = &ruleTypeArg
r.SHA256 = &sha256Arg
r.Identifier = &identifierArg
r.FilePath = &filepathArg
}
24 changes: 16 additions & 8 deletions internal/cli/flags/rule_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ package flags

import (
"fmt"
"strings"

"github.com/airbnb/rudolph/pkg/types"
)

const (
binType = "binary"
binTypeShort = "bin"
certType = "certificate"
certTypeShort = "cert"
monitorMode = "monitor"
lockdownMode = "lockdown"
defaultClientMode = "monitor"
binType = "binary"
binTypeShort = "bin"
certType = "certificate"
certTypeShort = "cert"
teamIDType = "teamid"
signingIDType = "signingid"
)

// ruleType is a custom type for use as a CLI flag representing the type of rule being applied
Expand All @@ -34,11 +34,15 @@ func (i *RuleType) AsRuleType() types.RuleType {
}

func (i *RuleType) Set(s string) error {
switch s {
switch strings.ToLower(s) {
case binType, binTypeShort:
*i = RuleType(types.RuleTypeBinary)
case certType, certTypeShort:
*i = RuleType(types.RuleTypeCertificate)
case teamIDType:
*i = RuleType(types.RuleTypeTeamID)
case signingIDType:
*i = RuleType(types.RuleTypeSigningID)
default:
return fmt.Errorf(`invalid rule type; must be "binary" or "cert"`)
}
Expand All @@ -56,6 +60,10 @@ func (i *RuleType) String() string {
return binType
case types.RuleTypeCertificate:
return certType
case types.RuleTypeTeamID:
return teamIDType
case types.RuleTypeSigningID:
return signingIDType
}

// No default
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/flags/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (t *TargetFlags) AddTargetFlags(cmd *cobra.Command) {
}

func (t *TargetFlags) AddTargetFlagsRules(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&t.IsGlobal, "global", "g", false, "Retrive rules that apply globally.")
cmd.Flags().BoolVarP(&t.IsGlobal, "global", "g", false, "Retrieve rules that apply globally.")
cmd.Flags().StringVarP(&t.MachineID, "machine", "m", "", "Retrieve rules for a single machine. Omit to apply to the current machine.")

}
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/rule/rule-allow.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func init() {
rf := flags.RuleInfoFlags{}

var ruleAllowCmd = &cobra.Command{
Use: "allow [-f <file-path>|-s <sha>] -t <rule-type> [-m <machine-id>|--global]",
Use: "allow [-f <file-path>|-i <identifier/sha256>] -t <rule-type> [-m <machine-id>|--global]",
Short: "Create a rule that applies the Allowlist policy to the specified file",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
37 changes: 24 additions & 13 deletions internal/cli/rule/rule-common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rule
import (
"bufio"
"fmt"
"log"
"os"
"strings"
"time"
Expand All @@ -28,19 +29,23 @@ var (
)

func applyPolicyForPath(timeProvider clock.TimeProvider, client dynamodb.DynamoDBClient, policy types.Policy, tf flags.TargetFlags, rf flags.RuleInfoFlags) (err error) {
// Second, determine the rule type and sha256
// Second, determine the rule type and identifier
ruleType := (*rf.RuleType).AsRuleType()
var description string
var sha256 string
var identifier string

if *rf.FilePath != "" {
fileInfo, err := santa_sensor.RunSantaFileInfo(*rf.FilePath)
if err != nil {
return fmt.Errorf("encountered an error while attempting to get file information for %q", *rf.FilePath)
}

sha256 = fileInfo.SHA256
identifier = fileInfo.SHA256
description = fmt.Sprintf("%s from %s", fileInfo.Path, tf.SelfMachineID) // FIXME (derek.wang) tf.SelfMachineID is Not initialized.
if ruleType == types.Certificate {

switch ruleType {
case types.RuleTypeBinary:
break
case types.RuleTypeCertificate:
if len(fileInfo.SigningChain) == 0 {
return fmt.Errorf("NO SIGNING INFO FOUND FOR GIVEN BINARY")
}
Expand All @@ -51,12 +56,18 @@ func applyPolicyForPath(timeProvider clock.TimeProvider, client dynamodb.DynamoD
return fmt.Errorf("NO CERTIFICATE NAME FOUND FOR GIVEN BINARY")
}

sha256 = fileInfo.SigningChain[0].SHA256
identifier = fileInfo.SigningChain[0].SHA256
description = fmt.Sprintf("%v, by %v (%v)", fileInfo.SigningChain[0].CommonName, fileInfo.SigningChain[0].Organization, fileInfo.SigningChain[0].OrganizationalUnit)
case types.RuleTypeTeamID:
identifier = fileInfo.TeamID
case types.RuleTypeSigningID:
identifier = fileInfo.SigningID
default:
log.Printf("error (recovered): encountered unknown ruleType: (%+v)", ruleType)
return fmt.Errorf("error (recovered): encountered unknown ruleType: (%+v)", ruleType)
}

} else if *rf.SHA256 != "" {
sha256 = *rf.SHA256
} else if *rf.Identifier != "" {
identifier = *rf.Identifier
}

// TODO
Expand Down Expand Up @@ -93,7 +104,7 @@ func applyPolicyForPath(timeProvider clock.TimeProvider, client dynamodb.DynamoD

fmt.Println("Uploading the following rule:")
fmt.Println(" MachineID: ", machineID, suffix)
fmt.Println(" SHA256: ", sha256)
fmt.Println(" Identifier/SHA256: ", identifier)
fmt.Println(" Policy: ", policy, " (", string(policyDescription), ")")
fmt.Println(" RuleType: ", ruleType, " (", string(ruleTypeDescription), ")")
fmt.Println(" Description: ", description)
Expand All @@ -105,13 +116,13 @@ func applyPolicyForPath(timeProvider clock.TimeProvider, client dynamodb.DynamoD
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
if text == "ok" || text == "yes" {
if strings.ToLower(text) == "ok" || strings.ToLower(text) == "yes" {
// Do rule creation
if tf.IsGlobal {
err = globalrules.AddNewGlobalRule(timeProvider, client, sha256, ruleType, policy, description)
err = globalrules.AddNewGlobalRule(timeProvider, client, identifier, ruleType, policy, description)
} else {
expires := timeProvider.Now().Add(time.Hour * machinerules.MachineRuleDefaultExpirationHours).UTC()
err = machinerules.AddNewMachineRule(client, machineID, sha256, ruleType, policy, description, expires)
err = machinerules.AddNewMachineRule(client, machineID, identifier, ruleType, policy, description, expires)
}
if err != nil {
return fmt.Errorf("could not upload rule to DynamoDB: %w", err)
Expand Down
54 changes: 47 additions & 7 deletions internal/cli/rule/rule-remove.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package rule

import (
"bufio"
"fmt"
"os"
"strings"

"github.com/airbnb/rudolph/internal/cli/flags"
"github.com/airbnb/rudolph/pkg/clock"
Expand All @@ -18,9 +21,10 @@ func init() {
tf := flags.TargetFlags{}

var removeRuleCmd = &cobra.Command{
Use: "remove <rule-name>",
Use: `remove <rule-name> ex: 'TeamID#1234567'`,
Aliases: []string{"delete"},
Short: "Removes/deletes a rule from the backing store",
Long: `<rule-name> | <RuleType: Binary,Certificate,TeamID,SigningID>#<Rule Identifier/SHA256: abcdef12345-12345-12345> | 'TeamID#1234567'`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
region, _ := cmd.Flags().GetString("region")
Expand All @@ -47,15 +51,51 @@ func init() {
}

func removeRule(globalRemover globalrules.RuleRemovalService, machineRuleRemover machinerules.RuleRemovalService, ruleName string, tf flags.TargetFlags) error {
if !tf.IsGlobal {
machineID, err := tf.GetMachineID()
// First, determine which machine to apply
var machineID string
if !tf.IsGlobal || tf.IsTargetSelf() {
var err error
machineID, err = tf.GetMachineID()
if err != nil {
return fmt.Errorf("failed to get MachineID: %v", err)
return fmt.Errorf("failed to get MachineID: %w", err)
}
return machineRuleRemover.RemoveMachineRule(machineID, ruleName)
}

idempotencyKey := uuid.NewString()
fmt.Println("Removing the following rule:")
if machineID != "" {
fmt.Println(" MachineID: ", machineID)
}
fmt.Println(" Identifier/SHA256: ", ruleName)
fmt.Println("")
fmt.Println(`Apply changes? (Enter: "yes" or "ok")`)
fmt.Print("> ")

// Read confirmation
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
if strings.ToLower(text) == "ok" || strings.ToLower(text) == "yes" {
// Do rule deletion
if !tf.IsGlobal {
machineID, err := tf.GetMachineID()
if err != nil {
return fmt.Errorf("failed to get MachineID: %v", err)
}
return machineRuleRemover.RemoveMachineRule(machineID, ruleName)
}

idempotencyKey := uuid.NewString()

err := globalRemover.RemoveGlobalRule(ruleName, idempotencyKey)
if err != nil {
return fmt.Errorf("failed to remove global rule: %v", err)
}

fmt.Println("Successfully sent a rule to dynamodb")
} else {
fmt.Println("Well ok then")
}
fmt.Println("")

return globalRemover.RemoveGlobalRule(ruleName, idempotencyKey)
return nil
}
Loading

0 comments on commit 7c425c1

Please sign in to comment.