Skip to content

Commit

Permalink
Merge branch 'sonic-net:master' into upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
hdwhdw authored Nov 22, 2024
2 parents d62df54 + 2c4b9c8 commit 5d68f51
Show file tree
Hide file tree
Showing 16 changed files with 944 additions and 74 deletions.
47 changes: 36 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@ go.mod:

$(GO_DEPS): go.mod $(PATCHES) swsscommon_wrap
$(GO) mod vendor
$(GO) mod download golang.org/x/[email protected]
$(GO) mod download github.com/jipanyang/[email protected]
cp -r $(GOPATH)/pkg/mod/golang.org/x/[email protected]/* vendor/golang.org/x/crypto/
cp -r $(GOPATH)/pkg/mod/github.com/jipanyang/[email protected]/* vendor/github.com/jipanyang/gnxi/

# Apply patch from sonic-mgmt-common, ignore glog.patch because glog version changed
sed -i 's/patch -d $${DEST_DIR}\/github.com\/golang\/glog/\#patch -d $${DEST_DIR}\/github.com\/golang\/glog/g' $(MGMT_COMMON_DIR)/patches/apply.sh
$(MGMT_COMMON_DIR)/patches/apply.sh vendor
sed -i 's/#patch -d $${DEST_DIR}\/github.com\/golang\/glog/patch -d $${DEST_DIR}\/github.com\/golang\/glog/g' $(MGMT_COMMON_DIR)/patches/apply.sh

chmod -R u+w vendor
patch -d vendor -p0 < patches/gnmi_cli.all.patch
patch -d vendor -p0 < patches/gnmi_set.patch
patch -d vendor -p0 < patches/gnmi_get.patch
patch -d vendor -p0 < patches/gnmi_path.patch
patch -d vendor -p0 < patches/gnmi_xpath.patch
git apply patches/0001-Updated-to-filter-and-write-to-file.patch

touch $@

go-deps: $(GO_DEPS)
Expand All @@ -69,28 +69,53 @@ go-deps-clean:
$(RM) -r vendor

sonic-gnmi: $(GO_DEPS)
# advancetls 1.0.0 release need following patch to build by go-1.19
patch -d vendor -p0 < patches/0002-Fix-advance-tls-build-with-go-119.patch
# build service first which depends on advancetls
ifeq ($(CROSS_BUILD_ENVIRON),y)
$(GO) build -o ${GOBIN}/telemetry -mod=vendor $(BLD_FLAGS) github.com/sonic-net/sonic-gnmi/telemetry
ifneq ($(ENABLE_DIALOUT_VALUE),0)
$(GO) build -o ${GOBIN}/dialout_client_cli -mod=vendor $(BLD_FLAGS) github.com/sonic-net/sonic-gnmi/dialout/dialout_client_cli
endif
$(GO) build -o ${GOBIN}/gnmi_get -mod=vendor github.com/jipanyang/gnxi/gnmi_get
$(GO) build -o ${GOBIN}/gnmi_set -mod=vendor github.com/jipanyang/gnxi/gnmi_set
$(GO) build -o ${GOBIN}/gnmi_cli -mod=vendor github.com/openconfig/gnmi/cmd/gnmi_cli
$(GO) build -o ${GOBIN}/gnoi_client -mod=vendor github.com/sonic-net/sonic-gnmi/gnoi_client
$(GO) build -o ${GOBIN}/gnmi_dump -mod=vendor github.com/sonic-net/sonic-gnmi/gnmi_dump
else
$(GO) install -mod=vendor $(BLD_FLAGS) github.com/sonic-net/sonic-gnmi/telemetry
ifneq ($(ENABLE_DIALOUT_VALUE),0)
$(GO) install -mod=vendor $(BLD_FLAGS) github.com/sonic-net/sonic-gnmi/dialout/dialout_client_cli
endif
$(GO) install -mod=vendor github.com/sonic-net/sonic-gnmi/gnoi_client
$(GO) install -mod=vendor github.com/sonic-net/sonic-gnmi/gnmi_dump
endif

# download and apply patch for gnmi client, which will break advancetls
# backup crypto and gnxi
mkdir backup_crypto
cp -r vendor/golang.org/x/crypto/* backup_crypto/

# download and patch crypto and gnxi
$(GO) mod download golang.org/x/[email protected]
cp -r $(GOPATH)/pkg/mod/golang.org/x/[email protected]/* vendor/golang.org/x/crypto/
chmod -R u+w vendor
patch -d vendor -p0 < patches/gnmi_cli.all.patch
patch -d vendor -p0 < patches/gnmi_set.patch
patch -d vendor -p0 < patches/gnmi_get.patch
git apply patches/0001-Updated-to-filter-and-write-to-file.patch

ifeq ($(CROSS_BUILD_ENVIRON),y)
$(GO) build -o ${GOBIN}/gnmi_get -mod=vendor github.com/jipanyang/gnxi/gnmi_get
$(GO) build -o ${GOBIN}/gnmi_set -mod=vendor github.com/jipanyang/gnxi/gnmi_set
$(GO) build -o ${GOBIN}/gnmi_cli -mod=vendor github.com/openconfig/gnmi/cmd/gnmi_cli
else
$(GO) install -mod=vendor github.com/jipanyang/gnxi/gnmi_get
$(GO) install -mod=vendor github.com/jipanyang/gnxi/gnmi_set
$(GO) install -mod=vendor github.com/openconfig/gnmi/cmd/gnmi_cli
$(GO) install -mod=vendor github.com/sonic-net/sonic-gnmi/gnoi_client
$(GO) install -mod=vendor github.com/sonic-net/sonic-gnmi/gnmi_dump
endif

# restore old version
rm -rf vendor/golang.org/x/crypto/
mv backup_crypto/ vendor/golang.org/x/crypto/

swsscommon_wrap:
make -C swsscommon

Expand Down
204 changes: 203 additions & 1 deletion gnmi_server/clientCertAuth.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package gnmi

import (
"crypto/tls"
"crypto/x509"
"io"
"net/http"
"time"
"github.com/sonic-net/sonic-gnmi/common_utils"
"github.com/sonic-net/sonic-gnmi/swsscommon"
"github.com/golang/glog"
Expand All @@ -9,9 +14,103 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
"google.golang.org/grpc/security/advancedtls"
)

func ClientCertAuthenAndAuthor(ctx context.Context, serviceConfigTableName string) (context.Context, error) {
const DEFAULT_CRL_EXPIRE_DURATION time.Duration = 24 * 60* 60 * time.Second

type Crl struct {
thisUpdate time.Time
nextUpdate time.Time
crl []byte
}

// CRL content cache
var CrlCache map[string]*Crl = nil

// CRL content cache
var CrlDxpireDuration time.Duration = DEFAULT_CRL_EXPIRE_DURATION

func InitCrlCache() {
if CrlCache == nil {
CrlCache = make(map[string]*Crl)
}
}

func ReleaseCrlCache() {
for mapkey, _ := range(CrlCache) {
delete(CrlCache, mapkey)
}
}

func AppendCrlToCache(url string, rawCRL []byte) {
crl := new(Crl)
crl.thisUpdate = time.Now()
crl.nextUpdate = time.Now()
crl.crl = rawCRL

CrlCache[url] = crl
}

func GetCrlExpireDuration() time.Duration {
return CrlDxpireDuration
}

func SetCrlExpireDuration(duration time.Duration) {
CrlDxpireDuration = duration
}

func CrlExpired(crl *Crl) bool {
now := time.Now()
expireTime := crl.thisUpdate.Add(GetCrlExpireDuration())
glog.Infof("CrlExpired expireTime: %s, now: %s", expireTime.Format(time.ANSIC), now.Format(time.ANSIC))
// CRL expiresion policy follow the policy of Get-CRLFreshness command in following doc:
// https://learn.microsoft.com/en-us/archive/blogs/russellt/get-crlfreshness
// The policy are:
// 1. CRL expired when current time is after CRL expiresion time, which defined in "Next CRL Publish" extension.
// Because CRL cached in memory, GNMI support OnDemand CRL referesh by restart GNMI service.
return now.After(expireTime)
}

func CrlNeedUpdate(crl *Crl) bool {
now := time.Now()
glog.Infof("CrlNeedUpdate nextUpdate: %s, now: %s", crl.nextUpdate.Format(time.ANSIC), now.Format(time.ANSIC))
return now.After(crl.nextUpdate)
}

func RemoveExpiredCrl() {
for mapkey, crl := range(CrlCache) {
if CrlExpired(crl) {
glog.Infof("RemoveExpiredCrl key: %s", mapkey)
delete(CrlCache, mapkey)
}
}
}

func SearchCrlCache(url string) (bool, *Crl) {
crl, exist := CrlCache[url]
if !exist {
glog.Infof("SearchCrlCache not found cache for url: %s", url)
return false, nil
}

if CrlExpired(crl) {
glog.Infof("SearchCrlCache crl expired: %s", url)
delete(CrlCache, url)
return false, nil
}

if CrlNeedUpdate(crl) {
glog.Infof("SearchCrlCache crl need update: %s", url)
delete(CrlCache, url)
return false, nil
}

glog.Infof("SearchCrlCache found cache for url: %s", url)
return true, crl
}

func ClientCertAuthenAndAuthor(ctx context.Context, serviceConfigTableName string, enableCrl bool) (context.Context, error) {
rc, ctx := common_utils.GetContext(ctx)
p, ok := peer.FromContext(ctx)
if !ok {
Expand Down Expand Up @@ -44,9 +143,112 @@ func ClientCertAuthenAndAuthor(ctx context.Context, serviceConfigTableName strin
}
}

if enableCrl {
err := VerifyCertCrl(tlsAuth.State)
if err != nil {
glog.Infof("[%s] Failed to verify cert with CRL; %v", rc.ID, err)
return ctx, err
}
}

return ctx, nil
}

func TryDownload(url string) bool {
glog.Infof("Download CRL start: %s", url)
resp, err := http.Get(url)

if resp != nil {
defer resp.Body.Close()
}

if err != nil || resp.StatusCode != http.StatusOK {
glog.Infof("Download CRL: %s failed: %v", url, err)
return false
}

crlContent, err := io.ReadAll(resp.Body)
if err != nil {
glog.Infof("Download CRL: %s failed: %v", url, err)
return false
}

glog.Infof("Download CRL: %s successed", url)
AppendCrlToCache(url, crlContent)

return true
}

func GetCrlUrls(cert x509.Certificate) []string {
glog.Infof("Get Crl Urls for cert: %v", cert.CRLDistributionPoints)
return cert.CRLDistributionPoints
}

func DownloadNotCachedCrl(crlUrlArray []string) bool {
crlAvaliable := false
for _, crlUrl := range crlUrlArray{
exist, _ := SearchCrlCache(crlUrl)
if exist {
crlAvaliable = true
} else {
downloaded := TryDownload(crlUrl)
if downloaded {
crlAvaliable = true
}
}
}

return crlAvaliable
}

func CreateStaticCRLProvider() *advancedtls.StaticCRLProvider {
crlArray := make([][]byte, 1)
for mapkey, item := range(CrlCache) {
if CrlExpired(item) {
glog.Infof("CreateStaticCRLProvider remove expired crl: %s", mapkey)
delete(CrlCache, mapkey)
} else {
glog.Infof("CreateStaticCRLProvider add crl: %s content: %v", mapkey, item.crl)
crlArray = append(crlArray, item.crl)
}
}

return advancedtls.NewStaticCRLProvider(crlArray)
}

func VerifyCertCrl(tlsConnState tls.ConnectionState) error {
InitCrlCache()
// Check if any CRL already exist in local
crlUriArray := GetCrlUrls(*tlsConnState.VerifiedChains[0][0])
if len(crlUriArray) == 0 {
glog.Infof("Cert does not contains and CRL distribution points")
return nil
}

crlAvaliable := DownloadNotCachedCrl(crlUriArray)
if !crlAvaliable {
// Every certificate will contain multiple CRL distribution points.
// If all CRLs are not available, the certificate validation should be blocked.
glog.Infof("VerifyCertCrl can't download CRL and verify cert: %v", crlUriArray)
return status.Errorf(codes.Unauthenticated, "Can't download CRL and verify cert")
}

// Build CRL provider from cache and verify cert
crlProvider := CreateStaticCRLProvider()
err := advancedtls.CheckChainRevocation(tlsConnState.VerifiedChains, advancedtls.RevocationOptions{
DenyUndetermined: false,
CRLProvider: crlProvider,
})

if err != nil {
glog.Infof("VerifyCertCrl peer certificate revoked: %v", err.Error())
return status.Error(codes.Unauthenticated, "Peer certificate revoked")
}

glog.Infof("VerifyCertCrl verify cert passed: %v", crlUriArray)
return nil
}

func PopulateAuthStructByCommonName(certCommonName string, auth *common_utils.AuthInfo, serviceConfigTableName string) error {
if serviceConfigTableName == "" {
return status.Errorf(codes.Unauthenticated, "Service config table name should not be empty")
Expand Down
3 changes: 2 additions & 1 deletion gnmi_server/client_subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,13 @@ func (c *Client) Run(stream gnmipb.GNMI_SubscribeServer) (err error) {
/* For any other target or no target create new Transl Client. */
dc, err = sdc.NewTranslClient(prefix, paths, ctx, extensions, sdc.TranslWildcardOption{})
}
defer dc.Close()

if err != nil {
return grpc.Errorf(codes.NotFound, "%v", err)
}

defer dc.Close()

switch mode {
case gnmipb.SubscriptionList_STREAM:
c.stop = make(chan struct{}, 1)
Expand Down
Loading

0 comments on commit 5d68f51

Please sign in to comment.