Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Evolveum/midpoint
Browse files Browse the repository at this point in the history
* 'master' of github.com:Evolveum/midpoint:
  secrets providers: support for resolving secrets in BasicExpressionFunctions
  secrets providers: container/file/docker secrets provider
  • Loading branch information
katkav committed Feb 15, 2024
2 parents 9ea8f7d + 60f574f commit 630d918
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2010-2024 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.common.secrets;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ContainerSecretsProviderType;

public abstract class ContainerSecretsProvider<T extends ContainerSecretsProviderType> extends SecretsProviderImpl<T> {

private static final Trace LOGGER = TraceManager.getTrace(ContainerSecretsProvider.class);

private File parentDirectory;

private Charset charset;

public ContainerSecretsProvider(T configuration) {
super(configuration);
}

@Override
public void initialize() {
super.initialize();

parentDirectory = getParentDirectory();
if (parentDirectory == null) {
throw new IllegalStateException("No parent directory defined for secrets provider " + getIdentifier());
}

T config = getConfiguration();
charset = config.getCharset() != null ? Charset.forName(config.getCharset()) : StandardCharsets.UTF_8;
}

protected abstract @NotNull File getParentDirectory();

@Override
protected <ST> ST resolveSecret(@NotNull String key, @NotNull Class<ST> type) throws EncryptionException {
// to avoid path traversal
String filename = new File(key).getName();
File valueFile = new File(parentDirectory, filename);

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Reading secret from {}", valueFile.getAbsolutePath());
}

ST value = null;
if (valueFile.exists() && valueFile.isFile() && valueFile.canRead()) {
try (InputStream is = new FileInputStream(valueFile)) {
byte[] content = IOUtils.toByteArray(is);

value = mapValue(content, type);
} catch (IOException ex) {
throw new EncryptionException("Couldn't read secret from " + valueFile.getAbsolutePath(), ex);
}
}

return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,67 +8,24 @@
package com.evolveum.midpoint.common.secrets;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.DockerSecretsProviderType;

public class DockerSecretsProvider extends SecretsProviderImpl<DockerSecretsProviderType> {
public class DockerSecretsProvider extends ContainerSecretsProvider<DockerSecretsProviderType> {

private static final Trace LOGGER = TraceManager.getTrace(DockerSecretsProvider.class);
private static final File PARENT_DIRECTORY_WINDOWS = new File("C:\\ProgramData\\Docker\\secrets");

private Charset charset;
private static final File PARENT_DIRECTORY_LINUX = new File("/run/secrets");

public DockerSecretsProvider(DockerSecretsProviderType configuration) {
super(configuration);
}

@Override
public void initialize() {
super.initialize();

DockerSecretsProviderType config = getConfiguration();
charset = config.getCharset() != null ? Charset.forName(config.getCharset()) : StandardCharsets.UTF_8;
}

@Override
protected <ST> ST resolveSecret(@NotNull String key, @NotNull Class<ST> type) throws EncryptionException {
File parent;
if (SystemUtils.IS_OS_WINDOWS) {
parent = new File("C:\\ProgramData\\Docker\\secrets");
} else {
parent = new File("/run/secrets");
}

// to avoid path traversal
String filename = new File(key).getName();
File valueFile = new File(parent, filename);

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Reading secret from {}", valueFile.getAbsolutePath());
}

ST value = null;
if (valueFile.exists() && valueFile.isFile() && valueFile.canRead()) {
try (InputStream is = new FileInputStream(valueFile)) {
String content = IOUtils.toString(is, charset);

value = mapValue(content, type);
} catch (IOException ex) {
throw new EncryptionException("Couldn't read secret from " + valueFile.getAbsolutePath(), ex);
}
}

return value;
protected @NotNull File getParentDirectory() {
return SystemUtils.IS_OS_WINDOWS ? PARENT_DIRECTORY_WINDOWS : PARENT_DIRECTORY_LINUX;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,33 @@

package com.evolveum.midpoint.common.secrets;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.EnvironmentVariablesSecretsProviderType;

public class EnvironmentVariablesSecretsProvider extends SecretsProviderImpl<EnvironmentVariablesSecretsProviderType> {

private static final Trace LOGGER = TraceManager.getTrace(EnvironmentVariablesSecretsProvider.class);

public EnvironmentVariablesSecretsProvider(EnvironmentVariablesSecretsProviderType configuration) {
super(configuration);
}

@Override
protected <ST> ST resolveSecret(@NotNull String key, @NotNull Class<ST> type) throws EncryptionException {
protected <ST> ST resolveSecret(@NotNull String key, @NotNull Class<ST> type) {
String prefix = getConfiguration().getPrefix();

if (prefix != null && !key.startsWith(prefix)) {
throw new EncryptionException("Key not available in provider " + getIdentifier() + ": " + key);
String finalKey = StringUtils.isNotEmpty(prefix) ? prefix + key : key;

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Reading secret from environment variable {}", finalKey);
}

String value = System.getenv(key);
String value = System.getenv(finalKey);

return mapValue(value, type);
return mapValue(value.getBytes(), type);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2010-2024 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.common.secrets;

import java.io.File;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FileSecretsProviderType;

public class FileSecretsProvider extends ContainerSecretsProvider<FileSecretsProviderType> {

public FileSecretsProvider(@NotNull FileSecretsProviderType configuration) {
super(configuration);
}

@Override
protected @NotNull File getParentDirectory() {
String parentDirectoryPath = getConfiguration().getParentDirectoryPath();
if (parentDirectoryPath == null) {
throw new SystemException("No parent directory defined for secrets provider " + getIdentifier());
}

return new File(parentDirectoryPath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ protected <ST> ST resolveSecret(@NotNull String key, @NotNull Class<ST> type) th

String value = props.getProperty(key);

return mapValue(value, type);
return mapValue(value.getBytes(charset), type);
} catch (IOException ex) {
throw new EncryptionException("Couldn't read properties file in provider " + getIdentifier(), ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,15 @@ private <ST> ST getOrResolveSecret(String key, Class<ST> type) throws Encryption
*/
protected abstract <ST> ST resolveSecret(@NotNull String key, @NotNull Class<ST> type) throws EncryptionException;

protected <ST> ST mapValue(String value, Class<ST> type) {
protected <ST> ST mapValue(byte[] value, Class<ST> type) {
if (value == null) {
return null;
}

if (type == String.class) {
return (ST) value;
return (ST) new String(value);
} else if (type == ByteBuffer.class) {
return (ST) ByteBuffer.wrap(value.getBytes());
return (ST) ByteBuffer.wrap(value);
}

throw new IllegalStateException("Unsupported type " + type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class SecretsProviderManager {
Map.ofEntries(
Map.entry(DockerSecretsProviderType.class, DockerSecretsProvider.class),
Map.entry(PropertiesSecretsProviderType.class, PropertiesSecretsProvider.class),
Map.entry(EnvironmentVariablesSecretsProviderType.class, EnvironmentVariablesSecretsProvider.class)
Map.entry(EnvironmentVariablesSecretsProviderType.class, EnvironmentVariablesSecretsProvider.class),
Map.entry(FileSecretsProviderType.class, FileSecretsProvider.class)
);

public synchronized void configure(SecretsResolver consumer, SecretsProvidersType configuration) {
Expand All @@ -49,9 +50,9 @@ public synchronized void configure(SecretsResolver consumer, SecretsProvidersTyp
LOGGER.debug("Existing providers: {}", existingProviders.keySet());

List<SecretsProviderType> configurations = new ArrayList<>();
configurations.add(configuration.getEnvironmentVariablesSecretsProvider());
configurations.add(configuration.getDockerSecretsProvider());
configurations.addAll(configuration.getKubernetesSecretsProvider());
configurations.addAll(configuration.getEnvironmentVariablesSecretsProvider());
configurations.addAll(configuration.getFileSecretsProvider());
configurations.addAll(configuration.getPropertiesSecretsProvider());
configurations.addAll(configuration.getCustomSecretsProvider());

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

package com.evolveum.midpoint.common;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -17,6 +18,7 @@
import org.testng.annotations.Test;

import com.evolveum.midpoint.common.secrets.SecretsProviderManager;
import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.prism.crypto.SecretsProvider;
import com.evolveum.midpoint.prism.crypto.SecretsResolver;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
Expand All @@ -42,7 +44,7 @@ public void test100TestProvidersInitialization() throws Exception {

SecretsProvidersType providers = new SecretsProvidersType();
providers.setDockerSecretsProvider(docker);
providers.setEnvironmentVariablesSecretsProvider(env);
providers.getEnvironmentVariablesSecretsProvider().add(env);
providers.getPropertiesSecretsProvider().add(properties);

Map<String, SecretsProvider<?>> map = new ConcurrentHashMap<>();
Expand All @@ -63,6 +65,16 @@ public void removeSecretsProvider(@NotNull SecretsProvider<?> provider) {
public @NotNull List<SecretsProvider<?>> getSecretsProviders() {
return new ArrayList<>(map.values());
}

@Override
public @NotNull String resolveSecretString(@NotNull String provider, @NotNull String key) throws EncryptionException {
throw new UnsupportedOperationException("Not implemented");
}

@Override
public @NotNull ByteBuffer resolveSecretBinary(@NotNull String provider, @NotNull String key) throws EncryptionException {
throw new UnsupportedOperationException("Not implemented");
}
};

SecretsProviderManager manager = new SecretsProviderManager();
Expand Down

0 comments on commit 630d918

Please sign in to comment.