Compare commits
23 commits
060831d9c2
...
caaa30c747
Author | SHA1 | Date | |
---|---|---|---|
caaa30c747 | |||
405f3d6395 | |||
78c42e312c | |||
07e91271f4 | |||
456694c330 | |||
6d8f38d749 | |||
853477e54a | |||
4a27c9367e | |||
059e3cabec | |||
2bd6b3f18b | |||
8712e33c5a | |||
f47eb68505 | |||
764a790b11 | |||
5a4650f70e | |||
efab34d551 | |||
9cd237daff | |||
0af7437693 | |||
b6eb652929 | |||
8427a0adb8 | |||
d28b14fa50 | |||
6fcdc44952 | |||
54e1505db9 | |||
da1920673d |
37 changed files with 1280 additions and 364 deletions
6
Makefile
6
Makefile
|
@ -50,6 +50,10 @@ pkg/wg-manager: pkg/wg-manager/cmd/register.go pkg/wg-manager/cmd/main.go pkg/wg
|
||||||
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/login-validator pkg/monit pkg/postfix pkg/tftpd pkg/unbound pkg/wg-manager challenge-kernel login-initrd.img
|
||||||
linuxkit build -docker -format iso-bios $<
|
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
|
||||||
|
linuxkit pkg build -org nemunaire pkg/debian-tuto2/
|
||||||
|
touch pkg/debian-tuto2
|
||||||
|
|
||||||
tuto2-kernel: tuto2.yml
|
tuto2-kernel: tuto2.yml
|
||||||
linuxkit build -docker $<
|
linuxkit build -docker $<
|
||||||
tuto2-initrd.img: tuto2.yml
|
tuto2-initrd.img: tuto2.yml
|
||||||
|
@ -57,7 +61,7 @@ tuto2-initrd.img: tuto2.yml
|
||||||
tuto2-cmdline: tuto2.yml
|
tuto2-cmdline: tuto2.yml
|
||||||
linuxkit build -docker $<
|
linuxkit build -docker $<
|
||||||
|
|
||||||
tuto2.iso: tuto2.yml tuto2-kernel tuto2-initrd.img tuto2-cmdline
|
tuto2.iso: tuto2.yml pkg/debian-tuto2 tuto2-kernel tuto2-initrd.img tuto2-cmdline
|
||||||
linuxkit build -docker -format iso-bios $<
|
linuxkit build -docker -format iso-bios $<
|
||||||
|
|
||||||
tuto2-srs.iso: tuto2.iso pkg/debian-tuto2/isolinux.cfg
|
tuto2-srs.iso: tuto2.iso pkg/debian-tuto2/isolinux.cfg
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
@ -15,6 +17,10 @@ import (
|
||||||
"git.nemunai.re/lectures/adlin/libadlin"
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEFAULT_RESOLVER = "2a01:e0a:2b:2250::1"
|
||||||
|
)
|
||||||
|
|
||||||
var verbose = false
|
var verbose = false
|
||||||
|
|
||||||
// ICMP
|
// ICMP
|
||||||
|
@ -25,6 +31,7 @@ func check_ping(ip string, cb func(pkt *ping.Packet)) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer pinger.Stop()
|
||||||
|
|
||||||
pinger.Timeout = time.Second * 5
|
pinger.Timeout = time.Second * 5
|
||||||
pinger.Count = 1
|
pinger.Count = 1
|
||||||
|
@ -46,7 +53,7 @@ func get_GLUE(domain string) (aaaa net.IP, err error) {
|
||||||
m.SetEdns0(4096, true)
|
m.SetEdns0(4096, true)
|
||||||
|
|
||||||
var r *dns.Msg
|
var r *dns.Msg
|
||||||
r, _, err = client.Exchange(m, "[2a01:e0a:25a:9160::2]:53")
|
r, _, err = client.Exchange(m, "[2a01:e0a:2b:2250::b]:53")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -86,8 +93,10 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
||||||
err = errors.New("failed to get a valid answer")
|
err = errors.New("failed to get a valid answer")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r.Answer) > 0 {
|
for _, answer := range r.Answer {
|
||||||
aaaa = r.Answer[0].(*dns.AAAA).AAAA
|
if t, ok := answer.(*dns.AAAA); ok {
|
||||||
|
aaaa = t.AAAA
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -95,19 +104,33 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) {
|
||||||
|
|
||||||
// PORT 80
|
// PORT 80
|
||||||
|
|
||||||
func check_http(ip string) (err error) {
|
func check_http(ip, dn string) (err error) {
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req, errr := http.NewRequest("GET", fmt.Sprintf("http://[%s]/", ip), nil)
|
||||||
|
if errr != nil {
|
||||||
|
return errr
|
||||||
|
}
|
||||||
|
|
||||||
|
if dn != "" {
|
||||||
|
req.Header.Add("Host", strings.TrimSuffix(dn, "."))
|
||||||
|
}
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
resp, err = client.Get(fmt.Sprintf("http://[%s]/", ip))
|
resp, err = client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if dn != "" && resp.StatusCode >= 400 {
|
||||||
|
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = ioutil.ReadAll(resp.Body)
|
_, err = ioutil.ReadAll(resp.Body)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -116,18 +139,97 @@ func check_http(ip string) (err error) {
|
||||||
|
|
||||||
func check_https(domain, ip string) (err error) {
|
func check_https(domain, ip string) (err error) {
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
resp, err = http.Get(fmt.Sprintf("https://%s/", domain))
|
resp, err = http.Get(fmt.Sprintf("https://%s/", strings.TrimSuffix(domain, ".")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 300 && resp.StatusCode < 400 {
|
||||||
|
loc := resp.Header.Get("Location")
|
||||||
|
if loc != "" && strings.HasSuffix(dns.Fqdn(loc), domain) {
|
||||||
|
if dns.Fqdn(loc) == domain {
|
||||||
|
return fmt.Errorf("Redirection loop %s redirect to %s", domain, loc)
|
||||||
|
} else if err = check_https(dns.Fqdn(loc), ip); err != nil {
|
||||||
|
return fmt.Errorf("Error after following redirection to %s: %w", loc, err)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = ioutil.ReadAll(resp.Body)
|
_, err = ioutil.ReadAll(resp.Body)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MATRIX
|
||||||
|
|
||||||
|
type matrix_result struct {
|
||||||
|
WellKnownResult struct {
|
||||||
|
Result string `json:"result"`
|
||||||
|
}
|
||||||
|
DNSResult struct {
|
||||||
|
SRVError *struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConnectionReports map[string]struct {
|
||||||
|
Errors []string
|
||||||
|
}
|
||||||
|
ConnectionErrors map[string]struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
Version struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
FederationOK bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func check_matrix(domain string) (version string, err error) {
|
||||||
|
var resp *http.Response
|
||||||
|
resp, err = http.Get(fmt.Sprintf("https://federation-tester.adlin.nemunai.re/api/report?server_name=%s", strings.TrimSuffix(domain, ".")))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
var federationTest matrix_result
|
||||||
|
if federationTest.FederationOK {
|
||||||
|
version = federationTest.Version.Name + " " + federationTest.Version.Version
|
||||||
|
return version, nil
|
||||||
|
} else if err = json.NewDecoder(resp.Body).Decode(&federationTest); err != nil {
|
||||||
|
log.Printf("Error in chech_matrix, when decoding json:", err.Error())
|
||||||
|
return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", domain)
|
||||||
|
} else if federationTest.DNSResult.SRVError != nil && federationTest.WellKnownResult.Result != "" {
|
||||||
|
return "", fmt.Errorf("%s OR %s", federationTest.DNSResult.SRVError.Message, federationTest.WellKnownResult.Result)
|
||||||
|
} else if len(federationTest.ConnectionErrors) > 0 {
|
||||||
|
var msg strings.Builder
|
||||||
|
for srv, cerr := range federationTest.ConnectionErrors {
|
||||||
|
if msg.Len() > 0 {
|
||||||
|
msg.WriteString("; ")
|
||||||
|
}
|
||||||
|
msg.WriteString(srv)
|
||||||
|
msg.WriteString(": ")
|
||||||
|
msg.WriteString(cerr.Message)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("Connection errors: %s", msg.String())
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("An unimplemented error occurs. Please report to nemunaire. But know that federation seems to be broken. Check https://federationtester.matrix.org/#%s", domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
|
|
||||||
func minTunnelVersion(std adlin.Student) (int, error) {
|
func minTunnelVersion(std *adlin.Student, suffixip int) (int, error) {
|
||||||
tunnels, err := std.GetTunnelTokens()
|
tunnels, err := std.GetTunnelTokens()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -139,7 +241,7 @@ func minTunnelVersion(std adlin.Student) (int, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if tunnel.Dump != nil && tunnel.Version < minversion {
|
if tunnel.Dump != nil && tunnel.Version < minversion && suffixip == tunnel.SuffixIP {
|
||||||
minversion = tunnel.Version
|
minversion = tunnel.Version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,110 +255,175 @@ func studentsChecker() {
|
||||||
log.Println("Unable to check students:", err)
|
log.Println("Unable to check students:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("Checking students...")
|
check_matrix_for := (time.Now().Second()/30)*5 + time.Now().Minute()%5
|
||||||
|
|
||||||
for _, s := range students {
|
log.Printf("Checking students... (std_matrix%%10=%d)\n", check_matrix_for)
|
||||||
|
|
||||||
|
for istd, s := range students {
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
// Check ping
|
// Check ping
|
||||||
std := s
|
std := s
|
||||||
stdIP := adlin.StudentIP(std.Id).String() + "1"
|
tuns, err := std.GetActivesTunnels()
|
||||||
go check_ping(stdIP, func(pkt *ping.Packet) {
|
if err != nil {
|
||||||
tunnel_version, err := minTunnelVersion(std)
|
continue
|
||||||
if verbose {
|
}
|
||||||
log.Printf("%s PONG; version=%d (%v)\n", std.Login, tunnel_version, err)
|
|
||||||
}
|
|
||||||
std.OnPong(true)
|
|
||||||
|
|
||||||
if tunnel_version == 2147483647 || tunnel_version == 0 {
|
|
||||||
log.Printf("%s unknown tunnel version: %d skipping tests (%v)", std.Login, tunnel_version, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsIP := stdIP
|
|
||||||
// Is GLUE defined?
|
|
||||||
if glueIP, err := get_GLUE(std.MyDelegatedDomain()); glueIP != nil {
|
|
||||||
dnsIP = glueIP.String()
|
|
||||||
|
|
||||||
|
for _, tun := range tuns {
|
||||||
|
stdIP := tun.GetStudentIP()
|
||||||
|
go check_ping(stdIP, func(pkt *ping.Packet) {
|
||||||
|
tunnel_version, err := minTunnelVersion(std, tun.SuffixIP)
|
||||||
if verbose {
|
if verbose {
|
||||||
log.Printf("%s has defined GLUE: %s\n", std.Login, dnsIP)
|
log.Printf("%s PONG (on %x); version=%d (%v)\n", std.Login, tun.SuffixIP, tunnel_version, err)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
std.OnPong(true)
|
||||||
log.Printf("%s and GLUE: %s\n", std.Login, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check DNS
|
if tunnel_version == 2147483647 || tunnel_version == 0 {
|
||||||
if addr, err := check_dns(std.MyDelegatedDomain(), dnsIP); err == nil {
|
log.Printf("%s unknown tunnel version: %d skipping tests (%v)", std.Login, tunnel_version, err)
|
||||||
if verbose {
|
return
|
||||||
log.Printf("%s just unlocked DNS challenge\n", std.Login)
|
|
||||||
}
|
}
|
||||||
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+2, ""); err != nil {
|
|
||||||
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+2, ""); err != nil {
|
dnsIP := stdIP
|
||||||
|
var glueErr error
|
||||||
|
// Is GLUE defined?
|
||||||
|
if glueIP, err := get_GLUE(std.MyDelegatedDomain()); glueIP != nil {
|
||||||
|
dnsIP = glueIP.String()
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s has defined GLUE: %s\n", std.Login, dnsIP)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
log.Printf("%s and GLUE: %s\n", std.Login, err)
|
||||||
|
glueErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check DNS
|
||||||
|
if addr, err := check_dns(std.MyDelegatedDomain(), dnsIP); err == nil {
|
||||||
|
if addr == nil {
|
||||||
|
dnsAt := " at " + dnsIP
|
||||||
|
if glueErr != nil {
|
||||||
|
dnsAt = " + there is a problem with the GLUE record: " + glueErr.Error()
|
||||||
|
}
|
||||||
|
if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, fmt.Errorf("%s: empty response from the server%s", std.MyDelegatedDomain(), dnsAt)); errreg != nil {
|
||||||
|
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s just unlocked DNS challenge\n", std.Login)
|
||||||
|
}
|
||||||
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+3, addr.String()); err != nil {
|
||||||
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check HTTP with DNS
|
||||||
|
if glueErr != nil {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+4, fmt.Errorf("Unable to perform the test due to GLUE problem: %w", err))
|
||||||
|
} else if err := check_http(addr.String(), std.MyDelegatedDomain()); err == nil {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
|
||||||
|
}
|
||||||
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+4, ""); err != nil {
|
||||||
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+4, err)
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check HTTPs with DNS
|
||||||
|
if glueErr != nil {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+5, fmt.Errorf("Unable to perform the test due to GLUE problem: %w", err))
|
||||||
|
} else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
|
||||||
|
}
|
||||||
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+5, ""); err != nil {
|
||||||
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+5, err)
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Matrix (only if GLUE Ok and)
|
||||||
|
if glueErr == nil && istd%10 == check_matrix_for {
|
||||||
|
if v, err := check_matrix(std.MyDelegatedDomain()); err == nil {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s just unlocked Matrix challenge\n", std.Login)
|
||||||
|
}
|
||||||
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+6, v); err != nil {
|
||||||
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+6, err)
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and Matrix: %s\n", std.Login, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, err); errreg != nil {
|
||||||
|
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and DNS: %s\n", std.Login, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check HTTP without DNS
|
||||||
|
if err := check_http(stdIP, ""); err == nil {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s just unlocked HTTP IP (without DNS) challenge\n", std.Login)
|
||||||
|
}
|
||||||
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+0, ""); err != nil {
|
||||||
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+0, err)
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and HTTP IP (without DNS): %s\n", std.Login, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check HTTP with DNS
|
// Check DNS for association
|
||||||
if addr == nil {
|
if addr, err := check_dns(std.MyAssociatedDomain(), DEFAULT_RESOLVER); err == nil {
|
||||||
log.Printf("%s and HTTP (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String())
|
// Check HTTP on delegated domain
|
||||||
} else if err := check_http(addr.String()); err == nil {
|
if err := check_http(addr.String(), std.MyAssociatedDomain()); err == nil {
|
||||||
if verbose {
|
if verbose {
|
||||||
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
|
log.Printf("%s just unlocked HTTP (without DNS) challenge\n", std.Login)
|
||||||
}
|
}
|
||||||
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil {
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+1, ""); err != nil {
|
||||||
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil {
|
|
||||||
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+1, err)
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check HTTPs without DNS
|
||||||
|
if err := check_https(std.MyAssociatedDomain(), stdIP); err == nil {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
|
||||||
|
}
|
||||||
|
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+2, ""); err != nil {
|
||||||
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.RegisterChallengeError(100*(tunnel_version-1)+2, err)
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if verbose {
|
|
||||||
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check HTTPs with DNS
|
return
|
||||||
if addr == nil {
|
})
|
||||||
log.Printf("%s and HTTPS (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String())
|
}
|
||||||
} else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil {
|
|
||||||
if verbose {
|
|
||||||
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
|
|
||||||
}
|
|
||||||
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil {
|
|
||||||
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil {
|
|
||||||
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if verbose {
|
|
||||||
log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Check HTTP without DNS
|
|
||||||
if err := check_http(stdIP); err == nil {
|
|
||||||
if verbose {
|
|
||||||
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
|
|
||||||
}
|
|
||||||
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil {
|
|
||||||
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil {
|
|
||||||
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if verbose {
|
|
||||||
log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check HTTPs without DNS
|
|
||||||
if err := check_https(std.MyAssociatedDomain(), stdIP); err == nil {
|
|
||||||
if verbose {
|
|
||||||
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
|
|
||||||
}
|
|
||||||
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil {
|
|
||||||
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil {
|
|
||||||
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if verbose {
|
|
||||||
log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,9 @@ func DBCreate() (err error) {
|
||||||
CREATE TABLE IF NOT EXISTS students(
|
CREATE TABLE IF NOT EXISTS students(
|
||||||
id_student INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
id_student INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
login VARCHAR(255) NOT NULL UNIQUE,
|
login VARCHAR(255) NOT NULL UNIQUE,
|
||||||
time TIMESTAMP NOT NULL
|
time TIMESTAMP NOT NULL,
|
||||||
|
associatedDomain VARCHAR(255) UNIQUE,
|
||||||
|
delegatedDomain VARCHAR(255) UNIQUE
|
||||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||||
`); err != nil {
|
`); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -93,6 +95,19 @@ CREATE TABLE IF NOT EXISTS student_challenges(
|
||||||
CONSTRAINT token_found UNIQUE (id_student,challenge),
|
CONSTRAINT token_found UNIQUE (id_student,challenge),
|
||||||
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||||
|
`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS student_challenge_errors(
|
||||||
|
id_st INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
id_student INTEGER NOT NULL,
|
||||||
|
challenge INTEGER NOT NULL,
|
||||||
|
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
error VARCHAR(255) NOT NULL,
|
||||||
|
CONSTRAINT token_found UNIQUE (id_student,challenge),
|
||||||
|
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||||
|
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||||
`); err != nil {
|
`); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,22 +6,39 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AssociatedDomainSuffix = "adlin2021.p0m.fr."
|
AssociatedDomainSuffix = "adlin2022.p0m.fr."
|
||||||
DelegatedDomainSuffix = "srs.p0m.fr."
|
DelegatedDomainSuffix = "srs.p0m.fr."
|
||||||
)
|
)
|
||||||
|
|
||||||
func (student Student) MyDelegatedDomain() string {
|
func (student *Student) MyDelegatedDomain() string {
|
||||||
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix)
|
if student.DelegatedDomain != nil {
|
||||||
|
return *student.DelegatedDomain
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) MyAssociatedDomain() string {
|
func (student *Student) DefaultAssociatedDomain() string {
|
||||||
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix)
|
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) GetAssociatedDomains() (ds []string) {
|
func (student *Student) MyAssociatedDomain() string {
|
||||||
|
if student.AssociatedDomain != nil {
|
||||||
|
return *student.AssociatedDomain
|
||||||
|
} else {
|
||||||
|
return student.DefaultAssociatedDomain()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (student *Student) GetAssociatedDomains() (ds []string) {
|
||||||
|
defdn := student.DefaultAssociatedDomain()
|
||||||
|
ds = append(ds, defdn)
|
||||||
|
|
||||||
studentDomain := student.MyAssociatedDomain()
|
studentDomain := student.MyAssociatedDomain()
|
||||||
|
|
||||||
ds = append(ds, studentDomain)
|
if defdn != studentDomain {
|
||||||
|
ds = append(ds, studentDomain)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,14 @@ type Pong struct {
|
||||||
State bool
|
State bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) LastPongs() (pongs []Pong, err error) {
|
func (s *Student) LastPongs() (pongs []*Pong, err error) {
|
||||||
if rows, errr := DBQuery("SELECT time, state FROM student_pong WHERE id_student = ? ORDER BY time DESC", s.Id); errr != nil {
|
if rows, errr := DBQuery("SELECT time, state FROM student_pong WHERE id_student = ? ORDER BY time DESC", s.Id); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var p Pong
|
p := &Pong{}
|
||||||
if err = rows.Scan(&p.Date, &p.State); err != nil {
|
if err = rows.Scan(&p.Date, &p.State); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,40 +11,41 @@ type Session struct {
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSession(id []byte) (s Session, err error) {
|
func GetSession(id []byte) (s *Session, err error) {
|
||||||
|
s = new(Session)
|
||||||
err = DBQueryRow("SELECT id_session, id_student, time FROM student_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdStudent, &s.Time)
|
err = DBQueryRow("SELECT id_session, id_student, time FROM student_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdStudent, &s.Time)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSession() (Session, error) {
|
func NewSession() (*Session, error) {
|
||||||
session_id := make([]byte, 255)
|
session_id := make([]byte, 255)
|
||||||
if _, err := rand.Read(session_id); err != nil {
|
if _, err := rand.Read(session_id); err != nil {
|
||||||
return Session{}, err
|
return nil, err
|
||||||
} else if _, err := DBExec("INSERT INTO student_sessions (id_session, time) VALUES (?, ?)", session_id, time.Now()); err != nil {
|
} else if _, err := DBExec("INSERT INTO student_sessions (id_session, time) VALUES (?, ?)", session_id, time.Now()); err != nil {
|
||||||
return Session{}, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return Session{session_id, nil, time.Now()}, nil
|
return &Session{session_id, nil, time.Now()}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) NewSession() (Session, error) {
|
func (student *Student) NewSession() (*Session, error) {
|
||||||
session_id := make([]byte, 255)
|
session_id := make([]byte, 255)
|
||||||
if _, err := rand.Read(session_id); err != nil {
|
if _, err := rand.Read(session_id); err != nil {
|
||||||
return Session{}, err
|
return nil, err
|
||||||
} else if _, err := DBExec("INSERT INTO student_sessions (id_session, id_student, time) VALUES (?, ?, ?)", session_id, student.Id, time.Now()); err != nil {
|
} else if _, err := DBExec("INSERT INTO student_sessions (id_session, id_student, time) VALUES (?, ?, ?)", session_id, student.Id, time.Now()); err != nil {
|
||||||
return Session{}, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return Session{session_id, &student.Id, time.Now()}, nil
|
return &Session{session_id, &student.Id, time.Now()}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Session) SetStudent(student Student) (Session, error) {
|
func (s *Session) SetStudent(student *Student) (*Session, error) {
|
||||||
s.IdStudent = &student.Id
|
s.IdStudent = &student.Id
|
||||||
_, err := s.Update()
|
_, err := s.Update()
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Session) Update() (int64, error) {
|
func (s *Session) Update() (int64, error) {
|
||||||
if res, err := DBExec("UPDATE student_sessions SET id_student = ?, time = ? WHERE id_session = ?", s.IdStudent, s.Time, s.Id); err != nil {
|
if res, err := DBExec("UPDATE student_sessions SET id_student = ?, time = ? WHERE id_session = ?", s.IdStudent, s.Time, s.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
@ -54,7 +55,7 @@ func (s Session) Update() (int64, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Session) Delete() (int64, error) {
|
func (s *Session) Delete() (int64, error) {
|
||||||
if res, err := DBExec("DELETE FROM student_sessions WHERE id_session = ?", s.Id); err != nil {
|
if res, err := DBExec("DELETE FROM student_sessions WHERE id_session = ?", s.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
|
|
@ -16,14 +16,14 @@ type StudentKey struct {
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStudentKeys() (keys []StudentKey, err error) {
|
func GetStudentKeys() (keys []*StudentKey, err error) {
|
||||||
if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys"); errr != nil {
|
if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys"); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var k StudentKey
|
k := &StudentKey{}
|
||||||
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -37,14 +37,14 @@ func GetStudentKeys() (keys []StudentKey, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) GetKeys() (keys []StudentKey, err error) {
|
func (s *Student) GetKeys() (keys []*StudentKey, err error) {
|
||||||
if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_student = ?", s.Id); errr != nil {
|
if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_student = ?", s.Id); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var k StudentKey
|
k := &StudentKey{}
|
||||||
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,13 @@ func (s Student) GetKeys() (keys []StudentKey, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStudentKey(id int) (k StudentKey, err error) {
|
func getStudentKey(id int) (k *StudentKey, err error) {
|
||||||
|
k = new(StudentKey)
|
||||||
err = DBQueryRow("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_key=?", id).Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time)
|
err = DBQueryRow("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_key=?", id).Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) NewKey(key string) (k StudentKey, err error) {
|
func (s *Student) NewKey(key string) (k *StudentKey, err error) {
|
||||||
// Check key before importing it
|
// Check key before importing it
|
||||||
cmd := exec.Command("ssh-keygen", "-l", "-f", "-")
|
cmd := exec.Command("ssh-keygen", "-l", "-f", "-")
|
||||||
cmd.Stdin = strings.NewReader(key)
|
cmd.Stdin = strings.NewReader(key)
|
||||||
|
@ -101,20 +102,20 @@ func (s Student) NewKey(key string) (k StudentKey, err error) {
|
||||||
key = keyf[0] + " " + keyf[1]
|
key = keyf[0] + " " + keyf[1]
|
||||||
|
|
||||||
if res, err := DBExec("INSERT INTO student_keys (id_student, sshkey, time) VALUES (?, ?, ?)", s.Id, key, time.Now()); err != nil {
|
if res, err := DBExec("INSERT INTO student_keys (id_student, sshkey, time) VALUES (?, ?, ?)", s.Id, key, time.Now()); err != nil {
|
||||||
return StudentKey{}, err
|
return nil, err
|
||||||
} else if kid, err := res.LastInsertId(); err != nil {
|
} else if kid, err := res.LastInsertId(); err != nil {
|
||||||
return StudentKey{}, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
s.UnlockNewChallenge(11, "")
|
s.UnlockNewChallenge(11, "")
|
||||||
return StudentKey{kid, s.Id, key, time.Now()}, nil
|
return &StudentKey{kid, s.Id, key, time.Now()}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k StudentKey) GetStudent() (Student, error) {
|
func (k *StudentKey) GetStudent() (*Student, error) {
|
||||||
return GetStudent(int(k.IdStudent))
|
return GetStudent(int(k.IdStudent))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k StudentKey) Update() (int64, error) {
|
func (k *StudentKey) Update() (int64, error) {
|
||||||
if res, err := DBExec("UPDATE student_keys SET id_student = ?, sshkey = ?, time = ? WHERE id_key = ?", k.IdStudent, k.Key, k.Time, k.Id); err != nil {
|
if res, err := DBExec("UPDATE student_keys SET id_student = ?, sshkey = ?, time = ? WHERE id_key = ?", k.IdStudent, k.Key, k.Time, k.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
@ -124,7 +125,7 @@ func (k StudentKey) Update() (int64, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k StudentKey) Delete() (int64, error) {
|
func (k *StudentKey) Delete() (int64, error) {
|
||||||
if res, err := DBExec("DELETE FROM student_keys WHERE id_key = ?", k.Id); err != nil {
|
if res, err := DBExec("DELETE FROM student_keys WHERE id_key = ?", k.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
|
|
@ -3,26 +3,29 @@ package adlin
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Student struct {
|
type Student struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Login string `json:"login"`
|
Login string `json:"login"`
|
||||||
Time *time.Time `json:"time"`
|
Time *time.Time `json:"time"`
|
||||||
IP *string `json:"ip"`
|
IP *string `json:"ip"`
|
||||||
MAC *string `json:"mac"`
|
MAC *string `json:"mac"`
|
||||||
|
AssociatedDomain *string `json:"associated_domain,omitempty"`
|
||||||
|
DelegatedDomain *string `json:"delegated_domain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStudents() (students []Student, err error) {
|
func GetStudents() (students []*Student, err error) {
|
||||||
if rows, errr := DBQuery("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student GROUP BY id_student"); errr != nil {
|
if rows, errr := DBQuery("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student GROUP BY id_student"); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var s Student
|
s := &Student{}
|
||||||
if err = rows.Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC); err != nil {
|
if err = rows.Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
students = append(students, s)
|
students = append(students, s)
|
||||||
|
@ -35,13 +38,15 @@ func GetStudents() (students []Student, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStudent(id int) (s Student, err error) {
|
func GetStudent(id int) (s *Student, err error) {
|
||||||
err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE S.id_student=?", id).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC)
|
s = new(Student)
|
||||||
|
err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE S.id_student=?", id).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStudentByLogin(login string) (s Student, err error) {
|
func GetStudentByLogin(login string) (s *Student, err error) {
|
||||||
err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC)
|
s = new(Student)
|
||||||
|
err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,23 +56,23 @@ func StudentExists(login string) bool {
|
||||||
return err == nil && z == 1
|
return err == nil && z == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStudent(login string) (Student, error) {
|
func NewStudent(login string) (*Student, error) {
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
if res, err := DBExec("INSERT INTO students (login, time) VALUES (?, ?)", login, t); err != nil {
|
if res, err := DBExec("INSERT INTO students (login, time) VALUES (?, ?)", login, t); err != nil {
|
||||||
return Student{}, err
|
return nil, err
|
||||||
} else if sid, err := res.LastInsertId(); err != nil {
|
} else if sid, err := res.LastInsertId(); err != nil {
|
||||||
return Student{}, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return Student{sid, login, &t, nil, nil}, nil
|
return &Student{sid, login, &t, nil, nil, nil, nil}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) GetPKey() []byte {
|
func (s *Student) GetPKey() []byte {
|
||||||
return hmac.New(sha512.New512_224, []byte(SharedSecret)).Sum([]byte(s.Login))
|
return hmac.New(sha512.New512_224, []byte(SharedSecret)).Sum([]byte(s.Login))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) Update() (int64, error) {
|
func (s *Student) Update() (int64, error) {
|
||||||
if res, err := DBExec("UPDATE students SET login = ?, time = ? WHERE id_student = ?", s.Login, s.Time, s.Id); err != nil {
|
if res, err := DBExec("UPDATE students SET login = ?, time = ?, associatedDomain = ?, delegatedDomain = ? WHERE id_student = ?", s.Login, s.Time, s.AssociatedDomain, s.DelegatedDomain, s.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -76,7 +81,7 @@ func (s Student) Update() (int64, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) Delete() (int64, error) {
|
func (s *Student) Delete() (int64, error) {
|
||||||
if res, err := DBExec("DELETE FROM students WHERE id_student = ?", s.Id); err != nil {
|
if res, err := DBExec("DELETE FROM students WHERE id_student = ?", s.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
@ -100,18 +105,20 @@ type UnlockedChallenge struct {
|
||||||
Id int64 `json:"id,omitempty"`
|
Id int64 `json:"id,omitempty"`
|
||||||
IdStudent int64 `json:"id_student"`
|
IdStudent int64 `json:"id_student"`
|
||||||
Challenge int `json:"challenge,omitempty"`
|
Challenge int `json:"challenge,omitempty"`
|
||||||
Time time.Time `json:"time"`
|
Time *time.Time `json:"time,omitempty"`
|
||||||
Value interface{} `json:"value,omitempty"`
|
Value interface{} `json:"value,omitempty"`
|
||||||
|
LastCheck *time.Time `json:"last_check,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) GetStates() (ucs []UnlockedChallenge, err error) {
|
func (s *Student) GetStates() (ucs []*UnlockedChallenge, err error) {
|
||||||
if rows, errr := DBQuery("SELECT id_st, challenge, time FROM student_challenges WHERE id_student = ?", s.Id); errr != nil {
|
if rows, errr := DBQuery("SELECT id_st, challenge, time FROM student_challenges WHERE id_student = ?", s.Id); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var u UnlockedChallenge
|
u := &UnlockedChallenge{}
|
||||||
u.IdStudent = s.Id
|
u.IdStudent = s.Id
|
||||||
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time); err != nil {
|
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -126,14 +133,36 @@ func (s Student) GetStates() (ucs []UnlockedChallenge, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) GetStatesByChallenge() (ucs []UnlockedChallenge, err error) {
|
func (s *Student) GetChallengeErrors() (ucs []*ErroredChallenge, err error) {
|
||||||
|
if rows, errr := DBQuery("SELECT id_st, challenge, time, error FROM student_challenge_errors WHERE id_student = ?", s.Id); errr != nil {
|
||||||
|
return nil, errr
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
u := &ErroredChallenge{}
|
||||||
|
u.IdStudent = s.Id
|
||||||
|
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Error); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ucs = append(ucs, u)
|
||||||
|
}
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Student) GetStatesByChallenge() (ucs []*UnlockedChallenge, err error) {
|
||||||
if rows, errr := DBQuery("SELECT id_st, challenge, MIN(time), value FROM student_challenges WHERE id_student = ? GROUP BY challenge, id_student", s.Id); errr != nil {
|
if rows, errr := DBQuery("SELECT id_st, challenge, MIN(time), value FROM student_challenges WHERE id_student = ? GROUP BY challenge, id_student", s.Id); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var u UnlockedChallenge
|
u := &UnlockedChallenge{}
|
||||||
u.IdStudent = s.Id
|
u.IdStudent = s.Id
|
||||||
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Value); err != nil {
|
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Value); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -148,25 +177,57 @@ func (s Student) GetStatesByChallenge() (ucs []UnlockedChallenge, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) UnlockNewChallenge(challenge int, value string) (UnlockedChallenge, error) {
|
func (s *Student) UnlockChallenge(challenge int, value string) (uc *UnlockedChallenge, err error) {
|
||||||
|
if uc, err = s.UnlockNewChallenge(challenge, value); err != nil {
|
||||||
|
if uc, err = s.UpdateUnlockedChallenge(challenge, value); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.RegisterChallengeError(challenge, fmt.Errorf("OK"))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Student) UnlockNewChallenge(challenge int, value string) (*UnlockedChallenge, error) {
|
||||||
if res, err := DBExec("INSERT INTO student_challenges (id_student, challenge, time, value) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), value); err != nil {
|
if res, err := DBExec("INSERT INTO student_challenges (id_student, challenge, time, value) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), value); err != nil {
|
||||||
return UnlockedChallenge{}, err
|
return nil, err
|
||||||
} else if utid, err := res.LastInsertId(); err != nil {
|
} else if utid, err := res.LastInsertId(); err != nil {
|
||||||
return UnlockedChallenge{}, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return UnlockedChallenge{utid, s.Id, challenge, time.Now(), value}, err
|
now := time.Now()
|
||||||
|
return &UnlockedChallenge{utid, s.Id, challenge, &now, value, nil, ""}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) UpdateUnlockedChallenge(challenge int, value string) (UnlockedChallenge, error) {
|
func (s *Student) UpdateUnlockedChallenge(challenge int, value string) (*UnlockedChallenge, error) {
|
||||||
if _, err := DBExec("UPDATE student_challenges SET time = ?, value = ? WHERE id_student = ? AND challenge = ?", time.Now(), value, s.Id, challenge); err != nil {
|
if _, err := DBExec("UPDATE student_challenges SET time = ?, value = ? WHERE id_student = ? AND challenge = ?", time.Now(), value, s.Id, challenge); err != nil {
|
||||||
return UnlockedChallenge{}, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return UnlockedChallenge{0, s.Id, challenge, time.Now(), value}, err
|
now := time.Now()
|
||||||
|
return &UnlockedChallenge{0, s.Id, challenge, &now, value, nil, ""}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) RegisterAccess(ip, mac string) error {
|
type ErroredChallenge struct {
|
||||||
|
Id int64 `json:"id,omitempty"`
|
||||||
|
IdStudent int64 `json:"id_student"`
|
||||||
|
Challenge int `json:"challenge,omitempty"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Student) RegisterChallengeError(challenge int, err error) error {
|
||||||
|
if _, errr := DBExec("INSERT INTO student_challenge_errors (id_student, challenge, time, error) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), err.Error()); errr == nil {
|
||||||
|
return nil
|
||||||
|
} else if _, errr := DBExec("UPDATE student_challenge_errors SET time = ?, error = ? WHERE id_student = ? AND challenge = ?", time.Now(), err.Error(), s.Id, challenge); errr != nil {
|
||||||
|
return errr
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Student) RegisterAccess(ip, mac string) error {
|
||||||
if res, err := DBExec("INSERT INTO student_login (id_student, ip, mac, time) VALUES (?, ?, ?, ?)", s.Id, ip, mac, time.Now()); err != nil {
|
if res, err := DBExec("INSERT INTO student_login (id_student, ip, mac, time) VALUES (?, ?, ?, ?)", s.Id, ip, mac, time.Now()); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if _, err := res.LastInsertId(); err != nil {
|
} else if _, err := res.LastInsertId(); err != nil {
|
||||||
|
|
|
@ -12,10 +12,19 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const StdNetmask = 80
|
||||||
|
|
||||||
func StudentIP(idstd int64) net.IP {
|
func StudentIP(idstd int64) net.IP {
|
||||||
return net.ParseIP(fmt.Sprintf("2a01:e0a:2b:2252:%x::", idstd))
|
return net.ParseIP(fmt.Sprintf("2a01:e0a:2b:2252:%x::", idstd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StudentNet(idstd int64) *net.IPNet {
|
||||||
|
return &net.IPNet{
|
||||||
|
IP: StudentIP(idstd),
|
||||||
|
Mask: net.CIDRMask(StdNetmask, 128),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type WGDump struct {
|
type WGDump struct {
|
||||||
PubKey string
|
PubKey string
|
||||||
PSK string
|
PSK string
|
||||||
|
@ -28,32 +37,32 @@ type WGDump struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
wgDumpCache_data map[string]WGDump = nil
|
wgDumpCache_data map[string]*WGDump = nil
|
||||||
wgDumpCache_time time.Time
|
wgDumpCache_time time.Time
|
||||||
wgDumpCache_mutex sync.RWMutex
|
wgDumpCache_mutex sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
func _readWgDump() (wgd map[string]WGDump, err error) {
|
func _readWgDump() (wgd map[string]*WGDump, err error) {
|
||||||
out, errr := exec.Command("wg", "show", "wg-adlin", "dump").Output()
|
out, errr := exec.Command("wg", "show", "wg-adlin", "dump").Output()
|
||||||
|
|
||||||
if errr != nil {
|
if errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
}
|
}
|
||||||
|
|
||||||
wgd = map[string]WGDump{}
|
wgd = map[string]*WGDump{}
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
cols := strings.Fields(line)
|
cols := strings.Fields(line)
|
||||||
if len(cols) != 8 {
|
if len(cols) != 8 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
wgd[cols[0]] = WGDump{cols[0], cols[1], cols[2], cols[3], cols[4], cols[5], cols[6], cols[7]}
|
wgd[cols[0]] = &WGDump{cols[0], cols[1], cols[2], cols[3], cols[4], cols[5], cols[6], cols[7]}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func readWgDump() (wgd map[string]WGDump, err error) {
|
func readWgDump() (wgd map[string]*WGDump, err error) {
|
||||||
wgDumpCache_mutex.RLock()
|
wgDumpCache_mutex.RLock()
|
||||||
defer wgDumpCache_mutex.RUnlock()
|
defer wgDumpCache_mutex.RUnlock()
|
||||||
|
|
||||||
|
@ -94,38 +103,48 @@ type TunnelToken struct {
|
||||||
Dump *WGDump
|
Dump *WGDump
|
||||||
}
|
}
|
||||||
|
|
||||||
func tokenFromText(token string) []byte {
|
func (tt *TunnelToken) GetStudentIP() string {
|
||||||
|
if tt.SuffixIP == 0 {
|
||||||
|
return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent).String(), 1)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent).String(), tt.SuffixIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenFromText(token string) []byte {
|
||||||
sha := sha512.Sum512([]byte(token))
|
sha := sha512.Sum512([]byte(token))
|
||||||
return sha[:]
|
return sha[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTunnelToken(token []byte) (t TunnelToken, err error) {
|
func GetTunnelToken(token []byte) (t *TunnelToken, err error) {
|
||||||
|
t = new(TunnelToken)
|
||||||
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE token=? ORDER BY time DESC", token).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version)
|
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE token=? ORDER BY time DESC", token).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version)
|
||||||
if err == nil && t.PubKey != nil {
|
if err == nil && t.PubKey != nil {
|
||||||
if wgd, errr := readWgDump(); errr == nil {
|
if wgd, errr := readWgDump(); errr == nil {
|
||||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
||||||
t.Dump = &v
|
t.Dump = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) NewTunnelToken(suffixip int) (t TunnelToken, err error) {
|
func (student *Student) NewTunnelToken(suffixip int) (t *TunnelToken, err error) {
|
||||||
tok := make([]byte, 7)
|
tok := make([]byte, 7)
|
||||||
if _, err = rand.Read(tok); err != nil {
|
if _, err = rand.Read(tok); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "#", -1), "l", "$", -1), "I", ">", -1)
|
t = new(TunnelToken)
|
||||||
t.token = tokenFromText(t.TokenText)
|
t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "<", -1), "l", "$", -1), "I", ">", -1)
|
||||||
|
t.token = TokenFromText(t.TokenText)
|
||||||
t.IdStudent = student.Id
|
t.IdStudent = student.Id
|
||||||
|
|
||||||
_, err = DBExec("INSERT INTO student_tunnel_tokens (token, token_text, id_student, time, suffixip, version) VALUES (?, ?, ?, ?, ?, 0)", t.token, t.TokenText, student.Id, time.Now(), suffixip)
|
_, err = DBExec("INSERT INTO student_tunnel_tokens (token, token_text, id_student, time, suffixip, version) VALUES (?, ?, ?, ?, ?, 0)", t.token, t.TokenText, student.Id, time.Now(), suffixip)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) {
|
func (student *Student) GetTunnelTokens() (ts []*TunnelToken, err error) {
|
||||||
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil {
|
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else if wgd, errr := readWgDump(); errr != nil {
|
} else if wgd, errr := readWgDump(); errr != nil {
|
||||||
|
@ -134,13 +153,13 @@ func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var t TunnelToken
|
t := &TunnelToken{}
|
||||||
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil {
|
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t.PubKey != nil {
|
if t.PubKey != nil {
|
||||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
||||||
t.Dump = &v
|
t.Dump = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ts = append(ts, t)
|
ts = append(ts, t)
|
||||||
|
@ -153,7 +172,7 @@ func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) GetActivesTunnels() (ts []TunnelToken, err error) {
|
func (student *Student) GetActivesTunnels() (ts []*TunnelToken, err error) {
|
||||||
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil {
|
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else if wgd, errr := readWgDump(); errr != nil {
|
} else if wgd, errr := readWgDump(); errr != nil {
|
||||||
|
@ -162,13 +181,13 @@ func (student Student) GetActivesTunnels() (ts []TunnelToken, err error) {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var t TunnelToken
|
t := &TunnelToken{}
|
||||||
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil {
|
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t.PubKey != nil {
|
if t.PubKey != nil {
|
||||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
||||||
t.Dump = &v
|
t.Dump = v
|
||||||
ts = append(ts, t)
|
ts = append(ts, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,12 +200,13 @@ func (student Student) GetActivesTunnels() (ts []TunnelToken, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) GetTunnelToken(token []byte) (t TunnelToken, err error) {
|
func (student *Student) GetTunnelToken(token []byte) (t *TunnelToken, err error) {
|
||||||
|
t = new(TunnelToken)
|
||||||
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE token = ? AND id_student = ? ORDER BY time DESC", token, student.Id).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version)
|
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE token = ? AND id_student = ? ORDER BY time DESC", token, student.Id).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version)
|
||||||
if err == nil && t.PubKey != nil {
|
if err == nil && t.PubKey != nil {
|
||||||
if wgd, errr := readWgDump(); errr == nil {
|
if wgd, errr := readWgDump(); errr == nil {
|
||||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
||||||
t.Dump = &v
|
t.Dump = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +214,7 @@ func (student Student) GetTunnelToken(token []byte) (t TunnelToken, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TunnelToken) Update() (int64, error) {
|
func (t *TunnelToken) Update() (int64, error) {
|
||||||
newtoken := tokenFromText(t.TokenText)
|
newtoken := TokenFromText(t.TokenText)
|
||||||
tm := time.Now()
|
tm := time.Now()
|
||||||
|
|
||||||
if res, err := DBExec("UPDATE student_tunnel_tokens SET token = ?, token_text = ?, id_student = ?, pubkey = ?, time = ?, suffixip = ?, version = ? WHERE token = ?", newtoken, t.TokenText, t.IdStudent, t.PubKey, tm, t.SuffixIP, t.Version, t.token); err != nil {
|
if res, err := DBExec("UPDATE student_tunnel_tokens SET token = ?, token_text = ?, id_student = ?, pubkey = ?, time = ?, suffixip = ?, version = ? WHERE token = ?", newtoken, t.TokenText, t.IdStudent, t.PubKey, tm, t.SuffixIP, t.Version, t.token); err != nil {
|
||||||
|
@ -208,14 +228,24 @@ func (t *TunnelToken) Update() (int64, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStudentsTunnels() (ts []TunnelToken, err error) {
|
func (t *TunnelToken) Delete() (int64, error) {
|
||||||
|
if res, err := DBExec("DELETE FROM student_tunnel_tokens WHERE token = ? AND id_student = ?", t.token, t.IdStudent); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStudentsTunnels() (ts []*TunnelToken, err error) {
|
||||||
if rows, errr := DBQuery("SELECT T.token, T.token_text, T.id_student, T.pubkey, T.time, T.suffixip, T.version FROM student_tunnel_tokens T INNER JOIN (SELECT B.id_student, MAX(B.time) AS time FROM student_tunnel_tokens B WHERE B.pubkey IS NOT NULL GROUP BY id_student) L ON T.id_student = L.id_student AND T.time = L.time"); errr != nil {
|
if rows, errr := DBQuery("SELECT T.token, T.token_text, T.id_student, T.pubkey, T.time, T.suffixip, T.version FROM student_tunnel_tokens T INNER JOIN (SELECT B.id_student, MAX(B.time) AS time FROM student_tunnel_tokens B WHERE B.pubkey IS NOT NULL GROUP BY id_student) L ON T.id_student = L.id_student AND T.time = L.time"); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var t TunnelToken
|
t := &TunnelToken{}
|
||||||
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil {
|
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ func init() {
|
||||||
router.POST("/api/auth/logout", apiRawHandler(logout))
|
router.POST("/api/auth/logout", apiRawHandler(logout))
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateAuthToken(s adlin.Student, _ httprouter.Params, _ []byte) (interface{}, error) {
|
func validateAuthToken(s *adlin.Student, _ httprouter.Params, _ []byte) (interface{}, error) {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ type loginForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func completeAuth(w http.ResponseWriter, username string, session *adlin.Session) (err error) {
|
func completeAuth(w http.ResponseWriter, username string, session *adlin.Session) (err error) {
|
||||||
var std adlin.Student
|
var std *adlin.Student
|
||||||
if !adlin.StudentExists(username) {
|
if !adlin.StudentExists(username) {
|
||||||
if std, err = adlin.NewStudent(username); err != nil {
|
if std, err = adlin.NewStudent(username); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -60,9 +60,7 @@ func completeAuth(w http.ResponseWriter, username string, session *adlin.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
if session == nil {
|
if session == nil {
|
||||||
var s adlin.Session
|
session, err = std.NewSession()
|
||||||
s, err = std.NewSession()
|
|
||||||
session = &s
|
|
||||||
} else {
|
} else {
|
||||||
_, err = session.SetStudent(std)
|
_, err = session.SetStudent(std)
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ func OIDC_CRI_complete(w http.ResponseWriter, r *http.Request, ps httprouter.Par
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := completeAuth(w, claims.Username, &session); err != nil {
|
if err := completeAuth(w, claims.Username, session); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,7 +338,7 @@ func receiveChallenge(r *http.Request, ps httprouter.Params, body []byte) (inter
|
||||||
return nil, errors.New("This is not the expected token.")
|
return nil, errors.New("This is not the expected token.")
|
||||||
}
|
}
|
||||||
|
|
||||||
var std adlin.Student
|
var std *adlin.Student
|
||||||
|
|
||||||
if stdid, err := strconv.Atoi(gt.Login); err == nil {
|
if stdid, err := strconv.Atoi(gt.Login); err == nil {
|
||||||
if std, err = adlin.GetStudent(stdid); err != nil {
|
if std, err = adlin.GetStudent(stdid); err != nil {
|
||||||
|
@ -388,7 +388,7 @@ func receiveToken(r *http.Request, body []byte, chid int) (interface{}, error) {
|
||||||
if std, err := adlin.GetStudentByLogin(gt.Login); err != nil {
|
if std, err := adlin.GetStudentByLogin(gt.Login); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
if err := challenges[chid-1].Check(&std, >, chid); err != nil {
|
if err := challenges[chid-1].Check(std, >, chid); err != nil {
|
||||||
log.Printf("%s just try ch#%d: %s\n", std.Login, chid, err)
|
log.Printf("%s just try ch#%d: %s\n", std.Login, chid, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type checkGLUE struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.POST("/api/check/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/check/GLUE", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var uc checkGLUE
|
var uc checkGLUE
|
||||||
if err := json.Unmarshal(body, &uc); err != nil {
|
if err := json.Unmarshal(body, &uc); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -27,7 +27,7 @@ func init() {
|
||||||
return true, check_GLUE_respond(student, uc.Domain, uc.IP)
|
return true, check_GLUE_respond(student, uc.Domain, uc.IP)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
func check_GLUE_respond(student adlin.Student, domain string, ip string) (err error) {
|
func check_GLUE_respond(student *adlin.Student, domain string, ip string) (err error) {
|
||||||
if !strings.HasPrefix(ip, adlin.StudentIP(student.Id).String()) {
|
if !strings.HasPrefix(ip, adlin.StudentIP(student.Id).String()) {
|
||||||
return fmt.Errorf("%q is not your IP range")
|
return fmt.Errorf("%q is not your IP range")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,103 +22,151 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.GET("/api/adomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.GET("/api/adomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return student.GetAssociatedDomains(), nil
|
return student.GetAssociatedDomains(), nil
|
||||||
}))
|
}))
|
||||||
router.POST("/api/adomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/adomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
ue := &struct {
|
ue := &struct {
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
A string `json:"a"`
|
A string `json:"a"`
|
||||||
AAAA string `json:"aaaa"`
|
AAAA string `json:"aaaa"`
|
||||||
|
CNAME string `json:"cname,omitempty"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var aaaa net.IP
|
if ue.Domain != "" && ue.A == "" && ue.AAAA == "" && ue.CNAME == "" {
|
||||||
if ue != nil && len(ue.AAAA) > 0 {
|
student.AssociatedDomain = nil
|
||||||
aaaa = net.ParseIP(ue.AAAA)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, AddAssociatedDomains(student, aaaa)
|
if _, err := student.Update(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
} else if ue.CNAME != "" {
|
||||||
|
cname := dns.Fqdn(ue.CNAME)
|
||||||
|
student.AssociatedDomain = &cname
|
||||||
|
|
||||||
|
if _, err := student.Update(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
} else {
|
||||||
|
var aaaa net.IP
|
||||||
|
if ue != nil && len(ue.AAAA) > 0 {
|
||||||
|
aaaa = net.ParseIP(ue.AAAA)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, AddAssociatedDomains(student, aaaa)
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
router.GET("/api/adomains/:dn", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.GET("/api/adomains/:dn", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return GetAssociatedDomain(student, ps.ByName("dn"))
|
return GetAssociatedDomain(student, ps.ByName("dn"))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.GET("/api/ddomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.GET("/api/ddomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return []string{student.MyDelegatedDomain()}, nil
|
return []string{student.MyDelegatedDomain()}, nil
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/ddomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
|
ue := &struct {
|
||||||
|
NS string `json:"ns"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ue.NS == "" {
|
||||||
|
student.DelegatedDomain = nil
|
||||||
|
|
||||||
|
if _, err := student.Update(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
} else {
|
||||||
|
ns := dns.Fqdn(ue.NS)
|
||||||
|
student.DelegatedDomain = &ns
|
||||||
|
|
||||||
|
if _, err := student.Update(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "")
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "NS")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "NS")
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, AddNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
return true, AddNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, UpdateNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, ""))
|
return true, UpdateNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, ""))
|
||||||
}))
|
}))
|
||||||
router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "NS", strings.Join(ue.Values, " "))
|
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "NS", strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "AAAA")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "AAAA")
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, AddGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
return true, AddGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " "))
|
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "DS")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "DS")
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/ddomains/:dn/DS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, AddDSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
return true, AddDSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var ue Entry
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -175,7 +223,7 @@ func parseZoneRead(globalDomain string, domain string) (rr []Entry, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAssociatedDomain(student adlin.Student, dn string) (rrs []Entry, err error) {
|
func GetAssociatedDomain(student *adlin.Student, dn string) (rrs []Entry, err error) {
|
||||||
domains := student.GetAssociatedDomains()
|
domains := student.GetAssociatedDomains()
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
|
@ -201,7 +249,7 @@ func GetAssociatedDomain(student adlin.Student, dn string) (rrs []Entry, err err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func delAssociatedDomains(student adlin.Student, dn string) (err error) {
|
func delAssociatedDomains(student *adlin.Student, dn string) (err error) {
|
||||||
var adomains []Entry
|
var adomains []Entry
|
||||||
adomains, err = GetAssociatedDomain(student, dn)
|
adomains, err = GetAssociatedDomain(student, dn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -238,15 +286,15 @@ func delAssociatedDomains(student adlin.Student, dn string) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) {
|
func AddAssociatedDomains(student *adlin.Student, aaaa net.IP) (err error) {
|
||||||
err = delAssociatedDomains(student, student.MyAssociatedDomain())
|
err = delAssociatedDomains(student, student.DefaultAssociatedDomain())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if aaaa == nil {
|
if aaaa == nil {
|
||||||
aaaa = net.ParseIP(adlin.StudentIP(student.Id).String() + "1")
|
aaaa = net.ParseIP(adlin.StudentIP(student.Id).String() + "1")
|
||||||
} else if !strings.HasPrefix(aaaa.String(), adlin.StudentIP(student.Id).String()) {
|
} else if !adlin.StudentNet(student.Id).Contains(aaaa) {
|
||||||
return errors.New("The associated IP has to be in your IP range.")
|
return errors.New("The associated IP has to be in your IP range.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,12 +305,12 @@ func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) {
|
||||||
m2.Question[0] = dns.Question{adlin.AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m2.Question[0] = dns.Question{adlin.AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
rrA := new(dns.A)
|
rrA := new(dns.A)
|
||||||
rrA.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600}
|
rrA.Hdr = dns.RR_Header{Name: student.DefaultAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600}
|
||||||
rrA.A = net.IPv4(82, 64, 31, 248)
|
rrA.A = net.IPv4(82, 64, 31, 248)
|
||||||
m2.Insert([]dns.RR{rrA})
|
m2.Insert([]dns.RR{rrA})
|
||||||
|
|
||||||
rrAAAA := new(dns.AAAA)
|
rrAAAA := new(dns.AAAA)
|
||||||
rrAAAA.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600}
|
rrAAAA.Hdr = dns.RR_Header{Name: student.DefaultAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600}
|
||||||
rrAAAA.AAAA = aaaa
|
rrAAAA.AAAA = aaaa
|
||||||
m2.Insert([]dns.RR{rrAAAA})
|
m2.Insert([]dns.RR{rrAAAA})
|
||||||
|
|
||||||
|
@ -274,7 +322,7 @@ func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRRDelegatedDomain(student adlin.Student, dn string, rr string) (rrs []Entry, err error) {
|
func getRRDelegatedDomain(student *adlin.Student, dn string, rr string) (rrs []Entry, err error) {
|
||||||
domains := []string{student.MyDelegatedDomain()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
|
@ -300,7 +348,7 @@ func getRRDelegatedDomain(student adlin.Student, dn string, rr string) (rrs []En
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, ns string) (err error) {
|
func AddNSDelegatedDomain(student *adlin.Student, dn string, ttl uint32, ns string) (err error) {
|
||||||
for _, d := range []string{student.MyDelegatedDomain()} {
|
for _, d := range []string{student.MyDelegatedDomain()} {
|
||||||
m1 := new(dns.Msg)
|
m1 := new(dns.Msg)
|
||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
|
@ -323,7 +371,7 @@ func AddNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, ns strin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldns string, ns string) (err error) {
|
func UpdateNSDelegatedDomain(student *adlin.Student, dn string, ttl uint32, oldns string, ns string) (err error) {
|
||||||
for _, d := range []string{student.MyDelegatedDomain()} {
|
for _, d := range []string{student.MyDelegatedDomain()} {
|
||||||
m1 := new(dns.Msg)
|
m1 := new(dns.Msg)
|
||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
|
@ -351,7 +399,7 @@ func UpdateNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldns
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, aaaa string) (err error) {
|
func AddGLUEDelegatedDomain(student *adlin.Student, dn string, ttl uint32, aaaa string) (err error) {
|
||||||
domains := []string{student.MyDelegatedDomain()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
|
@ -387,7 +435,7 @@ func AddGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, aaaa s
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldaaaa string, aaaa string) (err error) {
|
func UpdateGLUEDelegatedDomain(student *adlin.Student, dn string, ttl uint32, oldaaaa string, aaaa string) (err error) {
|
||||||
domains := []string{student.MyDelegatedDomain()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
|
@ -429,7 +477,7 @@ func UpdateGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, old
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddDSDelegatedDomain(student adlin.Student, dn string, ttl uint32, rdata string) (err error) {
|
func AddDSDelegatedDomain(student *adlin.Student, dn string, ttl uint32, rdata string) (err error) {
|
||||||
domains := []string{student.MyDelegatedDomain()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
|
@ -479,7 +527,7 @@ func AddDSDelegatedDomain(student adlin.Student, dn string, ttl uint32, rdata st
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteRRDelegatedDomain(student adlin.Student, dn string, rr string, values ...string) (err error) {
|
func DeleteRRDelegatedDomain(student *adlin.Student, dn string, rr string, values ...string) (err error) {
|
||||||
domains := []string{student.MyDelegatedDomain()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
|
|
|
@ -65,7 +65,7 @@ func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []
|
||||||
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
student = &std
|
student = std
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ func apiHandler(f DispatchFunction, access ...func(*adlin.Student, *http.Request
|
||||||
return rawHandler(responseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }), access...)
|
return rawHandler(responseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }), access...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiAuthHandler(f func(adlin.Student, httprouter.Params, []byte) (interface{}, error), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
func apiAuthHandler(f func(*adlin.Student, httprouter.Params, []byte) (interface{}, error), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
return rawHandler(responseHandler(func(r *http.Request, ps httprouter.Params, b []byte) (interface{}, error) {
|
return rawHandler(responseHandler(func(r *http.Request, ps httprouter.Params, b []byte) (interface{}, error) {
|
||||||
if cookie, err := r.Cookie("auth"); err != nil {
|
if cookie, err := r.Cookie("auth"); err != nil {
|
||||||
return nil, errors.New("Authorization required")
|
return nil, errors.New("Authorization required")
|
||||||
|
@ -176,7 +176,7 @@ func apiAuthHandler(f func(adlin.Student, httprouter.Params, []byte) (interface{
|
||||||
}), access...)
|
}), access...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func studentHandler(f func(adlin.Student, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
|
func studentHandler(f func(*adlin.Student, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
|
||||||
return func(ps httprouter.Params, body []byte) (interface{}, error) {
|
return func(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
||||||
if student, err := adlin.GetStudentByLogin(ps.ByName("sid")); err != nil {
|
if student, err := adlin.GetStudentByLogin(ps.ByName("sid")); err != nil {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
height: calc(100vh / 5 - 0.3vh);
|
height: calc(100vh / 5 - 0.3vh);
|
||||||
margin-left: 0.2%;
|
margin-left: 0.2%;
|
||||||
margin-bottom: 0.2vh;
|
margin-bottom: 0.2vh;
|
||||||
width: calc(100vw / 13 - 0.22vw);
|
width: calc(100vw / 13 - 0.26vw);
|
||||||
}
|
}
|
||||||
.student-title {
|
.student-title {
|
||||||
width: calc(2 * (100vw / 13 - 0.22vw) + 0.2vw);
|
width: calc(2 * (100vw / 13 - 0.22vw) + 0.2vw);
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
.login {
|
.login {
|
||||||
max-width: 122px;
|
max-width: 122px;
|
||||||
}
|
}
|
||||||
.badge {
|
.badge.badge-sm {
|
||||||
font-size: 59%;
|
font-size: 59%;
|
||||||
padding: .15em .35em;
|
padding: .15em .35em;
|
||||||
}
|
}
|
||||||
|
@ -72,12 +72,12 @@
|
||||||
<ul ng-if="ips" style="padding-left:0">
|
<ul ng-if="ips" style="padding-left:0">
|
||||||
<li><strong>Domaine :</strong> <a href="http://{{ips.adn}}/">{{ips.adn}}</a></li>
|
<li><strong>Domaine :</strong> <a href="http://{{ips.adn}}/">{{ips.adn}}</a></li>
|
||||||
<li><strong>Délégation :</strong> <a href="http://{{ips.ddn}}/">{{ips.ddn}}</a></li>
|
<li><strong>Délégation :</strong> <a href="http://{{ips.ddn}}/">{{ips.ddn}}</a></li>
|
||||||
<li><strong>IPv6 :</strong> {{ips.wg}}1/96</li>
|
<li><strong>IPv6 :</strong> {{ips.wg}}/80</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div ng-repeat="(tutoid,tuto) in tuto_progress">
|
<div ng-repeat="(tutoid,tuto) in tuto_progress">
|
||||||
<hr>
|
<hr>
|
||||||
<h6>TP {{tutoid+1}}</h6>
|
<h6>TP {{tutoid+1}}</h6>
|
||||||
<span class="badge mr-1" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch]}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
|
<span class="badge mr-1" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch] || !mychallenges[ch].recent}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,7 +108,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card student d-flex flex-column justify-content-between" ng-repeat="(login, mychallenges) in students" style="background-image: url('https://photos.cri.epita.fr/square/{{ mychallenges.img | lowercase }}')">
|
<div class="card student d-flex flex-column justify-content-between" ng-repeat="(login, mychallenges) in students" ng-if="login != 'nemunaire'" style="background-image: url('https://photos.cri.epita.fr/square/{{ mychallenges.img | lowercase }}')">
|
||||||
<h5 class="login text-truncate" title="{{ login }}">
|
<h5 class="login text-truncate" title="{{ login }}">
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges['ping'] && mychallenges['ping'].recent < 120, 'badge-info': mychallenges['ping'] && mychallenges['ping'].recent >= 120 && mychallenges['ping'].recent < 300, 'badge-warning': mychallenges['ping'] && mychallenges['ping'].recent >= 300 && mychallenges['ping'].recent < 900, 'badge-danger': mychallenges['ping'] && mychallenges['ping'].recent >= 900, 'badge-dark': !mychallenges['ping']}" title="{{ mychallenges['ping'].time }}">
|
<span class="badge" ng-class="{'badge-success': mychallenges['ping'] && mychallenges['ping'].recent < 120, 'badge-info': mychallenges['ping'] && mychallenges['ping'].recent >= 120 && mychallenges['ping'].recent < 300, 'badge-warning': mychallenges['ping'] && mychallenges['ping'].recent >= 300 && mychallenges['ping'].recent < 900, 'badge-danger': mychallenges['ping'] && mychallenges['ping'].recent >= 900, 'badge-dark': !mychallenges['ping']}" title="{{ mychallenges['ping'].time }}">
|
||||||
💻
|
💻
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
<a class="text-dark" href="/dashboard/{{login}}">{{ login }}</a>
|
<a class="text-dark" href="/dashboard/{{login}}">{{ login }}</a>
|
||||||
</h5>
|
</h5>
|
||||||
<div class="d-flex flex-wrap justify-content-around" style="padding: 0">
|
<div class="d-flex flex-wrap justify-content-around" style="padding: 0">
|
||||||
<span class="badge" style="margin-left: .07rem" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch]}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
|
<span class="badge badge-sm" style="margin-left: .07rem" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch] || !mychallenges[ch].recent}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.icon"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -126,6 +126,7 @@
|
||||||
<script src="js/angular-resource.min.js"></script>
|
<script src="js/angular-resource.min.js"></script>
|
||||||
<script src="js/angular-route.min.js"></script>
|
<script src="js/angular-route.min.js"></script>
|
||||||
<script src="js/angular-sanitize.min.js"></script>
|
<script src="js/angular-sanitize.min.js"></script>
|
||||||
|
<script src="js/adlin-common.js"></script>
|
||||||
<script src="js/adlin-dashboard.js"></script>
|
<script src="js/adlin-dashboard.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
28
token-validator/htdocs/js/adlin-common.js
Normal file
28
token-validator/htdocs/js/adlin-common.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
var tuto_progress = [
|
||||||
|
{
|
||||||
|
1: { title: "Is alive?", icon: "👋", label: "Token 1"},
|
||||||
|
2: { title: "DMZ reached", icon: "📚", label: "Token 2"},
|
||||||
|
3: { title: "HTTPS on + time", icon: "⏲", label: "Token 3"},
|
||||||
|
4: { title: "DNS ok", icon: "🍰", label: "Token 4"},
|
||||||
|
5: { title: "On Internet", icon: "🌎", label: "Token 5"},
|
||||||
|
6: { title: "Bonus caché", icon: "b", label: "Bonus 0"},
|
||||||
|
7: { title: "Bonus ICMP", icon: "🏓", label: "Bonus 1"},
|
||||||
|
8: { title: "Bonus disk", icon: "💽", label: "Bonus 2"},
|
||||||
|
9: { title: "Bonus email", icon: "📧", label: "Bonus 3"},
|
||||||
|
10: { title: "Wg tunnel", icon: "🚇", label: "Tunnel up"},
|
||||||
|
11: { title: "Uploaded SSH key", icon: "💊", label: "SSH"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
100: { title: "HTTP on IP", icon: "0", label: "HTTP IP"},
|
||||||
|
101: { title: "HTTP on associated domain", icon: "1", label: "HTTP domain"},
|
||||||
|
102: { title: "HTTPS on associated domain", icon: "2", label: "HTTPS domain"},
|
||||||
|
103: { title: "DNS Delegation", icon: "3", label: "DNS"},
|
||||||
|
104: { title: "HTTP on delegated domain", icon: "4", label: "HTTP on NS"},
|
||||||
|
105: { title: "HTTPS on delegated domain", icon: "5", label: "HTTPS on NS"},
|
||||||
|
106: { title: "Matrix", icon: "6", label: "Matrix"},
|
||||||
|
107: { title: "DNSSEC (bonus)", icon: "7", label: "DNSSEC"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
200: { title: "HTTP", label: "HTTP"},
|
||||||
|
},
|
||||||
|
];
|
|
@ -1,32 +1,3 @@
|
||||||
var tuto_progress = [
|
|
||||||
{
|
|
||||||
1: { title: "Is alive?", label: "👋"},
|
|
||||||
2: { title: "DMZ reached", label: "📚"},
|
|
||||||
3: { title: "HTTPS on + time", label: "⏲"},
|
|
||||||
4: { title: "DNS ok", label: "🍰"},
|
|
||||||
5: { title: "On Internet", label: "🌎"},
|
|
||||||
6: { title: "Bonus caché", label: "b"},
|
|
||||||
7: { title: "Bonus ICMP", label: "🏓"},
|
|
||||||
8: { title: "Bonus disk", label: "💽"},
|
|
||||||
9: { title: "Bonus email", label: "📧"},
|
|
||||||
10: { title: "Wg tunnel", label: "🚇"},
|
|
||||||
11: { title: "Uploaded SSH key", label: "💊"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
100: { title: "HTTP", label: "HTTP"},
|
|
||||||
101: { title: "HTTPS", label: "HTTPS"},
|
|
||||||
102: { title: "DNS", label: "DNS"},
|
|
||||||
103: { title: "Matrix", label: "Matrix"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
200: { title: "HTTP", label: "HTTP"},
|
|
||||||
201: { title: "HTTPS", label: "HTTPS"},
|
|
||||||
202: { title: "DNS", label: "DNS"},
|
|
||||||
203: { title: "Matrix", label: "Matrix"},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
angular.module("AdLinApp", ["ngResource", "ngSanitize"])
|
angular.module("AdLinApp", ["ngResource", "ngSanitize"])
|
||||||
.factory("Student", function($resource) {
|
.factory("Student", function($resource) {
|
||||||
return $resource("/api/students/:studentId", { studentId: '@id' }, {
|
return $resource("/api/students/:studentId", { studentId: '@id' }, {
|
||||||
|
@ -65,7 +36,8 @@ angular.module("AdLinApp")
|
||||||
var refreshStd = function() {
|
var refreshStd = function() {
|
||||||
$scope.students = Student.query();
|
$scope.students = Student.query();
|
||||||
}
|
}
|
||||||
$interval(refreshStd, 1600000);
|
var myinterval = $interval(refreshStd, 1600000);
|
||||||
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
||||||
})
|
})
|
||||||
.controller("StudentsProgressionController", function($scope, $interval, Progression) {
|
.controller("StudentsProgressionController", function($scope, $interval, Progression) {
|
||||||
$scope.tuto_progress = tuto_progress;
|
$scope.tuto_progress = tuto_progress;
|
||||||
|
@ -109,7 +81,8 @@ angular.module("AdLinApp")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
refreshStd();
|
refreshStd();
|
||||||
$interval(refreshStd, 9750);
|
var myinterval = $interval(refreshStd, 9750);
|
||||||
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
||||||
})
|
})
|
||||||
.controller("StudentProgressionController", function($scope, $interval, $http, Student, StudentProgression) {
|
.controller("StudentProgressionController", function($scope, $interval, $http, Student, StudentProgression) {
|
||||||
$scope.tuto_progress = tuto_progress;
|
$scope.tuto_progress = tuto_progress;
|
||||||
|
@ -133,7 +106,8 @@ angular.module("AdLinApp")
|
||||||
}
|
}
|
||||||
$scope.$watch("onestudent", function(onestudent) {
|
$scope.$watch("onestudent", function(onestudent) {
|
||||||
refreshStd();
|
refreshStd();
|
||||||
$interval(refreshStd, 15000);
|
var myinterval = $interval(refreshStd, 15000);
|
||||||
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.controller("PingController", function($scope, $interval, $http) {
|
.controller("PingController", function($scope, $interval, $http) {
|
||||||
|
@ -150,7 +124,8 @@ angular.module("AdLinApp")
|
||||||
$scope.$watch("student", function(student) {
|
$scope.$watch("student", function(student) {
|
||||||
student.$promise.then(function(std) {
|
student.$promise.then(function(std) {
|
||||||
refreshPing();
|
refreshPing();
|
||||||
$interval(refreshPing, 15000);
|
var myinterval = $interval(refreshPing, 15000);
|
||||||
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -162,7 +137,8 @@ angular.module("AdLinApp")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
refreshSSH();
|
refreshSSH();
|
||||||
$interval(refreshSSH, 15500);
|
var myinterval = $interval(refreshSSH, 15500);
|
||||||
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
||||||
})
|
})
|
||||||
.controller("ProgressionController", function($scope, $interval, $http) {
|
.controller("ProgressionController", function($scope, $interval, $http) {
|
||||||
$scope.tuto_progress = tuto_progress;
|
$scope.tuto_progress = tuto_progress;
|
||||||
|
@ -179,5 +155,6 @@ angular.module("AdLinApp")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
refreshChal();
|
refreshChal();
|
||||||
$interval(refreshChal, 15750);
|
var myinterval = $interval(refreshChal, 15750);
|
||||||
|
$scope.$on('$destroy', function () { $interval.cancel(myinterval); });
|
||||||
})
|
})
|
||||||
|
|
|
@ -44,6 +44,17 @@ angular.module("AdLinApp")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.directive('integer', function() {
|
||||||
|
return {
|
||||||
|
require: 'ngModel',
|
||||||
|
link: function(scope, ele, attr, ctrl){
|
||||||
|
ctrl.$parsers.unshift(function(viewValue){
|
||||||
|
return parseInt(viewValue, 10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
.component('toast', {
|
.component('toast', {
|
||||||
bindings: {
|
bindings: {
|
||||||
date: '=',
|
date: '=',
|
||||||
|
@ -100,6 +111,8 @@ angular.module("AdLinApp")
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller("ProgressionController", function($scope, $interval, $http) {
|
.controller("ProgressionController", function($scope, $interval, $http) {
|
||||||
|
$scope.tuto_progress = tuto_progress;
|
||||||
|
|
||||||
$scope.mychallenges = {};
|
$scope.mychallenges = {};
|
||||||
var refreshChal = function() {
|
var refreshChal = function() {
|
||||||
$http.get("api/students/" + $scope.student.id + "/progress").then(function(response) {
|
$http.get("api/students/" + $scope.student.id + "/progress").then(function(response) {
|
||||||
|
@ -150,7 +163,10 @@ angular.module("AdLinApp")
|
||||||
};
|
};
|
||||||
$scope.updateTunnelInfo();
|
$scope.updateTunnelInfo();
|
||||||
|
|
||||||
$scope.updateTunnelsList = function() {
|
var noUpdate = 0
|
||||||
|
|
||||||
|
$scope.updateTunnelsList = function() {
|
||||||
|
if (noUpdate == 0)
|
||||||
$http({
|
$http({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: "api/wg/",
|
url: "api/wg/",
|
||||||
|
@ -211,11 +227,48 @@ angular.module("AdLinApp")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.editTunnel = function(tunnel) {
|
||||||
|
tunnel.edit = true;
|
||||||
|
noUpdate++;
|
||||||
|
tunnel.newData = {
|
||||||
|
TokenText: tunnel.TokenText,
|
||||||
|
SuffixIP: tunnel.SuffixIP,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.updateTunnel = function(tunnel) {
|
||||||
|
tunnel.pleaseWaitUpdate = true;
|
||||||
|
$http({
|
||||||
|
method: 'PUT',
|
||||||
|
url: "api/wg/" + encodeURIComponent(tunnel.TokenText),
|
||||||
|
data: tunnel.newData
|
||||||
|
}).then(function(response) {
|
||||||
|
noUpdate--;
|
||||||
|
tunnel.SuffixIP = tunnel.newData.SuffixIP;
|
||||||
|
tunnel.TokenText = tunnel.newData.TokenText;
|
||||||
|
tunnel.edit = false;
|
||||||
|
tunnel.pleaseWaitUpdate = false;
|
||||||
|
$scope.updateTunnelsList();
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "success",
|
||||||
|
title: "Maatma Tunnels",
|
||||||
|
msg: "Tunnel mise à jour avec succès !",
|
||||||
|
});
|
||||||
|
}, function(response) {
|
||||||
|
tunnel.pleaseWaitUpdate = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "danger",
|
||||||
|
title: "Maatma Tunnels",
|
||||||
|
msg: (response.data ? response.data.errmsg : "Impossible de contacter le serveur"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.dropTunnel = function(tunnel) {
|
$scope.dropTunnel = function(tunnel) {
|
||||||
tunnel.pleaseWaitDrop = true;
|
tunnel.pleaseWaitDrop = true;
|
||||||
$http({
|
$http({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: "api/wg/" + tunnel.TokenText,
|
url: "api/wg/" + encodeURIComponent(tunnel.TokenText),
|
||||||
data: {}
|
data: {}
|
||||||
}).then(function(response) {
|
}).then(function(response) {
|
||||||
$scope.updateTunnelsList();
|
$scope.updateTunnelsList();
|
||||||
|
@ -330,6 +383,123 @@ angular.module("AdLinApp")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.useMyAssociationD = function() {
|
||||||
|
$scope.assoc = {
|
||||||
|
"domain": $scope.adomains[0].domain,
|
||||||
|
"cname": $scope.student.associated_domain?$scope.student.associated_domain:"",
|
||||||
|
}
|
||||||
|
$('#AssocMyDomainModal').modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.newMyDomainAssociationD = function(assoc) {
|
||||||
|
$('#AssocMyDomainModal').modal('hide');
|
||||||
|
$scope.pleaseWaitNewAssociation = true;
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: "api/adomains/",
|
||||||
|
data: assoc,
|
||||||
|
}).then(function(response) {
|
||||||
|
$scope.updateAssociationD();
|
||||||
|
$scope.checkLoginState();
|
||||||
|
$scope.pleaseWaitNewAssociation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "success",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Votre domaine a bien été associé !",
|
||||||
|
});
|
||||||
|
}, function(response) {
|
||||||
|
$scope.pleaseWaitNewAssociation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "danger",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Erreur durant l'association du domaine : " + response.data.errmsg,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.delMyDomainAssociationD = function(assoc) {
|
||||||
|
$('#AssocMyDomainModal').modal('hide');
|
||||||
|
$scope.pleaseWaitNewAssociation = true;
|
||||||
|
assoc.cname = ''
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: "api/adomains/",
|
||||||
|
data: assoc,
|
||||||
|
}).then(function(response) {
|
||||||
|
$scope.updateAssociationD();
|
||||||
|
$scope.checkLoginState();
|
||||||
|
$scope.pleaseWaitNewAssociation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "success",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Votre domaine n'est plus pris en compte. Vous devez utiliser l'association qui vous a été attribuée sous adlin20xx.p0m.fr.",
|
||||||
|
});
|
||||||
|
}, function(response) {
|
||||||
|
$scope.pleaseWaitNewAssociation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "danger",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Erreur durant l'association du domaine : " + response.data.errmsg,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.useMyDelegationD = function() {
|
||||||
|
$scope.assoc = {
|
||||||
|
"ns": $scope.student.delegated_domain?$scope.student.delegated_domain:"",
|
||||||
|
}
|
||||||
|
$('#DelegateMyDomainModal').modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.newMyDomainDelegationD = function(assoc) {
|
||||||
|
$('#DelegateMyDomainModal').modal('hide');
|
||||||
|
$scope.pleaseWaitNewDelegation = true;
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: "api/ddomains/",
|
||||||
|
data: assoc,
|
||||||
|
}).then(function(response) {
|
||||||
|
$scope.checkLoginState();
|
||||||
|
$scope.pleaseWaitNewDelegation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "success",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Votre sous-domaine de délégation a bien été enregistré !",
|
||||||
|
});
|
||||||
|
}, function(response) {
|
||||||
|
$scope.pleaseWaitNewDelegation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "danger",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Erreur durant la délégation du domaine : " + response.data.errmsg,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.delMyDomainDelegatedD = function() {
|
||||||
|
$scope.pleaseWaitNewDelegation = true;
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: "api/ddomains/",
|
||||||
|
data: {},
|
||||||
|
}).then(function(response) {
|
||||||
|
$scope.checkLoginState();
|
||||||
|
$scope.pleaseWaitNewDelegation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "success",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Votre domaine n'est plus pris en compte. Vous devez utiliser la délégation qui vous a été attribuée sous srs.p0m.fr.",
|
||||||
|
});
|
||||||
|
}, function(response) {
|
||||||
|
$scope.pleaseWaitNewDelegation = false;
|
||||||
|
$scope.addToast({
|
||||||
|
variant: "danger",
|
||||||
|
title: "Maatma Domain Names",
|
||||||
|
msg: "Erreur durant la délégation du domaine : " + response.data.errmsg,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.addNS = function(domain) {
|
$scope.addNS = function(domain) {
|
||||||
$scope.nsrr = {
|
$scope.nsrr = {
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
<script src="js/angular-resource.min.js"></script>
|
<script src="js/angular-resource.min.js"></script>
|
||||||
<script src="js/angular-route.min.js"></script>
|
<script src="js/angular-route.min.js"></script>
|
||||||
<script src="js/angular-sanitize.min.js"></script>
|
<script src="js/angular-sanitize.min.js"></script>
|
||||||
|
<script src="js/adlin-common.js"></script>
|
||||||
<script src="js/adlin-main.js"></script>
|
<script src="js/adlin-main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
Noms de domaine
|
Noms de domaine
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<h3>Association simple</h3>
|
<h3>
|
||||||
|
Association simple
|
||||||
|
<span class="badge badge-pill badge-dark" title="Une association simple est un sous-domaine qui va vous être attribué, sans que vous ayez à gérer de serveur DNS. Ce domaine sera associé à une IP que vous pourrez changer par la suite. C'est la première étape, plus facile que la délégation.">?</span>
|
||||||
|
</h3>
|
||||||
|
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -28,6 +31,9 @@
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="pleaseWaitNewAssociation"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="pleaseWaitNewAssociation"></span>
|
||||||
Demander une nouvelle association
|
Demander une nouvelle association
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn ml-2" ng-class="{'btn-secondary': !adomains || !adomains.length, 'btn-success': adomains && adomains.length}" ng-disabled="!adomains || !adomains.length" ng-click="useMyAssociationD()">
|
||||||
|
Utiliser mon domaine
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
|
@ -35,7 +41,10 @@
|
||||||
|
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
|
|
||||||
<h3>Délégation</h3>
|
<h3>
|
||||||
|
Délégation
|
||||||
|
<span class="badge badge-pill badge-dark" title="Une délégation va vous permettre de gérer vous-même votre domaine sur Internet. Nous vous offrons login.srs.p0m.fr, mais vous pouvez aussi choisir de créer la délégation sur votre domaine, si vous en possédez un. Rendez-vous ensuite sur votre serveur pour y installer un serveur de noms de domaine autoritaire tel que nsd, bind, knot ou encore powerDNS...">?</span>
|
||||||
|
</h3>
|
||||||
|
|
||||||
<ul class="nav nav-tabs" id="ddomainTabs" role="tablist">
|
<ul class="nav nav-tabs" id="ddomainTabs" role="tablist">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
@ -48,11 +57,17 @@
|
||||||
<a class="nav-link" data-toggle="tab" data-target="#DNSSEC" role="tab" aria-controls="dnssec" aria-selected="false">DNSSEC</a>
|
<a class="nav-link" data-toggle="tab" data-target="#DNSSEC" role="tab" aria-controls="dnssec" aria-selected="false">DNSSEC</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content" id="myTabContent">
|
<div ng-if="!student.delegated_domain" class="tab-content" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="NS" role="tabpanel" aria-labelledby="ns-tab">
|
<div class="tab-pane fade show active" id="NS" role="tabpanel" aria-labelledby="ns-tab">
|
||||||
|
|
||||||
<div ng-repeat="domain in ddomains">
|
<div ng-repeat="domain in ddomains">
|
||||||
<h4 class="text-muted">{{ domain }}</h4>
|
<h4 class="text-muted">
|
||||||
|
{{ domain }}
|
||||||
|
<button class="btn btn-sm btn-info ml-2" ng-if="$first" ng-click="useMyDelegationD()">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="pleaseWaitNewDelegation"></span>
|
||||||
|
Utiliser mon domaine
|
||||||
|
</button>
|
||||||
|
</h4>
|
||||||
|
|
||||||
<table class="table table-striped table-hover" ng-controller="NSDomainsController">
|
<table class="table table-striped table-hover" ng-controller="NSDomainsController">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -65,7 +80,7 @@
|
||||||
<tr ng-repeat="rr in domainNS">
|
<tr ng-repeat="rr in domainNS">
|
||||||
<td><span ng-repeat="val in rr.values">{{ val }} </span></td>
|
<td><span ng-repeat="val in rr.values">{{ val }} </span></td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge badge-pill badge-secondary">Not implemented yet</span>
|
<!--span class="badge badge-pill badge-secondary">Not implemented yet</span-->
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-warning" ng-click="updateNS(domain, rr)">Modifier</button>
|
<button class="btn btn-warning" ng-click="updateNS(domain, rr)">Modifier</button>
|
||||||
|
@ -180,7 +195,31 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-if="student.delegated_domain">
|
||||||
|
<h4 class="text-muted">
|
||||||
|
{{ student.delegated_domain }}
|
||||||
|
<button class="btn btn-sm btn-danger ml-2" ng-if="student.delegated_domain" ng-click="delMyDomainDelegatedD()">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="pleaseWaitNewDelegation"></span>
|
||||||
|
Ne plus utiliser mon domaine
|
||||||
|
</button>
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
Vous avez choisi d'utiliser votre propre domaine pour réaliser la délégation.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
L'interface de maatma ne vous est plus utile, car pour réaliser la délégation, vous devez passer par l'interface de votre bureau d'enregistrement.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Pour rappel, voici les enregistrements à rajouter :
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
;; Delegation {{ student.delegated_domain }} to the given name server
|
||||||
|
{{ student.delegated_domain }} 300 IN NS ns.{{ student.delegated_domain }}
|
||||||
|
|
||||||
|
;; GLUE record to serve along with the previous record
|
||||||
|
ns.{{ student.delegated_domain }} 300 IN AAAA [your NS ip]
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal" id="AssocModal" tabindex="-1" role="dialog">
|
<div class="modal" id="AssocModal" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
|
@ -224,6 +263,85 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" id="AssocMyDomainModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Utiliser mon domaine</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>
|
||||||
|
Si tu possèdes ton propre domaine, tu peux l'utiliser à la place du domaine qui t'es proposé ici !
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Pour cela, rien de plus simple. Choisis un sous-domaine de ton choix, dans ton domaine (par exemple <code>adlin.nemunai.re</code>) :
|
||||||
|
</p>
|
||||||
|
<form class="ml-2 mr-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="mysubdomain">Ton sous-domaine :</label>
|
||||||
|
<input class="form-control" id="mysubdomain" ng-model="assoc.cname" autofocus>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p>
|
||||||
|
Ensuite, dans ta zone DNS, ajoute un alias pointant vers le domaine sous <code>adlin20xx.p0m.fr</code> qui t'es proposé :
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
{{ assoc.cname }} 300 IN CNAME {{ assoc.domain }}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
||||||
|
<button type="button" class="btn btn-primary" ng-disabled="!assoc.cname" ng-click="newMyDomainAssociationD(assoc)">Ok, c'est fait</button>
|
||||||
|
<button type="button" class="btn btn-danger" ng-show="student.associated_domain" ng-click="delMyDomainAssociationD(assoc)">Supprimer l'association enregistrée</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" id="DelegateMyDomainModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Utiliser mon domaine comme délégation</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>
|
||||||
|
Tu peux utiliser ton propre domaine à la place du domaine qui t'es proposé ici !
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Commence par choisir un sous-domaine de ton choix (différent de l'association ci-dessus), dans ton domaine (par exemple <code>adlin.nemunai.re</code>) :
|
||||||
|
</p>
|
||||||
|
<form class="ml-2 mr-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="mysubdomaind">Ton sous-domaine :</label>
|
||||||
|
<input class="form-control" id="mysubdomaind" ng-model="assoc.ns" autofocus>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p>
|
||||||
|
Ensuite, configure ta zone DNS, pour réaliser la délégation. L'interface
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
;; Delegation {{ assoc.ns }} to the given name server
|
||||||
|
{{ assoc.ns }} 300 IN NS ns.{{ assoc.ns }}
|
||||||
|
|
||||||
|
;; GLUE record to serve along with the previous record
|
||||||
|
ns.{{ assoc.ns }} 300 IN AAAA [your NS ip]
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
||||||
|
<button type="button" class="btn btn-primary" ng-disabled="!assoc.ns" ng-click="newMyDomainDelegationD(assoc)">Ok, c'est fait</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal" id="NSModal" tabindex="-1" role="dialog">
|
<div class="modal" id="NSModal" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
|
@ -10,26 +10,31 @@
|
||||||
Connectez-vous !
|
Connectez-vous !
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<h2 ng-if="isLogged">
|
<h2 ng-if="isLogged" class="mb-4">
|
||||||
Qu'allons-nous faire aujourd'hui ? <a href="/dashboard/{{ isLogged.login }}" class="text-muted">Voir l'avancement...</a>
|
Qu'allons-nous faire aujourd'hui ? <a href="/dashboard/{{ isLogged.login }}" class="text-muted">Voir l'avancement...</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div ng-controller="ProgressionController" class="row" ng-if="isLogged">
|
<div ng-controller="ProgressionController" ng-if="isLogged">
|
||||||
<div class="col">
|
<div ng-repeat="(tutoid,tuto) in tuto_progress" class="mb-2 row">
|
||||||
<strong>TP 2 </strong>
|
<strong class="col-auto mr-2">TP {{tutoid+1}}</strong>
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[100], 'badge-danger': !mychallenges[100]}">HTTP</span>
|
<div class="col">
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[101], 'badge-danger': !mychallenges[101]}">HTTPS</span>
|
<div class="ml-1 align-items-center" ng-class="{'d-inline-block': !mychallenges[ch] || !mychallenges[ch].error || mychallenges[ch].error == 'OK', 'd-flex': !(!mychallenges[ch] || !mychallenges[ch].error || mychallenges[ch].error == 'OK')}" ng-repeat="(ch,t) in tuto_progress[tutoid]">
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[102], 'badge-danger': !mychallenges[102]}">DNS</span>
|
<span class="badge mr-1" ng-class="{'badge-success': mychallenges[ch] && (!mychallenges[ch].error || mychallenges[ch].error == 'OK'), 'badge-warning': mychallenges[ch] && mychallenges[ch].error && mychallenges[ch].error != 'OK', 'badge-danger': !mychallenges[ch] || !mychallenges[ch].time}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[103], 'badge-danger': !mychallenges[103]}">Matrix</span>
|
<pre class="ml-1 mb-0" ng-show="mychallenges[ch] && mychallenges[ch].error && mychallenges[ch].error != 'OK'">{{ mychallenges[ch].error }}</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
</div>
|
||||||
<strong>TP 3 </strong>
|
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[200], 'badge-danger': !mychallenges[200]}">HTTP</span>
|
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[201], 'badge-danger': !mychallenges[201]}">HTTPS</span>
|
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[202], 'badge-danger': !mychallenges[202]}">DNS</span>
|
|
||||||
<span class="badge" ng-class="{'badge-success': mychallenges[203], 'badge-danger': !mychallenges[203]}">Matrix</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2 class="mt-4 mb-2" ng-if="isLogged">
|
||||||
|
Besoin d'aide ?
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p ng-if="isLogged">
|
||||||
|
Rejoignez-nous sur notre canal
|
||||||
|
<a href="https://matrix.to/#/#maatma:nemunai.re" target="_blank"><img src="https://matrix.to/images/matrix-logo-3303998978.svg" alt="Rejoingnez-nous sur notre canal Matrix"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-deck">
|
<div class="card-deck">
|
||||||
|
|
|
@ -6,22 +6,47 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>Token</th>
|
<th>
|
||||||
<th>Dernière utilisation</th>
|
Token
|
||||||
<th>Clef publique</th>
|
<span class="badge badge-pill badge-dark" title="Utilisez l'un de vos tokens pour connecter votre machine virtuelle à un réseau IPv6 routable sur Internet. Ce token vous sera demandé au premier démarrage de la VM.">?</span>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Suffix
|
||||||
|
<span class="badge badge-pill badge-dark" title="Si vous souhaitez pouvoir utiliser plusieurs tunnels en parallèle, tout en bénéficiant de la mise en place automatique du tunnel au démarrage, vous allez devoir vous définir un suffix. Il s'agit de la dernière partie de l'adresse qui sera attribué à votre machine.">?</span>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Dernière utilisation
|
||||||
|
<span class="badge badge-pill badge-dark" title="Si vous constatez une utilisation frauduleuse, révoquez votre tunnel.">?</span>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Clef publique
|
||||||
|
<span class="badge badge-pill badge-dark" title="La clef publique est automatiquement envoyée par votre machine virtuelle lorsqu'elle valide le token. C'est une clef cryptographique qui est utiliser pour chiffrer le canal VPN utilisé.">?</span>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="tunnel in tunnels" ng-class="{'bg-success': tunnel.dump}">
|
<tr ng-repeat="tunnel in tunnels" ng-class="{'bg-success': tunnel.dump}">
|
||||||
<td>
|
<td>
|
||||||
<span ng-if="tunnel.Dump" class="text-bold text-success">></span>
|
<span ng-if="tunnel.Dump" title="Tunnel actif (voir information ci-dessous)" class="font-weight-bold text-success">></span>
|
||||||
<span ng-if="!tunnel.Dump">❌</span>
|
<span ng-if="!tunnel.Dump" title="Tunnel inactif">❌</span>
|
||||||
</td>
|
</td>
|
||||||
<td><code>{{ tunnel.TokenText }}</code></td>
|
<td><code>{{ tunnel.TokenText }}</code></td>
|
||||||
|
<td ng-if="tunnel.edit">
|
||||||
|
<input class="form-control" ng-model="tunnel.newData.SuffixIP" placeholder="Suffixe d'IP par défaut" autofocus integer>
|
||||||
|
</td>
|
||||||
|
<td ng-if="!tunnel.edit && tunnel.SuffixIP">{{ tunnel.SuffixIP }}</td>
|
||||||
|
<td ng-if="!tunnel.edit && !tunnel.SuffixIP">Par défaut</td>
|
||||||
<td>{{ tunnel.Time | date:"medium" }}<span ng-if="tunnel.Version"> (VM TP {{ tunnel.Version }})</span></td>
|
<td>{{ tunnel.Time | date:"medium" }}<span ng-if="tunnel.Version"> (VM TP {{ tunnel.Version }})</span></td>
|
||||||
<td><code ng-show="tunnel.PubKey">{{ tunnel.PubKey }}</code><span ng-show="!tunnel.PubKey">(none)</span></td>
|
<td><code class="text-truncate" title="{{ tunnel.PubKey }}" ng-show="tunnel.PubKey">{{ tunnel.PubKey }}</code><span ng-show="!tunnel.PubKey">(none)</span></td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-sm btn-danger" ng-click="dropTunnel(tunnel)" disabled>
|
<button class="btn btn-sm btn-info" ng-click="editTunnel(tunnel)" ng-if="!tunnel.edit">
|
||||||
|
Éditer
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-success" ng-click="updateTunnel(tunnel)" ng-if="tunnel.edit">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="tunnel.pleaseWaitUpdate"></span>
|
||||||
|
Enregistrer
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-danger" ng-click="dropTunnel(tunnel)">
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="tunnel.pleaseWaitDrop"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="tunnel.pleaseWaitDrop"></span>
|
||||||
Révoquer
|
Révoquer
|
||||||
</button>
|
</button>
|
||||||
|
@ -42,7 +67,10 @@
|
||||||
|
|
||||||
<div class="card-deck mb-4">
|
<div class="card-deck mb-4">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h4 class="card-header">Paramètres du tunnel</h4>
|
<h4 class="card-header">
|
||||||
|
Paramètres du tunnel
|
||||||
|
<span class="float-right badge badge-pill badge-dark" title="Ce cadre vous montre les informations sur le routeur hébergeant le tunnel chez Maatma. Vérifiez notamment que vous avez les mêmes paramètres dans vos machines.">?</span>
|
||||||
|
</h4>
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
<li class="list-group-item"><strong>Statut :</strong> {{ wginfo.status }}</li>
|
<li class="list-group-item"><strong>Statut :</strong> {{ wginfo.status }}</li>
|
||||||
<li class="list-group-item"><strong>Clef publique du serveur :</strong> <code>{{ wginfo.srv_pubkey }}</code></li>
|
<li class="list-group-item"><strong>Clef publique du serveur :</strong> <code>{{ wginfo.srv_pubkey }}</code></li>
|
||||||
|
@ -51,10 +79,11 @@
|
||||||
<li class="list-group-item"><strong>Gateway/passerelle IPv6 :</strong> {{ wginfo.srv_gw6 }}</li>
|
<li class="list-group-item"><strong>Gateway/passerelle IPv6 :</strong> {{ wginfo.srv_gw6 }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="card" ng-repeat="tunnel in tunnels" ng-if="tunnel.Dump">
|
<div class="card mb-2" ng-repeat="tunnel in tunnels" ng-if="tunnel.Dump">
|
||||||
<h4 class="card-header">
|
<h4 class="card-header">
|
||||||
État de mon tunnel
|
État de mon tunnel
|
||||||
<span class="badge" ng-class="{'badge-success': PING && PING < 90, 'badge-danger': !PING || PING >= 90}" title="{{ PING_time }}">💻</span>
|
<span class="badge" ng-if="$first" ng-class="{'badge-success': PING && PING < 90, 'badge-danger': !PING || PING >= 90}" title="{{ PING_time }}">💻</span>
|
||||||
|
<span class="float-right badge badge-pill badge-dark" title="Voici les informations concernant l'un de vos tunnels actuellement actif (pas forcément connecté, reportez-vous à l'icône pour savoir cela).">?</span>
|
||||||
</h4>
|
</h4>
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
<li class="list-group-item"><strong>Clef publique pair :</strong> <code>{{ tunnel.Dump.PubKey }}</code></li>
|
<li class="list-group-item"><strong>Clef publique pair :</strong> <code>{{ tunnel.Dump.PubKey }}</code></li>
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
@ -11,12 +12,12 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.GET("/api/ips", apiHandler(showIPs))
|
router.GET("/api/ips", apiHandler(showIPs))
|
||||||
router.GET("/api/students/:sid/ips", apiHandler(studentHandler(func(student adlin.Student, body []byte) (interface{}, error) {
|
router.GET("/api/students/:sid/ips", apiHandler(studentHandler(func(student *adlin.Student, body []byte) (interface{}, error) {
|
||||||
return getStudentIPs(student), nil
|
return getStudentIPs(student), nil
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
func IPSuffix(s adlin.Student, network net.IPNet) net.IP {
|
func IPSuffix(s *adlin.Student, network net.IPNet) net.IP {
|
||||||
ipshift := s.Id*4 + 10
|
ipshift := s.Id*4 + 10
|
||||||
|
|
||||||
myIP := network.IP
|
myIP := network.IP
|
||||||
|
@ -54,13 +55,32 @@ func showIPs(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStudentIPs(student adlin.Student) (r map[string]string) {
|
func GetStudentTunnelIPs(student *adlin.Student) (ips []string) {
|
||||||
|
if ts, err := student.GetActivesTunnels(); err != nil || len(ts) == 0 || ts[0].SuffixIP == 0 {
|
||||||
|
ips = append(ips, adlin.StudentIP(student.Id).String()+"1")
|
||||||
|
} else {
|
||||||
|
for _, t := range ts {
|
||||||
|
ips = append(ips, t.GetStudentIP())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStudentIPs(student *adlin.Student) (r map[string]string) {
|
||||||
r = make(map[string]string)
|
r = make(map[string]string)
|
||||||
|
|
||||||
r["vlan0"] = IPSuffix(student, net.IPNet{net.ParseIP("172.23.0.0"), net.CIDRMask(17, 32)}).String()
|
r["vlan0"] = IPSuffix(student, net.IPNet{net.ParseIP("172.23.0.0"), net.CIDRMask(17, 32)}).String()
|
||||||
r["wg0"] = IPSuffix(student, net.IPNet{net.ParseIP("172.17.0.0"), net.CIDRMask(16, 32)}).String()
|
r["wg0"] = IPSuffix(student, net.IPNet{net.ParseIP("172.17.0.0"), net.CIDRMask(16, 32)}).String()
|
||||||
r["vlan7"] = IPSuffix(student, net.IPNet{net.ParseIP("172.23.142.0"), net.CIDRMask(23, 32)}).String()
|
r["vlan7"] = IPSuffix(student, net.IPNet{net.ParseIP("172.23.142.0"), net.CIDRMask(23, 32)}).String()
|
||||||
r["wg"] = adlin.StudentIP(student.Id).String()
|
|
||||||
|
for d, ip := range GetStudentTunnelIPs(student) {
|
||||||
|
key := "wg"
|
||||||
|
if d > 0 {
|
||||||
|
key += strconv.Itoa(d)
|
||||||
|
}
|
||||||
|
r[key] = ip
|
||||||
|
}
|
||||||
|
|
||||||
r["adn"] = student.MyAssociatedDomain()
|
r["adn"] = student.MyAssociatedDomain()
|
||||||
r["ddn"] = student.MyDelegatedDomain()
|
r["ddn"] = student.MyDelegatedDomain()
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,13 @@ var PongSecret = "felixfixit"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.GET("/api/students/:sid/ping", apiHandler(studentHandler(lastPing)))
|
router.GET("/api/students/:sid/ping", apiHandler(studentHandler(lastPing)))
|
||||||
router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student adlin.Student, body []byte) (interface{}, error) {
|
router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student *adlin.Student, body []byte) (interface{}, error) {
|
||||||
return student.LastPongs()
|
return student.LastPongs()
|
||||||
})))
|
})))
|
||||||
router.POST("/api/students/:sid/pong", apiHandler(studentHandler(stdPong), sslOnly))
|
router.POST("/api/students/:sid/pong", apiHandler(studentHandler(stdPong), sslOnly))
|
||||||
}
|
}
|
||||||
|
|
||||||
func lastPing(student adlin.Student, body []byte) (interface{}, error) {
|
func lastPing(student *adlin.Student, body []byte) (interface{}, error) {
|
||||||
if pongs, err := student.LastPongs(); err != nil {
|
if pongs, err := student.LastPongs(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if len(pongs) <= 0 {
|
} else if len(pongs) <= 0 {
|
||||||
|
@ -27,7 +27,7 @@ func lastPing(student adlin.Student, body []byte) (interface{}, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stdPong(student adlin.Student, body []byte) (interface{}, error) {
|
func stdPong(student *adlin.Student, body []byte) (interface{}, error) {
|
||||||
var gt givenToken
|
var gt givenToken
|
||||||
if err := json.Unmarshal(body, >); err != nil {
|
if err := json.Unmarshal(body, >); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -47,7 +47,7 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasSSHKeys(student adlin.Student, body []byte) (interface{}, error) {
|
func hasSSHKeys(student *adlin.Student, body []byte) (interface{}, error) {
|
||||||
if keys, err := student.GetKeys(); err != nil {
|
if keys, err := student.GetKeys(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,7 +141,7 @@ func dumpAuthorizedKeysFile(w io.Writer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dumpStdAuthorizedKeysFile(s adlin.Student, w io.Writer) {
|
func dumpStdAuthorizedKeysFile(s *adlin.Student, w io.Writer) {
|
||||||
seen := map[string]interface{}{}
|
seen := map[string]interface{}{}
|
||||||
|
|
||||||
if keys, _ := s.GetKeys(); keys != nil {
|
if keys, _ := s.GetKeys(); keys != nil {
|
||||||
|
|
|
@ -18,19 +18,19 @@ func init() {
|
||||||
if stds, err := adlin.GetStudents(); err != nil {
|
if stds, err := adlin.GetStudents(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
ret := map[string]map[string]adlin.UnlockedChallenge{}
|
ret := map[string]map[string]*adlin.UnlockedChallenge{}
|
||||||
for _, std := range stds {
|
for _, std := range stds {
|
||||||
if sts, err := std.GetStates(); err == nil {
|
if sts, err := std.GetStates(); err == nil {
|
||||||
ret[std.Login] = map[string]adlin.UnlockedChallenge{}
|
ret[std.Login] = map[string]*adlin.UnlockedChallenge{}
|
||||||
|
|
||||||
for _, s := range sts {
|
for _, s := range sts {
|
||||||
ret[std.Login][fmt.Sprintf("%d", s.Challenge)] = s
|
ret[std.Login][fmt.Sprintf("%d", s.Challenge)] = s
|
||||||
}
|
}
|
||||||
|
|
||||||
if pongs, err := std.LastPongs(); err == nil && len(pongs) > 0 {
|
if pongs, err := std.LastPongs(); err == nil && len(pongs) > 0 {
|
||||||
ret[std.Login]["ping"] = adlin.UnlockedChallenge{
|
ret[std.Login]["ping"] = &adlin.UnlockedChallenge{
|
||||||
IdStudent: std.Id,
|
IdStudent: std.Id,
|
||||||
Time: pongs[0].Date,
|
Time: &pongs[0].Date,
|
||||||
Value: pongs[0].State,
|
Value: pongs[0].State,
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -47,17 +47,17 @@ func init() {
|
||||||
}))
|
}))
|
||||||
router.POST("/api/students/", remoteValidatorHandler(apiHandler(createStudent)))
|
router.POST("/api/students/", remoteValidatorHandler(apiHandler(createStudent)))
|
||||||
router.GET("/api/students/:sid/", apiHandler(studentHandler(
|
router.GET("/api/students/:sid/", apiHandler(studentHandler(
|
||||||
func(std adlin.Student, _ []byte) (interface{}, error) {
|
func(std *adlin.Student, _ []byte) (interface{}, error) {
|
||||||
return std, nil
|
return std, nil
|
||||||
})))
|
})))
|
||||||
router.PUT("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(updateStudent))))
|
router.PUT("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(updateStudent))))
|
||||||
router.DELETE("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(
|
router.DELETE("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(
|
||||||
func(std adlin.Student, _ []byte) (interface{}, error) {
|
func(std *adlin.Student, _ []byte) (interface{}, error) {
|
||||||
return std.Delete()
|
return std.Delete()
|
||||||
}))))
|
}))))
|
||||||
router.GET("/api/students/:sid/progress", apiHandler(studentHandler(
|
router.GET("/api/students/:sid/progress", apiHandler(studentHandler(
|
||||||
func(std adlin.Student, _ []byte) (interface{}, error) {
|
func(std *adlin.Student, _ []byte) (interface{}, error) {
|
||||||
ret := map[string]adlin.UnlockedChallenge{}
|
ret := map[string]*adlin.UnlockedChallenge{}
|
||||||
|
|
||||||
if sts, err := std.GetStates(); err == nil {
|
if sts, err := std.GetStates(); err == nil {
|
||||||
for _, s := range sts {
|
for _, s := range sts {
|
||||||
|
@ -65,6 +65,20 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cerrors, err := std.GetChallengeErrors(); err == nil {
|
||||||
|
for _, cerr := range cerrors {
|
||||||
|
if _, ok := ret[fmt.Sprintf("%d", cerr.Challenge)]; ok {
|
||||||
|
ret[fmt.Sprintf("%d", cerr.Challenge)].Error = cerr.Error
|
||||||
|
ret[fmt.Sprintf("%d", cerr.Challenge)].LastCheck = &cerr.Time
|
||||||
|
} else {
|
||||||
|
ret[fmt.Sprintf("%d", cerr.Challenge)] = &adlin.UnlockedChallenge{
|
||||||
|
LastCheck: &cerr.Time,
|
||||||
|
Error: cerr.Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -82,7 +96,7 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var exist adlin.Student
|
var exist *adlin.Student
|
||||||
if exist, err = adlin.GetStudentByLogin(strings.TrimSpace(std.Login)); err != nil {
|
if exist, err = adlin.GetStudentByLogin(strings.TrimSpace(std.Login)); err != nil {
|
||||||
if exist, err = adlin.NewStudent(strings.TrimSpace(std.Login)); err != nil {
|
if exist, err = adlin.NewStudent(strings.TrimSpace(std.Login)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -96,8 +110,8 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return exist, nil
|
return exist, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStudent(current adlin.Student, body []byte) (interface{}, error) {
|
func updateStudent(current *adlin.Student, body []byte) (interface{}, error) {
|
||||||
var new adlin.Student
|
new := &adlin.Student{}
|
||||||
if err := json.Unmarshal(body, &new); err != nil {
|
if err := json.Unmarshal(body, &new); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,20 +27,22 @@ func init() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
router.GET("/api/wg/", apiAuthHandler(showWgTunnel))
|
router.GET("/api/wg/", apiAuthHandler(showWgTunnel))
|
||||||
router.GET("/api/wginfo", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.GET("/api/wginfo", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return getTunnelInfo(student.Id), nil
|
return getTunnelInfo(student.Id), nil
|
||||||
}))
|
}))
|
||||||
router.POST("/api/wg/", apiAuthHandler(genWgToken))
|
router.POST("/api/wg/", apiAuthHandler(genWgToken))
|
||||||
router.GET("/api/wg/:token", getWgTunnelInfo)
|
router.GET("/api/wg/:token", getWgTunnelInfo)
|
||||||
router.POST("/api/wg/:token", getWgTunnelInfo)
|
router.POST("/api/wg/:token", getWgTunnelInfo)
|
||||||
|
router.PUT("/api/wg/:token", apiAuthHandler(updateWgTunnel))
|
||||||
|
router.DELETE("/api/wg/:token", apiAuthHandler(deleteWgTunnel))
|
||||||
}
|
}
|
||||||
|
|
||||||
func showWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
func showWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
// Get tunnels assigned to the student
|
// Get tunnels assigned to the student
|
||||||
return student.GetTunnelTokens()
|
return student.GetTunnelTokens()
|
||||||
}
|
}
|
||||||
|
|
||||||
func genWgToken(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
func genWgToken(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
// Generate a token to access related wg info
|
// Generate a token to access related wg info
|
||||||
return student.NewTunnelToken(0)
|
return student.NewTunnelToken(0)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +63,7 @@ func getTunnelInfo(student int64) TunnelInfo {
|
||||||
SrvPubKey: srv_pubkey,
|
SrvPubKey: srv_pubkey,
|
||||||
SrvPort: 42912,
|
SrvPort: 42912,
|
||||||
CltIPv6: adlin.StudentIP(student),
|
CltIPv6: adlin.StudentIP(student),
|
||||||
CltRange: 80,
|
CltRange: adlin.StdNetmask,
|
||||||
SrvGW6: "2a01:e0a:2b:2252::1",
|
SrvGW6: "2a01:e0a:2b:2252::1",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,11 +123,16 @@ func getWgTunnelInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Param
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 is considered default for suffix, apply default now
|
||||||
|
if token.SuffixIP <= 0 {
|
||||||
|
token.SuffixIP = 1
|
||||||
|
}
|
||||||
|
|
||||||
syncWgConf()
|
syncWgConf()
|
||||||
|
|
||||||
tinfo := getTunnelInfo(token.IdStudent)
|
tinfo := getTunnelInfo(token.IdStudent)
|
||||||
|
|
||||||
var student adlin.Student
|
var student *adlin.Student
|
||||||
student, err = adlin.GetStudent(int(token.IdStudent))
|
student, err = adlin.GetStudent(int(token.IdStudent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
|
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
|
||||||
|
@ -138,11 +145,58 @@ PublicKey = %s
|
||||||
Endpoint = %s:%d
|
Endpoint = %s:%d
|
||||||
AllowedIPs = ::/0
|
AllowedIPs = ::/0
|
||||||
PersistentKeepalive = 5
|
PersistentKeepalive = 5
|
||||||
# MyIPv6=%s1/%d
|
# MyIPv6=%s%x/%d
|
||||||
# MyNetwork=%s/%d
|
# MyNetwork=%s/%d
|
||||||
# GWIPv6=%s
|
# GWIPv6=%s
|
||||||
# MyLogin=%s
|
# MyLogin=%s
|
||||||
`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6, student.Login)))
|
`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, token.SuffixIP, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6, student.Login)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
|
token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token")))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if token.IdStudent != student.Id {
|
||||||
|
return nil, fmt.Errorf("Unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
var newToken adlin.TunnelToken
|
||||||
|
if err := json.Unmarshal(body, &newToken); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token.TokenText = newToken.TokenText
|
||||||
|
token.PubKey = newToken.PubKey
|
||||||
|
token.SuffixIP = newToken.SuffixIP
|
||||||
|
|
||||||
|
if _, err = token.Update(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
syncWgConf()
|
||||||
|
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
|
token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token")))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if token.IdStudent != student.Id {
|
||||||
|
return nil, fmt.Errorf("Unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = token.Delete(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
syncWgConf()
|
||||||
|
|
||||||
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenWGConfig(w io.Writer) error {
|
func GenWGConfig(w io.Writer) error {
|
||||||
|
|
16
tuto2.yml
16
tuto2.yml
|
@ -1,9 +1,9 @@
|
||||||
kernel:
|
kernel:
|
||||||
image: linuxkit/kernel:4.19.104
|
image: linuxkit/kernel:4.19.121
|
||||||
cmdline: "console=tty0 console=ttyS0 root=/dev/sda1 root=/dev/sr0 adlin.format=/dev/sda quiet"
|
cmdline: "console=tty0 console=ttyS0 root=/dev/sda1 root=/dev/sr0 adlin.format=/dev/sda quiet"
|
||||||
|
|
||||||
init:
|
init:
|
||||||
- nemunaire/adlin-tuto2:41e341472a4a1b27dcf61c7d364f1f0a5f76fbe7
|
- nemunaire/adlin-tuto2:a68d5f224331628dc525edf383ec7429dfe001b0
|
||||||
|
|
||||||
files:
|
files:
|
||||||
- path: etc/hostname
|
- path: etc/hostname
|
||||||
|
@ -15,7 +15,7 @@ files:
|
||||||
|
|
||||||
- path: etc/resolv.conf
|
- path: etc/resolv.conf
|
||||||
contents: |
|
contents: |
|
||||||
nameserver 9.9.9.9
|
nameserver 9.9.9.10
|
||||||
nameserver 1.1.1.1
|
nameserver 1.1.1.1
|
||||||
uid: 0
|
uid: 0
|
||||||
gid: 0
|
gid: 0
|
||||||
|
@ -154,6 +154,14 @@ files:
|
||||||
/bin/ip -6 route del default
|
/bin/ip -6 route del default
|
||||||
/bin/ip -6 route add default via $(sed 's/^.*GWIPv6=//p;d' etc/wireguard/adlin.conf) pref high
|
/bin/ip -6 route add default via $(sed 's/^.*GWIPv6=//p;d' etc/wireguard/adlin.conf) pref high
|
||||||
|
|
||||||
|
# Download intermediate fixes
|
||||||
|
curl -s -f -H "X-ADLIN-time: $(stat -c %Y /boot)" https://adlin.nemunai.re/fix-vm2 | sh
|
||||||
|
|
||||||
|
# Retrieve ssh keys
|
||||||
|
mkdir -p root/.ssh/
|
||||||
|
[ -f root/.ssh/authorized_keys ] || /usr/sbin/chroot . /usr/bin/curl -s -f https://cri.epita.fr/$(sed 's/^.*MyLogin=//p;d' etc/wireguard/adlin.conf).keys > root/.ssh/authorized_keys
|
||||||
|
[ -f etc/ssh/ssh_host_rsa_key ] || /usr/sbin/chroot . ssh-keygen -A
|
||||||
|
|
||||||
# To the user
|
# To the user
|
||||||
exec /usr/sbin/chroot . "${INITP}"
|
exec /usr/sbin/chroot . "${INITP}"
|
||||||
uid: 0
|
uid: 0
|
||||||
|
@ -170,7 +178,7 @@ files:
|
||||||
|
|
||||||
- path: etc/shadow
|
- path: etc/shadow
|
||||||
contents: |
|
contents: |
|
||||||
root:$6$QNuPvO59Xk4UO3le$3P0V2ef6dHlKgO1FHsKcPPgOvL.YeCOPFqfIVTtpYn5eEn3xkgGYeM1RMCQ9l/eTc6rRc.l.WeRe1iJVznVGj/:18336:0:99999:7:::
|
root:$6$dQXVLB.662ob0XJL$wRhh73Q.Z3mBRHhM0rSw96dE0bOFykfIXa2Z2ncu6WVSOpFLdv5J6Br9AHhalO4wwG3xgPqqhvCdEMdroR2r50:18336:0:99999:7:::
|
||||||
daemon:*:18316:0:99999:7:::
|
daemon:*:18316:0:99999:7:::
|
||||||
bin:*:18316:0:99999:7:::
|
bin:*:18316:0:99999:7:::
|
||||||
sys:*:18316:0:99999:7:::
|
sys:*:18316:0:99999:7:::
|
||||||
|
|
|
@ -20,7 +20,9 @@ Ansible est une solution de gestion de configuration. Basé sur
|
||||||
[YAML](http://www.yaml.org/spec/1.2/spec.html), sa principale particularité est
|
[YAML](http://www.yaml.org/spec/1.2/spec.html), sa principale particularité est
|
||||||
de ne pas nécessité de daemon sur les machines qu'il va gérer : tout se fait
|
de ne pas nécessité de daemon sur les machines qu'il va gérer : tout se fait
|
||||||
exclusivement via SSH, à partir de la machine d'un administrateur, possédant
|
exclusivement via SSH, à partir de la machine d'un administrateur, possédant
|
||||||
Ansible.
|
Ansible (ou bien d'un système de gestion de configuration tel qu'[Ansible
|
||||||
|
Tower](https://www.ansible.com/products/tower) ou
|
||||||
|
[AWX](https://github.com/ansible/awx)).
|
||||||
|
|
||||||
Son installation est très simple, car les dépendances sont minimes et l'outil
|
Son installation est très simple, car les dépendances sont minimes et l'outil
|
||||||
n'a pas besoin de base de données pour fonctionner : tout va se faire à partir
|
n'a pas besoin de base de données pour fonctionner : tout va se faire à partir
|
||||||
|
@ -48,7 +50,7 @@ système et des utilisateurs.
|
||||||
Un deuxième playbook est à rendre : `login-x-TP2/vitrine.yml`, celui-ci doit
|
Un deuxième playbook est à rendre : `login-x-TP2/vitrine.yml`, celui-ci doit
|
||||||
permettre de déployer (en parallèle de tous les autres), une page vitrine
|
permettre de déployer (en parallèle de tous les autres), une page vitrine
|
||||||
typique d'une entreprise (cf. la 4e question de cours ;)). Cette page doit être
|
typique d'une entreprise (cf. la 4e question de cours ;)). Cette page doit être
|
||||||
accessible depuis votre domaine <https://login-x.adlin2021.p0m.fr/>.
|
accessible depuis votre domaine <https://login-x.adlin2022.p0m.fr/>.
|
||||||
|
|
||||||
|
|
||||||
Mon première commande
|
Mon première commande
|
||||||
|
@ -85,7 +87,7 @@ Lancez ensuite la commande suivante :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
42sh$ ansible --inventory-file hosts all --module-name ping --user root --ask-pass
|
42sh$ ansible --inventory-file hosts all --module-name ping --user root
|
||||||
192.168.0.106 | SUCCESS => {
|
192.168.0.106 | SUCCESS => {
|
||||||
"changed": false,
|
"changed": false,
|
||||||
"ping": "pong"
|
"ping": "pong"
|
||||||
|
@ -97,6 +99,10 @@ Vous devriez avoir un retour similaire à celui-ci, indiquant simplement que la
|
||||||
connexion a bien été effectuée et que le nécessaire est bien installé sur la
|
connexion a bien été effectuée et que le nécessaire est bien installé sur la
|
||||||
machine distance.
|
machine distance.
|
||||||
|
|
||||||
|
Si votre clef SSH n'a pas été récupérée depuis le CRI, vous pouvez rajouter
|
||||||
|
l'option `--ask-pass`, afin de pouvoir indiquer le mot de passe de connexion au
|
||||||
|
compte, sinon vous obtiendrez une erreur plutôt incrompréhensible.
|
||||||
|
|
||||||
|
|
||||||
### Confort
|
### Confort
|
||||||
|
|
||||||
|
@ -281,7 +287,7 @@ pour davantage de détails et d'exemples.
|
||||||
La configuration de votre serveur SSH laisse à désirer. Corriger les problèmes
|
La configuration de votre serveur SSH laisse à désirer. Corriger les problèmes
|
||||||
énoncés par ces deux articles :
|
énoncés par ces deux articles :
|
||||||
|
|
||||||
- <https://ringyt.wordpress.com/2017/05/23/default-hardened-ssh-server-config/> ;
|
- <https://linuxhandbook.com/ssh-hardening-tips/> ;
|
||||||
- <https://stribika.github.io/2015/01/04/secure-secure-shell.html>.
|
- <https://stribika.github.io/2015/01/04/secure-secure-shell.html>.
|
||||||
|
|
||||||
Mettez en place un *handler* pour relancer votre serveur SSH en cas de
|
Mettez en place un *handler* pour relancer votre serveur SSH en cas de
|
||||||
|
|
|
@ -9,7 +9,7 @@ gestionnaire de configuration.
|
||||||
Après cet échauffement, vous devriez être prêt à créer un *playbook*
|
Après cet échauffement, vous devriez être prêt à créer un *playbook*
|
||||||
dédié à l'installation d'un serveur [Matrix](https://matrix.org/) :
|
dédié à l'installation d'un serveur [Matrix](https://matrix.org/) :
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
Vous connaissez et utilisez sans doute Slack, un service de messagerie
|
Vous connaissez et utilisez sans doute Slack, un service de messagerie
|
||||||
|
@ -34,7 +34,7 @@ dédié lorsque c'est possible (pas de `root` !), droits d'accès et
|
||||||
permissions des répertoires, etc.
|
permissions des répertoires, etc.
|
||||||
|
|
||||||
Profitez des [modules de base de
|
Profitez des [modules de base de
|
||||||
données](http://docs.ansible.com/ansible/latest/list_of_database_modules.html)
|
données](https://docs.ansible.com/ansible/2.9/modules/list_of_database_modules.html)
|
||||||
pour l'initialiser correctement. Et bien entendu de l'ensemble des
|
pour l'initialiser correctement. Et bien entendu de l'ensemble des
|
||||||
modules décrits dans la documentation standard !
|
modules décrits dans la documentation standard !
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ de données. Le *dump* obtenu est à placer dans `/var/backups/`.
|
||||||
## Client de test
|
## Client de test
|
||||||
|
|
||||||
Vous n'êtes pas tenu d'installer un client. Pour vos tests, vous pouvez
|
Vous n'êtes pas tenu d'installer un client. Pour vos tests, vous pouvez
|
||||||
utiliser <https://riot.im/app/>, en changeant l'adresse du serveur Matrix pour
|
utiliser <https://app.element.io/>, en changeant l'adresse du serveur Matrix pour
|
||||||
votre sous-domaine dédié à Matrix (normalement
|
votre sous-domaine dédié à Matrix (normalement
|
||||||
<https://matrix.login-x.srs.p0m.fr/>). (Conservez le serveur d'identité à
|
<https://matrix.login-x.srs.p0m.fr/>). (Conservez le serveur d'identité à
|
||||||
<https://vector.im>).
|
<https://vector.im>).
|
||||||
|
@ -68,7 +68,7 @@ votre sous-domaine dédié à Matrix (normalement
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
Pour valider l'installation de votre serveur, rejoignez le canal
|
Pour valider l'installation de votre serveur, rejoignez le canal
|
||||||
`#adlin:nemunai.re` et envoyez un message « Ping ! » à `@nemubot:nemunai.re`
|
`#adlin:nemunai.re` et envoyez un message « Ping ! » pour signaler votre
|
||||||
qui s'occupera de valider ce pallier.
|
présence.
|
||||||
|
|
||||||
Vous devriez également pouvoir tester entre-vous.
|
Vous devriez également pouvoir tester entre-vous.
|
||||||
|
|
|
@ -42,9 +42,10 @@ Tunnel IPv6
|
||||||
Au premier lancement de votre VM, la machine vous demandera d'indiquer un jeton
|
Au premier lancement de votre VM, la machine vous demandera d'indiquer un jeton
|
||||||
afin de mettre en place le tunnel IPv6.
|
afin de mettre en place le tunnel IPv6.
|
||||||
|
|
||||||
Afin d'en obtenir un, rendez-vous sur la page Tunnels et créez un nouveau
|
Afin d'en obtenir un, rendez-vous sur la [page
|
||||||
tunnel. Un jeton de 10 caractères s'affichera alors, c'est celui que vous
|
Tunnels](https://adlin.nemunai.re/maatma/tunnels) et créez un nouveau
|
||||||
devrez recopier dans le terminal (attention à la casse !).
|
tunnel. Un jeton de 10 caractères s'affichera alors, c'est celui que
|
||||||
|
vous devrez recopier dans le terminal (attention à la casse !).
|
||||||
|
|
||||||
|
|
||||||
### Test du tunnel
|
### Test du tunnel
|
||||||
|
|
|
@ -19,3 +19,136 @@ Voici les grandes étapes :
|
||||||
* tester avec `dig @9.9.9.9 ANY login-x.srs.p0m.fr` que votre serveur est bien joignable.
|
* tester avec `dig @9.9.9.9 ANY login-x.srs.p0m.fr` que votre serveur est bien joignable.
|
||||||
|
|
||||||
Un ouvrage de référence, qui répondra à l'intégralité de vos questions et manières d'utiliser votre serveur DNS se trouve à <http://www.zytrax.com/books/dns/>.
|
Un ouvrage de référence, qui répondra à l'intégralité de vos questions et manières d'utiliser votre serveur DNS se trouve à <http://www.zytrax.com/books/dns/>.
|
||||||
|
|
||||||
|
|
||||||
|
## Coller aux spécifications
|
||||||
|
|
||||||
|
Pourquoi une telle complexité apparente pour déléguer une zone DNS ?
|
||||||
|
|
||||||
|
Vous êtes sur le point d'obtenir le contrôle total d'un domaine :
|
||||||
|
`login-x.srs.p0m.fr.`. Cela signifie que vous allez pouvoir décider,
|
||||||
|
depuis votre propre serveur de noms (respectueux des
|
||||||
|
[standards](https://www.ietf.org/rfc/rfc1034.txt)), comment vous allez
|
||||||
|
répondre aux serveurs résolveurs des utilisateurs.
|
||||||
|
|
||||||
|
Le protocole DNS étant décentralisé, mais basé sur une arborescence
|
||||||
|
unique, il est nécessaire que les serveurs faisant autorité sur une
|
||||||
|
zone (`fr.`, `srs.p0m.fr.`, ...) fournissent toutes les informations
|
||||||
|
nécessaire pour que cette délégation fonctionne.
|
||||||
|
|
||||||
|
À cet instant, vous connaissez l'adresse IPv6 routable sur Internet de votre
|
||||||
|
serveur de noms (comme vous n'avez aujourd'hui qu'une seule machine, c'est
|
||||||
|
l'adresse de votre tunnel). Il va donc falloir indiquer à Internet que c'est
|
||||||
|
cette IP qu'il faut contacter, si l'on veut interroger le serveur faisant
|
||||||
|
autorité.
|
||||||
|
|
||||||
|
Concrètement, vous allez devoir vous attribuer un sous-domaine pour votre
|
||||||
|
serveur de noms, car c'est grâce à un enregistrement `NS` figurant dans la zone
|
||||||
|
parente (pour vous, la zone parente c'est `srs.p0m.fr`) que la délégation va se
|
||||||
|
faire.
|
||||||
|
|
||||||
|
Dans le cas basique, on va utiliser les serveurs de quelqu'un d'autre (d'un
|
||||||
|
hébergement spécialisé par exemple, ou celui de son bureau d'enregistrement
|
||||||
|
s'ils proposent ce service). Auquel cas, on fera figurer ce genre
|
||||||
|
d'informations :
|
||||||
|
|
||||||
|
```
|
||||||
|
42sh$ dig @e.ext.nic.fr. NS epita.fr.
|
||||||
|
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 17590
|
||||||
|
;; Flags: qr rd; QUERY: 1; ANSWER: 0; AUTHORITY: 3; ADDITIONAL: 0
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;; epita.fr. IN NS
|
||||||
|
|
||||||
|
;; AUTHORITY SECTION:
|
||||||
|
epita.fr. 172800 IN NS tooty.ionis-it.com.
|
||||||
|
epita.fr. 172800 IN NS kazooie.ionis-it.com.
|
||||||
|
epita.fr. 172800 IN NS banjo.ionis-it.com.
|
||||||
|
|
||||||
|
;; Received 100 B
|
||||||
|
;; Time 2042-12-04 13:42:23 CET
|
||||||
|
;; From 2a00:d78:0:102:193:176:144:22@53(UDP) in 13.9 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
La commande est envoyée spécifiquement aux serveurs de l'AFNIC, faisant
|
||||||
|
autorité pour la zone `fr.`. Les serveurs de l'AFNIC nous indiquent en retour,
|
||||||
|
que `epita.fr.` est déléguée à Ionis, et qu'elle dispose de 3 serveurs faisant
|
||||||
|
autorité.
|
||||||
|
|
||||||
|
Mais dans votre cas, vous hébergez vous-même votre propre serveur au sein de
|
||||||
|
votre zone, vous n'avez pas d'autre domaine à votre disposition. C'est
|
||||||
|
également le cas de Wikipédia :
|
||||||
|
|
||||||
|
```
|
||||||
|
42sh$ dig @d0.org.afilias-nst.org. NS wikipedia.org.
|
||||||
|
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 47396
|
||||||
|
;; Flags: qr rd; QUERY: 1; ANSWER: 0; AUTHORITY: 3; ADDITIONAL: 3
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;; wikipedia.org. IN NS
|
||||||
|
|
||||||
|
;; AUTHORITY SECTION:
|
||||||
|
wikipedia.org. 86400 IN NS ns0.wikimedia.org.
|
||||||
|
wikipedia.org. 86400 IN NS ns2.wikimedia.org.
|
||||||
|
wikipedia.org. 86400 IN NS ns1.wikimedia.org.
|
||||||
|
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
|
||||||
|
On voit ici que pour résoudre les sous-domaines de `wikipedia.org.`, il faut
|
||||||
|
demander à `nsX.wikimedia.org.`. Mais comment obtenir alors, l'adresse de ces
|
||||||
|
serveurs de noms, puisque l'on ne sait pas où les contacter ...!
|
||||||
|
|
||||||
|
C'est là que les *GLUE records* entrent en jeu !
|
||||||
|
|
||||||
|
J'ai volontairement tronqué la sortie de la commande précédente, en entier, c'est :
|
||||||
|
|
||||||
|
```
|
||||||
|
42sh$ dig @d0.org.afilias-nst.org. NS wikipedia.org.
|
||||||
|
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 47396
|
||||||
|
;; Flags: qr rd; QUERY: 1; ANSWER: 0; AUTHORITY: 3; ADDITIONAL: 3
|
||||||
|
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;; wikipedia.org. IN NS
|
||||||
|
|
||||||
|
;; AUTHORITY SECTION:
|
||||||
|
wikipedia.org. 86400 IN NS ns0.wikimedia.org.
|
||||||
|
wikipedia.org. 86400 IN NS ns2.wikimedia.org.
|
||||||
|
wikipedia.org. 86400 IN NS ns1.wikimedia.org.
|
||||||
|
|
||||||
|
;; ADDITIONAL SECTION:
|
||||||
|
ns0.wikimedia.org. 86400 IN A 208.80.154.238
|
||||||
|
ns1.wikimedia.org. 86400 IN A 208.80.153.231
|
||||||
|
ns2.wikimedia.org. 86400 IN A 91.198.174.239
|
||||||
|
|
||||||
|
;; Received 143 B
|
||||||
|
;; Time 2042-12-04 13:42:23 CET
|
||||||
|
;; From 2001:500:f::1@53(UDP) in 15.2 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
Afin d'éviter de tourner en rond sans jamais avoir réponse à notre question, en
|
||||||
|
même temps que de nous répondre sur qui sont les serveurs faisant autorité pour
|
||||||
|
la zone `wikipedia.org.`, les serveurs gérant la zone `org.` indiquent
|
||||||
|
également à quelle adresse ont peut les contacter.
|
||||||
|
|
||||||
|
Ce sont les administrateurs de la zone `wikipedia.org.` qui ont indiqué à
|
||||||
|
`org.` quels étaient leurs serveurs de noms. Et ils ont également donné des
|
||||||
|
*GLUE records*, pour permettre à la magie d'opérer.
|
||||||
|
|
||||||
|
À vous maintenant de créer votre zone, en envoyant sur Maatma, le nom de
|
||||||
|
domaine votre serveur de noms, ainsi que le *GLUE record* qui lui correspond.
|
||||||
|
|
||||||
|
|
||||||
|
## DNSSEC (bonus)
|
||||||
|
|
||||||
|
En bonus, vous devriez sécuriser les réponses envoyées par votre serveur DNS.
|
||||||
|
|
||||||
|
Pour ce faire, et toujours parce que l'on se trouve dans le cadre d'une
|
||||||
|
structure arborescente, les clefs publiques permettant de valider les
|
||||||
|
enregistrements d'un domaine en particulier, sont publiées dans la zone
|
||||||
|
parente. C'est pourquoi, vous disposez sur Maatma, d'une interface vous
|
||||||
|
permettant d'indiquer vos clefs publiques.
|
||||||
|
|
||||||
|
De nombreux articles sont disponibles sur Internet pour vous permettre de
|
||||||
|
configurer DNSSEC en fonction du serveur autoritaire que vous aurez choisi. À
|
||||||
|
vous de jouer !
|
||||||
|
|
|
@ -33,7 +33,7 @@ L'image d'installation
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Vous pouvez télécharger l'ISO du TP depuis
|
Vous pouvez télécharger l'ISO du TP depuis
|
||||||
<https://adlin.nemunai.re/ressources/tuto2.iso>.
|
<https://adlin.nemunai.re/resources/tuto2.iso>.
|
||||||
|
|
||||||
Cette image contient un système Debian minimaliste, en partie préinstallé afin
|
Cette image contient un système Debian minimaliste, en partie préinstallé afin
|
||||||
de vous permettre de commencer à travailler sans plus attendre !
|
de vous permettre de commencer à travailler sans plus attendre !
|
||||||
|
@ -261,8 +261,8 @@ machine, afin qu'elle ne teste que le lecteur de CD virtuel.
|
||||||
Connexion
|
Connexion
|
||||||
---------
|
---------
|
||||||
|
|
||||||
La machine ne se connecte pas au réseau toute seule, vous allez devoir l'aider
|
Si la machine ne se connecte pas au réseau toute seule, vous allez devoir
|
||||||
en reproduisant les étapes que nous avons apprises au TP précédent.
|
l'aider en reproduisant les étapes que nous avons apprises au TP précédent.
|
||||||
|
|
||||||
### Requête DHCP
|
### Requête DHCP
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ pouvoir émettre un paquet sur le réseau.
|
||||||
```
|
```
|
||||||
42sh$ ip link
|
42sh$ ip link
|
||||||
1: enp3s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
|
1: enp3s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,18 @@ title: Administration Linux avancée -- TP n^o^ 2
|
||||||
subtitle: "Maatma : l'hébergeur DIY"
|
subtitle: "Maatma : l'hébergeur DIY"
|
||||||
author: Pierre-Olivier *nemunaire* [Mercier]{.smallcaps}
|
author: Pierre-Olivier *nemunaire* [Mercier]{.smallcaps}
|
||||||
institute: EPITA
|
institute: EPITA
|
||||||
date: Lundi 16 mars 2020
|
date: Jeudi 4 mars 2021
|
||||||
abstract: |
|
abstract: |
|
||||||
Durant ce deuxième TP, nous allons apprendre à déployer des services sur un
|
Durant ce deuxième TP, nous allons apprendre à déployer des services sur un
|
||||||
serveur, de manière industrielle !
|
serveur, de manière industrielle !
|
||||||
|
|
||||||
\vspace{1em}
|
\vspace{1em}
|
||||||
|
|
||||||
La partie 4 de ce TP est un projet à rendre à <adlin@nemunai.re> au plus tard
|
La partie 5 de ce TP est un projet à rendre à <adlin@nemunai.re> au plus tard
|
||||||
le **lundi 30 mars 2020 à 00 h 42 du matin**. Consultez la dernière
|
le **jeudi 18 mars 2021 à 12 h 42**. Consultez la dernière
|
||||||
section de chaque partie pour plus d'information sur les éléments à
|
section de chaque partie pour plus d'information sur les éléments à
|
||||||
rendre. Et n'oubliez pas de répondre aux [questions de
|
rendre. Et n'oubliez pas de répondre aux [questions de
|
||||||
cours](https://adlin.nemunai.re/quiz/2).
|
cours](https://adlin.nemunai.re/quiz/9).
|
||||||
|
|
||||||
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
|
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
|
||||||
vous devrez m'envoyer vos rendus signés avec votre clef PGP. Pensez à
|
vous devrez m'envoyer vos rendus signés avec votre clef PGP. Pensez à
|
||||||
|
|
|
@ -20,7 +20,7 @@ reporter dans un fichiers au chapitre suivant !
|
||||||
Ma première vitrine
|
Ma première vitrine
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Sur le domaine `login_x.adlin2021.p0m.fr`, déployez une vitrine d'entreprise
|
Sur le domaine `login-x.adlin2022.p0m.fr`, déployez une vitrine d'entreprise
|
||||||
basique (pas besoin d'un Wordpress, un simple lot de pages HTML fera l'affaire).
|
basique (pas besoin d'un Wordpress, un simple lot de pages HTML fera l'affaire).
|
||||||
|
|
||||||
Vous aurez pour cela besoin d'un serveur web, dont le choix est laissé à votre
|
Vous aurez pour cela besoin d'un serveur web, dont le choix est laissé à votre
|
||||||
|
@ -29,11 +29,13 @@ discrétion.
|
||||||
Vous pouvez utiliser les services de [Let's Encrypt](https://letsencrypt.org/)
|
Vous pouvez utiliser les services de [Let's Encrypt](https://letsencrypt.org/)
|
||||||
pour obtenir un certificat TLS. Compte tenu des limitations imposées, vous ne
|
pour obtenir un certificat TLS. Compte tenu des limitations imposées, vous ne
|
||||||
pourrez pas tous en créer un aujourd'hui, mais n'hésitez pas à retenter un peu
|
pourrez pas tous en créer un aujourd'hui, mais n'hésitez pas à retenter un peu
|
||||||
plus tard dans la semaine.
|
plus tard dans la semaine. Vous pouvez également obtenir vos certificats depuis
|
||||||
|
de nombreux autres services gratuits similaire :
|
||||||
|
[ZeroSSL](https://zerossl.com/), [buypass](https://www.buypass.com/), ...
|
||||||
|
|
||||||
*D'ailleurs, si vous disposez de votre propre nom de domaine et que vous
|
D'ailleurs, si vous disposez de votre propre nom de domaine et que vous
|
||||||
souhaitez l'utiliser pour ce TP, n'hésitez pas à me solliciter pour que je
|
souhaitez l'utiliser pour ce TP, vous pouvez suivre les instructions dans
|
||||||
mette en place les redirections adéquates.*
|
l'interface de Maatma pour pouvoir l'utiliser.
|
||||||
|
|
||||||
|
|
||||||
Une fois votre serveur web configuré et votre vitrine installée, accédez à
|
Une fois votre serveur web configuré et votre vitrine installée, accédez à
|
||||||
|
|
|
@ -7,7 +7,7 @@ Accéder à la machine virtuelle
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
Une fois la machine virtuelle démarrée, vous pouvez vous y connecter en `root`
|
Une fois la machine virtuelle démarrée, vous pouvez vous y connecter en `root`
|
||||||
avec le mot de passe `adlin2021`.
|
avec le mot de passe `adlin2022`.
|
||||||
|
|
||||||
Vous pouvez également démarrer en mode *single user*, mais comme votre disque
|
Vous pouvez également démarrer en mode *single user*, mais comme votre disque
|
||||||
n'est sans doute pas encore utilisable à ce stade, vous ne pourrez pas changer
|
n'est sans doute pas encore utilisable à ce stade, vous ne pourrez pas changer
|
||||||
|
@ -30,6 +30,13 @@ local (sans passer par un NAT), n'oubliez pas de modifier le mot de passe
|
||||||
`root` pour éviter que n'importe qui sur le réseau local (et ayant accès à ce
|
`root` pour éviter que n'importe qui sur le réseau local (et ayant accès à ce
|
||||||
TP), ne rentre sur votre machine.
|
TP), ne rentre sur votre machine.
|
||||||
|
|
||||||
|
Afin de vous faciliter la configuration de la machine par la suite, vos clefs
|
||||||
|
SSH publiques, [déclarées au
|
||||||
|
CRI](https://cri.epita.fr/users/nemunaire/ssh-keys/), sont automatiquement
|
||||||
|
ajoutées à l'utilisateur `root`. Rendez-vous dans l'interface du CRI pour les
|
||||||
|
mettre à jour si besoin. Notez qu'elles ne sont retéléchargées que si le
|
||||||
|
fichier `authorized_keys` n'existe pas.
|
||||||
|
|
||||||
|
|
||||||
### Création d'un utilisateur
|
### Création d'un utilisateur
|
||||||
|
|
||||||
|
|
Reference in a new issue