Jhipster Microservice: Zuul Proxy Access Control: отфильтрованный несанкционированный доступ на конечной точке - PullRequest
0 голосов
/ 21 февраля 2019

У меня есть набор Jhipster Microservices, Consul и один шлюз (назовем его Gateway1), использующий Keycloak / OAuth2SSO для аутентификации.Я хочу включить несколько шлюзов (каждый обслуживающий отдельные приложения) и иметь дополнительные шлюзы (Gateway2, Gateway3 и т. Д.) Для аутентификации прокси-сервера к существующему шлюзу (Gateway1).

Я попытался добавить следующий маршрут к Gateway1 для обработкиthis;

zuul: # those values must be configured depending on the application specific needs
sensitive-headers: Cookie,Set-Cookie #see https://github.com/spring-cloud/spring-cloud-netflix/issues/3126
host:
    max-total-connections: 1000
    max-per-route-connections: 100
semaphore:
    max-semaphores: 500
ignoredServices: 'consul, Gateway1' # Prevent Consul and the "Gateway1" app itself from having a Zuul route automatically created for them
routes:
   Gateway1-uaa:
      path: /api/account/**
      serviceId: Gateway1
      sensitive-headers:

Как только я добавляю этот маршрут к Gateway1, после аутентификации я получаю следующие ошибки:

Контроль доступа: отфильтрованный несанкционированный доступ к конечной точке / api /account

Когда я нажимаю / api / authenticate, он возвращает моего пользователя.Так что я подозреваю, что это проблема с передачей токена через прокси?

Я попытался установить следующее безуспешно

  • ignoredServices в *,
  • sens-headers to empty,
  • ignore-security-headers - false.
  • отключение csrf, добавив .permitAll
  • к маршруту api / account и api / **

Есть мысли о том, почему это может не сработать?

ОБНОВЛЕНИЕ

Я выяснил, что ошибка вызывается классом AccessControlв моем проекте (это проект, сгенерированный Jhipster, поэтому он не знаком со всем кодом).

Маршрут для api / account / **, а вызов для api / account.В фильтре он проверяет, должен ли он фильтровать конечные точки из авторизованного списка и имеет следующий код:

          // If this route correspond to the current request URI
        // We do a substring to remove the "**" at the end of the route URL
        if (requestUri.startsWith(serviceUrl.substring(0, serviceUrl.length() - 3))) {
            return !isAuthorizedRequest(serviceUrl, serviceName, requestUri);
        }

Ну и requestUri.startsWith возвращает false, когда строка является полным соответствием (api / account vs api /Я предполагаю, что он ожидал / в конце. Мне придется вернуться и протестировать, используя api / account / *, чтобы увидеть, будет ли этот код работать тогда, как ожидалось.

Пока что яизменил его на 3 пробела, но теперь я, кажется, неоднократно выполнял DDoSing службу при совершении одного вызова API / учетной записи -

com.netflix.zuul.exception.ZuulException: Filter threw Exception
    at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:227)
    at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157)
    at com.netflix.zuul.FilterProcessor.error(FilterProcessor.java:105)
    at com.netflix.zuul.ZuulRunner.error(ZuulRunner.java:112)
    at com.netflix.zuul.http.ZuulServlet.error(ZuulServlet.java:145)
    at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:83)
    at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:165)
    at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:44)
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:52)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:981)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:873)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:858)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at com.st.aperture.gateway.config.SimpleCORSFilter.doFilter(SimpleCORSFilter.java:41)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:117)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:106)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at com.st.aperture.gateway.config.OAuth2Configuration$1.doFilterInternal(OAuth2Configuration.java:55)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:65)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
    at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:336)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.reflect.UndeclaredThrowableException: null
    at org.springframework.util.ReflectionUtils.rethrowRuntimeException(ReflectionUtils.java:354)
    at org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter.run(SendErrorFilter.java:99)
    at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:117)
    at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193)
    ... 119 common frames omitted
Caused by: java.io.IOException: An established connection was aborted by the software in your host machine
    at sun.nio.ch.SocketDispatcher.write0(Native Method)
    at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51)
    at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
    at sun.nio.ch.IOUtil.write(IOUtil.java:65)
    at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471)
    at org.xnio.nio.NioSocketConduit.write(NioSocketConduit.java:164)
    at io.undertow.server.protocol.http.HttpResponseConduit.write(HttpResponseConduit.java:609)
    at io.undertow.conduits.ChunkedStreamSinkConduit.flush(ChunkedStreamSinkConduit.java:269)
    at org.xnio.conduits.ConduitStreamSinkChannel.flush(ConduitStreamSinkChannel.java:162)
    at io.undertow.channels.DetachableStreamSinkChannel.flush(DetachableStreamSinkChannel.java:119)
    at org.xnio.channels.Channels.flushBlocking(Channels.java:63)
    at io.undertow.servlet.spec.ServletOutputStreamImpl.close(ServletOutputStreamImpl.java:617)
    at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextServletOutputStream.close(OnCommittedResponseWrapper.java:520)
    at io.undertow.servlet.spec.RequestDispatcherImpl.forwardImpl(RequestDispatcherImpl.java:236)
    at io.undertow.servlet.spec.RequestDispatcherImpl.forwardImplSetup(RequestDispatcherImpl.java:147)
    at io.undertow.servlet.spec.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:111)
    at org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequestDispatcher.forward(HeaderWriterFilter.java:143)
    at org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter.run(SendErrorFilter.java:94)
    ... 121 common frames omitted

Мне интересно, есть ли переадресация обратно на API / учетную запись или подобноепуть ... поиск продолжается!

Ответы [ 2 ]

0 голосов
/ 23 февраля 2019

Итак, раздраженный тем, что я могу что-то сломать, переименовав API и что Zuul плохо справляется с управлением префиксами, я решил, что лучше всего будет использовать собственный фильтр, который обрабатывает;

  1. удаление префиксов , т. е. всего, что находится перед путем, который не соответствует пути (например, отправка gw / api / account на мой маршрут / api / account не должна отправлять service1 / gw / api / account. Обратите внимание на мою реализациюне будет поддерживать сложные шаблоны сопоставления, такие как / api / ** / account /)
  2. не маршрутизировать «локальные» запросы , то есть просто пересылать запрос, если у запроса нет службыимя в нем (например, вызов api / account со шлюза, не должен запускать мою / api / account к маршруту шлюза. И определенно не 50 раз kthx Zuul. Не горько.)
  3. , несмотря на то, чтовышеуказанные правила, позволяющие другим запросам свободно маршрутизировать

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.FORWARD_TO_KEY;

import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.context.RequestContext;
import com.st.aperture.gateway.gateway.filters.RequestRewriter;
import com.netflix.zuul.ZuulFilter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;

public class SimplePreFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(SimplePreFilter.class);
    private ZuulProperties properties;
    private Map<String, ZuulRoute> routes;
    private DiscoveryClient discovery;

    public SimplePreFilter(ZuulProperties properties, DiscoveryClient discovery) {
        this.properties = properties;
        this.discovery = discovery;
        this.routes = this.properties.getRoutes();
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {

        return true;
    }

    @Override
    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String requestUrl = ctx.getRequest().getRequestURI().toString();

        List<String> services = discovery.getServices();

        String modifiedPath = "";
        Boolean internalService = true;

        for (String service : services) {
            if (requestUrl.contains(service + "/")) {
                // if the url contains a service name, then its not local
                internalService = false;
                break;
            }
        }

        // The routes are the statically configured routes
        for (ZuulRoute zr : routes.values()) {
            // This will break support for wildcards in the middle of paths, consider improving
            String simpleRoute = zr.getPath().replace("/**", "");
            if (requestUrl.contains(simpleRoute) && zr.isStripPrefix() == false) {
                modifiedPath = requestUrl.substring(requestUrl.indexOf(simpleRoute.substring(0, 5)));
                break;
            }
        }

        if (internalService) {
            ctx.put(FORWARD_TO_KEY, requestUrl);
            log.debug("ROUTING: Local Request - " + requestUrl);
        } else if (modifiedPath != "") {
            RequestRewriter requestRewriter = new RequestRewriter(request);
            requestRewriter.setRequestURI(modifiedPath);
            ctx.setRequest(requestRewriter);
            log.debug("ROUTING: Foreign Request: " + requestUrl + " route prefix stripped to: " + modifiedPath);
        } else {
            log.debug("ROUTING: Foreign Request: " + requestUrl);
        }

        return null;
    }

}
0 голосов
/ 21 февраля 2019

Итак, некоторые из очень сложных уроков, которые я усвоил в этом процессе -

Во-первых, я собираюсь поднять ошибку в фильтре Jhipster accessControl, как описано выше, так как он добавляет еще один уровень проверки маршрута.на уже запущенном маршруте и фильтрации хороших маршрутов.Если вы получаете Контроль доступа: отфильтрованный несанкционированный доступ к конечной точке / API / учетной записи не смотрите дальше - просто удалите его из конфигурации шлюза.

Второе - я думаю, что проблема либо вмоя установка, или просто вообще с zuul, где, если целевой путь / результирующий путь неверен - вы получаете ошибку 500, переданную по стеку вместо 404. Это может быть crsf, но это была плохая обработка ошибок.Так что, если вы видите «соединение с сервером прервано» или «тайм-аут чтения Hystrix» - это действительно может быть неверный целевой URL.

В-третьих, очень легко установить циклы и DDoS-свой собственный прокси, если API-интерфейсы точно следуютто же соглашение об именах.Например, я нажал на gateway2 / api / account и хочу, чтобы он направлялся к gateway1 / api / account, поэтому я установил route: path: api / account service: gateway1 strip-prefix: false

Ну, это прекрасно работает... но запрос возвращается gateway1 / api / account и перенаправляется СНОВА .. и СНОВА .. и СНОВА, пока вы не отключите цепь.

Я закончил тем, что изменил имя API на gateway1, и он отлично работает.

...