Skip to content

Commit

Permalink
add egress verfiy for zero egress HCP cluster (#272)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmeng authored Oct 1, 2024
1 parent 77eae31 commit 88e50b4
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 23 deletions.
8 changes: 5 additions & 3 deletions cmd/egress/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"strings"
"time"

"golang.org/x/oauth2/google"

"github.com/openshift/osd-network-verifier/cmd/utils"
"github.com/openshift/osd-network-verifier/pkg/data/cloud"
"github.com/openshift/osd-network-verifier/pkg/data/cpu"
Expand All @@ -20,7 +22,6 @@ import (
"github.com/openshift/osd-network-verifier/pkg/proxy"
"github.com/openshift/osd-network-verifier/pkg/verifier"
gcpverifier "github.com/openshift/osd-network-verifier/pkg/verifier/gcp"
"golang.org/x/oauth2/google"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -137,7 +138,7 @@ are set correctly before execution.
}

// AWS workflow
if platformType == cloud.AWSClassic || platformType == cloud.AWSHCP {
if platformType == cloud.AWSClassic || platformType == cloud.AWSHCP || platformType == cloud.AWSHCPZeroEgress {

if len(vei.Tags) == 0 {
vei.Tags = awsDefaultTags
Expand Down Expand Up @@ -253,7 +254,8 @@ are set correctly before execution.
},
}

validateEgressCmd.Flags().StringVar(&config.platformType, "platform", platformTypeDefault, fmt.Sprintf("(optional) infra platform type, which determines which endpoints to test. Either '%s', '%s', or '%s' (hypershift)", cloud.AWSClassic, cloud.GCPClassic, cloud.AWSHCP))
validateEgressCmd.Flags().StringVar(&config.platformType, "platform", platformTypeDefault, fmt.Sprintf("(optional) infra platform type, which determines which endpoints to test. "+
"Either '%s', '%s', '%s', or '%s' (hypershift)", cloud.AWSClassic, cloud.GCPClassic, cloud.AWSHCP, cloud.AWSHCPZeroEgress))
validateEgressCmd.Flags().StringVar(&config.vpcSubnetID, "subnet-id", "", "target subnet ID")
validateEgressCmd.Flags().StringVar(&config.cloudImageID, "image-id", "", "(optional) cloud image for the compute instance")
validateEgressCmd.Flags().StringVar(&config.instanceType, "instance-type", "", "(optional) compute instance type")
Expand Down
9 changes: 8 additions & 1 deletion pkg/data/cloud/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ var (
AWSHCP = Platform{
names: [2]string{"aws-hcp", "hostedcluster"},
}
AWSHCPZeroEgress = Platform{
names: [2]string{"aws-hcp-zeroegress"},
}
GCPClassic = Platform{
names: [2]string{"gcp-classic", "gcp"},
}
Expand Down Expand Up @@ -49,13 +52,17 @@ func ByName(name string) (Platform, error) {
return GCPClassic, nil
}

if slices.Contains(AWSHCPZeroEgress.names[:], normalizedName) {
return AWSHCPZeroEgress, nil
}

return Platform{}, fmt.Errorf("no platform with name %s", name)
}

// IsValid returns true if the Platform is non-empty and supported by the network verifier
func (plat Platform) IsValid() bool {
switch plat {
case AWSClassic, AWSHCP, GCPClassic:
case AWSClassic, AWSHCP, GCPClassic, AWSHCPZeroEgress:
return true
default:
return false
Expand Down
2 changes: 1 addition & 1 deletion pkg/data/cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (arch Architecture) DefaultInstanceType(platformType cloud.Platform) (strin
}

switch platformType {
case cloud.AWSClassic, cloud.AWSHCP:
case cloud.AWSClassic, cloud.AWSHCP, cloud.AWSHCPZeroEgress:
return arch.defaultAWSInstanceType, nil
case cloud.GCPClassic:
return arch.defaultGCPInstanceType, nil
Expand Down
10 changes: 10 additions & 0 deletions pkg/data/egress_lists/aws-hcp-zeroegress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
endpoints:
- host: sts.${AWS_REGION}.amazonaws.com
ports:
- 443
- host: example.dkr.ecr.${AWS_REGION}.amazonaws.com
ports:
- 443
- host: api.ecr.${AWS_REGION}.amazonaws.com
ports:
- 443
10 changes: 9 additions & 1 deletion pkg/data/egress_lists/egress_lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import (
"os"

"github.com/google/go-github/v63/github"
"github.com/openshift/osd-network-verifier/pkg/data/cloud"
"gopkg.in/yaml.v3"

"github.com/openshift/osd-network-verifier/pkg/data/cloud"
)

//go:embed aws-classic.yaml
Expand All @@ -27,6 +28,9 @@ var templateAWSHCP string
//go:embed gcp-classic.yaml
var templateGCPClassic string

//go:embed aws-hcp-zeroegress.yaml
var templateAWSHCPZeroEgress string

func GetLocalEgressList(platformType cloud.Platform) (string, error) {
if !platformType.IsValid() {
fmt.Printf("platform type %s is invalid", platformType)
Expand All @@ -39,6 +43,8 @@ func GetLocalEgressList(platformType cloud.Platform) (string, error) {
return templateAWSHCP, nil
case cloud.AWSClassic:
return templateAWSClassic, nil
case cloud.AWSHCPZeroEgress:
return templateAWSHCPZeroEgress, nil
default:
return "", fmt.Errorf("no egress list registered for platform '%s'", platformType)
}
Expand All @@ -58,6 +64,8 @@ func GetGithubEgressList(platformType cloud.Platform) (*github.RepositoryContent
path += cloud.AWSHCP.String()
case cloud.AWSClassic:
path += cloud.AWSClassic.String()
case cloud.AWSHCPZeroEgress:
path += cloud.AWSHCPZeroEgress.String()
default:
return nil, fmt.Errorf("no egress list registered for platform '%s'", platformType)
}
Expand Down
19 changes: 15 additions & 4 deletions pkg/probes/curl/curl_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
_ "embed"
"encoding/base64"
"fmt"
"net"
"os"
"strconv"
"strings"

"gopkg.in/yaml.v3"

"github.com/openshift/osd-network-verifier/pkg/data/cloud"

"github.com/openshift/osd-network-verifier/pkg/data/cpu"
handledErrors "github.com/openshift/osd-network-verifier/pkg/errors"
"github.com/openshift/osd-network-verifier/pkg/helpers"
Expand All @@ -33,8 +35,8 @@ var userDataTemplate string
//go:embed systemd-template.sh
var systemdTemplate string

const startingToken = "NV_CURLJSON_BEGIN"
const endingToken = "NV_CURLJSON_END"
const startingToken = "NV_CURLJSON_BEGIN" //nolint:gosec
const endingToken = "NV_CURLJSON_END" //nolint:gosec
const outputLinePrefix = "@NV@"

var presetUserDataVariables = map[string]string{
Expand All @@ -56,7 +58,7 @@ func (clp Probe) GetMachineImageID(platformType cloud.Platform, cpuArch cpu.Arch
return "", handledErrors.NewGenericError(fmt.Errorf("invalid platform type specified %s", platformType))
}

if platformType == cloud.AWSHCP {
if platformType == cloud.AWSHCP || platformType == cloud.AWSHCPZeroEgress {
// HCP uses the same AMIs as Classic
platformType = cloud.AWSClassic
}
Expand Down Expand Up @@ -190,7 +192,7 @@ func (clp Probe) GetExpandedUserData(userDataVariables map[string]string) (strin
// ParseProbeOutput accepts a string containing all probe output that appeared between
// the startingToken and the endingToken and a pointer to an Output object. outputDestination
// will be filled with the results from the egress check
func (clp Probe) ParseProbeOutput(probeOutput string, outputDestination *output.Output) {
func (clp Probe) ParseProbeOutput(ensurePrivate bool, probeOutput string, outputDestination *output.Output) {
// probeOutput first needs to be "repaired" due to curl and AWS bugs
repairedProbeOutput := helpers.FixLeadingZerosInJSON(helpers.RemoveTimestamps(probeOutput))
probeResults, errMap := bulkDeserializeCurlJSONProbeResult(repairedProbeOutput)
Expand All @@ -204,6 +206,15 @@ func (clp Probe) ParseProbeOutput(probeOutput string, outputDestination *output.
[]string{fmt.Sprintf("%s (%s)", url, probeResult.ErrorMsg)},
)
}
if ensurePrivate {
remoteIP := net.ParseIP(probeResult.RemoteIP)
if !remoteIP.IsPrivate() {
probeResult.ErrorMsg = "The endpoint is non private"
url := strings.Replace(probeResult.URL, "telnet", "tcp", 1)
outputDestination.SetEgressFailures(
[]string{fmt.Sprintf("%s (%s)", url, probeResult.ErrorMsg)})
}
}
}
for lineNum, err := range errMap {
outputDestination.AddError(
Expand Down
4 changes: 2 additions & 2 deletions pkg/probes/legacy/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (lgp Probe) GetMachineImageID(platformType cloud.Platform, cpuArch cpu.Arch
os.Exit(1)
}

if platformType == cloud.AWSHCP {
if platformType == cloud.AWSHCP || platformType == cloud.AWSHCPZeroEgress {
// HCP uses the same AMIs as Classic
platformType = cloud.AWSClassic
}
Expand Down Expand Up @@ -104,7 +104,7 @@ func (lgp Probe) GetExpandedUserData(userDataVariables map[string]string) (strin
// ParseProbeOutput accepts a string containing all probe output that appeared between
// the startingToken and the endingToken and a pointer to an Output object. outputDestination
// will be filled with the results from the egress check
func (lgp Probe) ParseProbeOutput(probeOutput string, outputDestination *output.Output) {
func (lgp Probe) ParseProbeOutput(ensurePrivate bool, probeOutput string, outputDestination *output.Output) {
// reSuccess indicates that network validation was successful
reSuccess := regexp.MustCompile(`Success!`)

Expand Down
2 changes: 1 addition & 1 deletion pkg/probes/package_probes.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ type Probe interface {
GetStartingToken() string
GetEndingToken() string
GetExpandedUserData(map[string]string) (string, error)
ParseProbeOutput(string, *output.Output)
ParseProbeOutput(bool, string, *output.Output)
}
6 changes: 3 additions & 3 deletions pkg/verifier/aws/aws_verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/go-playground/validator"
ocmlog "github.com/openshift-online/ocm-sdk-go/logging"

"github.com/openshift/osd-network-verifier/pkg/clients/aws"
"github.com/openshift/osd-network-verifier/pkg/data/cloud"
"github.com/openshift/osd-network-verifier/pkg/data/cpu"
Expand Down Expand Up @@ -348,7 +349,7 @@ func (a *AwsVerifier) createEC2Instance(input createEC2InstanceInput) (string, e
return instanceID, nil
}

func (a *AwsVerifier) findUnreachableEndpoints(ctx context.Context, instanceID string, probe probes.Probe) error {
func (a *AwsVerifier) findUnreachableEndpoints(ctx context.Context, instanceID string, probe probes.Probe, ensurePrivate bool) error {
var consoleOutput string

a.writeDebugLogs(ctx, "Scraping console output and waiting for user data script to complete...")
Expand Down Expand Up @@ -409,8 +410,7 @@ func (a *AwsVerifier) findUnreachableEndpoints(ctx context.Context, instanceID s

// Send probe's output off to the Probe interface for parsing
a.writeDebugLogs(ctx, fmt.Sprintf("probe output:\n---\n%s\n---", rawProbeOutput))
probe.ParseProbeOutput(rawProbeOutput, &a.Output)

probe.ParseProbeOutput(ensurePrivate, rawProbeOutput, &a.Output)
return true, nil
})

Expand Down
87 changes: 84 additions & 3 deletions pkg/verifier/aws/aws_verifier_test.go

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion pkg/verifier/aws/entry_point.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
awsTools "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"

"github.com/openshift/osd-network-verifier/pkg/data/cloud"

"github.com/openshift/osd-network-verifier/pkg/data/egress_lists"
handledErrors "github.com/openshift/osd-network-verifier/pkg/errors"
"github.com/openshift/osd-network-verifier/pkg/output"
Expand Down Expand Up @@ -244,7 +246,12 @@ func (a *AwsVerifier) ValidateEgress(vei verifier.ValidateEgressInput) *output.O
}

// findUnreachableEndpoints will call Probe.ParseProbeOutput(), which will store egress failures in a.Output.failures
err = a.findUnreachableEndpoints(vei.Ctx, instanceID, vei.Probe)
ensurePrivate := false
if vei.PlatformType == cloud.AWSHCPZeroEgress {
ensurePrivate = true
}
err = a.findUnreachableEndpoints(vei.Ctx, instanceID, vei.Probe, ensurePrivate)

if err != nil {
a.Output.AddError(err)
// Don't return yet; still need to terminate instance
Expand Down
7 changes: 4 additions & 3 deletions pkg/verifier/gcp/gcp_verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import (
"time"

ocmlog "github.com/openshift-online/ocm-sdk-go/logging"
"golang.org/x/oauth2/google"
computev1 "google.golang.org/api/compute/v1"

"github.com/openshift/osd-network-verifier/pkg/clients/gcp"
handledErrors "github.com/openshift/osd-network-verifier/pkg/errors"
"github.com/openshift/osd-network-verifier/pkg/helpers"
"github.com/openshift/osd-network-verifier/pkg/output"
"github.com/openshift/osd-network-verifier/pkg/probes"
"golang.org/x/oauth2/google"
computev1 "google.golang.org/api/compute/v1"
)

type GcpVerifier struct {
Expand Down Expand Up @@ -209,7 +210,7 @@ func (g *GcpVerifier) findUnreachableEndpoints(projectID, zone, instanceName str

// Send probe's output off to the Probe interface for parsing
g.Logger.Debug(context.TODO(), "probe output:\n---\n%s\n---", rawProbeOutput)
probe.ParseProbeOutput(rawProbeOutput, &g.Output)
probe.ParseProbeOutput(false, rawProbeOutput, &g.Output)

return true, nil
})
Expand Down

0 comments on commit 88e50b4

Please sign in to comment.