From 0a60a47d4d9c25ba41eaf75cbb524823c395282d Mon Sep 17 00:00:00 2001 From: Cheick Keita Date: Tue, 23 Aug 2022 09:14:01 -0700 Subject: [PATCH 1/6] porting the proxy state machine --- .../ApiService/OneFuzzTypes/Model.cs | 2 +- .../ApiService/onefuzzlib/Extension.cs | 16 ++ .../ApiService/onefuzzlib/ProxyOperations.cs | 147 +++++++++++++++++- .../ApiService/onefuzzlib/ReproOperations.cs | 12 +- .../ApiService/onefuzzlib/VmOperations.cs | 15 +- 5 files changed, 177 insertions(+), 15 deletions(-) diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index 855d5bf1de..ac87e38dcb 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -328,7 +328,7 @@ public record InstanceConfig [DefaultValue(InitMethod.DefaultConstructor)] NetworkConfig NetworkConfig, [DefaultValue(InitMethod.DefaultConstructor)] NetworkSecurityGroupConfig ProxyNsgConfig, AzureVmExtensionConfig? Extensions, - string? ProxyVmSku, + string ProxyVmSku, bool AllowPoolManagement = true, IDictionary? ApiAccessRules = null, IDictionary? GroupMembership = null, diff --git a/src/ApiService/ApiService/onefuzzlib/Extension.cs b/src/ApiService/ApiService/onefuzzlib/Extension.cs index dc60a8b73d..88135bb7ec 100644 --- a/src/ApiService/ApiService/onefuzzlib/Extension.cs +++ b/src/ApiService/ApiService/onefuzzlib/Extension.cs @@ -11,6 +11,7 @@ public interface IExtensions { Async.Task> FuzzExtensions(Pool pool, Scaleset scaleset); Async.Task> ReproExtensions(AzureLocation region, Os reproOs, Guid reproId, ReproConfig reproConfig, Container? setupContainer); + Task> ProxyManagerExtensions(string region, Guid proxyId); } public class Extensions : IExtensions { @@ -449,4 +450,19 @@ await _context.Containers.GetFileSasUrl( return extensionsDict; } + public async Task> ProxyManagerExtensions(string region, Guid proxyId) { + var config = await _context.Containers.GetFileSasUrl(new Container("proxy-config"), + $"{region}/{proxyId}/config.json", StorageType.Config, BlobSasPermissions.Read); + + var proxyManager = await _context.Containers.GetFileSasUrl(new Container("tools"), + $"linux/onefuzz-proxy-manager", StorageType.Config, BlobSasPermissions.Read); + + + var baseExtension = + await AgentConfig(region, Os.Linux, AgentMode.Proxy, new List { config, proxyManager }, true); + + var extensions = await GenericExtensions(region, Os.Linux); + extensions.Add(baseExtension); + return extensions; + } } diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs index dbdc4d9c80..2bd9d71109 100644 --- a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs @@ -1,5 +1,7 @@ using System.Threading.Tasks; using ApiService.OneFuzzLib.Orm; +using Azure.ResourceManager.Compute; +using Azure.ResourceManager.Compute.Models; using Azure.Storage.Sas; using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; @@ -8,7 +10,7 @@ namespace Microsoft.OneFuzz.Service; public interface IProxyOperations : IStatefulOrm { Task GetByProxyId(Guid proxyId); - Async.Task SetState(Proxy proxy, VmState state); + Async.Task SetState(Proxy proxy, VmState state); bool IsAlive(Proxy proxy); Async.Task SaveProxyConfig(Proxy proxy); bool IsOutdated(Proxy proxy); @@ -110,14 +112,15 @@ public async Async.Task SaveProxyConfig(Proxy proxy) { } - public async Async.Task SetState(Proxy proxy, VmState state) { + public async Async.Task SetState(Proxy proxy, VmState state) { if (proxy.State == state) { - return; + return proxy; } - await Replace(proxy with { State = state }); - + var newProxy = proxy with { State = state }; + await Replace(newProxy); await _context.Events.SendEvent(new EventProxyStateUpdated(proxy.Region, proxy.ProxyId, proxy.State)); + return newProxy; } @@ -133,4 +136,138 @@ public async Async.Task> GetForwards(Proxy proxy) { } return forwards; } + + public async Async.Task Init(Proxy proxy) { + var config = await _context.ConfigOperations.Fetch(); + var vm = GetVm(proxy, config); + var vmData = await _context.VmOperations.GetVm(vm.Name); + + if (vmData != null) { + if (vmData.ProvisioningState == "Failed") { + return await SetProvisionFailed(proxy, vmData); + } else { + await SaveProxyConfig(proxy); + return await SetState(proxy, VmState.ExtensionsLaunch); + } + } else { + var nsg = new Nsg(proxy.Region, proxy.Region); + var result = await _context.NsgOperations.Create(nsg); + if (!result.IsOk) { + return await SetFailed(proxy, result.ErrorV); + } + + var nsgConfig = config.ProxyNsgConfig; + var result2 = await _context.NsgOperations.SetAllowedSources(nsg, nsgConfig); + + if (!result2.IsOk) { + return await SetFailed(proxy, result2.ErrorV); + } + + var result3 = await _context.VmOperations.Create(vm with { Nsg = nsg }); + + if (!result3.IsOk) { + return await SetFailed(proxy, result3.ErrorV); + } + return proxy; + } + } + + private async System.Threading.Tasks.Task SetProvisionFailed(Proxy proxy, VirtualMachineData vmData) { + var errors = GetErrors(proxy, vmData).ToArray(); + await SetFailed(proxy, new Error(ErrorCode.PROXY_FAILED, errors)); + return proxy; + } + + private async Task SetFailed(Proxy proxy, Error error) { + if (proxy.Error != null) { + return proxy; + } + + _logTracer.Error($"vm failed: {proxy.Region} -{error}"); + await _context.Events.SendEvent(new EventProxyFailed(proxy.Region, proxy.ProxyId, error)); + return await SetState(proxy with { Error = error }, VmState.Stopping); + } + + + private static IEnumerable GetErrors(Proxy proxy, VirtualMachineData vmData) { + var instanceView = vmData.InstanceView; + yield return "provisioning failed"; + if (instanceView is null) { + yield break; + } + + foreach (var status in instanceView.Statuses) { + if (status.Level == StatusLevelTypes.Error) { + yield return $"code:{status.Code} status:{status.DisplayStatus} message:{status.Message}"; + } + } + } + + public static Vm GetVm(Proxy proxy, InstanceConfig config) { + var tags = config.VmssTags; + const string PROXY_IMAGE = "Canonical:UbuntuServer:18.04-LTS:latest"; + return new Vm( + // name should be less than 40 chars otherwise it gets truncated by azure + Name: $"proxy-{proxy.ProxyId:N}", + Region: proxy.Region, + Sku: config.ProxyVmSku, + Image: PROXY_IMAGE, + Auth: proxy.Auth, + Tags: config.VmssTags, + Nsg: null + ); + } + + public async Task ExtensionsLaunch(Proxy proxy) { + var config = await _context.ConfigOperations.Fetch(); + var vm = GetVm(proxy, config); + var vmData = await _context.VmOperations.GetVm(vm.Name); + + if (vmData == null) { + return await SetFailed(proxy, new Error(ErrorCode.PROXY_FAILED, new[] { "azure not able to find vm" })); + } + + if (vmData.ProvisioningState == "Failed") { + return await SetProvisionFailed(proxy, vmData); + } + + var ip = await _context.IpOperations.GetPublicIp(vmData.NetworkProfile.NetworkInterfaces[0].Id); + if (ip == null) { + return proxy; + } + + var newProxy = proxy with { Ip = ip }; + + var extensions = await _context.Extensions.ProxyManagerExtensions(newProxy.Region, newProxy.ProxyId); + var result = await _context.VmOperations.AddExtensions(vm, + extensions + .Select(e => e.GetAsVirtualMachineExtension()) + .ToDictionary(x => x.Item1, x => x.Item2)); + + if (!result.IsOk) { + return await SetFailed(newProxy, result.ErrorV); + } + + return await SetState(newProxy, VmState.Running); + } + + public async Task Stopping(Proxy proxy) { + var config = await _context.ConfigOperations.Fetch(); + var vm = GetVm(proxy, config); + if (!await _context.VmOperations.IsDeleted(vm)) { + _logTracer.Error($"stopping proxy: {proxy.Region}"); + await _context.VmOperations.Delete(vm); + return proxy; + } + + return await Stopped(proxy); + } + + private async Task Stopped(Proxy proxy) { + var stoppedVm = await SetState(proxy, VmState.Stopped); + _logTracer.Info($"removing proxy: {proxy.Region}"); + await _context.Events.SendEvent(new EventProxyDeleted(proxy.Region, proxy.ProxyId)); + await Delete(proxy); + return stoppedVm; + } } diff --git a/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs b/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs index 0d0181337b..b073d001c9 100644 --- a/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ReproOperations.cs @@ -18,7 +18,7 @@ public interface IReproOperations : IStatefulOrm { public Async.Task Stopped(Repro repro); - public Async.Task SetFailed(Repro repro, VirtualMachineResource vmData); + public Async.Task SetFailed(Repro repro, VirtualMachineData vmData); public Async.Task SetError(Repro repro, Error result); @@ -120,7 +120,7 @@ public async Async.Task Init(Repro repro) { var vm = await GetVm(repro, config); var vmData = await _context.VmOperations.GetVm(vm.Name); if (vmData != null) { - if (vmData.Data.ProvisioningState == "Failed") { + if (vmData.ProvisioningState == "Failed") { return await _context.ReproOperations.SetFailed(repro, vmData); } else { var scriptResult = await BuildReproScript(repro); @@ -167,13 +167,13 @@ public async Async.Task ExtensionsLaunch(Repro repro) { ); } - if (vmData.Data.ProvisioningState == "Failed") { + if (vmData.ProvisioningState == "Failed") { return await _context.ReproOperations.SetFailed(repro, vmData); } if (string.IsNullOrEmpty(repro.Ip)) { repro = repro with { - Ip = await _context.IpOperations.GetPublicIp(vmData.Data.NetworkProfile.NetworkInterfaces.First().Id) + Ip = await _context.IpOperations.GetPublicIp(vmData.NetworkProfile.NetworkInterfaces.First().Id) }; } @@ -196,8 +196,8 @@ await _context.ReproOperations.GetSetupContainer(repro) return repro; } - public async Async.Task SetFailed(Repro repro, VirtualMachineResource vmData) { - var errors = (await vmData.InstanceViewAsync()).Value.Statuses + public async Async.Task SetFailed(Repro repro, VirtualMachineData vmData) { + var errors = vmData.InstanceView.Statuses .Where(status => status.Level.HasValue && string.Equals(status.Level?.ToString(), "error", StringComparison.OrdinalIgnoreCase)) .Select(status => $"{status.Code} {status.DisplayStatus} {status.Message}") .ToArray(); diff --git a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs index e9273c759b..d1a22a5e80 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs @@ -11,7 +11,7 @@ public interface IVmOperations { Async.Task HasComponents(string name); - Async.Task GetVm(string name); + Task GetVm(string name); Async.Task Delete(Vm vm); @@ -64,14 +64,23 @@ public async Async.Task HasComponents(string name) { return false; } - public async Async.Task GetVm(string name) { + public async Task GetVm(string name) { // _logTracer.Debug($"getting vm: {name}"); try { - return await _context.Creds.GetResourceGroupResource().GetVirtualMachineAsync(name); + var result = await _context.Creds.GetResourceGroupResource().GetVirtualMachineAsync(name, InstanceViewTypes.InstanceView); + if (result == null) { + return null; + } + if (result.Value.HasData) { + return result.Value.Data; + } + } catch (RequestFailedException) { // _logTracer.Debug($"vm does not exist {ex}); return null; } + + return null; } public async Async.Task Delete(Vm vm) { From 29c821a27132333b4b0c64e899394ca633a3cc8f Mon Sep 17 00:00:00 2001 From: Cheick Keita Date: Wed, 24 Aug 2022 09:40:53 -0700 Subject: [PATCH 2/6] use async version --- src/ApiService/ApiService/onefuzzlib/IpOperations.cs | 6 ++++-- src/ApiService/ApiService/onefuzzlib/VmOperations.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/IpOperations.cs b/src/ApiService/ApiService/onefuzzlib/IpOperations.cs index 5f88c0b8e8..74d93da25a 100644 --- a/src/ApiService/ApiService/onefuzzlib/IpOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/IpOperations.cs @@ -60,12 +60,14 @@ public IpOperations(ILogTracer log, IOnefuzzContext context) { public async System.Threading.Tasks.Task DeleteNic(string resourceGroup, string name) { _logTracer.Info($"deleting nic {resourceGroup}:{name}"); - await _context.Creds.GetResourceGroupResource().GetNetworkInterfaceAsync(name).Result.Value.DeleteAsync(WaitUntil.Started); + var networkInterface = await _context.Creds.GetResourceGroupResource().GetNetworkInterfaceAsync(name); + await networkInterface.Value.DeleteAsync(WaitUntil.Started); } public async System.Threading.Tasks.Task DeleteIp(string resourceGroup, string name) { _logTracer.Info($"deleting ip {resourceGroup}:{name}"); - await _context.Creds.GetResourceGroupResource().GetPublicIPAddressAsync(name).Result.Value.DeleteAsync(WaitUntil.Started); + var publicIpAddressAsync = await _context.Creds.GetResourceGroupResource().GetPublicIPAddressAsync(name); + await publicIpAddressAsync.Value.DeleteAsync(WaitUntil.Started); } public async Task GetScalesetInstanceIp(Guid scalesetId, Guid machineId) { diff --git a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs index d1a22a5e80..f9f8f994c3 100644 --- a/src/ApiService/ApiService/onefuzzlib/VmOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/VmOperations.cs @@ -100,7 +100,7 @@ public async Async.Task DeleteVmComponents(string name, Nsg? nsg) { if (nic != null) { _logTracer.Info($"deleting nic {resourceGroup}:{name}"); if (nic.Data.NetworkSecurityGroup != null && nsg != null) { - await _context.NsgOperations.DissociateNic((Nsg)nsg, nic); + await _context.NsgOperations.DissociateNic(nsg, nic); return false; } await _context.IpOperations.DeleteNic(resourceGroup, name); From 2402181feac21c17f441f7286bc838c0f41077ca Mon Sep 17 00:00:00 2001 From: Cheick Keita Date: Wed, 24 Aug 2022 16:08:53 -0700 Subject: [PATCH 3/6] is used --- src/ApiService/ApiService/Functions/TimerProxy.cs | 3 ++- .../ApiService/onefuzzlib/ProxyOperations.cs | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ApiService/ApiService/Functions/TimerProxy.cs b/src/ApiService/ApiService/Functions/TimerProxy.cs index e6a05a950d..3c7b7bbe3e 100644 --- a/src/ApiService/ApiService/Functions/TimerProxy.cs +++ b/src/ApiService/ApiService/Functions/TimerProxy.cs @@ -26,7 +26,8 @@ public async Async.Task Run([TimerTrigger("00:00:30")] TimerInfo myTimer) { // As this function is called via a timer, this works around a user // requesting to use the proxy while this function is checking if it's // out of date - if (proxy.Outdated) { + if (proxy.Outdated && !(await _context.ProxyOperations.IsUsed(proxy))) { + _logger.Warning($"scaleset-proxy: outdated and not used: {proxy.Region}"); await proxyOperations.SetState(proxy, VmState.Stopping); // If something is "wrong" with a proxy, delete & recreate it } else if (!proxyOperations.IsAlive(proxy)) { diff --git a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs index 2bd9d71109..0e875e6021 100644 --- a/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ProxyOperations.cs @@ -16,6 +16,7 @@ public interface IProxyOperations : IStatefulOrm { bool IsOutdated(Proxy proxy); Async.Task GetOrCreate(string region); + Task IsUsed(Proxy proxy); } public class ProxyOperations : StatefulOrm, IProxyOperations { @@ -58,6 +59,15 @@ public ProxyOperations(ILogTracer log, IOnefuzzContext context) return newProxy; } + public async Task IsUsed(Proxy proxy) { + var forwards = await GetForwards(proxy); + if (forwards.Count == 0) { + _logTracer.Info($"no forwards {proxy.Region}"); + return false; + } + return true; + } + public bool IsAlive(Proxy proxy) { var tenMinutesAgo = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(10); From e2e6c866344dfac230d2d63ad6d9a2c5033cf255 Mon Sep 17 00:00:00 2001 From: Cheick Keita Date: Wed, 24 Aug 2022 19:11:31 -0700 Subject: [PATCH 4/6] rename base state to state --- src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs | 3 +-- src/ApiService/ApiService/onefuzzlib/orm/Orm.cs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs b/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs index f19cdeaaa0..03e17dfced 100644 --- a/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs +++ b/src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs @@ -17,8 +17,7 @@ public abstract record EntityBase { public static string NewSortedKey => $"{DateTimeOffset.MaxValue.Ticks - DateTimeOffset.UtcNow.Ticks}"; } -public abstract record StatefulEntityBase([property: JsonIgnore] T BaseState) : EntityBase() where T : Enum; - +public abstract record StatefulEntityBase(T State) : EntityBase() where T : Enum; /// How the value is populated diff --git a/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs b/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs index 078fdc414c..42adc66082 100644 --- a/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs +++ b/src/ApiService/ApiService/onefuzzlib/orm/Orm.cs @@ -205,7 +205,7 @@ public StatefulOrm(ILogTracer logTracer, IOnefuzzContext context) : base(logTrac /// /// public async Async.Task ProcessStateUpdate(T entity) { - TState state = entity.BaseState; + TState state = entity.State; var func = GetType().GetMethod(state.ToString()) switch { null => null, MethodInfo info => info.CreateDelegate(this) @@ -227,13 +227,13 @@ public StatefulOrm(ILogTracer logTracer, IOnefuzzContext context) : base(logTrac /// public async Async.Task ProcessStateUpdates(T entity, int MaxUpdates = 5) { for (int i = 0; i < MaxUpdates; i++) { - var state = entity.BaseState; + var state = entity.State; var newEntity = await ProcessStateUpdate(entity); if (newEntity == null) return null; - if (newEntity.BaseState.Equals(state)) { + if (newEntity.State.Equals(state)) { return newEntity; } } From f97f0f45bd0759666705c1b7661407f5adc549b5 Mon Sep 17 00:00:00 2001 From: Cheick Keita Date: Wed, 24 Aug 2022 21:16:49 -0700 Subject: [PATCH 5/6] fix auth fix extension path --- .../ApiService/Functions/Scaleset.cs | 45 +------------------ src/ApiService/ApiService/onefuzzlib/Auth.cs | 43 +++++++++++++++--- .../ApiService/onefuzzlib/Extension.cs | 2 +- .../ApiService/onefuzzlib/IpOperations.cs | 9 +++- 4 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/ApiService/ApiService/Functions/Scaleset.cs b/src/ApiService/ApiService/Functions/Scaleset.cs index e2bb3ad403..148a649b96 100644 --- a/src/ApiService/ApiService/Functions/Scaleset.cs +++ b/src/ApiService/ApiService/Functions/Scaleset.cs @@ -1,7 +1,4 @@ -using System.Buffers.Binary; -using System.Diagnostics; -using System.Security.Cryptography; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; @@ -117,7 +114,7 @@ private async Task Post(HttpRequestData req) { ScalesetId: Guid.NewGuid(), State: ScalesetState.Init, NeedsConfigUpdate: false, - Auth: GenerateAuthentication(), + Auth: Auth.BuildAuth(), PoolName: create.PoolName, VmSku: create.VmSku, Image: create.Image, @@ -155,44 +152,6 @@ private async Task Post(HttpRequestData req) { return await RequestHandling.Ok(req, ScalesetResponse.ForScaleset(scaleset)); } - private static Authentication GenerateAuthentication() { - using var rsa = RSA.Create(2048); - var privateKey = rsa.ExportRSAPrivateKey(); - var formattedPrivateKey = $"-----BEGIN RSA PRIVATE KEY-----\n{Convert.ToBase64String(privateKey)}\n-----END RSA PRIVATE KEY-----\n"; - - var publicKey = BuildPublicKey(rsa); - var formattedPublicKey = $"ssh-rsa {Convert.ToBase64String(publicKey)} onefuzz-generated-key"; - - return new Authentication( - Password: Guid.NewGuid().ToString(), - PublicKey: formattedPublicKey, - PrivateKey: formattedPrivateKey); - } - - private static ReadOnlySpan SSHRSABytes => new byte[] { (byte)'s', (byte)'s', (byte)'h', (byte)'-', (byte)'r', (byte)'s', (byte)'a' }; - - private static byte[] BuildPublicKey(RSA rsa) { - static Span WriteLengthPrefixedBytes(ReadOnlySpan src, Span dest) { - BinaryPrimitives.WriteInt32BigEndian(dest, src.Length); - dest = dest[sizeof(int)..]; - src.CopyTo(dest); - return dest[src.Length..]; - } - - var parameters = rsa.ExportParameters(includePrivateParameters: false); - - // public key format is "ssh-rsa", exponent, modulus, all written - // as (big-endian) length-prefixed bytes - var result = new byte[sizeof(int) + SSHRSABytes.Length + sizeof(int) + parameters.Modulus!.Length + sizeof(int) + parameters.Exponent!.Length]; - var spanResult = result.AsSpan(); - spanResult = WriteLengthPrefixedBytes(SSHRSABytes, spanResult); - spanResult = WriteLengthPrefixedBytes(parameters.Exponent, spanResult); - spanResult = WriteLengthPrefixedBytes(parameters.Modulus, spanResult); - Debug.Assert(spanResult.Length == 0); - - return result; - } - private async Task Patch(HttpRequestData req) { var request = await RequestHandling.ParseRequest(req); if (!request.IsOk) { diff --git a/src/ApiService/ApiService/onefuzzlib/Auth.cs b/src/ApiService/ApiService/onefuzzlib/Auth.cs index c6779854bf..517835cafa 100644 --- a/src/ApiService/ApiService/onefuzzlib/Auth.cs +++ b/src/ApiService/ApiService/onefuzzlib/Auth.cs @@ -1,14 +1,45 @@ namespace Microsoft.OneFuzz.Service; +using System.Buffers.Binary; +using System.Diagnostics; using System.Security.Cryptography; public class Auth { + + private static ReadOnlySpan SSHRSABytes => new byte[] { (byte)'s', (byte)'s', (byte)'h', (byte)'-', (byte)'r', (byte)'s', (byte)'a' }; + + private static byte[] BuildPublicKey(RSA rsa) { + static Span WriteLengthPrefixedBytes(ReadOnlySpan src, Span dest) { + BinaryPrimitives.WriteInt32BigEndian(dest, src.Length); + dest = dest[sizeof(int)..]; + src.CopyTo(dest); + return dest[src.Length..]; + } + + var parameters = rsa.ExportParameters(includePrivateParameters: false); + + // public key format is "ssh-rsa", exponent, modulus, all written + // as (big-endian) length-prefixed bytes + var result = new byte[sizeof(int) + SSHRSABytes.Length + sizeof(int) + parameters.Modulus!.Length + sizeof(int) + parameters.Exponent!.Length]; + var spanResult = result.AsSpan(); + spanResult = WriteLengthPrefixedBytes(SSHRSABytes, spanResult); + spanResult = WriteLengthPrefixedBytes(parameters.Exponent, spanResult); + spanResult = WriteLengthPrefixedBytes(parameters.Modulus, spanResult); + Debug.Assert(spanResult.Length == 0); + + return result; + } public static Authentication BuildAuth() { - var rsa = RSA.Create(2048); - string header = "-----BEGIN RSA PRIVATE KEY-----"; - string footer = "-----END RSA PRIVATE KEY-----"; - var privateKey = $"{header}\n{Convert.ToBase64String(rsa.ExportRSAPrivateKey())}\n{footer}"; - var publiceKey = $"{header}\n{Convert.ToBase64String(rsa.ExportRSAPublicKey())}\n{footer}"; - return new Authentication(Guid.NewGuid().ToString(), publiceKey, privateKey); + using var rsa = RSA.Create(2048); + var privateKey = rsa.ExportRSAPrivateKey(); + var formattedPrivateKey = $"-----BEGIN RSA PRIVATE KEY-----\n{Convert.ToBase64String(privateKey)}\n-----END RSA PRIVATE KEY-----\n"; + + var publicKey = BuildPublicKey(rsa); + var formattedPublicKey = $"ssh-rsa {Convert.ToBase64String(publicKey)} onefuzz-generated-key"; + + return new Authentication( + Password: Guid.NewGuid().ToString(), + PublicKey: formattedPublicKey, + PrivateKey: formattedPrivateKey); } } diff --git a/src/ApiService/ApiService/onefuzzlib/Extension.cs b/src/ApiService/ApiService/onefuzzlib/Extension.cs index 88135bb7ec..9aa399b2d3 100644 --- a/src/ApiService/ApiService/onefuzzlib/Extension.cs +++ b/src/ApiService/ApiService/onefuzzlib/Extension.cs @@ -451,7 +451,7 @@ await _context.Containers.GetFileSasUrl( } public async Task> ProxyManagerExtensions(string region, Guid proxyId) { - var config = await _context.Containers.GetFileSasUrl(new Container("proxy-config"), + var config = await _context.Containers.GetFileSasUrl(new Container("proxy-configs"), $"{region}/{proxyId}/config.json", StorageType.Config, BlobSasPermissions.Read); var proxyManager = await _context.Containers.GetFileSasUrl(new Container("tools"), diff --git a/src/ApiService/ApiService/onefuzzlib/IpOperations.cs b/src/ApiService/ApiService/onefuzzlib/IpOperations.cs index 74d93da25a..5f3d47b865 100644 --- a/src/ApiService/ApiService/onefuzzlib/IpOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/IpOperations.cs @@ -61,7 +61,14 @@ public IpOperations(ILogTracer log, IOnefuzzContext context) { public async System.Threading.Tasks.Task DeleteNic(string resourceGroup, string name) { _logTracer.Info($"deleting nic {resourceGroup}:{name}"); var networkInterface = await _context.Creds.GetResourceGroupResource().GetNetworkInterfaceAsync(name); - await networkInterface.Value.DeleteAsync(WaitUntil.Started); + try { + await networkInterface.Value.DeleteAsync(WaitUntil.Started); + } catch (RequestFailedException ex) { + if (ex.ErrorCode != "NicReservedForAnotherVm") { + throw; + } + _logTracer.Warning($"unable to delete nic {resourceGroup}:{name} {ex.Message}"); + } } public async System.Threading.Tasks.Task DeleteIp(string resourceGroup, string name) { From 5e9a66176b6436de5b6069a3303209ebebfcfb0b Mon Sep 17 00:00:00 2001 From: Cheick Keita Date: Thu, 25 Aug 2022 05:40:09 -0700 Subject: [PATCH 6/6] ignore log info in check logs --- src/integration-tests/integration-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integration-tests/integration-test.py b/src/integration-tests/integration-test.py index 87bc1c7251..cfbeb063bb 100755 --- a/src/integration-tests/integration-test.py +++ b/src/integration-tests/integration-test.py @@ -859,7 +859,7 @@ def check_logs_for_errors(self) -> None: break # ignore logging.info coming from Azure Functions - if entry.get("customDimensions", {}).get("LogLevel") == "Information": + if entry.get("customDimensions", {}).get("LogLevel") == "Information" or entry.get("severityLevel") <= 2: continue # ignore warnings coming from the rust code, only be concerned