@mtndev/cli 1.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/cjs/__chunks/VH7oDXx4.cjs +28 -0
- package/cjs/commands/aws/ec2/allow-ip/handler.cjs +261 -0
- package/cjs/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
- package/cjs/commands/aws/ec2/allow-ip/index.cjs +25 -0
- package/cjs/commands/aws/ec2/allow-ip/index.d.ts +3 -0
- package/cjs/commands/aws/ec2/allow-ip/schema.cjs +33 -0
- package/cjs/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
- package/cjs/commands/aws/ec2/allow-ip/utils.cjs +379 -0
- package/cjs/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
- package/cjs/commands/aws/iam/admin-group.cjs +111 -0
- package/cjs/commands/aws/iam/admin-group.d.ts +55 -0
- package/cjs/commands/aws/iam/break-glass/handler.cjs +127 -0
- package/cjs/commands/aws/iam/break-glass/handler.d.ts +7 -0
- package/cjs/commands/aws/iam/break-glass/index.cjs +7 -0
- package/cjs/commands/aws/iam/break-glass/index.d.ts +2 -0
- package/cjs/commands/aws/iam/break-glass/schema.cjs +21 -0
- package/cjs/commands/aws/iam/break-glass/schema.d.ts +25 -0
- package/cjs/commands/aws/iam/check-admin/handler.cjs +89 -0
- package/cjs/commands/aws/iam/check-admin/handler.d.ts +5 -0
- package/cjs/commands/aws/iam/check-admin/index.cjs +7 -0
- package/cjs/commands/aws/iam/check-admin/index.d.ts +2 -0
- package/cjs/commands/aws/iam/check-admin/schema.cjs +19 -0
- package/cjs/commands/aws/iam/check-admin/schema.d.ts +19 -0
- package/cjs/commands/aws/iam/policies/s3-restrictions-deny-non-s3-services.json +20 -0
- package/cjs/commands/aws/iam/policies/sts-federation.json +11 -0
- package/cjs/commands/aws/iam/policies/user-admins-deny-list.json +175 -0
- package/cjs/commands/aws/iam/policies/user-admins-deny-regions-list.json +41 -0
- package/cjs/commands/aws/iam/policies/user-admins-self-service-iam.json +56 -0
- package/cjs/commands/aws/iam/policies/user-admins-write-list.json +117 -0
- package/cjs/commands/aws/iam/remove-admin/handler.cjs +120 -0
- package/cjs/commands/aws/iam/remove-admin/handler.d.ts +6 -0
- package/cjs/commands/aws/iam/remove-admin/index.cjs +7 -0
- package/cjs/commands/aws/iam/remove-admin/index.d.ts +2 -0
- package/cjs/commands/aws/iam/remove-admin/schema.cjs +22 -0
- package/cjs/commands/aws/iam/remove-admin/schema.d.ts +28 -0
- package/cjs/commands/aws/iam/setup-acl/handler.cjs +227 -0
- package/cjs/commands/aws/iam/setup-acl/handler.d.ts +6 -0
- package/cjs/commands/aws/iam/setup-acl/index.cjs +13 -0
- package/cjs/commands/aws/iam/setup-acl/index.d.ts +3 -0
- package/cjs/commands/aws/iam/setup-acl/schema.cjs +19 -0
- package/cjs/commands/aws/iam/setup-acl/schema.d.ts +24 -0
- package/cjs/commands/aws/iam/setup-acl/utils.cjs +305 -0
- package/cjs/commands/aws/iam/setup-acl/utils.d.ts +61 -0
- package/cjs/commands/aws/iam/users/handler.cjs +59 -0
- package/cjs/commands/aws/iam/users/handler.d.ts +5 -0
- package/cjs/commands/aws/iam/users/index.cjs +18 -0
- package/cjs/commands/aws/iam/users/index.d.ts +3 -0
- package/cjs/commands/aws/iam/users/schema.cjs +17 -0
- package/cjs/commands/aws/iam/users/schema.d.ts +18 -0
- package/cjs/commands/aws/iam/users/utils.cjs +454 -0
- package/cjs/commands/aws/iam/users/utils.d.ts +119 -0
- package/cjs/commands/aws/index.cjs +223 -0
- package/cjs/commands/aws/index.d.ts +14 -0
- package/cjs/commands/aws/route53/add-record/handler.cjs +264 -0
- package/cjs/commands/aws/route53/add-record/handler.d.ts +36 -0
- package/cjs/commands/aws/route53/add-record/index.cjs +26 -0
- package/cjs/commands/aws/route53/add-record/index.d.ts +3 -0
- package/cjs/commands/aws/route53/add-record/schema.cjs +43 -0
- package/cjs/commands/aws/route53/add-record/schema.d.ts +45 -0
- package/cjs/commands/aws/route53/add-record/utils.cjs +404 -0
- package/cjs/commands/aws/route53/add-record/utils.d.ts +216 -0
- package/cjs/commands/aws/route53/remove-record/handler.cjs +114 -0
- package/cjs/commands/aws/route53/remove-record/handler.d.ts +6 -0
- package/cjs/commands/aws/route53/remove-record/index.cjs +5 -0
- package/cjs/commands/aws/route53/remove-record/index.d.ts +2 -0
- package/cjs/commands/aws/route53/remove-record/schema.cjs +22 -0
- package/cjs/commands/aws/route53/remove-record/schema.d.ts +30 -0
- package/cjs/commands/aws/schema.cjs +19 -0
- package/cjs/commands/aws/schema.d.ts +13 -0
- package/cjs/commands/aws/ses/send-email/handler.cjs +73 -0
- package/cjs/commands/aws/ses/send-email/handler.d.ts +5 -0
- package/cjs/commands/aws/ses/send-email/index.cjs +9 -0
- package/cjs/commands/aws/ses/send-email/index.d.ts +3 -0
- package/cjs/commands/aws/ses/send-email/schema.cjs +24 -0
- package/cjs/commands/aws/ses/send-email/schema.d.ts +39 -0
- package/cjs/commands/aws/ses/send-email/utils.cjs +87 -0
- package/cjs/commands/aws/ses/send-email/utils.d.ts +47 -0
- package/cjs/commands/aws/ses/smtp-password.cjs +72 -0
- package/cjs/commands/aws/ses/smtp-password.d.ts +21 -0
- package/cjs/commands/aws/utils/exec-aws.cjs +145 -0
- package/cjs/commands/aws/utils/exec-aws.d.ts +79 -0
- package/cjs/commands/aws/utils/resolve-policy-path.cjs +15 -0
- package/cjs/commands/aws/utils/resolve-policy-path.d.ts +8 -0
- package/cjs/commands/aws/utils/resolve-profile.cjs +93 -0
- package/cjs/commands/aws/utils/resolve-profile.d.ts +28 -0
- package/cjs/commands/aws/utils/tty.cjs +54 -0
- package/cjs/commands/aws/utils/tty.d.ts +24 -0
- package/cjs/commands/db/create-readonly-user/handler.cjs +254 -0
- package/cjs/commands/db/create-readonly-user/handler.d.ts +5 -0
- package/cjs/commands/db/create-readonly-user/index.cjs +18 -0
- package/cjs/commands/db/create-readonly-user/index.d.ts +3 -0
- package/cjs/commands/db/create-readonly-user/mysql.cjs +102 -0
- package/cjs/commands/db/create-readonly-user/mysql.d.ts +33 -0
- package/cjs/commands/db/create-readonly-user/postgres.cjs +209 -0
- package/cjs/commands/db/create-readonly-user/postgres.d.ts +31 -0
- package/cjs/commands/db/create-readonly-user/schema.cjs +39 -0
- package/cjs/commands/db/create-readonly-user/schema.d.ts +55 -0
- package/cjs/commands/db/create-readonly-user/utils.cjs +160 -0
- package/cjs/commands/db/create-readonly-user/utils.d.ts +112 -0
- package/cjs/commands/db/index.cjs +46 -0
- package/cjs/commands/db/index.d.ts +6 -0
- package/cjs/commands/doppler/handler.cjs +95 -9
- package/cjs/commands/doppler/index.cjs +33 -41
- package/cjs/commands/doppler/index.d.ts +1 -0
- package/cjs/commands/doppler/schema.cjs +130 -6
- package/cjs/commands/doppler/schema.d.ts +233 -17
- package/cjs/commands/doppler/set-secret.cjs +378 -0
- package/cjs/commands/doppler/set-secret.d.ts +113 -0
- package/cjs/commands/doppler/utils/exec.cjs +106 -12
- package/cjs/commands/doppler/utils.cjs +412 -30
- package/cjs/commands/doppler/utils.d.ts +32 -8
- package/cjs/commands/icons/handler.cjs +82 -8
- package/cjs/commands/icons/index.cjs +15 -31
- package/cjs/commands/icons/schema.cjs +101 -13
- package/cjs/commands/icons/schema.d.ts +1 -1
- package/cjs/commands/icons/utils.cjs +131 -13
- package/cjs/commands/index.cjs +54 -82
- package/cjs/commands/schema.cjs +25 -8
- package/cjs/index.cjs +3 -4
- package/esm/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
- package/esm/commands/aws/ec2/allow-ip/handler.js +258 -0
- package/esm/commands/aws/ec2/allow-ip/index.d.ts +3 -0
- package/esm/commands/aws/ec2/allow-ip/index.js +4 -0
- package/esm/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
- package/esm/commands/aws/ec2/allow-ip/schema.js +29 -0
- package/esm/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
- package/esm/commands/aws/ec2/allow-ip/utils.js +363 -0
- package/esm/commands/aws/iam/admin-group.d.ts +55 -0
- package/esm/commands/aws/iam/admin-group.js +104 -0
- package/esm/commands/aws/iam/break-glass/handler.d.ts +7 -0
- package/esm/commands/aws/iam/break-glass/handler.js +124 -0
- package/esm/commands/aws/iam/break-glass/index.d.ts +2 -0
- package/esm/commands/aws/iam/break-glass/index.js +4 -0
- package/esm/commands/aws/iam/break-glass/schema.d.ts +25 -0
- package/esm/commands/aws/iam/break-glass/schema.js +19 -0
- package/esm/commands/aws/iam/check-admin/handler.d.ts +5 -0
- package/esm/commands/aws/iam/check-admin/handler.js +86 -0
- package/esm/commands/aws/iam/check-admin/index.d.ts +2 -0
- package/esm/commands/aws/iam/check-admin/index.js +4 -0
- package/esm/commands/aws/iam/check-admin/schema.d.ts +19 -0
- package/esm/commands/aws/iam/check-admin/schema.js +17 -0
- package/esm/commands/aws/iam/policies/s3-restrictions-deny-non-s3-services.json +20 -0
- package/esm/commands/aws/iam/policies/sts-federation.json +11 -0
- package/esm/commands/aws/iam/policies/user-admins-deny-list.json +175 -0
- package/esm/commands/aws/iam/policies/user-admins-deny-regions-list.json +41 -0
- package/esm/commands/aws/iam/policies/user-admins-self-service-iam.json +56 -0
- package/esm/commands/aws/iam/policies/user-admins-write-list.json +117 -0
- package/esm/commands/aws/iam/remove-admin/handler.d.ts +6 -0
- package/esm/commands/aws/iam/remove-admin/handler.js +117 -0
- package/esm/commands/aws/iam/remove-admin/index.d.ts +2 -0
- package/esm/commands/aws/iam/remove-admin/index.js +4 -0
- package/esm/commands/aws/iam/remove-admin/schema.d.ts +28 -0
- package/esm/commands/aws/iam/remove-admin/schema.js +20 -0
- package/esm/commands/aws/iam/setup-acl/handler.d.ts +6 -0
- package/esm/commands/aws/iam/setup-acl/handler.js +224 -0
- package/esm/commands/aws/iam/setup-acl/index.d.ts +3 -0
- package/esm/commands/aws/iam/setup-acl/index.js +4 -0
- package/esm/commands/aws/iam/setup-acl/schema.d.ts +24 -0
- package/esm/commands/aws/iam/setup-acl/schema.js +18 -0
- package/esm/commands/aws/iam/setup-acl/utils.d.ts +61 -0
- package/esm/commands/aws/iam/setup-acl/utils.js +298 -0
- package/esm/commands/aws/iam/users/handler.d.ts +5 -0
- package/esm/commands/aws/iam/users/handler.js +56 -0
- package/esm/commands/aws/iam/users/index.d.ts +3 -0
- package/esm/commands/aws/iam/users/index.js +4 -0
- package/esm/commands/aws/iam/users/schema.d.ts +18 -0
- package/esm/commands/aws/iam/users/schema.js +16 -0
- package/esm/commands/aws/iam/users/utils.d.ts +119 -0
- package/esm/commands/aws/iam/users/utils.js +440 -0
- package/esm/commands/aws/index.d.ts +14 -0
- package/esm/commands/aws/index.js +142 -0
- package/esm/commands/aws/route53/add-record/handler.d.ts +36 -0
- package/esm/commands/aws/route53/add-record/handler.js +258 -0
- package/esm/commands/aws/route53/add-record/index.d.ts +3 -0
- package/esm/commands/aws/route53/add-record/index.js +4 -0
- package/esm/commands/aws/route53/add-record/schema.d.ts +45 -0
- package/esm/commands/aws/route53/add-record/schema.js +40 -0
- package/esm/commands/aws/route53/add-record/utils.d.ts +216 -0
- package/esm/commands/aws/route53/add-record/utils.js +389 -0
- package/esm/commands/aws/route53/remove-record/handler.d.ts +6 -0
- package/esm/commands/aws/route53/remove-record/handler.js +111 -0
- package/esm/commands/aws/route53/remove-record/index.d.ts +2 -0
- package/esm/commands/aws/route53/remove-record/index.js +3 -0
- package/esm/commands/aws/route53/remove-record/schema.d.ts +30 -0
- package/esm/commands/aws/route53/remove-record/schema.js +21 -0
- package/esm/commands/aws/schema.d.ts +13 -0
- package/esm/commands/aws/schema.js +16 -0
- package/esm/commands/aws/ses/send-email/handler.d.ts +5 -0
- package/esm/commands/aws/ses/send-email/handler.js +70 -0
- package/esm/commands/aws/ses/send-email/index.d.ts +3 -0
- package/esm/commands/aws/ses/send-email/index.js +4 -0
- package/esm/commands/aws/ses/send-email/schema.d.ts +39 -0
- package/esm/commands/aws/ses/send-email/schema.js +23 -0
- package/esm/commands/aws/ses/send-email/utils.d.ts +47 -0
- package/esm/commands/aws/ses/send-email/utils.js +84 -0
- package/esm/commands/aws/ses/smtp-password.d.ts +21 -0
- package/esm/commands/aws/ses/smtp-password.js +69 -0
- package/esm/commands/aws/utils/exec-aws.d.ts +79 -0
- package/esm/commands/aws/utils/exec-aws.js +137 -0
- package/esm/commands/aws/utils/resolve-policy-path.d.ts +8 -0
- package/esm/commands/aws/utils/resolve-policy-path.js +14 -0
- package/esm/commands/aws/utils/resolve-profile.d.ts +28 -0
- package/esm/commands/aws/utils/resolve-profile.js +88 -0
- package/esm/commands/aws/utils/tty.d.ts +24 -0
- package/esm/commands/aws/utils/tty.js +51 -0
- package/esm/commands/db/create-readonly-user/handler.d.ts +5 -0
- package/esm/commands/db/create-readonly-user/handler.js +251 -0
- package/esm/commands/db/create-readonly-user/index.d.ts +3 -0
- package/esm/commands/db/create-readonly-user/index.js +4 -0
- package/esm/commands/db/create-readonly-user/mysql.d.ts +33 -0
- package/esm/commands/db/create-readonly-user/mysql.js +98 -0
- package/esm/commands/db/create-readonly-user/postgres.d.ts +31 -0
- package/esm/commands/db/create-readonly-user/postgres.js +205 -0
- package/esm/commands/db/create-readonly-user/schema.d.ts +55 -0
- package/esm/commands/db/create-readonly-user/schema.js +35 -0
- package/esm/commands/db/create-readonly-user/utils.d.ts +112 -0
- package/esm/commands/db/create-readonly-user/utils.js +151 -0
- package/esm/commands/db/index.d.ts +6 -0
- package/esm/commands/db/index.js +31 -0
- package/esm/commands/doppler/handler.js +92 -9
- package/esm/commands/doppler/index.d.ts +1 -0
- package/esm/commands/doppler/index.js +6 -41
- package/esm/commands/doppler/schema.d.ts +233 -17
- package/esm/commands/doppler/schema.js +125 -7
- package/esm/commands/doppler/set-secret.d.ts +113 -0
- package/esm/commands/doppler/set-secret.js +370 -0
- package/esm/commands/doppler/utils/exec.js +100 -12
- package/esm/commands/doppler/utils.d.ts +32 -8
- package/esm/commands/doppler/utils.js +396 -30
- package/esm/commands/icons/handler.js +79 -8
- package/esm/commands/icons/index.js +4 -31
- package/esm/commands/icons/schema.d.ts +1 -1
- package/esm/commands/icons/schema.js +97 -15
- package/esm/commands/icons/utils.js +125 -13
- package/esm/commands/index.js +49 -78
- package/esm/commands/schema.js +22 -7
- package/esm/index.js +3 -4
- package/package.json +12 -2
- package/vitest.config.ts +11 -0
- package/cjs/__chunks/4GFJUCSP.cjs +0 -52
- package/cjs/__chunks/4SFWHCB3.cjs +0 -133
- package/cjs/__chunks/B2KFW7T7.cjs +0 -1
- package/cjs/__chunks/EMMC33QA.cjs +0 -401
- package/cjs/__chunks/INFOPAHN.cjs +0 -117
- package/cjs/__chunks/J36P6UNE.cjs +0 -75
- package/cjs/__chunks/NIMXXHJQ.cjs +0 -92
- package/cjs/__chunks/NSRFG4DF.cjs +0 -100
- package/cjs/__chunks/WNJ3ZYLI.cjs +0 -1
- package/cjs/__chunks/ZMDIOZPT.cjs +0 -22
- package/esm/__chunks/46HGXFME.js +0 -0
- package/esm/__chunks/6G4PXXKF.js +0 -75
- package/esm/__chunks/6L5RNR7G.js +0 -100
- package/esm/__chunks/FL7AILGH.js +0 -133
- package/esm/__chunks/HJSXC6HP.js +0 -117
- package/esm/__chunks/O262GK4B.js +0 -401
- package/esm/__chunks/O3CCA76Q.js +0 -22
- package/esm/__chunks/SJXQJ7RS.js +0 -92
- package/esm/__chunks/SNCKIAOR.js +0 -0
- package/esm/__chunks/X3R5PSWH.js +0 -52
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { promptChoice } from "../../utils/tty.js";
|
|
2
|
+
import { AwsCliError, getCallerIdentity, requireCommands } from "../../utils/exec-aws.js";
|
|
3
|
+
import { resolveProfile } from "../../utils/resolve-profile.js";
|
|
4
|
+
import { DEFAULT_REGION, allowIpOptionsSchema } from "./schema.js";
|
|
5
|
+
import { authorizeRule, describeSecurityGroups, extractRoamingFirstName, getCurrentPublicIp, getSecurityGroupDetails, instanceLabel, listEnabledRegions, listInstances, listSshIngressRules, printSshRules, resolveRoamingRule, revokeRule, roamingRuleDescription, ruleAlreadyAllowsIp, securityGroupLabel } from "./utils.js";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { ZodError } from "zod";
|
|
8
|
+
//#region src/commands/aws/ec2/allow-ip/handler.js
|
|
9
|
+
/**
|
|
10
|
+
* Resolve which region to operate in: explicit, else default if it has instances,
|
|
11
|
+
* else prompt from the enabled regions.
|
|
12
|
+
* @param {{ profile?: string; region?: string; verbose: boolean }} params
|
|
13
|
+
* @returns {Promise<string>}
|
|
14
|
+
*/
|
|
15
|
+
async function resolveRegion(params) {
|
|
16
|
+
const { profile, region, verbose } = params;
|
|
17
|
+
if (region) return region;
|
|
18
|
+
if (listInstances({
|
|
19
|
+
profile,
|
|
20
|
+
region: "us-east-1",
|
|
21
|
+
verbose
|
|
22
|
+
}).length > 0) return DEFAULT_REGION;
|
|
23
|
+
const regions = listEnabledRegions({
|
|
24
|
+
profile,
|
|
25
|
+
region: DEFAULT_REGION,
|
|
26
|
+
verbose
|
|
27
|
+
});
|
|
28
|
+
if (regions.length === 0) throw new Error("No enabled AWS regions found for this profile");
|
|
29
|
+
console.log(`No EC2 instances found in ${DEFAULT_REGION}.`);
|
|
30
|
+
const chosen = regions[await promptChoice({
|
|
31
|
+
message: "Choose a region:",
|
|
32
|
+
options: regions
|
|
33
|
+
})];
|
|
34
|
+
if (!chosen) throw new Error("No region selected");
|
|
35
|
+
return chosen;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Handle errors from authorize/revoke with the same UX as the original script.
|
|
39
|
+
* Returns when the situation is benign (duplicate); exits the process on a
|
|
40
|
+
* permissions failure; rethrows anything else.
|
|
41
|
+
* @param {AwsCliError} error
|
|
42
|
+
* @param {{
|
|
43
|
+
* securityGroup: import("./utils.js").SecurityGroup;
|
|
44
|
+
* sshRules: import("./utils.js").SshIngressRule[];
|
|
45
|
+
* newCidr: string;
|
|
46
|
+
* ruleDescription: string;
|
|
47
|
+
* action: "create" | "update";
|
|
48
|
+
* }} ctx
|
|
49
|
+
*/
|
|
50
|
+
function handleRuleChangeError(error, ctx) {
|
|
51
|
+
const { securityGroup, sshRules, newCidr, ruleDescription, action } = ctx;
|
|
52
|
+
if (error.code === "InvalidPermission.Duplicate") {
|
|
53
|
+
const matching = ruleAlreadyAllowsIp(sshRules, newCidr);
|
|
54
|
+
const label = matching ? matching.description : ruleDescription;
|
|
55
|
+
console.log(`Security group ${securityGroup.groupId} already allows SSH from ${newCidr} (${label}). Nothing to do.`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (error.code === "UnauthorizedOperation") {
|
|
59
|
+
console.error(`Could not ${action} the SSH rule: your AWS profile does not have ec2:AuthorizeSecurityGroupIngress or ec2:RevokeSecurityGroupIngress on ${securityGroup.groupName} (${securityGroup.groupId}).`);
|
|
60
|
+
console.error("");
|
|
61
|
+
console.error("Current single-host SSH rules on port 22:");
|
|
62
|
+
printSshRules(sshRules, (message) => console.error(message));
|
|
63
|
+
if (action === "create" && sshRules.length > 0) {
|
|
64
|
+
console.error("");
|
|
65
|
+
console.error("A rule may already exist. Re-run to update your named roaming rule, or ask an admin to grant security group ingress permissions.");
|
|
66
|
+
}
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* `aws ec2 allow-ip` handler: update or create a named roaming SSH rule for the
|
|
73
|
+
* caller's current public IP on a chosen EC2 instance's security group.
|
|
74
|
+
* @param {unknown} options
|
|
75
|
+
*/
|
|
76
|
+
async function allowIpCommand(options) {
|
|
77
|
+
try {
|
|
78
|
+
const { profile: profileOption, region: regionOption, ruleName, verbose } = allowIpOptionsSchema.parse(options);
|
|
79
|
+
const profile = await resolveProfile({ profile: profileOption });
|
|
80
|
+
if (!profile) {
|
|
81
|
+
console.error(chalk.red("Error: pass --profile or set AWS_PROFILE."));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
requireCommands(["aws"]);
|
|
85
|
+
const identity = getCallerIdentity({
|
|
86
|
+
profile,
|
|
87
|
+
region: DEFAULT_REGION,
|
|
88
|
+
verbose
|
|
89
|
+
});
|
|
90
|
+
const firstName = extractRoamingFirstName({
|
|
91
|
+
profile,
|
|
92
|
+
identity
|
|
93
|
+
});
|
|
94
|
+
const ruleDescription = ruleName ?? roamingRuleDescription(firstName);
|
|
95
|
+
console.log("Caller:");
|
|
96
|
+
console.log(` Account: ${identity.Account}`);
|
|
97
|
+
console.log(` Arn: ${identity.Arn}`);
|
|
98
|
+
console.log(` Roaming rule: ${ruleDescription}`);
|
|
99
|
+
console.log("");
|
|
100
|
+
const region = await resolveRegion({
|
|
101
|
+
profile,
|
|
102
|
+
region: regionOption,
|
|
103
|
+
verbose
|
|
104
|
+
});
|
|
105
|
+
console.log(`Region: ${region}`);
|
|
106
|
+
console.log("");
|
|
107
|
+
const instances = listInstances({
|
|
108
|
+
profile,
|
|
109
|
+
region,
|
|
110
|
+
verbose
|
|
111
|
+
});
|
|
112
|
+
if (instances.length === 0) {
|
|
113
|
+
console.log(`No EC2 instances found in ${region}.`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
const instance = instances[await promptChoice({
|
|
117
|
+
message: "Choose an EC2 instance:",
|
|
118
|
+
options: instances.map((instance) => instanceLabel(instance))
|
|
119
|
+
})];
|
|
120
|
+
if (!instance) throw new Error("No instance selected");
|
|
121
|
+
console.log("");
|
|
122
|
+
const securityGroups = describeSecurityGroups({
|
|
123
|
+
profile,
|
|
124
|
+
region,
|
|
125
|
+
groupIds: instance.securityGroupIds,
|
|
126
|
+
verbose
|
|
127
|
+
});
|
|
128
|
+
if (securityGroups.length === 0) {
|
|
129
|
+
console.log("No security groups found on the selected instance.");
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
let securityGroup;
|
|
133
|
+
if (securityGroups.length === 1) {
|
|
134
|
+
const onlyGroup = securityGroups[0];
|
|
135
|
+
if (!onlyGroup) throw new Error("No security group found on the selected instance");
|
|
136
|
+
securityGroup = onlyGroup;
|
|
137
|
+
console.log(`Security group: ${securityGroupLabel(securityGroup)}`);
|
|
138
|
+
} else securityGroup = securityGroups[await promptChoice({
|
|
139
|
+
message: "Choose a security group:",
|
|
140
|
+
options: securityGroups.map((group) => securityGroupLabel(group))
|
|
141
|
+
})];
|
|
142
|
+
if (!securityGroup) throw new Error("No security group selected");
|
|
143
|
+
console.log("");
|
|
144
|
+
const currentIp = await getCurrentPublicIp();
|
|
145
|
+
const newCidr = `${currentIp}/32`;
|
|
146
|
+
console.log(`Current public IP: ${currentIp}`);
|
|
147
|
+
console.log("");
|
|
148
|
+
const sgDetails = getSecurityGroupDetails({
|
|
149
|
+
profile,
|
|
150
|
+
region,
|
|
151
|
+
groupId: securityGroup.groupId,
|
|
152
|
+
verbose
|
|
153
|
+
});
|
|
154
|
+
if (!sgDetails) {
|
|
155
|
+
console.log("Could not load security group details.");
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const sshRules = listSshIngressRules(sgDetails);
|
|
159
|
+
const roamingRule = resolveRoamingRule(sshRules, ruleDescription);
|
|
160
|
+
if (roamingRule && roamingRule.cidrIp === newCidr) {
|
|
161
|
+
console.log(`Security group ${securityGroup.groupId} already allows SSH from ${newCidr} (${ruleDescription}). Nothing to do.`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const otherRuleAtIp = ruleAlreadyAllowsIp(sshRules, newCidr);
|
|
165
|
+
if (otherRuleAtIp && (!roamingRule || otherRuleAtIp.cidrIp !== roamingRule.cidrIp)) {
|
|
166
|
+
const label = otherRuleAtIp.description || "(no description)";
|
|
167
|
+
console.log(`Security group ${securityGroup.groupId} already allows SSH from ${newCidr} (${label}). Nothing to do.`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (roamingRule) {
|
|
171
|
+
console.log(`Updating SSH rule (${ruleDescription}): ${roamingRule.cidrIp} -> ${newCidr}`);
|
|
172
|
+
try {
|
|
173
|
+
revokeRule({
|
|
174
|
+
profile,
|
|
175
|
+
region,
|
|
176
|
+
groupId: securityGroup.groupId,
|
|
177
|
+
rule: roamingRule,
|
|
178
|
+
verbose
|
|
179
|
+
});
|
|
180
|
+
authorizeRule({
|
|
181
|
+
profile,
|
|
182
|
+
region,
|
|
183
|
+
groupId: securityGroup.groupId,
|
|
184
|
+
cidrIp: newCidr,
|
|
185
|
+
description: ruleDescription,
|
|
186
|
+
fromPort: roamingRule.fromPort,
|
|
187
|
+
toPort: roamingRule.toPort,
|
|
188
|
+
ipProtocol: roamingRule.ipProtocol,
|
|
189
|
+
verbose
|
|
190
|
+
});
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (error instanceof AwsCliError) {
|
|
193
|
+
handleRuleChangeError(error, {
|
|
194
|
+
securityGroup,
|
|
195
|
+
sshRules,
|
|
196
|
+
newCidr,
|
|
197
|
+
ruleDescription,
|
|
198
|
+
action: "update"
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
if (sshRules.length > 0) {
|
|
206
|
+
console.log("Existing single-host SSH rules (unchanged):");
|
|
207
|
+
printSshRules(sshRules);
|
|
208
|
+
console.log("");
|
|
209
|
+
}
|
|
210
|
+
console.log(`Creating new "${ruleDescription}" SSH rule for ${newCidr}`);
|
|
211
|
+
try {
|
|
212
|
+
authorizeRule({
|
|
213
|
+
profile,
|
|
214
|
+
region,
|
|
215
|
+
groupId: securityGroup.groupId,
|
|
216
|
+
cidrIp: newCidr,
|
|
217
|
+
description: ruleDescription,
|
|
218
|
+
verbose
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (error instanceof AwsCliError) {
|
|
222
|
+
handleRuleChangeError(error, {
|
|
223
|
+
securityGroup,
|
|
224
|
+
sshRules,
|
|
225
|
+
newCidr,
|
|
226
|
+
ruleDescription,
|
|
227
|
+
action: "create"
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
console.log("");
|
|
235
|
+
console.log(`Done. ${securityGroup.groupName} (${securityGroup.groupId}) now allows SSH from ${newCidr} (${ruleDescription}).`);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (error instanceof ZodError) {
|
|
238
|
+
console.error(chalk.red("\n❌ Invalid command options:"));
|
|
239
|
+
error.errors.forEach((err) => {
|
|
240
|
+
console.error(chalk.red(` ${err.path.join(".")}: ${err.message}`));
|
|
241
|
+
});
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
const verbose = options?.verbose || false;
|
|
245
|
+
console.error(chalk.red("\n❌ allow-ip failed:"));
|
|
246
|
+
if (error instanceof Error) {
|
|
247
|
+
console.error(chalk.red(` ${error.message}`));
|
|
248
|
+
if (verbose && error.stack) {
|
|
249
|
+
console.error(chalk.gray("\nStack trace:"));
|
|
250
|
+
console.error(chalk.gray(error.stack));
|
|
251
|
+
}
|
|
252
|
+
} else console.error(chalk.red(` ${String(error)}`));
|
|
253
|
+
console.error(chalk.yellow("\n💡 Use --verbose for detailed error information\n"));
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
//#endregion
|
|
258
|
+
export { allowIpCommand };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_REGION, DEFAULT_ROAMING_NAME, SSH_PORT, allowIpOptionsSchema } from "./schema.js";
|
|
2
|
+
import { authorizeRule, describeSecurityGroups, extractRoamingFirstName, getCurrentPublicIp, getSecurityGroupDetails, instanceLabel, listEnabledRegions, listInstances, listSshIngressRules, printSshRules, resolveRoamingRule, revokeRule, roamingRuleDescription, ruleAlreadyAllowsIp, securityGroupLabel, sshRuleLabel } from "./utils.js";
|
|
3
|
+
import { allowIpCommand } from "./handler.js";
|
|
4
|
+
export { DEFAULT_REGION, DEFAULT_ROAMING_NAME, SSH_PORT, allowIpCommand, allowIpOptionsSchema, authorizeRule, describeSecurityGroups, extractRoamingFirstName, getCurrentPublicIp, getSecurityGroupDetails, instanceLabel, listEnabledRegions, listInstances, listSshIngressRules, printSshRules, resolveRoamingRule, revokeRule, roamingRuleDescription, ruleAlreadyAllowsIp, securityGroupLabel, sshRuleLabel };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default region checked first before prompting.
|
|
3
|
+
*/
|
|
4
|
+
export const DEFAULT_REGION: "us-east-1";
|
|
5
|
+
/**
|
|
6
|
+
* SSH port managed by this command.
|
|
7
|
+
*/
|
|
8
|
+
export const SSH_PORT: 22;
|
|
9
|
+
/**
|
|
10
|
+
* Fallback first name when one cannot be derived from the profile/IAM user.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_ROAMING_NAME: "Declan";
|
|
13
|
+
/**
|
|
14
|
+
* Zod schema for `aws ec2 allow-ip` options.
|
|
15
|
+
*/
|
|
16
|
+
export const allowIpOptionsSchema: z.ZodObject<{
|
|
17
|
+
profile: z.ZodOptional<z.ZodString>;
|
|
18
|
+
region: z.ZodOptional<z.ZodString>;
|
|
19
|
+
ruleName: z.ZodOptional<z.ZodString>;
|
|
20
|
+
verbose: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
verbose: boolean;
|
|
23
|
+
profile?: string | undefined;
|
|
24
|
+
region?: string | undefined;
|
|
25
|
+
ruleName?: string | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
profile?: string | undefined;
|
|
28
|
+
region?: string | undefined;
|
|
29
|
+
verbose?: boolean | undefined;
|
|
30
|
+
ruleName?: string | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
export type AllowIpOptions = z.infer<typeof allowIpOptionsSchema>;
|
|
33
|
+
import { z } from "zod";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { profileField, regionField, verboseField } from "../../schema.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/commands/aws/ec2/allow-ip/schema.js
|
|
4
|
+
/**
|
|
5
|
+
* Default region checked first before prompting.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_REGION = "us-east-1";
|
|
8
|
+
/**
|
|
9
|
+
* SSH port managed by this command.
|
|
10
|
+
*/
|
|
11
|
+
const SSH_PORT = 22;
|
|
12
|
+
/**
|
|
13
|
+
* Fallback first name when one cannot be derived from the profile/IAM user.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_ROAMING_NAME = "Declan";
|
|
16
|
+
/**
|
|
17
|
+
* Zod schema for `aws ec2 allow-ip` options.
|
|
18
|
+
*/
|
|
19
|
+
const allowIpOptionsSchema = z.object({
|
|
20
|
+
profile: profileField,
|
|
21
|
+
region: regionField,
|
|
22
|
+
ruleName: z.string().optional().describe("SSH rule description to update or create (default: \"{FirstName} Roaming\" derived from the profile/IAM user)"),
|
|
23
|
+
verbose: verboseField
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {z.infer<typeof allowIpOptionsSchema>} AllowIpOptions
|
|
27
|
+
*/
|
|
28
|
+
//#endregion
|
|
29
|
+
export { DEFAULT_REGION, DEFAULT_ROAMING_NAME, SSH_PORT, allowIpOptionsSchema };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} Ec2Instance
|
|
3
|
+
* @property {string} instanceId
|
|
4
|
+
* @property {string} name
|
|
5
|
+
* @property {string} state
|
|
6
|
+
* @property {string} instanceType
|
|
7
|
+
* @property {string | null} publicIp
|
|
8
|
+
* @property {string[]} securityGroupIds
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} SecurityGroup
|
|
12
|
+
* @property {string} groupId
|
|
13
|
+
* @property {string} groupName
|
|
14
|
+
* @property {string} description
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} SshIngressRule
|
|
18
|
+
* @property {string} cidrIp
|
|
19
|
+
* @property {string} description
|
|
20
|
+
* @property {number} fromPort
|
|
21
|
+
* @property {number} toPort
|
|
22
|
+
* @property {string} ipProtocol
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Determine the caller's current public IPv4 address.
|
|
26
|
+
* @returns {Promise<string>}
|
|
27
|
+
*/
|
|
28
|
+
export function getCurrentPublicIp(): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* List regions that are opted-in / enabled for the profile.
|
|
31
|
+
* @param {{ profile?: string; region: string; verbose?: boolean }} params
|
|
32
|
+
* @returns {string[]}
|
|
33
|
+
*/
|
|
34
|
+
export function listEnabledRegions(params: {
|
|
35
|
+
profile?: string;
|
|
36
|
+
region: string;
|
|
37
|
+
verbose?: boolean;
|
|
38
|
+
}): string[];
|
|
39
|
+
/**
|
|
40
|
+
* List non-terminated EC2 instances in a region.
|
|
41
|
+
* @param {{ profile?: string; region: string; verbose?: boolean }} params
|
|
42
|
+
* @returns {Ec2Instance[]}
|
|
43
|
+
*/
|
|
44
|
+
export function listInstances(params: {
|
|
45
|
+
profile?: string;
|
|
46
|
+
region: string;
|
|
47
|
+
verbose?: boolean;
|
|
48
|
+
}): Ec2Instance[];
|
|
49
|
+
/**
|
|
50
|
+
* Describe security groups by id.
|
|
51
|
+
* @param {{ profile?: string; region: string; groupIds: string[]; verbose?: boolean }} params
|
|
52
|
+
* @returns {SecurityGroup[]}
|
|
53
|
+
*/
|
|
54
|
+
export function describeSecurityGroups(params: {
|
|
55
|
+
profile?: string;
|
|
56
|
+
region: string;
|
|
57
|
+
groupIds: string[];
|
|
58
|
+
verbose?: boolean;
|
|
59
|
+
}): SecurityGroup[];
|
|
60
|
+
/**
|
|
61
|
+
* Load full details for a single security group.
|
|
62
|
+
* @param {{ profile?: string; region: string; groupId: string; verbose?: boolean }} params
|
|
63
|
+
* @returns {any | null}
|
|
64
|
+
*/
|
|
65
|
+
export function getSecurityGroupDetails(params: {
|
|
66
|
+
profile?: string;
|
|
67
|
+
region: string;
|
|
68
|
+
groupId: string;
|
|
69
|
+
verbose?: boolean;
|
|
70
|
+
}): any | null;
|
|
71
|
+
/**
|
|
72
|
+
* Extract single-host SSH ingress rules (port 22, /32) from a security group.
|
|
73
|
+
* @param {any} securityGroup
|
|
74
|
+
* @returns {SshIngressRule[]}
|
|
75
|
+
*/
|
|
76
|
+
export function listSshIngressRules(securityGroup: any): SshIngressRule[];
|
|
77
|
+
/**
|
|
78
|
+
* Derive a first name from the IAM ARN or profile (md-{name}-...).
|
|
79
|
+
* @param {{ profile?: string; identity: { Arn?: string } }} params
|
|
80
|
+
* @returns {string}
|
|
81
|
+
*/
|
|
82
|
+
export function extractRoamingFirstName(params: {
|
|
83
|
+
profile?: string;
|
|
84
|
+
identity: {
|
|
85
|
+
Arn?: string;
|
|
86
|
+
};
|
|
87
|
+
}): string;
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} firstName
|
|
90
|
+
* @returns {string}
|
|
91
|
+
*/
|
|
92
|
+
export function roamingRuleDescription(firstName: string): string;
|
|
93
|
+
/**
|
|
94
|
+
* Find an existing rule whose description matches the roaming rule name.
|
|
95
|
+
* @param {SshIngressRule[]} sshRules
|
|
96
|
+
* @param {string} ruleDescription
|
|
97
|
+
* @returns {SshIngressRule | null}
|
|
98
|
+
*/
|
|
99
|
+
export function resolveRoamingRule(sshRules: SshIngressRule[], ruleDescription: string): SshIngressRule | null;
|
|
100
|
+
/**
|
|
101
|
+
* Find an existing rule that already allows the given CIDR.
|
|
102
|
+
* @param {SshIngressRule[]} sshRules
|
|
103
|
+
* @param {string} newCidr
|
|
104
|
+
* @returns {SshIngressRule | null}
|
|
105
|
+
*/
|
|
106
|
+
export function ruleAlreadyAllowsIp(sshRules: SshIngressRule[], newCidr: string): SshIngressRule | null;
|
|
107
|
+
/**
|
|
108
|
+
* Authorize a new SSH ingress rule.
|
|
109
|
+
* @param {{
|
|
110
|
+
* profile?: string;
|
|
111
|
+
* region: string;
|
|
112
|
+
* groupId: string;
|
|
113
|
+
* cidrIp: string;
|
|
114
|
+
* description: string;
|
|
115
|
+
* fromPort?: number;
|
|
116
|
+
* toPort?: number;
|
|
117
|
+
* ipProtocol?: string;
|
|
118
|
+
* verbose?: boolean;
|
|
119
|
+
* }} params
|
|
120
|
+
*/
|
|
121
|
+
export function authorizeRule(params: {
|
|
122
|
+
profile?: string;
|
|
123
|
+
region: string;
|
|
124
|
+
groupId: string;
|
|
125
|
+
cidrIp: string;
|
|
126
|
+
description: string;
|
|
127
|
+
fromPort?: number;
|
|
128
|
+
toPort?: number;
|
|
129
|
+
ipProtocol?: string;
|
|
130
|
+
verbose?: boolean;
|
|
131
|
+
}): void;
|
|
132
|
+
/**
|
|
133
|
+
* Revoke an existing SSH ingress rule.
|
|
134
|
+
* @param {{
|
|
135
|
+
* profile?: string;
|
|
136
|
+
* region: string;
|
|
137
|
+
* groupId: string;
|
|
138
|
+
* rule: SshIngressRule;
|
|
139
|
+
* verbose?: boolean;
|
|
140
|
+
* }} params
|
|
141
|
+
*/
|
|
142
|
+
export function revokeRule(params: {
|
|
143
|
+
profile?: string;
|
|
144
|
+
region: string;
|
|
145
|
+
groupId: string;
|
|
146
|
+
rule: SshIngressRule;
|
|
147
|
+
verbose?: boolean;
|
|
148
|
+
}): void;
|
|
149
|
+
/**
|
|
150
|
+
* @param {Ec2Instance} instance
|
|
151
|
+
* @returns {string}
|
|
152
|
+
*/
|
|
153
|
+
export function instanceLabel(instance: Ec2Instance): string;
|
|
154
|
+
/**
|
|
155
|
+
* @param {SecurityGroup} group
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
export function securityGroupLabel(group: SecurityGroup): string;
|
|
159
|
+
/**
|
|
160
|
+
* @param {SshIngressRule} rule
|
|
161
|
+
* @returns {string}
|
|
162
|
+
*/
|
|
163
|
+
export function sshRuleLabel(rule: SshIngressRule): string;
|
|
164
|
+
/**
|
|
165
|
+
* Print single-host SSH rules using the given logger.
|
|
166
|
+
* @param {SshIngressRule[]} sshRules
|
|
167
|
+
* @param {(message: string) => void} [log]
|
|
168
|
+
*/
|
|
169
|
+
export function printSshRules(sshRules: SshIngressRule[], log?: (message: string) => void): void;
|
|
170
|
+
export type Ec2Instance = {
|
|
171
|
+
instanceId: string;
|
|
172
|
+
name: string;
|
|
173
|
+
state: string;
|
|
174
|
+
instanceType: string;
|
|
175
|
+
publicIp: string | null;
|
|
176
|
+
securityGroupIds: string[];
|
|
177
|
+
};
|
|
178
|
+
export type SecurityGroup = {
|
|
179
|
+
groupId: string;
|
|
180
|
+
groupName: string;
|
|
181
|
+
description: string;
|
|
182
|
+
};
|
|
183
|
+
export type SshIngressRule = {
|
|
184
|
+
cidrIp: string;
|
|
185
|
+
description: string;
|
|
186
|
+
fromPort: number;
|
|
187
|
+
toPort: number;
|
|
188
|
+
ipProtocol: string;
|
|
189
|
+
};
|