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

Dev/ssl tls pki #236

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion internal/cacctmgr/CmdArgParser.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ var (
FlagJson bool
FlagConfigFilePath string

FlagResetCredential bool

// These flags are implemented,
// but not added to any cmd!
FlagNoHeader bool
Expand Down Expand Up @@ -270,6 +272,7 @@ var (
if err != util.ErrorSuccess {
os.Exit(err)
}

},
}
modifyQosCmd = &cobra.Command{
Expand Down Expand Up @@ -434,6 +437,32 @@ var (
}
},
}

ResetCredsCmd = &cobra.Command{
Use: "reset [flags] name",
Short: "Reset the user's credential.",
Long: "",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// Check if args is empty.
if len(args) == 0 {
// If args[0] is not provided, check if the --force flag is set.
if !FlagForce {
fmt.Println("Caution! This operation is risky and should only be used when replacing all certificates. If you are sure to proceed, use the --force option.")
os.Exit(1)
}
err := ResetUserCredential("", FlagForce)
if err != util.ErrorSuccess {
os.Exit(err)
}
} else {
err := ResetUserCredential(args[0], FlagForce)
if err != util.ErrorSuccess {
os.Exit(err)
}
}
},
}
)

func ParseCmdArgs() {
Expand Down Expand Up @@ -575,7 +604,6 @@ func init() {
modifyUserCmd.Flags().StringSliceVar(&FlagUserQosList, "set-allowed-qos-list", nil, "Overwrite allowed QoS list of the user (comma seperated list)")
modifyUserCmd.Flags().StringVar(&FlagQos.Name, "add-allowed-qos-list", "", "Add a single QoS to allowed QoS list")
modifyUserCmd.Flags().StringVar(&FlagQos.Name, "delete-allowed-qos-list", "", "Delete a single QoS from allowed QoS list")

// Other flags
modifyUserCmd.Flags().BoolVarP(&FlagForce, "force", "F", false, "Forced operation")

Expand Down Expand Up @@ -646,4 +674,10 @@ func init() {
}
}
}

/* -------------------------------------------------- resetCreds --------------------------------------------------- */
RootCmd.AddCommand(ResetCredsCmd)
{
ResetCredsCmd.Flags().BoolVarP(&FlagForce, "force", "", false, "Operation for handling mismatches between database and Vault data.")
}
}
35 changes: 35 additions & 0 deletions internal/cacctmgr/cacctmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -816,3 +816,38 @@ func UnblockAccountOrUser(name string, entityType protos.EntityType, account str
return util.ErrorBackend
}
}

func ResetUserCredential(value string, isForce bool) util.CraneCmdError {
var userList []string
if value != "" {
var err error
userList, err = util.ParseStringParamList(value, ",")
if err != nil {
log.Errorf("Invalid user list specified: %v.\n", err)
return util.ErrorCmdArg
}
}

req := protos.ResetUserCredentialRequest{UserList: userList, IsForce: isForce}
reply, err := stub.ResetUserCredential(context.Background(), &req)
if err != nil {
util.GrpcErrorPrintf(err, "Failed to reset user credential")
return util.ErrorNetwork
}
if FlagJson {
fmt.Println(util.FmtJson.FormatReply(reply))
if reply.GetOk() {
return util.ErrorSuccess
} else {
return util.ErrorBackend
}
}

if !reply.GetOk() {
fmt.Println(util.ErrMsg(reply.Reason))
return util.ErrorBackend
}

fmt.Printf("reset user %s credential succeeded.\n", value)
return util.ErrorSuccess
}
2 changes: 1 addition & 1 deletion internal/cfored/cfored.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func StartCfored() {
var wgAllRoutines sync.WaitGroup

ctldClient := &GrpcCtldClient{
ctldClientStub: util.GetStubToCtldByConfig(config),
ctldClientStub: util.GetStubToCtldForCfored(config),
ctldReplyChannel: make(chan *protos.StreamCtldReply, 8),
}
wgAllRoutines.Add(1)
Expand Down
6 changes: 3 additions & 3 deletions internal/cfored/ctldClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
)

