// HandleRequest handles a request to sign an SSH public key verified by an OpenIDConnect id_token
HandleRequest handles a request to sign an SSH public key verified by an OpenIDConnect id_token
// HandleRequest handles a request to sign an SSH public key verified by an OpenIDConnect id_token
func HandleRequest(ctx context.Context, event signer.SSHrimpEvent) (*signer.SSHrimpResult, error) {
	lambdaContext, ok := lambdacontext.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("lambdacontext not in ctx")
	}
	c := config.NewSSHrimp()
	if err := c.Read(config.GetPath()); err != nil {
		return nil, err
	}
	publicKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(event.PublicKey))
	if err != nil {
		return nil, fmt.Errorf("unable to parse public key: %v", err)
	}
	i, err := identity.NewIdentity(c)
	username, roles, err := i.Validate(event.Token)
	if err != nil {
		return nil, err
	}
	criticalOptions := make(map[string]string)
	if regexp.MustCompile(c.CertificateAuthority.ForceCommandRegex).MatchString(event.ForceCommand) {
		if event.ForceCommand != "" {
			criticalOptions["force-command"] = event.ForceCommand
		}
	} else {
		return nil, errors.New("forcecommand validation failed")
	}
	if regexp.MustCompile(c.CertificateAuthority.SourceAddressRegex).MatchString(event.SourceAddress) {
		if event.SourceAddress != "" {
			criticalOptions["source-address"] = event.SourceAddress
		}
	} else {
		return nil, errors.New("sourceaddress validation failed")
	}
	bytes := make([]byte, 32)
	nonce := make([]byte, len(bytes)*2)
	if _, err := rand.Read(bytes); err != nil {
		return nil, err
	}
	hex.Encode(nonce, bytes)
	serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
	if err != nil {
		return nil, err
	}
	now := time.Now()
	validAfterOffset, err := time.ParseDuration(c.CertificateAuthority.ValidAfterOffset)
	if err != nil {
		return nil, err
	}
	validBeforeOffset, err := time.ParseDuration(c.CertificateAuthority.ValidBeforeOffset)
	if err != nil {
		return nil, err
	}
	validAfter := now.Add(validAfterOffset)
	validBefore := now.Add(validBeforeOffset)
	extensions := make(map[string]string, len(c.CertificateAuthority.Extensions))
	for _, extension := range c.CertificateAuthority.Extensions {
		extensions[extension] = ""
	}
	keyID := fmt.Sprintf("request[%s] for[%s] from[%s] command[%s] ssh_key[%s] ca[%s] valid_to[%s]",
		lambdaContext.AwsRequestID,
		username,
		event.SourceAddress,
		event.ForceCommand,
		ssh.FingerprintSHA256(publicKey),
		lambdaContext.InvokedFunctionArn,
		validBefore.Format("2006/01/02 15:04:05"),
	)
	principals := make([]string, len(roles)+1)
	principals[0] = username
	for i, v := range roles {
		principals[i+1] = v
	}
	if c.CertificateAuthority.ProvisioningUser != "" {
		principals = append(principals, c.CertificateAuthority.ProvisioningUser)
	}
	certificate := ssh.Certificate{
		Nonce:           nonce,
		Key:             publicKey,
		Serial:          serial.Uint64(),
		CertType:        ssh.UserCert,
		KeyId:           keyID,
		ValidPrincipals: principals,
		Permissions: ssh.Permissions{
			CriticalOptions: criticalOptions,
			Extensions:      extensions,
		},
		ValidAfter:  uint64(validAfter.Unix()),
		ValidBefore: uint64(validBefore.Unix()),
	}
	kmsSigner := signer.NewKMSSigner(c.CertificateAuthority.KeyAlias)
	sshAlgorithmSigner, err := signer.NewAlgorithmSignerFromSigner(kmsSigner, ssh.SigAlgoRSASHA2256)
	if err != nil {
		return nil, err
	}
	if err := certificate.SignCert(rand.Reader, sshAlgorithmSigner); err != nil {
		return nil, err
	}
	pubkey, err := ssh.ParsePublicKey(certificate.Marshal())
	if err != nil {
		return nil, err
	}
	return &signer.SSHrimpResult{
		Certificate:  string(ssh.MarshalAuthorizedKey(pubkey)),
		ErrorMessage: "",
		ErrorType:    "",
	}, nil
}
func HandleRequest ( ctx context . Context , event signer . SSHrimpEvent ) ( * signer . SSHrimpResult , error ) { lambdaContext , ok := lambdacontext . FromContext ( ctx ) if ! ok { return nil , fmt . Errorf ( " " ) } c := config . NewSSHrimp ( ) if err := c . Read ( config . GetPath ( ) ) ; err != nil { return nil , err } publicKey , _ , _ , _ , err := ssh . ParseAuthorizedKey ( [ ] byte ( event . PublicKey ) ) if err != nil { return nil , fmt . Errorf ( " " , err ) } i , err := identity . NewIdentity ( c ) username , roles , err := i . Validate ( event . Token ) if err != nil { return nil , err } criticalOptions := make ( map [ string ] string ) if regexp . MustCompile ( c . CertificateAuthority . ForceCommandRegex ) . MatchString ( event . ForceCommand ) { if event . ForceCommand != " " { criticalOptions [ " " ] = event . ForceCommand } } else { return nil , errors . New ( " " ) } if regexp . MustCompile ( c . CertificateAuthority . SourceAddressRegex ) . MatchString ( event . SourceAddress ) { if event . SourceAddress != " " { criticalOptions [ " " ] = event . SourceAddress } } else { return nil , errors . New ( " " ) } bytes := make ( [ ] byte , 32 ) nonce := make ( [ ] byte , len ( bytes ) * 2 ) if _ , err := rand . Read ( bytes ) ; err != nil { return nil , err } hex . Encode ( nonce , bytes ) serial , err := rand . Int ( rand . Reader , big . NewInt ( math . MaxInt64 ) ) if err != nil { return nil , err } now := time . Now ( ) validAfterOffset , err := time . ParseDuration ( c . CertificateAuthority . ValidAfterOffset ) if err != nil { return nil , err } validBeforeOffset , err := time . ParseDuration ( c . CertificateAuthority . ValidBeforeOffset ) if err != nil { return nil , err } validAfter := now . Add ( validAfterOffset ) validBefore := now . Add ( validBeforeOffset ) extensions := make ( map [ string ] string , len ( c . CertificateAuthority . Extensions ) ) for _ , extension := range c . CertificateAuthority . Extensions { extensions [ extension ] = " " } keyID := fmt . Sprintf ( " " , lambdaContext . AwsRequestID , username , event . SourceAddress , event . ForceCommand , ssh . FingerprintSHA256 ( publicKey ) , lambdaContext . InvokedFunctionArn , validBefore . Format ( " " ) , ) principals := make ( [ ] string , len ( roles ) + 1 ) principals [ 0 ] = username for i , v := range roles { principals [ i + 1 ] = v } if c . CertificateAuthority . ProvisioningUser != " " { principals = append ( principals , c . CertificateAuthority . ProvisioningUser ) } certificate := ssh . Certificate { Nonce : nonce , Key : publicKey , Serial : serial . Uint64 ( ) , CertType : ssh . UserCert , KeyId : keyID , ValidPrincipals : principals , Permissions : ssh . Permissions { CriticalOptions : criticalOptions , Extensions : extensions , } , ValidAfter : uint64 ( validAfter . Unix ( ) ) , ValidBefore : uint64 ( validBefore . Unix ( ) ) , } kmsSigner := signer . NewKMSSigner ( c . CertificateAuthority . KeyAlias ) sshAlgorithmSigner , err := signer . NewAlgorithmSignerFromSigner ( kmsSigner , ssh . SigAlgoRSASHA2256 ) if err != nil { return nil , err } if err := certificate . SignCert ( rand . Reader , sshAlgorithmSigner ) ; err != nil { return nil , err } pubkey , err := ssh . ParsePublicKey ( certificate . Marshal ( ) ) if err != nil { return nil , err } return & signer . SSHrimpResult { Certificate : string ( ssh . MarshalAuthorizedKey ( pubkey ) ) , ErrorMessage : " " , ErrorType : " " , } , nil }
HandleRequest handles a request to sign an SSH public key verified by an OpenIDConnect id_token
HandleRequest handles a request to sign an SSH public key verified by an OpenIDConnect id_token
// Make sure we are running in a lambda context, to get the requestid and ARN // Load the configuration file, if not exsits, exit. // Validate the user supplied public key // Validate the user supplied identity token with the loaded configuration // Validate and add force commands or source address options // Generate a random nonce for the certificate // Generate a random serial number // Validate and set the certificate valid and expire times // Convert the extensions slice to a map // Create a key ID to be added to the certificate. Follows BLESS Key ID format // https://github.com/Netflix/bless // Merge username with additional principal roles //check for any defined provisioning user // Add additional roles for the provisioning account // Create the certificate struct with all our configured alues // Setup our Certificate Authority signer backed by KMS // Sign the certificate!! // Extract the public key (certificate) to return to the user // Success!