Skip to content

Binding to privileged ports on Mac OS #134

@jmezach

Description

@jmezach

I'm trying to get my Aspire AppHost to have a YARP resource bind to port 443 which works just fine on Windows, but I'm having some issues on Mac OS. As noted in microsoft/aspire#5508 (comment) there is a way on Mac OS to bind to a privileged port without having to run as root, but you have to bind to all addresses. So I used the following in my AppHost:

var gateway = builder.AddYarp("gateway")
       .WithEndpoint("https", e =>
       {
           e.UriScheme = "https";
           e.TargetPort = 5001;
           e.Port = 443;
           e.TargetHost = "0.0.0.0";
       })

I was hoping this would solve my issue, but it still wouldn't bind to port 443 and basically just fail silently. After doing some digging I think I've narrowed it down to how dcp works. Looking at the LookupIP function it handles 0.0.0.0 specifically to the IP addresses of all the network interfaces on the machine. So when it comes to creating the proxy it will effectively use the IP address of my network interface which results in a permission denied.

I have reproduced this by writing a small Go program with the following code:

package main

import (
	"context"
	"fmt"
	"net"
)

func main() {
	lc := net.ListenConfig{}
	tcpListener, err := lc.Listen(context.Background(), "tcp", AddressAndPort("192.168.40.118", 443))
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	// The listener is now bound, wait for a key press to close it
	fmt.Println("Listener is bound. Press Enter to close.")
	fmt.Scanln()

	defer tcpListener.Close()
}

func AddressAndPort(address string, port int32) string {
	return fmt.Sprintf("%s:%d", address, port)
}

Running this with go run test.go results in Error: listen tcp 192.168.40.118:443: bind: permission denied

If I simply change the first parameter to AddressAndPort to be "0.0.0.0" I get the following output: Listener is bound. Press Enter to close. At that point I'm able to connect to that port as well.

Now I'm left wondering why ListenIP resolves 0.0.0.0 to all local network interface IPs. It does call-out that this must be done manually, so there's probably a good reason for it, so what am I missing here?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions