【问题标题】:Spring Vault client - not able to connect to local dev Vault serverSpring Vault 客户端 - 无法连接到本地 dev Vault 服务器
【发布时间】:2019-08-03 01:18:24
【问题描述】:

我在本地安装了 Vault。根据这个官方教程https://learn.hashicorp.com/vault/

,我能够启动本地开发服务器并将一些秘密写入/读取到 Vault kv

然后我想创建一些非常基本的 Java/Spring Boot 演示客户端,它可以连接到我的本地 Vault 开发服务器以写入/读取机密。我阅读 Baeldung 教程以获取灵感 https://www.baeldung.com/spring-vault

这是我的 Vault-config.properties:

vault.uri=http://127.0.0.1:8200

vault.token=s.EXg6MQwUuB63Z7Xra4zybOut(最近一次启动服务器后生成的令牌)

然后是服务类:

@Service
public class CredentialsService {

    @Autowired
    private VaultTemplate vaultTemplate;

    public void secureCredentials(Credentials credentials) throws URISyntaxException {
        vaultTemplate.write("credentials/myapp", credentials);
    }

    public Credentials accessCredentials() throws URISyntaxException {
        VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
        return response.getData();
    }
}

配置类:

@Configuration
public class VaultConfig extends AbstractVaultConfiguration {

    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("s.EXg6MQwUuB63Z7Xra4zybOut");
    }

    @Override
    public VaultEndpoint vaultEndpoint() {
        return VaultEndpoint.create("host", 8200);
    }
}

还有这个:

@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {

}

一个域对象:

public class Credentials {

    private String username;
    private String password;

    public Credentials() {

    }
    public Credentials(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    @Override
    public String toString() {
        return "Credential [username=" + username + ", password=" + password + "]";
    }
}

最后是我的主要 Spring Boot 类:

@RestController
@ComponentScan
@SpringBootApplication
public class SpringVaultTutorial {

    @Autowired
    CredentialsService credentialsService;

    @RequestMapping("/")
    String home() throws URISyntaxException {
        Credentials credentials = new Credentials("oliver","exxeta123");
        credentialsService.secureCredentials(credentials);
        return credentialsService.accessCredentials().getUsername().toString();
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringVaultTutorial.class, args);
    }

}

主类应该写入秘密,然后立即读取它并打印用户名。但我收到此错误消息:

出现意外错误(类型=内部服务器错误,状态=500)。 “https://host:8200/v1/credentials/myapp”的 POST 请求出现 I/O 错误:主机;嵌套异常是 java.net.UnknownHostException: host

有人知道可能出了什么问题吗?

编辑: 根据 Arun 的建议,我遵循了本教程 https://drissamri.be/blog/java/enable-https-in-spring-boot/

我一直在尝试这两种方法。 1)修改application.properties:

server.port: 8443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: oliver
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat
security.require-ssl=true

修改后,当我调用https://localhost:8443 时,出现异常: javax.net.ssl.SSLException:无法识别的 SSL 消息,明文连接? 在 sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~[na:1.8.0_121] 在 sun.security.ssl.InputRecord.read(InputRecord.java:527) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) ~[na:1.8.0_121]

2) 基于教程的第二种方法是添加 ConnectorConfig 类:

@Configuration
public class ConnectorConfig {

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat =
                new TomcatServletWebServerFactory() {
                    @Override
                    protected void postProcessContext(Context context) {
                        SecurityConstraint securityConstraint = new SecurityConstraint();
                        securityConstraint.setUserConstraint("CONFIDENTIAL");
                        SecurityCollection collection = new SecurityCollection();
                        collection.addPattern("/*");
                        securityConstraint.addCollection(collection);
                        context.addConstraint(securityConstraint);
                    }
                };
        tomcat.addAdditionalTomcatConnectors(redirectConnector());
        return tomcat;
    }

    private Connector redirectConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        connector.setPort(8090);
        connector.setSecure(false);
        connector.setRedirectPort(8443);
        return connector;
    }
}

但是在调用将我重定向到https://localhost:8443 的 localhost:8090 之后,我得到了同样的错误:javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? 在 sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~

现在的问题是:我是否还必须在 Vault 端配置一些关于证书的东西?或者您认为 Java 客户端可能存在一些证书问题?但我觉得如果有 Java 证书问题,在启动过程中就会抛出异常。

