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

detect which cloud provider we are using #38

Merged
merged 10 commits into from
Jul 4, 2023

Conversation

devsecfranklin
Copy link
Collaborator

@devsecfranklin devsecfranklin commented May 9, 2023

The goal is to detect the cloud provider in a concise manner.

  • add some code to detect which cloud we are currently running on
  • now that we know the current cloud provider, we can branch off and do cloud-specific things

@devsecfranklin
Copy link
Collaborator Author

Here we see the debug Google Compute Engine since we are running as a pod in GKE

~/w/peirates (dev37-20230508±) ▶ scripts/connect.sh
☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠
deployment "peirates" successfully rolled out
☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
peirates   1/1     1            1           21s
☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠
NAME                        READY   STATUS    RESTARTS   AGE
peirates-69594b759c-ktsfc   1/1     Running   0          21s
☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠
My pod: peirates-69594b759c-ktsfc
☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠
Google Compute Engine
Read a service account token from /var/run/secrets/kubernetes.io/serviceaccount/token
2023/05/09 02:45:21 JWT provided on the command line.
________________________________________
|  ___  ____ _ ____ ____ ___ ____ ____ |
|  |__] |___ | |__/ |__|  |  |___ [__  |
|  |    |___ | |  \ |  |  |  |___ ___] |
|______________________________________|
\n

@devsecfranklin devsecfranklin changed the title Dev37 20230508 detect which cloud provider we are using May 9, 2023
@devsecfranklin devsecfranklin self-assigned this May 9, 2023
@devsecfranklin devsecfranklin added the enhancement New feature or request label May 9, 2023
Copy link
Member

@JayBeale JayBeale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we simplified, so we just make a table of those cloud detection items? They all come down to:

PATH-to-REQUEST. RESPONSE-EXPECTED. CUSTOM-HEADER->LHS,RHS
/v1. "Google Compute Engine". "Metadata-Flavor". "Google"
/instance. "Azure..." nil

go.mod Outdated Show resolved Hide resolved
@JayBeale JayBeale linked an issue May 9, 2023 that may be closed by this pull request
@devsecfranklin
Copy link
Collaborator Author

