Chef:启用Jenkins安全性会导致插件安装失败

时间:2017-06-05 17:48:06

标签: security jenkins plugins chef chef-recipe

我目前正在使用Chef在受管节点上部署Jenkins实例。我正在使用以下公共超市食谱:https://supermarket.chef.io/cookbooks/jenkins

我在配方文件中使用以下代码来启用身份验证:

jenkins_script 'activate global security' do
  command <<-EOH.gsub(/^ {4}/, '')
      import jenkins.model.*
      import hudson.security.*
      def instance = Jenkins.getInstance()

      def hudsonRealm = new HudsonPrivateSecurityRealm(false)
      hudsonRealm.createAccount("Administrator","Password")
      instance.setSecurityRealm(hudsonRealm)
      instance.save()

      def strategy = new GlobalMatrixAuthorizationStrategy()
        strategy.add(Jenkins.ADMINISTER, "Administrator")
        instance.setAuthorizationStrategy(strategy)

      instance.save()
  EOH
 end

这非常适合在第一次在受管节点上运行配方时在实例上设置安全性。它在Jenkins服务器上创建具有管理员权限的管理员用户。除了在Jenkins实例上启用安全性之外,还使用此配方安装插件。

启用安全性后,安装尚未存在但已指定安装的插件失败:

ERROR: anonymous is missing the Overall/Read permission

我认为这是与新创建的管理员帐户相关的错误,而Chef尝试使用匿名用户而不是管理员用户安装插件。是否有任何应该在我的配方文件中设置以解决此权限问题?

此处的目标是,如果插件升级到不需要的版本或完全卸载,运行配方将重新安装/回滚任何插件更改。目前,如果我还在Jenkins实例上启用了安全性,这似乎是不可能的。

EDIT 还应该注意,目前每次我需要以这种方式修复插件时,我必须禁用安全性然后运行整个配方(插件安装+安全启用)。

感谢您的帮助!

2 个答案:

答案 0 :(得分:0)

jenkins_plugin资源似乎没有公开任何身份验证选项,因此您可能需要构建自己的资源。如果你深入研究代码,你会发现cookbook中的底层执行层确实支持auth(以及其他一些东西),所以在copy-fork中可能很容易(并向我们发送一个补丁)只是那个资源。

答案 1 :(得分:0)

我们之所以遇到这个问题,是因为我们之前曾定义过:jenkins_username:jenkins_password,但是它们仅与remoting协议一起使用,为了支持通过REST API访问而弃用了这些协议SSH或HTTPS以及更高版本中的默认值为DISABLED。

我们最终结合了@StephenKing食谱中的逻辑以及来自chef-cookbooks/jenkinsthis GitHub issue comment on that repo的信息,以在通过实例上的Active Directory启用身份验证(我们使用SSH)后使插件安装正常工作。 / p>

我们基本上从https://github.com/TYPO3-cookbooks/jenkins-chefci/blob/e1b82e679074e96de5d6e668b0f10549c48b58d1/recipes/_jenkins_chef_user.rb中提取了该示例,并删除了如果不存在会自动生成密钥的部分(我们的实例仍然存在,并且大多需要确定性),然后用查找替换了File.read。在我们的加密数据包(或等效功能)中。

recipes / authentication.rb

require 'aws-sdk'
require 'net/ssh'
require 'openssl'

ssm = Aws::SSM::Client.new(region: 'us-west-2')

unless node.run_state[:jenkins_private_key]

  key_contents = ssm.get_parameter(name: node['jenkins_wrapper']['secrets']['chefjenkins']['id_rsa']['path'], with_decryption: true).parameter.value
  key_path = node['jenkins_wrapper']['secrets']['chefjenkins']['id_rsa']['path']


  key = OpenSSL::PKey::RSA.new key_contents
  # We use `log` here so we can assert the correct path was queried without exposing or hardcoding the secret in our tests
  log 'Successfully read existing private key from ' + key_path

  public_key = [key.ssh_type, [key.to_blob].pack('m0'), 'auto-generated key'].join(' ')

  # Create the Chef Jenkins user with the public key
  jenkins_user 'chefjenkins' do
    id 'chefjenkins' # This also matches up with an Active Directory user
    full_name 'Chef Client'
    public_keys [public_key]
  end

  # Set the private key on the Jenkins executor
  node.run_state[:jenkins_private_key] = key.to_pem

end

# This was our previous implementation that stopped working recently
# jenkins_password = ssm.get_parameter(name: node['jenkins_wrapper']['secrets']['chefjenkins']['path'], with_decryption: true).parameter.value
# node.run_state[:jenkins_username] = 'chefjenkins' # ~FC001
# node.run_state[:jenkins_password] = jenkins_password # ~FC001

recipes / enable_jenkins_sshd.rb

port = node['jenkins']['ssh']['port']
jenkins_script 'configure_sshd_access' do
  command <<-EOH.gsub(/^ {4}/, '')
    import jenkins.model.*
    def instance = Jenkins.getInstance()
    def sshd = instance.getDescriptor("org.jenkinsci.main.modules.sshd.SSHD")
    def currentPort = sshd.getActualPort()
    def expectedPort = #{port}

    if (currentPort != expectedPort) {
      sshd.setPort(expectedPort)
    }
    EOH
  not_if "grep #{port} /var/lib/jenkins/org.jenkinsci.main.modules.sshd.SSHD.xml"
  notifies :execute, 'jenkins_command[safe-restart]', :immediately
end

属性/default.rb

# Enable/disable SSHd.
# If the port is 0, Jenkins will serve SSHd on a random port
# If the port is > 0, Jenkins will serve SSHd on that port specifically
# If the port is is -1 turns off SSHd.
default['jenkins']['ssh']['port'] = 8222

# This happens to be our lookup path in AWS SSM, but
# this could be a local file on Jenkins or in databag or wherever
default['jenkins_wrapper']['secrets']['chefjenkins']['id_rsa']['path'] = 'jenkins_wrapper.users.chefjenkins.id_rsa'