Mettre en place une interface Web

This commit is contained in:
Marc van der Wal 2023-10-25 15:50:25 +02:00
parent b42985d9a5
commit a2ed2d18b8
22 changed files with 468 additions and 0 deletions

84
console/Dockerfile Normal file
View File

@ -0,0 +1,84 @@
FROM alpine:latest AS bootstrap-build
RUN mkdir /target
ADD https://github.com/twbs/bootstrap/archive/v5.3.0.zip /src/bootstrap-5.3.0.zip
RUN apk add sassc
RUN unzip -d /src /src/bootstrap-5.3.0.zip
RUN ln -nsf /src/bootstrap-5.3.0 /src/bootstrap
COPY scss /src
RUN /bin/sh -c "cd /src && sassc main.scss -t compressed /target/main.css"
FROM alpine:latest
RUN apk add \
bash \
bind \
bind-tools \
vim
# Dependencies for REST API
RUN apk add \
gcc \
libc-dev \
make \
perl-app-cpanminus \
perl-clone \
perl-config-any \
perl-data-optlist \
perl-dev \
perl-exporter-tiny \
perl-extutils-config \
perl-extutils-helpers \
perl-extutils-installpaths \
perl-file-sharedir \
perl-file-sharedir-install \
perl-file-slurp \
perl-file-which \
perl-hash-merge-simple \
perl-hash-multivalue \
perl-http-date \
perl-http-headers-fast \
perl-import-into \
perl-json \
perl-json-maybexs \
perl-module-build \
perl-module-build-tiny \
perl-module-implementation \
perl-module-runtime \
perl-moo \
perl-params-util \
perl-params-validate \
perl-path-tiny \
perl-plack \
perl-readonly \
perl-ref-util \
perl-role-tiny \
perl-safe-isa \
perl-sub-exporter \
perl-sub-install \
perl-sub-quote \
perl-template-toolkit \
perl-type-tiny \
perl-yaml
RUN apk add perl-io-socket-ssl perl-lwp-protocol-https
RUN cpanm -n -v \
Dancer2 \
REST::Client \
Module::Pluggable::Object
COPY web-api /src/web-ui
RUN chmod +x /src/web-ui/bin/app.psgi
COPY --from=bootstrap-build /src/bootstrap/dist/js/bootstrap.bundle.min.js \
/src/web-ui/public/javascripts/bootstrap.bundle.min.js
COPY --from=bootstrap-build /target/main.css /src/web-ui/public/css/main.css
ENTRYPOINT ["/src/web-ui/bin/app.psgi"]

49
console/scss/main.scss Normal file
View File

@ -0,0 +1,49 @@
@import "bootstrap/scss/mixins/banner";
@include bsBanner("");
// Configuration
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/utilities";
// Layout & components
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
@import "bootstrap/scss/images";
@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/tables";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/transitions";
@import "bootstrap/scss/dropdown";
@import "bootstrap/scss/button-group";
@import "bootstrap/scss/nav";
@import "bootstrap/scss/navbar";
@import "bootstrap/scss/card";
@import "bootstrap/scss/accordion";
@import "bootstrap/scss/breadcrumb";
@import "bootstrap/scss/pagination";
@import "bootstrap/scss/badge";
@import "bootstrap/scss/alert";
@import "bootstrap/scss/progress";
@import "bootstrap/scss/list-group";
@import "bootstrap/scss/close";
@import "bootstrap/scss/toasts";
@import "bootstrap/scss/modal";
@import "bootstrap/scss/tooltip";
@import "bootstrap/scss/popover";
@import "bootstrap/scss/carousel";
@import "bootstrap/scss/spinners";
@import "bootstrap/scss/offcanvas";
@import "bootstrap/scss/placeholders";
// Helpers
@import "bootstrap/scss/helpers";
// Utilities
@import "bootstrap/scss/utilities/api";

0
console/web-api/.dancer Normal file
View File

24
console/web-api/MANIFEST Normal file
View File

@ -0,0 +1,24 @@
MANIFEST
MANIFEST.SKIP
.dancer
Makefile.PL
config.yml
cpanfile
views/index.tt
views/layouts/main.tt
lib/Email/SpoofingDemo/Web.pm
t/002_index_route.t
t/001_base.t
environments/production.yml
environments/development.yml
bin/app.psgi
public/500.html
public/dispatch.cgi
public/dispatch.fcgi
public/favicon.ico
public/404.html
public/javascripts/jquery.js
public/css/error.css
public/css/style.css
public/images/perldancer-bg.jpg
public/images/perldancer.jpg

