Seventh refactor: Use a reverse proxy for HTTPS
We want the Jenkins web interface to use HTTPS, which we can accomplish with an Nginx reverse proxy. We also want to standardize the ports: the Jenkins app always binds to its default port, and the proxy always serves over 443 for HTTPS and 80 for HTTP.
If we want to keep vanilla HTTP available, we can provide an $ssl
parameter. If set to false
(the default), you can access Jenkins via both HTTP and HTTPS.
We can also add a $site_alias
parameter, so
the proxy can listen on a hostname other than the node's main FQDN.
class profile::jenkins::controller ( Boolean $ssl = false, Optional[String[1]] $site_alias = undef, # IMPORTANT: notice that $jenkins_port is removed. # ...
Set configure_firewall =>
false
in the Jenkins class:
class { 'jenkins': lts => true, repo => true, direct_download => $direct_download, version => 'latest', service_enable => true, service_ensure => running, configure_firewall => false, # <-- here install_java => $install_jenkins_java, manage_user => false, manage_group => false, manage_datadirs => false, # IMPORTANT: notice that port and config_hash are removed. }
We need to deploy SSL certificates where Nginx can reach them. Because we serve a lot of things over HTTPS, we already had a profile for that:
# Deploy the SSL certificate/chain/key for sites on this domain. include profile::ssl::delivery_wildcard
This is also a good time to add some info for the message of the day, handled by puppetlabs/motd:
motd::register { 'Jenkins CI controller (profile::jenkins::controller)': } if $site_alias { motd::register { 'jenkins-site-alias': content => @("END"), profile::jenkins::controller::proxy Jenkins site alias: ${site_alias} |-END order => 25, } }
The bulk of the work is handled by a new profile called
profile::jenkins::controller::proxy
. We're omitting the code for brevity;
in summary, what it does is:
- Include
profile::nginx
. - Use resource types from the jfryman/nginx to set up a vhost, and to force a redirect to HTTPS if we haven't enabled vanilla HTTP.
- Set up logstash forwarding for access and error logs.
- Include
profile::fw::https
to manage firewall rules, if necessary.
Then, we declare that profile in our main profile:
class { 'profile::jenkins::controller::proxy': site_alias => $site_alias, require_ssl => $ssl, }
We are now breaking rule 1, the most important rule of the roles and profiles method. Why?
Because profile::jenkins::controller::proxy
is a "private" profile that belongs
solely to profile::jenkins::controller
. It will never be declared by any
role or any other profile.
This is the only exception to rule 1: if you're separating out code for the sole purpose of readability --- that is, if you could paste the private profile's contents into the main profile for the exact same effect --- you can use a resource-like declaration on the private profile. This lets you consolidate your data lookups and make the private profile's inputs more visible, while keeping the main profile a little cleaner. If you do this, you must make sure to document that the private profile is private.
If there is any chance that this code might be reused by another profile, obey rule 1.
Diff of seventh refactor
@@ -1,8 +1,9 @@ # /etc/puppetlabs/code/environments/production/site/profile/manifests/jenkins/controller.pp class profile::jenkins::controller ( - String $jenkins_port = '9091', Boolean $backups_enabled = false, Boolean $manage_plugins = false, + Boolean $ssl = false, + Optional[String[1]] $site_alias = undef, Variant[String[1], Boolean] $direct_download = 'http://pkg.jenkins-ci.org/debian-stable/binary/jenkins_1.642.2_all.deb', Optional[String[1]] $jenkins_logs_to_syslog = undef, Boolean $install_jenkins_java = true, @@ -11,6 +12,9 @@ class profile::jenkins::controller ( # We rely on virtual resources that are ultimately declared by profile::server. include profile::server + # Deploy the SSL certificate/chain/key for sites on this domain. + include profile::ssl::delivery_wildcard + # Some default values that vary by OS: include profile::jenkins::params $jenkins_owner = $profile::jenkins::params::jenkins_owner @@ -22,6 +26,31 @@ class profile::jenkins::controller ( include profile::jenkins::controller::plugins } + motd::register { 'Jenkins CI controller (profile::jenkins::controller)': } + + # This adds the site_alias to the message of the day for convenience when + # logging into a server via FQDN. Because of the way motd::register works, we + # need a sort of funny formatting to put it at the end (order => 25) and to + # list a class so there isn't a random "--" at the end of the message. + if $site_alias { + motd::register { 'jenkins-site-alias': + content => @("END"), + profile::jenkins::controller::proxy + + Jenkins site alias: ${site_alias} + |-END + order => 25, + } + } + + # This is a "private" profile that sets up an Nginx proxy -- it's only ever + # declared in this class, and it would work identically pasted inline. + # But because it's long, this class reads more cleanly with it separated out. + class { 'profile::jenkins::controller::proxy': + site_alias => $site_alias, + require_ssl => $ssl, + } + # Sensitive info (like SSH keys) isn't checked into version control like the # rest of our modules; instead, it's served from a custom mount point on a # designated server. @@ -56,16 +85,11 @@ class profile::jenkins::controller ( version => 'latest', service_enable => true, service_ensure => running, - configure_firewall => true, + configure_firewall => false, install_java => $install_jenkins_java, manage_user => false, manage_group => false, manage_datadirs => false, - port => $jenkins_port, - config_hash => { - 'HTTP_PORT' => { 'value' => $jenkins_port }, - 'JENKINS_PORT' => { 'value' => $jenkins_port }, - }, } # When not using the jenkins module's java version, install java8.