com.eucalyptus.www.X509Download.java Source code

Java tutorial

Introduction

Here is the source code for com.eucalyptus.www.X509Download.java

Source

/*************************************************************************
 * Copyright 2009-2014 Eucalyptus Systems, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 *
 * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
 * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
 * additional information or have any questions.
 *
 * This file may incorporate work covered under the following copyright
 * and permission notice:
 *
 *   Software License Agreement (BSD License)
 *
 *   Copyright (c) 2008, Regents of the University of California
 *   All rights reserved.
 *
 *   Redistribution and use of this software in source and binary forms,
 *   with or without modification, are permitted provided that the
 *   following conditions are met:
 *
 *     Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *     Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer
 *     in the documentation and/or other materials provided with the
 *     distribution.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
 *   THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
 *   COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
 *   AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
 *   IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
 *   SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
 *   WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
 *   REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
 *   IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
 *   NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
 ************************************************************************/

package com.eucalyptus.www;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.X509Certificate;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.eucalyptus.auth.AuthenticationProperties;
import com.eucalyptus.bootstrap.Host;
import com.eucalyptus.bootstrap.Hosts;
import com.eucalyptus.cloudformation.CloudFormation;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.log4j.Logger;

import com.eucalyptus.auth.Accounts;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.principal.AccessKey;
import com.eucalyptus.auth.principal.Account;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.auth.principal.User.RegistrationStatus;
import com.eucalyptus.component.ComponentId;
import com.eucalyptus.loadbalancing.common.LoadBalancing;
import com.eucalyptus.cloudwatch.common.CloudWatch;
import com.eucalyptus.component.ServiceBuilder;
import com.eucalyptus.component.ServiceBuilders;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.ServiceUris;
import com.eucalyptus.component.Topology;
import com.eucalyptus.component.auth.SystemCredentials;
import com.eucalyptus.component.id.Euare;
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.component.id.Tokens;
import com.eucalyptus.compute.common.Compute;
import com.eucalyptus.crypto.Certs;
import com.eucalyptus.crypto.Crypto;
import com.eucalyptus.crypto.util.PEMFiles;
import com.eucalyptus.objectstorage.ObjectStorage;
import com.eucalyptus.autoscaling.common.AutoScaling;
import com.eucalyptus.util.Cidr;
import com.eucalyptus.util.Internets;
import com.eucalyptus.ws.StackConfiguration;
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
import com.google.common.net.InetAddresses;

public class X509Download extends HttpServlet {

    private static Logger LOG = Logger.getLogger(X509Download.class);
    public static String NAME_SHORT = "euca2";
    public static String PARAMETER_USERNAME = "user";
    public static String PARAMETER_ACCOUNTNAME = "account";
    public static String PARAMETER_KEYNAME = "keyName";
    public static String PARAMETER_CODE = "code";

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String code = request.getParameter(PARAMETER_CODE);
        String userName = request.getParameter(PARAMETER_USERNAME);
        String accountName = request.getParameter(PARAMETER_ACCOUNTNAME);
        String mimetype = "application/zip";
        if (accountName == null || "".equals(accountName)) {
            hasError(HttpServletResponse.SC_BAD_REQUEST, "No account name provided", response);
            return;
        }
        if (userName == null || "".equals(userName)) {
            hasError(HttpServletResponse.SC_BAD_REQUEST, "No user name provided", response);
            return;
        }
        if (code == null || "".equals(code)) {
            hasError(HttpServletResponse.SC_BAD_REQUEST, "Wrong user security code", response);
            return;
        }

        User user = null;
        try {
            Account account = Accounts.lookupAccountByName(accountName);
            user = account.lookupUserByName(userName);
            if (!user.isEnabled() || !RegistrationStatus.CONFIRMED.equals(user.getRegistrationStatus())) {
                hasError(HttpServletResponse.SC_FORBIDDEN, "Access is not authorized", response);
                return;
            }
        } catch (AuthException e) {
            hasError(HttpServletResponse.SC_BAD_REQUEST, "User does not exist", response);
            return;
        } catch (Exception e) {
            hasError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Fail to retrieve user data", response);
            return;
        }
        try {
            if (!code.equals(user.resetToken())) {
                hasError(HttpServletResponse.SC_FORBIDDEN, "Access is not authorized", response);
                return;
            }
        } catch (Exception e) {
            hasError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Can not reset user security code", response);
            return;
        }
        response.setContentType(mimetype);
        response.setHeader("Content-Disposition",
                "attachment; filename=\"" + X509Download.NAME_SHORT + "-" + userName + "-x509.zip\"");
        LOG.info("pushing out the X509 certificate for user " + userName);

