town/external/cmd/helpers/createkeyfile/main.go

111 lines
2.9 KiB
Go
Raw Permalink Normal View History

2023-03-03 22:01:21 +00:00
package main
/*
The purpose of this command is to be run as a new user. It initializes their ssh authorized_keys2 file, which allows them to ssh in for the first time.
This is an isolated command because creating a file and chowning it normally requires root permissions. We don't want to run the welcome command as root, so we give it `sudo` permission to run this one command as any user. The keyfile path is hardcoded, so if someone were to assume `welcome`'s identity, it could of course cause havoc but not delete
This is a port of the old createkeyfile.py script from the former admin system.
There are two functional changes:
1. It also creates `.ssh`. I can't remember if there was a reason the old script didn't do that as there is no record one way or the other. But having this command make `.ssh` means one fewer thing in the sudoers file.
2. It guards against overwriting of both .ssh and authorized_keys2. This is solely to limit the effect of a security breach. In the old admin system keys were managed via the django admin, meaning this script was used to add keys to authorized_keys2. these days we just edit authorized_keys2 directly, so this new command should only ever be used to initialize .ssh for a new user.
*/
import (
"fmt"
"os"
"os/user"
"path"
2023-03-04 00:15:12 +00:00
"strings"
2023-03-03 22:01:21 +00:00
)
const keyfileName = "authorized_keys2"
2023-03-06 20:34:29 +00:00
func quit(msg string, code int) {
2023-03-03 22:01:21 +00:00
fmt.Println(msg)
2023-03-06 20:34:29 +00:00
os.Exit(code)
2023-03-03 22:01:21 +00:00
}
func main() {
2023-03-04 00:18:45 +00:00
username := os.Args[1]
if username == "" {
2023-03-06 20:34:29 +00:00
quit("expected username as argument", 1)
2023-03-04 00:18:45 +00:00
}
2023-03-03 22:01:21 +00:00
u, err := user.Current()
if err != nil {
2023-03-06 20:34:29 +00:00
quit(err.Error(), 2)
2023-03-03 22:01:21 +00:00
}
2023-03-04 00:18:45 +00:00
if u.Username != username {
2023-03-06 20:34:29 +00:00
quit("that's my purse; I don't know you", 3)
2023-03-04 00:18:45 +00:00
}
2023-03-03 22:01:21 +00:00
sshPath := path.Join("/home", u.Username, ".ssh")
keyfilePath := path.Join(sshPath, keyfileName)
2023-03-04 00:15:12 +00:00
if err = os.Mkdir(sshPath, os.FileMode(0700)); err != nil {
2023-03-06 20:34:29 +00:00
quit(err.Error(), 4)
2023-03-03 22:01:21 +00:00
}
2023-03-04 00:33:01 +00:00
f, err := os.OpenFile(keyfilePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600)
2023-03-03 22:01:21 +00:00
if err != nil {
2023-03-06 20:34:29 +00:00
quit(fmt.Sprintf("failed to open %s: %s", keyfilePath, err.Error()), 5)
2023-03-03 22:01:21 +00:00
}
defer f.Close()
2023-03-04 00:15:12 +00:00
stdin := make([]byte, 90000) // arbitrary limit
2023-03-03 22:01:21 +00:00
n, err := os.Stdin.Read(stdin)
if err != nil {
2023-03-06 20:34:29 +00:00
quit(err.Error(), 6)
2023-03-03 22:01:21 +00:00
} else if n == 0 {
2023-03-06 20:34:29 +00:00
quit("nothing passed on STDIN", 7)
2023-03-03 22:01:21 +00:00
}
2023-03-04 00:15:12 +00:00
stdin = stdin[0:n]
if !strings.HasPrefix(string(stdin), "########## GREETINGS! ##########") {
// TODO further validation?
2023-03-06 20:34:29 +00:00
quit(fmt.Sprintf("file contents look wrong: %s", string(stdin)), 8)
2023-03-04 00:15:12 +00:00
}
2023-11-01 20:59:49 +00:00
_, err = f.Write(stdin)
2023-03-03 22:01:21 +00:00
if err != nil {
2023-03-06 20:34:29 +00:00
quit(err.Error(), 9)
2023-03-03 22:01:21 +00:00
}
2023-11-01 20:59:49 +00:00
_, err = f.WriteString("\n")
2023-03-03 22:01:21 +00:00
}
/*
The old script, in full:
#!/usr/bin/env python3
"""this script allows django to add public keys for a user. it's in its own
script so that a specific command can be added to the ttadmin user's sudoers
file."""
import sys
KEYFILE_PATH = '/home/{}/.ssh/authorized_keys2'
def main(argv):
username = argv[1]
with open(KEYFILE_PATH.format(username), 'w') as f:
f.write(sys.stdin.read())
if __name__ == '__main__':
exit(main(sys.argv))
*/