Last active
August 29, 2024 15:10
-
-
Save joncfoo/acdd780e8347fd55273daa255039ba75 to your computer and use it in GitHub Desktop.
Peer Credentials from Unix Domain Socket
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Unix Domain Socket Peer Credentials | |
// | |
// This program demonstrates setting up a HTTP server over a Unix Domain Socket | |
// and subsequently obtaining connecting clients credentials. The credentials | |
// contain the User ID, Group ID, and Process ID of the incoming connection (as | |
// connections are made by some local system user as opposed to a network | |
// connection). | |
// | |
// This has interesting use cases around local process security. For example, | |
// a server listening on a Unix Domain Socket can choose to allow or reject | |
// incoming connections based on any combination of User ID, Group ID, or | |
// Process ID. Couple this technique with process privilege separation to | |
// build secure programs. | |
// | |
// Note: error checking and other best practices have been omitted for brevity. | |
// | |
// Overview: | |
// 1. Create a unix domain socket over which we can serve HTTP requests | |
// 2. As connections to the server are created, grab the credentials associated | |
// with the connection. | |
// 3. Make use of the credentials in a HTTP handler | |
// | |
// References: | |
// | |
// Using SO_PEERCRED in Go | |
// - https://blog.jbowen.dev/2019/09/using-so_peercred-in-go/ | |
// Dropping privileges and passing a file descriptor | |
// - https://gist.github.com/jsimonetti/e31dced5875903d65677e66e103168cf | |
package main | |
import ( | |
"context" | |
"fmt" | |
"net" | |
"net/http" | |
"os" | |
"golang.org/x/sys/unix" | |
) | |
func main() { | |
// # Step 1 | |
// Set up a unix domain socket, a special file on disk over which a server | |
// and clients can communicate. | |
socketFile := "example.socket" | |
os.Remove(socketFile) | |
listener, _ := net.Listen("unix", socketFile) | |
// Context key for credentials we will obtain from incoming connections. | |
var credentialsContextKey = struct{}{} | |
// # Step 2 | |
// Create the HTTP server | |
server := &http.Server{ | |
// `ConnContext` allows us to inspect the incoming client connection | |
// and modify the `Context` which is subsequently made available to all | |
// HTTP handlers on the server for this connection. | |
ConnContext: func(ctx context.Context, c net.Conn) context.Context { | |
// Obtain the file abstraction from the incoming connection. | |
file, _ := c.(*net.UnixConn).File() | |
// Obtain the Unix Credentials from the underlying file descriptor. | |
// > The credentials are of type `*unix.Ucred`. | |
credentials, _ := unix.GetsockoptUcred(int(file.Fd()), unix.SOL_SOCKET, unix.SO_PEERCRED) | |
// Return a modified context that contains the credentials we obtained | |
return context.WithValue(ctx, credentialsContextKey, credentials) | |
}, | |
} | |
// # Step 3 | |
// Attach a simple HTTP handler where we can make use of the incoming | |
// connection's credentials. | |
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
// Retrieve the credentials from the request context | |
credentials := r.Context().Value(credentialsContextKey).(*unix.Ucred) | |
// Return the credentials over the HTTP response. | |
fmt.Fprintf(w, "Connection credentials: %+v\n", credentials) | |
}) | |
// Note: the above can be abstracted into middleware that chooses to | |
// enforce security conditions. | |
// Start the HTTP server | |
server.Serve(listener) | |
} | |
// Test the program. | |
// | |
// In one terminal, execute `go run ./main.go`. | |
// In another terminal, execute `curl --unix-socket example.socket http://socket/`. | |
// | |
// You should see output similar to the following: | |
// Connection credentials: &{Pid:9025 Uid:1000 Gid:1000} | |
// | |
// Try executing the `curl` command as the root user. |
Author
joncfoo
commented
Nov 7, 2020
Absolute hero @joncfoo 💌
Absolute hero @joncfoo 💌
Hope you found it useful!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment