1. 程式人生 > >SUSE安裝SaltStack後的pam認證問題

SUSE安裝SaltStack後的pam認證問題

            最近專案需要在幾個叢集的機器上部署SaltStack,作業系統有SUSE11 SP3和SP4兩個版本的。之前同事在centos的虛擬機器上測試一路順風順水,沒想到上SUSE遇到一堆坑。

            坑1:在SaltStack的官網上可以找到兩個SUSE用的源,從Document- Installation - SUSE下找到的源是opensuse的(http://download.opensuse.org/repositories/systemsmanagement:/saltstack/SLE_11_SP4/systemsmanagement:saltstack.repo),版本為2016.3.3;而從Downloads- SUSE - SLES11下找到的源是saltstack的(http://repo.saltstack.com/opensuse/SLE_11_SP4/systemsmanagement:saltstack.repo),版本為2015.8.12。版本不一致讓人疑惑,但坑不在這裡,真正的坑是,不管用哪個源裝都會少一些依賴,比如python-requests等幾個rpm包,要找到匹配的版本還是花了一番功夫的,我找到的4個包如下,地址就不放了。這還不算大坑。

python-requests-2.4.1-30.1.x86_64.rpm

python-ordereddict-1.1-0.7.31.x86_64.rpm

ca-certificates-1-9.1.noarch.rpm

libffi43-4.3.4_20091019-0.37.28.x86_64.rpm

            除了salt,salt-master,salt-minion外,我還裝了salt-api,因為需要一個restful的API供外部呼叫。裝完以後,還需要配置外部認證供salt-api使用(https://docs.saltstack.com/en/latest/topics/eauth/index.html),再新建一個作業系統帳號供api使用(我建的帳號為saltapi),無難度。全部配完,起salt-master和salt-api服務,顯示OK,但是呼叫的時候來坑了。

            坑2:首先嚐試用curl方式呼叫

curl -si localhost:8088/login -H"Accept:application/json" -H"Content-type:application/json" -d'{"username":"saltapi","password":"mypassword","eauth":"pam"}'

返回了一個401錯誤:

HTTP/1.1 401 Unauthorized
Content-Length: 1614
Access-Control-Expose-Headers: GET, POST
Vary: Accept-Encoding
Server: CherryPy/3.6.0
Allow: GET, HEAD, POST
Access-Control-Allow-Credentials: true
Date: Sat, 15 Oct 2016 11:14:43 GMT
Access-Control-Allow-Origin: *
Content-Type: text/html;charset=utf-8
Set-Cookie: session_id=9f4c49f186ad3026386a43372d7b2e538d216397; expires=Sat, 15 Oct 2016 21:14:43 GMT; Path=/
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
    <title>401 Unauthorized</title>
    <style type="text/css">
    #powered_by {
        margin-top: 20px;
        border-top: 2px solid black;
        font-style: italic;
    }
    #traceback {
        color: red;
    }
    </style>
</head>
    <body>
        <h2>401 Unauthorized</h2>
        <p>Could not authenticate using provided credentials</p>
        <pre id="traceback">Traceback (most recent call last):
  File "/usr/lib64/python2.6/site-packages/cherrypy/_cprequest.py", line 670, in respond
    response.body = self.handler()
  File "/usr/lib64/python2.6/site-packages/cherrypy/lib/encoding.py", line 217, in __call__
    self.body = self.oldhandler(*args, **kwargs)
  File "/usr/lib64/python2.6/site-packages/salt/netapi/rest_cherrypy/app.py", line 534, in hypermedia_handler
    ret = cherrypy.serving.request._hypermedia_inner_handler(*args, **kwargs)
  File "/usr/lib64/python2.6/site-packages/cherrypy/_cpdispatch.py", line 61, in __call__
    return self.callable(*self.args, **self.kwargs)
  File "/usr/lib64/python2.6/site-packages/salt/netapi/rest_cherrypy/app.py", line 1529, in POST
    'Could not authenticate using provided credentials')
HTTPError: (401, 'Could not authenticate using provided credentials')
</pre>
    <div id="powered_by">
      <span>
        Powered by <a href="http://www.cherrypy.org">CherryPy 3.6.0</a>
      </span>
    </div>
    </body>
</html>

            心裡覺得奇怪,該不會是salt-api沒裝好吧,畢竟有不少野地裡淘來的rpm啊。於是決定用salt命令在master的本機呼叫:

salt -a pam "*" test.ping

            執行後會提示輸入使用者名稱和密碼,輸入saltapi帳號資訊,依然是401錯誤。這下雷了。研究了一下salt的程式碼,pam認證都要過以下檔案,編輯一下以便除錯:

vi /usr/lib64/python2.6/site-packages/salt/auth/pam.py

            在179行加入:

print "retval after AUTHENTICATE:%s" % retval

            然後將salt-master服務停掉,在命令列前臺起一個debug模式的salt-master,來看到底什麼情況:

service salt-master stop

salt-master -l debug

            接下去就更雷了。在2015.8.12版本的機器上,執行到這裡列印“retval after AUTHENTICATE: 10”,而在2016.3.3版本的機器上列印“retvalafter AUTHENTICATE: 7”。關於這個返回值的解釋,可以看這個頁面(http://pubs.opengroup.org/onlinepubs/8329799/chap5.htm)。其中,7代表PAM_PERM_DENIED,10代表PAM_NEW_AUTHTOK_REQD。我一開始在一臺2015.8.12版本的虛擬機器上除錯,始終返回10,懷疑是PAM的token無法生成,翻遍了網上所有saltstack的文章都沒有解決方案。由於當時已經到晚上11點了,困得要死,決定先作個弊,跑過去再說。於是再修改pam.py檔案,將auth方法改為如下:

def auth(username, password, **kwargs):
    ‘’’
    Authenticate via pam
    ‘’’
    # return authenticate(username, password)
    if username==’saltapi’ and password==’mypassword’:
        return True
    else:
        return False

            作弊成功,果然跑通,拿到了通訊的token。

            第二天起來以後,覺得不甘心,繼續找原因,並且發現了2016.3.3版本下retval值為7,開始懷疑是許可權問題。起初在/etc/pam.d/下找問題,無果。偶然之下cat /etc/shadow 了一下,發現一個奇怪的帳號:

salt:*:17089:0:99999:7:::

            於是恍然大悟,雖然servicesalt-master ***是用root帳號跑的,但程序未必是root起的,ps一下檢視果然,salt-master都是salt帳號起的。而linux的/etc/shadow預設是隻有root帳號能讀的,於是:

setfacl -m u:salt:r /etc/shadow

            把昨天改的pam.py改回去,起salt-master服務,呼叫api拿token,成功!再深究一下,發現在/etc/salt/master下有以下一段:

# The user under which the salt master willrun. Salt will update all

# permissions to allow the specified userto run the master. The exception is

# the job cache, which must be deleted ifthis user is changed. If the

# modified files cause conflicts, setverify_env to False.

user: salt

syndic_user: salt

            結論是在SUSE環境下由於某些我不清楚的原因,saltstack安裝時只是建了salt帳號,並沒能正確修改許可權,導致不能讀取/etc/shadow,引發了奇怪的401錯誤。而在centos上是正確修改了的,所以沒碰到問題。至於修改是在rpm的postscripts裡,還是在salt-master起來後的程式碼裡,沒進一步深究,有空再更新。