Skip to content

Commit

Permalink
Add AWS cloud start
Browse files Browse the repository at this point in the history
  • Loading branch information
richiejp committed Oct 3, 2024
1 parent 035e757 commit d75a90d
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 13 deletions.
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
inherit version;
dontPatchShebangs = true;
};
vendorHash = "sha256-OmNKmih4OSJSp5Thuotn6SB/TLDvMHNmwFdzJxXyAe4="; #pkgs.lib.fakeHash;
vendorHash = pkgs.lib.fakeHash;
cli = pkgs.callPackage ./distros/nix/cli.nix {
inherit version vendorHash;
};
Expand Down
282 changes: 282 additions & 0 deletions go/cli/cloud/aws/lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
package aws

import (
"context"
"fmt"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"

"premai.io/Ayup/go/internal/terror"
)

const (
secretName = "ayup-preauth-conf"
iamRoleName = "ayup-read-preauth-secrets"
iamPolicyName = "ayup-read-preauth-secrets"
securityGroupName = "ayup-listen-tcp-50051"
vpcName = "ayup"
instanceProfileName = "ayup"
instanceType = "t2.micro"
keyName = "ayup-debug"
amiID = "ami-0abcdef1234567890" // Replace with a valid AMI ID
)

func StartEc2(ctx context.Context) error {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return terror.Errorf(ctx, "unable to load SDK config, %v", err)
}

// Create IAM policy
iamSvc := iam.NewFromConfig(cfg)
policyArn, err := createIAMPolicy(ctx, iamSvc)
if err != nil {
return terror.Errorf(ctx, "Failed to create IAM policy: %v", err)
}

// Create IAM role and attach policy
roleArn, err := createIAMRole(ctx, iamSvc, policyArn)
if err != nil {
return terror.Errorf(ctx, "Failed to create IAM role: %v", err)
}

// Create or get VPC
ec2Svc := ec2.NewFromConfig(cfg)
vpcID, err := getOrCreateVPC(ctx, ec2Svc)
if err != nil {
return terror.Errorf(ctx, "Failed to get or create VPC: %v", err)
}

// Create or get security group
securityGroupID, err := getOrCreateSecurityGroup(ctx, ec2Svc, vpcID)
if err != nil {
return terror.Errorf(ctx, "Failed to get or create security group: %v", err)
}

// Create secret in Secrets Manager
smSvc := secretsmanager.NewFromConfig(cfg)
err = createSecret(ctx, smSvc)
if err != nil {
return terror.Errorf(ctx, "Failed to create secret: %v", err)
}

// Launch EC2 instance
instanceID, err := launchEC2Instance(ctx, ec2Svc, roleArn, securityGroupID)
if err != nil {
return terror.Errorf(ctx, "Failed to launch EC2 instance: %v", err)
}

fmt.Printf("Successfully launched EC2 instance with ID: %s\n", instanceID)

return nil
}

func createIAMPolicy(ctx context.Context, svc *iam.Client) (string, error) {
policyDocument := `{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}
]
}`

result, err := svc.CreatePolicy(ctx, &iam.CreatePolicyInput{
PolicyName: aws.String(iamPolicyName),
PolicyDocument: aws.String(policyDocument),
})
if err != nil {
return "", err
}

return *result.Policy.Arn, nil
}

func createIAMRole(ctx context.Context, svc *iam.Client, policyArn string) (string, error) {
assumeRolePolicyDocument := `{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}`

_, err := svc.CreateRole(ctx, &iam.CreateRoleInput{
RoleName: aws.String(iamRoleName),
AssumeRolePolicyDocument: aws.String(assumeRolePolicyDocument),
})
if err != nil {
return "", err
}

_, err = svc.AttachRolePolicy(ctx, &iam.AttachRolePolicyInput{
RoleName: aws.String(iamRoleName),
PolicyArn: aws.String(policyArn),
})
if err != nil {
return "", err
}

profileResult, err := svc.CreateInstanceProfile(ctx, &iam.CreateInstanceProfileInput{
InstanceProfileName: aws.String(instanceProfileName),
})
if err != nil {
return "", err
}

_, err = svc.AddRoleToInstanceProfile(ctx, &iam.AddRoleToInstanceProfileInput{
InstanceProfileName: aws.String(instanceProfileName),
RoleName: aws.String(iamRoleName),
})
if err != nil {
return "", err
}

return *profileResult.InstanceProfile.Arn, nil
}

