Reputation: 61
The task in front of me is to take a user supplied plain-text string (i.e. a password), and turn it into something that can be inserted into /etc/shadow
as the hashed password string such that a user could then log in with the password originally supplied to generate the hash. This is a very common thing that we (re)solve over and over in the sysadmin world. There are a myriad of command line utils for doing this.
In this specific case, however, my constraint is that I need a pure Go solution that I can use in multiple contexts (a cli tool, an api, etc). My first attempt was just to use the bcrypt
library. At a glance it seemed to possess the attributes I required. It's pure Go, it's super simple to use (bonus), and it generates output that looks about like what I was after ... but it didn't work. The output resulting from use of this library cannot be (for example) pasted into /etc/shadow
as a user's password and then the user successfully able to log in with the original password.
I'm just wondering if anyone has run across this need in their day to day and solved it, who is willing to share their experiences and (gasp) code? I'm mainly wondering if there's a library targeting this approximate use-case someone would recommend? (my google fu may just have 'rona).
Upvotes: 0
Views: 1820
Reputation: 61
I'm sharing this here because the first thing I tried didn't work, and since I publicly asked, I felt it was fair to also publicly share the solution. This is a solution, specific to my situation. I'm not proclaiming it to be the solution, or that there aren't better solutions (please post those!). For my specific situation, here's what I came up with ... [EDIT: please note that user Marc suggested looking at this library in a comment above while I was composing this. Thank you, Marc, my fu is feeling better apparently]
As stated, my first attempt was just to use the bcrypt library, but it didn't work. On closer examination, I found that bcrypt only output in one format (i.e. uses one algorithm for hashing), which is apparently NOT compatible with the password system in Linux (at least not my particular distro). So while the output generally looked the way it should, the detail was:
bcrypt gives me something like: $2$10$sdfUILYhjd.HEdhjsdfgjhfdgjh.HEWjhndcjv
In that first field ($2
), my distro of Linux does not support type $2
apparently (?) (widely supported ones are $1, $5, $6
(i.e. md5, sha256, and sha512)). There didn't seem to be a way for me to specify a different algorithm in the bcrypt
go library. So, I cast about for other approaches/solutions. What I arrived at was:
package main
import (
"fmt"
"math/rand"
"os"
"time"
"github.com/tredoe/osutil/user/crypt/sha512_crypt"
)
func encryptPassword(userPassword string) string {
// Generate a random string for use in the salt
const charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
s := make([]byte, 8)
for i := range s {
s[i] = charset[seededRand.Intn(len(charset))]
}
salt := []byte(fmt.Sprintf("$6$%s", s))
// use salt to hash user-supplied password
c := sha512_crypt.New()
hash, err := c.Generate([]byte(userPassword), salt)
if err != nil {
fmt.Printf("error hashing user's supplied password: %s\n", err)
os.Exit(1)
}
return string(hash)
}
This function returns strings that look like:
$6$tZeuYPZ3$3mj70WOprJj5ytFFzC8gUFYk7eymQvaR4lDg5C0WzwBAMupRAan7BaC6EAbL9Eiyi2GZR6PQIQQa.y6kZLqh6
which you can simply paste directly into /etc/shadow
or supply to an installer as a hashed password (kickstart
, cloud-init
, etc) and go about your business. In my case, I'm writing a library function for an application that I can call from a command-line utility or from an api service that in turn supplies that hashed string as a parameter to cloud-init.
HTH others who may find this in the future.
Upvotes: 4