Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sanere utgående STS, standardisere proxy, maskinporten #1378

Merged
merged 2 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
package no.nav.vedtak.klient.http;

import no.nav.foreldrepenger.konfig.Environment;

import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.time.Duration;
import java.util.Optional;

public final class ProxyHttpClient extends BaseHttpClient {
private static final Environment ENV = Environment.current();

private static final String AZURE_HTTP_PROXY = "azure.http.proxy";
private static final String PROXY_KEY = "proxy.url";
private static final String DEFAULT_PROXY_URL = "http://webproxy.nais:8088";

private static ProxyHttpClient CLIENT;

private ProxyHttpClient() {
super(HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(15))
.proxy(Optional.ofNullable(ENV.isFss() ? URI.create(ENV.getProperty(AZURE_HTTP_PROXY, getDefaultProxy())) : null)
.map(p -> new InetSocketAddress(p.getHost(), p.getPort()))
.map(ProxySelector::of)
.orElse(HttpClient.Builder.NO_PROXY)).build());
.proxy(ProxyProperty.getProxySelectorIfFSS()).build());
}

public static synchronized ProxyHttpClient client() {
Expand All @@ -36,7 +21,4 @@ public static synchronized ProxyHttpClient client() {
return inst;
}

private static String getDefaultProxy() {
return ENV.getProperty(PROXY_KEY, DEFAULT_PROXY_URL);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package no.nav.vedtak.klient.http;

import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.util.Optional;

import no.nav.foreldrepenger.konfig.Environment;

/**
* Standard navn på environment injisert av NAIS når maskinporten er enabled
* Dvs naiserator:spec:maskinporten:enabled: true
*/
public class ProxyProperty {
private static final Environment ENV = Environment.current();

private static final String AZURE_HTTP_PROXY = "azure.http.proxy";
private static final String DEFAULT_PROXY_URL = "http://webproxy.nais:8088";

private ProxyProperty() {
}

public static URI getProxy() {
return URI.create(ENV.getProperty(AZURE_HTTP_PROXY, DEFAULT_PROXY_URL));
}

public static URI getProxyIfFSS() {
return ENV.isFss() ? getProxy() : null;
}

public static ProxySelector getProxySelector() {
var proxy = getProxy();
return ProxySelector.of(new InetSocketAddress(proxy.getHost(), proxy.getPort()));
}

public static ProxySelector getProxySelectorIfFSS() {
return ENV.isFss() ? getProxySelector() : HttpClient.Builder.NO_PROXY;
}

public static ProxySelector getProxySelector(URI proxy) {
return Optional.ofNullable(proxy)
.map(p -> new InetSocketAddress(p.getHost(), p.getPort()))
.map(ProxySelector::of)
.orElse(HttpClient.Builder.NO_PROXY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package no.nav.vedtak.sikkerhet.oidc.config;

/**
* Standard navn på environment injisert av NAIS når maskinporten er enabled
* Dvs naiserator:spec:maskinporten:enabled: true
*/
public enum MaskinportenProperty {
MASKINPORTEN_CLIENT_ID,
MASKINPORTEN_CLIENT_JWK,
MASKINPORTEN_SCOPES, // Må angis i naiserator:spec:maskinporten:scopes:consumes: (-name: "<scope>")
MASKINPORTEN_WELL_KNOWN_URL, // Sanere bruk av well known - bruk heller NAIS/env
MASKINPORTEN_ISSUER,
MASKINPORTEN_TOKEN_ENDPOINT

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import no.nav.foreldrepenger.konfig.Environment;
import no.nav.vedtak.exception.TekniskException;
import no.nav.vedtak.klient.http.ProxyProperty;
import no.nav.vedtak.sikkerhet.kontekst.Systembruker;
import no.nav.vedtak.sikkerhet.oidc.config.AzureProperty;
import no.nav.vedtak.sikkerhet.oidc.config.OpenIDConfiguration;
Expand All @@ -29,10 +30,6 @@ public final class OidcProviderConfig {
private static final Logger LOG = LoggerFactory.getLogger(OidcProviderConfig.class);

private static final String STS_WELL_KNOWN_URL = "oidc.sts.well.known.url";
private static final String AZURE_HTTP_PROXY = "azure.http.proxy"; // settes ikke av naiserator
private static final String PROXY_KEY = "proxy.url"; // FP-oppsett lite brukt
private static final String DEFAULT_PROXY_URL = "http://webproxy.nais:8088";

private static Set<OpenIDConfiguration> providers = new HashSet<>();

private final Set<OpenIDConfiguration> instanceProviders;
Expand Down Expand Up @@ -116,7 +113,7 @@ private static OpenIDConfiguration createStsConfiguration(String wellKnownUrl) {

@SuppressWarnings("unused")
private static OpenIDConfiguration createAzureAppConfiguration() {
var proxyUrl = (ENV.isFss() && ENV.isProd()) ? URI.create(ENV.getProperty(AZURE_HTTP_PROXY, getDefaultProxy())) : null;
var proxyUrl = (ENV.isFss() && ENV.isProd()) ? ProxyProperty.getProxy() : null;
return createConfiguration(OpenIDProvider.AZUREAD,
getAzureProperty(AzureProperty.AZURE_OPENID_CONFIG_ISSUER),
getAzureProperty(AzureProperty.AZURE_OPENID_CONFIG_JWKS_URI),
Expand Down Expand Up @@ -172,10 +169,6 @@ private static OpenIDConfiguration createConfiguration(OpenIDProvider type,
skipAudienceValidation);
}

private static String getDefaultProxy() {
return ENV.getProperty(PROXY_KEY, DEFAULT_PROXY_URL);
}

private static URI tilURI(String url, String key, OpenIDProvider provider) {
try {
return URI.create(url);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package no.nav.vedtak.sikkerhet.oidc.config.impl;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
Expand All @@ -19,6 +17,7 @@
import com.fasterxml.jackson.databind.ObjectReader;

import no.nav.vedtak.exception.TekniskException;
import no.nav.vedtak.klient.http.ProxyProperty;
import no.nav.vedtak.mapper.json.DefaultJsonMapper;

public class WellKnownConfigurationHelper {
Expand All @@ -28,7 +27,7 @@ public class WellKnownConfigurationHelper {

public static final String STANDARD_WELL_KNOWN_PATH = ".well-known/openid-configuration";

private static Map<String, WellKnownOpenIdConfiguration> wellKnownConfigMap = Collections.synchronizedMap(new LinkedHashMap<>());
private static final Map<String, WellKnownOpenIdConfiguration> wellKnownConfigMap = Collections.synchronizedMap(new LinkedHashMap<>());

public static WellKnownOpenIdConfiguration getWellKnownConfig(URI wellKnownUrl) {
return getWellKnownConfig(wellKnownUrl.toString(), null);
Expand Down Expand Up @@ -66,19 +65,17 @@ static Optional<String> getTokenEndpointFra(String wellKnownURL, URI proxyUrl) {
}

private static WellKnownOpenIdConfiguration hentWellKnownConfig(String wellKnownURL, URI proxy) {
try {
if (wellKnownURL == null) {
return null;
}
if (!wellKnownURL.toLowerCase().contains(STANDARD_WELL_KNOWN_PATH)) {
// TODO: øk til warn eller prøv å legge på / standard path med
LOG.info("WELLKNOWN OPENID-CONFIGURATION url uten standard suffix {}", wellKnownURL);
}
var useProxySelector = Optional.ofNullable(proxy)
.map(p -> new InetSocketAddress(p.getHost(), p.getPort()))
.map(ProxySelector::of)
.orElse(HttpClient.Builder.NO_PROXY);
var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).proxy(useProxySelector).build();
if (wellKnownURL == null) {
return null;
}
if (!wellKnownURL.toLowerCase().contains(STANDARD_WELL_KNOWN_PATH)) {
// TODO: øk til warn eller prøv å legge på / standard path med
LOG.info("WELLKNOWN OPENID-CONFIGURATION url uten standard suffix {}", wellKnownURL);
}
try (var client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.proxy(ProxyProperty.getProxySelector(proxy))
.build()) {
var request = HttpRequest.newBuilder().uri(URI.create(wellKnownURL)).header("accept", "application/json").GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
return response != null ? READER.readValue(response) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
Expand All @@ -22,6 +20,7 @@
import org.slf4j.LoggerFactory;

import no.nav.vedtak.exception.TekniskException;
import no.nav.vedtak.klient.http.ProxyProperty;

public class JwksKeyHandlerImpl implements JwksKeyHandler {

Expand Down Expand Up @@ -88,19 +87,14 @@ private static String nativeGet(URI url, boolean useProxyForJwks, URI proxy) {
if (url == null) {
throw new TekniskException("F-836283", "Mangler konfigurasjon av jwks url");
}
try {
if (useProxyForJwks && proxy == null) {
throw kunneIkkeOppdatereJwksCache(url, new IllegalArgumentException("Skal bruke proxy, men ingen verdi angitt"));
}
var useProxySelector = Optional.ofNullable(proxy)
.map(p -> new InetSocketAddress(p.getHost(), p.getPort()))
.map(ProxySelector::of)
.orElse(HttpClient.Builder.NO_PROXY);
var client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER)
.connectTimeout(Duration.ofSeconds(20))
.proxy(useProxySelector)
.build();
if (useProxyForJwks && proxy == null) {
throw kunneIkkeOppdatereJwksCache(url, new IllegalArgumentException("Skal bruke proxy, men ingen verdi angitt"));
}
try (var client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER)
.connectTimeout(Duration.ofSeconds(20))
.proxy(ProxyProperty.getProxySelector(proxy))
.build()) {

var request = HttpRequest.newBuilder().header("Accept", "application/json").timeout(Duration.ofSeconds(10)).uri(url).GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString(UTF_8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
import no.nav.vedtak.sikkerhet.oidc.config.OpenIDProvider;
import no.nav.vedtak.sikkerhet.oidc.token.impl.AzureBrukerTokenKlient;
import no.nav.vedtak.sikkerhet.oidc.token.impl.AzureSystemTokenKlient;
import no.nav.vedtak.sikkerhet.oidc.token.impl.StsSystemTokenKlient;
import no.nav.vedtak.sikkerhet.oidc.token.impl.TokenXExchangeKlient;
import no.nav.vedtak.sikkerhet.tokenx.TokenXchange;

public final class TokenProvider {

Expand Down Expand Up @@ -52,30 +50,13 @@ private static OpenIDToken getOutgoingTokenFor(RequestKontekst requestKontekst,
var identType = Optional.ofNullable(requestKontekst.getIdentType()).orElse(IdentType.InternBruker);
return switch (providerIncoming) {
case AZUREAD -> identType.erSystem() ? getAzureSystemToken(scopes) : veksleAzureAccessToken(requestKontekst.getUid(), incoming, scopes);
case TOKENX -> TokenXchange.exchange(incoming, scopes);
case TOKENX -> tokenXchange(incoming, scopes);
case STS -> getAzureSystemToken(scopes);
};
}

public static OpenIDToken getTokenForSystem() {
return getTokenForSystem(OpenIDProvider.STS, null);
}

public static OpenIDToken getTokenXFraKontekst() {
var kontekst = KONTEKST_PROVIDER.getKontekst();
if (kontekst instanceof RequestKontekst requestKontekst) {
return OpenIDProvider.TOKENX.equals(getProvider(requestKontekst.getToken())) ? requestKontekst.getToken() : null;
} else {
throw new IllegalStateException("Mangler SikkerhetContext - skal ikke provide token");
}
}

public static OpenIDToken getTokenForSystem(OpenIDProvider provider, String scopes) {
return switch (provider) {
case AZUREAD -> getAzureSystemToken(scopes);
case STS -> getStsSystemToken();
case TOKENX -> throw new IllegalStateException("Ikke bruk TokenX til kall i systemkontekst");
};
public static OpenIDToken getTokenForSystem(String scopes) {
return getAzureSystemToken(scopes);
}

// Endre til AzureClientId ved overgang til system = azure
Expand All @@ -91,10 +72,6 @@ public static String getCurrentConsumerId() {
return Optional.ofNullable(kontekst.getKonsumentId()).orElseGet(kontekst::getUid);
}

private static OpenIDToken getStsSystemToken() {
return StsSystemTokenKlient.hentAccessToken();
}

private static OpenIDToken getAzureSystemToken(String scopes) {
return AzureSystemTokenKlient.instance().hentAccessToken(scopes);
}
Expand All @@ -103,9 +80,8 @@ private static OpenIDToken veksleAzureAccessToken(String uid, OpenIDToken incomi
return AzureBrukerTokenKlient.instance().oboExchangeToken(uid, incoming, scopes);
}

public static OpenIDToken exchangeTokenX(OpenIDToken token, String assertion, String scopes) {
// Assertion må være generert av den som skal bytte. Et JWT, RSA-signert, basert på injisert private jwk
return TokenXExchangeKlient.instance().exchangeToken(token, assertion, scopes);
public static OpenIDToken tokenXchange(OpenIDToken token, String scopes) {
return TokenXExchangeKlient.instance().exchangeToken(token, scopes);
}

private static OpenIDProvider getProvider(OpenIDToken token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,43 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectReader;

import no.nav.vedtak.exception.TekniskException;
import no.nav.vedtak.klient.http.ProxyProperty;
import no.nav.vedtak.mapper.json.DefaultJsonMapper;

public class GeneriskTokenKlient {

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

private static final ObjectReader READER = DefaultJsonMapper.getObjectMapper().readerFor(OidcTokenResponse.class);

public static OidcTokenResponse hentTokenRetryable(HttpRequest request, URI proxy, int retries) {
int i = retries;
while (i-- > 0) {
try {
return hentToken(request, proxy);
} catch (TekniskException e) {
LOG.info("Feilet {}. gang ved henting av token. Prøver på nytt", retries - i, e);
}
}
return hentToken(request, proxy);
}


public static OidcTokenResponse hentToken(HttpRequest request, URI proxy) {
try {
var useProxySelector = Optional.ofNullable(proxy)
.map(p -> new InetSocketAddress(p.getHost(), p.getPort()))
.map(ProxySelector::of)
.orElse(HttpClient.Builder.NO_PROXY);
var client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER)
.connectTimeout(Duration.ofSeconds(20))
.proxy(useProxySelector)
.build();
try (var client = hentEllerByggHttpClient(proxy)) { // På sikt vurder å bruke en generell klient eller å cache. De er blitt autocloseable
var response = client.send(request, HttpResponse.BodyHandlers.ofString(UTF_8));
if (response == null || response.body() == null) {
throw new TekniskException("F-157385", "Kunne ikke hente token");
Expand All @@ -48,4 +54,13 @@ public static OidcTokenResponse hentToken(HttpRequest request, URI proxy) {
throw new TekniskException("F-432938", "InterruptedException ved henting av token", e);
}
}

private static HttpClient hentEllerByggHttpClient(URI proxy) {
return HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER)
.connectTimeout(Duration.ofSeconds(15))
.proxy(ProxyProperty.getProxySelector(proxy))
.build();
}

}
Loading