Mettre en place une interface Web
This commit is contained in:
parent
b42985d9a5
commit
a2ed2d18b8
|
@ -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"]
|
|
@ -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,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
|
|
@ -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-
|
|
@ -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-*' },
|
||||||
|
);
|
|
@ -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;
|
|
@ -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
|
|
@ -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";
|
||||||
|
};
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
@ -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);
|
|
@ -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
|
@ -0,0 +1,2 @@
|
||||||
|
<h1>Erreur 404</h1>
|
||||||
|
<h2>Page non trouvée</h2>
|
|
@ -0,0 +1,2 @@
|
||||||
|
<h1>Erreur 500</h1>
|
||||||
|
<h2>Erreur interne du serveur</h2>
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue