go-runas: call RPCs to yourself as a child dropping root

If you have a Go server running as root and want to run some of your code as adifferent user, it turns out that Go’s standard rpc package works wonderfully for this.

Here’s a fun hack of a library that makes this easy: go-runas

Usage, from example.go in that repo:

// ....

func main() {
    runas.Server.Register(&DemoService{})
    runas.MaybeRunChildServer()

    for _, user := range []string{"nobody", "daemon", "man", "syslog"} {
        var res WhoAmIResult
        client, err := runas.User(user)
        if err != nil {
            log.Printf("failed to get client for user %s: %v", user, err)
            continue
        }   
        client.Call("DemoService.WhoAmI", true, &res)
        log.Printf("user %s: %v", user, res)
    }
}

// This code runs as a different user, not root.
func (s *DemoService) WhoAmI(unused *bool, res *WhoAmIResult) os.Error {
    res.Uid = syscall.Getuid()
    res.Gid = syscall.Getgid()
    return nil
}

And the result:

$ 6g example.go && 6l -o example example.6 && sudo ./example
2011/04/22 12:14:57 user nobody: {65534 65534}
2011/04/22 12:14:57 user daemon: {1 1}
2011/04/22 12:14:57 user man: {6 12}
2011/04/22 12:14:57 user syslog: {101 103}

Marshalling JSON data structures the quick-and-dirty way

It’s easy to marshal arbitrarly-structured JSON in Go. This example uses a locally-defined type m (within the function scope) to provide shorthand for map[string]interface{} so that you can quite naturally express complex JSON data structures.

type m map[string]interface{}
data := m{"data": m{"object": m{
    "type":    "note",
    "content": text,
    "attachments": []m{{
        "type":    "photo",
        "content": photoContent,
        "links": m{
            "enclosure": []m{{
                "href": photoURL,
                "type": "image/png",
            }},
        },
    }},
}}}
b, err := json.Marshal(data)

This works in a pinch, but more often than not you’ll want to create explicit Go data structures instead of using this trick.