【问题讨论】:

    标签: java spring spring-boot hashicorp-vault spring-vault


    【解决方案1】:

    问题解决了。现在我可以从 Java 客户端连接到本地 Vault。如果将来有人想运行简单的 Java 客户端 - Vault 演示,我将代码粘贴在这里。

    控制器:

    @RestController
    @RequestMapping(Paths.ROOT)
    @Api(value = Paths.ROOT, description = "Endpoint for core testing")
    public class Controller {
    
        @Autowired
        CredentialsService credentialsService;
    
        @GetMapping("/")
        String home() throws URISyntaxException {
            Credentials credentials = new Credentials("oliver", "exxeta123");
            credentialsService.secureCredentials(credentials);
            return credentialsService.accessCredentials().toString();
        }
    
        @GetMapping("/test")
        public String test() throws IOException {
            // http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets
            VaultConfig vc = new VaultConfig();
            String bearerToken = vc.clientAuthentication().login().getToken();
            System.out.println(bearerToken);
            // credentialsService.accessCredentials()
    
            // Sending get request
            //URL url = new URL("http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets");
            // URL updated to match readme.adoc
            URL url = new URL("http://127.0.0.1:8200/v1/kv/my-secret");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    
            conn.setRequestProperty("Authorization", "Bearer " + bearerToken);
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestMethod("GET");
    
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String output;
    
            StringBuffer response = new StringBuffer();
            while ((output = in.readLine()) != null) {
                response.append(output);
            }
    
            in.close();
            // printing result from response
            return "Response: - " + response.toString();
        }
    
        @GetMapping(value = { "/add/{name}/{username}/{password}" })
        public ResponseEntity<String> addKey(@PathVariable(value = "name", required = false, name = "name") String name,
                @PathVariable(value = "username", required = false, name = "username") String username,
                @PathVariable(value = "password", required = false, name = "password") String password) throws URISyntaxException {
            Credentials credentials = new Credentials(username, password);
            credentialsService.secureCredentials(name, credentials);
            return new ResponseEntity<>("Add success: " + credentialsService.accessCredentials(name).getUsername(), HttpStatus.OK);
        }
    
        @GetMapping(value = {"/get", "/get/{name}"})
        public ResponseEntity<Credentials> getKey(@PathVariable(value = "name", required = false, name = "name") String name) {
            return new ResponseEntity<>(credentialsService.accessCredentials(name), HttpStatus.OK);
        }
    
        @GetMapping(value= {"/delete", "/delete/{name}"})
        public String removeKey(@PathVariable(value = "name", required = false, name = "name") String name) {
            return "Delete success: " + credentialsService.deleteCredentials(name);
        }
    
    }
    

    服务:

    @Service
    public class CredentialsService {
    
        private VaultTemplate vaultTemplate;
    
        /**
         * To Secure Credentials
         *
         * @param credentials
         * @return VaultResponse
         * @throws URISyntaxException
         */
        public void secureCredentials(Credentials credentials) throws URISyntaxException {
            //vaultTemplate.write("credentials/myapp", credentials);
            initVaultTemplate();
            vaultTemplate.write("kv/myapp", credentials);
        }
    
    
        public void secureCredentials(String storagePlace, Credentials credentials) {
            initVaultTemplate();
            vaultTemplate.write("kv/" + storagePlace, credentials);
        }
    
    
        /**
         * To Retrieve Credentials
         *
         * @return Credentials
         * @throws URISyntaxException
         */
        public Credentials accessCredentials() throws URISyntaxException {
            //VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
            initVaultTemplate();
            VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/myapp", Credentials.class);
            return response.getData();
            // TODO special case when there are no values
        }
    
        /**
         * @param nameOfsecrets key name
         * @return if is presented or empty object
         */
        public Credentials accessCredentials(String nameOfsecrets) {
            initVaultTemplate();
            VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/" + nameOfsecrets, Credentials.class);
            if (response != null) {
                return response.getData();
            } else {
                return new Credentials();
            }
        }
    
        public boolean deleteCredentials(String name) {
            initVaultTemplate();
            vaultTemplate.delete("kv/" + name);
            return true;
        }
    }
    
    private void initVaultTemplate() {
                VaultEndpoint endpoint = new VaultEndpoint();
                endpoint.setHost("localhost");
                endpoint.setPort(8200);
                endpoint.setScheme("http");
    
                vaultTemplate = new VaultTemplate(endpoint, new VaultConfig().clientAuthentication());
            }
    

    VaultConfig:

    @Configuration
    public class VaultConfig extends AbstractVaultConfiguration {
    
        @Override
        public ClientAuthentication clientAuthentication() {
            return new TokenAuthentication("00000000-0000-0000-0000-000000000000");
        }
    
        @Override
        public VaultEndpoint vaultEndpoint() {
            return VaultEndpoint.create("localhost", 8200);
        }
    
    }
    

    VaultEnvironmentConfig:

    @Configuration
    @PropertySource(value = { "vault-config.properties" })
    @Import(value = EnvironmentVaultConfiguration.class)
    public class VaultEnvironmentConfig {
    
    }
    

    主类:

    @SpringBootApplication
    @EnableSwagger2
    public class SpringVaultTutorial {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringVaultTutorial.class, args);
        }
    
        //SWAGGER DOCUMENTATION BEANS
        // default group contains all endpoints
        @Bean
        public Docket defaultApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .select()
                    .apis(RequestHandlerSelectors.any())
                    .paths(PathSelectors.any())//all
                    .build().apiInfo(apiInfo());
        }
    
        // Management group contains Spring Actuator endpoints
        @Bean
        public Docket swaggerAdminEndpoints() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .groupName(Paths.ROOT)
                    .apiInfo(apiInfo())
                    .select()
                    .paths(PathSelectors.regex("/v1/.*"))
                    .build()
                    .forCodeGeneration(true);
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("Vault Demo Application")
                    .description("Demo Application using vault")
                    .version("1.0")
                    .build();
        }
    
    }
    

    vault-config.properties:

    vault.uri=http://127.0.0.1:8200 vault.token=00000000-0000-0000-0000-000000000000

    application.properties:

        server.port=8443
        server.ssl.key-alias=selfsigned_localhost_sslserver
        server.ssl.key-password=changeit
        server.ssl.key-store=classpath:ssl-server.jks
        server.ssl.key-store-provider=SUN
        server.ssl.key-store-type=JKS
    

    路径:

    public class Paths {
        public static final String ROOT = "/v1";
    }
    

    凭据:

    public class Credentials {
    
        private String username;
        private String password;
    
        public Credentials() {
    
        }
    
        public Credentials(String username, String password) {
            this.username = username;
            this.password = password;
        }
    
        public String getUsername() {
            return username;
        }
    
        public String getPassword() {
            return password;
        }
    
        @Override
        public String toString() {
            return "Credential [username=" + username + ", password=" + password + "]";
        }
    
    }
    

    【讨论】:

    • 值得一提的是 VaultEndpoint.from(URI(vaultUrl)) 作为处理 VaultTemplate 的替代方法
    【解决方案2】:

    UnknownHostException 是由于名称为“主机”的服务器不可用。您可以在主机文件映射中添加一个条目到 localhost。或尝试在创建保管库时更改主机名

        @Override
        public VaultEndpoint vaultEndpoint() {
            return VaultEndpoint.create("localhost", 8200);
        }
    

    【讨论】:

    • 谢谢阿鲁纳查拉姆!你的建议帮助了我。但现在我收到新错误:无法识别的 SSL 消息,明文连接?嵌套异常是 javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?你知道怎么解决吗?
    • 启用ssl后,您需要提供证书drissamri.be/blog/java/enable-https-in-spring-boot您可以尝试禁用它以进行本地测试!
    • 嗨阿伦。我遵循了那个 SSL 教程。所以我将它添加到我的 application.properties 中: server.port: 8443 server.ssl.key-store: keystore.p12 server.ssl.key-store-password: Oliver server.ssl.keyStoreType: PKCS12 server.ssl.keyAlias: tomcat security.require-ssl=true 我用keygen创建了自己的证书。但是在我打电话给localhost:8443 之后,我仍然收到同样的错误 Unrecognized SSL message, plaintext connection?您在尝试通过 SSL 访问本地 Vault 服务器时是否遇到过类似问题?提前感谢您的任何帮助
    • 在使用 spring-boot 启用 ssl 后,您是否致电 localchost:8443?它会抛出任何要接受的认证吗?如果有的话,你能分享一个堆栈跟踪吗?
    • 嗨阿伦。我尝试了教程中的两种方法。我在编辑部分下添加了对原始问题的描述。请检查您是否有时间。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-09-24
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    • 2012-10-15
    相关资源
    最近更新 更多