All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.proofpoint.discovery.IpHostnameAuthManager Maven / Gradle / Ivy

The newest version!
package com.proofpoint.discovery;

import com.google.common.collect.Sets;
import com.proofpoint.audit.AuditLogger;
import com.proofpoint.discovery.client.ServiceDescriptor;
import com.proofpoint.discovery.client.ServiceInventoryConfig;
import com.proofpoint.discovery.client.ServiceSelector;
import com.proofpoint.log.Logger;
import com.proofpoint.units.Duration;

import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.ForbiddenException;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.proofpoint.discovery.AuthAuditRecord.auditRecord;
import static java.util.Objects.requireNonNull;

public class IpHostnameAuthManager implements AuthManager
{
    private static final Logger logger = Logger.get(IpHostnameAuthManager.class);
    private final DynamicStore dynamicStore;
    private final ServiceSelector discoverySelector;
    private final Duration updateInterval;
    private final Set discoveryAddrs = Sets.newConcurrentHashSet();
    private final ScheduledExecutorService executor;
    private final AuditLogger auditLogger;
    private Future future;

    @Inject
    public IpHostnameAuthManager(DynamicStore dynamicStore,
            ServiceSelector discoverySelector,
            ServiceInventoryConfig config,
            @ForAuthManager ScheduledExecutorService executor,
            AuditLogger auditLogger)
    {
        this.dynamicStore = requireNonNull(dynamicStore, "dynamicStore is null");
        this.discoverySelector = requireNonNull(discoverySelector, "discoverySelector is null");
        this.updateInterval = requireNonNull(config, "config is null").getUpdateInterval();
        this.executor = requireNonNull(executor, "executor is null");
        this.auditLogger = requireNonNull(auditLogger, "auditLogger is null");
    }

    @PreDestroy
    private void preDestroy()
    {
        if (future != null) {
            future.cancel(true);
        }
        executor.shutdownNow();
        future = null;
    }

    @PostConstruct
    private void startDiscoveryRefresh()
    {
        if (future == null) {
            future = executor.scheduleWithFixedDelay(() -> {
                try {
                    updateDiscoveryAddrs(discoverySelector.selectAllServices());
                }
                catch (Exception e) {
                    logger.warn(e, "Unable to refresh discovery servers. No replication will be permitted");
                }
            }, 0, updateInterval.toMillis(), TimeUnit.MILLISECONDS);
        }
    }

    private void updateDiscoveryAddrs(List descriptors)
    {
        Set otherDiscoveryHosts = descriptors.stream()
                .map(d -> d.getProperties().get("http"))
                .map(URI::create)
                .map(u -> {
                    try {
                        return InetAddress.getAllByName(u.getHost());
                    }
                    catch (UnknownHostException e) {
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .flatMap(Arrays::stream)
                .collect(Collectors.toSet());
        discoveryAddrs.removeIf(entry -> !otherDiscoveryHosts.contains(entry));
        discoveryAddrs.addAll(otherDiscoveryHosts);
    }

    @Override
    public void checkAuthAnnounce(Id nodeId, DynamicAnnouncement announcement, HttpServletRequest request)
    {
        try {
            InetAddress requesterAddress = InetAddress.getByName(request.getRemoteAddr());
            String nodeAnnouncer = dynamicStore.getAnnouncer(nodeId);
            if (mismatchedAnnouncer(nodeAnnouncer, requesterAddress)) {
                //NodeId was previously announced by a different IP
                auditLogger.audit(auditRecord("IP %s tried to re-announce node %s owned by IP %s", requesterAddress.getHostAddress(), nodeId.toString(), nodeAnnouncer));
                throw new ForbiddenException();
            }
            Set newAnnouncements = announcement.getServiceAnnouncements();
            for (DynamicServiceAnnouncement newAnnouncement : newAnnouncements) {
                Map properties = newAnnouncement.getProperties();
                if (invalidHostnameIp(properties.get("http"), requesterAddress) ||
                        invalidHostnameIp(properties.get("https"), requesterAddress) ||
                        invalidHostnameIp(properties.get("admin"), requesterAddress)) {
                    //Service is announcing a host with a different set of IPs
                    throw new ForbiddenException();
                }
            }
        }
        catch (UnknownHostException e) {
            //Unable to validate an IP or look up a hostname
            auditLogger.audit(auditRecord(e.getMessage()));
            throw new ForbiddenException();
        }
    }

    @Override
    public void checkAuthDelete(Id nodeId, HttpServletRequest request)
    {
        try {
            InetAddress requesterAddress = InetAddress.getByName(request.getRemoteAddr());
            String nodeAnnouncer = dynamicStore.getAnnouncer(nodeId);
            if (mismatchedAnnouncer(nodeAnnouncer, requesterAddress)) {
                auditLogger.audit(auditRecord("IP %s tried to delete node %s owned by IP %s", requesterAddress.getHostAddress(), nodeId.toString(), nodeAnnouncer));
                throw new ForbiddenException();
            }
        }
        catch (UnknownHostException e) {
            auditLogger.audit(auditRecord(e.getMessage()));
            throw new ForbiddenException();
        }
    }

    @Override
    public void checkAuthReplicate(HttpServletRequest request)
    {
        try {
            InetAddress requesterAddress = InetAddress.getByName(request.getRemoteAddr());
            if (!discoveryAddrs.contains(requesterAddress)) {
                auditLogger.audit(auditRecord("IP %s tried to replicate as Discovery", requesterAddress.getHostAddress()));
                throw new ForbiddenException();
            }
        }
        catch (UnknownHostException e) {
            auditLogger.audit(auditRecord(e.getMessage()));
            throw new ForbiddenException();
        }
    }

    private boolean mismatchedAnnouncer(@Nullable String nodeAnnouncer, InetAddress requesterAddr)
            throws UnknownHostException
    {
        return nodeAnnouncer != null && !requesterAddr.equals(InetAddress.getByName(nodeAnnouncer));
    }

    private boolean invalidHostnameIp(String hostname, InetAddress requesterAddress)
    {
        if (hostname == null) {
            return false;
        }
        try {
            URI hostUri = URI.create(hostname);
            InetAddress[] hostIps = InetAddress.getAllByName(hostUri.getHost());
            if (!Arrays.asList(hostIps).contains(requesterAddress)) {
                auditLogger.audit(auditRecord("IP %s tried to announce other host %s", requesterAddress.getHostAddress(), hostUri.getHost()));
                return true;
            }
            return false;
        }
        catch (UnknownHostException e) {
            auditLogger.audit(auditRecord(e.getMessage()));
            return true;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy