mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-09-04 23:27:01 +00:00
New functions (AddROSigner, etc.) and data structures for ROAgent.
Almost compatible with ssh-add now.
This commit is contained in:
committed by
Kyle Isom
parent
433afa9574
commit
3fc06e3b12
+39
-41
@@ -112,18 +112,18 @@ func getUserCredentials() {
|
||||
|
||||
if user == "" {
|
||||
user, err = readLine("Username: ")
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
}
|
||||
|
||||
if pswd == "" {
|
||||
pswd, err = gopass.GetPass("Password:")
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func processError(err error) {
|
||||
func processError(msg string, err error) {
|
||||
if err != nil {
|
||||
log.Fatal("error:", err)
|
||||
log.Fatalln(msg, ":", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func runCreate() {
|
||||
Password: pswd,
|
||||
}
|
||||
resp, err := roServer.Create(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
fmt.Println(resp.Status)
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ func runCreateUser() {
|
||||
HipchatName: hcName,
|
||||
}
|
||||
resp, err := roServer.CreateUser(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
fmt.Println(resp.Status)
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func runDelegate() {
|
||||
Labels: processCSL(labels),
|
||||
}
|
||||
resp, err := roServer.Delegate(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
fmt.Println(resp.Status)
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ func runRestore() {
|
||||
}
|
||||
|
||||
resp, err := roServer.Restore(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
if resp.Status != "ok" {
|
||||
fmt.Fprintf(os.Stderr, "failed: %s\n", resp.Status)
|
||||
@@ -189,7 +189,7 @@ func runRestore() {
|
||||
|
||||
var st core.StatusData
|
||||
err = json.Unmarshal(resp.Response, &st)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
fmt.Println("Restore delegation complete; persistence is now", st.Status)
|
||||
}
|
||||
@@ -201,13 +201,13 @@ func runSummary() {
|
||||
Password: pswd,
|
||||
}
|
||||
resp, err := roServer.Summary(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
fmt.Println(resp)
|
||||
}
|
||||
|
||||
func runEncrypt() {
|
||||
inBytes, err := ioutil.ReadFile(inPath)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
req := core.EncryptRequest{
|
||||
Name: user,
|
||||
Password: pswd,
|
||||
@@ -221,9 +221,9 @@ func runEncrypt() {
|
||||
}
|
||||
|
||||
resp, err := roServer.Encrypt(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
if resp.Status != "ok" {
|
||||
log.Fatal("response status error:", resp.Status)
|
||||
log.Fatalln("response status error", resp.Status)
|
||||
return
|
||||
}
|
||||
fmt.Println("Response Status:", resp.Status)
|
||||
@@ -233,7 +233,7 @@ func runEncrypt() {
|
||||
|
||||
func runReEncrypt() {
|
||||
inBytes, err := ioutil.ReadFile(inPath)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
// base64 decode the input
|
||||
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
|
||||
@@ -254,9 +254,9 @@ func runReEncrypt() {
|
||||
}
|
||||
|
||||
resp, err := roServer.ReEncrypt(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
if resp.Status != "ok" {
|
||||
log.Fatal("response status error:", resp.Status)
|
||||
log.Fatalln("response status error", resp.Status)
|
||||
return
|
||||
}
|
||||
fmt.Println("Response Status:", resp.Status)
|
||||
@@ -266,7 +266,7 @@ func runReEncrypt() {
|
||||
|
||||
func runDecrypt() {
|
||||
inBytes, err := ioutil.ReadFile(inPath)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
// base64 decode the input
|
||||
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
|
||||
@@ -300,25 +300,25 @@ func runDecrypt() {
|
||||
|
||||
select {
|
||||
case <-sigChan:
|
||||
log.Fatal("process is interrupted")
|
||||
log.Fatalln("process is interrupted")
|
||||
return
|
||||
|
||||
case <-time.After(30 * time.Second):
|
||||
}
|
||||
}
|
||||
default:
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
}
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
log.Fatal("response status error:", resp.Status)
|
||||
log.Fatalln("response status error", resp.Status)
|
||||
}
|
||||
|
||||
fmt.Println("Response Status:", resp.Status)
|
||||
var msg core.DecryptWithDelegates
|
||||
err = json.Unmarshal(resp.Response, &msg)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
fmt.Println("Secure:", msg.Secure)
|
||||
fmt.Println("Delegates:", msg.Delegates)
|
||||
ioutil.WriteFile(outPath, msg.Data, 0644)
|
||||
@@ -334,19 +334,19 @@ func runOrder() {
|
||||
Users: processCSL(users),
|
||||
}
|
||||
resp, err := roServer.Order(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
var o order.Order
|
||||
err = json.Unmarshal(resp.Response, &o)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
if pollInterval > 0 {
|
||||
for o.Delegated < 2 {
|
||||
time.Sleep(pollInterval)
|
||||
resp, err = roServer.OrderInfo(core.OrderInfoRequest{Name: user, Password: pswd, OrderNum: o.Num})
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
err = json.Unmarshal(resp.Response, &o)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
}
|
||||
}
|
||||
fmt.Println(resp.Status)
|
||||
@@ -354,7 +354,7 @@ func runOrder() {
|
||||
|
||||
func runOwner() {
|
||||
inBytes, err := ioutil.ReadFile(inPath)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
// attempt to base64 decode the input file
|
||||
base64decoded, err := base64.StdEncoding.DecodeString(string(inBytes))
|
||||
@@ -367,7 +367,7 @@ func runOwner() {
|
||||
}
|
||||
|
||||
resp, err := roServer.Owners(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(resp)
|
||||
@@ -380,7 +380,7 @@ func runStatus() {
|
||||
}
|
||||
|
||||
resp, err := roServer.Status(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(resp)
|
||||
@@ -393,7 +393,7 @@ func runResetPersisted() {
|
||||
}
|
||||
|
||||
resp, err := roServer.ResetPersisted(req)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(resp)
|
||||
@@ -404,7 +404,7 @@ func runSSHAgent() {
|
||||
|
||||
// Prepare a socket
|
||||
dir, err := ioutil.TempDir("", "ro_ssh_")
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
authSockPath := path.Join(dir, "roagent.sock")
|
||||
os.Setenv("SSH_AUTH_SOCK", authSockPath)
|
||||
@@ -412,11 +412,11 @@ func runSSHAgent() {
|
||||
|
||||
socket := net.UnixAddr{Net: "unix", Name: authSockPath}
|
||||
ear, err := net.ListenUnix("unix", &socket)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
// Process the arguments
|
||||
inBytes, err := ioutil.ReadFile(inPath)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
|
||||
if err != nil {
|
||||
@@ -425,15 +425,15 @@ func runSSHAgent() {
|
||||
}
|
||||
|
||||
inBytes, err = ioutil.ReadFile(pubKeyPath)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
pubKey, _, _, _, err := ssh.ParseAuthorizedKey(inBytes)
|
||||
if err != nil {
|
||||
log.Fatal("failed to parse SSH public key", err)
|
||||
}
|
||||
processError("failed to parse SSH public key", err)
|
||||
|
||||
// Make an agent
|
||||
roagent := roagent.NewROAgent(roServer, pubKey, encBytes, user, pswd)
|
||||
// sshagent := agent.NewKeyring()
|
||||
roagent, err := roagent.NewROAgent(roServer, pubKey, encBytes, user, pswd)
|
||||
processError("failed to start ROAgent", err)
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
@@ -447,9 +447,7 @@ func runSSHAgent() {
|
||||
|
||||
for {
|
||||
conn, err := ear.AcceptUnix()
|
||||
if err != nil {
|
||||
log.Fatal("error accepting socket connection", err)
|
||||
}
|
||||
processError("error accepting socket connection", err)
|
||||
|
||||
// Serve the agent
|
||||
go agent.ServeAgent(roagent, conn)
|
||||
@@ -486,7 +484,7 @@ func main() {
|
||||
} else {
|
||||
var err error
|
||||
roServer, err = client.NewRemoteServer(server, caPath)
|
||||
processError(err)
|
||||
processError("error", err)
|
||||
|
||||
getUserCredentials()
|
||||
cmd.Run()
|
||||
|
||||
+90
-36
@@ -7,6 +7,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"io"
|
||||
|
||||
"github.com/cloudflare/redoctober/client"
|
||||
@@ -15,12 +16,18 @@ import (
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
type ROSigner struct {
|
||||
type ROAgent struct {
|
||||
server *client.RemoteServer
|
||||
pub ssh.PublicKey
|
||||
encryptedKey []byte
|
||||
user string
|
||||
pswd string
|
||||
keyring []*ROSigner
|
||||
}
|
||||
|
||||
type ROSigner struct {
|
||||
agent *ROAgent
|
||||
pub ssh.PublicKey
|
||||
rawKey []byte
|
||||
encryptedKey []byte
|
||||
}
|
||||
|
||||
func (signer ROSigner) PublicKey() ssh.PublicKey {
|
||||
@@ -28,14 +35,15 @@ func (signer ROSigner) PublicKey() ssh.PublicKey {
|
||||
}
|
||||
|
||||
func (signer ROSigner) Sign(rand io.Reader, msg []byte) (signature *ssh.Signature, err error) {
|
||||
// TODO encryptedKey vs rawKey
|
||||
req := core.SSHSignWithRequest{
|
||||
Name: signer.user,
|
||||
Password: signer.pswd,
|
||||
Name: signer.agent.user,
|
||||
Password: signer.agent.pswd,
|
||||
Data: signer.encryptedKey,
|
||||
TBSData: msg,
|
||||
}
|
||||
|
||||
resp, err := signer.server.SSHSignWith(req)
|
||||
resp, err := signer.agent.server.SSHSignWith(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -55,70 +63,116 @@ func (signer ROSigner) Sign(rand io.Reader, msg []byte) (signature *ssh.Signatur
|
||||
return &sshSignature, nil
|
||||
}
|
||||
|
||||
type ROAgent struct {
|
||||
signer ROSigner
|
||||
}
|
||||
|
||||
// NewROAgent creates a new SSH agent which forwards signature requests to the
|
||||
// provided remote server
|
||||
func NewROAgent(server *client.RemoteServer, pubKey ssh.PublicKey, encryptedPrivKey []byte, user, pswd string) agent.Agent {
|
||||
return &ROAgent{
|
||||
ROSigner{
|
||||
server,
|
||||
pubKey,
|
||||
encryptedPrivKey,
|
||||
user,
|
||||
pswd,
|
||||
},
|
||||
func NewROAgent(server *client.RemoteServer, pubKey ssh.PublicKey, encryptedPrivKey []byte, user, pswd string) (agent.Agent, error) {
|
||||
// FIXME these arguments are extra
|
||||
roagent := &ROAgent{
|
||||
server,
|
||||
user,
|
||||
pswd,
|
||||
[]*ROSigner{},
|
||||
}
|
||||
|
||||
err := roagent.AddROSigner(pubKey, encryptedPrivKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to add new signer to the ROAgent")
|
||||
}
|
||||
|
||||
return roagent, nil
|
||||
}
|
||||
|
||||
// RemoveAll has no effect for the ROAgent
|
||||
// NewROSigner adds a new SSH identity to the ROAgent
|
||||
func (r *ROAgent) AddROSigner(pubKey ssh.PublicKey, encryptedPrivKey []byte) error {
|
||||
rosigner := &ROSigner{
|
||||
agent: r,
|
||||
pub: pubKey,
|
||||
encryptedKey: encryptedPrivKey,
|
||||
}
|
||||
r.keyring = append(r.keyring, rosigner)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll empties ROAgent's keyring
|
||||
func (r *ROAgent) RemoveAll() error {
|
||||
r.keyring = []*ROSigner{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove has no effect for the ROAgent
|
||||
// Removes the first matching key from ROAgent's keyring
|
||||
func (r *ROAgent) Remove(key ssh.PublicKey) error {
|
||||
return nil
|
||||
wanted := key.Marshal()
|
||||
for i, signer := range r.keyring {
|
||||
if bytes.Equal(signer.PublicKey().Marshal(), wanted) {
|
||||
// Order is not preserved
|
||||
r.keyring[i] = r.keyring[0]
|
||||
r.keyring = r.keyring[1:]
|
||||
log.Println("signer was removed")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("could not remove signer")
|
||||
}
|
||||
|
||||
// Lock has no effect for the ROAgent
|
||||
// Locks the ROAgent by removing the password
|
||||
// TODO should this encrypt the password instead?
|
||||
func (r *ROAgent) Lock(passphrase []byte) error {
|
||||
return nil
|
||||
if bytes.Equal(passphrase, []byte(r.pswd)) {
|
||||
r.pswd = ""
|
||||
return nil
|
||||
}
|
||||
return errors.New("could not lock the agent")
|
||||
}
|
||||
|
||||
// Unlock has no effect for the ROAgent
|
||||
// Unlocks the ROAgent by adding the password
|
||||
// FIXME ask papa RO if the password is correct
|
||||
func (r *ROAgent) Unlock(passphrase []byte) error {
|
||||
r.pswd = string(passphrase)
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns the identities known to the agent.
|
||||
func (r *ROAgent) List() ([]*agent.Key, error) {
|
||||
return []*agent.Key{
|
||||
{
|
||||
Format: r.signer.PublicKey().Type(),
|
||||
Blob: r.signer.PublicKey().Marshal(),
|
||||
Comment: "Red October encrypted SSH key",
|
||||
},
|
||||
}, nil
|
||||
list := make([]*agent.Key, len(r.keyring))
|
||||
for i, signer := range r.keyring {
|
||||
list[i] = &agent.Key{
|
||||
Format: signer.PublicKey().Type(),
|
||||
Blob: signer.PublicKey().Marshal(),
|
||||
Comment: r.user,
|
||||
}
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Add has no effect for the ROAgent
|
||||
// FIXME
|
||||
func (r *ROAgent) Add(key agent.AddedKey) error {
|
||||
signer, _ := ssh.NewSignerFromKey(key.PrivateKey)
|
||||
rosigner := &ROSigner{
|
||||
pub: signer.PublicKey(),
|
||||
encryptedKey: nil, //[]byte
|
||||
}
|
||||
r.keyring = append(r.keyring, rosigner)
|
||||
log.Println("new signer was added")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign returns a signature for the data.
|
||||
func (r *ROAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
|
||||
wanted := key.Marshal()
|
||||
if bytes.Equal(r.signer.PublicKey().Marshal(), wanted) {
|
||||
return r.signer.Sign(rand.Reader, data)
|
||||
for _, signer := range r.keyring {
|
||||
if bytes.Equal(signer.PublicKey().Marshal(), wanted) {
|
||||
return signer.Sign(rand.Reader, data)
|
||||
}
|
||||
}
|
||||
return nil, errors.New("wrong key requested")
|
||||
return nil, errors.New("requested key was not found on keyring")
|
||||
}
|
||||
|
||||
// Signers returns signers for all the known keys.
|
||||
func (r *ROAgent) Signers() ([]ssh.Signer, error) {
|
||||
return []ssh.Signer{r.signer}, nil
|
||||
list := make([]ssh.Signer, len(r.keyring))
|
||||
for i, signer := range r.keyring {
|
||||
list[i] = ssh.Signer(signer)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user