View File

@ -0,0 +1,17 @@
^\.git\/
maint
^tags$
.last_cover_stats
Makefile$
^blib
^pm_to_blib
^.*.bak
^.*.old
^t.*sessions
^cover_db
^.*\.log
^.*\.swp$
MYMETA.*
^.gitignore
^.svn\/
^Email-SpoofingDemo-Web-

View File

@ -0,0 +1,26 @@
use strict;
use warnings;
use ExtUtils::MakeMaker;
# Normalize version strings like 6.30_02 to 6.3002,
# so that we can do numerical comparisons on it.
my $eumm_version = $ExtUtils::MakeMaker::VERSION;
$eumm_version =~ s/_//;
WriteMakefile(
NAME => 'Email::SpoofingDemo::Web',
AUTHOR => q{Marc van der Wal <marc.vanderwal@afnic.fr>},
VERSION_FROM => 'lib/Email/SpoofingDemo/Web.pm',
ABSTRACT => 'Email spoofing demo: Web frontend',
($eumm_version >= 6.3001
? ('LICENSE'=> 'all-rights-reserved')
: ()),
PL_FILES => {},
PREREQ_PM => {
'Test::More' => 0,
'YAML' => 0,
'Dancer2' => 0.300000,
},
dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
clean => { FILES => 'Email-SpoofingDemo-Web-*' },
);

View File

@ -0,0 +1,9 @@
#!/usr/bin/perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";
use Email::SpoofingDemo::Web;
Email::SpoofingDemo::Web->to_app;

View File

@ -0,0 +1,17 @@
appname: "Email::SpoofingDemo::Web"
layout: "main"
charset: "UTF-8"
template: "template_toolkit"
# Specify the addresses of the API endpoints for the other components of the
# system
api:
dns: "ns.example:3000"
# Lists the DNS zones that can be edited by the user
editable_zones:
- example
- expediteur.example
- destinataire.example
- attaquant.example

11
console/web-api/cpanfile Normal file
View File

@ -0,0 +1,11 @@
requires "Dancer2" => "0.300000";
recommends "YAML" => "0";
recommends "URL::Encode::XS" => "0";
recommends "CGI::Deurl::XS" => "0";
recommends "HTTP::Parser::XS" => "0";
on "test" => sub {
requires "Test::More" => "0";
requires "HTTP::Request::Common" => "0";
};

View File

@ -0,0 +1,15 @@
# configuration file for development environment
logger: "console"
log: "core"
# should Dancer2 consider warnings as critical errors?
warnings: 1
# should Dancer2 show a stacktrace when an 5xx error is caught?
# if set to yes, public/500.html will be ignored and either
# views/500.tt, 'error_template' template, or a default error template will be used.
show_errors: 1
# print the banner
startup_info: 1

View File

@ -0,0 +1,16 @@
# configuration file for production environment
# only log warning and error messsages
log: "warning"
# log message to a file in logs/
logger: "file"
# don't consider warnings critical
warnings: 0
# hide errors
show_errors: 0
# disable server tokens in production environments
no_server_tokens: 1

View File

