Merge pull request #1902 from kubescape/copilot/fix-air-gapped-configuration

Fix air-gapped mode network access attempts
This commit is contained in:
Matthias Bertschy
2025-12-05 08:44:50 +01:00
committed by GitHub
3 changed files with 92 additions and 5 deletions
+5 -1
View File
@@ -90,7 +90,11 @@ func getResourceHandler(ctx context.Context, scanInfo *cautils.ScanInfo, tenantC
return resourcehandler.NewFileResourceHandler()
}
getter.GetKSCloudAPIConnector()
// Only initialize cloud connector if not in air-gapped mode
// This call initializes the global cloud API connector for later use
if !isAirGappedMode(scanInfo) {
_ = getter.GetKSCloudAPIConnector()
}
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, hostSensorHandler, rbacObjects, tenantConfig.GetContextName())
}
+24 -4
View File
@@ -66,9 +66,11 @@ func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInt
}
// ================== version testing ======================================
v := versioncheck.NewIVersionCheckHandler(ctx)
_ = v.CheckLatestVersion(ctx, versioncheck.NewVersionCheckRequest(scanInfo.AccountID, versioncheck.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", string(scanInfo.GetScanningContext()), k8sClient))
// Skip version check in air-gapped mode (when keep-local flag is set)
if !scanInfo.Local {
v := versioncheck.NewIVersionCheckHandler(ctx)
_ = v.CheckLatestVersion(ctx, versioncheck.NewVersionCheckRequest(scanInfo.AccountID, versioncheck.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", string(scanInfo.GetScanningContext()), k8sClient))
}
// ================== setup host scanner object ======================================
ctxHostScanner, spanHostScanner := otel.Tracer("").Start(ctx, "setup host scanner")
@@ -132,7 +134,15 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
interfaces := getInterfaces(ctxInit, scanInfo)
interfaces.report.SetTenantConfig(interfaces.tenantConfig)
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// Only create DownloadReleasedPolicy if not in air-gapped mode
var downloadReleasedPolicy *getter.DownloadReleasedPolicy
if isAirGappedMode(scanInfo) {
// In air-gapped mode (--keep-local or using local files via --use-from, --controls-config, --exceptions, or attack tracks),
// don't initialize the downloader to prevent network access
downloadReleasedPolicy = nil
} else {
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy() // download config inputs from github release
}
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(ctxInit, scanInfo.UseFrom, interfaces.tenantConfig.GetAccountID(), scanInfo.FrameworkScan, downloadReleasedPolicy)
@@ -274,3 +284,13 @@ func scanSingleImage(ctx context.Context, img string, svc *imagescan.Service, re
func isPrioritizationScanType(scanType cautils.ScanTypes) bool {
return scanType == cautils.ScanTypeCluster || scanType == cautils.ScanTypeRepo
}
// isAirGappedMode returns true if the scan is configured to run in air-gapped mode
// (i.e., without any network access to download policies, exceptions, or other artifacts)
func isAirGappedMode(scanInfo *cautils.ScanInfo) bool {
return scanInfo.Local ||
len(scanInfo.UseFrom) > 0 ||
scanInfo.ControlsInputs != "" ||
scanInfo.UseExceptions != "" ||
scanInfo.AttackTracks != ""
}
+63
View File
@@ -58,3 +58,66 @@ func TestIsPrioritizationScanType(t *testing.T) {
})
}
}
func TestIsAirGappedMode(t *testing.T) {
tests := []struct {
name string
scanInfo *cautils.ScanInfo
want bool
}{
{
name: "air-gapped with Local flag",
scanInfo: &cautils.ScanInfo{
Local: true,
},
want: true,
},
{
name: "air-gapped with UseFrom",
scanInfo: &cautils.ScanInfo{
UseFrom: []string{"/path/to/policy"},
},
want: true,
},
{
name: "air-gapped with ControlsInputs",
scanInfo: &cautils.ScanInfo{
ControlsInputs: "/path/to/controls",
},
want: true,
},
{
name: "air-gapped with UseExceptions",
scanInfo: &cautils.ScanInfo{
UseExceptions: "/path/to/exceptions",
},
want: true,
},
{
name: "air-gapped with AttackTracks",
scanInfo: &cautils.ScanInfo{
AttackTracks: "/path/to/attack-tracks",
},
want: true,
},
{
name: "not air-gapped - all empty",
scanInfo: &cautils.ScanInfo{},
want: false,
},
{
name: "air-gapped with multiple flags",
scanInfo: &cautils.ScanInfo{
Local: true,
UseFrom: []string{"/path/to/policy"},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, isAirGappedMode(tt.scanInfo))
})
}
}