type GrpcCtldClient struct {
ctldClientStub protos.CraneCtldClient
ctldClientStub protos.CraneCtldForCforedClient
ctldReplyChannel chan *protos.StreamCtldReply
}

Expand All @@ -42,7 +42,7 @@ const (
GracefulExit StateOfCtldClient = 4
)

func (client *GrpcCtldClient) CtldReplyReceiveRoutine(stream protos.CraneCtld_CforedStreamClient) {
func (client *GrpcCtldClient) CtldReplyReceiveRoutine(stream protos.CraneCtldForCfored_CforedStreamClient) {
for {
m := new(protos.StreamCtldReply)
if err := stream.RecvMsg(m); err != nil {
Expand All @@ -57,7 +57,7 @@ func (client *GrpcCtldClient) CtldReplyReceiveRoutine(stream protos.CraneCtld_Cf

func (client *GrpcCtldClient) StartCtldClientStream(wg *sync.WaitGroup) {
var request *protos.StreamCforedRequest
var stream protos.CraneCtld_CforedStreamClient
var stream protos.CraneCtldForCfored_CforedStreamClient
var err error

state := StartReg
Expand Down
6 changes: 6 additions & 0 deletions internal/util/err.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ func ErrMsg(err_code protos.ErrCode) string {
switch err_code {
case protos.ErrCode_ERR_UPDATE_DATABASE:
return "Fail to update data in database"
case protos.ErrCode_ERR_SIGN_CERTIFICATE:
return "User authentication failed"
case protos.ErrCode_ERR_DUPLICATE_CERTIFICATE:
return "The certificate has already been issued to the user. If the certificate is lost and needs to be reissued, please contact the administrator for assistance."
case protos.ErrCode_ERR_REVOKE_CERTIFICATE:
return "Revocation of the certificate failed, Please check the logs."
default:
break
}
Expand Down
118 changes: 83 additions & 35 deletions internal/util/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ import (
"os"
"path/filepath"

"google.golang.org/grpc/credentials"

log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
grpccodes "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
grpcstatus "google.golang.org/grpc/status"
)

func GetTCPSocket(bindAddr string, config *Config) (net.Listener, error) {
if config.UseTls {
CaCertContent, err := os.ReadFile(config.ServerCertFilePath)
CaCertContent, err := os.ReadFile(config.SslConfig.InternalCaFilePath)
if err != nil {
return nil, err
}
Expand All @@ -48,7 +49,7 @@ func GetTCPSocket(bindAddr string, config *Config) (net.Listener, error) {
return nil, err
}

cert, err := tls.LoadX509KeyPair(config.ServerCertFilePath, config.ServerKeyFilePath)
cert, err := tls.LoadX509KeyPair(config.SslConfig.CforedCertFilePath, config.SslConfig.CforedKeyFilePath)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -108,76 +109,123 @@ func GetUnixSocket(path string, mode fs.FileMode) (net.Listener, error) {
return socket, nil
}

// TODO: Refactor this to return ErrCodes instead of exiting.
func GetStubToCtldByConfig(config *Config) protos.CraneCtldClient {
var serverAddr string
var stub protos.CraneCtldClient

if config.UseTls {
serverAddr = fmt.Sprintf("%s.%s:%s",
config.ControlMachine, config.DomainSuffix, config.CraneCtldListenPort)
if err := SignAndSaveUserCertificate(config); err != ErrorSuccess {
os.Exit(err)
}

ServerCertContent, err := os.ReadFile(config.ServerCertFilePath)
if err != nil {
log.Errorln("Read server certificate error: " + err.Error())
os.Exit(ErrorGeneric)
}
serverAddr = fmt.Sprintf("%s.%s:%s",
config.ControlMachine, config.DomainSuffix, config.CraneCtldListenPort)
userKeyPath, err := ExpandPath(DefaultUserConfigPath + "/user.key")
if err != nil {
os.Exit(ErrorGeneric)
}
userCertPath, err := ExpandPath(DefaultUserConfigPath + "/user.pem")
if err != nil {
os.Exit(ErrorGeneric)
}
externalCertPath, err := ExpandPath(DefaultUserConfigPath + "/external.pem")
if err != nil {
os.Exit(ErrorGeneric)
}

ServerKeyContent, err := os.ReadFile(config.ServerKeyFilePath)
if err != nil {
log.Errorln("Read server key error: " + err.Error())
os.Exit(ErrorGeneric)
}
cert, _ := tls.LoadX509KeyPair(userCertPath, userKeyPath)

CaCertContent, err := os.ReadFile(config.CaCertFilePath)
if err != nil {
log.Errorln("Read CA certifacate error: " + err.Error())
os.Exit(ErrorGeneric)
}
CaCertContent, err := os.ReadFile(externalCertPath)
if err != nil {
log.Errorln("Failed to read ExternalCertFile: " + err.Error())
os.Exit(ErrorGeneric)
}

caPool := x509.NewCertPool()
if ok := caPool.AppendCertsFromPEM(CaCertContent); !ok {
log.Errorln("Failed to append cert Content.")
os.Exit(ErrorGeneric)
}

creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
})
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds))
if err != nil {
log.Errorln("Cannot connect to CraneCtld: " + err.Error())
os.Exit(ErrorBackend)
}

stub = protos.NewCraneCtldClient(conn)

return stub
}

func GetStubToCtldForCfored(config *Config) protos.CraneCtldForCforedClient {
var serverAddr string
var stub protos.CraneCtldForCforedClient

tlsKeyPair, err := tls.X509KeyPair(ServerCertContent, ServerKeyContent)
if config.UseTls {
serverAddr = fmt.Sprintf("%s.%s:%s",
config.ControlMachine, config.DomainSuffix, config.CraneCtldForCforedListenPort)
cert, _ := tls.LoadX509KeyPair(config.SslConfig.CforedCertFilePath, config.SslConfig.CforedKeyFilePath)

CaCertContent, err := os.ReadFile(config.SslConfig.InternalCertFilePath)
if err != nil {
log.Errorln("tlsKeyPair error: " + err.Error())
log.Errorln("Failed to read InternalCertFile: " + err.Error())
os.Exit(ErrorGeneric)
}

caPool := x509.NewCertPool()
if ok := caPool.AppendCertsFromPEM(CaCertContent); !ok {
log.Errorln("AppendCertsFromPEM error: " + err.Error())
log.Errorln("Failed to append cert Content.")
os.Exit(ErrorGeneric)
}

creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{tlsKeyPair},
RootCAs: caPool,
InsecureSkipVerify: false,
// NextProtos is a list of supported application level protocols, in
// order of preference.
NextProtos: []string{"h2"},
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
})

conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds))
if err != nil {
log.Errorln("Cannot connect to CraneCtld: " + err.Error())
os.Exit(ErrorBackend)
}

stub = protos.NewCraneCtldClient(conn)
stub = protos.NewCraneCtldForCforedClient(conn)
} else {
serverAddr = fmt.Sprintf("%s:%s", config.ControlMachine, config.CraneCtldListenPort)
serverAddr = fmt.Sprintf("%s:%s", config.ControlMachine, config.CraneCtldForCforedListenPort)

conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Errorf("Cannot connect to CraneCtld %s: %s", serverAddr, err.Error())
os.Exit(ErrorBackend)
}

stub = protos.NewCraneCtldClient(conn)
stub = protos.NewCraneCtldForCforedClient(conn)
}

return stub
}

func GetStubToCtldForSign(config *Config) protos.SignServiceClient {
var serverAddr string
var stub protos.SignServiceClient

serverAddr = fmt.Sprintf("%s:%s", config.ControlMachine, config.CraneCtldForSignListenPort)

conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Errorf("Cannot connect to CraneCtld %s: %s", serverAddr, err.Error())
os.Exit(ErrorBackend)
}

stub = protos.NewSignServiceClient(conn)

return stub

}

func GrpcErrorPrintf(err error, format string, a ...any) {
s := fmt.Sprintf(format, a...)
if rpcErr, ok := grpcstatus.FromError(err); ok {
Expand Down
Loading