使用數據庫統一管理ssh登陸用戶密鑰信息
需求是由一個開發部門提出的,他們需要在他們的測試服務器上開放root權限給所有的開發人員,同時要給每個開發人員配置無密碼登錄,由於使用ssh默認的認證文件存儲公鑰不方便人員信息的管理(例如一個人離職後不方便在大量的key中找到他的key,且需要在大量的服務器上全部刪除)。
基於上述需求,我們決定將所有人員的key和其對應的fingerprint保存在數據庫中,而且每個fingerprint都對應一個真實用戶名,在用戶登錄的時候,雖然都是使用root用戶登錄,但是,其登錄的時候不會再到autherized_keys文件中查找key,而是根據用戶傳遞的fingerprint去數據庫中查找對應的公鑰。
爲了實現上述的需求,我們需要用戶在使用ssh登錄的時候首先去執行一個取公鑰的腳本,然後再根據腳本取回的key來驗證用戶。這裏需要用到兩個ssh的配置參數AuthorizedKeysCommand和AuthorizedKeysCommandUser,第一個參數用來指定登錄時執行的腳本文件,第二個參數用來指定這個腳本的用戶,這裏要注意,這個腳本權限必須是700且屬組屬主必須是root。這兩個配置參數在低版本的openssh中無法使用,需要首先升級openssh的版本,而且在升級時需要我們修改一部分openssh的源碼,本文中升級到了ssh 6.6p1這個版本。
一、升級openssh
準備升級openssh的源碼包,在官網上找到6.6版本的源碼包並下載:
[root@localhost ~]# wget http://ftp.jaist.ac.jp/pub/OpenBSD/OpenSSH/portable/openssh-6.6p1.tar.gz [root@localhost ~]# tar -zxf openssh-6.6p1.tar.gz -C /usr/src/ [root@localhost ~]# cd /usr/src/openssh-6.6p1
解包完成後,我們要更改一下其源碼,更改解壓後目錄下的auth2-pubkey.c文件
//512行左右 struct passwd *pw; struct stat st; int status, devnull, p[2], i; pid_t pid; //char *username, errmsg[512]; 註釋掉此行代碼 char *username, *fp, errmsg[512]; 添加此行代碼 if (options.authorized_keys_command == NULL || options.authorized_keys_command[0] != '/') return 0; //552行左右 if (pipe(p) != 0) { error("%s: pipe: %s", __func__, strerror(errno)); goto out; } // debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"", 註釋掉這行 // options.authorized_keys_command, user_pw->pw_name, pw->pw_name); 註釋掉這行 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); 添加這行 debug3("Running AuthorizedKeysCommand: \"%s %s %s\" as \"%s\"", options.authorized_keys_command, user_pw->pw_name, fp, pw->pw_name); /* * Don't want to call this in the child, where it can fatal() and * run cleanup_exit() code. */ restore_uid(); switch ((pid = fork())) { case -1: /* error */ //602行左右 /* stdin is pointed to /dev/null at this point */ if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) { error("%s: dup2: %s", __func__, strerror(errno)); _exit(1); } execl(options.authorized_keys_command, // options.authorized_keys_command, user_pw->pw_name, NULL); 註釋掉此行 options.authorized_keys_command, user_pw->pw_name, fp, NULL); 添加此行 error("AuthorizedKeysCommand %s exec failed: %s", options.authorized_keys_command, strerror(errno)); _exit(127); default: /* parent */ break; } free(fp); 添加此行 temporarily_use_uid(pw);
改完上述源碼後,我們將更改後的源碼打成rpm包來升級:
[root@localhost ~]# cd /usr/src/openssh-6.6p1/contrib/redhat #本文系統爲CentOS 6.5 [root@localhost ~]# vim openssh.spec #更改spec文檔,關閉一些用不到的參數 # Do we want to disable building of x11-askpass? (1=yes 0=no) %define no_x11_askpass 1 #此處設置爲1,不構建x11-askpass # Do we want to disable building of gnome-askpass? (1=yes 0=no) %define no_gnome_askpass 1 #此處設置爲1,不構建gnome-askpass
[root@localhost ~]# cd /usr/src/ [root@localhost ~]# tar -zcf openssh-6.6p1.tar.gz openssh-6.6p1/ 注意:此處必須在/usr/src/目錄下打包,否則製作rpm包時會報錯,且文件名必須如上所示 [root@localhost ~]# cd /usr/src/openssh-6.6p1/contrib/redhat [root@localhost ~]# rpmbuild -bb openssh.spec #開始製作rpm包 注意:製作rpm包的時候可能會遇到依賴包不全的問題,常見的有pam pam-devel glibc glibc-devel tcp_wrappers-devel openssl098e gcc等,此處不再一一贅述。
打包完成後,在/usr/src/redhat/RPMS/x86_64目錄下有打好的rpm包。用rpm方式升級安裝。我在安裝的時候遇到了版本衝突的問題,然後直接強制安裝。
安裝完成後,修改/etc/ssh/sshd_config配置文件:
#AuthorizedKeysFile .ssh/authorized_keys //註釋掉這一行 #AuthorizedPrincipalsFile none AuthorizedKeysCommand /bin/ssh-zhy.sh //添加這一行,指定查找ssh-key的腳本 AuthorizedKeysCommandUser root //添加這一行,指定運行腳本的用戶,必須是root
AuthorizedKeysCommand迴向ssh-key腳本傳遞兩個位置參數,$1是登陸用戶名,$2是登陸用戶的fingerprint,在腳本中可以直接用$2來獲取fingerprint。
準備取ssh-key的腳本,該腳本可以是shell腳本,也可以是其他腳本,只要能從數據庫中取到對應的key就可以了,此處我使用的是shell腳本。數據庫中保存各用戶的key和fingerprint,根據fingerprint來取對應的key值,這樣就保證所有用戶即使都使用root登陸,也可以根據其fingerprint值來對應查找到對應的登陸人員從而完成人員記錄。
準備好腳本後,放到配置文件中配置的位置,改腳本文件屬主和屬組爲root,腳本權限爲700,這兩步是必須設置的。配置完成後準備好存儲key的數據庫,然後重啓sshd服務。此時再登陸的時候就會去數據庫中查找key信息,如果在數據庫中找不到對應的key,依然會去authorized_keys文件中找。
配置好後,我們只需要通過管理數據庫,就可以很方便的管理所有人的key。也可以方便後期的管理界面的開發。