~/w/d/peirates (dev37-20230508±) ▶︎︎ make
go fmt ./...
peirates.go
/home/franklin/go/bin/golint github.com/inguardians/peirates github.com/inguardians/peirates/cmd/peirates github.com/inguardians/peirates/lib
/home/franklin/workspace/dead10c5/peirates/attack-create-hostfs-pod.go:135:9: if block ends with a return statement, so drop this else and outdent its block
/home/franklin/workspace/dead10c5/peirates/attack-create-hostfs-pod.go:148:10: if block ends with a return statement, so drop this else and outdent its block
/home/franklin/workspace/dead10c5/peirates/aws.go:29:2: struct field AccessKeyId should be AccessKeyID
/home/franklin/workspace/dead10c5/peirates/aws.go:34:1: exported function PullIamCredentialsFromEnvironmentVariables should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/aws.go:47:1: exported function EnterIamCredentialsForAWS should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/aws.go:152:1: exported function AWSSTSAssumeRole should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/aws.go:215:1: exported function GetAWSRegionAndZone should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/aws.go:297:1: comment on exported function ListAWSBuckets should be of the form "ListAWSBuckets ..."
/home/franklin/workspace/dead10c5/peirates/aws.go:322:1: exported function KopsAttackAWS should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/aws.go:399:5: var byteEncodedJsonOutput should be byteEncodedJSONOutput
/home/franklin/workspace/dead10c5/peirates/commandline.go:1:1: package comment should be of the form "Package peirates ..."
/home/franklin/workspace/dead10c5/peirates/commandline.go:12:6: exported type CommandLineOptions should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/config.go:19:7: exported const ServiceAccountPath should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/config.go:21:6: exported type ServerInfo should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/config.go:33:1: exported function ImportPodServiceAccountToken should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/gcp.go:15:6: exported type GCPToken should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/gcp.go:59:9: if block ends with a return statement, so drop this else and outdent its block
/home/franklin/workspace/dead10c5/peirates/gcp.go:66:1: exported function KopsAttackGCP should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/gcp.go:87:9: if block ends with a return statement, so drop this else and outdent its block
/home/franklin/workspace/dead10c5/peirates/kubeapi.go:14:6: exported type RequestConfig should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/kubeapi.go:18:2: struct field Https should be HTTPS
/home/franklin/workspace/dead10c5/peirates/kubeapi.go:19:2: struct field IgnoreHttpsErrors should be IgnoreHTTPSErrors
/home/franklin/workspace/dead10c5/peirates/kubeapi.go:109:1: exported function RequestSimple should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/kubectl_interactive.go:8:6: don't use underscores in Go names; func kubectl_interactive should be kubectlInteractive
/home/franklin/workspace/dead10c5/peirates/misc_utils.go:10:1: exported function ReadLineStripWhitespace should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/misc_utils.go:17:1: comment on exported function ReadLine should be of the form "ReadLine ..."
/home/franklin/workspace/dead10c5/peirates/node_secrets.go:14:6: exported type SecretFromPodViaNodeFS should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/peirates.go:19:5: exported var UseAuthCanI should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/peirates.go:172:6: exported type AWSS3BucketObject should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/peirates.go:304:6: exported type PodNamespaceContainerTuple should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/peirates.go:1183:5: don't use underscores in Go names; var check_pattern_1 should be checkPattern1
/home/franklin/workspace/dead10c5/peirates/peirates.go:1194:6: don't use underscores in Go names; var check_pattern_2 should be checkPattern2
/home/franklin/workspace/dead10c5/peirates/portscan.go:12:6: don't use underscores in Go names; func scan_worker should be scanWorker
/home/franklin/workspace/dead10c5/peirates/portscan.go:14:3: don't use underscores in Go names; var ip_port should be ipPort
/home/franklin/workspace/dead10c5/peirates/portscan.go:30:6: don't use underscores in Go names; func scan_controller should be scanController
/home/franklin/workspace/dead10c5/peirates/service_account_utils.go:58:1: exported function MakeClientCertificateKeyPair should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/lib/cloud_detection.go:14:6: exported type Clouds should have comment or be unexported
/home/franklin/workspace/dead10c5/peirates/lib/cloud_detection.go:25:1: exported function Detect should have comment or be unexported
/home/franklin/go/bin/gosec -quiet -no-fail ./...
Results:


[/home/franklin/workspace/dead10c5/peirates/kubeapi.go:84] - G402 (CWE-295): TLS InsecureSkipVerify set true. (Confidence: HIGH, Severity: HIGH)
    83:                 // Set up our copy to ignore HTTPS errors
  > 84:                 transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
    85:         }



[/home/franklin/workspace/dead10c5/peirates/http_utils.go:96] - G402 (CWE-295): TLS InsecureSkipVerify set true. (Confidence: HIGH, Severity: HIGH)
    95:                                         RootCAs:            caCertPool,
  > 96:                                         InsecureSkipVerify: ignoreTLSErrors,
    97:                                 },



[/home/franklin/workspace/dead10c5/peirates/exec-via-kubelet-api.go:81] - G402 (CWE-295): TLS InsecureSkipVerify set true. (Confidence: HIGH, Severity: HIGH)
    80:                                                                 tr := &http.Transport{
  > 81:                                                                         TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    82:                                                                 }



[/home/franklin/workspace/dead10c5/peirates/aws.go:265] - G402 (CWE-295): TLS InsecureSkipVerify set true. (Confidence: HIGH, Severity: HIGH)
    264:        tr := &http.Transport{
  > 265:                TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    266:        }



