Compare commits
No commits in common. "5f3a201ffd0cf1fdf53f1db761aa6420cb300b82" and "b53cbc3ea56404953bf6813adade8c02ad4d1610" have entirely different histories.
5f3a201ffd
...
b53cbc3ea5
|
@ -24,8 +24,6 @@ steps:
|
||||||
- zig build -Dtarget=x86_64-windows
|
- zig build -Dtarget=x86_64-windows
|
||||||
- zig build -Dtarget=aarch64-linux
|
- zig build -Dtarget=aarch64-linux
|
||||||
- zig build -Dtarget=riscv64-linux
|
- zig build -Dtarget=riscv64-linux
|
||||||
- zig build -Dtarget=x86_64-macos
|
|
||||||
- zig build -Dtarget=aarch64-macos
|
|
||||||
- name: notify
|
- name: notify
|
||||||
image: plugins/matrix
|
image: plugins/matrix
|
||||||
when:
|
when:
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,4 +9,3 @@ src/models/
|
||||||
smithy/zig-out/
|
smithy/zig-out/
|
||||||
libs/
|
libs/
|
||||||
src/git_version.zig
|
src/git_version.zig
|
||||||
zig-out
|
|
||||||
|
|
|
@ -20,10 +20,7 @@ This is for x86_linux. Tested targets:
|
||||||
* aarch64-linux
|
* aarch64-linux
|
||||||
* x86_64-windows
|
* x86_64-windows
|
||||||
* arm-linux
|
* arm-linux
|
||||||
* aarch64-macos
|
|
||||||
* x86_64-macos
|
|
||||||
|
|
||||||
Tested targets are built, but not continuously tested, by CI.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub fn build(b: *Builder) !void {
|
||||||
exe.setTarget(target);
|
exe.setTarget(target);
|
||||||
exe.setBuildMode(mode);
|
exe.setBuildMode(mode);
|
||||||
|
|
||||||
if (target.getOs().tag != .macos) exe.linkage = .static;
|
exe.linkage = .static;
|
||||||
|
|
||||||
// TODO: Strip doesn't actually fully strip the executable. If we're on
|
// TODO: Strip doesn't actually fully strip the executable. If we're on
|
||||||
// linux we can run strip on the result, probably at the expense
|
// linux we can run strip on the result, probably at the expense
|
||||||
|
|
|
@ -25,9 +25,9 @@ pub const Credentials = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: Self) void {
|
||||||
std.crypto.utils.secureZero(u8, self.secret_key);
|
for (self.secret_key) |_, i| self.secret_key[i] = 0;
|
||||||
self.allocator.free(self.secret_key);
|
|
||||||
self.allocator.free(self.access_key);
|
self.allocator.free(self.access_key);
|
||||||
|
self.allocator.free(self.secret_key);
|
||||||
if (self.session_token) |t| self.allocator.free(t);
|
if (self.session_token) |t| self.allocator.free(t);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const auth = @import("aws_authentication.zig");
|
const auth = @import("aws_authentication.zig");
|
||||||
const zfetch = @import("zfetch");
|
|
||||||
|
|
||||||
const log = std.log.scoped(.aws_credentials);
|
const log = std.log.scoped(.aws_credentials);
|
||||||
|
|
||||||
|
@ -25,29 +24,23 @@ pub const Options = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn getCredentials(allocator: std.mem.Allocator, options: Options) !auth.Credentials {
|
pub fn getCredentials(allocator: std.mem.Allocator, options: Options) !auth.Credentials {
|
||||||
if (try getEnvironmentCredentials(allocator)) |cred| {
|
if (try getEnvironmentCredentials(allocator)) |cred| return cred;
|
||||||
log.debug("Found credentials in environment. Access key: {s}", .{cred.access_key});
|
|
||||||
return cred;
|
|
||||||
}
|
|
||||||
// TODO: 2-5
|
// TODO: 2-5
|
||||||
// Note that boto and Java disagree on where this fits in the order
|
// Note that boto and Java disagree on where this fits in the order
|
||||||
if (try getWebIdentityToken(allocator)) |cred| return cred;
|
if (try getWebIdentityToken(allocator)) |cred| return cred;
|
||||||
if (try getProfileCredentials(allocator, options.profile)) |cred| return cred;
|
if (try getProfileCredentials(allocator, options.profile)) |cred| return cred;
|
||||||
|
|
||||||
if (try getContainerCredentials(allocator)) |cred| return cred;
|
|
||||||
// I don't think we need v1 at all?
|
|
||||||
if (try getImdsv2Credentials(allocator)) |cred| return cred;
|
|
||||||
return error.NotImplemented;
|
return error.NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getEnvironmentCredentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
fn getEnvironmentCredentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
||||||
const secret_key = (try getEnvironmentVariable(allocator, "AWS_SECRET_ACCESS_KEY")) orelse return null;
|
const secret_key = (try getEnvironmentVariable(allocator, "AWS_SECRET_ACCESS_KEY")) orelse return null;
|
||||||
defer allocator.free(secret_key); //yes, we're not zeroing. But then, the secret key is in an environment var anyway
|
defer allocator.free(secret_key); //yes, we're not zeroing. But then, the secret key is in an environment var anyway
|
||||||
|
const mutable_key = try allocator.dupe(u8, secret_key);
|
||||||
// Use cross-platform API (requires allocation)
|
// Use cross-platform API (requires allocation)
|
||||||
return auth.Credentials.init(
|
return auth.Credentials.init(
|
||||||
allocator,
|
allocator,
|
||||||
(try getEnvironmentVariable(allocator, "AWS_ACCESS_KEY_ID")) orelse return null,
|
(try getEnvironmentVariable(allocator, "AWS_ACCESS_KEY_ID")) orelse return null,
|
||||||
try allocator.dupe(u8, secret_key),
|
mutable_key,
|
||||||
(try getEnvironmentVariable(allocator, "AWS_SESSION_TOKEN")) orelse
|
(try getEnvironmentVariable(allocator, "AWS_SESSION_TOKEN")) orelse
|
||||||
try getEnvironmentVariable(allocator, "AWS_SECURITY_TOKEN"), // Security token is backward compat only
|
try getEnvironmentVariable(allocator, "AWS_SECURITY_TOKEN"), // Security token is backward compat only
|
||||||
);
|
);
|
||||||
|
@ -70,178 +63,6 @@ fn getWebIdentityToken(allocator: std.mem.Allocator) !?auth.Credentials {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
fn getContainerCredentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
|
||||||
_ = allocator;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getImdsv2Credentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
|
||||||
try zfetch.init();
|
|
||||||
defer zfetch.deinit();
|
|
||||||
|
|
||||||
var token: [65535]u8 = undefined;
|
|
||||||
var len: usize = undefined;
|
|
||||||
// Get token
|
|
||||||
{
|
|
||||||
var headers = zfetch.Headers.init(allocator);
|
|
||||||
defer headers.deinit();
|
|
||||||
|
|
||||||
try headers.appendValue("X-aws-ec2-metadata-token-ttl-seconds", "21600");
|
|
||||||
var req = try zfetch.Request.init(allocator, "http://169.254.169.254/latest/api/token", null);
|
|
||||||
defer req.deinit();
|
|
||||||
try req.do(.PUT, headers, "");
|
|
||||||
if (req.status.code != 200) {
|
|
||||||
log.warn("Bad status code received from IMDS v2: {}", .{req.status.code});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const reader = req.reader();
|
|
||||||
const read = try reader.read(&token);
|
|
||||||
if (read == 0 or read == 65535) {
|
|
||||||
log.warn("Unexpected zero or long response from IMDS v2: {s}", .{token});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
len = read;
|
|
||||||
}
|
|
||||||
log.debug("Got token from IMDSv2", .{});
|
|
||||||
const role_name = try getImdsRoleName(allocator, token[0..len]);
|
|
||||||
if (role_name == null) {
|
|
||||||
log.info("No role is associated with this instance", .{});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
defer allocator.free(role_name.?);
|
|
||||||
log.debug("Got role name '{s}'", .{role_name});
|
|
||||||
return getImdsCredentials(allocator, role_name.?, token[0..len]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getImdsRoleName(allocator: std.mem.Allocator, imds_token: []u8) !?[]const u8 {
|
|
||||||
// {
|
|
||||||
// "Code" : "Success",
|
|
||||||
// "LastUpdated" : "2022-02-09T05:42:09Z",
|
|
||||||
// "InstanceProfileArn" : "arn:aws:iam::550620852718:instance-profile/ec2-dev",
|
|
||||||
// "InstanceProfileId" : "AIPAYAM4POHXCFNKZ7HU2"
|
|
||||||
// }
|
|
||||||
var buf: [65535]u8 = undefined;
|
|
||||||
var headers = zfetch.Headers.init(allocator);
|
|
||||||
defer headers.deinit();
|
|
||||||
try headers.appendValue("X-aws-ec2-metadata-token", imds_token);
|
|
||||||
|
|
||||||
var req = try zfetch.Request.init(allocator, "http://169.254.169.254/latest/meta-data/iam/info", null);
|
|
||||||
defer req.deinit();
|
|
||||||
|
|
||||||
try req.do(.GET, headers, null);
|
|
||||||
|
|
||||||
if (req.status.code != 200 and req.status.code != 404) {
|
|
||||||
log.warn("Bad status code received from IMDS iam endpoint: {}", .{req.status.code});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (req.status.code == 404) return null;
|
|
||||||
const reader = req.reader();
|
|
||||||
const read = try reader.read(&buf);
|
|
||||||
if (read == 65535) {
|
|
||||||
log.warn("Unexpected zero or long response from IMDS endpoint post token: {s}", .{buf});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (read == 0) return null;
|
|
||||||
|
|
||||||
const ImdsResponse = struct {
|
|
||||||
Code: []const u8,
|
|
||||||
LastUpdated: []const u8,
|
|
||||||
InstanceProfileArn: []const u8,
|
|
||||||
InstanceProfileId: []const u8,
|
|
||||||
};
|
|
||||||
const imds_response = blk: {
|
|
||||||
var stream = std.json.TokenStream.init(buf[0..read]);
|
|
||||||
const res = std.json.parse(ImdsResponse, &stream, .{ .allocator = allocator }) catch |e| {
|
|
||||||
log.err("Unexpected Json response from IMDS endpoint: {s}", .{buf});
|
|
||||||
log.err("Error parsing json: {}", .{e});
|
|
||||||
if (@errorReturnTrace()) |trace| {
|
|
||||||
std.debug.dumpStackTrace(trace.*);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
break :blk res;
|
|
||||||
};
|
|
||||||
defer std.json.parseFree(ImdsResponse, imds_response, .{ .allocator = allocator });
|
|
||||||
|
|
||||||
const role_arn = imds_response.InstanceProfileArn;
|
|
||||||
const first_slash = std.mem.indexOf(u8, role_arn, "/"); // I think this is valid
|
|
||||||
if (first_slash == null) {
|
|
||||||
log.err("Could not find role name in arn '{s}'", .{role_arn});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return try allocator.dupe(u8, role_arn[first_slash.? + 1 ..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Note - this internal function assumes zfetch is initialized prior to use
|
|
||||||
fn getImdsCredentials(allocator: std.mem.Allocator, role_name: []const u8, imds_token: []u8) !?auth.Credentials {
|
|
||||||
var buf: [65535]u8 = undefined;
|
|
||||||
var headers = zfetch.Headers.init(allocator);
|
|
||||||
defer headers.deinit();
|
|
||||||
try headers.appendValue("X-aws-ec2-metadata-token", imds_token);
|
|
||||||
|
|
||||||
const url = try std.fmt.allocPrint(allocator, "http://169.254.169.254/latest/meta-data/iam/security-credentials/{s}/", .{role_name});
|
|
||||||
defer allocator.free(url);
|
|
||||||
var req = try zfetch.Request.init(allocator, url, null);
|
|
||||||
defer req.deinit();
|
|
||||||
|
|
||||||
try req.do(.GET, headers, null);
|
|
||||||
|
|
||||||
if (req.status.code != 200) {
|
|
||||||
log.warn("Bad status code received from IMDS role endpoint: {}", .{req.status.code});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const reader = req.reader();
|
|
||||||
const read = try reader.read(&buf);
|
|
||||||
if (read == 0 or read == 65535) {
|
|
||||||
log.warn("Unexpected zero or long response from IMDS role endpoint: {s}", .{buf});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const ImdsResponse = struct {
|
|
||||||
Code: []const u8,
|
|
||||||
LastUpdated: []const u8,
|
|
||||||
Type: []const u8,
|
|
||||||
AccessKeyId: []const u8,
|
|
||||||
SecretAccessKey: []const u8,
|
|
||||||
Token: []const u8,
|
|
||||||
Expiration: []const u8,
|
|
||||||
};
|
|
||||||
const imds_response = blk: {
|
|
||||||
var stream = std.json.TokenStream.init(buf[0..read]);
|
|
||||||
const res = std.json.parse(ImdsResponse, &stream, .{ .allocator = allocator }) catch |e| {
|
|
||||||
log.err("Unexpected Json response from IMDS endpoint: {s}", .{buf});
|
|
||||||
log.err("Error parsing json: {}", .{e});
|
|
||||||
if (@errorReturnTrace()) |trace| {
|
|
||||||
std.debug.dumpStackTrace(trace.*);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
break :blk res;
|
|
||||||
};
|
|
||||||
defer std.json.parseFree(ImdsResponse, imds_response, .{ .allocator = allocator });
|
|
||||||
|
|
||||||
const ret = auth.Credentials.init(
|
|
||||||
allocator,
|
|
||||||
try allocator.dupe(u8, imds_response.AccessKeyId),
|
|
||||||
try allocator.dupe(u8, imds_response.SecretAccessKey),
|
|
||||||
try allocator.dupe(u8, imds_response.Token),
|
|
||||||
);
|
|
||||||
log.debug("IMDSv2 credentials found. Access key: {s}", .{ret.access_key});
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
// {
|
|
||||||
// "Code" : "Success",
|
|
||||||
// "LastUpdated" : "2022-02-08T23:49:02Z",
|
|
||||||
// "Type" : "AWS-HMAC",
|
|
||||||
// "AccessKeyId" : "ASEXAMPLE",
|
|
||||||
// "SecretAccessKey" : "example",
|
|
||||||
// "Token" : "IQoJb==",
|
|
||||||
// "Expiration" : "2022-02-09T06:02:23Z"
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getProfileCredentials(allocator: std.mem.Allocator, options: Profile) !?auth.Credentials {
|
fn getProfileCredentials(allocator: std.mem.Allocator, options: Profile) !?auth.Credentials {
|
||||||
var default_path: ?[]const u8 = null;
|
var default_path: ?[]const u8 = null;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user