func getOrCreateVPC(ctx context.Context, svc *ec2.Client) (string, error) {
// Check if VPC exists
result, err := svc.DescribeVpcs(ctx, &ec2.DescribeVpcsInput{
Filters: []ec2types.Filter{
{
Name: aws.String("tag:Name"),
Values: []string{vpcName},
},
},
})
if err != nil {
return "", err
}

if len(result.Vpcs) > 0 {
return *result.Vpcs[0].VpcId, nil
}

// Create VPC if it doesn't exist
createResult, err := svc.CreateVpc(ctx, &ec2.CreateVpcInput{
CidrBlock: aws.String("10.0.0.0/16"),
})
if err != nil {
return "", err
}

// Add Name tag to VPC
_, err = svc.CreateTags(ctx, &ec2.CreateTagsInput{
Resources: []string{*createResult.Vpc.VpcId},
Tags: []ec2types.Tag{
{
Key: aws.String("Name"),
Value: aws.String(vpcName),
},
},
})
if err != nil {
return "", err
}

return *createResult.Vpc.VpcId, nil
}

func getOrCreateSecurityGroup(ctx context.Context, svc *ec2.Client, vpcID string) (string, error) {
// Check if security group exists
result, err := svc.DescribeSecurityGroups(ctx, &ec2.DescribeSecurityGroupsInput{
Filters: []ec2types.Filter{
{
Name: aws.String("group-name"),
Values: []string{securityGroupName},
},
{
Name: aws.String("vpc-id"),
Values: []string{vpcID},
},
},
})
if err != nil {
return "", err
}

if len(result.SecurityGroups) > 0 {
return *result.SecurityGroups[0].GroupId, nil
}

// Create security group if it doesn't exist
createResult, err := svc.CreateSecurityGroup(ctx, &ec2.CreateSecurityGroupInput{
GroupName: aws.String(securityGroupName),
Description: aws.String("Security group for EC2 instance"),
VpcId: aws.String(vpcID),
})
if err != nil {
return "", err
}

// Authorize inbound traffic on port 50051/tcp
_, err = svc.AuthorizeSecurityGroupIngress(ctx, &ec2.AuthorizeSecurityGroupIngressInput{
GroupId: aws.String(*createResult.GroupId),
IpPermissions: []ec2types.IpPermission{
{
IpProtocol: aws.String("tcp"),
FromPort: aws.Int32(50051),
ToPort: aws.Int32(50051),
IpRanges: []ec2types.IpRange{
{
CidrIp: aws.String("0.0.0.0/0"),
},
},
},
},
})
if err != nil {
return "", err
}

return *createResult.GroupId, nil
}

func createSecret(ctx context.Context, svc *secretsmanager.Client) error {
_, err := svc.CreateSecret(ctx, &secretsmanager.CreateSecretInput{
Name: aws.String(secretName),
SecretString: aws.String(secretValue),
})
return err
}

func launchEC2Instance(ctx context.Context, svc *ec2.Client, roleArn, securityGroupID string) (string, error) {
runResult, err := svc.RunInstances(ctx, &ec2.RunInstancesInput{
ImageId: aws.String(amiID),
InstanceType: ec2types.InstanceTypeT2Micro,
KeyName: aws.String(keyName),
MinCount: aws.Int32(1),
MaxCount: aws.Int32(1),
IamInstanceProfile: &ec2types.IamInstanceProfileSpecification{
Arn: aws.String(roleArn),
},
SecurityGroupIds: []string{securityGroupID},
})
if err != nil {
return "", err
}

instanceID := *runResult.Instances[0].InstanceId

// Wait for the instance to be in running state
err = svc.WaitUntilInstanceRunning(ctx, &ec2.DescribeInstancesInput{
InstanceIds: []string{instanceID},
})
if err != nil {
return "", err
}

return instanceID, nil
}
14 changes: 12 additions & 2 deletions go/cmd/ay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,16 @@ type Globals struct {
Tracer trace.Tracer
}

