在 ASP.NET Core 中设置 Kestrel Unix 套接字文件权限?

Set Kestrel Unix socket file permissions in ASP.NET Core?

Kestrel 可以配置为使用 Unix 套接字与反向代理(即 nginx)通信以获得轻微的性能优势。但是,每次 kestrel 服务器 stops/starts 重置套接字的权限并根据系统配置阻止 nginx 访问套接字时,都会删除并重新创建套接字文件。

有什么简单可靠的方法可以确保在创建时打开 Kestrel 的 Unix 套接字权限?

以下示例 Program.Main 演示了如何通过 P/Invoke 使用 chmod。此解决方案允许在 Windows 上使用托管套接字,并在通过 appsettings.json 配置 ListenUnixSocket 时切换到 libuv,在这种情况下,在启动时直接调用 chmod 来建立套接字权限。

Chmodclass 的代码主要来自:https://silvercircle.github.io/2018/08/26/serving-net-core-kestrel-linux-unix-sockets/

UseLibuv 需要通过 NuGet 依赖 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv

namespace UnixSocketDemo
{
    using Microsoft.AspNetCore;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Threading.Tasks;
    using static System.String;

    public class Program
    {
        private static string _unixSocket = null;

        public static async Task Main(string[] args)
        {
            var webHost = BuildWebHost(args);
            await webHost.StartAsync();
            if (!IsNullOrWhiteSpace(_unixSocket))
                Chmod.Set(_unixSocket);

            await webHost.WaitForShutdownAsync();
        }

        public static IWebHost BuildWebHost(string[] args)
        {
            var builder = WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, config) => config.SetBasePath(Directory.GetCurrentDirectory()))
                .UseStartup<Startup>();

            var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
            var kestrelConfig = config.GetSection("Kestrel");
            if (kestrelConfig.Exists())
            {
                _unixSocket = kestrelConfig.GetValue<string>("ListenUnixSocket");
                if (!IsNullOrWhiteSpace(_unixSocket))
                    builder.UseLibuv();

                builder.ConfigureKestrel((hostingContext, serverOptions) =>
                {
                    serverOptions.Configure(kestrelConfig);
                    if (!IsNullOrWhiteSpace(_unixSocket))
                        serverOptions.ListenUnixSocket(_unixSocket);
                });
            }

            return builder.Build();
        }

        private static class Chmod
        {
            [DllImport("libc", EntryPoint="chmod", SetLastError = true)]
            [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "interop")]
            private static extern int chmod(string pathname, int mode);

            // user permissions
            const int S_IRUSR = 0x100;
            const int S_IWUSR = 0x80;
            const int S_IXUSR = 0x40;

            // group permission
            const int S_IRGRP = 0x20;
            const int S_IWGRP = 0x10;
            const int S_IXGRP = 0x8;

            // other permissions
            const int S_IROTH = 0x4;
            const int S_IWOTH = 0x2;
            const int S_IXOTH = 0x1;

            public static void Set(string filename)
            {
                const int _0755 =
                    S_IRUSR | S_IXUSR | S_IWUSR
                    | S_IRGRP | S_IXGRP | S_IWGRP
                    | S_IROTH | S_IXOTH | S_IWOTH;

                if (0 != chmod(Path.GetFullPath(filename), (int)_0755))
                    throw new Exception("Could not set Unix socket permissions");
            }
        }
    }
}