手動生成/etc/shadow文件中的密碼

shadow文件的格式就不說了。就說說它的第二列——密碼列。python

一般,passwd直接爲用戶指定密碼就ok了。但在某些狀況下,要爲待建立的用戶事先指定密碼,還要求是加密後的密碼,例如kickstart文件中的rootpw指令,ansible建立用戶時提早指定密碼等,這時候不得不手動生成合理的密碼。算法

 

先說說shadow文件中第二列的格式,它是加密後的密碼,它有些玄機,不一樣的特殊字符表示特殊的意義:shell

  • ①.該列留空,即"::",表示該用戶沒有密碼。
  • ②.該列爲"!",即":!:",表示該用戶被鎖,被鎖將沒法登錄,可是可能其餘的登陸方式是不受限制的,如ssh公鑰認證的方式,su的方式。
  • ③.該列爲"*",即":*:",也表示該用戶被鎖,和"!"效果是同樣的。
  • ④.該列以"!"或"!!"開頭,則也表示該用戶被鎖。
  • ⑤.該列爲"!!",即":!!:",表示該用戶歷來沒設置過密碼。
  • ⑥.若是格式爲"$id$salt$hashed",則表示該用戶密碼正常。其中$id$的id表示密碼的加密算法,$1$表示使用MD5算法,$2a$表示使用Blowfish算法,"$2y$"是另外一算法長度的Blowfish,"$5$"表示SHA-256算法,而"$6$"表示SHA-512算法,

目前基本上都使用sha-512算法的,但不管是md5仍是sha-256都仍然支持。$salt$是加密時使用的salt,hashed纔是真正的密碼部分。dom

下文都以生成明文"123456"對應的加密密碼爲例。ssh

要生成md5算法的密碼,使用openssl便可。工具

openssl passwd -1 '123456'
openssl passwd -1 -salt 'abcdefg' '123456'

生成密碼後,直接將其拷貝或替換到shadow文件的第二列便可。例如:替換root用戶的密碼this

shell> field=$(awk -F ':' '/^root/{print $2}' /etc/shadow)
shell> password=$(openssl passwd -1 123456)
shell> sed -i '/^root/s%'$field'%'$password'%' /etc/shadow

但openssl passwd不支持生成sha-256和sha-512算法的密碼。在CentOS 6上,能夠藉助grub提供的密碼生成工具grub-crypt生成。加密

[root@server1 ~]# grub-crypt -h
Usage: grub-crypt [OPTION]...
Encrypt a password.

  -h, --help              Print this message and exit
  -v, --version           Print the version information and exit
  --md5                   Use MD5 to encrypt the password
  --sha-256               Use SHA-256 to encrypt the password
  --sha-512               Use SHA-512 to encrypt the password (default)

Report bugs to <bug-grub@gnu.org>.
EOF
[root@server1 ~]# grub-crypt --sha-512
Password: 
Retype password: 
$6$nt4hMDAYqYjudvfo$AKIZ3Z0o6/6HV6GKXqq21VEmh.ADFAZUQw2mvbIlplKx7gu9MQiEWjdmHnF2YPnYzgce1cP/bzDguVnUkMg/N.

grub-crypt實際上是一個python腳本,交互式生成密碼。如下是grub-crypt文件的內容。spa

[root@server1 ~]# cat /sbin/grub-crypt 
#! /usr/bin/python

'''Generate encrypted passwords for GRUB.'''

import crypt
import getopt
import getpass
import sys

def usage():
    '''Output usage message to stderr and exit.'''
    print >> sys.stderr, 'Usage: grub-crypt [OPTION]...'
    print >> sys.stderr, 'Try `$progname --help\' for more information.'
    sys.exit(1)

def gen_salt():                      # 生成隨機的salt
    '''Generate a random salt.'''
    ret = ''
    with open('/dev/urandom', 'rb') as urandom:
        while True:
            byte = urandom.read(1)
            if byte in ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
                        './0123456789'):
                ret += byte
                if len(ret) == 16:
                    break
    return ret

def main():
    '''Top level.'''
    crypt_type = '$6$' # SHA-256
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'hv',
                                   ('help', 'version', 'md5', 'sha-256',
                                    'sha-512'))
    except getopt.GetoptError, err:
        print >> sys.stderr, str(err)
        usage()
    if args:
        print >> sys.stderr, 'Unexpected argument `%s\'' % (args[0],)
        usage()
    for (opt, _) in opts:
        if opt in ('-h', '--help'):
            print (
'''Usage: grub-crypt [OPTION]...
Encrypt a password.

  -h, --help              Print this message and exit
  -v, --version           Print the version information and exit
  --md5                   Use MD5 to encrypt the password
  --sha-256               Use SHA-256 to encrypt the password
  --sha-512               Use SHA-512 to encrypt the password (default)

Report bugs to <bug-grub@gnu.org>.
EOF''')
            sys.exit(0)
        elif opt in ('-v', '--version'):
            print 'grub-crypt (GNU GRUB 0.97)'
            sys.exit(0)
        elif opt == '--md5':
            crypt_type = '$1$'
        elif opt == '--sha-256':
            crypt_type = '$5$'
        elif opt == '--sha-512':
            crypt_type = '$6$'
        else:
            assert False, 'Unhandled option'
    password = getpass.getpass('Password: ')
    password2 = getpass.getpass('Retype password: ')
    if not password:
        print >> sys.stderr, 'Empty password is not permitted.'
        sys.exit(1)
    if password != password2:
        print >> sys.stderr, 'Sorry, passwords do not match.'
        sys.exit(1)
    salt = crypt_type + gen_salt()
    print crypt.crypt(password, salt)      # 生成最終的加密密碼

if __name__ == '__main__':
    main()

很不幸,CentOS 7上默認安裝的是grub2,它不提供grub-crypt。所以參照grub-crypt內容,使用下面的python語句簡單代替grub-crypt,這一樣也是交互式的。code

python -c 'import crypt,getpass;pw=getpass.getpass();print(crypt.crypt(pw) if (pw==getpass.getpass("Confirm: ")) else exit())'

若是不想交互式,再改爲以下形式:

python -c 'import crypt,getpass;pw="123456";print(crypt.crypt(pw))'

如今就方便多了,直接將結果賦值給變量便可。

[root@server1 ~]# a=$(python -c 'import crypt,getpass;pw="123456";print(crypt.crypt(pw))')
[root@server1 ~]# echo $a
$6$uKhnBg5A4/jC8KaU$scXof3ZwtYWl/6ckD4GFOpsQa8eDu6RDbHdlFcRLd/2cDv5xYe8hzw5ekYCV5L2gLBBSfZ.Uc166nz6TLchlp.

例如,ansible建立用戶並指定密碼:

a=$(python -c 'import crypt,getpass;pw="123456";print(crypt.crypt(pw))')
ansible  192.168.100.55 -m user -a 'name=longshuai5 password="$a" update_password=always'
相關文章
相關標籤/搜索