/* * Copyright 2016-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.tick42.varandb.boot.env; import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.OriginTrackedValue; import org.springframework.boot.origin.TextResourceOrigin; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.io.FileSystemResource; import static java.util.stream.Collectors.toMap; /** * An {@link EnvironmentPostProcessor} that knows how to load docker/kubernetes secrets. * * The contents of every file under {@code /run/secrets} can be easily consumed * by {@link ConfigurableEnvironment Environment} users via {@code secret.*} properties. * * @author Vladimir Tsanev */ public class SecretEnvironmentPostProcessor implements EnvironmentPostProcessor { private static final Logger log = LoggerFactory.getLogger(SecretEnvironmentPostProcessor.class); private String prefix = "secret."; private final Path directory; public SecretEnvironmentPostProcessor() { this(new File("/run/secrets").toPath()); } public SecretEnvironmentPostProcessor(Path directory) { this.directory = directory; } @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { Map map; map = createSecretsMap(); if (!map.isEmpty()) { MutablePropertySources sources = environment.getPropertySources(); PropertySource source = new OriginTrackedMapPropertySource("secrets", map); sources.addFirst(source); } } Map createSecretsMap() { try { if (this.directory.toFile().exists()) { return Files.walk(this.directory, 1) .filter(p -> p.toFile().isFile()) .collect(toMap(this::getSecretName, this::readValueFromFile)); } } catch (Exception e) { log.error("Cloud not create secrets map", e); } return Collections.emptyMap(); } private String getSecretName(Path path) { return this.prefix + path.getFileName().toString(); } private OriginTrackedValue readValueFromFile(Path path) { char[] secret; try { byte[] bytes = Files.readAllBytes(path); secret = new String(bytes, StandardCharsets.UTF_8).toCharArray(); } catch (Exception e) { log.error("Could not read secret value from " + path, e); secret = null; } Origin origin = new TextResourceOrigin(new FileSystemResource(path.toFile()), null); return OriginTrackedValue.of(secret, origin); } }