Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Update to include latest Santa rule types of TeamID and SigningID support #44

Merged
merged 4 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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