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

OCM-13040 | test: Bastion proxy support username and password #81

Merged
merged 1 commit into from
Jan 2, 2025
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
7 changes: 7 additions & 0 deletions pkg/aws/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ const (
InstanceKeyNamePrefix = "ocm-ci"
AWSInstanceUser = "ec2-user"
BastionName = "ocm-bastion"

SSHPort = "22"

// Squid related
SquidConfigFilePath = "/etc/squid/squid.conf"
SquidPasswordFilePath = "/etc/squid/passwords"
SquidProxyPort = "3128"
)

var AmazonName = "amazon"
Expand Down
9 changes: 9 additions & 0 deletions pkg/aws/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package utils

import (
"fmt"
"github.com/openshift-online/ocm-common/pkg/log"
"path"
"strings"

"github.com/aws/aws-sdk-go-v2/aws/arn"
Expand All @@ -25,3 +28,9 @@ func GetPathFromArn(arnStr string) (string, error) {
func TruncateRoleName(name string) string {
return commonUtils.Truncate(name, commonUtils.MaxByteSize)
}

func GetPrivateKeyName(privateKeyPath string, keypairName string) string {
privateKeyName := fmt.Sprintf("%s-%s", path.Join(privateKeyPath, keypairName), "keyPair.pem")
log.LogInfo("Get private key name finished.")
return privateKeyName
}
134 changes: 89 additions & 45 deletions pkg/test/vpc_client/bastion.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import (
"fmt"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/openshift-online/ocm-common/pkg/file"
"net"
"golang.org/x/crypto/bcrypt"
"net/url"
"time"

CON "github.com/openshift-online/ocm-common/pkg/aws/consts"
awsUtils "github.com/openshift-online/ocm-common/pkg/aws/utils"
"github.com/openshift-online/ocm-common/pkg/log"
"github.com/openshift-online/ocm-common/pkg/utils"
)

// LaunchBastion will launch a bastion instance on the indicated zone.
Expand Down Expand Up @@ -94,55 +97,37 @@ func (vpc *VPC) LaunchBastion(imageID string, zone string, userData string, keyp
return inst, nil
}

func (vpc *VPC) PrepareBastionProxy(zone string, cidrBlock string, keypairName string,
privateKeyPath string) (*types.Instance, error) {
filters := []map[string][]string{
{
"vpc-id": {
vpc.VpcID,
},
},
{
"tag:Name": {
CON.BastionName,
},
},
}

insts, err := vpc.AWSClient.ListInstances([]string{}, filters...)
// PrepareBastionProxy will launch a bastion instance with squid proxy on the indicated zone and return the proxy url.
func (vpc *VPC) PrepareBastionProxy(zone string, keypairName string, privateKeyPath string) (proxyUrl string, err error) {
encodeUserData := generateShellCommand()
instance, err := vpc.LaunchBastion("", zone, encodeUserData, keypairName, privateKeyPath)
if err != nil {
return nil, err
log.LogError("Launch bastion failed")
return "", err
}
if len(insts) == 0 {
log.LogInfo("Didn't found an existing bastion, going to launch one")
if cidrBlock == "" {
cidrBlock = CON.RouteDestinationCidrBlock
}
_, _, err = net.ParseCIDR(cidrBlock)

privateKeyName := awsUtils.GetPrivateKeyName(privateKeyPath, keypairName)
hostName := fmt.Sprintf("%s:%s", *instance.PublicIpAddress, CON.SSHPort)
SSHExecuteCMDs, username, password, err := generateWriteSquidPasswordFileCommand()
if err != nil {
return "", err
}
for _, cmd := range SSHExecuteCMDs {
_, err = Exec_CMD(CON.AWSInstanceUser, privateKeyName, hostName, cmd)
if err != nil {
log.LogError("CIDR IP address format is invalid")
return nil, err
log.LogError("SSH execute command failed")
return "", err
}

userData := fmt.Sprintf(`#!/bin/bash
yum update -y
yum install -y squid
cd /etc/squid/
sudo mv ./squid.conf ./squid.conf.bak
sudo touch squid.conf
echo http_port 3128 >> /etc/squid/squid.conf
echo acl allowed_ips src %s >> /etc/squid/squid.conf
echo http_access allow allowed_ips >> /etc/squid/squid.conf
echo http_access deny all >> /etc/squid/squid.conf
systemctl start squid
systemctl enable squid`, cidrBlock)

encodeUserData := base64.StdEncoding.EncodeToString([]byte(userData))
return vpc.LaunchBastion("", zone, encodeUserData, keypairName, privateKeyPath)

}
log.LogInfo("Found existing bastion: %s", *insts[0].InstanceId)
return &insts[0], nil

// construct proxy url
proxy := &url.URL{
Scheme: "http",
Host: fmt.Sprintf("%s:%s", *instance.PublicIpAddress, CON.SquidProxyPort),
User: url.UserPassword(username, password),
}
proxyUrl = proxy.String()
return proxyUrl, nil
}

func (vpc *VPC) DestroyBastionProxy(instance types.Instance) error {
Expand All @@ -155,3 +140,62 @@ func (vpc *VPC) DestroyBastionProxy(instance types.Instance) error {
}
return nil
}

func generateBcryptPassword(plainPassword string) (string, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(plainPassword), bcrypt.DefaultCost)
if err != nil {
log.LogError("Generate hashed password failed")
return "", nil
}
log.LogInfo("Generate hashed password finished.")
return string(hashedPassword), nil
}

func generateShellCommand() string {
userData := fmt.Sprintf(`#!/bin/bash
yum update -y
sudo dnf install squid -y
cd /etc/squid/
sudo mv ./squid.conf ./squid.conf.bak
sudo touch squid.conf
echo http_port %s >> %s
echo auth_param basic program /usr/lib64/squid/basic_ncsa_auth %s >> %s
echo auth_param basic realm Squid Proxy Server >> %s
echo acl authenticated proxy_auth REQUIRED >> %s
echo http_access allow authenticated >> %s
echo http_access deny all >> %s
systemctl start squid
systemctl enable squid`, CON.SquidProxyPort, CON.SquidConfigFilePath, CON.SquidPasswordFilePath,
CON.SquidConfigFilePath, CON.SquidConfigFilePath, CON.SquidConfigFilePath, CON.SquidConfigFilePath,
CON.SquidConfigFilePath)

encodeUserData := base64.StdEncoding.EncodeToString([]byte(userData))
log.LogInfo("Generate user data to creating squid proxy successfully.")

return encodeUserData
}

func generateWriteSquidPasswordFileCommand() (SSHExecuteCMDs []string, username string,
password string, err error) {
username = utils.RandomLabel(5)
password = utils.GeneratePassword(10)

hashedPassword, err := generateBcryptPassword(password)
if err != nil {
log.LogError("Generate bcrypt password failed.")
return []string{}, "", "", err
}

line := fmt.Sprintf("%s:%s\n", username, hashedPassword)
remoteFilePath := CON.SquidPasswordFilePath

createFileCMD := fmt.Sprintf("sudo touch %s", remoteFilePath)
copyPasswordCMD := fmt.Sprintf("echo '%s' | sudo tee %s > /dev/null", line, remoteFilePath)
SSHExecuteCMDs = []string{
createFileCMD,
copyPasswordCMD,
}

log.LogInfo("Generate write squid password file command finished.")
return SSHExecuteCMDs, username, password, nil
}
35 changes: 35 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package utils

import (
"github.com/openshift-online/ocm-common/pkg/log"
"math/rand"
"time"
)
Expand Down Expand Up @@ -45,3 +46,37 @@ func Truncate(s string, truncateLength int) string {
}
return s
}

func GeneratePassword(length int) string {
lowercase := "abcdefghijklmnopqrstuvwxyz"
uppercase := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
digits := "0123456789"
special := "!#$^&*()-_=+{}|;:,.<>?/~`"
allChars := lowercase + uppercase + digits + special

var password []rune

password = append(password, rune(lowercase[randInt(len(lowercase))]))
password = append(password, rune(uppercase[randInt(len(uppercase))]))
password = append(password, rune(digits[randInt(len(digits))]))
password = append(password, rune(special[randInt(len(special))]))

for len(password) < length {
password = append(password, rune(allChars[randInt(len(allChars))]))
}

shuffleStrings(password)
log.LogInfo("Generate squid password finished.")
return string(password)
}

func randInt(max int) int {
return rand.Intn(max)
}

func shuffleStrings(s []rune) {
for i := len(s) - 1; i > 0; i-- {
j := randInt(i + 1)
s[i], s[j] = s[j], s[i]
}
}
Loading