commit 1f2b28aab3ce59403d35f8ed056e6a02d6972225 from: Izzy Blacklock date: Sat Sep 16 02:35:18 2023 UTC Moved poc into proper ConfigNow module structure with plugin model Currently outputs SmtpDove and prosody config files. commit - 6f0081a0cd3775c9646b81479428172e078019f8 commit + 1f2b28aab3ce59403d35f8ed056e6a02d6972225 blob - c16eb33aecf822e6972947862da982ad25fd136a blob + 30f9b58226f9e011b82831779ea5aefc47f56901 --- bin/configNow.pl +++ bin/configNow.pl @@ -2,56 +2,25 @@ use strict; use warnings; +use lib qw(./lib); +use IRCNOW::ConfigNow; +my $shellname = shift || 'bnsnet'; +my $username = shift || 'izzyb'; +my $shellConfig = new IRCNOW::ConfigNow( + type=>'shell', + shellname => $shellname, + username => $username, + ipv4 => '38.87.162.191', + ipv6 => '2602:fccf:1:1191::', + domain => 'user.planetofnix.com', +); -my $ipv4 = '38.87.162.191'; -my $ipv6 = '2602:fccf:1:1191::'; -my $domain = 'user.planetofnix.com'; - -my $shellname = shift; -my $username = shift; -my $password = shift; - - -my $shell = { - mail_domains => qq{$shellname.$domain\n}, - mail_mailname_config => qq{$shellname.$domain\n}, - dns => qq{ -$shellname 3600 IN A $ipv4 - 3600 IN AAAA $ipv6 - 3600 IN MX 10 mail -imap.$shellname 3600 IN CNAME imap -pop.$shellname 3600 IN CNAME pop -pop3.$shellname 3600 IN CNAME pop3 -smtp.$shellname 3600 IN CNAME smtp -mail.$shellname 3600 IN CNAME mail -_xmpp-client._tcp.$shellname 3600 IN SRV 0 5 5222 xmpp -_xmpp-server._tcp.$shellname 3600 IN SRV 0 5 5269 xmpp -}, - acme_client => qq{ -domain $shellname.$domain { - domain key "/etc/ssl/private/$shellname.$domain.key" - domain full chain certificate "/etc/ssl/$shellname.$domain.fullchain.pem" - sign with letsencrypt +for my $module ($shellConfig->list()) { + for my $file ($shellConfig->list($module)) { + print "$file: ".$shellConfig->filename($file) ."\n"; + print $shellConfig->output($file) . "\n"; + } } -}, - prosody_config => qq{ -VirtualHost "$shellname.$domain" -ssl = { - certificate = "/etc/prosody/certs/$shellname.$domain.fullchain.pem"; - key = "/etc/prosody/certs/$shellname.$domain.key"; -} -}, -}; -my $user = { - mail_Virtual => qq{$username\@$shellname.$domain vmail}, - mail_user => qq{$username\@$shellname.$domain: $username\@$shellname.$domain}, - mail_passwd => "$username\@$shellname.$domain:$password"."::::::userdb_quota_rule=*:storage=1G", -}; - -use Data::Dumper; -print Dumper($shell); -print Dumper($user); - blob - /dev/null blob + 85a6741b472c420bba08903374d9d07528282bba (mode 644) --- /dev/null +++ lib/IRCNOW/ConfigNow/Module/AcmeClient.pm @@ -0,0 +1,15 @@ +acme_client => qq{ +domain $shellname.$domain { + domain key "/etc/ssl/private/$shellname.$domain.key" + domain full chain certificate "/etc/ssl/$shellname.$domain.fullchain.pem" + sign with letsencrypt +} + +dns => { + filename => "/var/nsd/zones/master/$shellname + varlist => [qw(shellname ipv4 ipv6)], + template => qq{ +%s 3600 IN A %s + 3600 IN AAAA %s +}, + blob - /dev/null blob + b8ee5dca652b3cbb57b41bc62bc82d032d3441f9 (mode 644) --- /dev/null +++ lib/IRCNOW/ConfigNow/Module/Prosody.pm @@ -0,0 +1,38 @@ +package IRCNOW::ConfigNow::Module::Prosody; +use base qw{IRCNOW::ConfigNow::Module}; +use strict; +use warnings; +use Carp; + +sub new { + my $class = shift; + my $options = {@_}; + my $domain = $options->{vars}->{domain} || die "{domain} is a reqired option for IRCNOW::ConfigNow::Module::Prosody"; + return $class->SUPER::new( vars => $options->{vars}, files=>{ + prosody => { + filename => "/etc/prosody/prosody.cfg.lua", + varlist => [qw{shellname domain}], + template => sub { + my $shellname = shift; + my $domain = shift; + return qq{ +VirtualHost "$shellname.$domain" +ssl = { + certificate = "/etc/prosody/certs/$shellname.$domain.fullchain.pem"; + key = "/etc/prosody/certs/$shellname.$domain.key"; +} +}; + }, + }, + dns => { + filename => "/var/nsd/zones/master/$domain", + varlist => [qw(shellname shellname)], + template => qq{ +_xmpp-client._tcp.%s 3600 IN SRV 0 5 5222 xmpp +_xmpp-server._tcp.%s 3600 IN SRV 0 5 5269 xmpp +}, + }, + }); +} + +1; blob - /dev/null blob + 78acf3dd7cdbe0e6862e5411ed3c1d4542295b4e (mode 644) --- /dev/null +++ lib/IRCNOW/ConfigNow/Module/SmtpDove.pm @@ -0,0 +1,61 @@ +package IRCNOW::ConfigNow::Module::SmtpDove; +use base qw{IRCNOW::ConfigNow::Module}; +use strict; +use warnings; +use Carp; + +sub new { + my $class = shift; + my $options = {@_}; + my $domain = $options->{vars}->{domain} || die "{domain} is a reqired option for IRCNOW::ConfigNow::Module::SmtpDove"; + return $class->SUPER::new( vars => $options->{vars}, files=>{ + nsd => { + filename => qq{/var/nsd/zones/master/$domain}, + varlist => ['shellname'], + template => sub { + my $shellname = shift; + return qq{ +$shellname 3600 IN MX 10 mail +imap.$shellname 3600 IN CNAME imap +pop.$shellname 3600 IN CNAME pop +pop3.$shellname 3600 IN CNAME pop3 +smtp.$shellname 3600 IN CNAME smtp +mail.$shellname 3600 IN CNAME mail + }; + }, + }, + mail_domains => { + filename => qq{/etc/mail/domains}, + varlist => [qw{shellname domain}], + template => qq{%s.%s\n}, + }, + mail_mailname => { + filename => qq{/etc/mail/mailname}, + varlist => [qw{shellname domain}], + template => qq{%s.%s\n}, + }, + mail_Virtual => { + filename => qq{/etc/mail/virtuals}, + varlist => [qw{username shellname domain}], + template => qq{%s@%s.%s vmail\n}, + }, + mail_user => { + filename => qq{/etc/mail/users}, + varlist => [qw{username shellname domain username shellname domain}], + template => qq{%s@%s.%s: %s@%s.%s\n}, + }, + mail_passwd => { + filename => qq{/etc/mail/passwd}, + varlist => [qw{username shellname domain password}], + template => qq{%s@%s.%s:%s::::::userdb_quota_rule=*:storage=1G\n}, + }, + }); +} + +sub create_shell { +} +sub create_user { +} + + +1; blob - /dev/null blob + a80dc96608099a5dbf17dc77a5fd79e5c0168644 (mode 644) --- /dev/null +++ lib/IRCNOW/ConfigNow/Module.pm @@ -0,0 +1,82 @@ +package IRCNOW::ConfigNow::Module; +use strict; +use warnings; +use Carp; + +# +# Each module has a list of files it works with +# Each file has to have a filename, template, and varlist +# The template can be a sprintf string or a sub returning a string +# The varlist will be passed as params to either + +# This can be used as a base class for more advanced or complicated +# needs or by passing the params needed for a new module to new. + +sub new { + my $class =shift; + my $options = {@_}; + my $self = { + vars => [], + files => {}, + }; + if (exists $options->{vars}) { + $self->{vars}= $options->{vars}; + } + bless $self, $class; + if (exists $options->{files}) { + for my $file (keys %{$options->{files}}) { + # Insure we have the required parms; make a copy of them + #XXX Not sure if a deep copy is needed but loop need to verify the keys. + for my $k (qw{filename varlist template}) { + croak "Required param $k missing" unless exists $options->{files}->{$file}->{$k}; + $self->{files}->{$file}->{$k} = $options->{files}->{$file}->{$k}; + } + } + return $self; + } + croak "Required list of files to manipulate is missing"; +} + +sub list { + my $self = shift; + my @list = keys(%{$self->{files}}); + return @list; +} + +sub filename { + my $self = shift; + my $file = shift; + return $self->{files}->{$file}->{filename}; +} + +sub output { + my $self = shift; + my $file = shift || carp "File name required"; + my @params; + # Specified file doesn't exist in this module + return unless ( + exists $self->{files}->{$file} and + # Shouldn't have $file without {$file}->{template} + #XXX should track down where it's being defined into existance + exists $self->{files}->{$file}->{template} + ); + my $mod = $self->{files}->{$file}; + + for my $param ( @{$mod->{varlist}}) { + # Don't have needed param so return undef + unless (defined $self->{vars}->{$param}) { + return undef; + } + push @params, $self->{vars}->{$param}; + } + if (ref $mod->{template} eq 'CODE') { + return $mod->{template}->(@params); + } + + return sprintf($mod->{template}, @params); +} + + + +1; + blob - /dev/null blob + beb64851249690758ad47ae26c308a43333b2075 (mode 644) --- /dev/null +++ lib/IRCNOW/ConfigNow.pm @@ -0,0 +1,106 @@ +package IRCNOW::ConfigNow; +# +# +# +# +use strict; +use warnings; +use Carp; +sub new { + my $class = shift; + my $options = {@_}; + + # set sane defaults. + my $self = { + vars =>$options, # options could be hash template values + modules =>{}, # ConfigNow Modules + }; + bless $self, $class; + # Load any modules passed in + if (exists $options->{modules}) { + for (keys %{$options->{modules}}) { + $self->mod_load($_,$options->{modules}->{$_}); + } + } + # Check if a type was requested - types provide shortcuts for multiple modules. + if (exists $options->{type} and (lc( $options->{type} ) eq 'shell')) { + $self->mod_load('mail','IRCNOW::ConfigNow::Module::SmtpDove'); + $self->mod_load('xmpp','IRCNOW::ConfigNow::Module::Prosody'); + } + return $self; +} + + +sub filename { + my $self = shift; + my $file = shift; + + for my $mod (keys %{$self->{modules}}) { + my $filename = $self->{modules}->{$mod}->filename($file); + return $filename if defined $filename; + } +} + +sub list { + my $self = shift; + my $module = shift; + my @list; + + if (defined $module and exists $self->{modules}->{$module}) { + @list = $self->{modules}->{$module}->list(); + + } else { + @list = keys %{$self->{modules}}; + } + return @list; +} + +sub output { + my $self = shift; + my $file = shift || carp "!!!!!!!!!!! undef for file:"; + my $output = ""; + for my $mod (keys %{$self->{modules}}) { + $output .= $self->{modules}->{$mod}->output($file) || ""; + } + + return $output; +} + + +sub mod_load { + my $self=shift; + my $modName=shift; + my $module=shift; + if (ref $module and $module.isa('IRCNOW::ConfigNow::Module')) { + # Already have an object so attach it. + $self->{modules}->{$modName}=$module; + } else { + # Lets assume they passed us a full module name. + if (not defined eval "require $module") { +warn $@; + # Failed so try prepending IRCNOW::ConfigNow:: + $module = "IRCNOW::ConfigNow::$module"; + if (not defined eval "require $module") { +warn $@; + croak "Failed to load $module"; + } + } + } + # create module object and add to hash + my $obj = $module->new(vars => $self->{vars}) || croak 'Failed to load Module: $module'; + $self->{modules}->{$modName}=$obj; + return $obj; +} + + +sub vars { + my $self=shift; + my $vars = {@_}; + for (keys(%$vars)) { + $self->{vars}->{$_} = $vars->{$_}; + } +} + + +1; +