mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-01-09 23:47:54 +00:00
Replaced ssh subcommand with a working ssh-agent subcommand
This commit is contained in:
committed by
Kyle Isom
parent
e6481c0513
commit
433afa9574
20
README.md
20
README.md
@@ -347,8 +347,9 @@ would be a good option.
|
||||
|
||||
Red October can encrypt an SSH private key with a restriction that the key can
|
||||
be used to sign messages, but that it should not be returned as the result of a
|
||||
decrypt call. The ro client can use this feature to authenticate a user to a
|
||||
remote SSH server without ever handling the unencrypted private key directly.
|
||||
decrypt call. The ro client can use this feature to mimic an ssh-agent server
|
||||
which authenticates a user to a remote SSH server without ever handling the
|
||||
unencrypted private key directly.
|
||||
|
||||
Generate an ssh key without passphrase:
|
||||
|
||||
@@ -361,4 +362,17 @@ Encrypt with the "ssh-sign-with" usage only:
|
||||
|
||||
Use the remote server to authenticate to an SSH server
|
||||
|
||||
$ ro -server localhost:443 -in id_ed25519.encrypted -pubkey id_ed25519.pub ssh root@gibson
|
||||
$ RO_USER=alice RO_PASS=alice \
|
||||
./ro -server localhost:443 -ca server.crt \
|
||||
-in id_ed25519.encrypted -pubkey id_ed25519.pub ssh-agent
|
||||
|
||||
2018/02/05 05:21:13 Starting Red October Secret Shell Agent
|
||||
export SSH_AUTH_SOCK=/tmp/ro_ssh_267631424/roagent.sock
|
||||
|
||||
On a separate terminal, run:
|
||||
|
||||
$ export SSH_AUTH_SOCK=/tmp/ro_ssh_267631424/roagent.sock
|
||||
$ ssh-add -L # list of all public keys available through ro-agent
|
||||
$ ssh user@hostname
|
||||
|
||||
Other commands such as scp, git, etc. will also authenticate through ro.
|
||||
|
||||
100
cmd/ro/main.go
100
cmd/ro/main.go
@@ -6,12 +6,10 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -53,7 +51,6 @@ var commandSet = map[string]command{
|
||||
"delegate": command{Run: runDelegate, Desc: "do decryption delegation"},
|
||||
"encrypt": command{Run: runEncrypt, Desc: "encrypt a file"},
|
||||
"decrypt": command{Run: runDecrypt, Desc: "decrypt a file"},
|
||||
"ssh": command{Run: runSSH, Desc: "a wrapper for SSH using an RO-encrypted private key"},
|
||||
"ssh-agent": command{Run: runSSHAgent, Desc: "act as an SSH agent"},
|
||||
"re-encrypt": command{Run: runReEncrypt, Desc: "re-encrypt a file"},
|
||||
"order": command{Run: runOrder, Desc: "place an order for delegations"},
|
||||
@@ -89,6 +86,10 @@ func registerFlags() {
|
||||
}
|
||||
|
||||
func readLine(prompt string) (line string, err error) {
|
||||
// TODO: find a way to ask for username and password
|
||||
// such that the prompt itself is not captured in the
|
||||
// standard output. This way running `ro ... ssh-agent`
|
||||
// will set the SSH_AUTH_PATH environment variable.
|
||||
fmt.Printf(prompt)
|
||||
rd := bufio.NewReader(os.Stdin)
|
||||
line, err = rd.ReadString('\n')
|
||||
@@ -399,85 +400,62 @@ func runResetPersisted() {
|
||||
}
|
||||
|
||||
func runSSHAgent() {
|
||||
log.Println("Starting Red October Secret Shell Agent")
|
||||
|
||||
// Prepare a socket
|
||||
dir, err := ioutil.TempDir("", "ro_ssh_")
|
||||
processError(err)
|
||||
|
||||
authSockPath := path.Join(dir, "roagent.sock")
|
||||
os.Setenv("SSH_AUTH_SOCK", authSockPath)
|
||||
fmt.Printf("export SSH_AUTH_SOCK=%s\n", authSockPath)
|
||||
|
||||
socket := net.UnixAddr{Net: "unix", Name: authSockPath}
|
||||
ear, err := net.ListenUnix("unix", &socket)
|
||||
processError(err)
|
||||
|
||||
// Process the arguments
|
||||
inBytes, err := ioutil.ReadFile(inPath)
|
||||
processError(err)
|
||||
|
||||
// base64 decode the input
|
||||
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
|
||||
if err != nil {
|
||||
log.Println("failed to base64 decode the data, proceeding with raw data")
|
||||
encBytes = inBytes
|
||||
log.Println("failed to base64 decode the data, proceeding with raw data")
|
||||
encBytes = inBytes
|
||||
}
|
||||
|
||||
inBytes, err = ioutil.ReadFile(pubKeyPath)
|
||||
processError(err)
|
||||
|
||||
pubKey, _, _, _, err := ssh.ParseAuthorizedKey(inBytes)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("failed to parse SSH public key", err)
|
||||
log.Fatal("failed to parse SSH public key", err)
|
||||
}
|
||||
|
||||
// Make an agent
|
||||
roagent := roagent.NewROAgent(roServer, pubKey, encBytes, user, pswd)
|
||||
|
||||
authSockPath := os.Getenv("SSH_AUTH_SOCK")
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func(ear net.Listener, c chan os.Signal) {
|
||||
sig := <-c
|
||||
log.Printf("Caught signal %s: shutting down.", sig)
|
||||
os.RemoveAll(path.Dir(authSockPath))
|
||||
ear.Close()
|
||||
os.Exit(0)
|
||||
}(ear, sigChan)
|
||||
|
||||
if authSockPath == "" {
|
||||
log.Fatal("SSH_AUTH_SOCK not set")
|
||||
}
|
||||
for {
|
||||
conn, err := ear.AcceptUnix()
|
||||
if err != nil {
|
||||
log.Fatal("error accepting socket connection", err)
|
||||
}
|
||||
|
||||
listener, err := net.ListenUnix("unix", &net.UnixAddr{Name: authSockPath, Net: "unix"})
|
||||
if err != nil {
|
||||
log.Fatal("error listening on $SSH_AUTH_SOCK", err)
|
||||
}
|
||||
defer os.Remove(authSockPath)
|
||||
|
||||
conn, err := listener.AcceptUnix()
|
||||
if err != nil {
|
||||
log.Fatal("error accepting socket connection", err)
|
||||
}
|
||||
|
||||
err = agent.ServeAgent(roagent, conn)
|
||||
if err != nil && err != io.EOF {
|
||||
log.Fatal("error serving socket protocol", err)
|
||||
// Serve the agent
|
||||
go agent.ServeAgent(roagent, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func runSSH() {
|
||||
// First pick a path for our socket
|
||||
// TempDir will ensure that the directory is created with the correct permissions
|
||||
dir, err := ioutil.TempDir("", "ro_ssh_")
|
||||
if err != nil {
|
||||
log.Fatal("error getting temporary directory for SSH auth socket ", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
os.Setenv("SSH_AUTH_SOCK", path.Join(dir, "roagent.sock"))
|
||||
go runSSHAgent()
|
||||
|
||||
var sshPath string
|
||||
sshPath, err = exec.LookPath("ssh")
|
||||
if err != nil {
|
||||
log.Fatal("error finding path to ssh binary ", err)
|
||||
}
|
||||
|
||||
var p *os.Process
|
||||
p, err = os.StartProcess(sshPath, flag.Args(),
|
||||
&os.ProcAttr{
|
||||
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal("error starting ssh ", err)
|
||||
}
|
||||
|
||||
_, err = p.Wait()
|
||||
if err != nil {
|
||||
log.Fatal("error waiting on ssh ", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage: ro [options] subcommand")
|
||||
|
||||
Reference in New Issue
Block a user