Skip to content

Commit

Permalink
Azure tenant - multiple subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
ramanan-ravi committed Jun 18, 2024
1 parent 7d56bcc commit 1915ee8
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (c *CloudResourceChangesAWS) listAndProcessS3Objects(regionalFilePrefix str
EncodingType: aws.String("url"),
Prefix: aws.String(regionalFilePrefix),
}
if accId != c.config.CloudMetadata.ID {
if accId != c.config.AccountID {
params = params.SetExpectedBucketOwner(accId)
}
err = svc.ListObjectsV2Pages(params, func(resp *s3.ListObjectsV2Output, lastPage bool) bool {
Expand Down Expand Up @@ -257,7 +257,7 @@ func (c *CloudResourceChangesAWS) processCloudtrailEventLogFile(fileName string,
Bucket: aws.String(s3Bucket),
Key: aws.String(*key.Key),
}
if accId != c.config.CloudMetadata.ID {
if accId != c.config.AccountID {
s3ObjectInput.SetExpectedBucketOwner(accId)
}
_, err = downloader.Download(file, &s3ObjectInput)
Expand Down
2 changes: 2 additions & 0 deletions helm-chart/deepfence-cloud-scanner/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ spec:
value: "{{ .Values.cloudAccount.region }}"
- name: CLOUD_ACCOUNT_ID
value: "{{ .Values.cloudAccount.accountID }}"
- name: CLOUD_ORGANIZATION_ID
value: "{{ .Values.cloudAccount.organizationAccountID }}"
- name: ORGANIZATION_DEPLOYMENT
value: "{{ .Values.cloudAccount.isOrganizationDeployment }}"
- name: ROLE_NAME
Expand Down
1 change: 1 addition & 0 deletions helm-chart/deepfence-cloud-scanner/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ cloudAccount:
region: ""
# Is this organization deployment or single account deployment?
isOrganizationDeployment: false
organizationAccountID: ""
# Role name. The name should be same across all accounts in the Organization deployment.
# Role ARN example: arn:aws:iam::123456789012:role/deepfence-managed-cloud-scanner-role
# Role name in this case is deepfence-managed-cloud-scanner-role
Expand Down
6 changes: 3 additions & 3 deletions helm-chart/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ entries:
deepfence-cloud-scanner:
- apiVersion: v2
appVersion: 2.3.0
created: "2024-06-06T11:06:36.139253+05:30"
created: "2024-06-18T19:12:20.637723+05:30"
description: Deepfence Cloud Scanner
digest: f4a52c6d6bced63954c001dd534fc8706bfd504b8defbd32e21c07bd89f02c57
digest: f60582daf0ba69673788177432defc318b61f65bc1c86c334b38db2ba8fb3818
name: deepfence-cloud-scanner
type: application
urls:
- deepfence-cloud-scanner-1.0.0.tgz
version: 1.0.0
generated: "2024-06-06T11:06:36.138791+05:30"
generated: "2024-06-18T19:12:20.636193+05:30"
6 changes: 3 additions & 3 deletions internal/deepfence/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (c *Client) RegisterCloudAccount(monitoredAccountIDs []string) error {
IsOrganizationDeployment: &c.config.IsOrganizationDeployment,
MonitoredAccountIds: monitoredAccounts,
NodeId: nodeId,
OrganizationAccountId: &c.config.AccountID,
OrganizationAccountId: &c.config.OrganizationID,
Version: c.config.Version,
},
)
Expand All @@ -93,14 +93,14 @@ func (c *Client) RegisterCloudAccount(monitoredAccountIDs []string) error {
)
}

log.Debug().Msgf("Before CloudNodesAPI.RegisterCloudNodeAccountExecute")
log.Debug().Msgf("Registering on management console")
_, err := c.client.Client().CloudNodesAPI.RegisterCloudNodeAccountExecute(req)
if err != nil {
log.Error().Msgf("Request errored on registering on management console: %s", err.Error())
return err
}

log.Info().Msgf("RegisterCloudAccount complete")
log.Info().Msgf("Register cloud account complete")
return nil
}

Expand Down
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ func main() {
deepfence.SendSuccessfulDeploymentSignal(config.SuccessSignalUrl)
}

if config.IsOrganizationDeployment {
if config.OrganizationID == "" {
log.Fatal().Msgf("ORGANIZATION_ID is required in organization deployment")
}
}

switch config.CloudProvider {
case util.CloudProviderAWS:
if config.AWSCredentialSource != "EcsContainer" && config.AWSCredentialSource != "Ec2InstanceMetadata" && config.AWSCredentialSource != "Environment" {
Expand Down
20 changes: 2 additions & 18 deletions query_resource/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,7 @@ func QueryAndUpdateResources(config util.Config, cloudResourceTypesToRefresh map
count := 0
var errs = make([]error, 0)
for accountID, resourceTypesToRefresh := range cloudResourceTypesToRefresh {
accountIDPrefix := ""
if accountID != config.CloudMetadata.ID {
accountIDPrefix = config.CloudProvider + "_" + accountID + "."
}
accountIDPrefix := config.CloudProvider + "_" + accountID + "."

for _, cloudResourceInfo := range cloudProviderToResourceMap[config.CloudProvider] {
if !util.InSlice(cloudResourceInfo.Table, resourceTypesToRefresh) {
Expand All @@ -161,20 +158,7 @@ func QueryAndUpdateResources(config util.Config, cloudResourceTypesToRefresh map
func queryResources(accountId string, cloudResourceInfo CloudResourceInfo, config util.Config, cloudResourcesFile *os.File) (int, error) {
log.Debug().Msgf("Querying resources for %s", cloudResourceInfo.Table)

var query string
switch config.CloudProvider {
case util.CloudProviderAWS:
query = "steampipe query --output json \"select \\\"" + strings.Join(cloudResourceInfo.Columns[:], "\\\" , \\\"") + "\\\" from aws_" + accountId + "." + cloudResourceInfo.Table + " \""
case util.CloudProviderGCP:
if config.IsOrganizationDeployment {
query = "steampipe query --output json \"select \\\"" + strings.Join(cloudResourceInfo.Columns[:], "\\\" , \\\"") + "\\\" from gcp_" + strings.Replace(accountId, "-", "", -1) + "." + cloudResourceInfo.Table + " \""
} else {
query = "steampipe query --output json \"select \\\"" + strings.Join(cloudResourceInfo.Columns[:], "\\\" , \\\"") + "\\\" from " + cloudResourceInfo.Table + " \""
}
default:
query = "steampipe query --output json \"select \\\"" + strings.Join(cloudResourceInfo.Columns[:], "\\\" , \\\"") + "\\\" from " + cloudResourceInfo.Table + " \""
}

query := "steampipe query --output json \"select \\\"" + strings.Join(cloudResourceInfo.Columns[:], "\\\" , \\\"") + "\\\" from " + config.CloudProvider + "_" + strings.Replace(accountId, "-", "", -1) + "." + cloudResourceInfo.Table + " \""
var stdOut []byte
var stdErr error
for i := 0; i <= 3; i++ {
Expand Down
58 changes: 1 addition & 57 deletions scanner/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,70 +68,14 @@ func NewCloudComplianceScan(config util.Config) (*CloudComplianceScan, error) {
}, nil
}

func (c *CloudComplianceScan) RunComplianceScan() (util.ComplianceGroup, error) {
tempFileName := fmt.Sprintf("/tmp/%s.json", util.RandomString(12))
defer os.Remove(tempFileName)
cmd := fmt.Sprintf("cd %s && steampipe check --progress=false --output=none --export=%s %s", cloudProviderPath[c.CloudProvider], tempFileName, c.ComplianceBenchmark)

var stdOut []byte
var stdErr error
for i := 0; i <= 3; i++ {
stdOut, stdErr = exec.Command("bash", "-c", cmd).CombinedOutput()
if stdErr != nil {
log.Error().Msgf("Steampipe check error: %v for query: %s", stdErr, cmd)
log.Error().Msgf(string(stdOut))
if strings.Contains(string(stdOut), util.ErrSteampipeDB) || strings.Contains(string(stdOut), util.ErrSteampipeInvalidClientTokenID) {
util.RestartSteampipeService()
} else {
time.Sleep(util.SleepTime)
}
os.Remove(tempFileName)
continue
} else {
break
}
}

var complianceResults util.ComplianceGroup
if _, err := os.Stat(tempFileName); errors.Is(err, os.ErrNotExist) {
return complianceResults, fmt.Errorf("%s: %v", stdOut, stdErr)
}
tempFile, err := os.Open(tempFileName)
if err != nil {
return complianceResults, err
}
results, err := io.ReadAll(tempFile)
if err != nil {
return complianceResults, err
}
err = json.Unmarshal(results, &complianceResults)
if err != nil {
return complianceResults, err
}
return complianceResults, nil
}

func (c *CloudComplianceScan) RunComplianceScanBenchmark(ctx context.Context,
benchmark ctl.CloudComplianceScanBenchmark, accountId string) (*util.ComplianceGroup, error) {

tempFileName := fmt.Sprintf("/tmp/%s.json", util.RandomString(12))
defer os.Remove(tempFileName)
log.Debug().Msgf("Account ID: %s, config cloud metadata id: %s", accountId, c.CloudMetadata.ID)

var cmdStr string
switch c.CloudProvider {
case util.CloudProviderAWS:
cmdStr = fmt.Sprintf("cd %s && steampipe check --progress=false --output=none --search-path=%s_%s --export=%s %s", cloudProviderPath[c.CloudProvider], c.CloudProvider, strings.Replace(accountId, "-", "", -1), tempFileName, benchmark.Id)
case util.CloudProviderGCP:
if c.IsOrganizationDeployment {
cmdStr = fmt.Sprintf("cd %s && steampipe check --progress=false --output=none --search-path=%s_%s --export=%s %s", cloudProviderPath[c.CloudProvider], c.CloudProvider, strings.Replace(accountId, "-", "", -1), tempFileName, benchmark.Id)
} else {
cmdStr = fmt.Sprintf("cd %s && steampipe check --progress=false --output=none --export=%s %s", cloudProviderPath[c.CloudProvider], tempFileName, benchmark.Id)
}
default:
cmdStr = fmt.Sprintf("cd %s && steampipe check --progress=false --output=none --export=%s %s", cloudProviderPath[c.CloudProvider], tempFileName, benchmark.Id)
}

cmdStr := fmt.Sprintf("cd %s && steampipe check --progress=false --output=none --search-path=%s_%s --export=%s %s", cloudProviderPath[c.CloudProvider], c.CloudProvider, strings.Replace(accountId, "-", "", -1), tempFileName, benchmark.Id)
log.Debug().Msgf("Steampipe command: %s", cmdStr)
cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr)
//cmd.Env = os.Environ()
Expand Down
103 changes: 56 additions & 47 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
ctl "github.com/deepfence/ThreatMapper/deepfence_utils/controls"
"github.com/deepfence/ThreatMapper/deepfence_utils/log"
cloud_metadata "github.com/deepfence/cloud-scanner/cloud-metadata"
"github.com/deepfence/cloud-scanner/cloud_resource_changes"
"github.com/deepfence/cloud-scanner/internal/deepfence"
"github.com/deepfence/cloud-scanner/query_resource"
Expand Down Expand Up @@ -147,7 +146,7 @@ func getAWSCredentialsConfig(ctx context.Context, accountID, region, roleName st
return cfg, err
}

func (c *ComplianceScanService) fetchOrganizationAccountIDs() error {
func (c *ComplianceScanService) fetchAWSOrganizationAccountIDs() error {
organizationAccountIDs := []string{}

ctx := context.Background()
Expand Down Expand Up @@ -195,28 +194,27 @@ func (c *ComplianceScanService) RunRegisterServices() error {
if c.config.HttpServerRequired {
go c.runHttpServer()
}
if c.config.IsOrganizationDeployment {
err := c.fetchOrganizationAccountIDs()
if err != nil {
log.Warn().Msg(err.Error())
var err error
switch c.config.CloudProvider {
case util.CloudProviderAWS:
if c.config.IsOrganizationDeployment {
err = c.fetchAWSOrganizationAccountIDs()
if err != nil {
log.Warn().Msg(err.Error())
}
}
}
if c.config.CloudProvider == cloud_metadata.CloudProviderAWS {
processAwsCredentials(c)
} else if c.config.CloudProvider == cloud_metadata.CloudProviderGCP {
err := processGcpCredentials(c)
if err != nil {
log.Fatal().Msgf("%+v", err)
}
} else if c.config.CloudProvider == cloud_metadata.CloudProviderAzure {
processAzureCredentials()
case util.CloudProviderGCP:
processGcpCredentials(c)
case util.CloudProviderAzure:
processAzureCredentials(c)
}

log.Info().Msgf("Restarting the steampipe service")
util.RestartSteampipeService()

log.Info().Msgf("CloudResourceChanges Initialization started")
err := c.CloudResourceChanges.Initialize()
err = c.CloudResourceChanges.Initialize()
if err != nil {
log.Warn().Msgf("%+v", err)
}
Expand All @@ -238,14 +236,26 @@ func (c *ComplianceScanService) RunRegisterServices() error {
return nil
}

func processAzureCredentials() {
err := saveFileOverwrite(HomeDirectory+"/.steampipe/config/azure.spc",
"\nconnection \"azure\" {\n plugin = \"azure\"\n "+
" subscription_id = \""+os.Getenv("AZURE_SUBSCRIPTION_ID")+"\"\n"+
" tenant_id = \""+os.Getenv("AZURE_TENANT_ID")+"\"\n"+
" client_id = \""+os.Getenv("AZURE_CLIENT_ID")+"\"\n"+
" client_secret = \""+os.Getenv("AZURE_CLIENT_SECRET")+"\"\n"+
" ignore_error_codes = [\"AccessDenied\", \"AccessDeniedException\", \"NotAuthorized\", \"UnauthorizedOperation\", \"AuthorizationError\"]\n}\n")
func processAzureCredentials(c *ComplianceScanService) {
steampipeConfigFile := "connection \"azure_all\" {\n type = \"aggregator\" \n plugin = \"azure\" \n connections = [\"azure_*\"] \n} \n"
if c.config.IsOrganizationDeployment {
for _, accountID := range c.GetOrganizationAccountIDs() {
steampipeConfigFile += "\nconnection \"azure_" + strings.Replace(accountID, "-", "", -1) + "\" {\n plugin = \"azure\"\n " +
" subscription_id = \"" + accountID + "\"\n" +
" tenant_id = \"" + c.config.OrganizationID + "\"\n" +
" client_id = \"" + os.Getenv("AZURE_CLIENT_ID") + "\"\n" +
" client_secret = \"" + os.Getenv("AZURE_CLIENT_SECRET") + "\"\n" +
" ignore_error_codes = [\"AccessDenied\", \"AccessDeniedException\", \"NotAuthorized\", \"UnauthorizedOperation\", \"AuthorizationError\"]\n}\n"
}
} else {
steampipeConfigFile += "\nconnection \"azure_" + strings.Replace(c.config.AccountID, "-", "", -1) + "\" {\n plugin = \"azure\"\n " +
" subscription_id = \"" + c.config.AccountID + "\"\n" +
" tenant_id = \"" + c.config.OrganizationID + "\"\n" +
" client_id = \"" + os.Getenv("AZURE_CLIENT_ID") + "\"\n" +
" client_secret = \"" + os.Getenv("AZURE_CLIENT_SECRET") + "\"\n" +
" ignore_error_codes = [\"AccessDenied\", \"AccessDeniedException\", \"NotAuthorized\", \"UnauthorizedOperation\", \"AuthorizationError\"]\n}\n"
}
err := saveFileOverwrite(HomeDirectory+"/.steampipe/config/azure.spc", steampipeConfigFile)
if err != nil {
log.Fatal().Msgf(err.Error())
}
Expand Down Expand Up @@ -287,19 +297,19 @@ func processAwsCredentials(c *ComplianceScanService) {
}
}

func processGcpCredentials(c *ComplianceScanService) error {
organizationAccountIDs := c.GetOrganizationAccountIDs()
if len(organizationAccountIDs) > 0 {
steampipeConfigFile := "connection \"gcp_all\" {\n type = \"aggregator\" \n plugin = \"gcp\" \n connections = [\"gcp_*\"] \n} \n"
for _, accountID := range organizationAccountIDs {
func processGcpCredentials(c *ComplianceScanService) {
steampipeConfigFile := "connection \"gcp_all\" {\n type = \"aggregator\" \n plugin = \"gcp\" \n connections = [\"gcp_*\"] \n} \n"
if c.config.IsOrganizationDeployment {
for _, accountID := range c.GetOrganizationAccountIDs() {
steampipeConfigFile += "connection \"gcp_" + strings.Replace(accountID, "-", "", -1) + "\" {\n plugin = \"gcp\"\n project = \"" + accountID + "\"\n}\n"
}
err := saveFileOverwrite(HomeDirectory+"/.steampipe/config/gcp.spc", steampipeConfigFile)
if err != nil {
log.Fatal().Msgf(err.Error())
}
} else {
steampipeConfigFile += "connection \"gcp_" + strings.Replace(c.config.AccountID, "-", "", -1) + "\" {\n plugin = \"gcp\"\n project = \"" + c.config.AccountID + "\"\n}\n"
}
err := saveFileOverwrite(HomeDirectory+"/.steampipe/config/gcp.spc", steampipeConfigFile)
if err != nil {
log.Fatal().Msgf(err.Error())
}
return nil
}

func saveFileOverwrite(fileName string, fileContents string) error {
Expand All @@ -321,20 +331,19 @@ func (c *ComplianceScanService) refreshOrganizationAccountIDs() {
for {
select {
case <-ticker.C:
err := c.fetchOrganizationAccountIDs()
if err != nil {
log.Warn().Msg(err.Error())
continue
}
if c.config.CloudProvider == cloud_metadata.CloudProviderAWS {
processAwsCredentials(c)
} else if c.config.CloudProvider == cloud_metadata.CloudProviderGCP {
err := processGcpCredentials(c)
if err != nil {
log.Fatal().Msgf("%+v", err)
var err error
if c.config.CloudProvider == util.CloudProviderAWS {
if c.config.IsOrganizationDeployment {
err = c.fetchAWSOrganizationAccountIDs()
if err != nil {
log.Warn().Msg(err.Error())
}
}
} else if c.config.CloudProvider == cloud_metadata.CloudProviderAzure {
processAzureCredentials()
processAwsCredentials(c)
} else if c.config.CloudProvider == util.CloudProviderGCP {
processGcpCredentials(c)
} else if c.config.CloudProvider == util.CloudProviderAzure {
processAzureCredentials(c)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions util/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Config struct {
CloudProvider string `envconfig:"CLOUD_PROVIDER" json:"cloud_provider"`
CloudRegion string `envconfig:"CLOUD_REGION" json:"cloud_region"`
AccountID string `envconfig:"CLOUD_ACCOUNT_ID" json:"account_id"`
OrganizationID string `envconfig:"CLOUD_ORGANIZATION_ID" json:"organization_id"`
IsOrganizationDeployment bool `envconfig:"ORGANIZATION_DEPLOYMENT" default:"false" json:"is_organization_deployment"`
RoleName string `envconfig:"ROLE_NAME" json:"role_name"`
AWSCredentialSource string `envconfig:"AWS_CREDENTIAL_SOURCE" json:"aws_credential_source"`
Expand Down

0 comments on commit 1915ee8

Please sign in to comment.