Marc van der Wal f220e71544 Éditeur de zone : meilleurs messages de diagnostic
Quand le code appelle named-checkzone pour le contrôle de syntaxe DNS,
les messages renvoyés par cet utilitaire de contrôle sont lus et
interprétés, puis remontés à l’utilisateur.

Ce n’est pas parfait et ça donne une espèce de franglais, mais ça
suffira pour la démo et tant qu’on ne laisse pas les élèves manipuler
directement les fichiers de zone. Dans le cas contraire, il faudra
améliorer cela.
2023-10-25 15:50:31 +02:00

217 lines
6.0 KiB
Perl
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package Email::SpoofingDemo::Web;
use Dancer2;
use Dancer2::Plugin::Deferred;
use JSON;
use REST::Client;
our $VERSION = '0.1';
sub call_api {
my ($method, $target, $url, $body_parameters) = @_;
my $host = config->{'api'}{$target};
die "Invalid target: $target" unless defined $host;
my $client = REST::Client->new();
$client->setHost($host);
$client->setTimeout(5);
$client->addHeader('Accept' => 'application/json');
$client->addHeader('Content-Type' => 'application/json');
my $body_data;
$body_data = encode_json($body_parameters) if defined $body_parameters;
$client->request($method, $url, $body_data);
my $status = $client->responseCode();
if ($status =~ /^2\d\d$/) {
my $response;
if ($client->responseContent() ne '') {
$response = decode_json($client->responseContent());
}
return ($response, $status);
}
else {
warn "API request returned $status";
return ($client->responseContent(), $status);
}
}
get '/' => sub {
template 'index' => { 'title' => 'Accueil' };
};
get '/dns/zone-edit/:zone' => sub {
my $zone = route_parameters->get('zone');
if (not (grep { $_ eq $zone } @{config->{'editable_zones'}})) {
pass;
}
my $zone_contents = deferred 'zone_contents';
if (not defined $zone_contents) {
my ($response, $status) = call_api(GET => 'dns', "/zone/${zone}/file");
$zone_contents = $response->{contents};
}
template 'dns/zone-edit' => {
'title' => 'Éditeur de zone DNS',
'zone_to_edit' => $zone // '',
'zone_contents' => $zone_contents // '',
};
};
post '/dns/zone-edit/:zone' => sub {
my $zone = route_parameters->{'zone'};
unless (grep { $_ eq $zone } @{config->{'editable_zones'}}) {
pass;
}
my $contents = body_parameters->{'zone-contents'};
my ($data, $status) = call_api(PUT => 'dns', "/zone/${zone}/file", { contents => $contents });
if ($status ne '200') {
$data = decode_json($data);
}
deferred status => {
outcome => $data->{outcome},
messages => $data->{messages},
};
deferred zone_contents => $data->{contents};
redirect "/dns/zone-edit/$zone", 303;
};
get '/sender/dkim-keys' => sub {
my ($installed_keys, $status) = call_api(GET => 'sender', '/installed-keys');
($status eq '200') or die "API returned $status";
template 'sender/dkim-keys' => {
active_role => 'sender',
title => 'Gestion des clefs DKIM',
installed_keys => $installed_keys,
};
};
get '/dkim-generator/sender' => sub {
template 'dkim-generator/sender' => {
title => 'Générateur de clefs DKIM'
};
};
post '/dkim-generator/sender' => sub {
my $api_params = {
domain => body_parameters->get('domain'),
selector => body_parameters->get('selector'),
key_size => body_parameters->get('key-size')
};
my ($response, $status) = call_api(POST => 'sender', '/generate-dkim-key', $api_params);
($status eq 200) or die "API returned $status";
template 'dkim-generator/sender' => {
title => 'Générateur de clefs DKIM',
txt_record => $response->{'txt_record'}
};
};
get '/attacker/spoof' => sub {
my ($config, $status) = call_api(GET => 'attacker', '/config');
($status eq 200) or die "Could not get attackers configuration";
template 'attacker/spoof' => {
title => 'Usurpateur didentité de courriel',
default_helo => $config->{default_helo},
my_mail_from => $config->{my_mail_from},
scenarios => $config->{templates}
};
};
post '/attacker/spoof' => sub {
my $scenario = body_parameters->get('scenario');
my $replace_mail_from = (body_parameters->get('rfc5321-mailfrom') eq 'replace');
my $helo = body_parameters->get('helo');
my %api_params = (
scenario => $scenario,
replace_mail_from => ($replace_mail_from ? JSON::true : JSON::false),
helo => $helo
);
my ($logs, $status) = call_api(POST => 'attacker', '/spoof', \%api_params);
deferred 'logs' => $logs;
deferred 'selected_scenario' => $scenario;
deferred 'replace_mail_from' => $replace_mail_from;
redirect '/attacker/spoof', 303;
};
get '/sender/send-email' => sub {
my %template_params = (
title => 'Envoi de messages légitimes',
email_data => [
{
what => 'Confirmation de commande',
from => 'support@expediteur.example',
url => 'confirmation_email'
},
{
what => 'Newsletter',
from => 'info@newsletter.expediteur.example',
url => 'newsletter'
}
]
);
my $success = query_parameters->get('success');
if (defined $success) {
$template_params{success} = ($success eq 'success') ? 'success' : 'failure';
}
template 'sender/send-email' => \%template_params;
};
get '/recipient/settings' => sub {
my ($system_status, $http_code) = call_api(GET => 'recipient', '/status');
die if $http_code ne '200';
template 'recipient/settings' => {
title => 'Paramètres du système destinataire',
system_status => $system_status
};
};
post '/recipient/settings' => sub {
my %api_params = map {
$_ => body_parameters->{"$_-status"} ? 'enabled' : 'disabled'
} qw(spf dkim dmarc);
my (undef, $status) = call_api(PUT => 'recipient', '/status', \%api_params);
my $success = ($status eq 200) ? 'success' : 'failure';
redirect "/recipient/settings?success=$success", 303;
};
get '/sender/send-email/:email' => sub {
my $email = route_parameters->get('email');
my (undef, $response) = call_api(POST => 'sender', "/send-email/${email}");
my $success = ($response =~ /^2\d\d$/) ? 'success' : 'failure';
redirect "/sender/send-email?success=$success", 303;
};
get '/recipient/webmail' => sub {
template 'recipient/webmail' => {
title => 'Courriels'
};
};
any qr{.*} => sub {
template '404';
};
dance;
true;