Exported SSH keys

Most exported resource documentation starts with an SSH key example. sshkey is a Puppet type that creates or destroys entries in the ssh_known_hosts file used by SSH to verify the validity of remote servers. The sshkey example is a great use of exported resources, but since most examples put the declaration and collecting phases in the same class, it may be a confusing example for those starting out learning exported resources. It's important to remember that exporting and collecting are different operations.

sshkey collection for laptops

We'll outline an enterprise application of the sshkey example and define a class for login servers—any server that allows users to log in directly. Using that class to define exported resources for ssh_host_keys, we'll then create an ssh_client class that collects all the login server ssh_keys. In this way, we can apply the ssh_client class to any laptops that might connect and have them get updated SSH host keys. To make this an interesting example, we'll run Puppet as non-root on the laptop and have Puppet update the user's known_hosts file ~/.ssh/known_hosts instead of the system file. This is a slightly novel approach to running Puppet without root privileges.

We'll begin by defining an example::login_server class that exports the RSA and DSA SSH host keys. RSA and DSA are the two types of encryption keys that can be used by the SSH daemon; the name refers to the encryption algorithm used by each key type. We will need to check if a key of each type is defined as it is only a requirement that one type of key be defined for the SSH server to function, as shown in the following code:

class example::login_server {
  if ( $::sshrsakey != undef ) {
    @@sshkey {"$::fqdn-rsa":
      host_aliases => ["$::hostname","$::ipaddress"],
      key          => "$::sshrsakey",
      type         => 'rsa',
      tag          => 'example::login_server',
    }
  }
  if ( $::sshdsakey != undef ) {
    @@sshkey {"$::fqdn-dsa":
      host_aliases => ["$::hostname","$::ipaddress"],
      key          => "$::sshdsakey",
      type         => 'dsa',
      tag          => 'example::login_server',
    }
  }
}

This class will export two SSH key entries, one for the rsa key and another for the dsa key. It's important to populate the host_aliases array as we have done so that both the IP address and short hostname are verified with the key when using SSH.

Now we could define an example::laptop class that simply collects the keys and applies them to the system-wide ssh_known_hosts file. Instead, we will define a new fact, homedir in base/lib/facter/homedir.rb, to determine if Puppet is being run by a non-root user, as follows:

Facter.add(:homedir) do
  if Process.uid != 0 and ENV['HOME'] != nil
    setcode do
      begin
        ENV['HOME']
      rescue LoadError
        nil
      end
    end
  end
end

This simple fact checks the UID of the running Puppet process; if it is not 0 (root), it looks for the environment variable HOME and sets the fact homedir equal to the value of that environment variable.

Now we can key off this fact as a top scope variable in our definition of the example::laptop class as follows:

class example::laptop {
  # collect all the ssh keys
  if $::homedir != undef {
    Sshkey<<| tag == 'login_server' |>> {
      target => "$::homedir/.ssh/known_hosts"
    }   
  } else {
    Sshkey<<| tag == 'login_server' |>>
  }
}

Depending on the value of the $::homedir fact, we either define system-wide SSH keys or userdir keys. The SSH key collector (Sshkey<<| tag == 'login_server' |>>) uses the tag login_server to restrict the SSH key resources to those defined by our example::login_server class.

To test this module, we apply the example::login_server class to two servers, ssh1 and ssh2, thereby creating the exported resources. Now on our laptop, we run Puppet as ourselves and sign the key on Puppet master.

Note

If Puppet has already run as root or another user, the certificate may have already been generated for your laptop hostname; use the --certname option to puppet agent to request a new key.

We add the example::laptop class to our laptop machine and examine the output of our Puppet run.

Our laptop is likely not a normal client of our Puppet master, so when calling Puppet agent, we define the puppetserver and environment as follows:

t@mylaptop ~ $ puppet agent -t --environment production --server puppet.example.com --waitforcert 60
Info: Creating a new SSL key for mylaptop.example.com
Info: Caching certificate for ca
Info: csr_attributes file loading from /home/thomas/.puppetlabs/etc/puppet/csr_attributes.yaml
Info: Creating a new SSLcertificate request for mylaptop.example.com
Info: Certificate Request fingerprint (SHA256): 97:86:BF:BD:79:FB:B2:AC:0C:8E:80:D0:5E:D0:18:F9:42:BD:25:CC:A9:25:44:7B:30:7B:F9:C6:A2:11:6E:61
Info: Caching certificate for ca
Info: Caching certificate for mylaptop.example.com
Info: Caching certificate_revocation_list for ca
Info: Retrieving pluginfacts
...
Info: Loading facts
Info: Caching catalog for mylaptop.example.com
Info: Applying configuration version '1449337295'
Notice: /Stage[main]/Example::Laptop/Sshkey[ssh1.example.com-rsa]/ensure: created
Info: Computing checksum on file /home/thomas/.ssh/known_hosts
Notice: /Stage[main]/Example::Laptop/Sshkey[ssh2.example.com-rsa]/ensure: created
Info: Stage[main]: Unscheduling all events on Stage[main]
Notice: Applied catalog in 0.12 seconds

Since we ran the agent as non-root, the system-wide SSH keys in ssh_known_hosts cannot have been modified. Looking at ~/.ssh/known_hosts, we see the new entries at the bottom of the file as follows:

ssh1.example.com-rsa,ssh1,10.0.2.15ssh-rsaAAAAB3NzaC1yc2...
ssh2.example.com-rsa,ssh2,10.0.2.15ssh-rsaAAAAbd3dz56c2E...
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.94.153