Add service environment vault to all 40 un-inception implementations
Implements encrypted vault for storing service environment variables: - service env status <id> - Check vault status (GET /services/:id/env) - service env set <id> -e KEY=VAL - Set vault contents (PUT /services/:id/env) - service env export <id> - Export vault as .env format (POST /services/:id/env/export) - service env delete <id> - Delete vault (DELETE /services/:id/env) - Auto-vault on service creation with -e or --env-file flags All implementations use HMAC-SHA256 authentication and text/plain content type for vault PUT requests.
This commit is contained in:
parent
1b35f1099e
commit
87397949d1
40 changed files with 7523 additions and 291 deletions
179
Un.java
179
Un.java
|
|
@ -234,6 +234,12 @@ public class Un {
|
|||
String publicKey = keys[0];
|
||||
String secretKey = keys[1];
|
||||
|
||||
// Handle service env subcommand
|
||||
if (args.envAction != null) {
|
||||
cmdServiceEnv(args, publicKey, secretKey);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.serviceList) {
|
||||
Map<String, Object> result = apiRequest("/services", "GET", null, publicKey, secretKey);
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -374,11 +380,25 @@ public class Un {
|
|||
}
|
||||
|
||||
Map<String, Object> result = apiRequest("/services", "POST", payload, publicKey, secretKey);
|
||||
System.out.println(GREEN + "Service created: " + result.getOrDefault("id", "N/A") + RESET);
|
||||
String serviceId = (String) result.get("id");
|
||||
System.out.println(GREEN + "Service created: " + (serviceId != null ? serviceId : "N/A") + RESET);
|
||||
System.out.println("Name: " + result.getOrDefault("name", "N/A"));
|
||||
if (result.containsKey("url")) {
|
||||
System.out.println("URL: " + result.get("url"));
|
||||
}
|
||||
|
||||
// Auto-set vault if env vars provided
|
||||
if (serviceId != null && (!args.env.isEmpty() || (args.envFile != null && !args.envFile.isEmpty()))) {
|
||||
try {
|
||||
String envContent = buildEnvContent(args.env, args.envFile);
|
||||
if (!envContent.isEmpty() && envContent.length() <= 65536) {
|
||||
serviceEnvSet(serviceId, envContent, publicKey, secretKey);
|
||||
System.out.println(GREEN + "Vault configured with environment variables" + RESET);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println(YELLOW + "Warning: Failed to set vault: " + e.getMessage() + RESET);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -599,6 +619,141 @@ public class Un {
|
|||
return parseJson(response);
|
||||
}
|
||||
|
||||
private static String apiRequestText(String endpoint, String method, String body, String publicKey, String secretKey) throws Exception {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
String signatureData = timestamp + ":" + method + ":" + endpoint + ":" + body;
|
||||
String signature = hmacSha256(secretKey, signatureData);
|
||||
|
||||
URL url = new URL(API_BASE + endpoint);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod(method);
|
||||
conn.setRequestProperty("Authorization", "Bearer " + (publicKey != null ? publicKey : secretKey));
|
||||
conn.setRequestProperty("X-Timestamp", String.valueOf(timestamp));
|
||||
conn.setRequestProperty("X-Signature", signature);
|
||||
conn.setRequestProperty("Content-Type", "text/plain");
|
||||
conn.setConnectTimeout(30000);
|
||||
conn.setReadTimeout(300000);
|
||||
|
||||
if (body != null && !body.isEmpty()) {
|
||||
conn.setDoOutput(true);
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(body.getBytes("UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
int status = conn.getResponseCode();
|
||||
if (status < 200 || status >= 300) {
|
||||
String error = readStream(conn.getErrorStream());
|
||||
throw new Exception("HTTP " + status + " - " + error);
|
||||
}
|
||||
|
||||
return readStream(conn.getInputStream());
|
||||
}
|
||||
|
||||
private static String readEnvFile(String path) throws Exception {
|
||||
return new String(Files.readAllBytes(Paths.get(path)), "UTF-8");
|
||||
}
|
||||
|
||||
private static String buildEnvContent(List<String> envs, String envFile) throws Exception {
|
||||
StringBuilder parts = new StringBuilder();
|
||||
if (envFile != null && !envFile.isEmpty()) {
|
||||
parts.append(readEnvFile(envFile).trim());
|
||||
}
|
||||
for (String e : envs) {
|
||||
if (e.contains("=")) {
|
||||
if (parts.length() > 0) parts.append("\n");
|
||||
parts.append(e);
|
||||
}
|
||||
}
|
||||
return parts.toString();
|
||||
}
|
||||
|
||||
private static Map<String, Object> serviceEnvStatus(String serviceId, String publicKey, String secretKey) throws Exception {
|
||||
return apiRequest("/services/" + serviceId + "/env", "GET", null, publicKey, secretKey);
|
||||
}
|
||||
|
||||
private static boolean serviceEnvSet(String serviceId, String envContent, String publicKey, String secretKey) throws Exception {
|
||||
apiRequestText("/services/" + serviceId + "/env", "PUT", envContent, publicKey, secretKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Map<String, Object> serviceEnvExport(String serviceId, String publicKey, String secretKey) throws Exception {
|
||||
return apiRequest("/services/" + serviceId + "/env/export", "POST", null, publicKey, secretKey);
|
||||
}
|
||||
|
||||
private static boolean serviceEnvDelete(String serviceId, String publicKey, String secretKey) throws Exception {
|
||||
apiRequest("/services/" + serviceId + "/env", "DELETE", null, publicKey, secretKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void cmdServiceEnv(Args args, String publicKey, String secretKey) throws Exception {
|
||||
String action = args.envAction;
|
||||
String target = args.envTarget;
|
||||
|
||||
if (action == null) {
|
||||
System.err.println(RED + "Error: Usage: service env <status|set|export|delete> <service_id>" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (action.equals("status")) {
|
||||
if (target == null) {
|
||||
System.err.println(RED + "Error: Usage: service env status <service_id>" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
Map<String, Object> result = serviceEnvStatus(target, publicKey, secretKey);
|
||||
Boolean hasEnv = (Boolean) result.get("has_env");
|
||||
Number size = (Number) result.get("size");
|
||||
String updatedAt = (String) result.get("updated_at");
|
||||
System.out.println("Service: " + target);
|
||||
System.out.println("Has Vault: " + (hasEnv != null && hasEnv ? "Yes" : "No"));
|
||||
if (hasEnv != null && hasEnv) {
|
||||
System.out.println("Size: " + (size != null ? size.intValue() : 0) + " bytes");
|
||||
System.out.println("Updated: " + (updatedAt != null ? updatedAt : "N/A"));
|
||||
}
|
||||
} else if (action.equals("set")) {
|
||||
if (target == null) {
|
||||
System.err.println(RED + "Error: Usage: service env set <service_id> [-e KEY=VAL] [--env-file FILE]" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
String envContent = buildEnvContent(args.env, args.envFile);
|
||||
if (envContent.isEmpty()) {
|
||||
System.err.println(RED + "Error: No environment variables specified. Use -e KEY=VAL or --env-file FILE" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
if (envContent.length() > 65536) {
|
||||
System.err.println(RED + "Error: Environment content exceeds 64KB limit" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
serviceEnvSet(target, envContent, publicKey, secretKey);
|
||||
System.out.println(GREEN + "Vault updated for service: " + target + RESET);
|
||||
} else if (action.equals("export")) {
|
||||
if (target == null) {
|
||||
System.err.println(RED + "Error: Usage: service env export <service_id>" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
Map<String, Object> result = serviceEnvExport(target, publicKey, secretKey);
|
||||
String content = (String) result.get("content");
|
||||
if (content != null && !content.isEmpty()) {
|
||||
System.out.print(content);
|
||||
if (!content.endsWith("\n")) {
|
||||
System.out.println();
|
||||
}
|
||||
} else {
|
||||
System.err.println(YELLOW + "Vault is empty" + RESET);
|
||||
}
|
||||
} else if (action.equals("delete")) {
|
||||
if (target == null) {
|
||||
System.err.println(RED + "Error: Usage: service env delete <service_id>" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
serviceEnvDelete(target, publicKey, secretKey);
|
||||
System.out.println(GREEN + "Vault deleted for service: " + target + RESET);
|
||||
} else {
|
||||
System.err.println(RED + "Error: Unknown env action: " + action + ". Use status, set, export, or delete" + RESET);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readStream(InputStream is) throws IOException {
|
||||
if (is == null) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
|
@ -810,6 +965,11 @@ public class Un {
|
|||
String serviceDumpBootstrap = null;
|
||||
String serviceDumpFile = null;
|
||||
|
||||
// Vault args
|
||||
String envFile = null;
|
||||
String envAction = null;
|
||||
String envTarget = null;
|
||||
|
||||
// Key args
|
||||
boolean keyExtend = false;
|
||||
}
|
||||
|
|
@ -873,6 +1033,16 @@ public class Un {
|
|||
result.serviceDumpBootstrap = args[++i];
|
||||
} else if (arg.equals("--dump-file")) {
|
||||
result.serviceDumpFile = args[++i];
|
||||
} else if (arg.equals("--env-file")) {
|
||||
result.envFile = args[++i];
|
||||
} else if (arg.equals("env") && "service".equals(result.command)) {
|
||||
// service env <action> <target>
|
||||
if (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
||||
result.envAction = args[++i];
|
||||
if (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
||||
result.envTarget = args[++i];
|
||||
}
|
||||
}
|
||||
} else if (arg.equals("--extend")) {
|
||||
result.keyExtend = true;
|
||||
} else if (!arg.startsWith("-")) {
|
||||
|
|
@ -923,6 +1093,13 @@ public class Un {
|
|||
System.out.println(" --dump-bootstrap ID Dump bootstrap script");
|
||||
System.out.println(" --dump-file FILE File to save bootstrap (with --dump-bootstrap)");
|
||||
System.out.println();
|
||||
System.out.println("Service env (vault) commands:");
|
||||
System.out.println(" service env status ID Check vault status");
|
||||
System.out.println(" service env set ID [-e K=V] Set vault contents");
|
||||
System.out.println(" service env export ID Export vault contents");
|
||||
System.out.println(" service env delete ID Delete vault");
|
||||
System.out.println(" --env-file FILE Read env vars from file");
|
||||
System.out.println();
|
||||
System.out.println("Key options:");
|
||||
System.out.println(" --extend Open browser to extend key");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue