diff --git a/Makefile b/Makefile index c26d023b8cb1f94c79d98c681885b8f6f27d38ef..60bad1af4a20e5d5fe0bf62a2ac561db1c17383d 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,11 @@ container: build container-debug: docker buildx build --rm -t gnmi-target-debug --load -f ./Dockerfile.debug . +self-certs: + mkdir -p ./artifacts/ssl/private + mkdir -p ./artifacts/ssl/certs + openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout ./artifacts/ssl/private/gnmi-target-selfsigned.key -out ./artifacts/ssl/certs/gnmi-target-selfsigned.crt + # other targets cross: go build -o ${OUTPUT} diff --git a/README.md b/README.md index 7e69173d2a2d875277d8a0b7616cf21be4c4f296..70803cfcf1c432aba0d048c5475441c79cd1961e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,28 @@ To update the generated ygot structs from yang modules you can simply run `make To use the target: (after the obligatory `go install`) run `gnmi-target start` to start the gNMI server with default configuration (OS client: ubuntu) -To request all data with `gnmiclient`: run `gnmic -a localhost:7030 -u admin -p admsdsdin --insecure get --path "/"` +### Options to start the gNMI target + +To start the target with TLS you have to provide a certificate (command line option `--cert <path-to-cert>`) and a private key (command line option `--key <path-to-key>`) + +It is recommended to always use TLS. + +For testing purposes the Makefile provides the option `make self-certs`. This generates a self-signed certificate and the corresponding private key. They are both stored under the directory `artifacts/ssl` in the source code directory. + +However, you can also run the target with not TLS enabled by specifying the command line option `--insecure`. + +## Requesting Data from the target + +### With TLS and self-signed certificates + +To request all data with `gnmiclient` run this command + +`gnmic -a localhost:7030 -u admin -p admsdsdin --skip-verify get --path "/"` + +### With no TLS +To request all data with `gnmiclient`: run this command + +`gnmic -a localhost:7030 -u admin -p admsdsdin --insecure get --path "/"` --- diff --git a/cmd/root.go b/cmd/root.go index 0733ed033935ba65b58cb2bc367b8cdf9bb40f7b..060477616ca2ecd1e5715a61ead7141263e98ed8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,20 +1,20 @@ /* -Copyright © 2021 da/net Research Group <danet@h-da.de> +Copyright © 2023 da/net Research Group <danet@h-da.de> All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE diff --git a/cmd/start.go b/cmd/start.go index 03e81f33750f8f3759ee99d47268c9a5a2e59b5d..48dceeff0dff5ede7bfe93b00f3473af5465407a 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -1,5 +1,5 @@ /* -Copyright © 2021 da/net Research Group <danet@h-da.de> +Copyright © 2023 da/net Research Group <danet@h-da.de> All rights reserved. Redistribution and use in source and binary forms, with or without @@ -41,23 +41,28 @@ import ( ) var ( - bindAddress string - configFile string - osclient string - logLevel string + bindAddress string // IP/Port to bind to listen for the gnmi server + configFile string // my config file to use + osclient string // TODO unclear + logLevel string // which loglevel to use + certFile string // the location of the file containing the X.509 certificates for the gnmi server + keyFile string // the location of the file containing the key for the certificates for the gnmi server + insecure *bool // set to true if insecure operations is needed, i.e., do not use TLS + // Below is qkdn specific information udpQL1AddrString string ql1Name string udpQL2AddrString string ql2Name string + // End of qkdn specific information ) // startCmd represents the start command var startCmd = &cobra.Command{ Use: "start", Short: "Start gnmi server", - Long: `A Linux GNMI target that does GNMI target stuff`, + Long: `A gNMI target`, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Startup GNMI Target...") + fmt.Println("Startup of GNMI Target...") lvl := viper.GetString("logLevel") // parse string, this is built-in feature of logrus @@ -77,7 +82,7 @@ var startCmd = &cobra.Command{ } gnmitTarget := gnmitarget.NewGnmiTarget(&qkdnBootInfo) - if err := gnmitTarget.Start(viper.GetString("bindAddress")); err != nil { + if err := gnmitTarget.Start(viper.GetString("bindAddress"), viper.GetString("certFile"), viper.GetString("keyFile"), *insecure); err != nil { logrus.Fatal(err) } }, @@ -88,7 +93,9 @@ func init() { startCmd.Flags().StringVarP(&configFile, "config", "c", "config/openconfig.json", "system configuration") startCmd.Flags().StringVarP(&bindAddress, "bind_address", "a", ":7030", "address to bind to") startCmd.Flags().StringVarP(&logLevel, "log", "l", "info", "loglevel") - startCmd.Flags().Bool("tls", false, "Use Viper for configuration") + insecure = startCmd.Flags().Bool("insecure", false, "If true do not use TLS") + startCmd.Flags().StringVarP(&certFile, "cert", "", "", "location of the cert file") + startCmd.Flags().StringVarP(&keyFile, "key", "", "", "location of the key file") startCmd.Flags().StringVarP(&osclient, "osclient", "o", "ubuntu", "os client to use by system") startCmd.Flags().StringVarP(&udpQL1AddrString, "my_QLE_socket", "", "[::1]:50900", "local quantum element's address") startCmd.Flags().StringVarP(&ql1Name, "my_name", "", "ekms-ql1", "The name of the local quantumlayer") @@ -98,7 +105,9 @@ func init() { viper.BindPFlag("bindAddress", startCmd.Flags().Lookup("bind_address")) viper.BindPFlag("configFile", startCmd.Flags().Lookup("config")) viper.BindPFlag("logLevel", startCmd.Flags().Lookup("log")) - viper.BindPFlag("useTLS", startCmd.Flags().Lookup("tls")) + viper.BindPFlag("insecure", startCmd.Flags().Lookup("insecure")) + viper.BindPFlag("certFile", startCmd.Flags().Lookup("cert")) + viper.BindPFlag("keyFile", startCmd.Flags().Lookup("key")) viper.BindPFlag("osclient", startCmd.Flags().Lookup("osclient")) viper.BindPFlag("my_QLE_socket", startCmd.Flags().Lookup("my-address")) viper.BindPFlag("my_name", startCmd.Flags().Lookup("my-name")) diff --git a/gnmitarget/config.go b/gnmitarget/config.go index 7e690f726637c3d3d9ad32b2e4706922e10c8c46..d78f26b44a376c6cade88f5406a3b549e713e00b 100644 --- a/gnmitarget/config.go +++ b/gnmitarget/config.go @@ -127,5 +127,10 @@ func (gt *GnmiTarget) InitializeConfig() (ygot.ValidatedGoStruct, error) { }() } - return conf, customerrs.CombinedErrListError{Errors: errs} + if len(errs) != 0 { + return conf, customerrs.CombinedErrListError{Errors: errs} + } else { + return conf, nil + } + } diff --git a/gnmitarget/target.go b/gnmitarget/target.go index acd7c789b409d9aa45f85be321e2517ee5c9c654..11111b8e58484dc279076e54584f5265efdf269a 100644 --- a/gnmitarget/target.go +++ b/gnmitarget/target.go @@ -1,8 +1,6 @@ package gnmitarget import ( - "time" - "code.fbi.h-da.de/danet/gnmi-target/etsiqkdnclient" "code.fbi.h-da.de/danet/gnmi-target/gnmiserver" not "code.fbi.h-da.de/danet/gnmi-target/notifications" @@ -13,6 +11,7 @@ import ( "reflect" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/reflection" "code.fbi.h-da.de/danet/gnmi-target/modeldata" @@ -41,10 +40,7 @@ func NewGnmiTarget(qkdnBootInfo *etsiqkdnclient.EtsiQkdClientInfo) *GnmiTarget { } } -func (gt *GnmiTarget) Start(bindAddress string) error { - //NOTE: This sleep is currently just for testing purposes to play around with - //containerlab. - time.Sleep(5 * time.Second) +func (gt *GnmiTarget) Start(bindAddress string, certFile string, keyFile string, insecure bool) error { schema, err := gnmitargetygot.Schema() if err != nil { @@ -67,6 +63,7 @@ func (gt *GnmiTarget) Start(bindAddress string) error { } } + // Initialize ygot structs with the current device configuration config, err := gt.InitializeConfig() if err != nil { log.Error(err) @@ -77,21 +74,35 @@ func (gt *GnmiTarget) Start(bindAddress string) error { log.Fatalf("error in creating GNMI target: %v", err) } - // Create new GRPC Server without service registert - grpcServer := grpc.NewServer() + var grpcServer *grpc.Server + + if insecure == false { + // Setup credentials for secured message transport + grpcCredentials, err := credentials.NewServerTLSFromFile(certFile, keyFile) + if err != nil { + log.Fatalf("error in opening the TLS certificate: %v", err) + } + + // Create new GRPC Server without service registered + grpcServer = grpc.NewServer(grpc.Creds(grpcCredentials)) + } else { + log.Infof("\n\n*****WARNING*********WARNING*****\nStarting without secured gnmi server!\nAll gnmi transmissions are unencrypted\n*****WARNING*********WARNING*****\n\n") + // Create new GRPC Server without service registered + grpcServer = grpc.NewServer() + } // Register GNMI Server pbGNMI.RegisterGNMIServer(grpcServer, gnmiServer) reflection.Register(grpcServer) // Start Server - log.Infof("Starting target server to listen on %s", bindAddress) + log.Infof("Starting gnmi-target server to listen on %s", bindAddress) listener, err := net.Listen("tcp", bindAddress) if err != nil { log.Fatalf("Failed to list due to: %v", err) } - log.Info("Target is starting to serve requests") + log.Info("gnmi-target is ready to serve requests.") err = grpcServer.Serve(listener) if err != nil { log.Fatalf("Failed to serve requests due to: %v", err)