Package org.apache.whirr.service

Source Code of org.apache.whirr.service.FirewallManager

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.apache.whirr.service;

import static org.jclouds.scriptbuilder.domain.Statements.exec;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.apache.whirr.Cluster;
import org.apache.whirr.Cluster.Instance;
import org.apache.whirr.ClusterSpec;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.ec2.EC2ApiMetadata;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.domain.IpProtocol;
import org.jclouds.openstack.nova.v2_0.NovaApiMetadata;
import org.jclouds.openstack.nova.v2_0.domain.Ingress;
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.scriptbuilder.domain.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

public class FirewallManager {

  public static class StoredRule {
    private Rule rule;
    private List<String> cidrs;
    private Set<Instance> instances;
     
    public StoredRule(Rule rule, List<String> cidrs, Set<Instance> instances) {
      this.rule = rule;
      this.cidrs = cidrs;
      this.instances = instances;
    }

    /**
     * Get the Rule object for this stored rule.
     */
    public Rule rule() {
      return rule;
    }

    /**
     * Get the CIDRs for this stored rule.
     */
    public List<String> cidrs() {
      return cidrs;
    }

    /**
     * Get the set of Instances for this stored rule.
     */
    public Set<Instance> instances() {
      return instances;
    }
  }
   
  public static class Rule {

    public static Rule create() {
      return new Rule();
    }

    private String source;
    private Set<Instance> destinations;
    private Predicate<Instance> destinationPredicate;
    private int[] ports;

    private Rule() {
    }

    /**
     * @param source The allowed source IP for traffic. If not set, this will
     * default to {@link ClusterSpec#getClientCidrs()}, or, if that is not set,
     *  to the client's originating IP.
     */
    public Rule source(String source) {
      this.source = source;
      return this;
    }

    /**
     * @param destination The allowed destination instance.
     */
    public Rule destination(Instance destination) {
      this.destinations = Collections.singleton(destination);
      return this;
    }

    /**
     * @param destinations The allowed destination instances.
     */
    public Rule destination(Set<Instance> destinations) {
      this.destinations = destinations;
      return this;
    }

    /**
     * @param destinationPredicate A predicate which is used to evaluate the
     * allowed destination instances.
     */
    public Rule destination(Predicate<Instance> destinationPredicate) {
      this.destinationPredicate = destinationPredicate;
      return this;
    }

    /**
     * @param port The port on the destination which is to be opened. Overrides
     * any previous calls to {@link #port(int)} or {@link #ports(int...)}.
     */
    public Rule port(int port) {
      this.ports = new int[] { port };
      return this;
    }

    /**
     * @param ports The ports on the destination which are to be opened.
     * Overrides
     * any previous calls to {@link #port(int)} or {@link #ports(int...)}.
     */
    public Rule ports(int... ports) {
      this.ports = ports;
      return this;
    }
  }

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

  private ComputeServiceContext computeServiceContext;
  private ClusterSpec clusterSpec;
  private Cluster cluster;
  private Set<StoredRule> storedRules;
   
  public FirewallManager(ComputeServiceContext computeServiceContext,
                         ClusterSpec clusterSpec, Cluster cluster) {
    this.computeServiceContext = computeServiceContext;
    this.clusterSpec = clusterSpec;
    this.cluster = cluster;
    this.storedRules = Sets.newHashSet();
  }

  public void addRules(Rule... rules) throws IOException {
    for (Rule rule : rules) {
      addRule(rule);
    }
  }

  public void addRules(Set<Rule> rules) throws IOException {
    for (Rule rule : rules) {
      addRule(rule);
    }
  }

  /**
   * Rules are additive. If no source is set then it will default
   * to {@link ClusterSpec#getClientCidrs()}, or, if that is not set,
   * to the client's originating IP. If no destinations or ports
   * are set then the rule has not effect.
   *
   * @param rule The rule to add to the firewall.
   * @throws IOException
   */
  public void addRule(Rule rule) throws IOException {
    Set<Instance> instances = Sets.newHashSet();
    if (rule.destinations != null) {
      instances.addAll(rule.destinations);
    }
    if (rule.destinationPredicate != null) {
      instances.addAll(cluster.getInstancesMatching(rule.destinationPredicate));
    }
    List<String> cidrs;
    if (rule.source == null) {
      cidrs = clusterSpec.getClientCidrs();
      if (cidrs == null || cidrs.isEmpty()) {
        cidrs = Lists.newArrayList(getOriginatingIp());
      }
    } else {
      cidrs = Lists.newArrayList(rule.source + "/32");
    }

    storedRules.add(new StoredRule(rule, cidrs, instances));
  }