        byte[] x509zip = null;
        try {
            x509zip = getX509Zip(user);
        } catch (Exception e) {
            LOG.debug(e, e);
            hasError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Fail to return user credentials", response);
            return;
        }
        try {
            ServletOutputStream op = response.getOutputStream();

            response.setContentLength(x509zip.length);

            op.write(x509zip);
            op.flush();

        } catch (Exception e) {
            LOG.error(e, e);
        }
    }

    public static void hasError(int statusCode, String message, HttpServletResponse response) {
        try {
            response.setStatus(statusCode);
            response.setContentType("text/plain; charset=utf-8");
            response.getWriter().print(getError(message));
            response.getWriter().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static byte[] getX509Zip(User u) throws Exception {
        X509Certificate cloudCert = null;
        final X509Certificate x509;
        String userAccessKey = null;
        String userSecretKey = null;
        KeyPair keyPair = null;
        try {
            for (AccessKey k : u.getKeys()) {
                if (k.isActive()) {
                    userAccessKey = k.getAccessKey();
                    userSecretKey = k.getSecretKey();
                }
            }
            if (userAccessKey == null) {
                AccessKey k = u.createKey();
                userAccessKey = k.getAccessKey();
                userSecretKey = k.getSecretKey();
            }
            keyPair = Certs.generateKeyPair();
            x509 = Certs.generateCertificate(keyPair, u.getName());
            x509.checkValidity();
            u.addCertificate(x509);
            cloudCert = SystemCredentials.lookup(Eucalyptus.class).getCertificate();
        } catch (Exception e) {
            LOG.fatal(e, e);
            throw e;
        }
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(byteOut);
        ZipArchiveEntry entry = null;
        String fingerPrint = Certs.getFingerPrint(keyPair.getPublic());
        if (fingerPrint != null) {
            String baseName = X509Download.NAME_SHORT + "-" + u.getName() + "-"
                    + fingerPrint.replaceAll(":", "").toLowerCase().substring(0, 8);

            zipOut.setComment("To setup the environment run: source /path/to/eucarc");
            StringBuilder sb = new StringBuilder();
            //TODO:GRZE:FIXME velocity
            String userNumber = u.getAccount().getAccountNumber();
            sb.append("EUCA_KEY_DIR=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd -P)");
            final Optional<String> computeUrl = remotePublicify(Compute.class);
            if (computeUrl.isPresent()) {
                sb.append(entryFor("EC2_URL", null, computeUrl));
            } else {
                sb.append("\necho WARN:  Eucalyptus URL is not configured. >&2");
                ServiceBuilder<? extends ServiceConfiguration> builder = ServiceBuilders.lookup(Compute.class);
                ServiceConfiguration localConfig = builder.newInstance(Internets.localHostAddress(),
                        Internets.localHostAddress(), Internets.localHostAddress(), Eucalyptus.INSTANCE.getPort());
                sb.append("\nexport EC2_URL=" + ServiceUris.remotePublicify(localConfig));
            }

            sb.append(entryFor("S3_URL", "An OSG is either not registered or not configured. S3_URL is not set. "
                    + "Please register an OSG and/or set a valid s3 endpoint and download credentials again. "
                    + "Or set S3_URL manually to http://OSG-IP:8773/services/objectstorage",
                    remotePublicify(ObjectStorage.class)));
            sb.append(entryFor("EUARE_URL", "EUARE URL is not configured.", remotePublicify(Euare.class)));
            sb.append(entryFor("TOKEN_URL", "TOKEN URL is not configured.", remotePublicify(Tokens.class)));
            sb.append(entryFor("AWS_AUTO_SCALING_URL", "Auto Scaling service URL is not configured.",
                    remotePublicify(AutoScaling.class)));
            sb.append(entryFor("AWS_CLOUDFORMATION_URL", null, remotePublicify(CloudFormation.class)));
            sb.append(entryFor("AWS_CLOUDWATCH_URL", "Cloud Watch service URL is not configured.",
                    remotePublicify(CloudWatch.class)));
            sb.append(entryFor("AWS_ELB_URL", "Load Balancing service URL is not configured.",
                    remotePublicify(LoadBalancing.class)));
            sb.append("\nexport EUSTORE_URL=" + StackConfiguration.DEFAULT_EUSTORE_URL);
            sb.append("\nexport EC2_PRIVATE_KEY=${EUCA_KEY_DIR}/" + baseName + "-pk.pem");
            sb.append("\nexport EC2_CERT=${EUCA_KEY_DIR}/" + baseName + "-cert.pem");
            sb.append("\nexport EC2_JVM_ARGS=-Djavax.net.ssl.trustStore=${EUCA_KEY_DIR}/jssecacerts");
            sb.append("\nexport EUCALYPTUS_CERT=${EUCA_KEY_DIR}/cloud-cert.pem");
            sb.append("\nexport EC2_ACCOUNT_NUMBER='" + u.getAccount().getAccountNumber() + "'");
            sb.append("\nexport EC2_ACCESS_KEY='" + userAccessKey + "'");
            sb.append("\nexport EC2_SECRET_KEY='" + userSecretKey + "'");
            sb.append("\nexport AWS_ACCESS_KEY='" + userAccessKey + "'");
            sb.append("\nexport AWS_SECRET_KEY='" + userSecretKey + "'");
            sb.append("\nexport AWS_CREDENTIAL_FILE=${EUCA_KEY_DIR}/iamrc");
            sb.append("\nexport EC2_USER_ID='" + userNumber + "'");
            sb.append(
                    "\nalias ec2-bundle-image=\"ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user ${EC2_ACCOUNT_NUMBER} --ec2cert ${EUCALYPTUS_CERT}\"");
            sb.append(
                    "\nalias ec2-upload-bundle=\"ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL}\"");
            sb.append("\n");
            zipOut.putArchiveEntry(entry = new ZipArchiveEntry("eucarc"));
            entry.setUnixMode(0600);
            zipOut.write(sb.toString().getBytes("UTF-8"));
            zipOut.closeArchiveEntry();

            sb = new StringBuilder();
            sb.append("AWSAccessKeyId=").append(userAccessKey).append('\n');
            sb.append("AWSSecretKey=").append(userSecretKey);
            zipOut.putArchiveEntry(entry = new ZipArchiveEntry("iamrc"));
            entry.setUnixMode(0600);
            zipOut.write(sb.toString().getBytes("UTF-8"));
            zipOut.closeArchiveEntry();

            /** write the private key to the zip stream **/
            zipOut.putArchiveEntry(entry = new ZipArchiveEntry("cloud-cert.pem"));
            entry.setUnixMode(0600);
            zipOut.write(PEMFiles.getBytes(cloudCert));
            zipOut.closeArchiveEntry();

            zipOut.putArchiveEntry(entry = new ZipArchiveEntry("jssecacerts"));
            entry.setUnixMode(0600);
            KeyStore tempKs = KeyStore.getInstance("jks");
            tempKs.load(null);
            tempKs.setCertificateEntry("eucalyptus", cloudCert);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            tempKs.store(bos, "changeit".toCharArray());
            zipOut.write(bos.toByteArray());
            zipOut.closeArchiveEntry();

            /** write the private key to the zip stream **/
            zipOut.putArchiveEntry(entry = new ZipArchiveEntry(baseName + "-pk.pem"));
            entry.setUnixMode(0600);
            zipOut.write(PEMFiles.getBytes("RSA PRIVATE KEY",
                    Crypto.getCertificateProvider().getEncoded(keyPair.getPrivate())));
            zipOut.closeArchiveEntry();

            /** write the X509 certificate to the zip stream **/
            zipOut.putArchiveEntry(entry = new ZipArchiveEntry(baseName + "-cert.pem"));
            entry.setUnixMode(0600);
            zipOut.write(PEMFiles.getBytes(x509));
            zipOut.closeArchiveEntry();
        }
        /** close the zip output stream and return the bytes **/
        zipOut.close();
        return byteOut.toByteArray();
    }

    private static Optional<String> remotePublicify(final Class<? extends ComponentId> componentClass) {
        Optional<String> url = Optional.absent();
        if (Topology.isEnabled(componentClass))
            try {
                url = Optional.of(hostMap(ServiceUris.remotePublicify(Topology.lookup(componentClass))).toString());
            } catch (final Exception e) {
                LOG.error("Failed to get URL for service " + componentClass.getSimpleName(), e);
            }
        return url;
    }

    @SuppressWarnings("ConstantConditions")
    private static URI hostMap(final URI uri) {
        final Optional<Cidr> hostMatcher = InetAddresses.isInetAddress(uri.getHost())
                ? Cidr.parse().apply(AuthenticationProperties.CREDENTIAL_DOWNLOAD_HOST_MATCH)
                : Optional.<Cidr>absent();
        if (hostMatcher.isPresent()) {
            final Host host = Hosts.lookup(InetAddresses.forString(uri.getHost()));
            if (host != null) {
                final Optional<InetAddress> mappedHost = Iterables.tryFind(host.getHostAddresses(),
                        hostMatcher.get());
                if (mappedHost.isPresent()) {
                    return URI
                            .create(uri.toString().replaceFirst(uri.getHost(), mappedHost.get().getHostAddress()));
                }
            }
        }
        return uri;
    }

    private static String entryFor(final String variable, final String warning, final Optional<String> url) {
        if (url.isPresent()) {
            return "\nexport " + variable + "=" + url.get();
        } else if (warning != null) {
            return "\necho WARN:  " + warning + " >&2";
        } else {
            return "";
        }
    }

    private static String getError(String message) {
        StringBuilder builder = new StringBuilder();
        builder.append("Getting credentials failed:\n");
        builder.append(message);
        return builder.toString();
    }

}