feat: aws auth
This commit is contained in:
@@ -1,18 +1,50 @@
|
||||
import { InfisicalSDK } from "..";
|
||||
import { ApiV1AuthUniversalAuthLoginPostRequest } from "../infisicalapi_client";
|
||||
import { DefaultApi as InfisicalApi } from "../infisicalapi_client";
|
||||
import { MACHINE_IDENTITY_ID_ENV_NAME } from "./constants";
|
||||
import { getAwsRegion, performAwsIamLogin } from "./util";
|
||||
import AWS from "aws-sdk";
|
||||
|
||||
type AuthenticatorFunction = (accessToken: string) => InfisicalSDK;
|
||||
|
||||
type AwsAuthLoginOptions = {
|
||||
identityId?: string;
|
||||
};
|
||||
|
||||
export default class AuthClient {
|
||||
sdkAuthenticator: AuthenticatorFunction;
|
||||
apiClient: InfisicalApi;
|
||||
baseUrl: string;
|
||||
|
||||
constructor(authenticator: AuthenticatorFunction, apiInstance: InfisicalApi) {
|
||||
constructor(authenticator: AuthenticatorFunction, apiInstance: InfisicalApi, baseUrl: string) {
|
||||
this.sdkAuthenticator = authenticator;
|
||||
this.apiClient = apiInstance;
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
awsIamAuth = {
|
||||
login: async (options: AwsAuthLoginOptions) => {
|
||||
const identityId = options.identityId || process.env[MACHINE_IDENTITY_ID_ENV_NAME];
|
||||
|
||||
if (!identityId) {
|
||||
throw new Error("Identity ID is required for AWS IAM authentication");
|
||||
}
|
||||
|
||||
const iamRequest = await performAwsIamLogin(this.baseUrl, identityId, await getAwsRegion());
|
||||
|
||||
const res = await this.apiClient.apiV1AuthAwsAuthLoginPost({
|
||||
apiV1AuthAwsAuthLoginPostRequest: {
|
||||
iamHttpRequestMethod: iamRequest.iamHttpRequestMethod,
|
||||
iamRequestBody: Buffer.from(iamRequest.iamRequestBody).toString("base64"),
|
||||
iamRequestHeaders: Buffer.from(JSON.stringify(iamRequest.iamRequestHeaders)).toString("base64"),
|
||||
identityId
|
||||
}
|
||||
});
|
||||
|
||||
return this.sdkAuthenticator(res.data.accessToken);
|
||||
}
|
||||
};
|
||||
|
||||
universalAuth = {
|
||||
login: async (options: ApiV1AuthUniversalAuthLoginPostRequest) => {
|
||||
const res = await this.apiClient.apiV1AuthUniversalAuthLoginPost({
|
||||
|
||||
3
src/custom/constants.ts
Normal file
3
src/custom/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const MACHINE_IDENTITY_ID_ENV_NAME = "INFISICAL_MACHINE_IDENTITY_ID";
|
||||
export const AWS_TOKEN_METADATA_URI = "http://169.254.169.254/latest/api/token";
|
||||
export const AWS_IDENTITY_DOCUMENT_URI = "http://169.254.169.254/latest/dynamic/instance-identity/document";
|
||||
@@ -1,8 +1,7 @@
|
||||
import { RawAxiosRequestConfig } from "axios";
|
||||
import { Configuration, DefaultApi as InfisicalApi } from "../infisicalapi_client";
|
||||
import { DefaultApi as InfisicalApi } from "../infisicalapi_client";
|
||||
import type {
|
||||
DefaultApiApiV3SecretsRawSecretNameDeleteRequest,
|
||||
DefaultApiApiV3SecretsRawSecretNameGetRequest,
|
||||
DefaultApiApiV3SecretsRawSecretNamePatchRequest,
|
||||
DefaultApiApiV3SecretsRawSecretNamePostRequest
|
||||
} from "../infisicalapi_client";
|
||||
|
||||
56
src/custom/util.ts
Normal file
56
src/custom/util.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import axios from "axios";
|
||||
import { AWS_IDENTITY_DOCUMENT_URI, AWS_TOKEN_METADATA_URI } from "./constants";
|
||||
import AWS from "aws-sdk";
|
||||
import aws4 from "aws4";
|
||||
export const getAwsRegion = async () => {
|
||||
const region = process.env.AWS_REGION; // Typically found in lambda runtime environment
|
||||
if (region) {
|
||||
return region;
|
||||
}
|
||||
|
||||
try {
|
||||
const timeout = 1000;
|
||||
|
||||
const tokenRes = await axios.get(AWS_TOKEN_METADATA_URI, {
|
||||
headers: {
|
||||
"X-aws-ec2-metadata-token-ttl-seconds": "21600"
|
||||
},
|
||||
timeout: 5_000 // 5 seconds
|
||||
});
|
||||
|
||||
const identityResponse = await axios.get<{ region: string }>(AWS_IDENTITY_DOCUMENT_URI, {
|
||||
headers: {
|
||||
"X-aws-ec2-metadata-token": tokenRes.data,
|
||||
Accept: "application/json"
|
||||
},
|
||||
timeout: timeout
|
||||
});
|
||||
|
||||
return identityResponse.data.region;
|
||||
} catch (e) {
|
||||
throw new Error("Failed to retrieve AWS region");
|
||||
}
|
||||
};
|
||||
|
||||
export const performAwsIamLogin = async (baseUrl: string, identityId: string, region: string) => {
|
||||
const body = "Action=GetCallerIdentity&Version=2011-06-15";
|
||||
|
||||
const signOpts = aws4.sign(
|
||||
{
|
||||
service: "sts",
|
||||
path: `/?${body}`,
|
||||
region
|
||||
},
|
||||
{
|
||||
accessKeyId: AWS.config.credentials?.accessKeyId,
|
||||
secretAccessKey: AWS.config.credentials?.secretAccessKey
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
iamHttpRequestMethod: "POST",
|
||||
iamRequestUrl: signOpts.host,
|
||||
iamRequestBody: body,
|
||||
iamRequestHeaders: signOpts.headers
|
||||
} as const;
|
||||
};
|
||||
@@ -38,7 +38,7 @@ class InfisicalSDK {
|
||||
})
|
||||
);
|
||||
|
||||
this.#authClient = new AuthClient(this.authenticate.bind(this), this.#apiInstance);
|
||||
this.#authClient = new AuthClient(this.authenticate.bind(this), this.#apiInstance, this.#basePath);
|
||||
this.#dynamicSecretsClient = new DynamicSecretsClient(this.#apiInstance, this.#requestOptions);
|
||||
this.#secretsClient = new SecretsClient(this.#apiInstance, this.#requestOptions);
|
||||
this.rest = () => buildRestClient(this.#apiInstance, this.#requestOptions);
|
||||
@@ -61,7 +61,7 @@ class InfisicalSDK {
|
||||
this.rest = () => buildRestClient(this.#apiInstance, this.#requestOptions);
|
||||
this.#secretsClient = new SecretsClient(this.#apiInstance, this.#requestOptions);
|
||||
this.#dynamicSecretsClient = new DynamicSecretsClient(this.#apiInstance, this.#requestOptions);
|
||||
this.#authClient = new AuthClient(this.authenticate.bind(this), this.#apiInstance);
|
||||
this.#authClient = new AuthClient(this.authenticate.bind(this), this.#apiInstance, this.#basePath);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user