@ -0,0 +1,70 @@
package Email::SpoofingDemo::Web;
use Dancer2;
use JSON;
use REST::Client;
our $VERSION = '0.1';
get '/' => sub {
template 'index' => { 'title' => 'Accueil' };
};
get '/dns/zone-edit/:zone' => sub {
my $zone = route_parameters->get('zone');
if (defined $zone and not (grep { $_ eq $zone } @{config->{'editable_zones'}})) {
pass;
}
my $zone_contents;
if (defined $zone) {
my $client = REST::Client->new();
$client->setHost(config->{'api'}{'dns'});
$client->GET("/zone/${zone}/file");
my $response = from_json($client->responseContent());
$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 $client = REST::Client->new();
$client->setHost(config->{'api'}{'dns'});
$client->PUT("/zone/${zone}/file",
encode_json({ contents => $contents }),
{
"Content-Type" => "application/json",
"Accept" => "application/json"
});
my $success;
if ($client->responseCode() eq '200') {
$success = 'success';
} else {
warn "Got " . $client->responseCode() . " from upstream: " . $client->responseContent();
$success = 'failure'
}
redirect "/dns/zone-edit/$zone?success=$success", 303;
};
any qr{.*} => sub {
template '404';
};
dance;
true;

View File

@ -0,0 +1,16 @@
#!/usr/bin/env perl
BEGIN { $ENV{DANCER_APPHANDLER} = 'PSGI';}
use Dancer2;
use FindBin '$RealBin';
use Plack::Runner;
# For some reason Apache SetEnv directives don't propagate
# correctly to the dispatchers, so forcing PSGI and env here
# is safer.
set apphandler => 'PSGI';
set environment => 'production';
my $psgi = path($RealBin, '..', 'bin', 'app.psgi');
die "Unable to read startup script: $psgi" unless -r $psgi;
Plack::Runner->run($psgi);

View File

@ -0,0 +1,18 @@
#!/usr/bin/env perl
BEGIN { $ENV{DANCER_APPHANDLER} = 'PSGI';}
use Dancer2;
use FindBin '$RealBin';
use Plack::Handler::FCGI;
# For some reason Apache SetEnv directives don't propagate
# correctly to the dispatchers, so forcing PSGI and env here
# is safer.
set apphandler => 'PSGI';
set environment => 'production';
my $psgi = path($RealBin, '..', 'bin', 'app.psgi');
my $app = do($psgi);
die "Unable to read startup script: $@" if $@;
my $server = Plack::Handler::FCGI->new(nproc => 5, detach => 1);
$server->run($app);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
<h1>Erreur 404</h1>
<h2>Page non trouvée</h2>

View File

@ -0,0 +1,2 @@
<h1>Erreur 500</h1>
<h2>Erreur interne du serveur</h2>

View File

@ -0,0 +1,19 @@
<div class="container">
<div class="row mt-3 mb-4">
<main class="col-12">
<form id="edit-form" method="POST">
<div class="mb-3">
<h1>Modification de la zone <span class="font-monospace">[% zone_to_edit %]</span>:</h1>
</div>
<div class="mb-3">
<textarea class="form-control font-monospace" cols="80" id="zone-contents" name="zone-contents" rows="25">
[%- zone_contents -%]
</textarea>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Valider</button>
</div>
</form>
</main>
</div>
</div>

View File

View File

@ -0,0 +1,42 @@
[% BLOCK role %]
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
[% label %]
</a>
[% END %]
<nav class="navbar navbar-expand-md bg-light mb-3">
<div class="container-fluid">
<a class="navbar-brand" href="#">Menu</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Afficher ou cacher la navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-md-0">
<li class="nav-item dropdown">
[% PROCESS role id="attacker" label="Attaquant" %]
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="[% request.uri_base %]/attacker/spoof">Envoyer des messages frauduleux</a></li>
<li><a class="dropdown-item" href="[% request.uri_base %]/dns/zone-edit/attaquant.example">Éditer la zone DNS</a></li>
</ul>
</li>
<li class="nav-item dropdown">
[% PROCESS role id="sender" label="Expéditeur" %]
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="[% request.uri_base %]/sender/send-email">Envoyer un message légitime</a></li>
<li><a class="dropdown-item" href="[% request.uri_base %]/sender/dkim-keys">Afficher des clefs DKIM</a></li>
<li><a class="dropdown-item" href="[% request.uri_base %]/dkim-generator/sender">Générer des clefs DKIM</a></li>
<li><a class="dropdown-item" href="[% request.uri_base %]/dns/zone-edit/expediteur.example">Éditer la zone DNS</a></li>
</ul>
</li>
<li class="nav-item dropdown">
[% PROCESS role id="recipient" label="Destinataire" %]
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="[% request.uri_base %]/dns/zone-edit/destinataire.example">Éditer la zone DNS</a></li>
<li><a class="dropdown-item" href="[% request.uri_base %]/recipient/webmail">Relever les courriels</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="[% settings.charset %]">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>[% title %]</title>
<link rel="stylesheet" href="[% request.uri_base %]/css/main.css">
</head>
<body>
[% INCLUDE layouts/_nav.tt %]
<script src="[% request.uri_base %]/jquery-3.6.4.min.js"></script>
[% content %]
<script src="[% request.uri_base %]/javascripts/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -2,6 +2,20 @@ version: '3.8'
name: 'spf-dkim-dmarc-workshop' name: 'spf-dkim-dmarc-workshop'
services: services:
console:
image: spf-dkim-dmarc-workshop/console
build: ./console
hostname: console
dns:
- 172.31.0.53
networks:
internal:
ipv4_address: 172.31.0.10
ipv6_address: fd4a:8c4:c28b::10
external:
ports:
- "3000:3000"
dns: dns:
image: spf-dkim-dmarc-workshop/dns image: spf-dkim-dmarc-workshop/dns
build: ./dns build: ./dns