Compare commits

...

20 Commits

Author SHA1 Message Date
7b8cc1920e token-validator: Don't print error on duplicate challenge validation
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-02-27 12:45:23 +01:00
805301a7ce CI/CD: Try new publication mechanism 2022-02-27 12:45:23 +01:00
6812cfff58 challenge: Update some references 2022-02-27 12:45:23 +01:00
74cbd90e20 challenge: Handle root password 2022-02-27 12:45:23 +01:00
1fa4649c70 challenge: beep compatible with busybox 2022-02-27 12:45:23 +01:00
26c162f319 login-validator: Save shadow file by IP 2022-02-27 12:45:23 +01:00
59abc217d1 login-validator: Append shadow to original challenge archive 2022-02-27 12:45:23 +01:00
f317d11fac login-validator: Add gzip compression 2022-02-27 12:45:23 +01:00
aa753b4075 login-validator: Generate cpio archive 2022-02-27 12:45:23 +01:00
0e3bdb4a6b shadow-up: Add logging 2022-02-27 12:45:23 +01:00
d896911077 login-validator: Can receive shadow files 2022-02-27 12:45:23 +01:00
41a38f99d3 challenge: Add shadow-up ctr 2022-02-27 12:45:23 +01:00
03e77c09e0 Update Makefile deps 2022-02-27 12:45:23 +01:00
368782591f Use chrony in server 2022-02-27 12:45:23 +01:00
92a90078cf shadow-up: New package 2022-02-27 12:45:23 +01:00
32f17d9b4b server: Transmit SNI 2022-02-27 12:45:23 +01:00
f01c51722a challenge: Remove openntpd: doesn't work 2022-02-27 12:45:23 +01:00
f143d94f9a chrony: New package to replace openntpd 2022-02-27 12:45:23 +01:00
f0845f4afe Update alpine version 2022-02-27 12:45:23 +01:00
f059c0b208 login-app: Try with an older golang 2022-02-27 12:45:23 +01:00
39 changed files with 562 additions and 59 deletions

View File

