You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何远程重启非本地网络/非AD域的计算机?

跨非域/公网环境实现远程重启的可行方案

你的核心问题在于:Windows原生的远程重启工具(比如shutdown /m、PsShutdown、PsExec)依赖SMB/WinRM协议,这些协议在公网或非信任网络中通常会被防火墙/运营商封禁,而且非域环境下的身份验证也容易触发安全拦截。结合你已经在通过HTTP端口检测机器状态的场景,最靠谱的方案是在目标机器上部署一个带认证的轻量级HTTP服务,专门处理重启请求——毕竟HTTP/HTTPS是公网环境下最容易穿透防火墙的协议。

下面是具体的实现步骤和代码修改建议:

步骤1:在目标机器上部署重启服务

我们用C#写一个Windows服务,以管理员权限运行,提供一个带API密钥认证的HTTP接口来触发重启。这个服务会监听指定端口(比如你已经在检测的8080),收到合法请求后调用系统关机命令。

重启服务代码示例

using System;
using System.ServiceProcess;
using System.Web.Http;
using System.Net.Http;
using System.Diagnostics;
using Newtonsoft.Json;

namespace RemoteRebootService
{
    // 处理重启请求的API控制器
    public class RebootController : ApiController
    {
        [HttpPost]
        [Route("api/reboot")]
        public IHttpActionResult TriggerReboot([FromBody] RebootRequest request)
        {
            // 用API密钥做简单认证,务必替换成你自己的复杂密钥
            if (request.ApiKey != "YourSuperSecureRebootKey_123!")
            {
                return Unauthorized();
            }

            try
            {
                // 调用shutdown命令,/r=重启,/t 0=立即执行,/f=强制关闭所有程序
                var processStartInfo = new ProcessStartInfo
                {
                    FileName = "shutdown.exe",
                    Arguments = "/r /t 0 /f",
                    Verb = "runas", // 确保以管理员权限执行
                    CreateNoWindow = true,
                    WindowStyle = ProcessWindowStyle.Hidden
                };
                Process.Start(processStartInfo);
                return Ok("重启请求已执行");
            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }
        }
    }

    // 请求参数模型
    public class RebootRequest
    {
        public string ApiKey { get; set; }
    }

    // Windows服务宿主
    public class RebootWindowsService : ServiceBase
    {
        private HttpSelfHostServer _httpServer;

        protected override void OnStart(string[] args)
        {
            // 监听所有网卡的8080端口,可根据需要修改端口
            var config = new HttpSelfHostConfiguration("http://+:8080");
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}",
                defaults: new { action = RouteParameter.Optional }
            );
            _httpServer = new HttpSelfHostServer(config);
            _httpServer.OpenAsync().Wait();
        }

        protected override void OnStop()
        {
            _httpServer.CloseAsync().Wait();
        }
    }
}

部署服务注意事项

  1. 编译成Windows服务后,用installutil.exe安装(需要管理员权限):
    installutil.exe RemoteRebootService.exe
    
  2. 在服务管理器中,将该服务的运行账户设置为本地系统账户(默认拥有管理员权限,能执行重启命令)。
  3. 在目标机器的防火墙中,开放8080端口的入站HTTP请求;如果是公网机器,还要确保路由器做好端口映射,把公网端口转发到目标机器的8080端口。

步骤2:修改你的监控程序,添加重启请求逻辑

现在修改你现有代码中的TestConnection方法,当检测到机器离线时,发送带认证的POST请求到目标机器的重启接口:

static void TestConnection()
{
    var timer = 1000;
    if (!int.TryParse(ConfigurationManager.AppSettings.Get("IntervalTimer"), out timer))
    {
        timer = 1000;
    }
    var machines = GetConfig();
    var client = new HttpClient();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    while (true)
    {
        foreach (var item in machines)
        {
            var targetParts = item.Split(':');
            var targetIp = targetParts[0];
            var targetPort = targetParts[1];
            var monitorUrl = $"http://{targetIp}:{targetPort}";

            try
            {
                var httpResponse = client.GetAsync(monitorUrl).GetAwaiter().GetResult();
                httpResponse.EnsureSuccessStatusCode(); // 确保HTTP状态码是2xx
                Console.WriteLine($"{item} | Status: Online");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"{item} | Status: Offline, 尝试发送重启请求...");
                try
                {
                    // 发送重启请求到目标机器的重启接口
                    var rebootUrl = $"http://{targetIp}:8080/api/reboot/TriggerReboot";
                    var rebootRequest = new { ApiKey = "YourSuperSecureRebootKey_123!" };
                    var jsonContent = new StringContent(
                        JsonConvert.SerializeObject(rebootRequest),
                        Encoding.UTF8,
                        "application/json"
                    );
                    var rebootResponse = client.PostAsync(rebootUrl, jsonContent).GetAwaiter().GetResult();
                    
                    if (rebootResponse.IsSuccessStatusCode)
                    {
                        Console.WriteLine($"{item} | 重启请求发送成功");
                    }
                    else
                    {
                        Console.WriteLine($"{item} | 重启请求失败: {rebootResponse.ReasonPhrase}");
                    }
                }
                catch (Exception rebootEx)
                {
                    Console.WriteLine($"{item} | 发送重启请求失败: {rebootEx.Message}");
                }
            }
        }
        Thread.Sleep(timer);
    }
}

为什么之前的方案不行?

  • PsShutdown/PsExec:依赖SMB协议(445端口),公网环境下这个端口几乎被所有运营商和防火墙封禁,而且非域环境下需要传递明文用户名密码,极易被安全软件拦截。
  • Azure应用:如果没有正确配置目标机器的网络访问权限(比如公网IP、端口映射),Azure服务无法直接访问到目标机器的内部服务。

额外安全建议

  • 把HTTP改成HTTPS:使用HttpsSelfHostServer,配置SSL证书,避免API密钥被明文传输。
  • 限制IP访问:在防火墙中只允许你的监控机器IP访问目标的重启端口,进一步降低风险。
  • 增强认证:可以把简单的API密钥换成JWT令牌或其他更安全的认证方式。

内容的提问来源于stack exchange,提问作者M.Sejersen

火山引擎 最新活动