Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add support for multi-master FreeIPA replicas.
This patch lands the first part of the multi-master replica code.

NOTE: unfortunately, at this time the "secure secrets management" [1]
does not work in conjunction with this multi-master replicas patch.

[1]
https://ttboj.wordpress.com/2014/06/06/securely-managing-secrets-for-freeipa-with-puppet/
  • Loading branch information
purpleidea committed Jul 11, 2014
1 parent 84fb95e commit b621b1a
Show file tree
Hide file tree
Showing 18 changed files with 857 additions and 56 deletions.
74 changes: 74 additions & 0 deletions lib/facter/ipa_peering.rb
@@ -0,0 +1,74 @@
# FreeIPA templating module by James
# Copyright (C) 2012-2013+ James Shubin
# Written by James Shubin <james@shubin.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

require 'facter'

# regexp to match an fqdn pattern, eg: ipa1.example.com
regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}$/ # TODO: is this right ?
prefix = 'peer_'

# find the module_vardir
dir = Facter.value('puppet_vardirtmp') # nil if missing
if dir.nil? # let puppet decide if present!
dir = Facter.value('puppet_vardir')
if dir.nil?
var = nil
else
var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash
end
else
var = dir.gsub(/\/$/, '')+'/'
end

if var.nil?
# if we can't get a valid vardirtmp, then we can't continue
module_vardir = nil
peerdir = nil
else
module_vardir = var+'ipa/'
peerdir = module_vardir+'replica/peering/'
end

# create facts from externally collected peer files
peer = ''
found = []
if not(peerdir.nil?) and File.directory?(peerdir)
Dir.glob(peerdir+prefix+'*').each do |f|

b = File.basename(f)
# strip off leading prefix
fqdn = b[prefix.length, b.length-prefix.length]
peer = File.open(f, 'r').read.strip.downcase # read into str
if peer.length > 0 and regexp.match(peer) and peer == fqdn
# avoid: http://projects.puppetlabs.com/issues/22455
found.push(peer)
# TODO: print warning on else...
end
end
end

# FIXME: alternatively, each host could have a "create" time, and they could be
# sorted according to that time first, and alphabetically second... the idea is
# to be able to provide a consistent ordering that doesn't change with joins...
Facter.add('ipa_server_replica_peers') do
#confine :operatingsystem => %w{CentOS, RedHat, Fedora}
setcode {
found.sort.join(',')
}
end

# vim: ts=8
71 changes: 71 additions & 0 deletions lib/facter/ipa_replica.rb
@@ -0,0 +1,71 @@
# FreeIPA templating module by James
# Copyright (C) 2012-2013+ James Shubin
# Written by James Shubin <james@shubin.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

require 'facter'

# regexp to match an fqdn pattern, eg: ipa1.example.com
regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}$/ # TODO: is this right ?
prefix = 'replica-info-'
ending = '.gpg'

# find the module_vardir
dir = Facter.value('puppet_vardirtmp') # nil if missing
if dir.nil? # let puppet decide if present!
dir = Facter.value('puppet_vardir')
if dir.nil?
var = nil
else
var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash
end
else
var = dir.gsub(/\/$/, '')+'/'
end

if var.nil?
# if we can't get a valid vardirtmp, then we can't continue
valid_replicadir = nil
else
module_vardir = var+'ipa/'
valid_replicadir = module_vardir.gsub(/\/$/, '')+'/replica/install/'
end

found = []

if not(valid_replicadir.nil?) and File.directory?(valid_replicadir)
Dir.glob(valid_replicadir+prefix+'*'+ending).each do |f|
b = File.basename(f)

g = b.slice(prefix.length, b.length-prefix.length-ending.length)

if g.length > 0 and regexp.match(g)
if not found.include?(g)
found.push(g)
end
# TODO: print warning on else...
end
end
end

Facter.add('ipa_replica_prepared_fqdns') do
#confine :operatingsystem => %w{CentOS, RedHat, Fedora}
setcode {
# TODO: facter should support native list types :)
found.sort.join(',')
}
end

# vim: ts=8
72 changes: 72 additions & 0 deletions lib/puppet/parser/functions/ipa_topology_flat.rb
@@ -0,0 +1,72 @@
# FreeIPA templating module by James
# Copyright (C) 2012-2013+ James Shubin
# Written by James Shubin <james@shubin.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

# TODO: is 'flat' the correct topological name for what this algorithm outputs?

