Pierre-Olivier Mercier
d61f28875e
All checks were successful
continuous-integration/drone/push Build is passing
413 lines
15 KiB
Go
413 lines
15 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
ping "github.com/prometheus-community/pro-bing"
|
|
|
|
"git.nemunai.re/srs/adlin/libadlin"
|
|
)
|
|
|
|
const (
|
|
DEFAULT_RESOLVER = "2a01:e0a:518:830::1"
|
|
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. Taken from miekg/dns
|
|
)
|
|
|
|
var (
|
|
verbose = false
|
|
verbose2 = false
|
|
domainsHostingMap = map[string]string{}
|
|
)
|
|
|
|
// Main
|
|
|
|
func minTunnelVersion(std *adlin.Student, suffixip int) (int, error) {
|
|
tunnels, err := std.GetTunnelTokens()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var minversion int = 2147483647
|
|
for _, tunnel := range tunnels {
|
|
if tunnel.Version == 0 {
|
|
continue
|
|
}
|
|
|
|
if tunnel.Dump != nil && tunnel.Version < minversion && suffixip == tunnel.SuffixIP {
|
|
minversion = tunnel.Version
|
|
}
|
|
}
|
|
|
|
return minversion, nil
|
|
}
|
|
|
|
func studentChecker(std *adlin.Student, also_check_matrix bool, offline bool) {
|
|
tuns, err := std.GetActivesTunnels()
|
|
if err != nil {
|
|
if offline {
|
|
tuns, err = std.GetDefaultTunnels()
|
|
if err != nil {
|
|
if verbose {
|
|
log.Printf("SKip %s as I'm unable to generate default tunnels: %s", std.Login, err.Error())
|
|
}
|
|
return
|
|
}
|
|
} else {
|
|
if verbose2 {
|
|
log.Printf("SKip %s due to error when getting active tunnels: %s", std.Login, err.Error())
|
|
}
|
|
return
|
|
}
|
|
}
|
|
if verbose2 && len(tuns) == 0 {
|
|
log.Printf("%s has no active tunnels: %s", std.Login, err.Error())
|
|
}
|
|
|
|
for _, tun := range tuns {
|
|
stdIP := tun.GetStudentIP()
|
|
if verbose2 {
|
|
log.Printf("Tests %s on %s...", std.Login, stdIP)
|
|
}
|
|
// Check ping
|
|
err = check_ping(stdIP, func(pkt *ping.Packet) {
|
|
tunnel_version, err := minTunnelVersion(std, tun.SuffixIP)
|
|
if verbose {
|
|
log.Printf("%s PONG (on %x); version=%d (%v)\n", std.Login, tun.SuffixIP, tunnel_version, err)
|
|
}
|
|
std.OnPong(true)
|
|
|
|
if !offline && (tunnel_version == 2147483647 || tunnel_version == 0) {
|
|
log.Printf("%s unknown tunnel version: %d skipping tests (%v)", std.Login, tunnel_version, err)
|
|
return
|
|
}
|
|
|
|
// PingResolver
|
|
if has_test(CheckMap[tunnel_version], PingResolver) {
|
|
tmp := strings.Split(stdIP, ":")
|
|
tmp[len(tmp)-1] = "2"
|
|
stdResolverIP := strings.Join(tmp, ":")
|
|
go check_ping(stdResolverIP, func(_ *ping.Packet) {
|
|
if verbose {
|
|
log.Printf("%s resolver PONG", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][PingResolver], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
}, nil)
|
|
}
|
|
|
|
// Firewalled
|
|
if has_test(CheckMap[tunnel_version], Firewalled) {
|
|
if err = check_firewall("tcp", stdIP); err == nil {
|
|
if verbose {
|
|
log.Printf("%s just unlocked firewalled challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][Firewalled], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][Firewalled], err)
|
|
if verbose {
|
|
log.Printf("%s and firewalled: %s\n", std.Login, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
dnsIP := stdIP
|
|
var glueErr error
|
|
// Is GLUE defined?
|
|
if glueIP, err := get_GLUE(std); 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
|
|
}
|
|
|
|
snicheck1 := false
|
|
snicheck1_tested := false
|
|
|
|
// Check DNS
|
|
if addr, err := check_dns_both(std.MyDelegatedDomain(), dnsIP); err == nil {
|
|
if addr == nil {
|
|
dnsAt := ""
|
|
if glueErr != nil {
|
|
dnsAt = " + there is a problem with the GLUE record: " + glueErr.Error()
|
|
}
|
|
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][DNSDelegation], fmt.Errorf("dig @%s %s AAAA: empty response from the server%s", dnsIP, std.MyDelegatedDomain(), dnsAt)); errreg != nil {
|
|
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
|
|
}
|
|
} else if tunnel_version == 3 && dnsIP == stdIP {
|
|
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][DNSDelegation], fmt.Errorf("%s: you shouldn't use DNAT on IPv6 (NAT on IPv6 is bad); instead, point your GLUE record to nsauth directly", std.MyDelegatedDomain())); 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(CheckMap[tunnel_version][DNSDelegation], 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(CheckMap[tunnel_version][HTTPonDelegatedDomain], fmt.Errorf("Unable to perform the test due to GLUE problem: %w", glueErr))
|
|
} else if err := check_http(addr.String(), std.MyDelegatedDomain()); err == nil {
|
|
if tunnel_version == 3 {
|
|
// Try port 80 on miniflux => should not respond if DNAT is correctly configured
|
|
minifluxIP := tun.GetServerIP(6)
|
|
if err := check_http(minifluxIP, std.MyDelegatedDomain()); err == nil {
|
|
if verbose {
|
|
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), "Bad DNAT config")
|
|
}
|
|
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonDelegatedDomain], fmt.Errorf("Your DNAT on IPv6 is badly configured. See HTTP IP result.")); errreg != nil {
|
|
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
|
|
}
|
|
} else {
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonDelegatedDomain], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonDelegatedDomain], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonDelegatedDomain], 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(CheckMap[tunnel_version][HTTPSonDelegatedDomain], fmt.Errorf("Unable to perform the test due to GLUE problem: %w", glueErr))
|
|
} else if err := check_https(std.MyDelegatedDomain()); err == nil {
|
|
snicheck1 = true
|
|
snicheck1_tested = true
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPSonDelegatedDomain], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
} else {
|
|
snicheck1_tested = true
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSonDelegatedDomain], 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 defer contraint)
|
|
if glueErr == nil && also_check_matrix {
|
|
if has_test(CheckMap[tunnel_version], MatrixSrv) {
|
|
// Check Matrix Federation first
|
|
if v, err := check_matrix_federation(std.MyDelegatedDomain()); err == nil {
|
|
if verbose {
|
|
log.Printf("%s just unlocked Matrix federation challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][MatrixSrv], v); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][MatrixSrv], err)
|
|
if verbose {
|
|
log.Printf("%s and Matrix federation: %s\n", std.Login, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check Matrix Client
|
|
if has_test(CheckMap[tunnel_version], MatrixClt) {
|
|
if v, err := check_matrix_client(std.MyDelegatedDomain()); err == nil {
|
|
if verbose {
|
|
log.Printf("%s just unlocked Matrix client challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][MatrixClt], v); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][MatrixClt], err)
|
|
if verbose {
|
|
log.Printf("%s and Matrix client: %s\n", std.Login, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check DNSSEC (only if GLUE Ok)
|
|
if glueErr == nil {
|
|
if err := check_dnssec(std.MyDelegatedDomain(), dnsIP); err == nil {
|
|
if verbose {
|
|
log.Printf("%s just unlocked DNSSEC challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][DNSSEC], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][DNSSEC], err)
|
|
if verbose {
|
|
log.Printf("%s and DNSSEC: %s\n", std.Login, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][DNSDelegation], 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 tunnel_version == 3 {
|
|
// Try port 80 on miniflux => should not respond if DNAT is correctly configured
|
|
minifluxIP := tun.GetServerIP(6)
|
|
if err := check_http(minifluxIP, ""); err == nil {
|
|
if verbose {
|
|
log.Printf("%s and HTTP IP (without DNS): %s\n", std.Login, "Bad DNAT config")
|
|
}
|
|
if errreg := std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonIP], fmt.Errorf("Your DNAT on IPv6 is too large: it seems that all requests to port 80 behind the router are redirected to web host. Eg. [news]:80 should not respond, however it responds with contents.")); errreg != nil {
|
|
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
|
|
}
|
|
} else {
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTP IP (without DNS) challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonIP], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTP IP (without DNS) challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonIP], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonIP], err)
|
|
if verbose {
|
|
log.Printf("%s and HTTP IP (without DNS): %s\n", std.Login, err)
|
|
}
|
|
}
|
|
|
|
// Check DNS for association
|
|
if addr, err := check_dns_udp(std.MyAssociatedDomain(), DEFAULT_RESOLVER); err == nil {
|
|
// Check HTTP on delegated domain
|
|
if err := check_http(addr.String(), std.MyAssociatedDomain()); err == nil {
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTP (without DNS) challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPonAssociatedDomain], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPonAssociatedDomain], 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()); err == nil {
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPSonAssociatedDomain], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
|
|
// SNI check: validate if this check + HTTPS on delegation is validated
|
|
if snicheck1 {
|
|
if std.MyAssociatedDomain() == std.MyDelegatedDomain() {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSSNI], fmt.Errorf("associated and delegated domains have to be different. Please use eg. adlin.example.com as associated domain and wonderfulwebsite.example.com as delegation. Feel free to choose whatever you want that doesn't already exists in your zone!"))
|
|
if verbose {
|
|
log.Printf("%s and HTTPS-SNI: %s\n", std.Login, "associated and delegated domains not accessible at the same time through HTTPS")
|
|
}
|
|
} else {
|
|
if verbose {
|
|
log.Printf("%s just unlocked HTTPS-SNI challenge\n", std.Login)
|
|
}
|
|
if _, err := std.UnlockChallenge(CheckMap[tunnel_version][HTTPSSNI], ""); err != nil {
|
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSonAssociatedDomain], err)
|
|
if verbose {
|
|
log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if snicheck1_tested && !snicheck1 {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][HTTPSSNI], fmt.Errorf("associated and delegated domain are not accessible through HTTPS at the same time, see errors for thoses checks"))
|
|
if verbose {
|
|
log.Printf("%s and HTTPS-SNI: %s\n", std.Login, "associated and delegated domains not accessible at the same time through HTTPS")
|
|
}
|
|
}
|
|
|
|
return
|
|
}, func(stats *ping.Statistics) {
|
|
if verbose2 {
|
|
log.Printf("%s: %s ping statistics: %d packets transmitted, %d packets received, %v%% packet loss", std.Login, stdIP, stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
|
|
}
|
|
if stats.PacketsRecv > 0 {
|
|
return
|
|
}
|
|
|
|
tunnel_version, err := minTunnelVersion(std, tun.SuffixIP)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if !has_test(CheckMap[tunnel_version], Firewalled) {
|
|
return
|
|
}
|
|
|
|
if e, err := std.GetChallengeError(CheckMap[tunnel_version][Firewalled]); err == nil && len(e.Error) > 0 {
|
|
std.RegisterChallengeError(CheckMap[tunnel_version][Firewalled], fmt.Errorf("Your host is unreachable, no more tests are performed. Check that you are not filtering the tunnel (Wireguard uses UDP datagrams), nor ICMPv6 as your host needs to respond to PING."))
|
|
}
|
|
})
|
|
if err != nil && verbose {
|
|
log.Printf("%s: Unable to perform ping to %s: %s", std.Login, stdIP, err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
func studentsChecker(offline bool) {
|
|
students, err := adlin.GetStudents()
|
|
if err != nil {
|
|
log.Println("Unable to check students:", err)
|
|
return
|
|
}
|
|
check_matrix_for := (time.Now().Second()/30)*5 + time.Now().Minute()%5
|
|
|
|
log.Printf("Checking students... (std_matrix%%10=%d)\n", check_matrix_for)
|
|
|
|
for istd, s := range students {
|
|
time.Sleep(250 * time.Millisecond)
|
|
go studentChecker(s, istd%10 == check_matrix_for, offline)
|
|
}
|
|
}
|