[/home/franklin/workspace/dead10c5/peirates/peirates.go:776] - G101 (CWE-798): Potential hardcoded credentials (Confidence: LOW, Severity: HIGH)
    775:                        /* #gosec G101 - this is not a hardcoded credential */
  > 776:                        if secretType != "kubernetes.io/service-account-token" {
    777:                                println("[-] This secret is not a service account token.")



[/home/franklin/workspace/dead10c5/peirates/config.go:274] - G101 (CWE-798): Potential hardcoded credentials (Confidence: LOW, Severity: HIGH)
    273:        /* #gosec G101 - this is not a hardcoded credential */
  > 274:        const podVolumeSecretDir = "/volumes/kubernetes.io~secret/"
    275: 



[/home/franklin/workspace/dead10c5/peirates/peirates.go:472] - G204 (CWE-78): Subprocess launched with variable (Confidence: HIGH, Severity: MEDIUM)
    471:                                /* #gosec G204 - this code is intended to run arbitrary commands for the user */
  > 472:                                cmd := exec.Command(command, arguments...)
    473:                                out, err := cmd.CombinedOutput()



[/home/franklin/workspace/dead10c5/peirates/inject-into-pod-alpha.go:47] - G204 (CWE-78): Subprocess launched with variable (Confidence: HIGH, Severity: MEDIUM)
    46:                         /* #gosec G204 - this code is intended to run arbitrary commands for the user */
  > 47:                         execErr := syscall.Exec(path, args, env)
    48:                         if execErr != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:357] - G204 (CWE-78): Subprocess launched with variable (Confidence: HIGH, Severity: MEDIUM)
    356:                                                        /* #gosec G204 - this code runs the openssl command file names and directories found in the directory structure */
  > 357:                                                        cmd := exec.Command(command, arguments...)
    358:                                                        out, err := cmd.CombinedOutput()



