@mtndev/cli 0.0.0-dev-20260626045221 → 0.0.0-dev-20260914234436
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 +84 -3
- package/cjs/__chunks/2FSYDFUV.cjs +146 -0
- package/cjs/__chunks/3A5ZYU5M.cjs +105 -0
- package/cjs/__chunks/3NOHGUCF.cjs +9 -0
- package/cjs/__chunks/3T2LXW7J.cjs +387 -0
- package/cjs/__chunks/4XT5JEAS.cjs +1 -0
- package/cjs/__chunks/AZEB4H4I.cjs +208 -0
- package/cjs/__chunks/B5ENMOCD.cjs +288 -0
- package/cjs/__chunks/B6XM5KSF.cjs +338 -0
- package/cjs/__chunks/BDU2UCE5.cjs +1 -0
- package/cjs/__chunks/BLMSMR5E.cjs +46 -0
- package/cjs/__chunks/CMUD7LLN.cjs +47 -0
- package/cjs/__chunks/DTQ2LB3I.cjs +49 -0
- package/cjs/__chunks/FM5MVRQF.cjs +23 -0
- package/cjs/__chunks/FRHST54J.cjs +135 -0
- package/cjs/__chunks/FU4VP2LN.cjs +1 -0
- package/cjs/__chunks/G5K4PVIL.cjs +26 -0
- package/cjs/__chunks/{IEGW3TSF.cjs → GU3R54MK.cjs} +45 -18
- package/cjs/__chunks/GUL36IFZ.cjs +61 -0
- package/cjs/__chunks/GZMTNK5N.cjs +1 -0
- package/cjs/__chunks/H74JSDBU.cjs +30 -0
- package/cjs/__chunks/{EMMC33QA.cjs → JB7T7PKQ.cjs} +170 -33
- package/cjs/__chunks/JSL2JDMW.cjs +1 -0
- package/cjs/__chunks/K4QNHRTP.cjs +163 -0
- package/cjs/__chunks/KU76LC42.cjs +192 -0
- package/cjs/__chunks/LFDSHANP.cjs +34 -0
- package/cjs/__chunks/LHE24ECK.cjs +192 -0
- package/cjs/__chunks/LIKQ3SI4.cjs +1 -0
- package/cjs/__chunks/LXDF5QVZ.cjs +313 -0
- package/cjs/__chunks/LXOOJR2R.cjs +74 -0
- package/cjs/__chunks/N5MXFOQZ.cjs +167 -0
- package/cjs/__chunks/N7D56LPP.cjs +25 -0
- package/cjs/__chunks/NFXQNOQH.cjs +393 -0
- package/cjs/__chunks/NLKJIUEX.cjs +126 -0
- package/cjs/__chunks/NT2XXAI6.cjs +89 -0
- package/cjs/__chunks/NWZGFHKH.cjs +121 -0
- package/cjs/__chunks/R3GUC7O7.cjs +110 -0
- package/cjs/__chunks/RPVAXPJ4.cjs +1 -0
- package/cjs/__chunks/S3LVCWPN.cjs +23 -0
- package/cjs/__chunks/SBBNW2BF.cjs +249 -0
- package/cjs/__chunks/T2A7TN4F.cjs +81 -0
- package/cjs/__chunks/TKNNDDIB.cjs +97 -0
- package/cjs/__chunks/TNSRDJPR.cjs +1 -0
- package/cjs/__chunks/UWHPE2GI.cjs +20 -0
- package/cjs/__chunks/W6K7BV74.cjs +1 -0
- package/cjs/__chunks/WBOQ6AUN.cjs +320 -0
- package/cjs/__chunks/WHGN5PZI.cjs +299 -0
- package/cjs/__chunks/WOYF2L6T.cjs +11 -0
- package/cjs/__chunks/X26XY2N6.cjs +18 -0
- package/cjs/__chunks/XMKOKRXZ.cjs +16 -0
- package/cjs/__chunks/XTJPQ3EX.cjs +1 -0
- package/cjs/__chunks/YRHFKTW2.cjs +63 -0
- package/cjs/commands/aws/ec2/allow-ip/handler.cjs +12 -0
- package/cjs/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
- package/cjs/commands/aws/ec2/allow-ip/index.cjs +55 -0
- package/cjs/commands/aws/ec2/allow-ip/index.d.ts +3 -0
- package/cjs/commands/aws/ec2/allow-ip/schema.cjs +13 -0
- package/cjs/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
- package/cjs/commands/aws/ec2/allow-ip/utils.cjs +39 -0
- package/cjs/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
- package/cjs/commands/aws/iam/admin-group.cjs +15 -0
- package/cjs/commands/aws/iam/admin-group.d.ts +55 -0
- package/cjs/commands/aws/iam/break-glass/handler.cjs +12 -0
- package/cjs/commands/aws/iam/break-glass/handler.d.ts +7 -0
- package/cjs/commands/aws/iam/break-glass/index.cjs +19 -0
- package/cjs/commands/aws/iam/break-glass/index.d.ts +2 -0
- package/cjs/commands/aws/iam/break-glass/schema.cjs +12 -0
- package/cjs/commands/aws/iam/break-glass/schema.d.ts +25 -0
- package/cjs/commands/aws/iam/check-admin/handler.cjs +10 -0
- package/cjs/commands/aws/iam/check-admin/handler.d.ts +5 -0
- package/cjs/commands/aws/iam/check-admin/index.cjs +17 -0
- package/cjs/commands/aws/iam/check-admin/index.d.ts +2 -0
- package/cjs/commands/aws/iam/check-admin/schema.cjs +12 -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.cjs +28 -0
- package/cjs/commands/aws/iam/policies/sts-federation.cjs +19 -0
- package/cjs/commands/aws/iam/policies/user-admins-deny-list.cjs +183 -0
- package/cjs/commands/aws/iam/policies/user-admins-deny-regions-list.cjs +49 -0
- package/cjs/commands/aws/iam/policies/user-admins-self-service-iam.cjs +64 -0
- package/cjs/commands/aws/iam/policies/user-admins-write-list.cjs +125 -0
- package/cjs/commands/aws/iam/remove-admin/handler.cjs +11 -0
- package/cjs/commands/aws/iam/remove-admin/handler.d.ts +6 -0
- package/cjs/commands/aws/iam/remove-admin/index.cjs +18 -0
- package/cjs/commands/aws/iam/remove-admin/index.d.ts +2 -0
- package/cjs/commands/aws/iam/remove-admin/schema.cjs +12 -0
- package/cjs/commands/aws/iam/remove-admin/schema.d.ts +28 -0
- package/cjs/commands/aws/iam/setup-acl/handler.cjs +14 -0
- package/cjs/commands/aws/iam/setup-acl/handler.d.ts +6 -0
- package/cjs/commands/aws/iam/setup-acl/index.cjs +33 -0
- package/cjs/commands/aws/iam/setup-acl/index.d.ts +3 -0
- package/cjs/commands/aws/iam/setup-acl/schema.cjs +7 -0
- package/cjs/commands/aws/iam/setup-acl/schema.d.ts +24 -0
- package/cjs/commands/aws/iam/setup-acl/utils.cjs +20 -0
- package/cjs/commands/aws/iam/setup-acl/utils.d.ts +61 -0
- package/cjs/commands/aws/iam/users/handler.cjs +12 -0
- package/cjs/commands/aws/iam/users/handler.d.ts +5 -0
- package/cjs/commands/aws/iam/users/index.cjs +41 -0
- package/cjs/commands/aws/iam/users/index.d.ts +3 -0
- package/cjs/commands/aws/iam/users/schema.cjs +7 -0
- package/cjs/commands/aws/iam/users/schema.d.ts +18 -0
- package/cjs/commands/aws/iam/users/utils.cjs +30 -0
- package/cjs/commands/aws/iam/users/utils.d.ts +119 -0
- package/cjs/commands/aws/index.cjs +229 -0
- package/cjs/commands/aws/index.d.ts +14 -0
- package/cjs/commands/aws/route53/add-record/handler.cjs +18 -0
- package/cjs/commands/aws/route53/add-record/handler.d.ts +36 -0
- package/cjs/commands/aws/route53/add-record/index.cjs +57 -0
- package/cjs/commands/aws/route53/add-record/index.d.ts +3 -0
- package/cjs/commands/aws/route53/add-record/schema.cjs +11 -0
- package/cjs/commands/aws/route53/add-record/schema.d.ts +45 -0
- package/cjs/commands/aws/route53/add-record/utils.cjs +37 -0
- package/cjs/commands/aws/route53/add-record/utils.d.ts +216 -0
- package/cjs/commands/aws/route53/remove-record/handler.cjs +14 -0
- package/cjs/commands/aws/route53/remove-record/handler.d.ts +6 -0
- package/cjs/commands/aws/route53/remove-record/index.cjs +18 -0
- package/cjs/commands/aws/route53/remove-record/index.d.ts +2 -0
- package/cjs/commands/aws/route53/remove-record/schema.cjs +8 -0
- package/cjs/commands/aws/route53/remove-record/schema.d.ts +30 -0
- package/cjs/commands/aws/schema.cjs +10 -0
- package/cjs/commands/aws/schema.d.ts +13 -0
- package/cjs/commands/aws/ses/send-email/handler.cjs +12 -0
- package/cjs/commands/aws/ses/send-email/handler.d.ts +5 -0
- package/cjs/commands/aws/ses/send-email/index.cjs +23 -0
- package/cjs/commands/aws/ses/send-email/index.d.ts +3 -0
- package/cjs/commands/aws/ses/send-email/schema.cjs +7 -0
- package/cjs/commands/aws/ses/send-email/schema.d.ts +39 -0
- package/cjs/commands/aws/ses/send-email/utils.cjs +11 -0
- package/cjs/commands/aws/ses/send-email/utils.d.ts +47 -0
- package/cjs/commands/aws/ses/smtp-password.cjs +10 -0
- package/cjs/commands/aws/ses/smtp-password.d.ts +21 -0
- package/cjs/commands/aws/utils/exec-aws.cjs +16 -0
- package/cjs/commands/aws/utils/exec-aws.d.ts +79 -0
- package/cjs/commands/aws/utils/resolve-policy-path.cjs +6 -0
- package/cjs/commands/aws/utils/resolve-policy-path.d.ts +8 -0
- package/cjs/commands/aws/utils/resolve-profile.cjs +11 -0
- package/cjs/commands/aws/utils/resolve-profile.d.ts +28 -0
- package/cjs/commands/aws/utils/tty.cjs +10 -0
- package/cjs/commands/aws/utils/tty.d.ts +24 -0
- package/cjs/commands/db/create-readonly-user/handler.cjs +11 -0
- package/cjs/commands/db/create-readonly-user/handler.d.ts +5 -0
- package/cjs/commands/db/create-readonly-user/index.cjs +40 -0
- package/cjs/commands/db/create-readonly-user/index.d.ts +3 -0
- package/cjs/commands/db/create-readonly-user/mysql.cjs +9 -0
- package/cjs/commands/db/create-readonly-user/mysql.d.ts +33 -0
- package/cjs/commands/db/create-readonly-user/postgres.cjs +9 -0
- package/cjs/commands/db/create-readonly-user/postgres.d.ts +31 -0
- package/cjs/commands/db/create-readonly-user/schema.cjs +12 -0
- package/cjs/commands/db/create-readonly-user/schema.d.ts +55 -0
- package/cjs/commands/db/create-readonly-user/utils.cjs +22 -0
- package/cjs/commands/db/create-readonly-user/utils.d.ts +112 -0
- package/cjs/commands/db/index.cjs +44 -0
- package/cjs/commands/db/index.d.ts +6 -0
- package/cjs/commands/doppler/handler.cjs +6 -4
- package/cjs/commands/doppler/index.cjs +32 -5
- package/cjs/commands/doppler/index.d.ts +1 -0
- package/cjs/commands/doppler/schema.cjs +10 -2
- package/cjs/commands/doppler/schema.d.ts +233 -17
- package/cjs/commands/doppler/set-secret.cjs +19 -0
- package/cjs/commands/doppler/set-secret.d.ts +113 -0
- package/cjs/commands/doppler/utils.cjs +6 -2
- package/cjs/commands/doppler/utils.d.ts +32 -8
- package/cjs/commands/icons/schema.d.ts +1 -1
- package/cjs/commands/index.cjs +91 -12
- package/esm/__chunks/2FTTEFEK.js +97 -0
- package/esm/__chunks/33UYMUQD.js +338 -0
- package/esm/__chunks/3BKWTHLJ.js +25 -0
- package/esm/__chunks/3HE4BEJX.js +105 -0
- package/esm/__chunks/3QLI5BDL.js +387 -0
- package/esm/__chunks/5GIUAFXL.js +89 -0
- package/esm/__chunks/73BJG53F.js +11 -0
- package/esm/__chunks/7VF4Q43Q.js +0 -0
- package/esm/__chunks/AEDEAYF5.js +0 -0
- package/esm/__chunks/AZCP3SFJ.js +0 -0
- package/esm/__chunks/B2DVQ2UL.js +61 -0
- package/esm/__chunks/CVQVHLW5.js +0 -0
- package/esm/__chunks/D62SPZXC.js +0 -0
- package/esm/__chunks/DLEJ25GZ.js +121 -0
- package/esm/__chunks/DMYFNF54.js +192 -0
- package/esm/__chunks/DNFTUMGD.js +23 -0
- package/esm/__chunks/DVKD6YSR.js +49 -0
- package/esm/__chunks/EMSH4P76.js +20 -0
- package/esm/__chunks/FFKFVMIE.js +167 -0
- package/esm/__chunks/FNXUONAV.js +110 -0
- package/esm/__chunks/G4NJHFFU.js +320 -0
- package/esm/__chunks/IEOGAQYI.js +46 -0
- package/esm/__chunks/IZFGBYGE.js +34 -0
- package/esm/__chunks/J34UVKLA.js +26 -0
- package/esm/__chunks/JZPBBBIG.js +74 -0
- package/esm/__chunks/KWUJP5MG.js +135 -0
- package/esm/__chunks/M35URMT4.js +313 -0
- package/esm/__chunks/NF3M2L6C.js +81 -0
- package/esm/__chunks/PNPPDYKC.js +299 -0
- package/esm/__chunks/QFXN4EHM.js +393 -0
- package/esm/__chunks/QKDDLIWD.js +146 -0
- package/esm/__chunks/QSE66AKY.js +208 -0
- package/esm/__chunks/QUF7PS4R.js +18 -0
- package/esm/__chunks/S7IJPJ4R.js +126 -0
- package/esm/__chunks/SD3E3EPH.js +163 -0
- package/esm/__chunks/T3TTUHTW.js +30 -0
- package/esm/__chunks/TIOVE6HR.js +0 -0
- package/esm/__chunks/TXKMEIDQ.js +0 -0
- package/esm/__chunks/U7HAE2RF.js +288 -0
- package/esm/__chunks/UXWY7BYE.js +63 -0
- package/esm/__chunks/VKAKISRG.js +192 -0
- package/esm/__chunks/WD2YR6I7.js +0 -0
- package/esm/__chunks/WOZFZKFY.js +0 -0
- package/esm/__chunks/XI7GZDE2.js +0 -0
- package/esm/__chunks/XOUWDB3T.js +249 -0
- package/esm/__chunks/XQN2TA4S.js +23 -0
- package/esm/__chunks/XXK32TIM.js +16 -0
- package/esm/__chunks/{O262GK4B.js → YGWTVPZE.js} +164 -27
- package/esm/__chunks/{XK7EMDSU.js → YRG7PT33.js} +44 -17
- package/esm/__chunks/ZHKVJO2U.js +47 -0
- package/esm/__chunks/ZO55YUSY.js +9 -0
- package/esm/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
- package/esm/commands/aws/ec2/allow-ip/handler.js +12 -0
- package/esm/commands/aws/ec2/allow-ip/index.d.ts +3 -0
- package/esm/commands/aws/ec2/allow-ip/index.js +55 -0
- package/esm/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
- package/esm/commands/aws/ec2/allow-ip/schema.js +13 -0
- package/esm/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
- package/esm/commands/aws/ec2/allow-ip/utils.js +39 -0
- package/esm/commands/aws/iam/admin-group.d.ts +55 -0
- package/esm/commands/aws/iam/admin-group.js +15 -0
- package/esm/commands/aws/iam/break-glass/handler.d.ts +7 -0
- package/esm/commands/aws/iam/break-glass/handler.js +12 -0
- package/esm/commands/aws/iam/break-glass/index.d.ts +2 -0
- package/esm/commands/aws/iam/break-glass/index.js +19 -0
- package/esm/commands/aws/iam/break-glass/schema.d.ts +25 -0
- package/esm/commands/aws/iam/break-glass/schema.js +12 -0
- package/esm/commands/aws/iam/check-admin/handler.d.ts +5 -0
- package/esm/commands/aws/iam/check-admin/handler.js +10 -0
- package/esm/commands/aws/iam/check-admin/index.d.ts +2 -0
- package/esm/commands/aws/iam/check-admin/index.js +17 -0
- package/esm/commands/aws/iam/check-admin/schema.d.ts +19 -0
- package/esm/commands/aws/iam/check-admin/schema.js +12 -0
- package/esm/commands/aws/iam/policies/s3-restrictions-deny-non-s3-services.js +28 -0
- package/esm/commands/aws/iam/policies/sts-federation.js +19 -0
- package/esm/commands/aws/iam/policies/user-admins-deny-list.js +183 -0
- package/esm/commands/aws/iam/policies/user-admins-deny-regions-list.js +49 -0
- package/esm/commands/aws/iam/policies/user-admins-self-service-iam.js +64 -0
- package/esm/commands/aws/iam/policies/user-admins-write-list.js +125 -0
- package/esm/commands/aws/iam/remove-admin/handler.d.ts +6 -0
- package/esm/commands/aws/iam/remove-admin/handler.js +11 -0
- package/esm/commands/aws/iam/remove-admin/index.d.ts +2 -0
- package/esm/commands/aws/iam/remove-admin/index.js +18 -0
- package/esm/commands/aws/iam/remove-admin/schema.d.ts +28 -0
- package/esm/commands/aws/iam/remove-admin/schema.js +12 -0
- package/esm/commands/aws/iam/setup-acl/handler.d.ts +6 -0
- package/esm/commands/aws/iam/setup-acl/handler.js +14 -0
- package/esm/commands/aws/iam/setup-acl/index.d.ts +3 -0
- package/esm/commands/aws/iam/setup-acl/index.js +33 -0
- package/esm/commands/aws/iam/setup-acl/schema.d.ts +24 -0
- package/esm/commands/aws/iam/setup-acl/schema.js +7 -0
- package/esm/commands/aws/iam/setup-acl/utils.d.ts +61 -0
- package/esm/commands/aws/iam/setup-acl/utils.js +20 -0
- package/esm/commands/aws/iam/users/handler.d.ts +5 -0
- package/esm/commands/aws/iam/users/handler.js +12 -0
- package/esm/commands/aws/iam/users/index.d.ts +3 -0
- package/esm/commands/aws/iam/users/index.js +41 -0
- package/esm/commands/aws/iam/users/schema.d.ts +18 -0
- package/esm/commands/aws/iam/users/schema.js +7 -0
- package/esm/commands/aws/iam/users/utils.d.ts +119 -0
- package/esm/commands/aws/iam/users/utils.js +30 -0
- package/esm/commands/aws/index.d.ts +14 -0
- package/esm/commands/aws/index.js +229 -0
- package/esm/commands/aws/route53/add-record/handler.d.ts +36 -0
- package/esm/commands/aws/route53/add-record/handler.js +18 -0
- package/esm/commands/aws/route53/add-record/index.d.ts +3 -0
- package/esm/commands/aws/route53/add-record/index.js +57 -0
- package/esm/commands/aws/route53/add-record/schema.d.ts +45 -0
- package/esm/commands/aws/route53/add-record/schema.js +11 -0
- package/esm/commands/aws/route53/add-record/utils.d.ts +216 -0
- package/esm/commands/aws/route53/add-record/utils.js +37 -0
- package/esm/commands/aws/route53/remove-record/handler.d.ts +6 -0
- package/esm/commands/aws/route53/remove-record/handler.js +14 -0
- package/esm/commands/aws/route53/remove-record/index.d.ts +2 -0
- package/esm/commands/aws/route53/remove-record/index.js +18 -0
- package/esm/commands/aws/route53/remove-record/schema.d.ts +30 -0
- package/esm/commands/aws/route53/remove-record/schema.js +8 -0
- package/esm/commands/aws/schema.d.ts +13 -0
- package/esm/commands/aws/schema.js +10 -0
- package/esm/commands/aws/ses/send-email/handler.d.ts +5 -0
- package/esm/commands/aws/ses/send-email/handler.js +12 -0
- package/esm/commands/aws/ses/send-email/index.d.ts +3 -0
- package/esm/commands/aws/ses/send-email/index.js +23 -0
- package/esm/commands/aws/ses/send-email/schema.d.ts +39 -0
- package/esm/commands/aws/ses/send-email/schema.js +7 -0
- package/esm/commands/aws/ses/send-email/utils.d.ts +47 -0
- package/esm/commands/aws/ses/send-email/utils.js +11 -0
- package/esm/commands/aws/ses/smtp-password.d.ts +21 -0
- package/esm/commands/aws/ses/smtp-password.js +10 -0
- package/esm/commands/aws/utils/exec-aws.d.ts +79 -0
- package/esm/commands/aws/utils/exec-aws.js +16 -0
- package/esm/commands/aws/utils/resolve-policy-path.d.ts +8 -0
- package/esm/commands/aws/utils/resolve-policy-path.js +6 -0
- package/esm/commands/aws/utils/resolve-profile.d.ts +28 -0
- package/esm/commands/aws/utils/resolve-profile.js +11 -0
- package/esm/commands/aws/utils/tty.d.ts +24 -0
- package/esm/commands/aws/utils/tty.js +10 -0
- package/esm/commands/db/create-readonly-user/handler.d.ts +5 -0
- package/esm/commands/db/create-readonly-user/handler.js +11 -0
- package/esm/commands/db/create-readonly-user/index.d.ts +3 -0
- package/esm/commands/db/create-readonly-user/index.js +40 -0
- package/esm/commands/db/create-readonly-user/mysql.d.ts +33 -0
- package/esm/commands/db/create-readonly-user/mysql.js +9 -0
- package/esm/commands/db/create-readonly-user/postgres.d.ts +31 -0
- package/esm/commands/db/create-readonly-user/postgres.js +9 -0
- package/esm/commands/db/create-readonly-user/schema.d.ts +55 -0
- package/esm/commands/db/create-readonly-user/schema.js +12 -0
- package/esm/commands/db/create-readonly-user/utils.d.ts +112 -0
- package/esm/commands/db/create-readonly-user/utils.js +22 -0
- package/esm/commands/db/index.d.ts +6 -0
- package/esm/commands/db/index.js +44 -0
- package/esm/commands/doppler/handler.js +5 -3
- package/esm/commands/doppler/index.d.ts +1 -0
- package/esm/commands/doppler/index.js +32 -5
- package/esm/commands/doppler/schema.d.ts +233 -17
- package/esm/commands/doppler/schema.js +11 -3
- package/esm/commands/doppler/set-secret.d.ts +113 -0
- package/esm/commands/doppler/set-secret.js +19 -0
- package/esm/commands/doppler/utils.d.ts +32 -8
- package/esm/commands/doppler/utils.js +5 -1
- package/esm/commands/icons/schema.d.ts +1 -1
- package/esm/commands/index.js +86 -7
- package/package.json +12 -2
- package/vitest.config.ts +11 -0
- package/cjs/__chunks/MFAORPG6.cjs +0 -52
- package/esm/__chunks/3KUXVWUE.js +0 -52
- /package/cjs/__chunks/{WNJ3ZYLI.cjs → 3BY5Y4W7.cjs} +0 -0
- /package/esm/__chunks/{46HGXFME.js → 5ZBVGSOK.js} +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyMysqlReadonly,
|
|
3
|
+
listMysqlDatabases
|
|
4
|
+
} from "./3HE4BEJX.js";
|
|
5
|
+
import {
|
|
6
|
+
applyPostgresReadonly,
|
|
7
|
+
listPostgresDatabases
|
|
8
|
+
} from "./SD3E3EPH.js";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_MYSQL_HOST_PATTERN,
|
|
11
|
+
DEFAULT_READONLY_USERNAME,
|
|
12
|
+
ENGINES,
|
|
13
|
+
createReadonlyUserOptionsSchema
|
|
14
|
+
} from "./T3TTUHTW.js";
|
|
15
|
+
import {
|
|
16
|
+
buildReadonlyUrl,
|
|
17
|
+
detectEngineFromUrl,
|
|
18
|
+
generatePassword,
|
|
19
|
+
parseConnectionUrl,
|
|
20
|
+
resolveAdminUrl
|
|
21
|
+
} from "./DLEJ25GZ.js";
|
|
22
|
+
import {
|
|
23
|
+
prompt,
|
|
24
|
+
promptChoice,
|
|
25
|
+
promptYesNo
|
|
26
|
+
} from "./ZHKVJO2U.js";
|
|
27
|
+
|
|
28
|
+
// src/commands/db/create-readonly-user/handler.js
|
|
29
|
+
import chalk from "chalk";
|
|
30
|
+
import { ZodError } from "zod";
|
|
31
|
+
async function resolveEngine(engineOption, url) {
|
|
32
|
+
if (engineOption) {
|
|
33
|
+
return engineOption;
|
|
34
|
+
}
|
|
35
|
+
if (url) {
|
|
36
|
+
const detected = detectEngineFromUrl(url);
|
|
37
|
+
if (detected) {
|
|
38
|
+
return detected;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const index = await promptChoice({
|
|
42
|
+
message: "Choose a database engine:",
|
|
43
|
+
options: ["postgres", "mysql"]
|
|
44
|
+
});
|
|
45
|
+
const chosen = ENGINES[index];
|
|
46
|
+
if (!chosen) {
|
|
47
|
+
throw new Error("No engine selected");
|
|
48
|
+
}
|
|
49
|
+
return chosen;
|
|
50
|
+
}
|
|
51
|
+
async function resolveUrl(params) {
|
|
52
|
+
const { urlOption, engine } = params;
|
|
53
|
+
const fromEnv = resolveAdminUrl({ url: urlOption, engine });
|
|
54
|
+
if (fromEnv) {
|
|
55
|
+
return fromEnv;
|
|
56
|
+
}
|
|
57
|
+
const example = engine === "postgres" ? "postgresql://admin:pass@host:5432/postgres" : "mysql://admin:pass@host:3306/";
|
|
58
|
+
const raw = await prompt(` Admin connection URL (e.g. ${example}): `);
|
|
59
|
+
if (!raw) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"Admin connection URL is required (pass --url or set DATABASE_URL)"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return raw;
|
|
65
|
+
}
|
|
66
|
+
async function resolveScope(params) {
|
|
67
|
+
const { databaseOption, databases } = params;
|
|
68
|
+
if (databaseOption) {
|
|
69
|
+
const normalized = databaseOption.trim().toLowerCase();
|
|
70
|
+
if (normalized === "all" || normalized === "*") {
|
|
71
|
+
return { mode: "all", databases };
|
|
72
|
+
}
|
|
73
|
+
if (!databases.includes(databaseOption)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Database "${databaseOption}" not found. Available: ${databases.join(", ") || "(none)"}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return { mode: "one", databases: [databaseOption] };
|
|
79
|
+
}
|
|
80
|
+
if (databases.length === 0) {
|
|
81
|
+
throw new Error("No user databases found to grant access on.");
|
|
82
|
+
}
|
|
83
|
+
const options = [
|
|
84
|
+
...databases.map((name) => name),
|
|
85
|
+
`all (${databases.length} databases)`
|
|
86
|
+
];
|
|
87
|
+
const index = await promptChoice({
|
|
88
|
+
message: "Grant read-only on which database?",
|
|
89
|
+
options
|
|
90
|
+
});
|
|
91
|
+
if (index === options.length - 1) {
|
|
92
|
+
return { mode: "all", databases };
|
|
93
|
+
}
|
|
94
|
+
const chosen = databases[index];
|
|
95
|
+
if (!chosen) {
|
|
96
|
+
throw new Error("No database selected");
|
|
97
|
+
}
|
|
98
|
+
return { mode: "one", databases: [chosen] };
|
|
99
|
+
}
|
|
100
|
+
async function resolveUsername(usernameOption) {
|
|
101
|
+
if (usernameOption) {
|
|
102
|
+
return usernameOption;
|
|
103
|
+
}
|
|
104
|
+
const raw = await prompt(
|
|
105
|
+
` Read-only username [default: ${DEFAULT_READONLY_USERNAME}]: `
|
|
106
|
+
);
|
|
107
|
+
const username = raw || DEFAULT_READONLY_USERNAME;
|
|
108
|
+
if (!/^[A-Za-z0-9_]+$/.test(username)) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"Username must contain only letters, numbers, and underscores"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return username;
|
|
114
|
+
}
|
|
115
|
+
async function resolvePassword(passwordOption) {
|
|
116
|
+
if (passwordOption) {
|
|
117
|
+
return { password: passwordOption, generated: false };
|
|
118
|
+
}
|
|
119
|
+
const generate = await promptYesNo({
|
|
120
|
+
message: " Generate a random password?",
|
|
121
|
+
defaultYes: true
|
|
122
|
+
});
|
|
123
|
+
if (generate) {
|
|
124
|
+
return { password: generatePassword(), generated: true };
|
|
125
|
+
}
|
|
126
|
+
const password = await prompt(" Password: ");
|
|
127
|
+
if (!password) {
|
|
128
|
+
throw new Error("Password is required");
|
|
129
|
+
}
|
|
130
|
+
return { password, generated: false };
|
|
131
|
+
}
|
|
132
|
+
function redactUrl(url) {
|
|
133
|
+
try {
|
|
134
|
+
const parsed = new URL(url);
|
|
135
|
+
if (parsed.password) {
|
|
136
|
+
parsed.password = "***";
|
|
137
|
+
}
|
|
138
|
+
return parsed.toString();
|
|
139
|
+
} catch {
|
|
140
|
+
return url;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function createReadonlyUserCommand(options) {
|
|
144
|
+
try {
|
|
145
|
+
const {
|
|
146
|
+
engine: engineOption,
|
|
147
|
+
url: urlOption,
|
|
148
|
+
database: databaseOption,
|
|
149
|
+
username: usernameOption,
|
|
150
|
+
password: passwordOption,
|
|
151
|
+
hostPattern: hostPatternOption,
|
|
152
|
+
ssl: sslOption,
|
|
153
|
+
yes,
|
|
154
|
+
dryRun,
|
|
155
|
+
verbose
|
|
156
|
+
} = createReadonlyUserOptionsSchema.parse(options);
|
|
157
|
+
console.log(chalk.blue("\nCreate read-only database user"));
|
|
158
|
+
console.log("");
|
|
159
|
+
const urlForDetect = resolveAdminUrl({
|
|
160
|
+
url: urlOption,
|
|
161
|
+
engine: engineOption
|
|
162
|
+
});
|
|
163
|
+
const engine = await resolveEngine(engineOption, urlForDetect);
|
|
164
|
+
console.log(`Engine: ${engine}`);
|
|
165
|
+
const url = await resolveUrl({ urlOption, engine });
|
|
166
|
+
const config = parseConnectionUrl({
|
|
167
|
+
url,
|
|
168
|
+
engine,
|
|
169
|
+
sslOption
|
|
170
|
+
});
|
|
171
|
+
console.log(
|
|
172
|
+
`Admin: ${config.user}@${config.host}:${config.port}` + (config.database ? `/${config.database}` : "")
|
|
173
|
+
);
|
|
174
|
+
console.log("");
|
|
175
|
+
const listed = engine === "postgres" ? await listPostgresDatabases(config) : await listMysqlDatabases(config);
|
|
176
|
+
if (verbose) {
|
|
177
|
+
console.log(`Found databases: ${listed.join(", ") || "(none)"}`);
|
|
178
|
+
console.log("");
|
|
179
|
+
}
|
|
180
|
+
const scope = await resolveScope({
|
|
181
|
+
databaseOption,
|
|
182
|
+
databases: listed
|
|
183
|
+
});
|
|
184
|
+
console.log(
|
|
185
|
+
scope.mode === "all" ? `Scope: all (${scope.databases.length} databases)` : `Scope: ${scope.databases[0]}`
|
|
186
|
+
);
|
|
187
|
+
console.log("");
|
|
188
|
+
const username = await resolveUsername(usernameOption);
|
|
189
|
+
const { password, generated } = await resolvePassword(passwordOption);
|
|
190
|
+
const hostPattern = hostPatternOption ?? DEFAULT_MYSQL_HOST_PATTERN;
|
|
191
|
+
console.log("");
|
|
192
|
+
console.log("Plan:");
|
|
193
|
+
console.log(` User: ${username}`);
|
|
194
|
+
console.log(
|
|
195
|
+
` Password: ${generated ? "(generated)" : "(provided)"}${dryRun ? " [dry-run]" : ""}`
|
|
196
|
+
);
|
|
197
|
+
if (engine === "mysql") {
|
|
198
|
+
console.log(` Host: ${hostPattern}`);
|
|
199
|
+
}
|
|
200
|
+
console.log(
|
|
201
|
+
scope.mode === "all" ? ` Grants: SELECT on ALL databases (${scope.databases.length})` : ` Grants: SELECT on ${scope.databases[0]}`
|
|
202
|
+
);
|
|
203
|
+
if (scope.mode === "all" && engine === "postgres") {
|
|
204
|
+
console.log(
|
|
205
|
+
chalk.yellow(
|
|
206
|
+
" Note: Databases created later will NOT get these grants automatically \u2014 re-run this command."
|
|
207
|
+
)
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
console.log("");
|
|
211
|
+
if (!yes && !dryRun) {
|
|
212
|
+
const confirmed = await promptYesNo({
|
|
213
|
+
message: "Apply these changes?",
|
|
214
|
+
defaultYes: false
|
|
215
|
+
});
|
|
216
|
+
if (!confirmed) {
|
|
217
|
+
console.log("Aborted.");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
console.log("");
|
|
221
|
+
}
|
|
222
|
+
const result = engine === "postgres" ? await applyPostgresReadonly({
|
|
223
|
+
config,
|
|
224
|
+
username,
|
|
225
|
+
password,
|
|
226
|
+
databases: scope.databases,
|
|
227
|
+
dryRun,
|
|
228
|
+
verbose
|
|
229
|
+
}) : await applyMysqlReadonly({
|
|
230
|
+
config,
|
|
231
|
+
username,
|
|
232
|
+
password,
|
|
233
|
+
databases: scope.mode === "all" ? "all" : scope.databases,
|
|
234
|
+
hostPattern,
|
|
235
|
+
dryRun,
|
|
236
|
+
verbose
|
|
237
|
+
});
|
|
238
|
+
if (dryRun) {
|
|
239
|
+
console.log(chalk.cyan("Dry run \u2014 SQL that would run:"));
|
|
240
|
+
for (const statement of result.statements) {
|
|
241
|
+
console.log(` ${statement}`);
|
|
242
|
+
}
|
|
243
|
+
console.log("");
|
|
244
|
+
console.log(chalk.cyan("No changes made."));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
console.log(
|
|
248
|
+
chalk.green(
|
|
249
|
+
result.created ? `Created user "${username}" and granted read-only access.` : `Updated user "${username}" password and refreshed read-only grants.`
|
|
250
|
+
)
|
|
251
|
+
);
|
|
252
|
+
console.log("");
|
|
253
|
+
const sampleDb = scope.mode === "one" ? scope.databases[0] ?? "" : scope.databases[0] ?? "";
|
|
254
|
+
const readonlyUrl = buildReadonlyUrl({
|
|
255
|
+
engine,
|
|
256
|
+
host: config.host,
|
|
257
|
+
port: config.port,
|
|
258
|
+
username,
|
|
259
|
+
password,
|
|
260
|
+
database: sampleDb
|
|
261
|
+
});
|
|
262
|
+
console.log("Connection (secret shown once \u2014 copy now):");
|
|
263
|
+
console.log(
|
|
264
|
+
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
265
|
+
);
|
|
266
|
+
console.log(readonlyUrl);
|
|
267
|
+
if (scope.mode === "all") {
|
|
268
|
+
console.log("");
|
|
269
|
+
console.log(
|
|
270
|
+
chalk.gray(
|
|
271
|
+
` (URL uses database "${sampleDb}" as an example; user can connect to any granted database.)`
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
console.log(
|
|
276
|
+
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
277
|
+
);
|
|
278
|
+
console.log("");
|
|
279
|
+
console.log(chalk.gray(`Admin URL used: ${redactUrl(url)}`));
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (error instanceof ZodError) {
|
|
282
|
+
console.error(chalk.red("\n\u274C Invalid command options:"));
|
|
283
|
+
error.errors.forEach((err) => {
|
|
284
|
+
console.error(chalk.red(` ${err.path.join(".")}: ${err.message}`));
|
|
285
|
+
});
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
const verbose = (
|
|
289
|
+
/** @type {any} */
|
|
290
|
+
options?.verbose || false
|
|
291
|
+
);
|
|
292
|
+
console.error(chalk.red("\n\u274C create-readonly-user failed:"));
|
|
293
|
+
if (error instanceof Error) {
|
|
294
|
+
console.error(chalk.red(` ${error.message}`));
|
|
295
|
+
if (verbose && error.stack) {
|
|
296
|
+
console.error(chalk.gray("\nStack trace:"));
|
|
297
|
+
console.error(chalk.gray(error.stack));
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
console.error(chalk.red(` ${String(error)}`));
|
|
301
|
+
}
|
|
302
|
+
console.error(
|
|
303
|
+
chalk.yellow(
|
|
304
|
+
"\n\u{1F4A1} Pass --url or set DATABASE_URL. Use --verbose for details.\n"
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export {
|
|
312
|
+
createReadonlyUserCommand
|
|
313
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runAws,
|
|
3
|
+
runAwsVault
|
|
4
|
+
} from "./FNXUONAV.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/aws/iam/admin-group.js
|
|
7
|
+
import { spawnSync } from "child_process";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
var ADMIN_GROUP_NAME = "acl-admin-do-not-use";
|
|
10
|
+
var VAULT_DURATION = process.env.AWS_VAULT_DURATION ?? "15m";
|
|
11
|
+
function listVaultProfiles() {
|
|
12
|
+
const result = spawnSync("aws-vault", ["list", "--credentials"], {
|
|
13
|
+
encoding: "utf-8"
|
|
14
|
+
});
|
|
15
|
+
if (result.status !== 0) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return (result.stdout ?? "").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
19
|
+
}
|
|
20
|
+
function resolveAdminScanProfiles({ profile, all = false }) {
|
|
21
|
+
const targetProfile = profile ?? process.env.AWS_PROFILE;
|
|
22
|
+
if (!all && targetProfile) {
|
|
23
|
+
return [targetProfile];
|
|
24
|
+
}
|
|
25
|
+
const profiles = listVaultProfiles();
|
|
26
|
+
if (profiles.length === 0) {
|
|
27
|
+
throw new Error("No aws-vault profiles with credentials found.");
|
|
28
|
+
}
|
|
29
|
+
return profiles;
|
|
30
|
+
}
|
|
31
|
+
function groupUserNames(group) {
|
|
32
|
+
return (group.Users ?? []).map((user) => user.UserName);
|
|
33
|
+
}
|
|
34
|
+
function resolveProfileScan(params) {
|
|
35
|
+
const { profile, verbose = false } = params;
|
|
36
|
+
const idArgs = ["sts", "get-caller-identity"];
|
|
37
|
+
const groupArgs = ["iam", "get-group", "--group-name", ADMIN_GROUP_NAME];
|
|
38
|
+
const directRun = (args, options = {}) => runAws({ profile, args, expectJson: options.expectJson ?? true, verbose });
|
|
39
|
+
const vaultRun = (args, options = {}) => runAwsVault({
|
|
40
|
+
profile,
|
|
41
|
+
args,
|
|
42
|
+
duration: VAULT_DURATION,
|
|
43
|
+
expectJson: options.expectJson ?? true,
|
|
44
|
+
verbose
|
|
45
|
+
});
|
|
46
|
+
try {
|
|
47
|
+
const identity = directRun(idArgs);
|
|
48
|
+
const group = directRun(groupArgs);
|
|
49
|
+
return {
|
|
50
|
+
accountId: identity.Account,
|
|
51
|
+
arn: identity.Arn,
|
|
52
|
+
users: groupUserNames(group),
|
|
53
|
+
run: directRun
|
|
54
|
+
};
|
|
55
|
+
} catch (directError) {
|
|
56
|
+
try {
|
|
57
|
+
const identity = vaultRun(idArgs);
|
|
58
|
+
const group = vaultRun(groupArgs);
|
|
59
|
+
return {
|
|
60
|
+
accountId: identity.Account,
|
|
61
|
+
arn: identity.Arn,
|
|
62
|
+
users: groupUserNames(group),
|
|
63
|
+
run: vaultRun
|
|
64
|
+
};
|
|
65
|
+
} catch {
|
|
66
|
+
if (verbose) {
|
|
67
|
+
const reason = directError instanceof Error ? directError.message.split("\n")[0] ?? "" : String(directError);
|
|
68
|
+
console.log(chalk.dim(`skip ${profile} \u2014 ${reason}`));
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
ADMIN_GROUP_NAME,
|
|
77
|
+
listVaultProfiles,
|
|
78
|
+
resolveAdminScanProfiles,
|
|
79
|
+
groupUserNames,
|
|
80
|
+
resolveProfileScan
|
|
81
|
+
};
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authorizeRule,
|
|
3
|
+
describeSecurityGroups,
|
|
4
|
+
extractRoamingFirstName,
|
|
5
|
+
getCurrentPublicIp,
|
|
6
|
+
getSecurityGroupDetails,
|
|
7
|
+
instanceLabel,
|
|
8
|
+
listEnabledRegions,
|
|
9
|
+
listInstances,
|
|
10
|
+
listSshIngressRules,
|
|
11
|
+
printSshRules,
|
|
12
|
+
resolveRoamingRule,
|
|
13
|
+
revokeRule,
|
|
14
|
+
roamingRuleDescription,
|
|
15
|
+
ruleAlreadyAllowsIp,
|
|
16
|
+
securityGroupLabel
|
|
17
|
+
} from "./U7HAE2RF.js";
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_REGION,
|
|
20
|
+
allowIpOptionsSchema
|
|
21
|
+
} from "./J34UVKLA.js";
|
|
22
|
+
import {
|
|
23
|
+
AwsCliError,
|
|
24
|
+
getCallerIdentity,
|
|
25
|
+
requireCommands
|
|
26
|
+
} from "./FNXUONAV.js";
|
|
27
|
+
import {
|
|
28
|
+
resolveProfile
|
|
29
|
+
} from "./5GIUAFXL.js";
|
|
30
|
+
import {
|
|
31
|
+
promptChoice
|
|
32
|
+
} from "./ZHKVJO2U.js";
|
|
33
|
+
|
|
34
|
+
// src/commands/aws/ec2/allow-ip/handler.js
|
|
35
|
+
import chalk from "chalk";
|
|
36
|
+
import { ZodError } from "zod";
|
|
37
|
+
async function resolveRegion(params) {
|
|
38
|
+
const { profile, region, verbose } = params;
|
|
39
|
+
if (region) {
|
|
40
|
+
return region;
|
|
41
|
+
}
|
|
42
|
+
const instances = listInstances({ profile, region: DEFAULT_REGION, verbose });
|
|
43
|
+
if (instances.length > 0) {
|
|
44
|
+
return DEFAULT_REGION;
|
|
45
|
+
}
|
|
46
|
+
const regions = listEnabledRegions({
|
|
47
|
+
profile,
|
|
48
|
+
region: DEFAULT_REGION,
|
|
49
|
+
verbose
|
|
50
|
+
});
|
|
51
|
+
if (regions.length === 0) {
|
|
52
|
+
throw new Error("No enabled AWS regions found for this profile");
|
|
53
|
+
}
|
|
54
|
+
console.log(`No EC2 instances found in ${DEFAULT_REGION}.`);
|
|
55
|
+
const index = await promptChoice({
|
|
56
|
+
message: "Choose a region:",
|
|
57
|
+
options: regions
|
|
58
|
+
});
|
|
59
|
+
const chosen = regions[index];
|
|
60
|
+
if (!chosen) {
|
|
61
|
+
throw new Error("No region selected");
|
|
62
|
+
}
|
|
63
|
+
return chosen;
|
|
64
|
+
}
|
|
65
|
+
function handleRuleChangeError(error, ctx) {
|
|
66
|
+
const { securityGroup, sshRules, newCidr, ruleDescription, action } = ctx;
|
|
67
|
+
if (error.code === "InvalidPermission.Duplicate") {
|
|
68
|
+
const matching = ruleAlreadyAllowsIp(sshRules, newCidr);
|
|
69
|
+
const label = matching ? matching.description : ruleDescription;
|
|
70
|
+
console.log(
|
|
71
|
+
`Security group ${securityGroup.groupId} already allows SSH from ${newCidr} (${label}). Nothing to do.`
|
|
72
|
+
);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (error.code === "UnauthorizedOperation") {
|
|
76
|
+
console.error(
|
|
77
|
+
`Could not ${action} the SSH rule: your AWS profile does not have ec2:AuthorizeSecurityGroupIngress or ec2:RevokeSecurityGroupIngress on ${securityGroup.groupName} (${securityGroup.groupId}).`
|
|
78
|
+
);
|
|
79
|
+
console.error("");
|
|
80
|
+
console.error("Current single-host SSH rules on port 22:");
|
|
81
|
+
printSshRules(sshRules, (message) => console.error(message));
|
|
82
|
+
if (action === "create" && sshRules.length > 0) {
|
|
83
|
+
console.error("");
|
|
84
|
+
console.error(
|
|
85
|
+
"A rule may already exist. Re-run to update your named roaming rule, or ask an admin to grant security group ingress permissions."
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
async function allowIpCommand(options) {
|
|
93
|
+
try {
|
|
94
|
+
const {
|
|
95
|
+
profile: profileOption,
|
|
96
|
+
region: regionOption,
|
|
97
|
+
ruleName,
|
|
98
|
+
verbose
|
|
99
|
+
} = allowIpOptionsSchema.parse(options);
|
|
100
|
+
const profile = await resolveProfile({ profile: profileOption });
|
|
101
|
+
if (!profile) {
|
|
102
|
+
console.error(chalk.red("Error: pass --profile or set AWS_PROFILE."));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
requireCommands(["aws"]);
|
|
106
|
+
const identity = getCallerIdentity({
|
|
107
|
+
profile,
|
|
108
|
+
region: DEFAULT_REGION,
|
|
109
|
+
verbose
|
|
110
|
+
});
|
|
111
|
+
const firstName = extractRoamingFirstName({ profile, identity });
|
|
112
|
+
const ruleDescription = ruleName ?? roamingRuleDescription(firstName);
|
|
113
|
+
console.log("Caller:");
|
|
114
|
+
console.log(` Account: ${identity.Account}`);
|
|
115
|
+
console.log(` Arn: ${identity.Arn}`);
|
|
116
|
+
console.log(` Roaming rule: ${ruleDescription}`);
|
|
117
|
+
console.log("");
|
|
118
|
+
const region = await resolveRegion({
|
|
119
|
+
profile,
|
|
120
|
+
region: regionOption,
|
|
121
|
+
verbose
|
|
122
|
+
});
|
|
123
|
+
console.log(`Region: ${region}`);
|
|
124
|
+
console.log("");
|
|
125
|
+
const instances = listInstances({ profile, region, verbose });
|
|
126
|
+
if (instances.length === 0) {
|
|
127
|
+
console.log(`No EC2 instances found in ${region}.`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const instanceIndex = await promptChoice({
|
|
131
|
+
message: "Choose an EC2 instance:",
|
|
132
|
+
options: instances.map((instance2) => instanceLabel(instance2))
|
|
133
|
+
});
|
|
134
|
+
const instance = instances[instanceIndex];
|
|
135
|
+
if (!instance) {
|
|
136
|
+
throw new Error("No instance selected");
|
|
137
|
+
}
|
|
138
|
+
console.log("");
|
|
139
|
+
const securityGroups = describeSecurityGroups({
|
|
140
|
+
profile,
|
|
141
|
+
region,
|
|
142
|
+
groupIds: instance.securityGroupIds,
|
|
143
|
+
verbose
|
|
144
|
+
});
|
|
145
|
+
if (securityGroups.length === 0) {
|
|
146
|
+
console.log("No security groups found on the selected instance.");
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
let securityGroup;
|
|
150
|
+
if (securityGroups.length === 1) {
|
|
151
|
+
const onlyGroup = securityGroups[0];
|
|
152
|
+
if (!onlyGroup) {
|
|
153
|
+
throw new Error("No security group found on the selected instance");
|
|
154
|
+
}
|
|
155
|
+
securityGroup = onlyGroup;
|
|
156
|
+
console.log(`Security group: ${securityGroupLabel(securityGroup)}`);
|
|
157
|
+
} else {
|
|
158
|
+
const groupIndex = await promptChoice({
|
|
159
|
+
message: "Choose a security group:",
|
|
160
|
+
options: securityGroups.map((group) => securityGroupLabel(group))
|
|
161
|
+
});
|
|
162
|
+
securityGroup = securityGroups[groupIndex];
|
|
163
|
+
}
|
|
164
|
+
if (!securityGroup) {
|
|
165
|
+
throw new Error("No security group selected");
|
|
166
|
+
}
|
|
167
|
+
console.log("");
|
|
168
|
+
const currentIp = await getCurrentPublicIp();
|
|
169
|
+
const newCidr = `${currentIp}/32`;
|
|
170
|
+
console.log(`Current public IP: ${currentIp}`);
|
|
171
|
+
console.log("");
|
|
172
|
+
const sgDetails = getSecurityGroupDetails({
|
|
173
|
+
profile,
|
|
174
|
+
region,
|
|
175
|
+
groupId: securityGroup.groupId,
|
|
176
|
+
verbose
|
|
177
|
+
});
|
|
178
|
+
if (!sgDetails) {
|
|
179
|
+
console.log("Could not load security group details.");
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
const sshRules = listSshIngressRules(sgDetails);
|
|
183
|
+
const roamingRule = resolveRoamingRule(sshRules, ruleDescription);
|
|
184
|
+
if (roamingRule && roamingRule.cidrIp === newCidr) {
|
|
185
|
+
console.log(
|
|
186
|
+
`Security group ${securityGroup.groupId} already allows SSH from ${newCidr} (${ruleDescription}). Nothing to do.`
|
|
187
|
+
);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const otherRuleAtIp = ruleAlreadyAllowsIp(sshRules, newCidr);
|
|
191
|
+
if (otherRuleAtIp && (!roamingRule || otherRuleAtIp.cidrIp !== roamingRule.cidrIp)) {
|
|
192
|
+
const label = otherRuleAtIp.description || "(no description)";
|
|
193
|
+
console.log(
|
|
194
|
+
`Security group ${securityGroup.groupId} already allows SSH from ${newCidr} (${label}). Nothing to do.`
|
|
195
|
+
);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (roamingRule) {
|
|
199
|
+
console.log(
|
|
200
|
+
`Updating SSH rule (${ruleDescription}): ${roamingRule.cidrIp} -> ${newCidr}`
|
|
201
|
+
);
|
|
202
|
+
try {
|
|
203
|
+
revokeRule({
|
|
204
|
+
profile,
|
|
205
|
+
region,
|
|
206
|
+
groupId: securityGroup.groupId,
|
|
207
|
+
rule: roamingRule,
|
|
208
|
+
verbose
|
|
209
|
+
});
|
|
210
|
+
authorizeRule({
|
|
211
|
+
profile,
|
|
212
|
+
region,
|
|
213
|
+
groupId: securityGroup.groupId,
|
|
214
|
+
cidrIp: newCidr,
|
|
215
|
+
description: ruleDescription,
|
|
216
|
+
fromPort: roamingRule.fromPort,
|
|
217
|
+
toPort: roamingRule.toPort,
|
|
218
|
+
ipProtocol: roamingRule.ipProtocol,
|
|
219
|
+
verbose
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (error instanceof AwsCliError) {
|
|
223
|
+
handleRuleChangeError(error, {
|
|
224
|
+
securityGroup,
|
|
225
|
+
sshRules,
|
|
226
|
+
newCidr,
|
|
227
|
+
ruleDescription,
|
|
228
|
+
action: "update"
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
if (sshRules.length > 0) {
|
|
236
|
+
console.log("Existing single-host SSH rules (unchanged):");
|
|
237
|
+
printSshRules(sshRules);
|
|
238
|
+
console.log("");
|
|
239
|
+
}
|
|
240
|
+
console.log(`Creating new "${ruleDescription}" SSH rule for ${newCidr}`);
|
|
241
|
+
try {
|
|
242
|
+
authorizeRule({
|
|
243
|
+
profile,
|
|
244
|
+
region,
|
|
245
|
+
groupId: securityGroup.groupId,
|
|
246
|
+
cidrIp: newCidr,
|
|
247
|
+
description: ruleDescription,
|
|
248
|
+
verbose
|
|
249
|
+
});
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error instanceof AwsCliError) {
|
|
252
|
+
handleRuleChangeError(error, {
|
|
253
|
+
securityGroup,
|
|
254
|
+
sshRules,
|
|
255
|
+
newCidr,
|
|
256
|
+
ruleDescription,
|
|
257
|
+
action: "create"
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
console.log("");
|
|
265
|
+
console.log(
|
|
266
|
+
`Done. ${securityGroup.groupName} (${securityGroup.groupId}) now allows SSH from ${newCidr} (${ruleDescription}).`
|
|
267
|
+
);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (error instanceof ZodError) {
|
|
270
|
+
console.error(chalk.red("\n\u274C Invalid command options:"));
|
|
271
|
+
error.errors.forEach((err) => {
|
|
272
|
+
console.error(chalk.red(` ${err.path.join(".")}: ${err.message}`));
|
|
273
|
+
});
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
const verbose = (
|
|
277
|
+
/** @type {any} */
|
|
278
|
+
options?.verbose || false
|
|
279
|
+
);
|
|
280
|
+
console.error(chalk.red("\n\u274C allow-ip failed:"));
|
|
281
|
+
if (error instanceof Error) {
|
|
282
|
+
console.error(chalk.red(` ${error.message}`));
|
|
283
|
+
if (verbose && error.stack) {
|
|
284
|
+
console.error(chalk.gray("\nStack trace:"));
|
|
285
|
+
console.error(chalk.gray(error.stack));
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
console.error(chalk.red(` ${String(error)}`));
|
|
289
|
+
}
|
|
290
|
+
console.error(
|
|
291
|
+
chalk.yellow("\n\u{1F4A1} Use --verbose for detailed error information\n")
|
|
292
|
+
);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export {
|
|
298
|
+
allowIpCommand
|
|
299
|
+
};
|