diff --git a/chaoscenter/authentication/api/main.go b/chaoscenter/authentication/api/main.go index b0bffcd41d6..4736a48af97 100644 --- a/chaoscenter/authentication/api/main.go +++ b/chaoscenter/authentication/api/main.go @@ -4,11 +4,13 @@ import ( "flag" "fmt" "net" + "net/http" "runtime" "time" response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers" "github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig" + "google.golang.org/grpc/credentials" grpcHandler "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/grpc" "github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware" @@ -132,6 +134,14 @@ func main() { validatedAdminSetup(applicationService) go runGrpcServer(applicationService) + if utils.EnableInternalTls { + if utils.CustomTlsCertPath != "" && utils.TlSKeyPath != "" { + go runGrpcServerWithTLS(applicationService) + } else { + log.Fatalf("Failure to start chaoscenter authentication GRPC server due to empty TLS cert file path and TLS key path") + } + } + runRestServer(applicationService) } @@ -186,6 +196,29 @@ func runRestServer(applicationService services.ApplicationService) { routes.ProjectRouter(app, applicationService) log.Infof("Listening and serving HTTP on %s", utils.Port) + + if utils.EnableInternalTls { + log.Infof("Listening and serving HTTPS on %s", utils.PortHttps) + if utils.CustomTlsCertPath != "" && utils.TlSKeyPath != "" { + conf := utils.GetTlsConfig() + + server := http.Server{ + Addr: utils.PortHttps, + Handler: app, + TLSConfig: conf, + } + log.Infof("Listening and serving HTTPS on %s", utils.Port) + go func() { + err := server.ListenAndServeTLS("", "") + if err != nil { + log.Fatalf("Failure to start litmus-portal authentication REST server due to %v", err) + } + }() + } else { + log.Fatalf("Failure to start chaoscenter authentication REST server due to empty TLS cert file path and TLS key path") + } + } + err := app.Run(utils.Port) if err != nil { log.Fatalf("Failure to start litmus-portal authentication REST server due to %v", err) @@ -205,6 +238,34 @@ func runGrpcServer(applicationService services.ApplicationService) { log.Infof("Listening and serving gRPC on %s", utils.GrpcPort) err = grpcServer.Serve(lis) if err != nil { - log.Fatalf("Failure to start litmus-portal authentication GRPC server due to %v", err) + log.Fatalf("Failure to start chaoscenter authentication GRPC server due to %v", err) + } +} + +func runGrpcServerWithTLS(applicationService services.ApplicationService) { + + // Starting gRPC server + lis, err := net.Listen("tcp", utils.GrpcPortHttps) + if err != nil { + log.Fatalf("Failure to start litmus-portal authentication server due to %s", err) + } + + // configuring TLS config based on provided certificates & keys + conf := utils.GetTlsConfig() + + // create tls credentials + tlsCredentials := credentials.NewTLS(conf) + + // create grpc server with tls credential + grpcServer := grpc.NewServer(grpc.Creds(tlsCredentials)) + + grpcApplicationServer := grpcHandler.ServerGrpc{ApplicationService: applicationService} + + grpcPresenter.RegisterAuthRpcServiceServer(grpcServer, &grpcApplicationServer) + + log.Infof("Listening and serving gRPC on %s with TLS", utils.GrpcPort) + err = grpcServer.Serve(lis) + if err != nil { + log.Fatalf("Failure to start chaoscenter authentication GRPC server due to %v", err) } } diff --git a/chaoscenter/authentication/pkg/utils/configs.go b/chaoscenter/authentication/pkg/utils/configs.go index 252e5a3b67c..684861ad7f6 100644 --- a/chaoscenter/authentication/pkg/utils/configs.go +++ b/chaoscenter/authentication/pkg/utils/configs.go @@ -1,8 +1,12 @@ package utils import ( + "crypto/tls" + "crypto/x509" "os" "strconv" + + log "github.com/sirupsen/logrus" ) var ( @@ -20,9 +24,15 @@ var ( DexClientID = os.Getenv("DEX_OAUTH_CLIENT_ID") DexClientSecret = os.Getenv("DEX_OAUTH_CLIENT_SECRET") DexOIDCIssuer = os.Getenv("OIDC_ISSUER") + EnableInternalTls = getEnvAsBool("ENABLE_INTERNAL_TLS", false) + CustomTlsCertPath = os.Getenv("CUSTOM_TLS_CERT_PATH") + TlSKeyPath = os.Getenv("TLS_KEY_PATH") + CaCertPath = os.Getenv("CA_CERT_PATH") DBName = "auth" Port = ":3000" + PortHttps = ":3001" GrpcPort = ":3030" + GrpcPortHttps = ":3031" UserCollection = "users" ProjectCollection = "project" AuthConfigCollection = "auth-config" @@ -33,6 +43,7 @@ var ( PasswordEncryptionCost = 15 DefaultLitmusGqlGrpcEndpoint = "localhost" DefaultLitmusGqlGrpcPort = ":8000" + //DefaultLitmusGqlGrpcPortHttps = ":8001" // enable when in use ) func getEnvAsInt(name string, defaultVal int) int { @@ -50,3 +61,32 @@ func getEnvAsBool(name string, defaultVal bool) bool { } return defaultVal } + +func GetTlsConfig() *tls.Config { + + // read ca's cert, verify to client's certificate + caPem, err := os.ReadFile(CaCertPath) + if err != nil { + log.Fatal(err) + } + + // create cert pool and append ca's cert + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(caPem) { + log.Fatal(err) + } + + // read server cert & key + serverCert, err := tls.LoadX509KeyPair(CustomTlsCertPath, TlSKeyPath) + if err != nil { + log.Fatal(err) + } + + // configuring TLS config based on provided certificates & keys to + conf := &tls.Config{ + Certificates: []tls.Certificate{serverCert}, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: certPool, + } + return conf +} diff --git a/chaoscenter/client-ext.conf b/chaoscenter/client-ext.conf new file mode 100644 index 00000000000..a83907fe4a9 --- /dev/null +++ b/chaoscenter/client-ext.conf @@ -0,0 +1 @@ +subjectAltName=DNS:*.someclient.com,IP:0.0.0.0,DNS:localhost diff --git a/chaoscenter/graphql/server/pkg/authorization/validate.go b/chaoscenter/graphql/server/pkg/authorization/validate.go index a7b5e77329d..15e2a58b048 100644 --- a/chaoscenter/graphql/server/pkg/authorization/validate.go +++ b/chaoscenter/graphql/server/pkg/authorization/validate.go @@ -5,6 +5,7 @@ import ( "errors" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/grpc" + "github.com/sirupsen/logrus" grpc2 "google.golang.org/grpc" ) @@ -20,7 +21,8 @@ func ValidateRole(ctx context.Context, projectID string, requiredRoles, invitation) if err != nil { - return errors.New("permission_denied") + logrus.Error(err) + return errors.New("permission_denied: " + err.Error()) } return nil } diff --git a/chaoscenter/graphql/server/pkg/grpc/auth_grpc_client.go b/chaoscenter/graphql/server/pkg/grpc/auth_grpc_client.go index 96c9c73ee72..a5d188f1628 100644 --- a/chaoscenter/graphql/server/pkg/grpc/auth_grpc_client.go +++ b/chaoscenter/graphql/server/pkg/grpc/auth_grpc_client.go @@ -3,6 +3,9 @@ package grpc import ( "context" "errors" + "strconv" + + "google.golang.org/grpc/credentials" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/protos" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/utils" @@ -13,10 +16,34 @@ import ( // GetAuthGRPCSvcClient returns an RPC client for Authentication service func GetAuthGRPCSvcClient(conn *grpc.ClientConn) (protos.AuthRpcServiceClient, *grpc.ClientConn) { - conn, err := grpc.Dial(utils.Config.LitmusAuthGrpcEndpoint+utils.Config.LitmusAuthGrpcPort, grpc.WithBlock(), grpc.WithInsecure()) + + enableHTTPSConnection, err := strconv.ParseBool(utils.Config.EnableInternalTls) if err != nil { - logrus.Fatalf("did not connect: %s", err) + logrus.Errorf("unable to parse boolean value %v", err) } + + if enableHTTPSConnection { + if utils.Config.ServerTlsCertPath != "" { + // configuring TLS config based on provided certificates & keys + conf := utils.GetTlsConfig(utils.Config.ClientTlsCertPath, utils.Config.ClientTlsKeyPath, false) + + tlsCredential := credentials.NewTLS(conf) + + // Set up a connection to the server. + conn, err = grpc.NewClient(utils.Config.LitmusAuthGrpcEndpoint+utils.Config.LitmusAuthGrpcPortHttps, grpc.WithTransportCredentials(tlsCredential)) + if err != nil { + logrus.Fatalf("did not connect: %v", err) + } + } else { + logrus.Fatalf("Failure to start chaoscenter authentication REST server due to empty TLS cert file path and TLS key path") + } + } else { + conn, err = grpc.Dial(utils.Config.LitmusAuthGrpcEndpoint+utils.Config.LitmusAuthGrpcPort, grpc.WithBlock(), grpc.WithInsecure()) + if err != nil { + logrus.Fatalf("did not connect: %s", err) + } + } + return protos.NewAuthRpcServiceClient(conn), conn } diff --git a/chaoscenter/graphql/server/server.go b/chaoscenter/graphql/server/server.go index ea8729bc9f5..c849ea6a02d 100644 --- a/chaoscenter/graphql/server/server.go +++ b/chaoscenter/graphql/server/server.go @@ -4,6 +4,8 @@ import ( "regexp" "strconv" + "google.golang.org/grpc/credentials" + "github.com/gin-gonic/gin" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/api/middleware" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/chaoshub" @@ -35,6 +37,7 @@ import ( "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb/config" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/handlers" pb "github.com/litmuschaos/litmus/chaoscenter/graphql/server/protos" + "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus" "google.golang.org/grpc" ) @@ -100,7 +103,20 @@ func main() { if err := validateVersion(); err != nil { log.Fatal(err) } + + enableHTTPSConnection, err := strconv.ParseBool(utils.Config.EnableInternalTls) + if err != nil { + logrus.Errorf("unable to parse boolean value %v", err) + } + go startGRPCServer(utils.Config.RpcPort, mongodbOperator) // start GRPC serve + if enableHTTPSConnection { + if utils.Config.ServerTlsCertPath != "" && utils.Config.ServerTlsKeyPath != "" { + go startGRPCServerWithTLS(mongodbOperator) // start GRPC serve + } else { + log.Fatalf("Failure to start chaoscenter authentication REST server due to empty TLS cert file path and TLS key path") + } + } srv := handler.New(generated.NewExecutableSchema(graph.NewConfig(mongodbOperator))) srv.AddTransport(transport.POST{}) @@ -152,8 +168,24 @@ func main() { projectEventChannel := make(chan string) go projects.ProjectEvents(projectEventChannel, mongodb.MgoClient, mongodbOperator) - log.Infof("chaos manager running at http://localhost:%s", utils.Config.HttpPort) + if enableHTTPSConnection { + log.Infof("graphql server running at https://localhost:%s", utils.Config.HttpsPort) + // configuring TLS config based on provided certificates & keys + conf := utils.GetTlsConfig(utils.Config.ServerTlsCertPath, utils.Config.ServerTlsKeyPath, true) + + server := http.Server{ + Addr: ":" + utils.Config.HttpsPort, + Handler: router, + TLSConfig: conf, + } + if utils.Config.ServerTlsCertPath != "" && utils.Config.ServerTlsKeyPath != "" { + go log.Fatal(server.ListenAndServeTLS("", "")) + } + } + + log.Infof("graphql server running at http://localhost:%s", utils.Config.HttpPort) log.Fatal(http.ListenAndServe(":"+utils.Config.HttpPort, router)) + } // startGRPCServer initializes, registers services to and starts the gRPC server for RPC calls @@ -172,3 +204,28 @@ func startGRPCServer(port string, mongodbOperator mongodb.MongoOperator) { log.Infof("GRPC server listening on %v", lis.Addr()) log.Fatal(grpcServer.Serve(lis)) } + +// startGRPCServerWithTLS initializes, registers services to and starts the gRPC server for RPC calls +func startGRPCServerWithTLS(mongodbOperator mongodb.MongoOperator) { + + lis, err := net.Listen("tcp", ":"+utils.Config.RpcPortHttps) + if err != nil { + log.Fatal("failed to listen: %w", err) + } + + // configuring TLS config based on provided certificates & keys + conf := utils.GetTlsConfig(utils.Config.ServerTlsCertPath, utils.Config.ServerTlsKeyPath, true) + + // create tls credentials + tlsCredentials := credentials.NewTLS(conf) + + // create grpc server with tls credential + grpcServer := grpc.NewServer(grpc.Creds(tlsCredentials)) + + // Register services + + pb.RegisterProjectServer(grpcServer, &projects.ProjectServer{Operator: mongodbOperator}) + + log.Infof("GRPC server listening on %v", lis.Addr()) + log.Fatal(grpcServer.Serve(lis)) +} diff --git a/chaoscenter/graphql/server/utils/config.go b/chaoscenter/graphql/server/utils/config.go new file mode 100644 index 00000000000..3aace2703a3 --- /dev/null +++ b/chaoscenter/graphql/server/utils/config.go @@ -0,0 +1,47 @@ +package utils + +import ( + "crypto/tls" + "crypto/x509" + "os" + + log "github.com/sirupsen/logrus" +) + +func GetTlsConfig(certPath string, keyPath string, isServerConfig bool) *tls.Config { + + // read ca's cert, verify to client's certificate + caPem, err := os.ReadFile(Config.CaCertPath) + if err != nil { + log.Fatal(err) + } + + // create cert pool and append ca's cert + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(caPem) { + log.Fatal(err) + } + + // read server cert & key + cert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + log.Fatal(err) + } + + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: certPool, + } + + if isServerConfig { + // configuring TLS config based on provided certificates & keys to + conf := &tls.Config{ + Certificates: []tls.Certificate{cert}, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: certPool, + } + return conf + } + + return config +} diff --git a/chaoscenter/graphql/server/utils/variables.go b/chaoscenter/graphql/server/utils/variables.go index a1e386f997f..7cd2600d655 100644 --- a/chaoscenter/graphql/server/utils/variables.go +++ b/chaoscenter/graphql/server/utils/variables.go @@ -23,17 +23,26 @@ type Configuration struct { TlsCertB64 string `split_words:"true"` LitmusAuthGrpcEndpoint string `split_words:"true" default:"localhost"` LitmusAuthGrpcPort string `split_words:"true" default:":3030"` + LitmusAuthGrpcPortHttps string `split_words:"true" default:":3031"` KubeConfigFilePath string `split_words:"true"` RemoteHubMaxSize string `split_words:"true"` SkipSslVerify string `split_words:"true"` HttpPort string `split_words:"true" default:"8080"` + HttpsPort string `split_words:"true" default:"8081"` RpcPort string `split_words:"true" default:"8000"` + RpcPortHttps string `split_words:"true" default:"8001"` InfraCompatibleVersions string `required:"true" split_words:"true"` DefaultHubGitURL string `required:"true" default:"https://github.com/litmuschaos/chaos-charts"` DefaultHubBranchName string `required:"true" split_words:"true"` CustomChaosHubPath string `split_words:"true" default:"/tmp/"` DefaultChaosHubPath string `split_words:"true" default:"/tmp/default/"` EnableGQLIntrospection string `split_words:"true" default:"false"` + EnableInternalTls string `split_words:"true" default:"false"` + ServerTlsCertPath string `split_words:"true"` + ServerTlsKeyPath string `split_words:"true"` + ClientTlsCertPath string `split_words:"true"` + ClientTlsKeyPath string `split_words:"true"` + CaCertPath string `split_words:"true"` AllowedOrigins []string `split_words:"true" default:"^(http://|https://|)litmuschaos.io(:[0-9]+|)?,^(http://|https://|)localhost(:[0-9]+|)"` } diff --git a/chaoscenter/manifests/litmus-namespaced-scope.yaml b/chaoscenter/manifests/litmus-namespaced-scope.yaml index 125dc82cde1..0edbe9827d8 100644 --- a/chaoscenter/manifests/litmus-namespaced-scope.yaml +++ b/chaoscenter/manifests/litmus-namespaced-scope.yaml @@ -245,9 +245,23 @@ spec: value: '["ci"]' - name: ALLOWED_ORIGINS value: ".*" + - name: ENABLE_INTERNAL_TLS + value: "false" + - name: SERVER_TLS_CERT_PATH + value: "" + - name: SERVER_TLS_KEY_PATH + value: "" + - name: CLIENT_TLS_CERT_PATH + value: "" + - name: CLIENT_KEY_CERT_PATH + value: "" + - name: CA_CERT_PATH + value: "" ports: - containerPort: 8080 - containerPort: 8000 + - containerPort: 8081 + - containerPort: 8001 imagePullPolicy: Always resources: requests: @@ -272,6 +286,12 @@ spec: - name: graphql-rpc-server port: 8000 targetPort: 8000 + - name: graphql-server-https + port: 9004 + targetPort: 8081 + - name: graphql-rpc-server-https + port: 8001 + targetPort: 8001 selector: component: litmusportal-server --- @@ -318,9 +338,19 @@ spec: value: ":8000" - name: ALLOWED_ORIGINS value: ".*" + - name: ENABLE_INTERNAL_TLS + value: "false" + - name: CUSTOM_TLS_CERT_PATH + value: "" + - name: TLS_KEY_PATH + value: "" + - name: CA_CERT_PATH + value: "" ports: - containerPort: 3000 - containerPort: 3030 + - containerPort: 3001 + - containerPort: 3031 imagePullPolicy: Always resources: requests: @@ -345,5 +375,11 @@ spec: - name: auth-rpc-server port: 3030 targetPort: 3030 + - name: auth-server-https + port: 9005 + targetPort: 3001 + - name: auth-rpc-server-https + port: 3031 + targetPort: 3031 selector: component: litmusportal-auth-server diff --git a/chaoscenter/manifests/litmus-without-resources.yaml b/chaoscenter/manifests/litmus-without-resources.yaml index 4b60c95ab45..075b4d239c4 100644 --- a/chaoscenter/manifests/litmus-without-resources.yaml +++ b/chaoscenter/manifests/litmus-without-resources.yaml @@ -229,9 +229,23 @@ spec: value: '["ci"]' - name: ALLOWED_ORIGINS value: ".*" + - name: ENABLE_INTERNAL_TLS + value: "false" + - name: SERVER_TLS_CERT_PATH + value: "" + - name: SERVER_TLS_KEY_PATH + value: "" + - name: CLIENT_TLS_CERT_PATH + value: "" + - name: CLIENT_KEY_CERT_PATH + value: "" + - name: CA_CERT_PATH + value: "" ports: - containerPort: 8080 - containerPort: 8000 + - containerPort: 8081 + - containerPort: 8001 imagePullPolicy: Always --- apiVersion: v1 @@ -247,6 +261,12 @@ spec: - name: graphql-rpc-server port: 8000 targetPort: 8000 + - name: graphql-server-https + port: 9004 + targetPort: 8081 + - name: graphql-rpc-server-https + port: 8001 + targetPort: 8001 selector: component: litmusportal-server --- @@ -293,9 +313,19 @@ spec: value: ":8000" - name: ALLOWED_ORIGINS value: ".*" + - name: ENABLE_INTERNAL_TLS + value: "false" + - name: CUSTOM_TLS_CERT_PATH + value: "" + - name: TLS_KEY_PATH + value: "" + - name: CA_CERT_PATH + value: "" ports: - containerPort: 3000 - containerPort: 3030 + - containerPort: 3001 + - containerPort: 3031 imagePullPolicy: Always --- apiVersion: v1 @@ -311,5 +341,11 @@ spec: - name: auth-rpc-server port: 3030 targetPort: 3030 + - name: auth-server-https + port: 9005 + targetPort: 3001 + - name: auth-rpc-server-https + port: 3031 + targetPort: 3031 selector: component: litmusportal-auth-server diff --git a/chaoscenter/mtls-helper.sh b/chaoscenter/mtls-helper.sh new file mode 100644 index 00000000000..93082f3d881 --- /dev/null +++ b/chaoscenter/mtls-helper.sh @@ -0,0 +1,37 @@ +# delete pem file +rm *.pem + +# Create CA private key and self-signed certificate +# adding -nodes to not encrypt the private key +openssl req -x509 -newkey rsa:4096 -nodes -days 365 -keyout ca-key.pem -out ca-cert.pem -subj "/C=TR/ST=ASIA/L=ISTANBUL/O=DEV/OU=TUTORIAL/CN=*.litmuschaos.io/emailAddress=litmuschaos@gmail.com" + +echo "CA's self-signed certificate" +openssl x509 -in ca-cert.pem -noout -text + +# Create Web Server private key and CSR +# adding -nodes to not encrypt the private key +openssl req -newkey rsa:4096 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=TR/ST=ASIA/L=ISTANBUL/O=DEV/OU=BLOG/CN=*.litmuschaos.io/emailAddress=litmuschaos@gmail.com" + +# Sign the Web Server Certificate Request (CSR) +openssl x509 -req -in server-req.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile server-ext.conf + +echo "Server's signed certificate" +openssl x509 -in server-cert.pem -noout -text + +# Verify certificate +echo "Verifying certificate" +openssl verify -CAfile ca-cert.pem server-cert.pem + +# Generate client's private key and certificate signing request (CSR) +openssl req -newkey rsa:4096 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=TR/ST=EUROPE/L=ISTANBUL/O=DEV/OU=CLIENT/CN=*.litmuschaos.io/emailAddress=litmuschaos@gmail.com" + +# Sign the Client Certificate Request (CSR) +openssl x509 -req -in client-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extfile client-ext.conf + +echo "Client's signed certificate" +openssl x509 -in client-cert.pem -noout -text + + + + + diff --git a/chaoscenter/server-ext.conf b/chaoscenter/server-ext.conf new file mode 100644 index 00000000000..b25c22fd28e --- /dev/null +++ b/chaoscenter/server-ext.conf @@ -0,0 +1 @@ +subjectAltName=DNS:*.litmuschaos.io,IP:0.0.0.0,DNS:localhost