Skip to content

Commit

Permalink
Add flag -useKey for ssh without keys
Browse files Browse the repository at this point in the history
In some fully isolated environments, the key used to encrypt
the ssh connection is redundant. With this commit, we can use
cpud/cpu without a key file by
* server: ./cpud -pk=
* client: ./cpu -useKey=false $HOST bash

-useKey as default is True.

Signed-off-by: Changyuan Lyu <changyuanl@google.com>
  • Loading branch information
Lencerf committed Apr 2, 2024
1 parent e30097a commit a1625e1
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 32 deletions.
44 changes: 27 additions & 17 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,24 @@ type Cmd struct {
// be set directly.
Host string
// HostName as found in .ssh/config; set to Host if not found
HostName string
Args []string
Root string
HostKeyFile string
PrivateKeyFile string
Port string
Timeout time.Duration
Env []string
SessionIn io.WriteCloser
SessionOut io.Reader
SessionErr io.Reader
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Row int
Col int
hasTTY bool // Set if we have a TTY
HostName string
Args []string
Root string
HostKeyFile string
PrivateKeyFile string
DisablePrivateKey bool
Port string
Timeout time.Duration
Env []string
SessionIn io.WriteCloser
SessionOut io.Reader
SessionErr io.Reader
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Row int
Col int
hasTTY bool // Set if we have a TTY
// NameSpace is a string as defined in the cpu documentation.
NameSpace string
// FSTab is an fstab(5)-format string
Expand Down Expand Up @@ -218,6 +219,15 @@ func WithPrivateKeyFile(key string) Set {
}
}

// WithDisablePrivateKey disables using private keys to encrypt the SSH
// connection.
func WithDisablePrivateKey(disable bool) Set {
return func(c *Cmd) error {
c.DisablePrivateKey = disable
return nil
}
}

// WithHostKeyFile adds a host key to a Cmd
func WithHostKeyFile(key string) Set {
return func(c *Cmd) error {
Expand Down
13 changes: 13 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,16 @@ func TestQuoteArg(t *testing.T) {
}
}
}

