Last active
February 13, 2021 13:27
-
-
Save thecb4/6f8937d6f153e160572a00a189bc58a7 to your computer and use it in GitHub Desktop.
Swift for Vault
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
version: '3.8' | |
services: | |
vault: | |
image: vault | |
container_name: vault | |
environment: | |
VAULT_API_ADDR: 'http://0.0.0.0:8302' | |
ports: | |
- "8302:8302" | |
restart: always | |
volumes: | |
- ./volumes/logs:/vault/logs | |
- ./volumes/data:/vault/data | |
- ./volumes/config:/vault/config | |
cap_add: | |
- IPC_LOCK | |
entrypoint: vault server -config=/vault/config/vault.hcl | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env swift | |
import Foundation | |
import CryptoKit | |
extension Substring { | |
func trimmed() -> Substring { | |
guard let i = lastIndex(where: { $0 != " " }) else { | |
return "" | |
} | |
return self[...i] | |
} | |
} | |
public extension String { | |
func trimmingLines() -> String { | |
split(separator: "\n", omittingEmptySubsequences: false) | |
.map { $0.trimmed() } | |
.joined(separator: "\n") | |
} | |
func base64Decoded() -> String? { | |
if let data = Data(base64Encoded: self) { | |
return String(data: data, encoding: .utf8) | |
} else { | |
print("not base64 encoded") | |
} | |
return nil | |
} | |
} | |
extension Data { | |
func urlSafeBase64EncodedString() -> String { | |
return base64EncodedString() | |
.replacingOccurrences(of: "+", with: "-") | |
.replacingOccurrences(of: "/", with: "_") | |
.replacingOccurrences(of: "=", with: "") | |
} | |
} | |
struct Path { | |
private let path: String | |
init(_ path: String) { | |
self.path = path | |
} | |
func delete() throws { | |
try FileManager.default.removeItem(atPath: path) | |
} | |
func create() throws { | |
try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true) | |
} | |
} | |
@discardableResult | |
func executeCommand( | |
command: String, | |
cwd: URL? = nil, // To allow for testing of file based output | |
env: [String: String]? = nil | |
) throws -> String { | |
let splitCommand = command.split(separator: " ") | |
let arguments = splitCommand.dropFirst().map(String.init) | |
let commandName = String(splitCommand.first!) | |
let commandURL = URL(fileURLWithPath: commandName) | |
let process = Process() | |
if #available(macOS 10.13, *) { | |
process.executableURL = commandURL | |
} else { | |
process.launchPath = commandURL.path | |
} | |
process.arguments = arguments | |
if let workingDirectory = cwd { | |
process.currentDirectoryURL = workingDirectory | |
} | |
if let env = env { | |
process.environment = env | |
} | |
let output = Pipe() | |
process.standardOutput = output | |
let error = Pipe() | |
process.standardError = error | |
try process.run() | |
process.waitUntilExit() | |
let outputData = output.fileHandleForReading.readDataToEndOfFile() | |
let outputActual = String(data: outputData, encoding: .utf8)! | |
.trimmingCharacters(in: .whitespacesAndNewlines) | |
let errorData = error.fileHandleForReading.readDataToEndOfFile() | |
let errorActual = String(data: errorData, encoding: .utf8)! | |
.trimmingCharacters(in: .whitespacesAndNewlines) | |
let finalString = errorActual + outputActual | |
return finalString | |
} | |
func sendPost(to urlString: String, with body: Data, handler: @escaping (Data?, URLResponse?, Error?) -> Void ) { | |
/* Configure session, choose between: | |
* defaultSessionConfiguration | |
* ephemeralSessionConfiguration | |
* backgroundSessionConfigurationWithIdentifier: | |
And set session-wide properties, such as: HTTPAdditionalHeaders, | |
HTTPCookieAcceptPolicy, requestCachePolicy or timeoutIntervalForRequest. | |
*/ | |
let sessionConfig = URLSessionConfiguration.default | |
/* Create session, and optionally set a URLSessionDelegate. */ | |
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil) | |
guard let url = URL(string: urlString) else {return} | |
var request = URLRequest(url: url) | |
request.httpMethod = "POST" | |
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0) | |
// Headers | |
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") | |
// JSON Body | |
request.httpBody = body | |
print(request) | |
/* Start a new Task */ | |
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in | |
handler(data, response, error) | |
semaphore.signal() | |
}) | |
task.resume() | |
semaphore.wait() | |
session.finishTasksAndInvalidate() | |
} | |
func sendPost(to urlString: String, with body: Data, using token: String, handler: @escaping (Data?, URLResponse?, Error?) -> Void ) { | |
/* Configure session, choose between: | |
* defaultSessionConfiguration | |
* ephemeralSessionConfiguration | |
* backgroundSessionConfigurationWithIdentifier: | |
And set session-wide properties, such as: HTTPAdditionalHeaders, | |
HTTPCookieAcceptPolicy, requestCachePolicy or timeoutIntervalForRequest. | |
*/ | |
let sessionConfig = URLSessionConfiguration.default | |
/* Create session, and optionally set a URLSessionDelegate. */ | |
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil) | |
guard let url = URL(string: urlString) else {return} | |
var request = URLRequest(url: url) | |
request.httpMethod = "POST" | |
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0) | |
// Headers | |
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") | |
request.addValue(token, forHTTPHeaderField: "X-Vault-Token") | |
// JSON Body | |
request.httpBody = body | |
print(request) | |
/* Start a new Task */ | |
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in | |
handler(data, response, error) | |
semaphore.signal() | |
}) | |
task.resume() | |
semaphore.wait() | |
session.finishTasksAndInvalidate() | |
} | |
func sendGet(to urlString: String, handler: @escaping (Data?, URLResponse?, Error?) -> Void ) { | |
/* Configure session, choose between: | |
* defaultSessionConfiguration | |
* ephemeralSessionConfiguration | |
* backgroundSessionConfigurationWithIdentifier: | |
And set session-wide properties, such as: HTTPAdditionalHeaders, | |
HTTPCookieAcceptPolicy, requestCachePolicy or timeoutIntervalForRequest. | |
*/ | |
let sessionConfig = URLSessionConfiguration.default | |
/* Create session, and optionally set a URLSessionDelegate. */ | |
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil) | |
guard let url = URL(string: urlString) else {return} | |
var request = URLRequest(url: url) | |
request.httpMethod = "GET" | |
// https://www.amarendrasingh.com/swift/urlsession-and-synchronous-http-request/ | |
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0) | |
print(request) | |
/* Start a new Task */ | |
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in | |
handler(data, response, error) | |
semaphore.signal() | |
}) | |
task.resume() | |
semaphore.wait() | |
session.finishTasksAndInvalidate() | |
} | |
func sendGet(to urlString: String, using token: String, handler: @escaping (Data?, URLResponse?, Error?) -> Void ) { | |
/* Configure session, choose between: | |
* defaultSessionConfiguration | |
* ephemeralSessionConfiguration | |
* backgroundSessionConfigurationWithIdentifier: | |
And set session-wide properties, such as: HTTPAdditionalHeaders, | |
HTTPCookieAcceptPolicy, requestCachePolicy or timeoutIntervalForRequest. | |
*/ | |
let sessionConfig = URLSessionConfiguration.default | |
/* Create session, and optionally set a URLSessionDelegate. */ | |
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil) | |
guard let url = URL(string: urlString) else {return} | |
var request = URLRequest(url: url) | |
request.httpMethod = "GET" | |
// https://www.amarendrasingh.com/swift/urlsession-and-synchronous-http-request/ | |
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0) | |
// Headers | |
request.addValue(token, forHTTPHeaderField: "X-Vault-Token") | |
print(request) | |
/* Start a new Task */ | |
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in | |
handler(data, response, error) | |
semaphore.signal() | |
}) | |
task.resume() | |
semaphore.wait() | |
session.finishTasksAndInvalidate() | |
} | |
struct VaultInitializationStatus: Decodable { | |
let initialized: Bool | |
} | |
struct InitConfiguration: Encodable { | |
let secret_shares: Int | |
let secret_threshold: Int | |
} | |
struct SealConfiguration: Decodable { | |
let keys: [String] | |
let root_token: String | |
} | |
struct UnsealKey: Encodable { | |
let key: String | |
} | |
struct UnsealResponse: Decodable { | |
let type: String | |
let initialized: Bool | |
let sealed: Bool | |
let t: Int | |
let n: Int | |
let progress: Int | |
let nonce: String | |
let version: String | |
let migration: Bool | |
let cluster_name: String | |
let cluster_id: String | |
let recover_seal: Bool | |
let storage_type: String | |
} | |
struct SecretsConfiguration: Encodable { | |
let type: String | |
} | |
struct AuthenticationEnablement: Encodable { | |
struct Configuration: Encodable { | |
let default_lease_ttl: String | |
let max_lease_ttl: String | |
let force_no_cache: Bool | |
} | |
let type: String | |
let description: String | |
let config: AuthenticationEnablement.Configuration | |
let local: Bool | |
let seal_wrap: Bool | |
let external_entropy_access: Bool | |
} | |
struct JWTAuthConfiguration: Encodable { | |
let jwt_validation_pubkeys: [String] | |
let bound_issuer: String | |
let jwt_supported_algs: [String] | |
} | |
struct VaultPolicy: Encodable { | |
let name: String | |
let policy: String | |
} | |
struct JwtRole: Encodable { | |
let name: String | |
let role_type: String | |
let bound_audiences: [String] | |
let user_claim: String | |
let policies: [String] | |
let token_max_ttl: String | |
} | |
struct JWT: Codable { | |
struct Header: Codable { | |
let alg = "ES256" | |
let kid = "0001" | |
let typ = "JWT" | |
} | |
struct Payload: Codable { | |
let iss = "io.thecb4" | |
let iat = Date().timeIntervalSince1970 | |
let exp = (Date() + 5 * 60).timeIntervalSince1970 | |
let aud = "io.thecb4-v1" | |
let user_email: String | |
} | |
let header = Header() | |
let payload: Payload | |
init(user email: String) { | |
self.payload = Payload(user_email: email) | |
} | |
} | |
extension JWT { | |
func sign(using privateKey: P256.Signing.PrivateKey) -> String { | |
let headerJSONData = try! JSONEncoder().encode(header) | |
let headerBase64String = headerJSONData.urlSafeBase64EncodedString() | |
let payloadJSONData = try! JSONEncoder().encode(payload) | |
let payloadBase64String = payloadJSONData.urlSafeBase64EncodedString() | |
let toSign = (headerBase64String + "." + payloadBase64String).data(using: .utf8)! | |
let signature = try! privateKey.signature(for: toSign) | |
let rawSignature = signature.rawRepresentation | |
let signatureBase64String = Data(rawSignature).urlSafeBase64EncodedString() | |
if privateKey.publicKey.isValidSignature(signature, for: toSign) { | |
print("The signature is valid") | |
} else { | |
print("The signature is not valid") | |
} | |
let token = [headerBase64String, payloadBase64String, signatureBase64String].joined(separator: ".") | |
return token | |
} | |
} | |
struct JwtLogin: Encodable { | |
let role: String | |
let jwt: String | |
} | |
struct JwtAuthenticatedLogin: Decodable { | |
struct Auth: Decodable { | |
let client_token: String | |
let policies: [String] | |
let lease_duration: Int | |
} | |
let auth: Auth | |
} | |
func validateVaultInit(at urlPath: String) -> VaultInitializationStatus? { | |
var status: VaultInitializationStatus? | |
sendGet(to: vaultHost + "/v1/sys/init") { (data: Data?, response: URLResponse?, error: Error?) in | |
print("getting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
status = try? JSONDecoder().decode(VaultInitializationStatus.self, from: data) | |
print(str) | |
print(String(describing: sealConfiguration)) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
return status | |
} | |
func vaultInit(at urlPath: String, with configuration: InitConfiguration) -> SealConfiguration? { | |
var sealConfiguration: SealConfiguration? | |
guard let body = try? JSONEncoder().encode(configuration) else { return nil } | |
sendPost(to: vaultHost + "/v1/sys/init", with: body) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
sealConfiguration = try? JSONDecoder().decode(SealConfiguration.self, from: data) | |
print(str) | |
print(String(describing: sealConfiguration)) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
return sealConfiguration | |
} | |
func vaultUnseal(at urlPath: String, with unsealKey: UnsealKey, using token: String) -> UnsealResponse? { | |
var unsealResponse: UnsealResponse? | |
guard let body = try? JSONEncoder().encode(unsealKey) else { return nil } | |
sendPost(to: vaultHost + "/v1/sys/unseal", with: body, using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
unsealResponse = try? JSONDecoder().decode(UnsealResponse.self, from: data) | |
print(str) | |
print(String(describing: unsealResponse)) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
return unsealResponse | |
} | |
// should return 204 | |
func mountSecrets(configuration: SecretsConfiguration, using token: String) { | |
guard let body = try? JSONEncoder().encode(configuration) else { return } | |
sendPost(to: vaultHost + "/v1/sys/mounts/secret", with: body, using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
print(str) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
} | |
// should return 204 | |
func createSecret(on path: String, value: Data, using token: String) { | |
// guard let body = try? JSONEncoder().encode(configuration) else { return } | |
sendPost(to: vaultHost + "/v1/\(path)", with: value, using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("created secret") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
print(str) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
} | |
func readSecret(on path: String, using token: String) -> String? { | |
var secret: String? | |
sendGet(to: vaultHost + "/v1/\(path)", using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("received secret") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
secret = String(decoding: data, as: UTF8.self) | |
print(secret) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
return secret | |
} | |
// should return 204 | |
func vaultEnableAuth(for authentication: AuthenticationEnablement, using token: String) { | |
guard let body = try? JSONEncoder().encode(authentication) else { return } | |
sendPost(to: vaultHost + "/v1/sys/auth/jwt", with: body, using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
print(str) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
} | |
// should return 204 | |
func vaultConfigureJwtAuth(with configuration: JWTAuthConfiguration, using token: String) { | |
guard let body = try? JSONEncoder().encode(configuration) else { return } | |
sendPost(to: vaultHost + "/v1/auth/jwt/config", with: body, using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
print(str) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
} | |
func vaultCreatePolicy(_ policy: VaultPolicy, using token: String) { | |
guard let body = try? JSONEncoder().encode(policy) else { return } | |
sendPost(to: vaultHost + "/v1/sys/policies/acl/\(policy.name)", with: body, using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
print(str) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
} | |
func vaultCreateJwtRole(_ role: JwtRole, using token: String) { | |
guard let body = try? JSONEncoder().encode(role) else { return } | |
sendPost(to: vaultHost + "/v1/auth/jwt/role/\(role.name)", with: body, using: token) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
print(str) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
} | |
func vaultJwtLogin(_ login: JwtLogin) -> JwtAuthenticatedLogin? { | |
guard let body = try? JSONEncoder().encode(login) else { return nil } | |
var authenticatedLogin: JwtAuthenticatedLogin? | |
sendPost(to: vaultHost + "/v1/auth/jwt/login", with: body) { (data: Data?, response: URLResponse?, error: Error?) in | |
print("posting data") | |
if (error == nil) { | |
// Success | |
let statusCode = (response as! HTTPURLResponse).statusCode | |
print("URL Session Task Succeeded: HTTP \(statusCode)") | |
if let data = data, let response = response { | |
let str = String(decoding: data, as: UTF8.self) | |
authenticatedLogin = try? JSONDecoder().decode(JwtAuthenticatedLogin.self, from: data) | |
print(str) | |
print(String(describing: authenticatedLogin)) | |
print(response) | |
} | |
} | |
else { | |
// Failure | |
print("URL Session Task Failed: %@", error!.localizedDescription); | |
} | |
} | |
return authenticatedLogin | |
} | |
let path = ProcessInfo.processInfo.environment["PATH"]! | |
var environment = ["PATH": path] | |
let vaultConfig = | |
""" | |
ui = true | |
storage "file" { | |
path = "/vault/data" | |
} | |
listener "tcp" { | |
address = "0.0.0.0:8302" | |
tls_disable = "true" | |
} | |
""" | |
do{ try Path("volumes/config").delete() } catch {} | |
do{ try Path("volumes/data").delete() } catch {} | |
do{ try Path("volumes/logs").delete() } catch {} | |
do{ try Path("volumes/config").create() } catch {} | |
do{ try Path("volumes/data").create() } catch {} | |
do{ try Path("volumes/logs").create() } catch {} | |
try vaultConfig.write(toFile: "volumes/config/vault.hcl", atomically: true, encoding: .utf8) | |
print("Starting vault") | |
let up = try executeCommand(command: "/usr/local/bin/docker-compose up -d --remove-orphans --force-recreate --always-recreate-deps --renew-anon-volumes", env: environment) | |
print(up) | |
let vaultHost = "http://0.0.0.0:8302" | |
var status = validateVaultInit(at: vaultHost) | |
let initConfiguration = InitConfiguration(secret_shares: 6, secret_threshold: 3) | |
let sealConfiguration = vaultInit(at: vaultHost, with: initConfiguration) | |
let unsealResponse1 = vaultUnseal(at: vaultHost, with: UnsealKey(key: sealConfiguration!.keys[0]), using: sealConfiguration!.root_token) | |
let unsealResponse2 = vaultUnseal(at: vaultHost, with: UnsealKey(key: sealConfiguration!.keys[1]), using: sealConfiguration!.root_token) | |
let unsealResponse3 = vaultUnseal(at: vaultHost, with: UnsealKey(key: sealConfiguration!.keys[2]), using: sealConfiguration!.root_token) | |
status = validateVaultInit(at: vaultHost) | |
let secretsConfiguration = SecretsConfiguration(type: "kv-v1") | |
mountSecrets(configuration: secretsConfiguration, using: sealConfiguration!.root_token) | |
let jwtEnablement = AuthenticationEnablement( | |
type: "jwt", | |
description: "", | |
config: AuthenticationEnablement.Configuration( | |
default_lease_ttl: "0s", | |
max_lease_ttl: "0s", | |
force_no_cache: false | |
), | |
local: false, | |
seal_wrap: false, | |
external_entropy_access: false | |
) | |
vaultEnableAuth(for: jwtEnablement, using: sealConfiguration!.root_token) | |
let privateKey = P256.Signing.PrivateKey() | |
let jwtAuthConfiguration = JWTAuthConfiguration( | |
jwt_validation_pubkeys: [privateKey.publicKey.pemRepresentation], | |
bound_issuer: "io.thecb4", | |
jwt_supported_algs: ["ES256"] | |
) | |
vaultConfigureJwtAuth(with: jwtAuthConfiguration, using: sealConfiguration!.root_token) | |
let appPolicy = VaultPolicy( | |
name: "app", | |
policy: | |
""" | |
path "secret/app/*" { | |
capabilities = ["create", "read"] | |
} | |
""" | |
) | |
vaultCreatePolicy(appPolicy, using: sealConfiguration!.root_token) | |
let jwtRole = JwtRole( | |
name: "jwtRole", | |
role_type: "jwt", | |
bound_audiences: ["io.thecb4-v1"], | |
user_claim: "user_email", | |
policies: ["app"], | |
token_max_ttl: "0s" | |
) | |
vaultCreateJwtRole(jwtRole, using: sealConfiguration!.root_token) | |
struct Header: Encodable { | |
let alg = "ES256" | |
let kid = "0001" | |
let typ = "JWT" | |
} | |
struct Payload: Encodable { | |
let iss = "io.thecb4" | |
let iat = Date().timeIntervalSince1970 | |
let exp = (Date() + 5 * 60).timeIntervalSince1970 | |
let aud = "io.thecb4-v1" | |
let user_email = "cavelle@tehcb4.io" | |
} | |
let jwtToken = JWT(user: "cavelle@tehcb4.io").sign(using: privateKey) | |
let jwtLogin = JwtLogin( | |
role: "jwtRole", | |
jwt: JWT(user: "cavelle@tehcb4.io").sign(using: privateKey) | |
) | |
let authenticated = vaultJwtLogin(jwtLogin) | |
print("your jwt login token is \(authenticated?.auth.client_token ?? "invalid")") | |
let secret = | |
""" | |
{"password": "my-long-password"} | |
""".data(using: .utf8)! | |
createSecret(on: "secret/app/info", value: secret, using: authenticated!.auth.client_token) | |
if let retrievedSecret = readSecret(on: "secret/app/info", using: authenticated!.auth.client_token) { | |
print(retrievedSecret) | |
} else { | |
print("Couldn't retrieve secret") | |
} | |
let down = try executeCommand(command: "/usr/local/bin/docker-compose down") | |
print(down) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment