org.zalando.stups.fullstop.plugin.RunInstancePlugin.java Source code

Java tutorial

Introduction

Here is the source code for org.zalando.stups.fullstop.plugin.RunInstancePlugin.java

Source

/**
 * Copyright 2015 Zalando SE
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zalando.stups.fullstop.plugin;

import com.amazonaws.AmazonClientException;
import com.amazonaws.regions.Region;
import com.amazonaws.services.cloudtrail.processinglibrary.model.CloudTrailEvent;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.DescribeSecurityGroupsRequest;
import com.amazonaws.services.ec2.model.DescribeSecurityGroupsResult;
import com.amazonaws.services.ec2.model.IpPermission;
import com.amazonaws.services.ec2.model.SecurityGroup;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.zalando.stups.fullstop.aws.ClientProvider;
import org.zalando.stups.fullstop.events.CloudTrailEventPredicate;
import org.zalando.stups.fullstop.violation.ViolationStore;
import org.zalando.stups.fullstop.violation.entity.ViolationBuilder;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.util.stream.Collectors.toList;
import static org.zalando.stups.fullstop.events.CloudTrailEventPredicate.fromSource;
import static org.zalando.stups.fullstop.events.CloudTrailEventPredicate.withName;
import static org.zalando.stups.fullstop.events.CloudtrailEventSupport.PUBLIC_IP_JSON_PATH;
import static org.zalando.stups.fullstop.events.CloudtrailEventSupport.SECURITY_GROUP_IDS_JSON_PATH;
import static org.zalando.stups.fullstop.events.CloudtrailEventSupport.getAccountId;
import static org.zalando.stups.fullstop.events.CloudtrailEventSupport.getRegion;
import static org.zalando.stups.fullstop.events.CloudtrailEventSupport.read;
import static org.zalando.stups.fullstop.plugin.Bool.not;
import static org.zalando.stups.fullstop.plugin.IpPermissionPredicates.withToPort;

/**
 *
 * @author  jbellmann
 */
@Component
public class RunInstancePlugin extends AbstractFullstopPlugin {

    private static final Logger LOG = LoggerFactory.getLogger(RunInstancePlugin.class);

    private static final String EC2_SOURCE_EVENTS = "ec2.amazonaws.com";
    private static final String EVENT_NAME = "RunInstances";

    private final CloudTrailEventPredicate eventFilter = fromSource(EC2_SOURCE_EVENTS)
            .andWith(withName(EVENT_NAME));

    private final ViolationStore violationStore;

    private final ClientProvider clientProvider;

    private final Function<SecurityGroup, String> transformer = new SecurityGroupToString();

    Predicate<IpPermission> filter = withToPort(443).negate().and(withToPort(22).negate());

    @Autowired
    public RunInstancePlugin(final ClientProvider clientProvider, final ViolationStore violationStore) {
        this.clientProvider = clientProvider;
        this.violationStore = violationStore;
    }

    @Override
    public boolean supports(final CloudTrailEvent event) {

        return eventFilter.test(event);
    }

    @Override
    public void processEvent(final CloudTrailEvent event) {

        if (not(hasPublicIp(event))) {

            // no public IP, so skip more checks
            return;
        }

        Optional<List<SecurityGroup>> securityGroupList = getSecurityGroups(event);
        if (not(securityGroupList.isPresent())) {

            // no securityGroups, maybe instance already down
            return;
        }

        if (securityGroupList.get().stream().anyMatch(SecurityGroupPredicates.anyMatch(filter))) {

            String message = String.format("SecurityGroups configured with ports not allowed: %s",
                    getPorts(securityGroupList.get()));
            violationStore.save(new ViolationBuilder(message).withEvent(event).build());
        }
    }

    protected Set<String> getPorts(final List<SecurityGroup> securityGroups) {

        Set<String> result = Sets.newHashSet();
        for (SecurityGroup sg : securityGroups) {
            List<IpPermission> ipPermissions = sg.getIpPermissions();
            for (IpPermission p : ipPermissions) {
                result.add(p.getToPort().toString());
            }
        }

        return result;
    }

    protected boolean hasPublicIp(final CloudTrailEvent cloudTrailEvent) {
        return !read(cloudTrailEvent, PUBLIC_IP_JSON_PATH, true).isEmpty();
    }

    protected List<String> transformSecurityGroupsIntoStrings(final List<SecurityGroup> securityGroups) {

        return securityGroups.stream().map(transformer).collect(toList());
    }

    protected List<String> readSecurityGroupIds(final CloudTrailEvent cloudTrailEvent) {

        return read(cloudTrailEvent, SECURITY_GROUP_IDS_JSON_PATH, true);
    }

    protected Optional<List<SecurityGroup>> getSecurityGroups(final CloudTrailEvent event) {

        List<String> securityGroupIds = readSecurityGroupIds(event);

        return getSecurityGroupsForIds(securityGroupIds, event);
    }

    protected Optional<List<SecurityGroup>> getSecurityGroupsForIds(final List<String> securityGroupIds,
            final CloudTrailEvent event) {

        Region region = getRegion(event);
        String accountId = getAccountId(event);

        AmazonEC2Client amazonEC2Client = getClient(accountId, region);

        if (amazonEC2Client == null) {
            throw new RuntimeException(
                    String.format("Somehow we could not create an Client with accountId: %s and region: %s",
                            accountId, region.toString()));
        } else {
            try {
                DescribeSecurityGroupsRequest request = new DescribeSecurityGroupsRequest();
                request.setGroupIds(securityGroupIds);

                DescribeSecurityGroupsResult result = amazonEC2Client.describeSecurityGroups(request);

                return Optional.of(result.getSecurityGroups());
            } catch (AmazonClientException e) {

                // TODO, better ways?
                String message = String.format("Unable to get SecurityGroups for SecurityGroupIds [%s] | %s",
                        securityGroupIds.toString(), e.getMessage());

                violationStore.save(new ViolationBuilder(message).withEvent(event).build());
                return Optional.empty();
            }

        }

    }

    protected AmazonEC2Client getClient(final String accountId, final Region region) {
        return clientProvider.getClient(AmazonEC2Client.class, accountId, region);
    }

}