From f59b7cca68b931b268eb73a00420539608b3e832 Mon Sep 17 00:00:00 2001 From: Jared Goodwin Date: Thu, 22 Feb 2024 11:25:58 -0800 Subject: [PATCH] Fix auto-update and mac install. --- Agent/Program.cs | 1 + Agent/Services/MacOS/AppLauncherMac.cs | 6 +- Agent/Services/Windows/UpdaterWin.cs | 14 ++--- Server/API/ClientDownloadsController.cs | 4 +- Server/Components/Pages/Downloads.razor | 32 +++++++--- Server/Options/ApplicationOptions.cs | 1 + Server/Program.cs | 15 +++-- Server/Server.csproj | 1 + Server/appsettings.json | 3 +- Server/wwwroot/Content/Install-MacOS-arm64.sh | 18 +++--- Server/wwwroot/Content/Install-MacOS-x64.sh | 17 +++--- Server/wwwroot/Content/Install-Remotely.ps1 | 58 +++++++++++++++---- Shared/Services/ElevationDetectorMac.cs | 14 +++++ Shared/Services/FileLoggerDefaults.cs | 2 +- docker-compose/docker-compose.yml | 19 +++++- submodules/Immense.RemoteControl | 2 +- 16 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 Shared/Services/ElevationDetectorMac.cs diff --git a/Agent/Program.cs b/Agent/Program.cs index af954741b..82b8800b2 100644 --- a/Agent/Program.cs +++ b/Agent/Program.cs @@ -123,6 +123,7 @@ private static void RegisterServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); } else { diff --git a/Agent/Services/MacOS/AppLauncherMac.cs b/Agent/Services/MacOS/AppLauncherMac.cs index 238ba8615..e5d1a9284 100644 --- a/Agent/Services/MacOS/AppLauncherMac.cs +++ b/Agent/Services/MacOS/AppLauncherMac.cs @@ -10,17 +10,17 @@ public class AppLauncherMac : IAppLauncher public async Task LaunchChatService(string pipeName, string userConnectionId, string requesterName, string orgName, string orgId, HubConnection hubConnection) { - await hubConnection.SendAsync("DisplayMessage", "Feature under development.", "Feature is under development.", "bg-warning", userConnectionId); + await hubConnection.SendAsync("DisplayMessage", "Feature under development.", "Currently unsupported", "bg-warning", userConnectionId); return 0; } public async Task LaunchRemoteControl(int targetSessionId, string sessionId, string accessKey, string userConnectionId, string requesterName, string orgName, string orgId, HubConnection hubConnection) { - await hubConnection.SendAsync("DisplayMessage", "Feature under development.", "Feature is under development.", "bg-warning", userConnectionId); + await hubConnection.SendAsync("DisplayMessage", "Feature under development.", "Currently unsupported", "bg-warning", userConnectionId); } public async Task RestartScreenCaster(string[] viewerIds, string sessionId, string accessKey, string userConnectionId, string requesterName, string orgName, string orgId, HubConnection hubConnection, int targetSessionID = -1) { - await hubConnection.SendAsync("DisplayMessage", "Feature under development.", "Feature is under development.", "bg-warning", userConnectionId); + await hubConnection.SendAsync("DisplayMessage", "Feature under development.", "Currently unsupported", "bg-warning", userConnectionId); } } diff --git a/Agent/Services/Windows/UpdaterWin.cs b/Agent/Services/Windows/UpdaterWin.cs index cef171519..3d76bec47 100644 --- a/Agent/Services/Windows/UpdaterWin.cs +++ b/Agent/Services/Windows/UpdaterWin.cs @@ -130,25 +130,23 @@ public async Task InstallLatestVersion() var zipPath = Path.Combine(Path.GetTempPath(), "RemotelyUpdate.zip"); - var installerPath = Path.Combine(Path.GetTempPath(), "Remotely_Installer.exe"); + var installerPath = Path.Combine(Path.GetTempPath(), "Install-Remotely.ps1"); var platform = Environment.Is64BitOperatingSystem ? "x64" : "x86"; await _updateDownloader.DownloadFile( - $"{serverUrl}/Content/Remotely_Installer.exe", + $"{serverUrl}/Content/Install-Remotely.ps1", installerPath); await _updateDownloader.DownloadFile( $"{serverUrl}/api/AgentUpdate/DownloadPackage/win-{platform}", zipPath); - foreach (var proc in Process.GetProcessesByName("Remotely_Installer")) - { - proc.Kill(); - } - _logger.LogInformation("Launching installer to perform update."); - Process.Start(installerPath, $"-install -quiet -path {zipPath} -serverurl {serverUrl} -organizationid {connectionInfo.OrganizationID}"); + Process.Start( + "powershell.exe", + $"-ExecutionPolicy Bypass -File \"{installerPath}\" -Path \"{zipPath}\" " + + $"-OrganizationId {connectionInfo.OrganizationID} -ServerUrl {connectionInfo.Host}"); } catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout) { diff --git a/Server/API/ClientDownloadsController.cs b/Server/API/ClientDownloadsController.cs index 1967a4854..65d1a2e35 100644 --- a/Server/API/ClientDownloadsController.cs +++ b/Server/API/ClientDownloadsController.cs @@ -200,8 +200,8 @@ private async Task GetInstallFile(string organizationId, string p return NotFound(); } - fileContents[hostIndex] = $"$HostName = \"{effectiveScheme}://{Request.Host}\""; - fileContents[orgIndex] = $"$Organization = \"{organizationId}\""; + fileContents[hostIndex] = $"[string]$HostName = \"{effectiveScheme}://{Request.Host}\""; + fileContents[orgIndex] = $"[string]$Organization = \"{organizationId}\""; var fileBytes = Encoding.UTF8.GetBytes(string.Join("\n", fileContents)); return File(fileBytes, "application/octet-stream", "Install-Remotely.ps1"); diff --git a/Server/Components/Pages/Downloads.razor b/Server/Components/Pages/Downloads.razor index 9463481df..7b9678efb 100644 --- a/Server/Components/Pages/Downloads.razor +++ b/Server/Components/Pages/Downloads.razor @@ -146,13 +146,6 @@ macOS x64 Files Only

- @*macOS arm64 (11.01) -

- macOS arm64 Bash Installer -
- macOS arm64 Files Only -

*@ -

Example Install:
@@ -169,6 +162,31 @@ sudo [path]/Install-MacOS-x64.sh --uninstall

+ +
+ macOS arm64 +

+ macOS arm64 Bash Installer +
+ macOS arm64 Files Only +

+ +

+

Example Install:
+ + sudo [path]/Install-MacOS-arm64.sh +

+

+

Example Local Install:
+ + sudo [path]/Install-MacOS-arm64.sh --path [path]/Remotely-MacOS-arm64.zip +

+

+

Example Uninstall:
+ + sudo [path]/Install-MacOS-arm64.sh --uninstall +

+
} diff --git a/Server/Options/ApplicationOptions.cs b/Server/Options/ApplicationOptions.cs index 1a11cc3b8..ed5d39a3f 100644 --- a/Server/Options/ApplicationOptions.cs +++ b/Server/Options/ApplicationOptions.cs @@ -4,4 +4,5 @@ public class ApplicationOptions { public const string SectionKey = "ApplicationOptions"; public string DbProvider { get; set; } = "SQLite"; + public string? DockerGatewayIp { get; set; } } diff --git a/Server/Program.cs b/Server/Program.cs index da91809d4..b75498453 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -36,6 +36,10 @@ services.Configure( configuration.GetSection(ApplicationOptions.SectionKey)); +var appOptions = configuration + .GetSection(ApplicationOptions.SectionKey) + .Get(); + services .AddRazorComponents() .AddInteractiveServerComponents(); @@ -47,7 +51,7 @@ services.AddScoped(); services.AddScoped(); -var dbProvider = configuration["ApplicationOptions:DbProvider"]?.ToLower(); +var dbProvider = appOptions?.DbProvider?.ToLower(); switch (dbProvider) { @@ -168,7 +172,10 @@ options.ForwardLimit = null; // Default Docker host. We want to allow forwarded headers from this address. - options.KnownProxies.Add(IPAddress.Parse("172.17.0.1")); + if (IPAddress.TryParse(appOptions?.DockerGatewayIp, out var dockerGatewayIp)) + { + options.KnownProxies.Add(dockerGatewayIp); + } if (settings.KnownProxies is { Count: >0 } knownProxies) { @@ -257,6 +264,8 @@ var app = builder.Build(); +app.UseForwardedHeaders(); + app.UseRateLimiter(); if (settings.UseHttpLogging) @@ -264,8 +273,6 @@ app.UseHttpLogging(); } -app.UseForwardedHeaders(); - if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); diff --git a/Server/Server.csproj b/Server/Server.csproj index 76f3c56b3..f5d1e7111 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -29,6 +29,7 @@ + diff --git a/Server/appsettings.json b/Server/appsettings.json index b752a55aa..d5a5d8632 100644 --- a/Server/appsettings.json +++ b/Server/appsettings.json @@ -20,6 +20,7 @@ } }, "ApplicationOptions": { - "DbProvider": "SQLite" + "DbProvider": "SQLite", + "DockerGatewayIp": "172.28.0.1" } } diff --git a/Server/wwwroot/Content/Install-MacOS-arm64.sh b/Server/wwwroot/Content/Install-MacOS-arm64.sh index a58534187..43547654c 100644 --- a/Server/wwwroot/Content/Install-MacOS-arm64.sh +++ b/Server/wwwroot/Content/Install-MacOS-arm64.sh @@ -16,7 +16,7 @@ ArgLength=${#Args[@]} for (( i=0; i<${ArgLength}; i+=2 )); do if [ "${Args[$i]}" = "--uninstall" ]; then - launchctl unload -w /Library/LaunchDaemons/remotely-agent.plist + sudo launchctl bootout system /Library/LaunchDaemons/remotely-agent.plist rm -r -f $InstallDir/ rm -f /Library/LaunchDaemons/remotely-agent.plist exit @@ -41,9 +41,6 @@ Owner=$(ls -l /usr/local/bin/brew | awk '{print $3}') su - $Owner -c "brew update" -# Install .NET Runtime -su - $Owner -c "brew install --cask dotnet" - # Install other dependencies su - $Owner -c "brew install curl" su - $Owner -c "brew install jq" @@ -71,9 +68,10 @@ else rm -f "$UpdatePackagePath" fi -unzip -o $InstallDir/Remotely-MacOS-arm64.zip +unzip -o $InstallDir/Remotely-MacOS-arm64.zip -d $InstallDir rm -f $InstallDir/Remotely-MacOS-arm64.zip - +chmod +x $InstallDir/Remotely_Agent +chmod +x $InstallDir/Desktop/Remotely_Desktop connectionInfo="{ \"DeviceID\":\"$GUID\", @@ -95,8 +93,7 @@ plistFile=" com.translucency.remotely-agent ProgramArguments - /usr/local/bin/dotnet - $InstallDir/Remotely_Agent.dll + $InstallDir/Remotely_Agent KeepAlive @@ -104,5 +101,6 @@ plistFile=" " echo "$plistFile" > "/Library/LaunchDaemons/remotely-agent.plist" -launchctl load -w /Library/LaunchDaemons/remotely-agent.plist -launchctl kickstart -k system/com.translucency.remotely-agent \ No newline at end of file + +sudo launchctl bootstrap system /Library/LaunchDaemons/remotely-agent.plist +sudo launchctl kickstart -k system/com.translucency.remotely-agent \ No newline at end of file diff --git a/Server/wwwroot/Content/Install-MacOS-x64.sh b/Server/wwwroot/Content/Install-MacOS-x64.sh index 74794cd9f..56c6c2ec9 100644 --- a/Server/wwwroot/Content/Install-MacOS-x64.sh +++ b/Server/wwwroot/Content/Install-MacOS-x64.sh @@ -16,7 +16,7 @@ ArgLength=${#Args[@]} for (( i=0; i<${ArgLength}; i+=2 )); do if [ "${Args[$i]}" = "--uninstall" ]; then - launchctl unload -w /Library/LaunchDaemons/remotely-agent.plist + sudo launchctl bootout system /Library/LaunchDaemons/remotely-agent.plist rm -r -f $InstallDir/ rm -f /Library/LaunchDaemons/remotely-agent.plist exit @@ -41,9 +41,6 @@ Owner=$(ls -l /usr/local/bin/brew | awk '{print $3}') su - $Owner -c "brew update" -# Install .NET Runtime -su - $Owner -c "brew install --cask dotnet" - # Install other dependencies su - $Owner -c "brew install curl" su - $Owner -c "brew install jq" @@ -71,9 +68,10 @@ else rm -f "$UpdatePackagePath" fi -unzip -o $InstallDir/Remotely-MacOS-x64.zip +unzip -o $InstallDir/Remotely-MacOS-x64.zip -d $InstallDir rm -f $InstallDir/Remotely-MacOS-x64.zip - +chmod +x $InstallDir/Remotely_Agent +chmod +x $InstallDir/Desktop/Remotely_Desktop connectionInfo="{ \"DeviceID\":\"$GUID\", @@ -95,8 +93,7 @@ plistFile=" com.translucency.remotely-agent ProgramArguments - /usr/local/bin/dotnet - $InstallDir/Remotely_Agent.dll + $InstallDir/Remotely_Agent KeepAlive @@ -104,5 +101,5 @@ plistFile=" " echo "$plistFile" > "/Library/LaunchDaemons/remotely-agent.plist" -launchctl load -w /Library/LaunchDaemons/remotely-agent.plist -launchctl kickstart -k system/com.translucency.remotely-agent \ No newline at end of file +sudo launchctl bootstrap system /Library/LaunchDaemons/remotely-agent.plist +sudo launchctl kickstart -k system/com.translucency.remotely-agent \ No newline at end of file diff --git a/Server/wwwroot/Content/Install-Remotely.ps1 b/Server/wwwroot/Content/Install-Remotely.ps1 index 078ea2641..150fe9c7a 100644 --- a/Server/wwwroot/Content/Install-Remotely.ps1 +++ b/Server/wwwroot/Content/Install-Remotely.ps1 @@ -12,13 +12,24 @@ param ( [string]$DeviceAlias, [string]$DeviceGroup, [string]$Path, + [string]$OrganizationId, + [string]$ServerUrl, [switch]$Uninstall ) [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 $LogPath = "$env:TEMP\Remotely_Install.txt" + [string]$HostName = $null +if ($ServerUrl) { + $HostName = $ServerUrl +} + [string]$Organization = $null +if ($OrganizationId) { + $Organization = $OrganizationId +} + $ConnectionInfo = $null if ([System.Environment]::Is64BitOperatingSystem){ @@ -47,17 +58,35 @@ function Is-Administrator() { function Run-StartupChecks { - if ($HostName -eq $null -or $Organization -eq $null) { + if (!$HostName -or !$Organization) { Write-Log "Required parameters are missing. Please try downloading the installer again." Do-Exit } - if ((Is-Administrator) -eq $false) { + if (!(Is-Administrator)) { Write-Log -Message "Install script requires elevation. Attempting to self-elevate..." Start-Sleep -Seconds 3 - $param = "-f `"$($MyInvocation.ScriptName)`"" - Start-Process -FilePath powershell.exe -ArgumentList "-DeviceAlias $DeviceAlias -DeviceGroup $DeviceGroup -Path $Path" -Verb RunAs + $Params = "-File `"$($MyInvocation.ScriptName)`""; + if ($OrganizationId) { + $Params += " -OrganizationId $OrganizationId" + } + if ($ServerUrl) { + $Params += " -ServerUrl $ServerUrl" + } + if ($DeviceAlias) { + $Params += " -DeviceAlias $DeviceAlias" + } + if ($DeviceGroup) { + $Params += " -DeviceGroup $DeviceGroup" + } + if ($Path) { + $Params += " -Path `"$Path`"" + } + if ($Uninstall) { + $Params += " -Uninstall" + } + Start-Process -FilePath powershell.exe -ArgumentList $Params -Verb RunAs exit } } @@ -75,9 +104,15 @@ function Uninstall-Remotely { } function Install-Remotely { + $HeadResponse = Invoke-WebRequest -Uri "$HostName/Content/Remotely-Win-$Platform.zip" -Method Head -UseBasicParsing + $ETag = $HeadResponse.Headers["ETag"] + if (!$Etag) { + Write-Host "Failed to get ETag from server. Aborting install." + } + if ((Test-Path -Path "$InstallPath") -and (Test-Path -Path "$InstallPath\ConnectionInfo.json")) { $ConnectionInfo = Get-Content -Path "$InstallPath\ConnectionInfo.json" | ConvertFrom-Json - if ($ConnectionInfo -ne $null) { + if ($ConnectionInfo) { $ConnectionInfo.Host = $HostName $ConnectionInfo.OrganizationID = $Organization $ConnectionInfo.ServerVerificationToken = "" @@ -87,7 +122,7 @@ function Install-Remotely { New-Item -ItemType Directory -Path "$InstallPath" -Force } - if ($ConnectionInfo -eq $null) { + if (!$ConnectionInfo) { $ConnectionInfo = @{ DeviceID = (New-Guid).ToString(); Host = $HostName; @@ -108,7 +143,7 @@ function Install-Remotely { else { $ProgressPreference = 'SilentlyContinue' Write-Log "Downloading client..." - Invoke-WebRequest -Uri "$HostName/Content/Remotely-Win-$Platform.zip" -OutFile "$env:TEMP\Remotely-Win-$Platform.zip" + Invoke-WebRequest -Uri "$HostName/Content/Remotely-Win-$Platform.zip" -OutFile "$env:TEMP\Remotely-Win-$Platform.zip" -UseBasicParsing $ProgressPreference = 'Continue' } @@ -124,6 +159,8 @@ function Install-Remotely { New-Item -ItemType File -Path "$InstallPath\ConnectionInfo.json" -Value (ConvertTo-Json -InputObject $ConnectionInfo) -Force + New-Item -ItemType File -Path "$InstallPath\etag.txt" -Value $ETag -Force + if ($DeviceAlias -or $DeviceGroup) { $DeviceSetupOptions = @{ DeviceAlias = $DeviceAlias; @@ -131,8 +168,9 @@ function Install-Remotely { OrganizationID = $Organization; DeviceID = $ConnectionInfo.DeviceID; } - - Invoke-RestMethod -Method Post -ContentType "application/json" -Uri "$HostName/api/devices" -Body $DeviceSetupOptions -UseBasicParsing + + $Body = $DeviceSetupOptions | ConvertTo-Json + Invoke-RestMethod -Method Post -ContentType "application/json" -Uri "$HostName/api/devices" -Body $Body } New-Service -Name "Remotely_Service" -BinaryPathName "$InstallPath\Remotely_Agent.exe" -DisplayName "Remotely Service" -StartupType Automatic -Description "Background service that maintains a connection to the Remotely server. The service is used for remote support and maintenance by this computer's administrators." @@ -144,7 +182,6 @@ try { Run-StartupChecks Write-Log "Install/uninstall logs are being written to `"$LogPath`"" - Write-Log if ($Uninstall) { Write-Log "Uninstall started." @@ -154,7 +191,6 @@ try { } else { Write-Log "Install started." - Write-Log Install-Remotely Write-Log "Install completed." exit diff --git a/Shared/Services/ElevationDetectorMac.cs b/Shared/Services/ElevationDetectorMac.cs new file mode 100644 index 000000000..9408c5698 --- /dev/null +++ b/Shared/Services/ElevationDetectorMac.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Remotely.Shared.Services; + +public class ElevationDetectorMac : IElevationDetector +{ + [DllImport("libc", SetLastError = true)] + private static extern uint geteuid(); + + public bool IsElevated() + { + return geteuid() == 0; + } +} diff --git a/Shared/Services/FileLoggerDefaults.cs b/Shared/Services/FileLoggerDefaults.cs index f4d68d274..5cdbcf433 100644 --- a/Shared/Services/FileLoggerDefaults.cs +++ b/Shared/Services/FileLoggerDefaults.cs @@ -31,7 +31,7 @@ public static string LogsFolderPath return logsPath; } - if (OperatingSystem.IsLinux()) + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { if (EnvironmentHelper.IsDebug) { diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 872c0771b..1eb4e5153 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -1,13 +1,25 @@ version: '3.4' +networks: + remotely: + name: remotely-network + driver: bridge + ipam: + config: + - subnet: 172.28.0.0/16 + gateway: 172.28.0.1 + services: remotely: image: immybot/remotely:latest + container_name: remotely volumes: - /var/www/remotely:/app/AppData restart: unless-stopped ports: - "5000:5000" + networks: + - remotely environment: - ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_HTTP_PORTS=5000 @@ -16,9 +28,14 @@ services: # Values for DbProvider are SQLite, SQLServer, and PostgreSQL. - Remotely_ApplicationOptions__DbProvider=SQLite + # The Docker gateway will be used in Forwarded Headers, which is necessary to + # to correctly determine request hostname and scheme within the app. If you change + # the network config, you must update this value as well. + - Remotely_ApplicationOptions__DockerGateway=172.28.0.1 # This path shouldn't be changed. It points to the Docker volume. - Remotely_ConnectionStrings__SQLite=Data Source=/app/AppData/Remotely.db # If using SQL Server, change the connection string to point to your SQL Server instance. - Remotely_ConnectionStrings__SQLServer=Server=(localdb)\\mssqllocaldb;Database=Remotely-Server-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true # If using PostgreSQL, change the connection string to point to your PostgreSQL instance. - - Remotely_ConnectionStrings__PostgreSQL=Server=Host=localhost;Database=Remotely;Username=postgres; \ No newline at end of file + - Remotely_ConnectionStrings__PostgreSQL=Server=Host=localhost;Database=Remotely;Username=postgres; + \ No newline at end of file diff --git a/submodules/Immense.RemoteControl b/submodules/Immense.RemoteControl index 49384c61a..986742db0 160000 --- a/submodules/Immense.RemoteControl +++ b/submodules/Immense.RemoteControl @@ -1 +1 @@ -Subproject commit 49384c61a5b9f8bbd4fb42ae877e38e789904236 +Subproject commit 986742db0d9aa54ba35531b29519b2372e6423aa