Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default OPA policies #242

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Last Token matches TLSInfo - checks to make sure that the Last Token (JWT) is signed by the same cert as found in TLSInfo.

package defaultpolicies

default last_token_matches_tls = false

last_token_matches_tls {
token := input.connection.path.path_segments[input.connection.path.index].token
cert := input.auth_info.certificate
io.jwt.verify_es256(token, cert) # signature verification
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2020 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 tests

import (
"github.com/dgrijalva/jwt-go"
"github.com/networkservicemesh/api/pkg/api/networkservice"
)

const (
KEY = "test"
)

func generateTokenWithClaims(claims *jwt.StandardClaims) string {
token, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(KEY))
return token
}

func getConnectionWithTokens(tokens []string) *networkservice.Connection {
rv := &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{},
},
}

for _, token := range tokens {
rv.Path.PathSegments = append(rv.Path.PathSegments, &networkservice.PathSegment{
Token: token,
})
}

return rv
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) 2020 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 tests

import (
"context"
"io/ioutil"
"testing"
"time"

"github.com/networkservicemesh/sdk/pkg/tools/opautils"

"github.com/dgrijalva/jwt-go"
"github.com/open-policy-agent/opa/rego"
"github.com/stretchr/testify/require"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)


func TestNoTokensExpiredPolicy(t *testing.T){
suits := []struct {
name string
tokens []string
isNotExpired bool
}{
{
name: "simple positive test with one token",
tokens: []string{
generateTokenWithClaims(&jwt.StandardClaims{
ExpiresAt: time.Date(3000, 1, 1, 1, 1, 1, 1, time.UTC).Unix(),
}),
},
isNotExpired: true,
},
{
name: "negative test with expired/not expired tokens",
tokens: []string{
generateTokenWithClaims(&jwt.StandardClaims{
ExpiresAt: time.Date(3000, 1, 1, 1, 1, 1, 1, time.UTC).Unix(),
}),
generateTokenWithClaims(&jwt.StandardClaims{
ExpiresAt: time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC).Unix(),
}),
},
isNotExpired: false,
},
}

policyBytes, err := ioutil.ReadFile("../tokensexpired.rego")
require.Nil(t, err)

p, err := rego.New(
rego.Query("data.defaultpolicies.no_tokens_expired"),
rego.Module("tokensexpired.rego", string(policyBytes))).PrepareForEval(context.Background())
require.Nilf(t, err, "failed to create new rego policy: %v", err)

for i := range suits {
s := suits[i]

conn := getConnectionWithTokens(s.tokens)
require.Nil(t, err)

input, err := opautils.PreparedOpaInput(conn, nil, opautils.Request, opautils.Client)
require.Nil(t, err)

t.Run(s.name, func(t *testing.T) {
checkResult := func(err error) {
if s.isNotExpired {
require.Nil(t, err)
return
}

require.NotNil(t, err)
s, ok := status.FromError(err)
require.True(t, ok, "error without error status code")
require.Equal(t, s.Code(), codes.PermissionDenied, "wrong error status code")
}

err = opautils.CheckPolicy(context.Background(), &p, input)
checkResult(err)
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) 2020 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 tests

import (
"context"
"io/ioutil"
"testing"

"github.com/networkservicemesh/sdk/pkg/tools/opautils"

"github.com/dgrijalva/jwt-go"
"github.com/open-policy-agent/opa/rego"
"github.com/stretchr/testify/require"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func TestTokensMatchingPolicy(t *testing.T){
suits := []struct {
name string
tokens []string
isMatching bool
}{
{
name: "simple positive test with two tokens",
tokens: []string{
generateTokenWithClaims(&jwt.StandardClaims{
Audience: "nsmgr1",
Subject: "nsc",
}),
generateTokenWithClaims(&jwt.StandardClaims{
Audience: "nsmgr2",
Subject: "nsmgr1",
}),
},
isMatching: true,
},
{
name: "negative test with not matches tokens",
tokens: []string{
generateTokenWithClaims(&jwt.StandardClaims{
Audience: "nsmgr1",
Subject: "nsc",
}),
generateTokenWithClaims(&jwt.StandardClaims{
Audience: "nsmgr2",
Subject: "nsmgr1",
}),
generateTokenWithClaims(&jwt.StandardClaims{
Audience: "nse",
Subject: "illegal subject",
}),
},
isMatching: false,
},
}

policyBytes, err := ioutil.ReadFile("../tokensmatching.rego")
require.Nil(t, err)

p, err := rego.New(
rego.Query("data.defaultpolicies.valid_sub_aud_in_path"),
rego.Module("tokensmatching.rego", string(policyBytes))).PrepareForEval(context.Background())
require.Nilf(t, err, "failed to create new rego policy: %v", err)

for i := range suits {
s := suits[i]

conn := getConnectionWithTokens(s.tokens)
require.Nil(t, err)

input, err := opautils.PreparedOpaInput(conn, nil, opautils.Request, opautils.Client)
require.Nil(t, err)

t.Run(s.name, func(t *testing.T) {
checkResult := func(err error) {
if s.isMatching {
require.Nil(t, err)
return
}

require.NotNil(t, err)
s, ok := status.FromError(err)
require.True(t, ok, "error without error status code")
require.Equal(t, s.Code(), codes.PermissionDenied, "wrong error status code")
}

err = opautils.CheckPolicy(context.Background(), &p, input)
checkResult(err)
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# No Tokens in path expired - none of the tokens in the chain are expired.

package defaultpolicies

default no_tokens_expired = false

no_tokens_expired {
not tokens_expired
}

tokens_expired {
token := input.connection.path.path_segments[_].token
[_, payload, _] := io.jwt.decode(token) #get jwt claims
now > payload.exp
}

now = s {
ns := time.now_ns()
s := ns / 1e9
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Token.Aud for token n in path matches Token.Sub for n+1 token in chain

package defaultpolicies

default not_match_tokens_exist = false
default valid_sub_aud_in_path = false

valid_sub_aud_in_path {
not not_match_tokens_exist
}

not_match_tokens_exist {
token := input.connection.path.path_segments[i].token
next_token := input.connection.path.path_segments[i+1].token
[_, payload, _] := io.jwt.decode(token)
[_, next_payload, _] := io.jwt.decode(next_token)
payload.aud != next_payload.sub
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Tokens valid - checks the validity of some or all of the tokens in the path.

package defaultpolicies

default tokens_valid = false

tokens_valid {
token := input.connection.path.path_segments[input.connection.path.index].token
cert := input.auth_info.certificate
spiffe_id := input.auth_info.spiffe_id # spiffe_id from tlsInfo(SVIDx509Cert)
io.jwt.verify_es256(token, cert) # signature verification
[_, payload, _] := io.jwt.decode(token)
payload.sub == spiffe_id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2020 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 usecases

import (
"github.com/dgrijalva/jwt-go"

"github.com/networkservicemesh/sdk/pkg/tools/token"

"google.golang.org/grpc/credentials"

"time"
)

const (
KEY = "test"
)

func tokenGeneratorFunc(claims *jwt.StandardClaims) token.GeneratorFunc {
return func(_ credentials.AuthInfo) (string, time.Time, error) {
genToken, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(KEY))
return genToken, time.Time{}, nil;
}
}
Loading