func (m *AwsVpcBackend) detectRouteTableID(instanceID string, ec2c *ec2.EC2) error { resp, err := ec2c.Instances([]string{instanceID}, nil) if err != nil { return fmt.Errorf("error getting instance info: %v", err) } if len(resp.Reservations) == 0 { return fmt.Errorf("no reservations found") } if len(resp.Reservations[0].Instances) == 0 { return fmt.Errorf("no matching instance found with id: %v", instanceID) } subnetID := resp.Reservations[0].Instances[0].SubnetId vpcID := resp.Reservations[0].Instances[0].VpcId log.Info("Subnet-ID: ", subnetID) log.Info("VPC-ID: ", vpcID) filter := ec2.NewFilter() filter.Add("association.subnet-id", subnetID) res, err := ec2c.DescribeRouteTables(nil, filter) if err != nil { return fmt.Errorf("error describing routeTables for subnetID %s: %v", subnetID, err) } if len(res.RouteTables) != 0 { m.cfg.RouteTableID = res.RouteTables[0].RouteTableId return nil } filter = ec2.NewFilter() filter.Add("association.main", "true") filter.Add("vpc-id", vpcID) res, err = ec2c.DescribeRouteTables(nil, filter) if err != nil { log.Info("error describing route tables: ", err) } if len(res.RouteTables) == 0 { return fmt.Errorf("main route table not found") } m.cfg.RouteTableID = res.RouteTables[0].RouteTableId return nil }
func (m *AwsVpcBackend) checkMatchingRoutes(instanceID, subnet string, ec2c *ec2.EC2) (bool, error) { filter := ec2.NewFilter() filter.Add("route.destination-cidr-block", subnet) filter.Add("route.state", "active") matchingRouteFound := false resp, err := ec2c.DescribeRouteTables([]string{m.cfg.RouteTableID}, filter) if err != nil { return matchingRouteFound, err } for _, routeTable := range resp.RouteTables { for _, route := range routeTable.Routes { if subnet == route.DestinationCidrBlock && route.State == "active" { if route.InstanceId == instanceID { matchingRouteFound = true break } log.Errorf("Deleting invalid *active* matching route: %s, %s \n", route.DestinationCidrBlock, route.InstanceId) } } } return matchingRouteFound, nil }
func (m *AwsVpcBackend) DetectRouteTableID(instanceID string, ec2c *ec2.EC2) (string, error) { resp, err := ec2c.Instances([]string{instanceID}, nil) if err != nil { return "", fmt.Errorf("error getting instance info: %v", err) } subnetID := resp.Reservations[0].Instances[0].SubnetId log.Info("SubnetId: ", subnetID) filter := ec2.NewFilter() filter.Add("association.subnet-id", subnetID) res, err := ec2c.DescribeRouteTables(nil, filter) if err != nil { return "", fmt.Errorf("error describing routeTables for subnetID %s: %v", subnetID, err) } return res.RouteTables[0].RouteTableId, nil }
func (s *ServerTests) TestGroupFiltering(c *C) { g := make([]ec2.SecurityGroup, 4) for i := range g { resp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName(fmt.Sprintf("testgroup%d", i)), Description: fmt.Sprintf("testdescription%d", i)}) c.Assert(err, IsNil) g[i] = resp.SecurityGroup c.Logf("group %d: %v", i, g[i]) defer s.ec2.DeleteSecurityGroup(g[i]) } perms := [][]ec2.IPPerm{ {{ Protocol: "tcp", FromPort: 100, ToPort: 200, SourceIPs: []string{"1.2.3.4/32"}, }}, {{ Protocol: "tcp", FromPort: 200, ToPort: 300, SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}}, }}, {{ Protocol: "udp", FromPort: 200, ToPort: 400, SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}}, }}, } for i, ps := range perms { _, err := s.ec2.AuthorizeSecurityGroup(g[i], ps) c.Assert(err, IsNil) } groups := func(indices ...int) (gs []ec2.SecurityGroup) { for _, index := range indices { gs = append(gs, g[index]) } return } type groupTest struct { about string groups []ec2.SecurityGroup // groupIds argument to SecurityGroups method. filters []filterSpec // filters argument to SecurityGroups method. results []ec2.SecurityGroup // set of expected result groups. allowExtra bool // specified results may be incomplete. err string // expected error. } filterCheck := func(name, val string, gs []ec2.SecurityGroup) groupTest { return groupTest{ about: "filter check " + name, filters: []filterSpec{{name, []string{val}}}, results: gs, allowExtra: true, } } tests := []groupTest{ { about: "check that SecurityGroups returns all groups", results: groups(0, 1, 2, 3), allowExtra: true, }, { about: "check that specifying two group ids returns them", groups: idsOnly(groups(0, 2)), results: groups(0, 2), }, { about: "check that specifying names only works", groups: namesOnly(groups(0, 2)), results: groups(0, 2), }, { about: "check that specifying a non-existent group id gives an error", groups: append(groups(0), ec2.SecurityGroup{Id: "sg-eeeeeeeee"}), err: `.*\(InvalidGroup\.NotFound\)`, }, { about: "check that a filter allowed two groups returns both of them", filters: []filterSpec{ {"group-id", []string{g[0].Id, g[2].Id}}, }, results: groups(0, 2), }, { about: "check that the previous filter works when specifying a list of ids", groups: groups(1, 2), filters: []filterSpec{ {"group-id", []string{g[0].Id, g[2].Id}}, }, results: groups(2), }, { about: "check that a filter allowing no groups returns none", filters: []filterSpec{ {"group-id", []string{"sg-eeeeeeeee"}}, }, }, filterCheck("description", "testdescription1", groups(1)), filterCheck("group-name", g[2].Name, groups(2)), filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(0)), filterCheck("ip-permission.group-name", g[1].Name, groups(1, 2)), filterCheck("ip-permission.protocol", "udp", groups(2)), filterCheck("ip-permission.from-port", "200", groups(1, 2)), filterCheck("ip-permission.to-port", "200", groups(0)), // TODO owner-id } for i, t := range tests { c.Logf("%d. %s", i, t.about) var f *ec2.Filter if t.filters != nil { f = ec2.NewFilter() for _, spec := range t.filters { f.Add(spec.name, spec.values...) } } resp, err := s.ec2.SecurityGroups(t.groups, f) if t.err != "" { c.Check(err, ErrorMatches, t.err) continue } c.Assert(err, IsNil) groups := make(map[string]*ec2.SecurityGroup) for j := range resp.Groups { group := &resp.Groups[j].SecurityGroup c.Check(groups[group.Id], IsNil, Commentf("duplicate group id: %q", group.Id)) groups[group.Id] = group } // If extra groups may be returned, eliminate all groups that // we did not create in this session apart from the default group. if t.allowExtra { namePat := regexp.MustCompile(sessionName("testgroup[0-9]")) for id, g := range groups { if !namePat.MatchString(g.Name) { delete(groups, id) } } } c.Check(groups, HasLen, len(t.results)) for j, g := range t.results { rg := groups[g.Id] c.Assert(rg, NotNil, Commentf("group %d (%v) not found; got %#v", j, g, groups)) c.Check(rg.Name, Equals, g.Name, Commentf("group %d (%v)", j, g)) } } }
func (s *ServerTests) TestInstanceFiltering(c *C) { groupResp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName("testgroup1"), Description: "testgroup one description"}) c.Assert(err, IsNil) group1 := groupResp.SecurityGroup defer s.ec2.DeleteSecurityGroup(group1) groupResp, err = s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName("testgroup2"), Description: "testgroup two description"}) c.Assert(err, IsNil) group2 := groupResp.SecurityGroup defer s.ec2.DeleteSecurityGroup(group2) insts := make([]*ec2.Instance, 3) inst, err := s.ec2.RunInstances(&ec2.RunInstances{ MinCount: 2, ImageId: imageId, InstanceType: "t1.micro", SecurityGroups: []ec2.SecurityGroup{group1}, }) c.Assert(err, IsNil) insts[0] = &inst.Instances[0] insts[1] = &inst.Instances[1] defer terminateInstances(c, s.ec2, insts) imageId2 := "ami-e358958a" // Natty server, i386, EBS store inst, err = s.ec2.RunInstances(&ec2.RunInstances{ ImageId: imageId2, InstanceType: "t1.micro", SecurityGroups: []ec2.SecurityGroup{group2}, }) c.Assert(err, IsNil) insts[2] = &inst.Instances[0] ids := func(indices ...int) (instIds []string) { for _, index := range indices { instIds = append(instIds, insts[index].InstanceId) } return } tests := []struct { about string instanceIds []string // instanceIds argument to Instances method. filters []filterSpec // filters argument to Instances method. resultIds []string // set of instance ids of expected results. allowExtra bool // resultIds may be incomplete. err string // expected error. }{ { about: "check that Instances returns all instances", resultIds: ids(0, 1, 2), allowExtra: true, }, { about: "check that specifying two instance ids returns them", instanceIds: ids(0, 2), resultIds: ids(0, 2), }, { about: "check that specifying a non-existent instance id gives an error", instanceIds: append(ids(0), "i-deadbeef"), err: `.*\(InvalidInstanceID\.NotFound\)`, }, { about: "check that a filter allowed both instances returns both of them", filters: []filterSpec{ {"instance-id", ids(0, 2)}, }, resultIds: ids(0, 2), }, { about: "check that a filter allowing only one instance returns it", filters: []filterSpec{ {"instance-id", ids(1)}, }, resultIds: ids(1), }, { about: "check that a filter allowing no instances returns none", filters: []filterSpec{ {"instance-id", []string{"i-deadbeef12345"}}, }, }, { about: "check that filtering on group id works", filters: []filterSpec{ {"group-id", []string{group1.Id}}, }, resultIds: ids(0, 1), }, { about: "check that filtering on group name works", filters: []filterSpec{ {"group-name", []string{group1.Name}}, }, resultIds: ids(0, 1), }, { about: "check that filtering on image id works", filters: []filterSpec{ {"image-id", []string{imageId}}, }, resultIds: ids(0, 1), allowExtra: true, }, { about: "combination filters 1", filters: []filterSpec{ {"image-id", []string{imageId, imageId2}}, {"group-name", []string{group1.Name}}, }, resultIds: ids(0, 1), }, { about: "combination filters 2", filters: []filterSpec{ {"image-id", []string{imageId2}}, {"group-name", []string{group1.Name}}, }, }, } for i, t := range tests { c.Logf("%d. %s", i, t.about) var f *ec2.Filter if t.filters != nil { f = ec2.NewFilter() for _, spec := range t.filters { f.Add(spec.name, spec.values...) } } resp, err := s.ec2.Instances(t.instanceIds, f) if t.err != "" { c.Check(err, ErrorMatches, t.err) continue } c.Assert(err, IsNil) insts := make(map[string]*ec2.Instance) for _, r := range resp.Reservations { for j := range r.Instances { inst := &r.Instances[j] c.Check(insts[inst.InstanceId], IsNil, Commentf("duplicate instance id: %q", inst.InstanceId)) insts[inst.InstanceId] = inst } } if !t.allowExtra { c.Check(insts, HasLen, len(t.resultIds), Commentf("expected %d instances got %#v", len(t.resultIds), insts)) } for j, id := range t.resultIds { c.Check(insts[id], NotNil, Commentf("instance id %d (%q) not found; got %#v", j, id, insts)) } } }
func (s *ServerTests) TestIPPerms(c *C) { g0 := s.makeTestGroup(c, "goamz-test0", "ec2test group 0") defer s.ec2.DeleteSecurityGroup(g0) g1 := s.makeTestGroup(c, "goamz-test1", "ec2test group 1") defer s.ec2.DeleteSecurityGroup(g1) resp, err := s.ec2.SecurityGroups([]ec2.SecurityGroup{g0, g1}, nil) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 2) c.Assert(resp.Groups[0].IPPerms, HasLen, 0) c.Assert(resp.Groups[1].IPPerms, HasLen, 0) ownerId := resp.Groups[0].OwnerId // test some invalid parameters // TODO more _, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{ Protocol: "tcp", FromPort: 0, ToPort: 1024, SourceIPs: []string{"z127.0.0.1/24"}, }}) c.Assert(err, NotNil) c.Check(err.(*ec2.Error).Code, Equals, "InvalidPermission.Malformed") // Check that AuthorizeSecurityGroup adds the correct authorizations. _, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{ Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceIPs: []string{"127.0.0.0/24"}, SourceGroups: []ec2.UserSecurityGroup{{ Name: g1.Name, }, { Id: g0.Id, }}, }, { Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceIPs: []string{"200.1.1.34/32"}, }}) c.Assert(err, IsNil) resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 1) c.Assert(resp.Groups[0].IPPerms, HasLen, 1) perm := resp.Groups[0].IPPerms[0] srcg := perm.SourceGroups c.Assert(srcg, HasLen, 2) // Normalize so we don't care about returned order. if srcg[0].Name == g1.Name { srcg[0], srcg[1] = srcg[1], srcg[0] } c.Check(srcg[0].Name, Equals, g0.Name) c.Check(srcg[0].Id, Equals, g0.Id) c.Check(srcg[0].OwnerId, Equals, ownerId) c.Check(srcg[1].Name, Equals, g1.Name) c.Check(srcg[1].Id, Equals, g1.Id) c.Check(srcg[1].OwnerId, Equals, ownerId) sort.Strings(perm.SourceIPs) c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24", "200.1.1.34/32"}) // Check that we can't delete g1 (because g0 is using it) _, err = s.ec2.DeleteSecurityGroup(g1) c.Assert(err, NotNil) c.Check(err.(*ec2.Error).Code, Equals, "InvalidGroup.InUse") _, err = s.ec2.RevokeSecurityGroup(g0, []ec2.IPPerm{{ Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceGroups: []ec2.UserSecurityGroup{{Id: g1.Id}}, }, { Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceIPs: []string{"200.1.1.34/32"}, }}) c.Assert(err, IsNil) resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 1) c.Assert(resp.Groups[0].IPPerms, HasLen, 1) perm = resp.Groups[0].IPPerms[0] srcg = perm.SourceGroups c.Assert(srcg, HasLen, 1) c.Check(srcg[0].Name, Equals, g0.Name) c.Check(srcg[0].Id, Equals, g0.Id) c.Check(srcg[0].OwnerId, Equals, ownerId) c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24"}) // We should be able to delete g1 now because we've removed its only use. _, err = s.ec2.DeleteSecurityGroup(g1) c.Assert(err, IsNil) _, err = s.ec2.DeleteSecurityGroup(g0) c.Assert(err, IsNil) f := ec2.NewFilter() f.Add("group-id", g0.Id, g1.Id) resp, err = s.ec2.SecurityGroups(nil, f) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 0) }
func (m *AwsVpcBackend) Init(extIface *net.Interface, extIP net.IP) (*backend.SubnetDef, error) { // Parse our configuration if len(m.config.Backend) > 0 { if err := json.Unmarshal(m.config.Backend, &m.cfg); err != nil { return nil, fmt.Errorf("error decoding VPC backend config: %v", err) } } // Acquire the lease form subnet manager attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extIP), } l, err := m.sm.AcquireLease(m.ctx, m.network, &attrs) switch err { case nil: m.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } // Figure out this machine's EC2 instance ID and region identity, err := getInstanceIdentity() if err != nil { return nil, fmt.Errorf("error getting EC2 instance identity: %v", err) } instanceID, ok := identity["instanceId"].(string) if !ok { return nil, fmt.Errorf("invalid EC2 instance ID: %v", identity["instanceId"]) } regionVal, _ := identity["region"].(string) region, ok := aws.Regions[regionVal] if !ok { return nil, fmt.Errorf("invalid AWS region: %v", identity["region"]) } // Setup the EC2 client auth, err := aws.GetAuth("", "") if err != nil { return nil, fmt.Errorf("error getting AWS credentials from environment: %v", err) } ec2c := ec2.New(auth, region) if m.cfg.RouteTableID == "" { log.Infof("RouteTableID not passed as config parameter, attempting to detect") routeTableID, err := m.DetectRouteTableID(instanceID, ec2c) if err != nil { return nil, err } log.Info("Detected routeRouteTableID: ", routeTableID) m.cfg.RouteTableID = routeTableID } filter := ec2.NewFilter() filter.Add("route.destination-cidr-block", l.Subnet.String()) filter.Add("route.state", "active") resp, err := ec2c.DescribeRouteTables([]string{m.cfg.RouteTableID}, filter) if err != nil { log.Errorf("Error describing route tables: %v", err) if ec2Err, ok := err.(*ec2.Error); ok { if ec2Err.Code == "UnauthorizedOperation" { log.Errorf("Note: describeRouteTables permission cannot be bound to any resource") } } } else { for _, routeTable := range resp.RouteTables { for _, route := range routeTable.Routes { if l.Subnet.String() == route.DestinationCidrBlock && route.State == "active" { log.Errorf("Matching *active* entry to: %s that will be deleted: %s, %s \n", l.Subnet.String(), route.DestinationCidrBlock, route.GatewayId) } } } } // Delete route for this machine's subnet if it already exists if _, err := ec2c.DeleteRoute(m.cfg.RouteTableID, l.Subnet.String()); err != nil { if ec2err, ok := err.(*ec2.Error); !ok || ec2err.Code != "InvalidRoute.NotFound" { // an error other than the route not already existing occurred return nil, fmt.Errorf("error deleting existing route for %s: %v", l.Subnet.String(), err) } } // Add the route for this machine's subnet route := &ec2.CreateRoute{ RouteTableId: m.cfg.RouteTableID, InstanceId: instanceID, DestinationCidrBlock: l.Subnet.String(), } if _, err := ec2c.CreateRoute(route); err != nil { return nil, fmt.Errorf("unable to add route %+v: %v", route, err) } return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil }