Skip to content

Commit

Permalink
Credentialz-4 (#3358)
Browse files Browse the repository at this point in the history
* Credentialz-4

"This code is a Contribution to the OpenConfig Feature Profiles project ("Work") made under the Google Software Grant and Corporate Contributor License Agreement ("CLA") and governed by the Apache License 2.0. No other rights or licenses in or to any of Nokia's intellectual property are granted for any other purpose. This code is provided on an "as is" basis without any warranties of any kind."

* Fix telemetry check

"This code is a Contribution to the OpenConfig Feature Profiles project ("Work") made under the Google Software Grant and Corporate Contributor License Agreement ("CLA") and governed by the Apache License 2.0. No other rights or licenses in or to any of Nokia's intellectual property are granted for any other purpose. This code is provided on an "as is" basis without any warranties of any kind."

* Credz library

"This code is a Contribution to the OpenConfig Feature Profiles project ("Work") made under the Google Software Grant and Corporate Contributor License Agreement ("CLA") and governed by the Apache License 2.0. No other rights or licenses in or to any of Nokia's intellectual property are granted for any other purpose. This code is provided on an "as is" basis without any warranties of any kind."
  • Loading branch information
dipchauh authored Oct 25, 2024
1 parent 19dfd26 commit ac8a655
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Credentialz-4: SSH Public Key Authentication

## Summary

Test that Credentialz properly configures authorized SSH public keys for a given user, and that
the DUT properly allows or disallows authentication based on the configured settings.


## Procedure

* Create a user ssh keypair with `ssh-keygen`
* Set a username of `testuser`
* Perform the following tests and assert the expected result:
* Case 1: Failure
* Attempt to ssh into the server with the `testuser` username, presenting the ssh key.
* Assert that authentication has failed (because the key is not authorized)
* Case 2: Success
* Configure the previously created ssh public key as an authorized key for the
`testuser` using gnsi.Credentialz/AuthorizedKeysRequest
* Authenticate with the `testuser` username and the previously created public key via SSH
* Assert that authentication has been successful
* Ensure telemetry values for version and created-on match the values set by
RotateHostParameters for
`/oc-sys:system/oc-sys:aaa/oc-sys:authentication/oc-sys:users/oc-sys:user/oc-sys:state:authorized-keys-list-version`
and
`/oc-sys:system/oc-sys:aaa/oc-sys:authentication/oc-sys:users/oc-sys:user/oc-sys:state:authorized-keys-list-created-on`
* Ensure that access accept telemetry counters are incremented
`/oc-sys:system/oc-sys:ssh-server/oc-sys:state:counters:access-accepts`
`/oc-sys:system/oc-sys:ssh-server/oc-sys:state:counters:last-access-accept`


## OpenConfig Path and RPC Coverage

The below yaml defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here.

```yaml
paths:
## State Paths ##
/system/aaa/authentication/users/user/state/authorized-keys-list-version:
/system/aaa/authentication/users/user/state/authorized-keys-list-created-on:
/system/ssh-server/state/counters/access-accepts:
/system/ssh-server/state/counters/last-access-accept:

rpcs:
gnsi:
credentialz.v1.Credentialz.RotateAccountCredentials:
```
## Minimum DUT platform requirement
N/A
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto
# proto-message: Metadata

uuid: "3e6294aa-8f9d-4c8d-9041-4a2f4cd84c36"
plan_id: "Credentialz-4"
description: "SSH Public Key Authentication"
testbed: TESTBED_DUT

platform_exceptions: {
platform: {
vendor: NOKIA
}
deviations: {
ssh_server_counters_unsupported: true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sshpublickeyauthentication

import (
"os"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/openconfig/featureprofiles/internal/security/credz"

"github.com/openconfig/featureprofiles/internal/deviations"
"github.com/openconfig/featureprofiles/internal/fptest"
"github.com/openconfig/ondatra"
"github.com/openconfig/ondatra/gnmi"
)

const (
username = "testuser"
authorizedKeysListVersion = "v1.0"
)

var (
authorizedKeysListCreatedOn = time.Now().Unix()
)

func TestMain(m *testing.M) {
fptest.RunTests(m)
}

func TestCredentialz(t *testing.T) {
dut := ondatra.DUT(t, "dut")
target := credz.GetDutTarget(t, dut)

// Create temporary directory for storing ssh keys/certificates.
dir, err := os.MkdirTemp("", "")
if err != nil {
t.Fatalf("Creating temp dir, err: %s", err)
}
defer func(dir string) {
err = os.RemoveAll(dir)
if err != nil {
t.Logf("Error removing temp directory, error: %s", err)
}
}(dir)

credz.CreateSSHKeyPair(t, dir, username)
credz.SetupUser(t, dut, username)

t.Run("auth should fail ssh public key not authorized for user", func(t *testing.T) {
_, err := credz.SSHWithKey(t, target, username, dir)
if err == nil {
t.Fatalf("Dialing ssh succeeded, but we expected to fail.")
}
})

t.Run("auth should succeed ssh public key authorized for user", func(t *testing.T) {
// Push authorized key to the dut.
credz.RotateAuthorizedKey(t,
dut,
dir,
username,
authorizedKeysListVersion,
uint64(authorizedKeysListCreatedOn))

var startingAcceptCounter, startingLastAcceptTime uint64
if !deviations.SSHServerCountersUnsupported(dut) {
startingAcceptCounter, startingLastAcceptTime = credz.GetAcceptTelemetry(t, dut)
}

// Verify ssh with key succeeds.
_, err := credz.SSHWithKey(t, target, username, dir)
if err != nil {
t.Fatalf("Dialing ssh failed, but we expected to succeed. error: %v", err)
}

// Verify ssh counters.
if !deviations.SSHServerCountersUnsupported(dut) {
endingAcceptCounter, endingLastAcceptTime := credz.GetAcceptTelemetry(t, dut)
if endingAcceptCounter <= startingAcceptCounter {
t.Fatalf("SSH server accept counter did not increment after successful login. startCounter: %v, endCounter: %v", startingAcceptCounter, endingAcceptCounter)
}
if startingLastAcceptTime == endingLastAcceptTime {
t.Fatalf("SSH server accept last timestamp did not update after successful login. Timestamp: %v", endingLastAcceptTime)
}
}

// Verify authorized keys telemetry.
userState := gnmi.Get(t, dut, gnmi.OC().System().Aaa().Authentication().User(username).State())
gotAuthorizedKeysListVersion := userState.GetAuthorizedKeysListVersion()
if !cmp.Equal(gotAuthorizedKeysListVersion, authorizedKeysListVersion) {
t.Fatalf(
"Telemetry reports authorized keys list version is not correct\n\tgot: %s\n\twant: %s",
gotAuthorizedKeysListVersion, authorizedKeysListVersion,
)
}
gotAuthorizedKeysListCreatedOn := userState.GetAuthorizedKeysListCreatedOn()
if !cmp.Equal(time.Unix(0, int64(gotAuthorizedKeysListCreatedOn)), time.Unix(authorizedKeysListCreatedOn, 0)) {
t.Fatalf(
"Telemetry reports authorized keys list version on is not correct\n\tgot: %d\n\twant: %d",
gotAuthorizedKeysListCreatedOn, authorizedKeysListCreatedOn,
)
}
})

t.Cleanup(func() {
// Cleanup user authorized key after test.
credz.RotateAuthorizedKey(t, dut, "", username, "", 0)
})
}

0 comments on commit ac8a655

Please sign in to comment.