  /**
   * Logs information about the StoredRule we're adding
   * @param storedRule the StoredRule we're adding
   */
  private void logInstanceRules(StoredRule storedRule) {
    Iterable<String> instanceIds =
      Iterables.transform(storedRule.instances(), new Function<Instance, String>() {
          @Override
          public String apply(@Nullable Instance instance) {
            return instance == null ? "<null>" : instance.getId();
          }
        });
     
     
     
    LOG.info("Authorizing firewall ingress to {} on ports {} for {}",
             new Object[] { instanceIds, storedRule.rule().ports, storedRule.cidrs() });
  }

  /**
   * Authorizes all rules via jclouds security groups interface.
   */
  public void authorizeAllRules() {
    for (StoredRule storedRule : storedRules) {
      logInstanceRules(storedRule);
      authorizeIngress(computeServiceContext, storedRule.instances(),
                       clusterSpec, storedRule.cidrs(), storedRule.rule().ports);
    }
  }

  /**
   * Returns a list of Statements for executing iptables for the stored rules.
   * @return List of iptables Statements.
   */
  public List<Statement> getRulesAsStatements() {
    List<Statement> ruleStatements = Lists.newArrayList();

    for (StoredRule storedRule : storedRules) {
      logInstanceRules(storedRule);
      for (String cidr : storedRule.cidrs()) {
        for (int port : storedRule.rule().ports) {
          ruleStatements.add(exec(String.format("iptables -I INPUT 1 -p tcp --dport %d --source %s -j ACCEPT || true",
                                                port, cidr)));
        }
      }
    }

    ruleStatements.add(exec("iptables-save || true"));

    return ruleStatements;
  }

  /**
   * @return the IP address of the client on which this code is running.
   * @throws IOException
   */
  private String getOriginatingIp() throws IOException {
    if ("stub".equals(clusterSpec.getProvider())) {
      return "62.217.232.123";
    }

    URL url = new URL("http://checkip.amazonaws.com/");
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.connect();
    return IOUtils.toString(connection.getInputStream()).trim() + "/32";
  }

  public static void authorizeIngress(ComputeServiceContext computeServiceContext,
                                      Set<Instance> instances, ClusterSpec clusterSpec, List<String> cidrs, int... ports) {

    if (EC2ApiMetadata.CONTEXT_TOKEN.isAssignableFrom(computeServiceContext.getBackendType())) {
      // This code (or something like it) may be added to jclouds (see
      // http://code.google.com/p/jclouds/issues/detail?id=336).
      // Until then we need this temporary workaround.
      String region = AWSUtils.parseHandle(Iterables.get(instances, 0).getId())[0];
      EC2Client ec2Client = computeServiceContext.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi();
      String groupName = "jclouds#" + clusterSpec.getClusterName();
      for (String cidr : cidrs) {
        for (int port : ports) {
          try {
            ec2Client.getSecurityGroupServices()
              .authorizeSecurityGroupIngressInRegion(region, groupName,
                                                     IpProtocol.TCP, port, port, cidr);
          } catch(IllegalStateException e) {
            LOG.warn(e.getMessage());
            /* ignore, it means that this permission was already granted */
          }
        }
      }
    } else if (NovaApiMetadata.CONTEXT_TOKEN.isAssignableFrom(computeServiceContext.getBackendType())) {
      // This code (or something like it) may be added to jclouds (see
      // http://code.google.com/p/jclouds/issues/detail?id=336).
      // Until then we need this temporary workaround.
      Optional<? extends SecurityGroupApi> securityGroupApi = computeServiceContext.unwrap(NovaApiMetadata.CONTEXT_TOKEN)
        .getApi()
        .getSecurityGroupExtensionForZone(clusterSpec.getTemplate().getLocationId());

      if (securityGroupApi.isPresent()) {
        final String groupName = "jclouds-" + clusterSpec.getClusterName();
        Optional<? extends SecurityGroup> group = securityGroupApi.get().list().firstMatch(new Predicate<SecurityGroup>() {
            @Override
            public boolean apply(SecurityGroup secGrp) {
              return secGrp.getName().equals(groupName);
            }
          });

        if (group.isPresent()) {
          for (String cidr : cidrs) {
            for (int port : ports) {
              try {
                securityGroupApi.get().createRuleAllowingCidrBlock(group.get().getId(),
                                                                   Ingress.builder()
                                                                   .ipProtocol(org.jclouds.openstack.nova.v2_0.domain.IpProtocol.TCP)
                                                                   .fromPort(port).toPort(port).build(),
                                                                   cidr);
                       
              } catch(IllegalStateException e) {
                LOG.warn(e.getMessage());
                /* ignore, it means that this permission was already granted */
              }
            }
          }
        } else {
          LOG.warn("Expected security group " + groupName + " does not exist.");
        }
      } else {
        LOG.warn("OpenStack security group extension not available for this cloud.");
      }
    }
  }
}
TOP

Related Classes of org.apache.whirr.service.FirewallManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.