populate root config from access_keys.csv
This commit is contained in:
		
							parent
							
								
									9902de2837
								
							
						
					
					
						commit
						87ac512dd4
					
				
					 2 changed files with 162 additions and 15 deletions
				
			
		|  | @ -10,21 +10,35 @@ const Self = @This(); | |||
| allocator: std.mem.Allocator, | ||||
| root_account_key: *[encryption.key_length]u8, | ||||
| 
 | ||||
| pub var root_key_mapping: ?std.StringHashMap([]const u8) = null; | ||||
| 
 | ||||
| pub fn accountForId(allocator: std.mem.Allocator, account_id: []const u8) !Self { | ||||
|     // TODO: Allow environment variables to house encoded keys. If not in the | ||||
|     //       environment, check with LocalDB table to get it. We're | ||||
|     //       building LocalDB, though, so we need that working first... | ||||
|     if (!std.mem.eql(u8, account_id, "1234")) { | ||||
|         log.err("Got account id '{s}', but only '1234' is valid right now", .{account_id}); | ||||
|         return error.NotImplemented; | ||||
|     if (std.mem.eql(u8, account_id, "1234")) { | ||||
|         var key = try allocator.alloc(u8, encryption.key_length); | ||||
|         errdefer allocator.free(key); | ||||
|         try encryption.decodeKey(key[0..encryption.key_length], test_account_key.*); | ||||
|         return Self{ | ||||
|             .allocator = allocator, | ||||
|             .root_account_key = key[0..encryption.key_length], | ||||
|         }; | ||||
|     } | ||||
|     var key = try allocator.alloc(u8, encryption.key_length); | ||||
|     errdefer allocator.free(key); | ||||
|     try encryption.decodeKey(key[0..encryption.key_length], test_account_key.*); | ||||
|     return Self{ | ||||
|         .allocator = allocator, | ||||
|         .root_account_key = key[0..encryption.key_length], | ||||
|     }; | ||||
| 
 | ||||
|     // Check our root mappings (populated elsewhere) | ||||
|     if (root_key_mapping) |m| { | ||||
|         if (m.get(account_id)) |k| { | ||||
|             var key = try allocator.alloc(u8, encryption.key_length); | ||||
|             errdefer allocator.free(key); | ||||
|             try encryption.decodeKey(key[0..encryption.key_length], @constCast(k[0..encryption.encoded_key_length]).*); | ||||
|             return Self{ | ||||
|                 .allocator = allocator, | ||||
|                 .root_account_key = key[0..encryption.key_length], | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Check STS | ||||
|     log.err("Got account id '{s}', but could not find this ('1234' is test account). STS GetAccessKeyInfo not implemented", .{account_id}); | ||||
|     return error.NotImplemented; | ||||
| } | ||||
| 
 | ||||
| pub fn deinit(self: Self) void { | ||||
|  |  | |||
							
								
								
									
										137
									
								
								src/main.zig
									
										
									
									
									
								
							
							
						
						
									
										137
									
								
								src/main.zig
									
										
									
									
									
								
							|  | @ -4,6 +4,7 @@ const universal_lambda_interface = @import("universal_lambda_interface"); | |||
| const universal_lambda_options = @import("universal_lambda_build_options"); | ||||
| const signing = @import("aws-signing"); | ||||
| const AuthenticatedRequest = @import("AuthenticatedRequest.zig"); | ||||
| const Account = @import("Account.zig"); | ||||
| 
 | ||||
| const log = std.log.scoped(.dynamodb); | ||||
| 
 | ||||
|  | @ -95,6 +96,11 @@ fn authenticateUser(allocator: std.mem.Allocator, context: universal_lambda_inte | |||
|         .target = target, | ||||
|         .headers = headers, | ||||
|     }; | ||||
|     if (root_creds == null) { | ||||
|         root_creds = std.StringHashMap(signing.Credentials).init(allocator); | ||||
|         root_account_mapping = std.StringHashMap([]const u8).init(allocator); | ||||
|         Account.root_key_mapping = std.StringHashMap([]const u8).init(allocator); | ||||
|     } | ||||
|     const auth_bypass = | ||||
|         @import("builtin").mode == .Debug and try std.process.hasEnvVar(allocator, "DEBUG_AUTHN_BYPASS"); | ||||
|     const is_authenticated = auth_bypass or | ||||
|  | @ -118,17 +124,144 @@ fn authenticateUser(allocator: std.mem.Allocator, context: universal_lambda_inte | |||
|     } | ||||
| } | ||||
| 
 | ||||
| // TODO: Get hook these functions up to IAM for great good | ||||
| var test_credential: signing.Credentials = undefined; | ||||
| var root_creds: ?std.StringHashMap(signing.Credentials) = null; | ||||
| var root_account_mapping: std.StringHashMap([]const u8) = undefined; | ||||
| var creds_buf: [8192]u8 = undefined; | ||||
| fn getCreds(access: []const u8) ?signing.Credentials { | ||||
|     // We have 3 levels of access here | ||||
|     // | ||||
|     // 1. Test creds, used strictly for debugging | ||||
|     // 2. Creds from the root file, ideally used only for bootstrapping | ||||
|     // 3. Creds from STS GetAccessKeyInfo API call, which should be 99%+ of ops | ||||
|     if (std.mem.eql(u8, access, "ACCESS")) return test_credential; | ||||
|     fillRootCreds() catch |e| { | ||||
|         log.err("Error filling root creds. Base authentication will not work until this is fixed: {}", .{e}); | ||||
|         return null; | ||||
|     }; | ||||
|     log.debug("Creds for access key {s}: {any}", .{ access, root_creds.?.get(access) != null }); | ||||
|     if (root_creds.?.get(access)) |c| return c; | ||||
|     log.err("Creds not found in store. STS GetAccessKeyInfo call is not yet implemented", .{}); | ||||
|     return null; | ||||
| } | ||||
| 
 | ||||
| fn fillRootCreds() !void { | ||||
|     if (root_creds.?.count() > 0) return; | ||||
|     var fb_allocator = std.heap.FixedBufferAllocator.init(&creds_buf); | ||||
|     const allocator = fb_allocator.allocator(); | ||||
|     var file = std.fs.cwd().openFile("access_keys.csv", .{}) catch |e| { | ||||
|         log.err("Could not open access_keys.csv to access root creds: {}", .{e}); | ||||
|         return e; | ||||
|     }; | ||||
|     defer file.close(); | ||||
|     var buf_reader = std.io.bufferedReader(file.reader()); | ||||
|     const reader = buf_reader.reader(); | ||||
| 
 | ||||
|     var file_buf: [8192]u8 = undefined; // intentionally kept small here...this should be used sparingly | ||||
|     var file_fb_allocator = std.heap.FixedBufferAllocator.init(&file_buf); | ||||
|     const file_allocator = file_fb_allocator.allocator(); | ||||
| 
 | ||||
|     var line = std.ArrayList(u8).init(file_allocator); | ||||
|     defer line.deinit(); | ||||
| 
 | ||||
|     const line_writer = line.writer(); | ||||
|     var line_num: usize = 1; | ||||
|     while (reader.streamUntilDelimiter(line_writer, '\n', null)) : (line_num += 1) { | ||||
|         defer line.clearRetainingCapacity(); | ||||
|         var relevant_line = line.items[0 .. std.mem.indexOfScalar(u8, line.items, '#') orelse line.items.len]; | ||||
|         const relevant_line_trimmed = std.mem.trim(u8, relevant_line, " \t"); | ||||
|         var value_iterator = std.mem.splitScalar(u8, relevant_line_trimmed, ','); | ||||
|         if (std.mem.trim(u8, value_iterator.peek().?, " \t").len == 0) continue; | ||||
|         var val_num: usize = 0; | ||||
|         var access_key: []const u8 = undefined; | ||||
|         var secret_key: []const u8 = undefined; | ||||
|         var account_id: []const u8 = undefined; | ||||
|         var existing_key: []const u8 = undefined; | ||||
|         var new_key: []const u8 = undefined; | ||||
|         while (value_iterator.next()) |val| : (val_num += 1) { | ||||
|             const actual_val = std.mem.trim(u8, val, " \t"); | ||||
|             switch (val_num) { | ||||
|                 0 => access_key = actual_val, | ||||
|                 1 => secret_key = actual_val, | ||||
|                 2 => account_id = actual_val, | ||||
|                 3 => existing_key = actual_val, | ||||
|                 4 => new_key = actual_val, | ||||
|                 else => { | ||||
|                     log.err("access_keys.csv Error on line {d}: too many values", .{line_num}); | ||||
|                     return error.TooManyValues; | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
|         if (val_num < 4) { | ||||
|             log.err("access_keys.csv Error on line {d}: too few values", .{line_num}); | ||||
|             return error.TooFewValues; | ||||
|         } | ||||
|         const global_access_key = try allocator.dupe(u8, access_key); | ||||
|         try root_creds.?.put(global_access_key, .{ | ||||
|             .access_key = global_access_key, // we need to copy all these into our global buffer | ||||
|             .secret_key = try allocator.dupe(u8, secret_key), | ||||
|             .session_token = null, | ||||
|             .allocator = NullAllocator.init(), | ||||
|         }); | ||||
|         const global_account_id = try allocator.dupe(u8, account_id); | ||||
|         try root_account_mapping.put(global_access_key, global_account_id); | ||||
|         try Account.root_key_mapping.?.put(global_account_id, try allocator.dupe(u8, existing_key)); | ||||
|         // TODO: key rotation will need another hash map, can be triggered on val_num == 5 | ||||
| 
 | ||||
|     } else |e| switch (e) { | ||||
|         error.EndOfStream => {}, // will this work without \n at the end of file? | ||||
|         else => return e, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const NullAllocator = struct { | ||||
|     const thing = 0; | ||||
|     const vtable = std.mem.Allocator.VTable{ | ||||
|         .alloc = alloc, | ||||
|         .resize = resize, | ||||
|         .free = free, | ||||
|     }; | ||||
| 
 | ||||
|     fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 { | ||||
|         _ = ctx; | ||||
|         _ = len; | ||||
|         _ = ptr_align; | ||||
|         _ = ret_addr; | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool { | ||||
|         _ = ctx; | ||||
|         _ = buf; | ||||
|         _ = buf_align; | ||||
|         _ = new_len; | ||||
|         _ = ret_addr; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void { | ||||
|         _ = ctx; | ||||
|         _ = buf; | ||||
|         _ = buf_align; | ||||
|         _ = ret_addr; | ||||
|     } | ||||
| 
 | ||||
|     pub fn init() std.mem.Allocator { | ||||
|         return .{ | ||||
|             .ptr = @ptrFromInt(@intFromPtr(&thing)), | ||||
|             .vtable = &vtable, | ||||
|         }; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| fn accountForAccessKey(allocator: std.mem.Allocator, access_key: []const u8) ![]const u8 { | ||||
|     _ = allocator; | ||||
|     log.debug("Finding account for access key: '{s}'", .{access_key}); | ||||
|     return "1234"; | ||||
|     // Since this happens after authentication, we can assume our root creds store | ||||
|     // is populated | ||||
|     if (root_account_mapping.get(access_key)) |account| return account; | ||||
|     log.err("Creds not found in store. STS GetAccessKeyInfo call is not yet implemented", .{}); | ||||
|     return error.NotImplemented; | ||||
| } | ||||
| /// Function assumes an authenticated request, so signing.verify must be called | ||||
| /// and returned true before calling this function. If authentication header | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue