Repository: jkroepke/openvpn-auth-oauth2
Pull request #491 (2025-05-06 16:38:39)

feat: add CLI flag to disable re-authentication
#### What this PR does / why we need it

#### Which issue this PR fixes

*(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

- ref #462

#### Special notes for your reviewer

#### Particularly user-facing changes

#### Checklist

Complete these before marking the PR as `ready to review`:

<!-- [Place an '[x]' (no spaces) in all applicable fields.] -->

- [ ] [DCO](https://github.com/prometheus-community/helm-charts/blob/main/CONTRIBUTING.md#sign-off-your-work) signed
- [ ] The PR title has a summary of the changes
- [ ] The PR body has a summary to reflect any significant (and particularly user-facing) changes introduced by this PR



Patch:
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 1ac8cdf..f7e9d1b 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -93,6 +93,7 @@ openvpn:
     # password: ""
     # socket-group: ""
     # socket-mode: 660
+  reauthentication: true
 ```
 </details>
 
@@ -220,6 +221,8 @@ Usage of openvpn-auth-oauth2:
     	The unix file permission mode for the pass-through socket. Used only, if openvpn.pass-through.address starts with unix:// (env: CONFIG_OPENVPN_PASS__THROUGH_SOCKET__MODE) (default 660)
   --openvpn.password value
     	openvpn management interface password. If argument starts with file:// it reads the secret from a file. (env: CONFIG_OPENVPN_PASSWORD)
+  --openvpn.reauthentication
+    	If set to false, openvpn-auth-oauth2 rejects all re-authentication requests. (env: CONFIG_OPENVPN_REAUTHENTICATION) (default true)
   --version
     	show version
 ```
diff --git a/internal/config/flags.go b/internal/config/flags.go
index 8ec612d..97af451 100644
--- a/internal/config/flags.go
+++ b/internal/config/flags.go
@@ -141,21 +141,21 @@ func (c *Config) flagSetOpenVPN(flagSet *flag.FlagSet) {
 		"bypass oauth authentication for CNs. Comma separated list.",
 	)
 	flagSet.BoolVar(
-		&c.OpenVpn.ClientConfig.Enabled,
+		&c.OpenVPN.ClientConfig.Enabled,
 		"openvpn.client-config.enabled",
-		lookupEnvOrDefault("openvpn.client-config.enabled", c.OpenVpn.ClientConfig.Enabled),
+		lookupEnvOrDefault("openvpn.client-config.enabled", c.OpenVPN.ClientConfig.Enabled),
 		"If true, openvpn-auth-oauth2 will read the CCD directory for additional configuration. This function mimic the client-config-dir directive in OpenVPN.",
 	)
 	flagSet.TextVar(
-		&c.OpenVpn.ClientConfig.Path,
+		&c.OpenVPN.ClientConfig.Path,
 		"openvpn.client-config.path",
-		lookupEnvOrDefault("openvpn.client-config.path", c.OpenVpn.ClientConfig.Path),
+		lookupEnvOrDefault("openvpn.client-config.path", c.OpenVPN.ClientConfig.Path),
 		"Path to the CCD directory. openvpn-auth-oauth2 will look for an file with an .conf suffix and returns the content back.",
 	)
 	flagSet.StringVar(
-		&c.OpenVpn.ClientConfig.TokenClaim,
+		&c.OpenVPN.ClientConfig.TokenClaim,
 		"openvpn.client-config.token-claim",
-		lookupEnvOrDefault("openvpn.client-config.token-claim", c.OpenVpn.ClientConfig.TokenClaim),
+		lookupEnvOrDefault("openvpn.client-config.token-claim", c.OpenVPN.ClientConfig.TokenClaim),
 		"If non-empty, the value of the token claim is used to lookup the configuration file in the CCD directory. If empty, the common name is used.",
 	)
 	flagSet.StringVar(
diff --git a/internal/config/types.go b/internal/config/types.go
index 04655f1..74af577 100644
--- a/internal/config/types.go
+++ b/internal/config/types.go
@@ -52,7 +52,7 @@ type OpenVPN struct {
 	Addr               types.URL          `json:"addr"                 yaml:"addr"`
 	AuthTokenUser      bool               `json:"auth-token-user"      yaml:"auth-token-user"`
 	AuthPendingTimeout time.Duration      `json:"auth-pending-timeout" yaml:"auth-pending-timeout"`
-	Bypass             OpenVpnBypass      `json:"bypass"               yaml:"bypass"`
+	Bypass             OpenVPNBypass      `json:"bypass"               yaml:"bypass"`
 	ClientConfig       OpenVPNConfig      `json:"client-config"        yaml:"client-config"`
 	CommonName         OpenVPNCommonName  `json:"common-name"          yaml:"common-name"`
 	CommandTimeout     time.Duration      `json:"command-timeout"      yaml:"command-timeout"`
diff --git a/internal/config/validate.go b/internal/config/validate.go
index 7c0b098..0537591 100644
--- a/internal/config/validate.go
+++ b/internal/config/validate.go
@@ -107,12 +107,12 @@ func validateOAuth2Config(conf Config) error {
 		}
 	}
 
-	if conf.OpenVpn.ClientConfig.Enabled {
-		if conf.OpenVpn.CommonName.Mode == CommonNameModeOmit {
+	if conf.OpenVPN.ClientConfig.Enabled {
+		if conf.OpenVPN.CommonName.Mode == CommonNameModeOmit {
 			return errors.New("openvpn.common-name.mode: omit is not supported with openvpn.ccd.enabled")
 		}
 
-		file, err := conf.OpenVpn.ClientConfig.Path.Open(".")
+		file, err := conf.OpenVPN.ClientConfig.Path.Open(".")
 		if err != nil {
 			return fmt.Errorf("openvpn.ccd.path: %w", err)
 		}
diff --git a/internal/openvpn/callbacks.go b/internal/openvpn/callbacks.go
index 10c71dc..2cf85e9 100644
--- a/internal/openvpn/callbacks.go
+++ b/internal/openvpn/callbacks.go
@@ -95,11 +95,11 @@ func (c *Client) DenyClient(logger *slog.Logger, client state.ClientIdentifier,
 }
 
 func (c *Client) readClientConfig(username string) ([]string, error) {
-	if !c.conf.OpenVpn.ClientConfig.Enabled || c.conf.OpenVpn.ClientConfig.Path.IsEmpty() || len(username) == 0 {
+	if !c.conf.OpenVPN.ClientConfig.Enabled || c.conf.OpenVPN.ClientConfig.Path.IsEmpty() || len(username) == 0 {
 		return make([]string, 0), nil
 	}
 
-	clientConfigFile, err := c.conf.OpenVpn.ClientConfig.Path.Open(username + ".conf")
+	clientConfigFile, err := c.conf.OpenVPN.ClientConfig.Path.Open(username + ".conf")
 	if err != nil {
 		return make([]string, 0), fmt.Errorf("failed to open client config file: %w", err)
 	}
diff --git a/packaging/etc/openvpn-auth-oauth2/config.yaml b/packaging/etc/openvpn-auth-oauth2/config.yaml
index e40fcfe..67c39a9 100644
--- a/packaging/etc/openvpn-auth-oauth2/config.yaml
+++ b/packaging/etc/openvpn-auth-oauth2/config.yaml
@@ -72,6 +72,7 @@
 #    mode: plain
 #  password: ""
 #  override-username: false # (requires OpenVPN 2.7 server)
+#  reauthentication: true
 #pass-through:
 #  enabled: false
 #  address: "unix:///run/openvpn/pass-through.sock"


Test patch:
diff --git a/cmd/daemon/full_test.go b/cmd/daemon/full_test.go
index 996ade5..9e6dc96 100644
--- a/cmd/daemon/full_test.go
+++ b/cmd/daemon/full_test.go
@@ -189,10 +189,10 @@ func TestFull(t *testing.T) {
 					">CLIENT:ENV,END",
 				}, "\r\n")
 
-				testutils.SendMessage(t, managementInterfaceConn, msg+"\r\n")
+				testutils.SendMessagef(t, managementInterfaceConn, msg+"\r\n")
 
 				authURL := testutils.ReadLine(t, managementInterfaceConn, reader)
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-pending-auth command succeeded")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-pending-auth command succeeded")
 
 				_, authURL, _ = strings.Cut(authURL, `"`)
 				authURL, _, _ = strings.Cut(authURL, `"`)
@@ -207,7 +207,7 @@ func TestFull(t *testing.T) {
 					defer wg.Done()
 
 					testutils.ReadLine(t, managementInterfaceConn, reader)
-					testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+					testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 				}()
 
 				resp, err := httpClient.Do(request)
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 8886afb..8ff4075 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -123,6 +123,7 @@ openvpn:
         password: "password"
         socket-group: "group"
         socket-mode: 0666
+    reauthentication: false
 http:
     listen: ":9001"
     secret: "1jd93h5b6s82lf03jh5b2hf9"
@@ -204,7 +205,8 @@ http:
 						SocketMode:  0o666,
 						Password:    "password",
 					},
-					CommandTimeout: 10 * time.Second,
+					CommandTimeout:   10 * time.Second,
+					ReAuthentication: false,
 				},
 				OAuth2: config.OAuth2{
 					Issuer: types.URL{URL: &url.URL{
diff --git a/internal/internal_test.go b/internal/internal_test.go
index cc239f0..362592e 100644
--- a/internal/internal_test.go
+++ b/internal/internal_test.go
@@ -59,10 +59,10 @@ func BenchmarkFull(b *testing.B) {
 	wgc := sync.WaitGroup{}
 
 	for b.Loop() {
-		testutils.SendMessage(b, managementInterfaceConn, ">CLIENT:CONNECT,0,1\r\n>CLIENT:ENV,n_clients=0\r\n>CLIENT:ENV,password=\r\n>CLIENT:ENV,untrusted_port=17016\r\n>CLIENT:ENV,untrusted_ip=192.168.65.1\r\n>CLIENT:ENV,common_name=user@example.com\r\n>CLIENT:ENV,username=\r\n>CLIENT:ENV,IV_BS64DL=1\r\n>CLIENT:ENV,IV_SSO=webauth,openurl,crtext\r\n>CLIENT:ENV,IV_GUI_VER=OCmacOS_3.4.4-4629\r\n>CLIENT:ENV,IV_AUTO_SESS=1\r\n>CLIENT:ENV,IV_CIPHERS=AES-128-GCM:AES-192-GCM:AES-256-GCM:CHACHA20-POLY1305\r\n>CLIENT:ENV,IV_MTU=1600\r\n>CLIENT:ENV,IV_PROTO=990\r\n>CLIENT:ENV,IV_TCPNL=1\r\n>CLIENT:ENV,IV_NCP=2\r\n>CLIENT:ENV,IV_PLAT=mac\r\n>CLIENT:ENV,IV_VER=3.8.1\r\n>CLIENT:ENV,tls_serial_hex_0=51:b3:55:90:65:af:71:5c:d5:52:2b:0b:00:14:8d:ee\r\n>CLIENT:ENV,tls_serial_0=108598624241397715647038806614705737198\r\n>CLIENT:ENV,tls_digest_sha256_0=d3:6d:1d:96:f8:bd:7e:e8:db:c4:0f:53:a1:76:f0:ca:9e:78:63:bf:c6:4a:ac:b9:e6:ed:84:62:f5:ac:5d:b8\r\n>CLIENT:ENV,tls_digest_0=b7:73:bd:6c:31:31:49:63:0d:0c:11:6d:0c:13:d0:b4:8f:97:33:7d\r\n>CLIENT:ENV,tls_id_0=CN=user@example.com\r\n>CLIENT:ENV,X509_0_CN=user@example.com\r\n>CLIENT:ENV,tls_serial_hex_1=01:b3:95:f8:1a:9f:9f:fe:7c:27:ad:29:c1:93:23:ae:08:7f:ab:36\r\n>CLIENT:ENV,tls_serial_1=9713888317380397892476539918183380788698917686\r\n>CLIENT:ENV,tls_digest_sha256_1=75:1a:a1:63:bb:e9:c7:f3:e3:bf:e1:08:f1:36:b7:36:90:04:da:dd:b8:78:b1:cf:d5:ac:09:b6:36:31:a7:db\r\n>CLIENT:ENV,tls_digest_1=d4:bc:00:89:e5:01:0c:27:3d:ea:4a:b5:42:8b:f7:3d:19:7a:a2:25\r\n>CLIENT:ENV,tls_id_1=CN=Easy-RSA CA\r\n>CLIENT:ENV,X509_1_CN=Easy-RSA CA\r\n>CLIENT:ENV,remote_port_1=1194\r\n>CLIENT:ENV,local_port_1=1194\r\n>CLIENT:ENV,proto_1=udp\r\n>CLIENT:ENV,daemon_pid=7\r\n>CLIENT:ENV,daemon_start_time=1703401559\r\n>CLIENT:ENV,daemon_log_redirect=0\r\n>CLIENT:ENV,daemon=0\r\n>CLIENT:ENV,verb=3\r\n>CLIENT:ENV,config=/etc/openvpn/openvpn.conf\r\n>CLIENT:ENV,ifconfig_local=100.64.0.1\r\n>CLIENT:ENV,ifconfig_netmask=255.255.255.0\r\n>CLIENT:ENV,script_context=init\r\n>CLIENT:ENV,tun_mtu=1500\r\n>CLIENT:ENV,dev=tun0\r\n>CLIENT:ENV,dev_type=tun\r\n>CLIENT:ENV,redirect_gateway=0\r\n>CLIENT:ENV,END\r\n")
+		testutils.SendMessagef(b, managementInterfaceConn, ">CLIENT:CONNECT,0,1\r\n>CLIENT:ENV,n_clients=0\r\n>CLIENT:ENV,password=\r\n>CLIENT:ENV,untrusted_port=17016\r\n>CLIENT:ENV,untrusted_ip=192.168.65.1\r\n>CLIENT:ENV,common_name=user@example.com\r\n>CLIENT:ENV,username=\r\n>CLIENT:ENV,IV_BS64DL=1\r\n>CLIENT:ENV,IV_SSO=webauth,openurl,crtext\r\n>CLIENT:ENV,IV_GUI_VER=OCmacOS_3.4.4-4629\r\n>CLIENT:ENV,IV_AUTO_SESS=1\r\n>CLIENT:ENV,IV_CIPHERS=AES-128-GCM:AES-192-GCM:AES-256-GCM:CHACHA20-POLY1305\r\n>CLIENT:ENV,IV_MTU=1600\r\n>CLIENT:ENV,IV_PROTO=990\r\n>CLIENT:ENV,IV_TCPNL=1\r\n>CLIENT:ENV,IV_NCP=2\r\n>CLIENT:ENV,IV_PLAT=mac\r\n>CLIENT:ENV,IV_VER=3.8.1\r\n>CLIENT:ENV,tls_serial_hex_0=51:b3:55:90:65:af:71:5c:d5:52:2b:0b:00:14:8d:ee\r\n>CLIENT:ENV,tls_serial_0=108598624241397715647038806614705737198\r\n>CLIENT:ENV,tls_digest_sha256_0=d3:6d:1d:96:f8:bd:7e:e8:db:c4:0f:53:a1:76:f0:ca:9e:78:63:bf:c6:4a:ac:b9:e6:ed:84:62:f5:ac:5d:b8\r\n>CLIENT:ENV,tls_digest_0=b7:73:bd:6c:31:31:49:63:0d:0c:11:6d:0c:13:d0:b4:8f:97:33:7d\r\n>CLIENT:ENV,tls_id_0=CN=user@example.com\r\n>CLIENT:ENV,X509_0_CN=user@example.com\r\n>CLIENT:ENV,tls_serial_hex_1=01:b3:95:f8:1a:9f:9f:fe:7c:27:ad:29:c1:93:23:ae:08:7f:ab:36\r\n>CLIENT:ENV,tls_serial_1=9713888317380397892476539918183380788698917686\r\n>CLIENT:ENV,tls_digest_sha256_1=75:1a:a1:63:bb:e9:c7:f3:e3:bf:e1:08:f1:36:b7:36:90:04:da:dd:b8:78:b1:cf:d5:ac:09:b6:36:31:a7:db\r\n>CLIENT:ENV,tls_digest_1=d4:bc:00:89:e5:01:0c:27:3d:ea:4a:b5:42:8b:f7:3d:19:7a:a2:25\r\n>CLIENT:ENV,tls_id_1=CN=Easy-RSA CA\r\n>CLIENT:ENV,X509_1_CN=Easy-RSA CA\r\n>CLIENT:ENV,remote_port_1=1194\r\n>CLIENT:ENV,local_port_1=1194\r\n>CLIENT:ENV,proto_1=udp\r\n>CLIENT:ENV,daemon_pid=7\r\n>CLIENT:ENV,daemon_start_time=1703401559\r\n>CLIENT:ENV,daemon_log_redirect=0\r\n>CLIENT:ENV,daemon=0\r\n>CLIENT:ENV,verb=3\r\n>CLIENT:ENV,config=/etc/openvpn/openvpn.conf\r\n>CLIENT:ENV,ifconfig_local=100.64.0.1\r\n>CLIENT:ENV,ifconfig_netmask=255.255.255.0\r\n>CLIENT:ENV,script_context=init\r\n>CLIENT:ENV,tun_mtu=1500\r\n>CLIENT:ENV,dev=tun0\r\n>CLIENT:ENV,dev_type=tun\r\n>CLIENT:ENV,redirect_gateway=0\r\n>CLIENT:ENV,END\r\n")
 
 		authURL = testutils.ReadLine(b, managementInterfaceConn, reader)
-		testutils.SendMessage(b, managementInterfaceConn, "SUCCESS: client-pending-auth command succeeded")
+		testutils.SendMessagef(b, managementInterfaceConn, "SUCCESS: client-pending-auth command succeeded")
 
 		_, authURL, _ = strings.Cut(authURL, `"`)
 		authURL, _, _ = strings.Cut(authURL, `"`)
@@ -76,7 +76,7 @@ func BenchmarkFull(b *testing.B) {
 			defer wgc.Done()
 
 			testutils.ReadLine(b, managementInterfaceConn, reader)
-			testutils.SendMessage(b, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+			testutils.SendMessagef(b, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 		}()
 
 		resp, _ = httpClient.Do(request)
diff --git a/internal/oauth2/handler_test.go b/internal/oauth2/handler_test.go
index a8183e6..2bb169c 100644
--- a/internal/oauth2/handler_test.go
+++ b/internal/oauth2/handler_test.go
@@ -246,10 +246,10 @@ func TestHandler(t *testing.T) {
 				conf.OAuth2.Validate.Roles = make([]string, 0)
 				conf.OAuth2.Validate.Issuer = true
 				conf.OAuth2.Validate.IPAddr = false
-				conf.OpenVpn.Bypass.CommonNames = make([]string, 0)
-				conf.OpenVpn.AuthTokenUser = true
-				conf.OpenVpn.ClientConfig.Enabled = true
-				conf.OpenVpn.ClientConfig.Path = types.FS{
+				conf.OpenVPN.Bypass.CommonNames = make([]string, 0)
+				conf.OpenVPN.AuthTokenUser = true
+				conf.OpenVPN.ClientConfig.Enabled = true
+				conf.OpenVPN.ClientConfig.Path = types.FS{
 					FS: fstest.MapFS{
 						"name.conf": &fstest.MapFile{
 							Data: []byte("push \"ping 60\"\npush \"ping-restart 180\"\r\npush \"ping-timer-rem\" 0\r\n"),
@@ -279,10 +279,10 @@ func TestHandler(t *testing.T) {
 				conf.OAuth2.Validate.Roles = make([]string, 0)
 				conf.OAuth2.Validate.Issuer = true
 				conf.OAuth2.Validate.IPAddr = false
-				conf.OpenVpn.Bypass.CommonNames = make([]string, 0)
-				conf.OpenVpn.AuthTokenUser = true
-				conf.OpenVpn.ClientConfig.Enabled = true
-				conf.OpenVpn.ClientConfig.Path = types.FS{
+				conf.OpenVPN.Bypass.CommonNames = make([]string, 0)
+				conf.OpenVPN.AuthTokenUser = true
+				conf.OpenVPN.ClientConfig.Enabled = true
+				conf.OpenVPN.ClientConfig.Path = types.FS{
 					FS: fstest.MapFS{},
 				}
 
@@ -429,7 +429,7 @@ func TestHandler(t *testing.T) {
 
 			if !tc.preAllow {
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, `client-deny 0 1 "client rejected: http client ip 127.0.0.1 and vpn ip 127.0.0.2 is different"`)
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-deny command succeeded")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-deny command succeeded")
 			}
 
 			select {
@@ -479,11 +479,11 @@ func TestHandler(t *testing.T) {
 			switch {
 			case !tc.postAllow:
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, `client-deny 0 1 "client rejected"`)
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-deny command succeeded")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-deny command succeeded")
 			case tc.state.Client.UsernameIsDefined == 1:
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth-nt 0 1")
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
-			case tc.conf.OpenVpn.ClientConfig.Enabled:
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+			case tc.conf.OpenVPN.ClientConfig.Enabled:
 				if tc.state.Client.CommonName == "name" {
 					testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth 0 1\r\n"+
 						"push \"ping 60\"\r\n"+
@@ -491,14 +491,14 @@ func TestHandler(t *testing.T) {
 						"push \"ping-timer-rem\" 0\r\n"+
 						"push \"auth-token-user bmFtZQ==\"\r\n"+
 						"END")
-					testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+					testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 				} else {
 					testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth 0 1\r\npush \"auth-token-user Y2xpZW50\"\r\nEND")
-					testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+					testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 				}
 			default:
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth 0 1\r\npush \"auth-token-user bmFtZQ==\"\r\nEND")
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 			}
 
 			select {
diff --git a/internal/oauth2/refresh_test.go b/internal/oauth2/refresh_test.go
index b4bbbcd..d28f86a 100644
--- a/internal/oauth2/refresh_test.go
+++ b/internal/oauth2/refresh_test.go
@@ -52,6 +52,18 @@ func TestRefreshReAuth(t *testing.T) {
 			}(),
 			rt: http.DefaultTransport,
 		},
+		{
+			name:                     "ReAuthentication disabled",
+			clientCommonName:         "test",
+			nonInteractiveShouldWork: true,
+			conf: func() config.Config {
+				conf := config.Defaults
+				conf.OpenVPN.ReAuthentication = false
+
+				return conf
+			}(),
+			rt: http.DefaultTransport,
+		},
 		{
 			name:                     "Refresh with empty common name",
 			clientCommonName:         "",
@@ -260,14 +272,14 @@ func TestRefreshReAuth(t *testing.T) {
 
 			time.Sleep(time.Millisecond * 100)
 
-			testutils.SendMessage(t, managementInterfaceConn,
+			testutils.SendMessagef(t, managementInterfaceConn,
 				">CLIENT:CONNECT,1,2\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=%s\r\n>CLIENT:ENV,session_state=Initial\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END",
 				tc.clientCommonName,
 			)
 
 			auth := testutils.ReadLine(t, managementInterfaceConn, reader)
 			assert.Contains(t, auth, "client-pending-auth 1 2 \"WEB_AUTH::")
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
 
 			authURL := strings.TrimPrefix(strings.Split(auth, `"`)[1], "WEB_AUTH::")
 
@@ -288,6 +300,17 @@ func TestRefreshReAuth(t *testing.T) {
 				resp, reqErr = httpClient.Do(request) //nolint:bodyclose
 			}()
 
+			t.Cleanup(func() {
+				require.NoError(t, managementInterfaceConn.Close())
+
+				select {
+				case err := <-errOpenVPNClientCh:
+					require.NoError(t, err, logger.String())
+				case <-time.After(1 * time.Second):
+					t.Fatalf("timeout waiting for connection to close. Logs:\n\n%s", logger.String())
+				}
+			})
+
 			switch {
 			case tc.conf.OpenVPN.OverrideUsername:
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth 1 2")
@@ -313,7 +336,7 @@ func TestRefreshReAuth(t *testing.T) {
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth-nt 1 2")
 			}
 
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 
 			wg.Wait()
 
@@ -326,39 +349,48 @@ func TestRefreshReAuth(t *testing.T) {
 			_ = resp.Body.Close()
 
 			// Testing ReAuth
-			testutils.SendMessage(t, managementInterfaceConn,
+			testutils.SendMessagef(t, managementInterfaceConn,
+				">CLIENT:ESTABLISHED,0\r\n>CLIENT:ENV,common_name=bypass\r\n>CLIENT:ENV,END\r\n",
+			)
+
+			// Testing ReAuth
+			testutils.SendMessagef(t, managementInterfaceConn,
 				">CLIENT:REAUTH,1,3\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=%s\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END",
 				tc.clientCommonName,
 			)
 
+			if !tc.conf.OpenVPN.ReAuthentication {
+				testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-deny 1 3 \"client re-authentication not enabled\"")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-deny command succeeded")
+
+				return
+			}
+
 			if !tc.nonInteractiveShouldWork {
 				auth := testutils.ReadLine(t, managementInterfaceConn, reader)
 				assert.Contains(t, auth, "client-pending-auth 1 3 \"WEB_AUTH::")
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
-
-				openVPNClient.Shutdown()
-				wg.Wait()
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
 
 				return
 			}
 
 			testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth-nt 1 3")
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 
 			// Testing ReAuth
-			testutils.SendMessage(t, managementInterfaceConn,
+			testutils.SendMessagef(t, managementInterfaceConn,
 				">CLIENT:REAUTH,1,4\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=%s\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END",
 				tc.clientCommonName,
 			)
 
 			testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-auth-nt 1 4")
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: client-auth command succeeded")
 
 			// Test Disconnect
-			testutils.SendMessage(t, managementInterfaceConn, ">CLIENT:DISCONNECT,1\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
+			testutils.SendMessagef(t, managementInterfaceConn, ">CLIENT:DISCONNECT,1\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
 
 			// Test ReAuth after DC
-			testutils.SendMessage(t, managementInterfaceConn, ">CLIENT:REAUTH,1,4\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
+			testutils.SendMessagef(t, managementInterfaceConn, ">CLIENT:REAUTH,1,4\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
 
 			auth = testutils.ReadLine(t, managementInterfaceConn, reader)
 
@@ -368,10 +400,10 @@ func TestRefreshReAuth(t *testing.T) {
 				require.Contains(t, auth, "client-pending-auth 1 4 \"WEB_AUTH::")
 			}
 
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
 
 			// Test ReAuth after DC with different CID
-			testutils.SendMessage(t, managementInterfaceConn, ">CLIENT:CONNECT,2,3\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
+			testutils.SendMessagef(t, managementInterfaceConn, ">CLIENT:CONNECT,2,3\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=AuthenticatedEmptyUser\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
 
 			auth = testutils.ReadLine(t, managementInterfaceConn, reader)
 
@@ -381,10 +413,10 @@ func TestRefreshReAuth(t *testing.T) {
 				require.Contains(t, auth, "client-pending-auth 2 3 \"WEB_AUTH::")
 			}
 
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
 
 			// Test ReAuth after DC with different CID with invalid session
-			testutils.SendMessage(t, managementInterfaceConn, ">CLIENT:CONNECT,3,3\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=Expired\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
+			testutils.SendMessagef(t, managementInterfaceConn, ">CLIENT:CONNECT,3,3\r\n>CLIENT:ENV,untrusted_ip=127.0.0.1\r\n>CLIENT:ENV,common_name=test\r\n>CLIENT:ENV,session_id=session_id\r\n>CLIENT:ENV,session_state=Expired\r\n>CLIENT:ENV,IV_SSO=webauth\r\n>CLIENT:ENV,END")
 
 			auth = testutils.ReadLine(t, managementInterfaceConn, reader)
 
@@ -394,17 +426,7 @@ func TestRefreshReAuth(t *testing.T) {
 				assert.Contains(t, auth, `client-pending-auth 3 3 "WEB_AUTH::`)
 			}
 
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
-
-			require.NoError(t, managementInterfaceConn.Close())
-
-			wg.Wait()
-			select {
-			case err := <-errOpenVPNClientCh:
-				require.NoError(t, err, logger.String())
-			case <-time.After(1 * time.Second):
-				t.Fatalf("timeout waiting for connection to close. Logs:\n\n%s", logger.String())
-			}
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: %s command succeeded", strings.SplitN(auth, " ", 2)[0])
 		})
 	}
 }
diff --git a/internal/openvpn/bench_test.go b/internal/openvpn/bench_test.go
index 850ff1d..c89c2be 100644
--- a/internal/openvpn/bench_test.go
+++ b/internal/openvpn/bench_test.go
@@ -70,9 +70,9 @@ func BenchmarkOpenVPNHandler(b *testing.B) {
 	for _, tt := range tests {
 		b.Run(tt.name, func(b *testing.B) {
 			for b.Loop() {
-				testutils.SendMessage(b, managementInterfaceConn, tt.client)
+				testutils.SendMessagef(b, managementInterfaceConn, tt.client)
 				assert.Contains(b, testutils.ReadLine(b, managementInterfaceConn, reader), "client-pending-auth 0 1 \"WEB_AUTH::")
-				testutils.SendMessage(b, managementInterfaceConn, "SUCCESS: client-pending-auth command succeeded")
+				testutils.SendMessagef(b, managementInterfaceConn, "SUCCESS: client-pending-auth command succeeded")
 			}
 
 			b.ReportAllocs()
@@ -115,9 +115,9 @@ func BenchmarkOpenVPNPassthrough(b *testing.B) {
 	require.NoError(b, err)
 
 	if conf.OpenVPN.Password != "" {
-		testutils.SendMessage(b, managementInterfaceConn, "ENTER PASSWORD:")
+		testutils.SendMessagef(b, managementInterfaceConn, "ENTER PASSWORD:")
 		testutils.ExpectMessage(b, managementInterfaceConn, reader, conf.OpenVPN.Password.String())
-		testutils.SendMessage(b, managementInterfaceConn, "SUCCESS: password is correct")
+		testutils.SendMessagef(b, managementInterfaceConn, "SUCCESS: password is correct")
 	}
 
 	testutils.ExpectVersionAndReleaseHold(b, managementInterfaceConn, reader)
@@ -169,9 +169,9 @@ func BenchmarkOpenVPNPassthrough(b *testing.B) {
 
 	for _, tt := range tests {
 		b.Run(tt.command, func(b *testing.B) {
-			testutils.SendMessage(b, passThroughConn, tt.command)
+			testutils.SendMessagef(b, passThroughConn, tt.command)
 			testutils.ExpectMessage(b, managementInterfaceConn, reader, tt.command)
-			testutils.SendMessage(b, managementInterfaceConn, tt.response)
+			testutils.SendMessagef(b, managementInterfaceConn, tt.response)
 			testutils.ExpectMessage(b, passThroughConn, passThroughReader, tt.response)
 
 			b.ReportAllocs()
diff --git a/internal/openvpn/main_test.go b/internal/openvpn/main_test.go
index 2c05db4..8f50f52 100644
--- a/internal/openvpn/main_test.go
+++ b/internal/openvpn/main_test.go
@@ -298,11 +298,11 @@ func TestClientFull(t *testing.T) {
 
 			if tc.conf.OpenVPN.Password != "" {
 				testutils.SendAndExpectMessage(t, managementInterfaceConn, reader, "ENTER PASSWORD:", tc.conf.OpenVPN.Password.String())
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: password is correct")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: password is correct")
 			}
 
 			testutils.ExpectVersionAndReleaseHold(t, managementInterfaceConn, reader)
-			testutils.SendMessage(t, managementInterfaceConn, tc.client)
+			testutils.SendMessagef(t, managementInterfaceConn, tc.client)
 
 			if tc.err != nil {
 				_, _ = reader.ReadString('\n')
@@ -324,10 +324,10 @@ func TestClientFull(t *testing.T) {
 				require.Contains(t, logger.String(), `CLIENT:ENV,password=***`, logger.String())
 			}
 
-			testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: %s command succeeded\r\n", strings.SplitN(auth, " ", 2)[0])
+			testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: %s command succeeded\r\n", strings.SplitN(auth, " ", 2)[0])
 
 			if strings.Contains(auth, "client-deny") {
-				testutils.SendMessage(t, managementInterfaceConn, ">CLIENT:DISCONNECT,0\r\n>CLIENT:ENV,END")
+				testutils.SendMessagef(t, managementInterfaceConn, ">CLIENT:DISCONNECT,0\r\n>CLIENT:ENV,END")
 			} else if strings.Contains(auth, "WEB_AUTH::") {
 				matches := regexp.MustCompile(`state=(.+)"`).FindStringSubmatch(auth)
 				require.Len(t, matches, 2)
@@ -382,9 +382,9 @@ func TestClientInvalidPassword(t *testing.T) {
 
 	reader := bufio.NewReader(managementInterfaceConn)
 
-	testutils.SendMessage(t, managementInterfaceConn, "ENTER PASSWORD:")
+	testutils.SendMessagef(t, managementInterfaceConn, "ENTER PASSWORD:")
 	testutils.ExpectMessage(t, managementInterfaceConn, reader, conf.OpenVPN.Password.String())
-	testutils.SendMessage(t, managementInterfaceConn, "ERROR: bad password")
+	testutils.SendMessagef(t, managementInterfaceConn, "ERROR: bad password")
 
 	select {
 	case err := <-errOpenVPNClientCh:
@@ -452,7 +452,7 @@ func TestClientInvalidVersion(t *testing.T) {
 				"version",
 			)
 
-			testutils.SendMessage(t, managementInterfaceConn, tc.version)
+			testutils.SendMessagef(t, managementInterfaceConn, tc.version)
 
 			select {
 			case err := <-errOpenVPNClientCh:
@@ -558,7 +558,7 @@ func TestCommandTimeout(t *testing.T) {
 
 	defer func() {
 		testutils.ExpectMessage(t, managementInterfaceConn, reader, "help")
-		testutils.SendMessage(t, managementInterfaceConn, "")
+		testutils.SendMessagef(t, managementInterfaceConn, "")
 	}()
 
 	_, err = openVPNClient.SendCommandf("help")
@@ -622,7 +622,7 @@ func TestDeadLocks(t *testing.T) {
 			testutils.ExpectVersionAndReleaseHold(t, managementInterfaceConn, reader)
 
 			for range 12 {
-				testutils.SendMessage(t, managementInterfaceConn, tc.message)
+				testutils.SendMessagef(t, managementInterfaceConn, tc.message)
 			}
 
 			require.NoError(t, managementInterfaceConn.Close())
@@ -694,7 +694,7 @@ func TestInvalidCommandResponses(t *testing.T) {
 				"hold release",
 			)
 
-			testutils.SendMessage(t, managementInterfaceConn, tc.message)
+			testutils.SendMessagef(t, managementInterfaceConn, tc.message)
 
 			require.NoError(t, managementInterfaceConn.Close())
 
diff --git a/internal/openvpn/passthrough_test.go b/internal/openvpn/passthrough_test.go
index 3e7bcca..3f4a4b5 100644
--- a/internal/openvpn/passthrough_test.go
+++ b/internal/openvpn/passthrough_test.go
@@ -209,15 +209,15 @@ func TestPassThroughFull(t *testing.T) {
 			reader := bufio.NewReader(managementInterfaceConn)
 
 			if tt.conf.OpenVPN.Password != "" {
-				testutils.SendMessage(t, managementInterfaceConn, "ENTER PASSWORD:")
+				testutils.SendMessagef(t, managementInterfaceConn, "ENTER PASSWORD:")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, tt.conf.OpenVPN.Password.String())
 
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: password is correct")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: password is correct")
 			}
 
 			testutils.ExpectVersionAndReleaseHold(t, managementInterfaceConn, reader)
-			testutils.SendMessage(t, managementInterfaceConn, "")
-			testutils.SendMessage(t, managementInterfaceConn, "\r\n")
+			testutils.SendMessagef(t, managementInterfaceConn, "")
+			testutils.SendMessagef(t, managementInterfaceConn, "\r\n")
 
 			var passThroughAddr []string
 
@@ -272,9 +272,9 @@ func TestPassThroughFull(t *testing.T) {
 			testutils.ExpectMessage(t, passThroughConn, passThroughReader, ">INFO:OpenVPN Management Interface Version 5 -- type 'help' for more info")
 
 			for range 10 {
-				testutils.SendMessage(t, passThroughConn, "")
-				testutils.SendMessage(t, passThroughConn, "\n")
-				testutils.SendMessage(t, passThroughConn, "\r\n")
+				testutils.SendMessagef(t, passThroughConn, "")
+				testutils.SendMessagef(t, passThroughConn, "\n")
+				testutils.SendMessagef(t, passThroughConn, "\r\n")
 
 				testutils.SendAndExpectMessage(t, passThroughConn, passThroughReader,
 					"hold",
@@ -282,21 +282,21 @@ func TestPassThroughFull(t *testing.T) {
 				)
 
 				// PID
-				testutils.SendMessage(t, passThroughConn, "pid")
+				testutils.SendMessagef(t, passThroughConn, "pid")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "pid")
-				testutils.SendMessage(t, managementInterfaceConn, "SUCCESS: pid=7")
+				testutils.SendMessagef(t, managementInterfaceConn, "SUCCESS: pid=7")
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, "SUCCESS: pid=7")
 
 				// unknown command
-				testutils.SendMessage(t, passThroughConn, "foo")
+				testutils.SendMessagef(t, passThroughConn, "foo")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "foo")
-				testutils.SendMessage(t, managementInterfaceConn, "ERROR: unknown command, enter 'help' for more options")
+				testutils.SendMessagef(t, managementInterfaceConn, "ERROR: unknown command, enter 'help' for more options")
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, "ERROR: unknown command, enter 'help' for more options")
 
 				// kill 1
-				testutils.SendMessage(t, passThroughConn, "kill 1")
+				testutils.SendMessagef(t, passThroughConn, "kill 1")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "kill 1")
-				testutils.SendMessage(t, managementInterfaceConn, "ERROR: common name '1' not found")
+				testutils.SendMessagef(t, managementInterfaceConn, "ERROR: common name '1' not found")
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, "ERROR: common name '1' not found")
 
 				// client-auth-nt 1
@@ -306,46 +306,46 @@ func TestPassThroughFull(t *testing.T) {
 				)
 
 				// client-kill 1
-				testutils.SendMessage(t, passThroughConn, "client-kill 1")
+				testutils.SendMessagef(t, passThroughConn, "client-kill 1")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "client-kill 1")
-				testutils.SendMessage(t, managementInterfaceConn, "ERROR: client-kill command failed")
+				testutils.SendMessagef(t, managementInterfaceConn, "ERROR: client-kill command failed")
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, "ERROR: client-kill command failed")
 
 				// version
-				testutils.SendMessage(t, passThroughConn, "version")
+				testutils.SendMessagef(t, passThroughConn, "version")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "version")
-				testutils.SendMessage(t, managementInterfaceConn, "OpenVPN Version: openvpn-auth-oauth2\r\nManagement Interface Version: 5\r\nEND")
+				testutils.SendMessagef(t, managementInterfaceConn, "OpenVPN Version: openvpn-auth-oauth2\r\nManagement Interface Version: 5\r\nEND")
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, "OpenVPN Version: openvpn-auth-oauth2\r\nManagement Interface Version: 5\r\nEND")
 
 				// status
-				testutils.SendMessage(t, passThroughConn, "status")
+				testutils.SendMessagef(t, passThroughConn, "status")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "status")
-				testutils.SendMessage(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultStatus)
+				testutils.SendMessagef(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultStatus)
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, OpenVPNManagementInterfaceCommandResultStatus)
 
 				// status 2
-				testutils.SendMessage(t, passThroughConn, "status 2")
+				testutils.SendMessagef(t, passThroughConn, "status 2")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "status 2")
-				testutils.SendMessage(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultStatus2)
+				testutils.SendMessagef(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultStatus2)
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, OpenVPNManagementInterfaceCommandResultStatus2)
 
 				// status 3
-				testutils.SendMessage(t, passThroughConn, "status 3")
+				testutils.SendMessagef(t, passThroughConn, "status 3")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "status 3")
-				testutils.SendMessage(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultStatus3)
+				testutils.SendMessagef(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultStatus3)
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, OpenVPNManagementInterfaceCommandResultStatus3)
 			}
 
 			// help
 			for range 10 {
-				testutils.SendMessage(t, passThroughConn, "help")
+				testutils.SendMessagef(t, passThroughConn, "help")
 				testutils.ExpectMessage(t, managementInterfaceConn, reader, "help")
-				testutils.SendMessage(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultHelp)
+				testutils.SendMessagef(t, managementInterfaceConn, OpenVPNManagementInterfaceCommandResultHelp)
 				testutils.ExpectMessage(t, passThroughConn, passThroughReader, OpenVPNManagementInterfaceCommandResultHelp)
 			}
 
 			if tt.scheme == openvpn.SchemeUnix {
-				testutils.SendMessage(t, passThroughConn, " exit ")
+				testutils.SendMessagef(t, passThroughConn, " exit ")
 				require.NoError(t, passThroughConn.Close())
 
 				gid, err := testutils.GetGIDOfFile(tt.conf.OpenVPN.Passthrough.Address.Path)
@@ -358,7 +358,7 @@ func TestPassThroughFull(t *testing.T) {
 
 				assert.Equal(t, os.FileMode(tt.conf.OpenVPN.Passthrough.SocketMode).String(), permission) //nolint:gosec
 			} else {
-				testutils.SendMessage(t, passThroughConn, " quit ")
+				testutils.SendMessagef(t, passThroughConn, " quit ")
 				require.NoError(t, passThroughConn.Close())
 			}
 
diff --git a/internal/utils/testutils/main.go b/internal/utils/testutils/main.go
index 9de2be9..85b97a0 100644
--- a/internal/utils/testutils/main.go
+++ b/internal/utils/testutils/main.go
@@ -43,8 +43,8 @@ const (
 func ExpectVersionAndReleaseHold(tb testing.TB, conn net.Conn, reader *bufio.Reader) {
 	tb.Helper()
 
-	SendMessage(tb, conn, ">INFO:OpenVPN Management Interface Version 5 -- type 'help' for more info")
-	SendMessage(tb, conn, ">HOLD:Waiting for hold release:0")
+	SendMessagef(tb, conn, ">INFO:OpenVPN Management Interface Version 5 -- type 'help' for more info")
+	SendMessagef(tb, conn, ">HOLD:Waiting for hold release:0")
 
 	var expectedCommand int
 
@@ -52,11 +52,11 @@ func ExpectVersionAndReleaseHold(tb testing.TB, conn net.Conn, reader *bufio.Rea
 		line := ReadLine(tb, conn, reader)
 		switch line {
 		case "hold release":
-			SendMessage(tb, conn, "SUCCESS: hold release succeeded")
+			SendMessagef(tb, conn, "SUCCESS: hold release succeeded")
 
 			expectedCommand++
 		case "version":
-			SendMessage(tb, conn, "OpenVPN Version: OpenVPN Mock\r\nManagement Interface Version: 5\r\nEND")
+			SendMessagef(tb, conn, "OpenVPN Version: OpenVPN Mock\r\nManagement Interface Version: 5\r\nEND")
 
 			expectedCommand++
 		default:
@@ -67,7 +67,7 @@ func ExpectVersionAndReleaseHold(tb testing.TB, conn net.Conn, reader *bufio.Rea
 	require.Equal(tb, 2, expectedCommand)
 }
 
-func SendMessage(tb testing.TB, conn net.Conn, sendMessage string, args ...any) {
+func SendMessagef(tb testing.TB, conn net.Conn, sendMessage string, args ...any) {
 	tb.Helper()
 
 	require.NotNil(tb, conn, "connection is nil")
@@ -106,7 +106,7 @@ func ExpectMessage(tb testing.TB, conn net.Conn, reader *bufio.Reader, expectMes
 func SendAndExpectMessage(tb testing.TB, conn net.Conn, reader *bufio.Reader, sendMessage, expectMessage string) {
 	tb.Helper()
 
-	SendMessage(tb, conn, sendMessage)
+	SendMessagef(tb, conn, sendMessage)
 	ExpectMessage(tb, conn, reader, expectMessage)
 }
 
@@ -213,8 +213,8 @@ func SetupMockEnvironment(ctx context.Context, tb testing.TB, conf config.Config
 		conf.HTTP.Template = config.Defaults.HTTP.Template
 	}
 
-	if conf.OpenVpn.ClientConfig.Path.IsEmpty() {
-		conf.OpenVpn.ClientConfig.Path = config.Defaults.OpenVpn.ClientConfig.Path
+	if conf.OpenVPN.ClientConfig.Path.IsEmpty() {
+		conf.OpenVPN.ClientConfig.Path = config.Defaults.OpenVPN.ClientConfig.Path
 	}
 
 	conf.OpenVPN.Addr = types.URL{URL: &url.URL{Scheme: managementInterface.Addr().Network(), Host: managementInterface.Addr().String()}}