Skip to content

Commit

Permalink
support enable-oslogin-sk key (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
hopkiw authored Aug 31, 2021
1 parent 1947fa5 commit ee9760d
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 22 deletions.
6 changes: 6 additions & 0 deletions google_guest_agent/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ type attributes struct {
BlockProjectKeys bool
EnableOSLogin *bool
TwoFactor *bool
SecurityKey *bool
SSHKeys []string
WindowsKeys windowsKeys
Diagnostics string
Expand Down Expand Up @@ -170,6 +171,7 @@ func (a *attributes) UnmarshalJSON(b []byte) error {
OldSSHKeys string `json:"sshKeys"`
SSHKeys string `json:"ssh-keys"`
TwoFactor string `json:"enable-oslogin-2fa"`
SecurityKey string `json:"enable-oslogin-sk"`
WindowsKeys windowsKeys `json:"windows-keys"`
WSFCAddresses string `json:"wsfc-addrs"`
WSFCAgentPort string `json:"wsfc-agent-port"`
Expand Down Expand Up @@ -211,6 +213,10 @@ func (a *attributes) UnmarshalJSON(b []byte) error {
if err == nil {
a.TwoFactor = mkbool(value)
}
value, err = strconv.ParseBool(temp.SecurityKey)
if err == nil {
a.SecurityKey = mkbool(value)
}
// So SSHKeys will be nil instead of []string{}
if temp.SSHKeys != "" {
a.SSHKeys = strings.Split(temp.SSHKeys, "\n")
Expand Down
6 changes: 3 additions & 3 deletions google_guest_agent/non_windows_accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ func (a *accountsMgr) diff() bool {
}
}
// If we've just disabled OS Login.
oldOslogin, _ := getOSLoginEnabled(oldMetadata)
newOslogin, _ := getOSLoginEnabled(newMetadata)
oldOslogin, _, _ := getOSLoginEnabled(oldMetadata)
newOslogin, _, _ := getOSLoginEnabled(newMetadata)
if oldOslogin && !newOslogin {
return true
}
Expand All @@ -92,7 +92,7 @@ func (a *accountsMgr) timeout() bool {
}

func (a *accountsMgr) disabled(os string) bool {
oslogin, _ := getOSLoginEnabled(newMetadata)
oslogin, _, _ := getOSLoginEnabled(newMetadata)
return false ||
os == "windows" || oslogin ||
!config.Section("Daemons").Key("accounts_daemon").MustBool(true)
Expand Down
36 changes: 25 additions & 11 deletions google_guest_agent/oslogin.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type osloginMgr struct{}

// We also read project keys first, letting instance-level keys take
// precedence.
func getOSLoginEnabled(md *metadata) (bool, bool) {
func getOSLoginEnabled(md *metadata) (bool, bool, bool) {
var enable bool
if md.Project.Attributes.EnableOSLogin != nil {
enable = *md.Project.Attributes.EnableOSLogin
Expand All @@ -50,16 +50,24 @@ func getOSLoginEnabled(md *metadata) (bool, bool) {
if md.Instance.Attributes.TwoFactor != nil {
twofactor = *md.Instance.Attributes.TwoFactor
}
return enable, twofactor
var skey bool
if md.Project.Attributes.SecurityKey != nil {
skey = *md.Project.Attributes.SecurityKey
}
if md.Instance.Attributes.SecurityKey != nil {
skey = *md.Instance.Attributes.SecurityKey
}
return enable, twofactor, skey
}

func (o *osloginMgr) diff() bool {
oldEnable, oldTwoFactor := getOSLoginEnabled(oldMetadata)
enable, twofactor := getOSLoginEnabled(newMetadata)
oldEnable, oldTwoFactor, oldSkey := getOSLoginEnabled(oldMetadata)
enable, twofactor, skey := getOSLoginEnabled(newMetadata)
return oldMetadata.Project.ProjectID == "" ||
// True on first run or if any value has changed.
(oldTwoFactor != twofactor) ||
(oldEnable != enable)
(oldEnable != enable) ||
(oldSkey != skey)
}

func (o *osloginMgr) timeout() bool {
Expand All @@ -73,8 +81,8 @@ func (o *osloginMgr) disabled(os string) bool {
func (o *osloginMgr) set() error {
// We need to know if it was previously enabled for the clearing of
// metadata-based SSH keys.
oldEnable, _ := getOSLoginEnabled(oldMetadata)
enable, twofactor := getOSLoginEnabled(newMetadata)
oldEnable, _, _ := getOSLoginEnabled(oldMetadata)
enable, twofactor, skey := getOSLoginEnabled(newMetadata)

if enable && !oldEnable {
logger.Infof("Enabling OS Login")
Expand All @@ -87,7 +95,7 @@ func (o *osloginMgr) set() error {
logger.Infof("Disabling OS Login")
}

if err := writeSSHConfig(enable, twofactor); err != nil {
if err := writeSSHConfig(enable, twofactor, skey); err != nil {
logger.Errorf("Error updating SSH config: %v.", err)
}

Expand Down Expand Up @@ -172,12 +180,18 @@ func writeConfigFile(path, contents string) error {
return nil
}

func updateSSHConfig(sshConfig string, enable, twofactor bool) string {
func updateSSHConfig(sshConfig string, enable, twofactor, skey bool) string {
// TODO: this feels like a case for a text/template
challengeResponseEnable := "ChallengeResponseAuthentication yes"
authorizedKeysCommand := "AuthorizedKeysCommand /usr/bin/google_authorized_keys"
if skey {
authorizedKeysCommand = "AuthorizedKeysCommand /usr/bin/google_authorized_keys_sk"
}
if runtime.GOOS == "freebsd" {
authorizedKeysCommand = "AuthorizedKeysCommand /usr/local/bin/google_authorized_keys"
if skey {
authorizedKeysCommand = "AuthorizedKeysCommand /usr/local/bin/google_authorized_keys_sk"
}
}
authorizedKeysUser := "AuthorizedKeysCommandUser root"
twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive"
Expand Down Expand Up @@ -205,12 +219,12 @@ func updateSSHConfig(sshConfig string, enable, twofactor bool) string {
return strings.Join(filtered, "\n")
}

func writeSSHConfig(enable, twofactor bool) error {
func writeSSHConfig(enable, twofactor, skey bool) error {
sshConfig, err := ioutil.ReadFile("/etc/ssh/sshd_config")
if err != nil {
return err
}
proposed := updateSSHConfig(string(sshConfig), enable, twofactor)
proposed := updateSSHConfig(string(sshConfig), enable, twofactor, skey)
if proposed == string(sshConfig) {
return nil
}
Expand Down
55 changes: 47 additions & 8 deletions google_guest_agent/oslogin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,15 @@ func TestUpdateNSSwitchConfig(t *testing.T) {
func TestUpdateSSHConfig(t *testing.T) {
challengeResponseEnable := "ChallengeResponseAuthentication yes"
authorizedKeysCommand := "AuthorizedKeysCommand /usr/bin/google_authorized_keys"
authorizedKeysCommandSk := "AuthorizedKeysCommand /usr/bin/google_authorized_keys_sk"
authorizedKeysUser := "AuthorizedKeysCommandUser root"
twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive"
matchblock1 := `Match User sa_*`
matchblock2 := ` AuthenticationMethods publickey`

var tests = []struct {
contents, want []string
enable, twofactor bool
contents, want []string
enable, twofactor, skey bool
}{
{
// Full block is created, any others removed.
Expand All @@ -214,6 +215,7 @@ func TestUpdateSSHConfig(t *testing.T) {
},
enable: true,
twofactor: true,
skey: false,
},
{
// Full block is created, google comments removed.
Expand All @@ -239,6 +241,7 @@ func TestUpdateSSHConfig(t *testing.T) {
},
enable: true,
twofactor: true,
skey: false,
},
{
// Block is created without two-factor options.
Expand All @@ -256,6 +259,7 @@ func TestUpdateSSHConfig(t *testing.T) {
},
enable: true,
twofactor: false,
skey: false,
},
{
// Existing block is removed.
Expand All @@ -272,14 +276,36 @@ func TestUpdateSSHConfig(t *testing.T) {
},
enable: false,
twofactor: true,
skey: false,
},
{
// Skey binary is chosen instead.
contents: []string{
"line1",
"line2",
googleBlockStart,
"line3",
googleBlockEnd,
},
want: []string{
googleBlockStart,
authorizedKeysCommandSk,
authorizedKeysUser,
googleBlockEnd,
"line1",
"line2",
},
enable: true,
twofactor: false,
skey: true,
},
}

for idx, tt := range tests {
contents := strings.Join(tt.contents, "\n")
want := strings.Join(tt.want, "\n")

if res := updateSSHConfig(contents, tt.enable, tt.twofactor); res != want {
if res := updateSSHConfig(contents, tt.enable, tt.twofactor, tt.skey); res != want {
t.Errorf("test %v\nwant:\n%v\ngot:\n%v\n", idx, want, res)
}
}
Expand Down Expand Up @@ -440,42 +466,55 @@ func TestUpdateGroupConf(t *testing.T) {

func TestGetOSLoginEnabled(t *testing.T) {
var tests = []struct {
md string
enable, twofactor bool
md string
enable, twofactor, skey bool
}{
{
md: `{"instance": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}}`,
enable: true,
twofactor: true,
skey: false,
},
{
md: `{"project": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}}`,
enable: true,
twofactor: true,
skey: false,
},
{
// Instance keys take precedence
md: `{"project": {"attributes": {"enable-oslogin": "false", "enable-oslogin-2fa": "false"}}, "instance": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}}`,
enable: true,
twofactor: true,
skey: false,
},
{
// Instance keys take precedence
md: `{"project": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}, "instance": {"attributes": {"enable-oslogin": "false", "enable-oslogin-2fa": "false"}}}`,
enable: false,
twofactor: false,
skey: false,
},
{
// Handle weird values
md: `{"instance": {"attributes": {"enable-oslogin": "TRUE", "enable-oslogin-2fa": "foobar"}}}`,
enable: true,
twofactor: false,
skey: false,
},
{
// Mixed test
md: `{"project": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}, "instance": {"attributes": {"enable-oslogin-2fa": "false"}}}`,
enable: true,
twofactor: false,
skey: false,
},
{
// Skey test
md: `{"instance": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true", "enable-oslogin-sk": "true"}}}`,
enable: true,
twofactor: true,
skey: true,
},
}

Expand All @@ -484,9 +523,9 @@ func TestGetOSLoginEnabled(t *testing.T) {
if err := json.Unmarshal([]byte(tt.md), &md); err != nil {
t.Errorf("Failed to unmarshal metadata JSON for test %v: %v", idx, err)
}
enable, twofactor := getOSLoginEnabled(&md)
if enable != tt.enable || twofactor != tt.twofactor {
t.Errorf("Test %v failed. Expected: %v/%v Got: %v/%v", idx, tt.enable, tt.twofactor, enable, twofactor)
enable, twofactor, skey := getOSLoginEnabled(&md)
if enable != tt.enable || twofactor != tt.twofactor || skey != tt.skey {
t.Errorf("Test %v failed. Expected: %v/%v/%v Got: %v/%v/%v", idx, tt.enable, tt.twofactor, tt.skey, enable, twofactor, skey)
}
}
}

0 comments on commit ee9760d

Please sign in to comment.