module Puppet::Parser::Functions
newfunction(:ipa_topology_flat, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
Return an ipa N-N topology from a sorted list of hosts
Example:
$valid_peers = ipa_topology_flat($peers)
notice("valid peers is: ${valid_peers}")
This function is used internally for building automatic topologies.
ENDHEREDOC

Puppet::Parser::Functions.function('warning') # load function
# signature: replica, bricks -> bricks
unless args.length == 1
raise Puppet::ParseError, "ipa_topology_flat(): wrong number of arguments (#{args.length}; must be 1)"
end
if not(args[0].is_a?(Array))
raise Puppet::ParseError, "ipa_topology_flat(): expects the first argument to be an array, got #{args[0].inspect} which is of type #{args[0].class}"
end

peers = args[0]

if peers.uniq.length != peers.length # there are duplicates!
raise Puppet::ParseError, "ipa_topology_flat(): duplicates were found in the first argument!"
end

# NOTE: need at least one
if peers.length < 1
function_warning(["ipa_topology_flat(): peer list is empty"])
return {}
end

result = {}

peers.each do |x|

same = peers.dup # copy... to not destroy peers!
if same.delete(x).nil? # normally returns the value...
# TODO: return programming error: delete failed
end

# connect to every peer except yourself
result[x] = same

end

result # return
end
end

# vim: ts=8
76 changes: 76 additions & 0 deletions lib/puppet/parser/functions/ipa_topology_ring.rb
@@ -0,0 +1,76 @@
# FreeIPA templating module by James
# Copyright (C) 2012-2013+ James Shubin
# Written by James Shubin <james@shubin.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

# TODO: is 'ring' the correct topological name for what this algorithm outputs?

module Puppet::Parser::Functions
newfunction(:ipa_topology_ring, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
Return an ipa ring topology from a sorted list of hosts
Example:
$valid_peers = ipa_topology_ring($peers)
notice("valid peers is: ${valid_peers}")
This function is used internally for building automatic topologies.
ENDHEREDOC

Puppet::Parser::Functions.function('warning') # load function
# signature: replica, bricks -> bricks
unless args.length == 1
raise Puppet::ParseError, "ipa_topology_ring(): wrong number of arguments (#{args.length}; must be 1)"
end
if not(args[0].is_a?(Array))
raise Puppet::ParseError, "ipa_topology_ring(): expects the first argument to be an array, got #{args[0].inspect} which is of type #{args[0].class}"
end

peers = args[0]

if peers.uniq.length != peers.length # there are duplicates!
raise Puppet::ParseError, "ipa_topology_ring(): duplicates were found in the first argument!"
end

# NOTE: need at least one
if peers.length < 1
function_warning(["ipa_topology_ring(): peer list is empty"])
return {}
end

result = {}

i = 0
while i < peers.length do

x = peers[i] # from
if i < peers.length-1
y = peers[i+1] # to
else
y = peers[0] # wrap around
end

# store value as a list, in the ring case of length 1
result[x] = [y]

i+=1 # i++
end

result # return
end
end

# vim: ts=8
4 changes: 2 additions & 2 deletions manifests/client/service.pp
Expand Up @@ -156,7 +156,7 @@
Exec['ipa-install'],
Ipa::Client::Host["${valid_host}"],
],
alias => "ipa-server-kinit-${name}",
alias => "ipa-client-service-kinit-${name}",
}

$args01 = "--server='${valid_server}'" # contact this KDC server (ipa)
Expand All @@ -177,7 +177,7 @@
#Package['ipa-client'],
#Exec['ipa-install'],
#Ipa::Client::Host["${valid_host}"],
Exec["ipa-server-kinit-${name}"],
Exec["ipa-client-service-kinit-${name}"],
],
#alias => "ipa-getkeytab-${name}",
}
Expand Down
27 changes: 27 additions & 0 deletions manifests/common.pp
@@ -0,0 +1,27 @@
# FreeIPA templating module by James
# Copyright (C) 2012-2013+ James Shubin
# Written by James Shubin <james@shubin.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

class ipa::common(

) {

# TODO: patch freeipa to provide this in a stable way...
$ipa_installed = "/usr/bin/python -c 'import sys,ipaserver.install.installutils; sys.exit(0 if ipaserver.install.installutils.is_ipa_configured() else 1)'"

}

# vim: ts=8

0 comments on commit b621b1a

Please sign in to comment.