@ -86,7 +86,7 @@ platform:
arch: amd64
steps:
- name: render subject
- name: compose subject
image: pandoc/latex:2.17.1
commands:
- sed -i s/v3.12/v3.14/ /etc/apk/repositories
@ -98,11 +98,39 @@ steps:
- tar xf /tmp/FantasqueSansMono-Normal.tar.gz -C /usr/share/fonts/fantasque-sans-mono OTF/ TTF/ --strip-component=1
- mkdir dist
- make -C tutorial/ansible
- mv tutorial/ansible/tutorial.pdf dist/tutorial-2.pdf
- mv tutorial/ansible/tutorial.pdf dist/tutorial-2-${DRONE_TAG##srs20??-tutorial2-}.pdf
- name: deploy subject
image: appleboy/drone-scp
settings:
tar_tmp_path: /tmp/
host: adlin.nemunai.re
target: /var/www/nemunai.re/adlin/subjects/
source: dist/*
strip_components: 1
username:
from_secret: ssh_username
key:
from_secret: deploy_key
port:
from_secret: ssh_port
- name: link to latest subject
image: appleboy/drone-ssh
settings:
host: adlin.nemunai.re
username:
from_secret: ssh_username
key:
from_secret: deploy_key
port:
from_secret: ssh_port
script:
- ln -sf /var/www/nemunai.re/adlin/subjects/tutorial-2-${DRONE_TAG##srs20??-tutorial2-}.pdf /var/www/nemunai.re/adlin/tutorial-2.pdf
trigger:
ref:
- refs/tags/tutorial2-*
- refs/tags/srs20??-tutorial2-*
---
kind: pipeline
type: docker

View File

@ -1,9 +1,9 @@
LINUXKIT ?= $(GOPATH)/bin/linuxkit
tuto1: token-validator/token-validator server.iso
tuto1: login-initrd.img challenge token-validator/token-validator server.iso
pkg/login-app: pkg/login-app/cmd/login.go pkg/login-app/cmd/dialog-checklogin.go pkg/login-app/cmd/cmd pkg/login-app/cmd/dialog-login.go pkg/login-app/cmd/login-app pkg/login-app/cmd/dialog-errmsg.go pkg/login-app/cmd/main.go pkg/login-app/cmd/stream.go pkg/login-app/cmd/cinematic.go pkg/login-app/build.yml pkg/login-app/Dockerfile
$(LINUXKIT) pkg build --platforms linux/amd64 -org nemunaire pkg/login-app/
pkg/login-app: pkg/login-app/cmd/login.go pkg/login-app/cmd/dialog-checklogin.go pkg/login-app/cmd/dialog-login.go pkg/login-app/cmd/login-app pkg/login-app/cmd/dialog-errmsg.go pkg/login-app/cmd/main.go pkg/login-app/cmd/stream.go pkg/login-app/cmd/cinematic.go pkg/login-app/build.yml pkg/login-app/Dockerfile
$(LINUXKIT) pkg build -platforms linux/amd64 -org nemunaire pkg/login-app/
#$(LINUXKIT) pkg push -org nemunaire --sign=false pkg/login-app/
touch pkg/login-app
@ -14,10 +14,10 @@ token-validator/token-validator: token-validator/*.go
go generate ./token-validator
GOOS=linux GOARM=5 GOARCH=arm go build -tags netgo -ldflags '-w -extldflags "-static"' -o $@ ./token-validator
challenge: pkg/challenge/adlin pkg/challenge/issue pkg/challenge/init
pkg/challenge: pkg/challenge/adlin pkg/challenge/issue pkg/challenge/init
$(LINUXKIT) pkg build --platforms linux/amd64 -org nemunaire pkg/challenge/
challenge-initrd.img: challenge.yml subject/adlin.6.gz subject/adlin-TP1-topologie.png
challenge-initrd.img: challenge.yml subject/adlin.6.gz subject/adlin-TP1-topologie.png pkg/challenge pkg/shadow-up
$(LINUXKIT) build -docker $<
@ -25,6 +25,14 @@ pkg/arp-spoofer: pkg/arp-spoofer/cmd/main.go pkg/arp-spoofer/cmd/arp.go pkg/arp-
$(LINUXKIT) pkg build --platforms linux/amd64 -org nemunaire $@
touch $@
pkg/chrony: pkg/chrony/build.yml pkg/chrony/Dockerfile
$(LINUXKIT) pkg build --platforms linux/amd64 -org nemunaire pkg/chrony/
touch pkg/chrony
pkg/shadow-up: pkg/shadow-up/build.yml pkg/shadow-up/Dockerfile
$(LINUXKIT) pkg build --platforms linux/amd64 -org nemunaire pkg/shadow-up/
touch pkg/shadow-up
pkg/login-validator: pkg/login-validator/cmd/login.go pkg/login-validator/cmd/main.go pkg/login-validator/cmd/pxetpl.go pkg/login-validator/cmd/logout.go pkg/login-validator/cmd/auth.go pkg/login-validator/cmd/arp.go pkg/login-validator/cmd/auth_krb5.go pkg/login-validator/cmd/auth_ldap.go pkg/login-validator/cmd/students.go pkg/login-validator/cmd/auth_fwd.go pkg/login-validator/cmd/ssh.go pkg/login-validator/build.yml pkg/login-validator/Dockerfile
$(LINUXKIT) pkg build --platforms linux/amd64 -org nemunaire pkg/login-validator/
touch pkg/login-validator
@ -49,7 +57,7 @@ pkg/wg-manager: pkg/wg-manager/cmd/register.go pkg/wg-manager/cmd/main.go pkg/wg
$(LINUXKIT) pkg build --platforms linux/amd64 -org nemunaire pkg/wg-manager/
touch pkg/wg-manager
server.iso: server.yml students.csv ssl/fullchain.pem ssl/privkey.pem challenge-initrd.img pkg/arp-spoofer pkg/login-validator pkg/monit pkg/postfix pkg/tftpd pkg/unbound pkg/wg-manager challenge-kernel login-initrd.img
server.iso: server.yml students.csv ssl/fullchain.pem ssl/privkey.pem challenge-initrd.img pkg/arp-spoofer pkg/chrony pkg/login-validator pkg/monit pkg/postfix pkg/tftpd pkg/unbound pkg/wg-manager challenge-kernel login-initrd.img
$(LINUXKIT) build -docker -format iso-bios $<
pkg/debian-tuto2: pkg/debian-tuto2/sshd_config pkg/debian-tuto2/gai.conf pkg/debian-tuto2/isolinux.cfg pkg/debian-tuto2/build.yml pkg/debian-tuto2/default.script pkg/debian-tuto2/issue pkg/debian-tuto2/Dockerfile

View File

@ -1,5 +1,6 @@
init:
- nemunaire/challenge:aeaf6be5e366eca943d8d37e8fb0f39d149a42cf
- nemunaire/adlin-shadow-up:8139aa212579684475310cccbaad4925a86ee4f6
files:
- path: etc/motd
@ -75,9 +76,11 @@ files:
contents: |
#!/bin/sh
[ "$2" = "root" ] &&
echo -e "\\e[01mTip: vous souvenez-vous du mot de passe root ?\\e[0m" ||
echo -e "\\e[01mTip: cette machine n'est pas reliée à l'authentification de l'école.\\e[0m"
grep -q '^root:!' /etc/shadow && {
[ "$2" = "root" ] &&
echo -e "\\e[01mTip: vous souvenez-vous du mot de passe root ?\\e[0m" ||
echo -e "\\e[01mTip: cette machine n'est pas reliée à l'authentification de l'école.\\e[0m"
}
exec login $@
mode: "0755"

View File

@ -1,4 +1,4 @@
FROM alpine:edge AS mirror
FROM alpine:3.15 AS mirror
RUN mkdir -p /out/etc/apk/ && \
cp /etc/apk/repositories /out/etc/apk/ && \

View File

@ -1,4 +1,4 @@
FROM golang:alpine as gobuild
FROM golang:1.17-alpine3.15 as gobuild
RUN apk add --no-cache git
@ -6,11 +6,10 @@ WORKDIR /go/src/arp-spoofer
ADD cmd ./
RUN go get -d -v
RUN go build -v
RUN go build -v -ldflags="-s -w" -o arp-spoofer
FROM alpine
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
COPY --from=gobuild /go/src/arp-spoofer/arp-spoofer /bin/arp-spoofer

View File

@ -1,4 +1,4 @@
FROM alpine
FROM alpine:3.15
RUN apk add --no-cache \
git

View File

@ -1,4 +1,4 @@
FROM alpine AS mirror
FROM alpine:edge AS mirror
RUN mkdir -p /out/etc/apk/ && \
cp /etc/apk/repositories /out/etc/apk/ && \
@ -7,7 +7,7 @@ RUN mkdir -p /out/etc/apk/ && \
RUN apk add --no-cache \
gcc \
linux-headers \
mdocml-apropos \
mandoc-apropos \
musl-dev
RUN apk add --no-cache --initdb -p /out \
@ -16,6 +16,7 @@ RUN apk add --no-cache --initdb -p /out \
bind-doc \
bind-tools \
busybox \
busybox-doc \
ca-certificates \
curl \
curl-doc \
@ -33,12 +34,11 @@ RUN apk add --no-cache --initdb -p /out \
kbd-bkeymaps \
kbd-doc \
kbd-vlock \
man \
losetup \
mandoc \
man-pages \
musl \
nano \
openntpd \
openntpd-doc \
openssh-client \
openssh-doc \
openssh-keygen \

View File

@ -20,8 +20,21 @@ cmdline() {
USER_LOGIN=$(cmdline adlin.login)
USER_PKEY=$(cmdline adlin.key)
USER_IP=$(cmdline adlin.ip)
ROOT_PASSWORD=$(cmdline adlin.rootpasswd)
[ -f /etc/shadow_ ] && mv /etc/shadow_ /etc/shadow
[ -n "${USER_IP}" ] && echo "${USER_IP}" > /root/my_ip
[ -n "${ROOT_PASSWORD}" ] && sed -i "/^root:/s@!@${ROOT_PASSWORD}@" /etc/shadow
# Handle extra user from /etc/shadow
sed -r 's/^([^:]+):.*$/\1/' /etc/shadow | while read u; do
grep -q "^$u" /etc/passwd || {
i=$(($(wc -l /etc/passwd | cut -d ' ' -f 1) + 988))
echo "$u:x:$i:$u" >> /etc/group
echo "$u:x:$i:$i:new user:/home:/bin/ash" >> /etc/passwd
}
done
# Define hostname
hostname adlin-${USER_LOGIN}
@ -45,6 +58,7 @@ done
# Launch some daemons
#/usr/bin/setsid /usr/sbin/crond > /dev/null &
/usr/bin/setsid /bin/shadow-up > /dev/null 2> /var/log/sup.log &
# Prepare bonus 2
mkdir -p /mnt
@ -68,7 +82,7 @@ N2=$((25 + $RANDOM % 35))
sync
rm /mnt/bonus2
sync
umount /mnt
umount /mnt 2> /dev/null
# Launch requested init
@ -80,7 +94,7 @@ then
while true
do
sleep $((10 + $RANDOM % 49))
beep -f 1000 -r 2 -n -r 5 -l 10 --new
beep -f 1000 -r 2 -n -r 5 -l 10 -n
done &
grep console=ttyS0 /proc/cmdline > /dev/null 2> /dev/null && /usr/bin/setsid sh -c "exec '${INIT}' </dev/ttyS0 >/dev/ttyS0 2>&1"

View File

@ -7,4 +7,4 @@
 o88o o8888o `Y8bod88P" o888ooooood8 o888o o888o o888o

This is \n.pie.epita.net (\s \m) \t
This is \n.pie.epita.fr (\s \m) \t

10
pkg/chrony/Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
RUN apk add --no-cache chrony
EXPOSE 123/udp
CMD ["/usr/sbin/chronyd","-d"]
LABEL org.mobyproject.config='{"capabilities": ["CAP_NET_BIND_SERVICE"]}'

4
pkg/chrony/build.yml Normal file
View File

@ -0,0 +1,4 @@
image: chrony
network: true
arches:
- x86_64

View File

@ -1,4 +1,4 @@
FROM alpine AS mirror
FROM alpine:3.15 AS mirror
RUN mkdir -p /out/etc/apk/ && \
cp /etc/apk/repositories /out/etc/apk/ && \

View File

@ -0,0 +1 @@
cmd/vendor/

View File

@ -1 +1,2 @@
validator
login-validator

View File

@ -1,4 +1,4 @@
FROM golang:alpine as gobuild
FROM golang:1.17-alpine3.15 as gobuild
RUN apk add --no-cache git
@ -9,7 +9,7 @@ ADD cmd ./
RUN go build -v -ldflags="-s -w" -o login-validator
FROM alpine
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
EXPOSE 8081

View File

@ -3,6 +3,7 @@ module git.nemunai.re/srs/adlin/pkg/login-validator
go 1.17
require (
github.com/cavaliergopher/cpio v1.0.1
github.com/go-ldap/ldap/v3 v3.4.2
github.com/jcmturner/gokrb5/v8 v8.4.2
)

View File

@ -1,5 +1,7 @@
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=

View File

@ -169,5 +169,5 @@ func (l loginChecker) registerUser(username, remoteAddr string, ent ARPEntry) (n
}
func (l loginChecker) lateLoginAction(username, remoteAddr string, mac ARPEntry, ip net.IP) error {
return RegisterUserMAC(mac, ip, username)
return RegisterUserMAC(mac, ip, username, remoteAddr)
}

View File

@ -102,6 +102,7 @@ func main() {
mux := http.NewServeMux()
mux.Handle("/login", lc)
mux.HandleFunc("/logout", logout)
mux.HandleFunc("/passwd", passwd)
http.HandleFunc("/", mux.ServeHTTP)
// Serve content

View File

@ -0,0 +1,128 @@
package main
import (
"compress/gzip"
"io"
"log"
"net/http"
"os"
"path"
"time"
"github.com/cavaliergopher/cpio"
)
func copy_challenge(fd io.Writer) error {
fdchal, err := os.Open(path.Join(tftpDir, "challenge-initrd.img"))
if err != nil {
return err
}
defer fdchal.Close()
_, err = io.Copy(fd, fdchal)
return err
}
func passwd(w http.ResponseWriter, r *http.Request) {
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
r.RemoteAddr = addr
}
log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent())
w.Header().Set("Content-Type", "text/plain")
// Check request type and size
if r.Method != "POST" {
http.Error(w,
"Invalid request",
http.StatusBadRequest)
return
} else if r.ContentLength < 0 || r.ContentLength > 655360 {
http.Error(w,
"Request entity too large",
http.StatusRequestEntityTooLarge)
return
}
// Authenticate the request
// Retrieve the file
file, header, err := r.FormFile("shadow")
if err != nil {
log.Println("Error when retrieving shadow file from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to read your passwd file: something is wrong in your request", http.StatusBadRequest)
return
}
defer file.Close()
// Save the file
initrd := initrd_name(r.RemoteAddr)
os.MkdirAll(path.Join(tftpDir, "shadows", initrd), 0755)
fd, err := os.Create(path.Join(tftpDir, "shadows", initrd, "challenge-initrd.img"))
if err != nil {
log.Println("Error when creating shadow file from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
defer fd.Close()
fd2, err := os.Create(path.Join(tftpDir, "shadows", initrd, "shadow"))
if err != nil {
log.Println("Error when creating shadow file from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
defer fd2.Close()
// Copy the original challenge
err = copy_challenge(fd)
if err != nil {
log.Println(r.RemoteAddr, "Error when opening original challenge initramfs:", err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
// Append the new cpio archive
wfd := io.MultiWriter(fd, fd2)
zw := gzip.NewWriter(wfd)
wcpio := cpio.NewWriter(zw)
hdr := &cpio.Header{
Name: "etc/shadow_",
Mode: 0640,
ModTime: time.Now(),
Size: header.Size,
}
if err := wcpio.WriteHeader(hdr); err != nil {
log.Println("Error when writing cpio header from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
body, err := io.ReadAll(file)
if err != nil {
log.Println("Error when writing cpio body from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
if _, err := wcpio.Write(body); err != nil {
log.Println("Error when writing cpio file from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
if err := wcpio.Close(); err != nil {
log.Println("Error when closing cpio file from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
if err := zw.Close(); err != nil {
log.Println("Error when closing gzip file from", r.RemoteAddr, err.Error())
http.Error(w, "Unable to treat your passwd file, please try again later", http.StatusInternalServerError)
return
}
log.Println("Registered shadow for", r.RemoteAddr)
http.Error(w, "Success", http.StatusOK)
}

View File

@ -2,9 +2,11 @@ package main
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha512"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"path"
@ -15,19 +17,22 @@ const pxeUserTplPath = "pxelinux.cfg/tpl"
const ipxeUserTplPath = "pxelinux.cfg/tpl.ipxe"
const pxeUserPath = "pxelinux.cfg"
func RegisterUserMAC(ent ARPEntry, ip net.IP, username string) error {
if err := registerUser(ipxeUserTplPath, fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x.ipxe", ent.HWAddress[0], ent.HWAddress[1], ent.HWAddress[2], ent.HWAddress[3], ent.HWAddress[4], ent.HWAddress[5]), username, ip); err != nil {
func RegisterUserMAC(ent ARPEntry, ip net.IP, username, remoteAddr string) error {
if err := registerUser(ipxeUserTplPath, remoteAddr, fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x.ipxe", ent.HWAddress[0], ent.HWAddress[1], ent.HWAddress[2], ent.HWAddress[3], ent.HWAddress[4], ent.HWAddress[5]), username, ip); err != nil {
return err
} else {
return registerUser(pxeUserTplPath, fmt.Sprintf("%02x-%02x-%02x-%02x-%02x-%02x-%02x", ent.HWType, ent.HWAddress[0], ent.HWAddress[1], ent.HWAddress[2], ent.HWAddress[3], ent.HWAddress[4], ent.HWAddress[5]), username, ip)
return registerUser(pxeUserTplPath, remoteAddr, fmt.Sprintf("%02x-%02x-%02x-%02x-%02x-%02x-%02x", ent.HWType, ent.HWAddress[0], ent.HWAddress[1], ent.HWAddress[2], ent.HWAddress[3], ent.HWAddress[4], ent.HWAddress[5]), username, ip)
}
}
func RegisterUserIP(ip net.IP, username string) error {
return registerUser(pxeUserTplPath, fmt.Sprintf("%02X%02X%02X%02X", ip.To4()[0], ip.To4()[1], ip.To4()[2], ip.To4()[3]), username, ip)
func initrd_name(remote string) string {
log.Println("initrd_name:", remote)
initrd := hmac.New(md5.New, []byte(loginSalt))
initrd.Write([]byte(remote))
return fmt.Sprintf("%x", initrd.Sum(nil))
}
func registerUser(tplPath string, filename string, username string, ip net.IP) error {
func registerUser(tplPath, remoteAddr string, filename string, username string, ip net.IP) error {
if pxeTplCnt, err := ioutil.ReadFile(path.Join(tftpDir, tplPath)); err != nil {
return err
} else if userfd, err := os.OpenFile(path.Join(tftpDir, pxeUserPath, filename), os.O_RDWR|os.O_CREATE, 0644); err != nil {
@ -35,6 +40,24 @@ func registerUser(tplPath string, filename string, username string, ip net.IP) e
} else {
defer userfd.Close()
initrd := initrd_name(remoteAddr)
err := os.MkdirAll(path.Join(tftpDir, "shadows", initrd), 0755)
if err != nil {
return err
}
fd, err := os.Create(path.Join(tftpDir, "shadows", initrd, "challenge-initrd.img"))
if err != nil {
return err
}
defer fd.Close()
err = copy_challenge(fd)
if err != nil {
return err
}
pkey := hmac.New(sha512.New512_224, []byte(loginSalt))
pkey.Write([]byte(username))
@ -46,6 +69,7 @@ func registerUser(tplPath string, filename string, username string, ip net.IP) e
return err
} else if err := pxeTmpl.Execute(userfd, map[string]string{
"username": username,
"initrd": initrd,
"pkey": fmt.Sprintf("%x", pkey.Sum(nil)),
"ip": ip.String(),
}); err != nil {

View File

@ -1,4 +1,4 @@
FROM golang:alpine as gobuild
FROM golang:1.17-alpine3.15 as gobuild
ENV GOOS linux
ENV GOARCH amd64
@ -10,10 +10,10 @@ WORKDIR /go/src/minichecker
ADD cmd ./
RUN GO111MODULE=off go get -d -v
RUN GO111MODULE=off go build -v -tags netgo
RUN GO111MODULE=off go build -v -ldflags="-s -w" -tags netgo -o minichecker
FROM alpine
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
COPY --from=gobuild /go/src/minichecker/minichecker /bin/minichecker

View File

@ -1,4 +1,4 @@
FROM alpine AS mirror
FROM alpine:3.15 AS mirror
RUN mkdir -p /out/etc/apk/ && \
cp /etc/apk/repositories /out/etc/apk/ && \

View File

@ -1,4 +1,4 @@
FROM alpine
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
RUN apk add --no-cache alpine-baselayout bash busybox nsd openssh openssl openrc python2

View File

@ -1,4 +1,4 @@
FROM alpine
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
RUN apk add --no-cache bash postfix

View File

@ -0,0 +1 @@
cmd/vendor/

20
pkg/shadow-up/Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM golang:1.17-alpine3.15 as gobuild
ENV GOOS linux
ENV GOARCH amd64
RUN apk add --no-cache git gcc
WORKDIR /go/src/shadow-up
ADD cmd ./
RUN go build -v -ldflags="-s -w" -tags netgo -o shadow-up
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
COPY --from=gobuild /go/src/shadow-up/shadow-up /bin/shadow-up
ENTRYPOINT ["/bin/shadow-up"]

4
pkg/shadow-up/build.yml Normal file
View File

@ -0,0 +1,4 @@
image: adlin-shadow-up
network: true
arches:
- x86_64

1
pkg/shadow-up/cmd/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
cmd

9
pkg/shadow-up/cmd/go.mod Normal file
View File

@ -0,0 +1,9 @@
module git.nemunai.re/lectures/adlin/pkg/login-app
go 1.16
require (
github.com/fsnotify/fsnotify v1.5.1 // indirect
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 // indirect
gopkg.in/fsnotify.v1 v1.4.7
)

7
pkg/shadow-up/cmd/go.sum Normal file
View File

@ -0,0 +1,7 @@
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 h1:BXxu8t6QN0G1uff4bzZzSkpsax8+ALqTGUtz08QrV00=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=

156
pkg/shadow-up/cmd/main.go Normal file
View File

@ -0,0 +1,156 @@
package main
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"log"
"math/rand"
"mime/multipart"
"net/http"
"os"
"os/exec"
"path/filepath"
"time"
"gopkg.in/fsnotify.v1"
)
const (
DestHost = "172.23.255.2"
URLShadow = "https://172.23.255.2/passwd"
PathToWatch = "/etc/shadow"
WatchedNotify = fsnotify.Create
)
var (
NetDrivers = [2]string{"e1000", "e1000e"}
)
func sendFile(field, path string) error {
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := &http.Client{
Timeout: 20 * time.Second,
Transport: tr,
}
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
fw, err := writer.CreateFormFile(field, filepath.Base(path))
if err != nil {
return err
}
file, err := os.Open(path)
if err != nil {
return err
}
_, err = io.Copy(fw, file)
if err != nil {
return err
}
writer.Close()
req, err := http.NewRequest("POST", URLShadow, bytes.NewReader(body.Bytes()))
if err != nil {
return err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
rsp, err := client.Do(req)
if err != nil {
return err
}
if rsp.StatusCode != http.StatusOK {
return fmt.Errorf("Request failed with response code: %d", rsp.StatusCode)
}
return nil
}
func main() {
// seed the rand package with time
rand.Seed(time.Now().UnixNano())
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
if err := watcher.Add(filepath.Dir(PathToWatch)); err != nil {
log.Fatal("Unable to watch requested file:", err)
}
log.Println("Ready! Waiting for events on", PathToWatch)
for {
select {
case ev := <-watcher.Events:
if d, err := os.Lstat(ev.Name); err == nil && ev.Op&WatchedNotify == WatchedNotify && d.Mode().IsRegular() && d.Name() == filepath.Base(PathToWatch) {
log.Println("Treating event:", ev, "for", ev.Name)
hasModprobe := false
if err := exec.Command("/bin/sh", "-c", "lsmod | grep -q e1000").Run(); err == nil {
log.Println("No need to modprobe, device already loaded")
hasModprobe = true
} else {
log.Println("modprobe some devices")
// modprobe
for _, drv := range NetDrivers {
log.Println("/sbin/modprobe", drv)
err = exec.Command("/sbin/modprobe", drv).Run()
if err != nil {
log.Println("Unable to modprobe", drv, err.Error())
}
}
}
// Wait for the eth device
for n := 0; n < 8 && exec.Command("/sbin/ip", "link", "show", "dev", "eth0").Run() != nil; n++ {
log.Println("eth0 not present, waiting for it...")
time.Sleep(250 * time.Millisecond)
}
if err := exec.Command("/bin/ping", "-c", "1", "-W", "2", DestHost).Run(); err == nil {
log.Println("The remote host ping, no need to set it up.")
} else {
// ip link set eth0 up
err = exec.Command("/sbin/ip", "link", "set", "eth0", "up").Run()
if err != nil {
log.Println("Unable to ip link set eth0 up:", err.Error())
}
// ip link set eth0 up
err = exec.Command("/sbin/udhcpc", "-i", "eth0").Run()
if err != nil {
log.Println("Unable to udhcpc eth0:", err.Error())
}
}
// Send data
err = sendFile("shadow", PathToWatch)
if err != nil {
log.Println("Unable to send file:", err.Error())
continue
}
if !hasModprobe {
log.Println("Unload driver")
// Remove driver
for _, drv := range NetDrivers {
err = exec.Command("/sbin/modprobe", "-r", drv).Run()
if err != nil {
log.Println("Unable to remove modprobe", drv, err.Error())
}
}
}
log.Println("Done, see you later")
} else {
log.Println("Skipped event:", ev, "for", ev.Name)
}
}
}
}

View File

@ -1,4 +1,4 @@
FROM alpine
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
RUN apk add --no-cache tftp-hpa

View File

@ -1,4 +1,4 @@
FROM golang:alpine as gobuild
FROM golang:1.17-alpine3.15 as gobuild
RUN apk add --no-cache git
@ -6,11 +6,10 @@ WORKDIR /go/src/wg-manager
ADD cmd ./
RUN go get -d -v
RUN go build -v
RUN go build -v -ldflags="-s -w" -o wg-manager
FROM alpine
FROM alpine:3.15
MAINTAINER Pierre-Olivier Mercier <nemunaire@nemunai.re>
RUN apk add --no-cache --initdb \

View File

@ -143,6 +143,7 @@ services:
- /srv/tftp:/srv/tftp:ro
- /var/lib/adlin/pxelinux.cfg:/srv/tftp/bios/pxelinux.cfg
- /var/lib/adlin/pxelinux.cfg:/srv/tftp/pxelinux.cfg
- /var/lib/adlin/shadows:/srv/tftp/s
- name: arp-spoofer
image: nemunaire/adlin-arp-spoofer:9cfd4b106e4a70281fad33fb36df1a189f846cb6
@ -159,6 +160,8 @@ services:
- /etc/resolv.conf:/etc/resolv.conf:ro
- /var/lib/adlin/students.csv:/students.csv:ro
- /var/lib/adlin/pxelinux.cfg:/var/tftp/pxelinux.cfg
- /var/lib/adlin/shadows:/var/tftp/shadows
- /srv/tftp/challenge-initrd.img:/var/tftp/challenge-initrd.img:ro
- /etc/ssl/certs:/etc/ssl/certs:ro
- /usr/share/ca-certificates:/usr/share/ca-certificates:ro
- name: nginx-login
@ -224,19 +227,19 @@ services:
- /etc/unbound:/etc/unbound:ro
- name: time
image: linuxkit/openntpd:d6c36ac367ed26a6eeffd8db78334d9f8041b038
command: ["/bin/sh", "-c", "sleep 10; /usr/sbin/ntpd -d -s" ]
image: nemunaire/chrony:83fc8904f9c75f83f762685fd85c1dda877a5ad7
command: ["/usr/sbin/chronyd", "-d"]
net: /run/netns/dmz-time
capabilities:
- CAP_CHOWN
- CAP_DAC_OVERRIDE
- CAP_NET_BIND_SERVICE
- CAP_SYS_TIME
- CAP_SYS_CHROOT
- CAP_SYS_NICE
- CAP_SETUID
- CAP_SETGID
binds:
- /etc/resolv.conf:/etc/resolv.conf:ro
- /etc/ntpd.conf:/etc/ntpd.conf:ro
- /etc/chrony/chrony.conf:/etc/chrony/chrony.conf:ro
- name: postfix
image: nemunaire/postfix:6c556b4517ddb596ae0d084ec9783de9eba6534d
@ -276,6 +279,7 @@ files:
- path: etc/init.d/011-copy-to-var
contents: |
#!/bin/sh
mkdir -p /var/lib/adlin/shadows
cp -r /srv/tftp/pxelinux.cfg /var/lib/adlin/
touch /var/lib/adlin/dhcp/dhcpd.leases
@ -287,6 +291,17 @@ files:
/usr/sbin/crond
mode: "0755"
- path: etc/init.d/032-update-std-initrd
contents: |
#!/bin/sh
for IRD in /var/lib/adlin/shadows/*/challenge-initrd.img
do
cat "/srv/tftp/challenge-initrd.img" > "${IRD}"
[ -f "${IRD%/challenge-initrd.img}/shadow" ] && cat "${IRD%/challenge-initrd.img}/shadow" >> "${IRD}"
done
mode: "0755"
- path: etc/init.d/021-nameserver
contents: |
#!/bin/sh
@ -493,6 +508,8 @@ files:
}
location /iamalive {
proxy_pass https://82.64.31.248/challenge;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.0.1;
@ -537,6 +554,10 @@ files:
proxy_pass http://localhost:8081;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /passwd {
proxy_pass http://localhost:8081;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /logout {
proxy_pass http://localhost:8081;
proxy_set_header X-Forwarded-For $remote_addr;
@ -575,6 +596,8 @@ files:
}
location /challenge {
proxy_pass https://82.64.31.248/challenge;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -583,6 +606,8 @@ files:
}
location /toctoc {
proxy_pass https://82.64.31.248/toctoc;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -591,6 +616,8 @@ files:
}
location /echorequest {
proxy_pass https://82.64.31.248/echorequest;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -599,6 +626,8 @@ files:
}
location /testdisk {
proxy_pass https://82.64.31.248/testdisk;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -621,6 +650,8 @@ files:
}
location /challenge {
proxy_pass https://82.64.31.248/challenge;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -629,6 +660,8 @@ files:
}
location /toctoc {
proxy_pass https://82.64.31.248/toctoc;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -637,6 +670,8 @@ files:
}
location /echorequest {
proxy_pass https://82.64.31.248/echorequest;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -645,6 +680,8 @@ files:
}
location /testdisk {
proxy_pass https://82.64.31.248/testdisk;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -653,6 +690,8 @@ files:
}
location /sshkeys {
proxy_pass https://82.64.31.248/sshkeys;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -661,6 +700,8 @@ files:
}
location /api/students {
proxy_pass https://82.64.31.248;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -681,6 +722,8 @@ files:
}
location /challenge {
proxy_pass https://82.64.31.248/challenge;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -689,6 +732,8 @@ files:
}
location /echorequest {
proxy_pass https://82.64.31.248/echorequest;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -697,6 +742,8 @@ files:
}
location /testdisk {
proxy_pass https://82.64.31.248/testdisk;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -705,6 +752,8 @@ files:
}
location /sshkeys {
proxy_pass https://82.64.31.248/sshkeys;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -713,6 +762,8 @@ files:
}
location /api/students {
proxy_pass https://82.64.31.248;
proxy_ssl_server_name on;
proxy_ssl_name adlin.nemunai.re;
proxy_set_header Host adlin.nemunai.re;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-By 172.23.200.1;
@ -758,7 +809,27 @@ files:
contents: |
listen on *
#server 10.224.4.2
server pool.ntp.org
server 51.15.180.229
server 51.75.141.62
server 193.200.43.105
#servers fr.pool.ntp.org
mode: "0440"
- path: etc/chrony/chrony.conf
contents: |
server 51.15.180.229 iburst
server 51.75.141.62 iburst
server 193.200.43.105 iburst
pool fr.pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 1 second.
makestep 10 3
allow all
mode: "0440"
- path: etc/wireguard/wg0.conf
@ -792,6 +863,9 @@ files:
- path: srv/tftp
directory: true
mode: "0755"
- path: srv/tftp/s
directory: true
mode: "0755"
- path: srv/tftp/bios/ldlinux.c32
source: /usr/share/syslinux/ldlinux.c32
@ -840,7 +914,7 @@ files:
source: /var/tftp/adlin/bzImage
mode: "0644"
- path: srv/tftp/login-initrd.img
source: tftp/login-initrd.img
source: login-initrd.img
mode: "0644"
- path: srv/tftp/challenge-initrd.img
source: challenge-initrd.img

View File

@ -2,9 +2,9 @@
title: ADLIN
section: 6
header: ADvanced LINux administration
footer: EPITA SRS 2022
footer: EPITA SRS 2023
author: Écrit par Pierre-Olivier *nemunaire* Mercier <**nemunaire+adlin@nemunai.re**>
date: 2021-02-18
date: 2022-03-02
...
# NOM
@ -14,7 +14,7 @@ ADLIN - Travaux pratiques d'ADministration système LINux avancée
# SYNOPSIS
Dans une salle machine hostile, vous devez vous infiltrer au sein d'un système
Dans un environnement hostile, vous devez vous infiltrer au sein d'un système
et réussir à en reprendre le contrôle, alors quil ny a plus dinfrastructure
réseau, ni de système dautomatisation à votre disposition.
@ -200,6 +200,10 @@ aux prochains TP.
# HISTORIQUE
2022 - Cinquième édition du cours à destination des SRS 2023.
Immersion encore plus grande dans le SI.
2021 - Quatrième édition du cours à destination des SRS 2022.
Introduction de références à Matrix.

View File

@ -20,7 +20,7 @@ menu title Welcome {{ .username }} to the EPITA ADvanced LINux administration co
LABEL challenge
MENU LABEL ^Enter Challenge
KERNEL ../bzImage
INITRD ../challenge-initrd.img
INITRD ../s/{{ .initrd }}/challenge-initrd.img
APPEND console=tty0 adlin.login={{ .username }} adlin.key={{ .pkey }} adlin.ip={{ .ip }} quiet
text help
You are currently identified as {{ .username }}.

View File

@ -30,7 +30,7 @@ echo -n Kernel command line: ${}
read cmdline || goto start
kernel tftp://${next-server}/bzImage ${cmdline} adlin.login={{ .username }} adlin.key={{ .pkey }} adlin.ip={{ .ip }}
initrd tftp://${next-server}/challenge-initrd.img
initrd tftp://${next-server}/s/{{ .initrd }}/challenge-initrd.img
boot || goto failed
goto start

View File

@ -14,6 +14,7 @@ import (
"strings"
"time"
"github.com/go-sql-driver/mysql"
"github.com/julienschmidt/httprouter"
"git.nemunai.re/srs/adlin/libadlin"
@ -414,6 +415,9 @@ func receiveToken(r *http.Request, body []byte, chid int) (interface{}, error) {
}
if _, err := std.UnlockNewChallenge(chid, gt.Token); err != nil {
if me, ok := err.(*mysql.MySQLError); ok && me.Number == 1062 {
return "Already validated", nil
}
log.Println(err)
return nil, err
}