A pure-Go OpenVPN client implementation with userspace TUN device.
No CGo. No system drivers. No root required for the tunnel itself.
Implements the full OpenVPN protocol stack β TLS handshake, reliable transport, data channel encryption, and raw IP packet I/O through a virtual TUN interface. Supports TCP and UDP protocols, tls-auth, tls-crypt, and multiple cipher/auth combinations.
- TLS Handshake β Complete OpenVPN control channel over TLS
- Hard Reset / Soft Reset β Standard OpenVPN session negotiation
- Reliable Transport Layer β Packet ordering, retransmission, and ACK management
- Data Channel Encryption β AES-CBC and AES-GCM cipher suites
- HMAC Authentication β SHA1, SHA256, SHA512 packet authentication
- Key Derivation β TLS 1.0 PRF (RFC 2246) for data channel key generation
- Packet ID Replay Protection β Sequential packet ID tracking with overflow detection
- tls-auth β HMAC authentication of control channel packets
- tls-crypt β Encryption + authentication of control channel packets
- Static Key Parsing β OpenVPN static key V1 format support
- Key Direction β Configurable key direction (0/1) for tls-auth/tls-crypt
- Certificate Authentication β CA, client cert, and client key (file or inline)
- Username/Password Auth β
auth-user-passfrom file or inline block
- Pure-Go implementation β No kernel TUN/TAP drivers needed
- Raw IP packet I/O β Read/Write raw IPv4/IPv6 packets through the VPN tunnel
net.Conn-compatible interface β ImplementsRead(),Write(),LocalAddr(),RemoteAddr()- Tunnel info access β Retrieve assigned IP, gateway, IPv6, MTU, PeerID via
TunnelInfo() - Dual-stack support β IPv4 and IPv6 tunnel addresses
- 6-layer transport stack β NetworkIO β Muxer β Reliable β Control β TLS β Data
- Concurrent worker model β All protocol layers run as independent goroutines
- Channel-based message passing β Zero shared mutable state between workers
- Graceful shutdown β Worker manager with coordinated shutdown sequence
- Protocol framing β TCP length-prefixed and UDP raw framing support
- Functional options pattern β Flexible
ConfigwithWithConfigFile(),WithAuthFile(),WithLogger()
- Full
.ovpnfile parser β Reads standard OpenVPN configuration files - Supported directives:
remote,proto(tcp/udp),cipher,auth,devca,cert,key(file path or inline blocks)auth-user-pass(file path or inline block)tls-auth,tls-crypt(file path or inline blocks)key-direction,tls-version-maxcompress(empty/stub),comp-lzo noproxy-obfs4(pluggable transport support)
- Inline certificate support β
<ca>,<cert>,<key>,<tls-auth>,<tls-crypt>,<auth-user-pass> - Supported ciphers:
AES-128-CBC,AES-192-CBC,AES-256-CBC,AES-128-GCM,AES-192-GCM,AES-256-GCM - Supported auth:
SHA1,SHA256,SHA512
- Integration tests β Real VPN connection tests with multiple providers
- VPNGate support β Test with public VPNGate servers (no auth required)
- Surfshark support β Test with Surfshark (tls-auth + external credentials)
- HTTP exit IP verification β Confirms traffic exits through the VPN
- Raw TCP packet builders β Full IPv4 + TCP packet construction with checksums
go get github.com/galang-rs/ovpnpackage main
import (
"context"
"fmt"
"net"
"github.com/galang-rs/ovpn/pkg/config"
"github.com/galang-rs/ovpn/pkg/tunnel"
)
func main() {
// 1. Load configuration from .ovpn file
cfg := config.NewConfig(config.WithConfigFile("config.ovpn"))
// 2. Start the VPN tunnel
ctx := context.Background()
tun, err := tunnel.Start(ctx, &net.Dialer{}, cfg)
if err != nil {
panic(err)
}
defer tun.Close()
// 3. Get tunnel info
ti := tun.TunnelInfo()
fmt.Printf("Tunnel IP: %s\n", ti.IP)
fmt.Printf("Gateway: %s\n", ti.GW)
fmt.Printf("MTU: %d\n", ti.MTU)
if ti.IPv6 != "" {
fmt.Printf("IPv6: %s/%s\n", ti.IPv6, ti.IPv6NetMask)
}
// 4. Read/Write raw IP packets
buf := make([]byte, 4096)
n, _ := tun.Read(buf) // Read decrypted IP packet from VPN
fmt.Printf("Received %d bytes\n", n)
// tun.Write(ipPacket) // Send IP packet through VPN
}cfg := config.NewConfig(
config.WithConfigFile("surfshark.ovpn"),
config.WithAuthFile("auth.txt"), // username on line 1, password on line 2
)# Build the CLI
go build -o ovpn ./cmd/ovpn/
# Run with a config file
./ovpn config.ovpn
# Or use default (config.ovpn in current directory)
./ovpncfg := config.NewConfig(
config.WithOpenVPNOptions(&config.OpenVPNOptions{
Remote: "vpn.example.com",
Port: "1194",
Proto: config.ProtoUDP,
Cipher: "AES-256-GCM",
Auth: "SHA512",
Username: "myuser",
Password: "mypass",
CA: caCertPEM, // []byte
Cert: clientCertPEM, // []byte
Key: clientKeyPEM, // []byte
}),
config.WithLogger(myCustomLogger),
)username
password
# Test with VPNGate (public, no credentials needed)
go test ./pkg/vpn/... -v -run TestVPNConnectVPNGate -timeout 60s
# Test with Surfshark (requires valid auth.txt)
go test ./pkg/vpn/... -v -run TestVPNConnectSurfshark -timeout 60sovpn/
βββ cmd/
β βββ ovpn/
β βββ main.go # CLI entry point
βββ internal/
β βββ bytesx/ # Byte utilities
β βββ domain/
β β βββ opcode.go # OpenVPN opcodes (P_CONTROL_*, P_DATA_*, etc.)
β β βββ packet.go # Packet serialization/deserialization
β β βββ session.go # Session state & TunnelInfo
β β βββ notification.go # TLS notification types
β βββ optional/ # Generic Optional type
β βββ port/ # Port utilities
β βββ security/
β β βββ hmac.go # HMAC computation & verification
β β βββ statickey.go # OpenVPN static key V1 parser
β βββ session/
β β βββ manager.go # Session manager (state machine)
β β βββ datachannel_key.go # Data channel key derivation (TLS PRF)
β β βββ keysource.go # Random key source generation
β βββ transport/
β β βββ control/ # Control channel worker
β β βββ data/ # Data channel worker
β β βββ muxer/ # Protocol demuxer
β β βββ networkio/ # TCP/UDP I/O with framing
β β βββ reliable/ # Reliable transport (ACK, retransmit)
β β βββ tls/ # TLS session worker
β βββ tunstack/
β β βββ tun.go # Userspace TUN device
β βββ worker/
β βββ manager.go # Goroutine lifecycle manager
βββ pkg/
β βββ config/
β β βββ config.go # Config + functional options
β β βββ options.go # OpenVPNOptions, ciphers, auth methods
β β βββ parser.go # .ovpn file parser (with inline blocks)
β βββ tunnel/
β β βββ tunnel.go # Public tunnel API
β βββ vpn/
β βββ vpn_test.go # Integration tests
βββ go.mod
Zero external dependencies β uses only the Go standard library.
MIT License
Copyright (c) 2026 Galang Reisduanto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
ADDITIONAL TERMS:
1. Attribution β If you use this software in a product, an acknowledgment
in the product documentation or "About" section would be appreciated
but is not required.
2. Non-Endorsement β The name "galang-rs" or "Galang Reisduanto" may not
be used to endorse or promote products derived from this software without
specific prior written permission.
3. Good Faith β This software is shared in good faith for the benefit of
the open-source community. Commercial use is permitted and encouraged.
Have an idea, bug report, or custom feature request? Feel free to reach out!
π§ Email: galangreisduanto@gmail.com
If this project helped you, consider buying me a coffee! Your support helps keep the project active and maintained.
π§ PayPal: galangreisduanto1@gmail.com
Every donation, no matter how small, is greatly appreciated and motivates continued development. π
Made with β€οΈ by Galang Reisduanto