type AwsStartCmd struct {
CliP2pPrivKey string `env:"AYUP_CLIENT_P2P_PRIV_KEY" help:"The client's private key, generated automatically if not set"`
}

type DaemonPreauthCmd struct {
P2pPrivKey string `env:"AYUP_CLIENT_P2P_PRIV_KEY" help:"The client's private key, generated automatically if not set"`
CliP2pPrivKey string `env:"AYUP_CLIENT_P2P_PRIV_KEY" help:"The client's private key, generated automatically if not set"`
}

func (s *DaemonPreauthCmd) Run(g Globals) (err error) {
return daemon.RunPreauth(g.Ctx, s.P2pPrivKey)
return daemon.RunPreauth(g.Ctx, s.CliP2pPrivKey)
}

type PushCmd struct {
Expand Down Expand Up @@ -144,6 +148,12 @@ func (s *AssistantsList) Run(g Globals) error {
var cli struct {
Login LoginCmd `group:"Client:" cmd:"" help:"Login to the Ayup service"`

Cloud struct {
Aws struct {
Start AwsStartCmd `cmd:"" help:"Start, and authenticate with, an Ayup server on your AWS account"`
} `cmd:"" help:"Amazon"`
} `group:"Server:" cmd:"" help:"Host Ayup using cloud providers"`

Daemon struct {
Start DaemonStartCmd `cmd:"" help:"Start an Ayup service Daemon"`
StartInRootless DaemonStartInRootlessCmd `cmd:"" passthrough:"" help:"Start a utility daemon to do tasks such as port forwarding in the Rootlesskit namesapce" hidden:""`
Expand Down
10 changes: 5 additions & 5 deletions go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ go 1.22.7

require (
github.com/alecthomas/kong v1.2.1
github.com/aws/aws-sdk-go-v2 v1.31.0
github.com/aws/aws-sdk-go-v2/config v1.27.39
github.com/aws/aws-sdk-go-v2/service/ec2 v1.179.2
github.com/aws/aws-sdk-go-v2/service/iam v1.36.3
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.1.1
github.com/charmbracelet/huh v0.6.0
Expand All @@ -22,7 +27,6 @@ require (
github.com/multiformats/go-multiaddr v0.13.0
github.com/opencontainers/go-digest v1.0.0
github.com/tonistiigi/fsutil v0.0.0-20240902111258-43b9329361d9
go.opentelemetry.io/contrib/bridges/otelslog v0.5.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0
go.opentelemetry.io/otel v1.30.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.6.0
Expand All @@ -45,16 +49,13 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.39 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.37 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.23.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 // indirect
Expand Down Expand Up @@ -113,7 +114,6 @@ require (
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
Expand Down
9 changes: 4 additions & 5 deletions go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7Yuht
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.179.2 h1:rGBv2N0zWvNTKnxOfbBH4mNM8WMdDNkaxdqtz152G40=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.179.2/go.mod h1:W6sNzs5T4VpZn1Vy+FMKw8s24vt5k6zPJXcNOK0asBo=
github.com/aws/aws-sdk-go-v2/service/iam v1.36.3 h1:dV9iimLEHKYAz2qTi+tGAD9QCnAG2pLD7HUEHB7m4mI=
github.com/aws/aws-sdk-go-v2/service/iam v1.36.3/go.mod h1:HSvujsK8xeEHMIB18oMXjSfqaN9cVqpo/MtHJIksQRk=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg=
Expand Down Expand Up @@ -257,9 +261,6 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
Expand Down Expand Up @@ -569,8 +570,6 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib v1.17.0 h1:lJJdtuNsP++XHD7tXDYEFSpsqIc7DzShuXMR5PwkmzA=
go.opentelemetry.io/contrib v1.17.0/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs=
go.opentelemetry.io/contrib/bridges/otelslog v0.5.0 h1:lU3F57OSLK5mQ1PDBVAfDDaKCPv37MrEbCfTzsF4bz0=
go.opentelemetry.io/contrib/bridges/otelslog v0.5.0/go.mod h1:I84u06zJFr8T5D73fslEUbnRBimVVSBhuVw8L8I92AU=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 h1:gbhw/u49SS3gkPWiYweQNJGm/uJN5GkI/FrosxSHT7A=
Expand Down

0 comments on commit d75a90d

Please sign in to comment.