1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package main implements an example DTLS server which verifies client certificates.
// It also implements a basic Brute Force Attack protection.
package main
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"sync"
"time"
"github.com/pion/dtls/v3"
"github.com/pion/dtls/v3/examples/util"
)
func main() {
// Prepare the IP to connect to
addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444}
//
// Everything below is the pion-DTLS API! Thanks for using it ❤️.
//
// ************ Variables used to implement a basic Brute Force Attack protection *************
var (
attempts = make(map[string]int) // Map of attempts for each IP address.
attemptsMutex sync.Mutex // Mutex for the map of attempts.
attemptsCleaner = time.Now() // Time to be able to clean the map of attempts every X minutes.
)
certificate, err := util.LoadKeyAndCertificate("examples/certificates/server.pem",
"examples/certificates/server.pub.pem")
util.Check(err)
rootCertificate, err := util.LoadCertificate("examples/certificates/server.pub.pem")
util.Check(err)
certPool := x509.NewCertPool()
cert, err := x509.ParseCertificate(rootCertificate.Certificate[0])
util.Check(err)
certPool.AddCert(cert)
// Prepare the configuration of the DTLS connection
config := &dtls.Config{
Certificates: []tls.Certificate{certificate},
ExtendedMasterSecret: dtls.RequireExtendedMasterSecret,
ClientAuth: dtls.RequireAndVerifyClientCert,
ClientCAs: certPool,
// This function will be called on each connection attempt.
OnConnectionAttempt: func(addr net.Addr) error {
// *************** Brute Force Attack protection ***************
// Check if the IP address is in the map, and if the IP address has exceeded the limit
attemptsMutex.Lock()
defer attemptsMutex.Unlock()
// Here I implement a time cleaner for the map of attempts, every 5 minutes I will
// decrement by 1 the number of attempts for each IP address.
if time.Now().After(attemptsCleaner.Add(time.Minute * 5)) {
attemptsCleaner = time.Now()
for k, v := range attempts {
if v > 0 {
attempts[k]--
}
if attempts[k] == 0 {
delete(attempts, k)
}
}
}
// Check if the IP address is in the map, and the IP address has exceeded the limit (Brute Force Attack protection)
attemptIP := addr.(*net.UDPAddr).IP.String() //nolint
if attempts[attemptIP] > 10 {
return fmt.Errorf("too many attempts from this IP address") //nolint
}
// Here I increment the number of attempts for this IP address (Brute Force Attack protection)
attempts[attemptIP]++
// *************** END Brute Force Attack protection END ***************
return nil
},
}
// Connect to a DTLS server
listener, err := dtls.Listen("udp", addr, config)
util.Check(err)
defer func() {
util.Check(listener.Close())
}()
fmt.Println("Listening")
// Simulate a chat session
hub := util.NewHub()
go func() {
for {
// Wait for a connection.
conn, err := listener.Accept()
util.Check(err)
// defer conn.Close() // TODO: graceful shutdown
// `conn` is of type `net.Conn` but may be casted to `dtls.Conn`
// using `dtlsConn := conn.(*dtls.Conn)` in order to to expose
// functions like `ConnectionState` etc.
// *************** Brute Force Attack protection ***************
// Here I decrease the number of attempts for this IP address
attemptsMutex.Lock()
attemptIP := conn.(*dtls.Conn).RemoteAddr().(*net.UDPAddr).IP.String() //nolint
attempts[attemptIP]--
// If the number of attempts for this IP address is 0, I delete the IP address from the map
if attempts[attemptIP] == 0 {
delete(attempts, attemptIP)
}
attemptsMutex.Unlock()
// *************** END Brute Force Attack protection END ***************
// Perform the handshake with a 30-second timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
dtlsConn, ok := conn.(*dtls.Conn)
if ok {
util.Check(dtlsConn.HandshakeContext(ctx))
}
cancel()
// Register the connection with the chat hub
hub.Register(conn)
}
}()
// Start chatting
hub.Chat()
}
|