aboutsummaryrefslogtreecommitdiffstats
path: root/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java
blob: af0da93edc3cac7c5bbfaf4673afc76ac54222de (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.athenz.utils;

import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.X509CertificateUtils;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Optional;
import java.util.stream.StreamSupport;

import static java.util.stream.Collectors.toList;

/**
 * Misc utility methods for SIA provided credentials
 *
 * @author bjorncs
 */
public class SiaUtils {
    public static final Path DEFAULT_SIA_DIRECTORY = Paths.get("/var/lib/sia");

    private SiaUtils() {}

    public static Path getPrivateKeyFile(AthenzIdentity service) {
        return getPrivateKeyFile(DEFAULT_SIA_DIRECTORY, service);
    }

    public static Path getPrivateKeyFile(Path root, AthenzIdentity service) {
        return root
                .resolve("keys")
                .resolve(String.format("%s.%s.key.pem", service.getDomainName(), service.getName()));
    }

    public static Path getCertificateFile(AthenzIdentity service) {
        return getCertificateFile(DEFAULT_SIA_DIRECTORY, service);
    }

    public static Path getCertificateFile(Path root, AthenzIdentity service) {
        return root
                .resolve("certs")
                .resolve(String.format("%s.%s.cert.pem", service.getDomainName(), service.getName()));
    }

    public static Path getCaCertificatesFile() {
        return getCaCertificatesFile(DEFAULT_SIA_DIRECTORY);
    }

    public static Path getCaCertificatesFile(Path root) {
        return root.resolve("certs").resolve("ca.cert.pem");
    }

    public static Optional<PrivateKey> readPrivateKeyFile(AthenzIdentity service) {
        return readPrivateKeyFile(DEFAULT_SIA_DIRECTORY, service);
    }

    public static Optional<PrivateKey> readPrivateKeyFile(Path root, AthenzIdentity service) {
        try {
            Path privateKeyFile = getPrivateKeyFile(root, service);
            if (Files.notExists(privateKeyFile)) return Optional.empty();
            return Optional.of(KeyUtils.fromPemEncodedPrivateKey(new String(Files.readAllBytes(privateKeyFile))));
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static Optional<X509Certificate> readCertificateFile(AthenzIdentity service) {
        return readCertificateFile(DEFAULT_SIA_DIRECTORY, service);
    }

    public static Optional<X509Certificate> readCertificateFile(Path root, AthenzIdentity service) {
        try {
            Path certificateFile = getCertificateFile(root, service);
            if (Files.notExists(certificateFile)) return Optional.empty();
            return Optional.of(X509CertificateUtils.fromPem(new String(Files.readAllBytes(certificateFile))));
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static void writePrivateKeyFile(AthenzIdentity service, PrivateKey privateKey) {
        writePrivateKeyFile(DEFAULT_SIA_DIRECTORY, service, privateKey);
    }

    public static void writePrivateKeyFile(Path root, AthenzIdentity service, PrivateKey privateKey) {
        try {
            Path privateKeyFile = getPrivateKeyFile(root, service);
            Files.createDirectories(privateKeyFile.getParent());
            Path tempFile = toTempFile(privateKeyFile);
            Files.write(tempFile, KeyUtils.toPem(privateKey).getBytes());
            Files.move(tempFile, privateKeyFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static void writeCertificateFile(AthenzIdentity service, X509Certificate certificate) {
        writeCertificateFile(DEFAULT_SIA_DIRECTORY, service, certificate);
    }

    public static void writeCertificateFile(Path root, AthenzIdentity service, X509Certificate certificate) {
        try {
            Path certificateFile = getCertificateFile(root, service);
            Files.createDirectories(certificateFile.getParent());
            Path tempFile = toTempFile(certificateFile);
            Files.write(tempFile, X509CertificateUtils.toPem(certificate).getBytes());
            Files.move(tempFile, certificateFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static List<AthenzIdentity> findSiaServices() {
        return findSiaServices(DEFAULT_SIA_DIRECTORY);
    }

    public static List<AthenzIdentity> findSiaServices(Path root) {
        String keyFileSuffix = ".key.pem";
        Path keysDirectory = root.resolve("keys");
        if ( ! Files.exists(keysDirectory))
            return List.of();

        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(keysDirectory)) {
            return StreamSupport.stream(directoryStream.spliterator(), false)
                    .map(path -> path.getFileName().toString())
                    .filter(fileName -> fileName.endsWith(keyFileSuffix))
                    .map(fileName -> fileName.substring(0, fileName.length() - keyFileSuffix.length()))
                    .map(AthenzService::new)
                    .collect(toList());
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    private static Path toTempFile(Path file) {
        return file.getParent().resolve(file.getFileName().toString() + ".tmp");
    }
}