[/home/franklin/workspace/dead10c5/peirates/http_utils.go:84] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    83:                 if caCertPath != "" {
  > 84:                         caCert, err := ioutil.ReadFile(caCertPath)
    85:                         if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:519] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    518:                // if the etc-hosts file is there, parse it to find this pod's name
  > 519:                file, err := os.Open(etcHostPath)
    520:                if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:463] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    462:                                }
  > 463:                                namespaceBytes, err := ioutil.ReadFile(namespacePath)
    464:                                if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:452] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    451:                                }
  > 452:                                tokenBytes, err := ioutil.ReadFile(tokenFilePath)
    453:                                if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:318] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    317:                                }
  > 318:                                namespaceBytes, err := ioutil.ReadFile(namespacePath)
    319:                                if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:307] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    306:                                }
  > 307:                                tokenBytes, err := ioutil.ReadFile(tokenFilePath)
    308:                                if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:204] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    203:                        }
  > 204:                        contents, err := ioutil.ReadFile(path)
    205:                        if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:189] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    188:                        }
  > 189:                        contents, err := ioutil.ReadFile(path)
    190:                        if err != nil {



[/home/franklin/workspace/dead10c5/peirates/config.go:93] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    92: 
  > 93:                 kubeconfigFile, err := ioutil.ReadFile(path)
    94:                 if err != nil {



[/home/franklin/workspace/dead10c5/peirates/aws.go:135] - G107 (CWE-88): Potential HTTP request made with variable url (Confidence: MEDIUM, Severity: MEDIUM)
    134:        request := "http://169.254.169.254/latest/meta-data/iam/security-credentials/" + account
  > 135:        response2, err := http.Get(request)
    136:        if err != nil {



[/home/franklin/workspace/dead10c5/peirates/service_account_utils.go:117] - G307 (CWE-703): Deferring unsafe method "Close" on type "*os.File" (Confidence: HIGH, Severity: MEDIUM)
    116:        }
  > 117:        defer file.Close()
    118: 



Summary:
  Gosec  : dev
  Files  : 20
  Lines  : 4620
  Nosec  : 1
  Issues : 20

/home/franklin/go/bin/golangci-lint run
make: /home/franklin/go/bin/golangci-lint: No such file or directory
make: *** [Makefile:11: lint] Error 127

peirates.go Outdated
@@ -13,6 +13,7 @@ import (
"os/exec"
"regexp"
"strings"
L "./lib"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems angry about this location. Will consult the style guide.

/home/franklin/go/bin/golangci-lint run
peirates.go:6:2: local import "./lib" in non-local package (typecheck)
        L "./lib"
        ^
make: *** [Makefile:11: lint] Error 1
~/w/d/peirates (dev37-20230508±) ▶ 

@devsecfranklin
Copy link
Collaborator Author

What if we simplified, so we just make a table of those cloud detection items? They all come down to:

PATH-to-REQUEST. RESPONSE-EXPECTED. CUSTOM-HEADER->LHS,RHS /v1. "Google Compute Engine". "Metadata-Flavor". "Google" /instance. "Azure..." nil

I think the formatting of the result will be similar, agreed. But the difference is in the way we detect each different provider. I guess help me understand where in the code we would implement this?

@JayBeale
Copy link
Member

JayBeale commented Jun 22, 2023

With the exception of Openstack, we can detect cloud provider via a single HTTP request. I'm thinking this might be implementable as a table of:

  • Cloud provider
  • URL
  • HTTP Method
  • Custom Header(s) required
  • Pattern/string that indicates the specific cloud provider has been found

For OpenStack, it's a file read but that doesn't seem to be applicable to our use case. So we can leave that out.

The only other file reads are checking to see whether you're in a containerd-spawned container and whether you're in a Kubernetes pod. Both of those really belong in separate code.

Here's an example - but it would be better to use DoHTTPRequestAndGetBody() instead of having all this HTTP logic:

Function definition for DoHTTPRequestAndGetBody():

// DoHTTPRequestAndGetBody performs an HTTP request, and returns the full
// body of the reponse as a string. If ignoreTLSErrors is  true, all TLS
// errors, such as invalid certificates, will be ignored. If caCertPath is
// not an empty string, a TLS certificate will be read from the provided path
// and added to the pool of valid certificates.
func DoHTTPRequestAndGetBody(req *http.Request, https bool, ignoreTLSErrors bool, caCertPath string) ([]byte, error) {

Example:

type CloudProvider struct {
	Name              string
	URL               string
	HTTPMethod        string
	CustomHeader      string
	CustomHeaderValue string
	ResultString      string
}

func populateAndCheckCloudProviders() {
	providers := []CloudProvider{
		{
			Name:              "Provider1",
			URL:               "https://example.com/provider1",
			HTTPMethod:        "GET",
			CustomHeader:      "X-Custom-Header",
			CustomHeaderValue: "Value1",
			ResultString:      "Provider1 found",
		},
		{
			Name:              "Provider2",
			URL:               "https://example.com/provider2",
			HTTPMethod:        "POST",
			CustomHeader:      "",
			CustomHeaderValue: "",
			ResultString:      "Provider2 found",
		},
		// Add more cloud providers as needed
	}

	for _, provider := range providers {
		fmt.Printf("Checking %s...\n", provider.Name)

                // Use DoHTTPRequestAndGetBody()
		req, err := http.NewRequest(provider.HTTPMethod, provider.URL, nil)
		if err != nil {
			fmt.Printf("Failed to create request for %s: %v\n", provider.Name, err)
			continue
		}

		if provider.CustomHeader != "" {
			req.Header.Set(provider.CustomHeader, provider.CustomHeaderValue)
		}

                 // use DoHTTPRequestAndGetBody
		resp, err := client.Do(req)
		if err != nil {
			fmt.Printf("Failed to make request to %s: %v\n", provider.Name, err)
			continue
		}
		defer resp.Body.Close()

                 // Use DoHTTPRequestAndGetBody()
		if resp.StatusCode == http.StatusOK {
                      // Check if there's a body string returned that matches ResultString
		} else {
			fmt.Printf("%s responded with HTTP %d\n", provider.Name, resp.StatusCode)
		}
	}
}

Copy link
Member

@JayBeale JayBeale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but there's a quick copy-paste issue on the Digital Ocean detection.

HTTPMethod: "GET",
CustomHeader: "",
CustomHeaderValue: "",
ResultString: "Microsoft Azure",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a copy-paste error on this line that can be easily fixed.

@JayBeale JayBeale merged commit c3f2556 into inguardians:main Jul 4, 2023
0 of 4 checks passed
@JayBeale JayBeale deleted the dev37-20230508 branch July 4, 2023 05:27
@JayBeale JayBeale restored the dev37-20230508 branch July 4, 2023 06:11
JayBeale added a commit that referenced this pull request Jul 4, 2023
JayBeale added a commit that referenced this pull request Jul 4, 2023
JayBeale added a commit that referenced this pull request Jul 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Detect if we are pod, node, bare metal, or which cloud provider
2 participants