/** * Creates a security group with rules to: * * <ul> * <li>Allow SSH access on port 22 from the world * <li>Allow TCP, UDP and ICMP communication between machines in the same group * </ul> * * It needs to consider locationId as port ranges and groupId are cloud provider-dependent e.g * openstack nova wants from 1-65535 while aws-ec2 accepts from 0-65535. * * @param groupName The name of the security group to create * @param location The location in which the security group will be created * @param securityApi The API to use to create the security group * @return the created security group */ private SecurityGroup createBaseSecurityGroupInLocation( String groupName, Location location, SecurityGroupExtension securityApi) { SecurityGroup group = addSecurityGroupInLocation(groupName, location, securityApi); String groupId = group.getProviderId(); int fromPort = 0; if (isOpenstackNova(location)) { groupId = group.getId(); fromPort = 1; } // Note: For groupName to work with GCE we also need to tag the machines with the same ID. // See sourceTags section at https://developers.google.com/compute/docs/networking#firewalls IpPermission.Builder allWithinGroup = IpPermission.builder().groupId(groupId).fromPort(fromPort).toPort(65535); addPermission(allWithinGroup.ipProtocol(IpProtocol.TCP).build(), group, securityApi); addPermission(allWithinGroup.ipProtocol(IpProtocol.UDP).build(), group, securityApi); if (!isAzure(location)) { addPermission( allWithinGroup.ipProtocol(IpProtocol.ICMP).fromPort(-1).toPort(-1).build(), group, securityApi); } IpPermission sshPermission = IpPermission.builder() .fromPort(22) .toPort(22) .ipProtocol(IpProtocol.TCP) .cidrBlock(getBrooklynCidrBlock()) .build(); addPermission(sshPermission, group, securityApi); return group; }
/** * Applies the given security group permissions to the given node with the given compute service. * * <p>Takes no action if the compute service does not have a security group extension. * * @param permissions The set of permissions to be applied to the node * @param nodeId The id of the node to update * @param computeService The compute service to use to apply the changes */ @VisibleForTesting Map<String, SecurityGroup> addPermissionsToLocation( Iterable<IpPermission> permissions, final String nodeId, ComputeService computeService) { if (!computeService.getSecurityGroupExtension().isPresent()) { LOG.warn( "Security group extension for {} absent; cannot update node {} with {}", new Object[] {computeService, nodeId, permissions}); return ImmutableMap.of(); } final SecurityGroupExtension securityApi = computeService.getSecurityGroupExtension().get(); final String locationId = computeService.getContext().unwrap().getId(); // Expect to have two security groups on the node: one shared between all nodes in the location, // that is cached in sharedGroupCache, and one created by Jclouds that is unique to the node. // Relies on customize having been called before. This should be safe because the arguments // needed to call this method are not available until post-instance creation. SecurityGroup machineUniqueSecurityGroup = getSecurityGroup(nodeId, securityApi, locationId); MutableList<IpPermission> newPermissions = MutableList.copyOf(permissions); Iterables.removeAll(newPermissions, machineUniqueSecurityGroup.getIpPermissions()); MutableMap<String, SecurityGroup> addedSecurityGroups = MutableMap.of(); for (IpPermission permission : newPermissions) { SecurityGroup addedPermission = addPermission(permission, machineUniqueSecurityGroup, securityApi); addedSecurityGroups.put(addedPermission.getId(), addedPermission); } return addedSecurityGroups; }
@Test public void testApply() { IpPermissions authorization = IpPermissions.permitAnyProtocol(); org.jclouds.ec2.domain.SecurityGroup origGroup = org.jclouds.ec2.domain.SecurityGroup.builder() .region("us-east-1") .id("some-id") .name("some-group") .ownerId("some-owner") .description("some-description") .ipPermission(authorization) .build(); AWSEC2SecurityGroupToSecurityGroup parser = createGroupParser(ImmutableSet.of(provider)); SecurityGroup group = parser.apply(origGroup); assertEquals(group.getLocation(), provider); assertEquals(group.getId(), provider.getId() + "/" + origGroup.getId()); assertEquals(group.getProviderId(), origGroup.getId()); assertEquals(group.getName(), origGroup.getName()); assertEquals(group.getIpPermissions(), (Set<IpPermission>) origGroup); assertEquals(group.getOwnerId(), origGroup.getOwnerId()); }
/** * Loads the security groups attached to the node with the given ID and returns the group that is * unique to the node, per the application context. This method will also update {@link * #sharedGroupCache} if no mapping for the shared group's location previously existed (e.g. * Brooklyn was restarted and rebound to an existing application). * * <p>Notice that jclouds will attach 2 securityGroups to the node if the locationId is `aws-ec2` * so it needs to look for the uniqueSecurityGroup rather than the shared securityGroup. * * @param nodeId The id of the node in question * @param locationId The id of the location in question * @param securityApi The API to use to list security groups * @return the security group unique to the given node, or null if one could not be determined. */ private SecurityGroup getUniqueSecurityGroupForNodeCachingSharedGroupIfPreviouslyUnknown( String nodeId, String locationId, SecurityGroupExtension securityApi) { Set<SecurityGroup> groupsOnNode = securityApi.listSecurityGroupsForNode(nodeId); if (groupsOnNode == null || groupsOnNode.isEmpty()) { return null; } SecurityGroup unique; if (locationId.equals("aws-ec2")) { if (groupsOnNode.size() == 2) { String expectedSharedName = getNameForSharedSecurityGroup(); Iterator<SecurityGroup> it = groupsOnNode.iterator(); SecurityGroup shared = it.next(); if (shared.getName().endsWith(expectedSharedName)) { unique = it.next(); } else { unique = shared; shared = it.next(); } if (!shared.getName().endsWith(expectedSharedName)) { LOG.warn( "Couldn't determine which security group is shared between instances in app {}. Expected={}, found={}", new Object[] {applicationId, expectedSharedName, groupsOnNode}); return null; } // Shared entry might be missing if Brooklyn has rebound to an application SecurityGroup old = sharedGroupCache.asMap().putIfAbsent(shared.getLocation(), shared); LOG.info( "Loaded unique security group for node {} (in {}): {}", new Object[] {nodeId, applicationId, unique}); if (old == null) { LOG.info("Proactively set shared group for app {} to: {}", applicationId, shared); } return unique; } else { LOG.warn( "Expected to find two security groups on node {} in app {} (one shared, one unique). Found {}: {}", new Object[] {nodeId, applicationId, groupsOnNode.size(), groupsOnNode}); } } return Iterables.getOnlyElement(groupsOnNode); }
private void setSecurityGroupOnTemplate( final JcloudsLocation location, final Template template, final SecurityGroupExtension securityApi) { SecurityGroup shared; Tasks.setBlockingDetails( "Loading security group shared by instances in " + template.getLocation() + " in app " + applicationId); try { shared = sharedGroupCache.get( template.getLocation(), new Callable<SecurityGroup>() { @Override public SecurityGroup call() throws Exception { return getOrCreateSharedSecurityGroup(template.getLocation(), securityApi); } }); } catch (ExecutionException e) { throw Throwables.propagate(new Exception(e.getCause())); } finally { Tasks.resetBlockingDetails(); } Set<String> originalGroups = template.getOptions().getGroups(); template.getOptions().securityGroups(shared.getName()); if (!originalGroups.isEmpty()) { LOG.info( "Replaced configured security groups: configured={}, replaced with={}", originalGroups, template.getOptions().getGroups()); } else { LOG.debug( "Configured security groups at {} to: {}", location, template.getOptions().getGroups()); } }
protected SecurityGroup removePermission( final IpPermission permission, final SecurityGroup group, final SecurityGroupExtension securityApi) { LOG.debug("Removing permission from security group {}: {}", group.getName(), permission); Callable<SecurityGroup> callable = new Callable<SecurityGroup>() { @Override public SecurityGroup call() throws Exception { return securityApi.removeIpPermission(permission, group); } }; return runOperationWithRetry(callable); }
protected SecurityGroup addPermission( final IpPermission permission, final SecurityGroup group, final SecurityGroupExtension securityApi) { LOG.debug("Adding permission to security group {}: {}", group.getName(), permission); Callable<SecurityGroup> callable = new Callable<SecurityGroup>() { @Override public SecurityGroup call() throws Exception { try { return securityApi.addIpPermission(permission, group); } catch (AWSResponseException e) { if ("InvalidPermission.Duplicate".equals(e.getError().getCode())) { // already exists LOG.info( "Permission already exists for security group; continuing (logging underlying exception at debug): permission=" + permission + "; group=" + group); LOG.debug( "Permission already exists for security group; continuing: permission=" + permission + "; group=" + group, e); return null; } else { throw e; } } catch (Exception e) { Exceptions.propagateIfFatal(e); if (e.toString().contains("InvalidPermission.Duplicate")) { // belt-and-braces, in case // already exists LOG.info( "Permission already exists for security group; continuing (but unexpected exception type): permission=" + permission + "; group=" + group, e); return null; } else { throw Exceptions.propagate(e); } } } }; return runOperationWithRetry(callable); }
@Test public void testApplyWithGroup() { NovaSecurityGroupInZoneToSecurityGroup parser = createGroupParser(); SecurityGroupInZone origGroup = new SecurityGroupInZone(securityGroupWithGroup(), zone.getId()); SecurityGroup newGroup = parser.apply(origGroup); assertEquals( newGroup.getId(), origGroup.getZone() + "/" + origGroup.getSecurityGroup().getId()); assertEquals(newGroup.getProviderId(), origGroup.getSecurityGroup().getId()); assertEquals(newGroup.getName(), origGroup.getSecurityGroup().getName()); assertEquals(newGroup.getOwnerId(), origGroup.getSecurityGroup().getTenantId()); assertEquals( newGroup.getIpPermissions(), ImmutableSet.copyOf( transform( origGroup.getSecurityGroup().getRules(), NovaSecurityGroupToSecurityGroupTest.ruleConverter))); assertEquals(newGroup.getLocation().getId(), origGroup.getZone()); }