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

GCP Service Account Credential as docker env variable for cloud scanner #32

Merged
merged 4 commits into from
Oct 10, 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
6 changes: 5 additions & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ services:
# AZURE_CLIENT_SECRET: ""
# AZURE_SUBSCRIPTION_ID: ""

# Provide base64 encoded Service Account Keys for GCP Scanner
# GCP_SERVICE_ACCOUNT_CREDENTIAL: ""

DEPLOYMENT_MODE: "docker"
HOME_DIR: "/home/deepfence"
DF_INSTALL_DIR: "/data/home/deepfence"
Expand All @@ -73,4 +76,5 @@ services:

volumes:
cloud_scanner_data:
driver: local
driver: local

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 @@ -96,6 +96,7 @@ env_vars: {}
# AZURE_CLIENT_ID :
# AZURE_CLIENT_SECRET:
# AZURE_SUBSCRIPTION_ID:
# GCP_SERVICE_ACCOUNT_CREDENTIAL:

imagePullSecrets: []
nameOverride: ""
Expand Down
106 changes: 85 additions & 21 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package service

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"net"
Expand All @@ -25,6 +26,7 @@ import (
"github.com/deepfence/cloud-scanner/scanner"
"github.com/deepfence/cloud-scanner/util"
"google.golang.org/api/cloudresourcemanager/v1"
"google.golang.org/api/option"
)

var (
Expand Down Expand Up @@ -167,40 +169,67 @@ func (c *ComplianceScanService) fetchGCPOrganizationProjects() ([]util.AccountsT
return nil, err
}

c.organizationAccountIDsLock.Lock()
if c.config.IsOrganizationDeployment {
c.organizationAccountIDsLock.Lock()

accountsMap := make(map[string]struct{}, len(c.organizationAccountIDs))
for _, account := range c.organizationAccountIDs {
accountsMap[account.AccountID] = struct{}{}
}
accountsMap := make(map[string]struct{}, len(c.organizationAccountIDs))
for _, account := range c.organizationAccountIDs {
accountsMap[account.AccountID] = struct{}{}
}

var newAccounts []util.AccountsToRefresh
for _, account := range projects {
if _, ok := accountsMap[account.AccountID]; !ok {
newAccounts = append(newAccounts, util.AccountsToRefresh{
AccountID: account.AccountID,
NodeID: account.NodeID,
})
var newAccounts []util.AccountsToRefresh
for _, account := range projects {
if _, ok := accountsMap[account.AccountID]; !ok {
newAccounts = append(newAccounts, util.AccountsToRefresh{
AccountID: account.AccountID,
NodeID: account.NodeID,
})
}
}
}

c.organizationAccountIDs = projects
c.organizationAccountIDsLock.Unlock()
c.organizationAccountIDs = projects
c.organizationAccountIDsLock.Unlock()

return newAccounts, nil
return newAccounts, nil
}

return nil, nil
}

func (c *ComplianceScanService) fetchGCPProjects() ([]util.MonitoredAccount, error) {
log.Info().Msg("Fetching GCP projects")
ctx := context.Background()
crm, err := cloudresourcemanager.NewService(ctx)
if err != nil {
log.Error().Err(err).Msg("failed to fetch GCP projects")
return nil, err

var crm *cloudresourcemanager.Service
var err error

if c.config.GCPCredentials != "" && strings.TrimSpace(c.config.GCPCredentials) != "" {
// Save the GCP credentials to a file
credentialFilePath, err := saveGCPCredentialsToFile(c.config.GCPCredentials)
if err != nil {
log.Error().Err(err).Msg("Failed to save GCP credentials to file, falling back to default authentication")
} else {
log.Info().Msgf("GCP credentials saved to file at: %s", credentialFilePath)
clientOption := option.WithCredentialsFile(credentialFilePath)
crm, err = cloudresourcemanager.NewService(ctx, clientOption)
if err != nil {
log.Error().Err(err).Msg("Failed to create GCP client with provided credentials, falling back to default authentication")
}
}
}

if crm == nil {
crm, err = cloudresourcemanager.NewService(ctx)
if err != nil {
log.Error().Err(err).Msg("Failed to create GCP client with default authentication")
return nil, err
}
}

projectsRequest := crm.Projects.List().PageSize(1000)
projectsResponse, err := projectsRequest.Do()
if err != nil {
log.Error().Err(err).Msg("failed to fetch GCP projects")
log.Error().Err(err).Msg("Failed to fetch GCP projects")
return nil, err
}

Expand All @@ -212,9 +241,37 @@ func (c *ComplianceScanService) fetchGCPProjects() ([]util.MonitoredAccount, err
NodeID: util.GetNodeID(c.config.CloudProvider, project.ProjectId),
}
}

return organizationAccountIDs, nil
}


func saveGCPCredentialsToFile(credentials string) (string, error) {

configDir := util.HomeDirectory+"/.config/gcloud/"
credentialFilePath := configDir + "application_default_credentials.json"

// Check if the directory exists, create it if not
if _, err := os.Stat(configDir); os.IsNotExist(err) {
err = os.MkdirAll(configDir, 0700)
if err != nil {
return "", fmt.Errorf("failed to create directory: %w", err)
}
}

credBytes, err := base64.StdEncoding.DecodeString(credentials)
if err != nil {
return "", fmt.Errorf("failed to decode GCP credentials: %w", err)
}

err = os.WriteFile(credentialFilePath, credBytes, 0600)
if err != nil {
return "", fmt.Errorf("failed to write credentials to file: %w", err)
}

return credentialFilePath, nil
}

func (c *ComplianceScanService) fetchAzureTenantSubscriptions() ([]util.MonitoredAccount, error) {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
Expand Down Expand Up @@ -351,6 +408,12 @@ func (c *ComplianceScanService) RunRegisterServices() error {
return err
}
}
}else {
// Handle individual project credentials
if _, err := c.fetchGCPOrganizationProjects(); err != nil {
log.Error().Msg(err.Error())
return err
}
}
processGcpCredentials(c)
case util.CloudProviderAzure:
Expand Down Expand Up @@ -770,3 +833,4 @@ func (c *ComplianceScanService) handleRequest(conn net.Conn) {
}
}
}

2 changes: 2 additions & 0 deletions util/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type Config struct {
ScanInactiveThreshold int `envconfig:"SCAN_INACTIVE_THRESHOLD" default:"21600" json:"scan_inactive_threshold"`
CloudScannerPolicy string `envconfig:"CLOUD_SCANNER_POLICY" json:"cloud_scanner_policy"`
DeploymentMode string `envconfig:"DEPLOYMENT_MODE" json:"deployment_mode"`
GCPCredentials string `envconfig:"GCP_SERVICE_ACCOUNT_CREDENTIAL" json:"gcp_service_account_credential"`

CloudMetadata cloudmetadata.CloudMetadata `ignored:"true" json:"cloud_metadata"`
NodeID string `ignored:"true" json:"-"`
Expand Down Expand Up @@ -200,3 +201,4 @@ func init() {
SteampipeInstallDirectory = "/home/deepfence/.steampipe"
}
}