github.com/mholt/caddy-l4@v0.0.0-20241104153248-ec8fae209322/modules/l4wireguard/matcher.go (about) 1 // Copyright 2024 VNXME 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package l4wireguard 16 17 import ( 18 "bytes" 19 "encoding/binary" 20 "io" 21 "strconv" 22 23 "github.com/caddyserver/caddy/v2" 24 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" 25 26 "github.com/mholt/caddy-l4/layer4" 27 ) 28 29 func init() { 30 caddy.RegisterModule(&MatchWireGuard{}) 31 } 32 33 // MatchWireGuard is able to match WireGuard connections. 34 type MatchWireGuard struct { 35 // Zero may be used to match reserved zero bytes of Type field when 36 // they have non-zero values (e.g. for obfuscation purposes). E.g. it 37 // may be set to 4,285,988,864 (0xFF770000) in order to match custom 38 // handshake initiation messages starting with 0x010077FF byte sequence. 39 // Note: any non-zero value is a violation of the WireGuard protocol. 40 Zero uint32 `json:"zero,omitempty"` 41 } 42 43 // CaddyModule returns the Caddy module information. 44 func (m *MatchWireGuard) CaddyModule() caddy.ModuleInfo { 45 return caddy.ModuleInfo{ 46 ID: "layer4.matchers.wireguard", 47 New: func() caddy.Module { return new(MatchWireGuard) }, 48 } 49 } 50 51 // Match returns true if the connection looks like WireGuard. 52 func (m *MatchWireGuard) Match(cx *layer4.Connection) (bool, error) { 53 // Read a number of bytes 54 buf := make([]byte, MessageInitiationBytesTotal+1) 55 n, err := io.ReadAtLeast(cx, buf, 1) 56 if err != nil { 57 return false, err 58 } 59 60 switch n { 61 case MessageInitiationBytesTotal: // This is a handshake initiation message 62 // Parse MessageInitiation 63 msg := &MessageInitiation{} 64 if err = msg.FromBytes(buf[:MessageInitiationBytesTotal]); err != nil { 65 return false, nil 66 } 67 68 // Validate MessageInitiation 69 if msg.Type != (m.Zero&ReservedZeroFilter)|MessageTypeInitiation { 70 return false, nil 71 } 72 case MessageTransportBytesMin: // This is a keepalive message (with empty content) 73 // Parse MessageTransport 74 msg := &MessageTransport{} 75 if err = msg.FromBytes(buf[:MessageTransportBytesMin]); err != nil { 76 return false, nil 77 } 78 79 // Validate MessageTransport 80 if msg.Type != (m.Zero&ReservedZeroFilter)|MessageTypeTransport { 81 return false, nil 82 } 83 default: // This is anything else, can also be a valid non-empty transport message 84 return false, nil 85 } 86 87 return true, nil 88 } 89 90 // Provision prepares m's internal structures. 91 func (m *MatchWireGuard) Provision(_ caddy.Context) error { 92 return nil 93 } 94 95 // UnmarshalCaddyfile sets up the MatchWireGuard from Caddyfile tokens. Syntax: 96 // 97 // wireguard [<zero>] 98 func (m *MatchWireGuard) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { 99 _, wrapper := d.Next(), d.Val() // consume wrapper name 100 101 // Only one same-line argument is supported 102 if d.CountRemainingArgs() > 1 { 103 return d.ArgErr() 104 } 105 106 if d.NextArg() { 107 val, err := strconv.ParseUint(d.Val(), 10, 32) 108 if err != nil { 109 return d.Errf("parsing %s zero: %v", wrapper, err) 110 } 111 m.Zero = uint32(val) 112 } 113 114 // No blocks are supported 115 if d.NextBlock(d.Nesting()) { 116 return d.Errf("malformed %s option: blocks are not supported", wrapper) 117 } 118 119 return nil 120 121 } 122 123 // MessageInitiation is the first message 124 // which the initiator sends to the responder. 125 type MessageInitiation struct { 126 Type uint32 127 Sender uint32 128 Ephemeral [32]uint8 129 Static [32 + Poly1305TagSize]uint8 130 Timestamp [12 + Poly1305TagSize]uint8 131 MAC1 [16]uint8 132 MAC2 [16]uint8 133 } 134 135 func (msg *MessageInitiation) FromBytes(src []byte) error { 136 buf := bytes.NewBuffer(src) 137 if err := binary.Read(buf, MessageBytesOrder, &msg.Type); err != nil { 138 return err 139 } 140 if err := binary.Read(buf, MessageBytesOrder, &msg.Sender); err != nil { 141 return err 142 } 143 if err := binary.Read(buf, MessageBytesOrder, &msg.Ephemeral); err != nil { 144 return err 145 } 146 if err := binary.Read(buf, MessageBytesOrder, &msg.Static); err != nil { 147 return err 148 } 149 if err := binary.Read(buf, MessageBytesOrder, &msg.Timestamp); err != nil { 150 return err 151 } 152 if err := binary.Read(buf, MessageBytesOrder, &msg.MAC1); err != nil { 153 return err 154 } 155 if err := binary.Read(buf, MessageBytesOrder, &msg.MAC2); err != nil { 156 return err 157 } 158 return nil 159 } 160 161 func (msg *MessageInitiation) ToBytes() ([]byte, error) { 162 dst := bytes.NewBuffer(make([]byte, 0, MessageInitiationBytesTotal)) 163 if err := binary.Write(dst, MessageBytesOrder, &msg.Type); err != nil { 164 return nil, err 165 } 166 if err := binary.Write(dst, MessageBytesOrder, &msg.Sender); err != nil { 167 return nil, err 168 } 169 if err := binary.Write(dst, MessageBytesOrder, &msg.Ephemeral); err != nil { 170 return nil, err 171 } 172 if err := binary.Write(dst, MessageBytesOrder, &msg.Static); err != nil { 173 return nil, err 174 } 175 if err := binary.Write(dst, MessageBytesOrder, &msg.Timestamp); err != nil { 176 return nil, err 177 } 178 if err := binary.Write(dst, MessageBytesOrder, &msg.MAC1); err != nil { 179 return nil, err 180 } 181 if err := binary.Write(dst, MessageBytesOrder, &msg.MAC2); err != nil { 182 return nil, err 183 } 184 return dst.Bytes(), nil 185 } 186 187 // MessageTransport is the message which the initiator and 188 // the responder exchange after a successful handshake. 189 type MessageTransport struct { 190 Type uint32 191 Receiver uint32 192 Counter uint64 193 Content []uint8 194 } 195 196 func (msg *MessageTransport) FromBytes(src []byte) error { 197 buf := bytes.NewBuffer(src) 198 if err := binary.Read(buf, MessageBytesOrder, &msg.Type); err != nil { 199 return err 200 } 201 if err := binary.Read(buf, MessageBytesOrder, &msg.Receiver); err != nil { 202 return err 203 } 204 if err := binary.Read(buf, MessageBytesOrder, &msg.Counter); err != nil { 205 return err 206 } 207 if buf.Len() > 0 { 208 msg.Content = append(msg.Content, buf.Bytes()...) 209 } 210 return nil 211 } 212 213 func (msg *MessageTransport) ToBytes() ([]byte, error) { 214 dst := bytes.NewBuffer(make([]byte, 0, MessageTransportBytesMin-Poly1305TagSize+len(msg.Content))) 215 if err := binary.Write(dst, MessageBytesOrder, &msg.Type); err != nil { 216 return nil, err 217 } 218 if err := binary.Write(dst, MessageBytesOrder, &msg.Receiver); err != nil { 219 return nil, err 220 } 221 if err := binary.Write(dst, MessageBytesOrder, &msg.Counter); err != nil { 222 return nil, err 223 } 224 return append(dst.Bytes(), msg.Content...), nil 225 } 226 227 // Interface guards 228 var ( 229 _ caddy.Provisioner = (*MatchWireGuard)(nil) 230 _ caddyfile.Unmarshaler = (*MatchWireGuard)(nil) 231 _ layer4.ConnMatcher = (*MatchWireGuard)(nil) 232 ) 233 234 var ( 235 MessageBytesOrder = binary.LittleEndian 236 ) 237 238 // Refs: 239 // 240 // https://www.wireguard.com/protocol/ 241 // https://www.wireguard.com/papers/wireguard.pdf 242 // https://github.com/pirate/wireguard-docs 243 // https://github.com/WireGuard/wireguard-go/blob/master/device/noise-protocol.go 244 const ( 245 Poly1305TagSize int = 16 246 247 MessageInitiationBytesTotal int = 148 248 MessageResponseBytesTotal int = 92 249 MessageCookieReplyBytesTotal int = 64 250 MessageTransportBytesMin int = 32 251 252 MessageTypeInitiation uint32 = 1 253 MessageTypeResponse uint32 = 2 254 MessageTypeCookieReply uint32 = 3 255 MessageTypeTransport uint32 = 4 256 257 ReservedZeroFilter = ^(uint32(0)) >> 8 << 8 258 )