func TestCmdWithDisablePrivateKey(t *testing.T) {
c := Command("someserver", "bash")
if c.DisablePrivateKey {
t.Fatal("DisablePrivateKey of Cmd created by Command() is expected to be false, got true")
}
if err := c.SetOptions(WithDisablePrivateKey(true)); err != nil {
t.Fatalf("WithDisablePrivateKey returns unexpected err %v", err)
}
if !c.DisablePrivateKey {
t.Fatal("WithDisablePrivateKey(true) should set DisablePrivateKey to true, got false")
}
}
4 changes: 4 additions & 0 deletions client/fns.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ func (n nonce) String() string {
// UserKeyConfig sets up authentication for a User Key.
// It is required in almost all cases.
func (c *Cmd) UserKeyConfig() error {
if c.DisablePrivateKey {
verbose("Not using a key file to encrypt the ssh connection")
return nil
}
kf := c.PrivateKeyFile
if len(kf) == 0 {
kf = config.Get(c.Host, "IdentityFile")
Expand Down
13 changes: 13 additions & 0 deletions client/fns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,16 @@ func TestJoinFSTab(t *testing.T) {
}
}
}

func TestUserKeyConfigWithDisablePrivateKey(t *testing.T) {
cmd := &Cmd{
PrivateKeyFile: DefaultKeyFile,
DisablePrivateKey: true,
}
if err := cmd.UserKeyConfig(); err != nil {
t.Fatalf("UserKeyConfig() returns unexpected err: %v", err)
}
if len(cmd.config.Auth) != 0 {
t.Fatalf("cmd.config.Auth: got %v, want []", cmd.config.Auth)
}
}
5 changes: 5 additions & 0 deletions cmds/cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var (
fstab = flag.String("fstab", "", "pass an fstab to the cpud")
hostKeyFile = flag.String("hk", "" /*"/etc/ssh/ssh_host_rsa_key"*/, "file for host key")
keyFile = flag.String("key", "", "key file")
useKey = flag.Bool("useKey", true, "Use key file to encrypt the ssh connection")
namespace = flag.String("namespace", "/lib:/lib64:/usr:/bin:/etc:/home", "Default namespace for the remote process -- set to none for none")
network = flag.String("net", "", "network type to use. Defaults to whatever the cpu client defaults to")
port = flag.String("sp", "", "cpu default port")
Expand Down Expand Up @@ -80,6 +81,9 @@ func flags() {
// getKeyFile picks a keyfile if none has been set.
// It will use sshconfig, else use a default.
func getKeyFile(host, kf string) string {
if !*useKey {
return ""
}
verbose("getKeyFile for %q", kf)
if len(kf) == 0 {
kf = config.Get(host, "IdentityFile")
Expand Down Expand Up @@ -143,6 +147,7 @@ func newCPU(host string, args ...string) (retErr error) {
client.Debug9p = *dbg9p

if err := c.SetOptions(
client.WithDisablePrivateKey(!*useKey),
client.WithPrivateKeyFile(*keyFile),
client.WithHostKeyFile(*hostKeyFile),
client.WithPort(*port),
Expand Down
34 changes: 19 additions & 15 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,6 @@ func handler(s ssh.Session, cpud string) {
// handler and support for port forwarding for the 9p port.
func New(publicKeyFile, hostKeyFile, cpud string) (*ssh.Server, error) {
verbose("configure SSH server")
publicKeyOption := func(ctx ssh.Context, key ssh.PublicKey) bool {
data, err := os.ReadFile(publicKeyFile)
if err != nil {
fmt.Print(err)
return false
}
allowed, _, _, _, err := ssh.ParseAuthorizedKey(data)
if err != nil {
fmt.Print(err)
return false
}
return ssh.KeysEqual(key, allowed)
}

// Now we run as an ssh server, and each time we get a connection,
// we run that command after setting things up for it.
Expand All @@ -163,8 +150,7 @@ func New(publicKeyFile, hostKeyFile, cpud string) (*ssh.Server, error) {
}),
// Pick a reasonable default, which can be used for a call to listen and which
// will be overridden later from a listen.Addr
Addr: ":" + defaultPort,
PublicKeyHandler: publicKeyOption,
Addr: ":" + defaultPort,
ReversePortForwardingCallback: ssh.ReversePortForwardingCallback(func(ctx ssh.Context, host string, port uint32) bool {
verbose("ReversePortForwardingCallback: attempt to bind %v %v granted", host, port)
return true
Expand All @@ -178,6 +164,24 @@ func New(publicKeyFile, hostKeyFile, cpud string) (*ssh.Server, error) {
},
}

if len(publicKeyFile) > 0 {
server.PublicKeyHandler = func(ctx ssh.Context, key ssh.PublicKey) bool {
data, err := os.ReadFile(publicKeyFile)
if err != nil {
fmt.Print(err)
return false
}
allowed, _, _, _, err := ssh.ParseAuthorizedKey(data)
if err != nil {
fmt.Print(err)
return false
}
return ssh.KeysEqual(key, allowed)
}
} else {
log.Printf("Not encrypting SSH connections with a key file")
}

// we ignore the SetOption error; if it does not work out, we
// actually don't care.
_ = server.SetOption(ssh.HostKeyFile(hostKeyFile))
Expand Down
14 changes: 14 additions & 0 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,24 @@ import (
)

func TestNewServer(t *testing.T) {
s, err := New("key.pub", "", os.Args[0])
if err != nil {
t.Fatalf(`New("key.pub", "", %q): %v != nil`, os.Args[0], err)
}
if s.PublicKeyHandler == nil {
t.Fatalf(`New("key.pub", "", %q) returns a server without a public key handler`, os.Args[0])
}
t.Logf("New server: %v", s)
}

func TestNewServerWithoutKey(t *testing.T) {
s, err := New("", "", os.Args[0])
if err != nil {
t.Fatalf(`New("", "", %q): %v != nil`, os.Args[0], err)
}
if s.PublicKeyHandler != nil {
t.Fatalf(`New("", "", %q) returns a server with a public key handler`, os.Args[0])
}
t.Logf("New server: %v", s)
}

Expand Down

0 comments on commit a1625e1

Please sign in to comment.