Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ncm-metaconfig: ssh daemon configuration support #1377

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions ncm-metaconfig/src/main/metaconfig/ssh/client_attrs.tt
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
[% spacelist = ['SendEnv', 'GlobalKnownHostsFile', 'IgnoreUnknown', 'UserKnownHostsFile' ] -%]
[% commalist = ['Ciphers', 'HostbasedKeyTypes','HostKeyAlgorithms', 'KbdInteractiveDevices', 'KbdInteractiveDevices',
'MACs', 'PreferredAuthentications', 'CanonicalDomains', 'CanonicalizePermittedCNAMEs', 'KexAlgorithms',
] -%]
[% multilinelist = ['IdentityFile', 'RevokedHostKeys'] -%]
[% booleans = ['BatchMode', 'CanonicalizeFallbackLocal', 'ChallengeResponseAuthentication', 'CheckHostIP',
'ClearAllForwardings', 'Compression', 'EnableSSHKeysign', 'ExitOnForwardFailure', 'ForwardAgent', 'ForwardX11',
'ForwardX11Trusted', 'GatewayPorts', 'GSSAPIAuthentication', 'GSSAPIDelegateCredentials', 'HashKnownHosts',
'HostbasedAuthentication', 'IdentitiesOnly', 'KbdInteractiveAuthentication', 'NoHostAuthenticationForLocalhost',
'PasswordAuthentication', 'PermitLocalCommand', 'ProxyUseFdpass', 'PubkeyAuthentication', 'RhostsRSAAuthentication',
'RSAAuthentication', 'StreamLocalBindUnlink', 'TCPKeepAlive', 'UsePrivilegedPort', 'VisualHostKey',
[% spacelist = ['SendEnv', 'GlobalKnownHostsFile', 'IgnoreUnknown', 'Include', 'UserKnownHostsFile' ] -%]
[% commalist = ['CASignatureAlgorithms', 'CanonicalDomains', 'CanonicalizePermittedCNAMEs', 'Ciphers',
'GSSAPIKexAlgorithms', 'HostbasedKeyTypes','HostKeyAlgorithms',
'KbdInteractiveDevices', 'KexAlgorithms', 'KbdInteractiveDevices',
'MACs', 'PreferredAuthentications', 'PubkeyAcceptedKeyTypes', 'ProxyJump',
] -%]
[% multilinelist = ['CertificateFile', 'IdentityFile', 'RevokedHostKeys'] -%]

[%- FOREACH pair IN data.pairs -%]
[% NEXT IF pair.key == 'hostnames' || pair.key == 'matches' -%]
[% IF pair.value.is_boolean -%]
[% pair.key %] [% pair.value ? 'Yes' : 'No' %]
[% NEXT -%]
[% END -%]
[% SWITCH pair.key -%]
[% CASE booleans -%]
[% pair.key %] [% pair.value ? 'Yes' : 'No' %]
[% CASE spacelist -%]
[% pair.key %] [% pair.value.join(' ') %]
[% CASE commalist -%]
Expand All @@ -24,6 +21,10 @@
[% FOREACH line IN pair.value -%]
[% pair.key %] [% line %]
[% END -%]
[% CASE 'SetEnv' -%]
[% FOREACH item IN pair.value.pairs -%]
SetEnv [% item.key %]="[% item.value %]"
[% END -%]
[% CASE -%]
[% pair.key %] [% pair.value %]
[% END -%]
Expand Down
156 changes: 147 additions & 9 deletions ncm-metaconfig/src/main/metaconfig/ssh/pan/schema.pan
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,43 @@ include 'pan/types';
# rename these types to prevent conflicts
# we will remove these in an upcoming pr after template-library-core
# has been updated with the new types from ncm-ssh
type temp_ssh_ciphers = string with match (SELF, "^((blowfish|3des|aes128|aes192|aes256|cast128)-cbc" +
"|(aes128|aes192|aes256)-ctr|arcfour|arcfour(128|256)|(aes128-gcm|aes256-gcm|chacha20-poly1305)@openssh.com)$");
type temp_ssh_hostkeyalgorithms = string with match(SELF, "^(ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp(256|384|521)|" +
"(ssh-rsa-cert-v01|ssh-dss-cert-v01|ecdsa-sha2-nistp256-cert-v01|ecdsa-sha2-nistp384-cert-v01|" +
"ecdsa-sha2-nistp521-cert-v01|ssh-rsa-cert-v00|ssh-dss-cert-v00|ssh-ed25519-cert-v01)@openssh.com)$");
type temp_ssh_ciphers = string with match (SELF, "^[+-]?(" +
"(blowfish|3des|aes(128|192|256)|cast128)-cbc" +
"|aes(128|192|256)-ctr|arcfour|arcfour(128|256)" +
"|aes(128|256)-gcm|chacha20-poly1305)@openssh.com)$");
type temp_ssh_hostkeyalgorithms = string with match(SELF, "^[+-]?(" +
"ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp(256|384|521)|" +
"(ssh-rsa-cert-v0[01]|ssh-dss-cert-v01|ecdsa-sha2-nistp(256|384|521)-cert-v01|" +
"ssh-dss-cert-v00|ssh-ed25519-cert-v01)@openssh.com)$");
type temp_ssh_kbdinteractivedevices = string with match (SELF, "^(bsdauth|pam|skey)$");
type temp_ssh_kexalgorithms = string with match (SELF, "^(diffie-hellman-group(1-sha1|14-sha1|-exchange-sha1|" +
"-exchange-sha256)|ecdh-sha2-nistp(256|384|521)|curve25519-sha256@libssh.org|gss-gex-sha1-|" +
"gss-group1-sha1-|gss-group14-sha1-)$");
type temp_ssh_MACs = string with match(SELF, "^(hmac-(sha1|sha1-96|sha2-256|sha2-512|md5|md5-96|ripemd160)|" +
# Recent versions have distinct GSSAPIKexAlgorithms
type temp_ssh_gss_kexalgorithms = string with match (SELF, "^[+-]?(gss-(gex|group1|group14)-sha1-" +
"|gss-group14-sha256-|gss-group16-sha512-|gss-nistp256-sha256-|gss-curve25519-sha256-)$");
# Older versions include GSSAPI mechanisms in KEXAlgorithms, but only the SHA1 variants
type temp_ssh_kexalgorithms = string with match (SELF, "^[+-]?(" +
"diffie-hellman-group(1-sha1|14-sha1|-exchange-sha1|-exchange-sha256)" +
"|ecdh-sha2-nistp(256|384|521)|curve25519-sha256@libssh.org" +
"|gss-(gex|group1|group14)-sha1-)$");
type temp_ssh_MACs = string with match(SELF, "^[+-]?(hmac-(sha1|sha1-96|sha2-256|sha2-512|md5|md5-96|ripemd160)|" +
"(hmac-ripemd160|umac-64|umac-128|hmac-sha1-etm|hmac-sha1-96-etm|hmac-sha2-256-etm|hmac-sha2-512-etm|" +
"hmac-md5-etm|hmac-md5-96-etm|hmac-ripemd160-etm|umac-64-etm|umac-128-etm)@openssh.com)$");
type temp_ssh_CAAlgorithms = string with match(SELF, "^[+-]?(" +
"ecdsa-sha2-nistp(256|384|521)|ssh-ed25519|rsa-sha2-(256|512)|ssh-rsa)$");


type ssh_config_opts = {
'AddKeysToAgent' ? string with match (SELF, "^(yes|no|ask|confirm)$")
'AddressFamily' ? string with match (SELF, "^(any|inet|inet6)$")
'BatchMode' ? boolean
'BindAddress' ? string
'BindInterface' ? string
'CanonicalDomains' ? string[]
'CanonicalizeFallbackLocal' ? boolean
'CanonicalizeHostname' ? string with match (SELF, "^(yes|no|always)$")
'CanonicalizeMaxDots' ? long(0..)
'CanonicalizePermittedCNAMEs' ? string[]
'CASignatureAlgorithms' ? temp_ssh_CAAlgorithms[]
'CertificateFile' ? string[]
'ChallengeResponseAuthentication' ? boolean
'CheckHostIP' ? boolean
'Cipher' ? string with match (SELF, "^(blowfish|3des|des)$")
Expand All @@ -52,16 +66,24 @@ type ssh_config_opts = {
'GatewayPorts' ? boolean
'GlobalKnownHostsFile' ? string[]
'GSSAPIAuthentication' ? boolean
'GSSAPIClientIdentity' ? string
'GSSAPIDelegateCredentials' ? boolean
'GSSAPIKeyExchange' ? boolean
'GSSAPIKexAlgorithms' ? temp_ssh_gss_kexalgorithms[]
'GSSAPIRenewalForcesRekey' ? boolean
'GSSAPIServerIdentity' ? string
'GSSAPITrustDns' ? boolean
'HashKnownHosts' ? boolean
'HostbasedAuthentication' ? boolean
'HostbasedKeyTypes' ? string[]
'HostKeyAlgorithms' ? temp_ssh_hostkeyalgorithms[]
'HostKeyAlias' ? string
'HostName' ? string
'IdentitiesOnly' ? boolean
'IdentityAgent' ? string
'IdentityFile' ? string[]
'IgnoreUnknown' ? string[]
'Include' ? string[]
'IPQoS' ? string with match (SELF, "^(af[1234][123]|cs[0-7]|ef|lowdelay|throughput|reliability)$")
'KbdInteractiveAuthentication' ? boolean
'KbdInteractiveDevices' ? temp_ssh_kbdinteractivedevices[]
Expand All @@ -79,9 +101,12 @@ type ssh_config_opts = {
'PreferredAuthentications' ? string[]
'Protocol' ? long(1..2)
'ProxyCommand' ? string
'ProxyJump' ? string[]
'ProxyUseFdpass' ? boolean
'PubkeyAcceptedKeyTypes' ? temp_ssh_hostkeyalgorithms[]
'PubkeyAuthentication' ? boolean
'RekeyLimit' ? string
'RemoteCommand' ? string
'RemoteForward' ? string
'RequestTTY' ? string with match (SELF, "^(yes|no|force|auto)$")
'RevokedHostKeys' ? string[]
Expand All @@ -90,9 +115,11 @@ type ssh_config_opts = {
'SendEnv' ? string[]
'ServerAliveCountMax' ? long(0..)
'ServerAliveInterval' ? long(0..)
'SetEnv' ? string{}
'StreamLocalBindMask' ? string
'StreamLocalBindUnlink' ? boolean
'StrictHostKeyChecking' ? string with match (SELF, "^(yes|no|ask)$")
'SyslogFacility' ? string with match(SELF, "^(DAEMON|USER|AUTH|LOCAL[0-7])$")
'TCPKeepAlive' ? boolean
'Tunnel' ? string with match (SELF, "^(yes|no|point-to-point|ethernet)$")
'TunnelDevice' ? string
Expand Down Expand Up @@ -123,3 +150,114 @@ type ssh_config_file = {
'main' ? ssh_config_opts
};

# Not all options may appear inside a Match block
type sshd_config_match_opts = {
'AcceptEnv' ? string[]
'AllowAgentForwarding' ? boolean
'AllowGroups' ? string[]
'AllowStreamLocalForwarding' ? string with match (SELF, "^(yes|all|no|local|remote)$")
'AllowTcpForwarding' ? string with match (SELF, "^(yes|all|no|local|remote)$")
'AllowUsers' ? string[]
'AuthenticationMethods' ? string[] # Don't go into details - it does not seem to worth the effort
'AuthorizedKeysCommand' ? absolute_file_path
'AuthorizedKeysCommandUser' ? string
'AuthorizedKeysFile' ? string[]
'AuthorizedPrincipalsCommand' ? absolute_file_path
'AuthorizedPrincipalsCommandUser' ? string
'AuthorizedPrincipalsFile' ? string[]
'Banner' ? string
'ChrootDirectory' ? string
'ClientAliveCountMax' ? long(1..)
'ClientAliveInterval' ? long(0..)
'DenyGroups' ? string[]
'DenyUsers' ? string[]
'ForceCommand' ? string
'GatewayPorts' ? string with match (SELF, "^(yes|no|clientspecified)$")
'GSSAPIAuthentication' ? boolean
'HostbasedAcceptedKeyTypes' ? temp_ssh_hostkeyalgorithms[]
'HostbasedAuthentication' ? boolean
'HostbasedUsesNameFromPacketOnly' ? boolean
'IPQoS' ? string[] with length(SELF) == 1 || length(SELF) == 2
'KbdInteractiveAuthentication' ? boolean
'KerberosAuthentication' ? boolean
'LogLevel' ? string with match (SELF, "^(QUIET|FATAL|ERROR|INFO|VERBOSE|DEBUG[123]?)$")
'MaxAuthTries' ? long(1..)
'MaxSessions' ? long(0..)
'PasswordAuthentication' ? boolean
'PermitEmptyPasswords' ? boolean
'PermitListen' ? string[] # type_hostport would not allow wildcards
'PermitOpen' ? string[] # type_hostport would not allow wildcards
'PermitRootLogin' ? string with match (SELF, "^(yes|prohibit-password|without-password|forced-commands-only|no)$")
'PermitTTY' ? boolean
'PermitTunnel' ? string with match (SELF, "^(yes|point-to-point|ethernet|no)$")
'PermitUserRC' ? boolean
'PubkeyAcceptedKeyTypes' ? temp_ssh_hostkeyalgorithms[]
'PubkeyAuthentication' ? boolean
'RekeyLimit' ? string[] with length(SELF) == 1 || length(SELF) == 2
'RSAAuthentication' ? boolean
'RhostsRSAAuthentication' ? boolean
'RevokedKeys' ? string
'RDomain' ? string
'SetEnv' ? string{}
'StreamLocalBindMask' ? string with match (SELF, "^[0-7]{3,5}$")
'StreamLocalBindUnlink' ? boolean
'TrustedUserCAKeys' ? string
'X11DisplayOffset' ? long(0..)
'X11Forwarding' ? boolean
'X11UseLocalHost' ? boolean
};

type sshd_config_match = {
"matches" : string[]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gombasg why did you choose this so freeform? why not a list of dicts wit allowed keys and arbitrary values?

include sshd_config_match_opts
};

type sshd_config_opts = {
include sshd_config_match_opts
'AddressFamily' ? string with match (SELF, "^(any|inet|inet6)$")
'CASignatureAlgorithms' ? temp_ssh_CAAlgorithms[]
'ChallengeResponseAuthentication' ? boolean
'Ciphers' ? temp_ssh_ciphers[]
'Compression' ? boolean
'DisableForwarding' ? boolean
'ExposeAuthInfo' ? boolean
'FingerprintHash' ? string with match (SELF, "^(md5|sha256)$")
'GSSAPICleanupCredentials' ? boolean
'GSSAPIKeyExchange' ? boolean
'GSSAPIKexAlgorithms' ? temp_ssh_gss_kexalgorithms[]
'GSSAPIStrictAcceptorCheck' ? boolean
'GSSAPIStoreCredentialsOnRekey' ? boolean
'HostCertificate' ? string
'HostKey' ? string[]
'HostKeyAgent' ? string
'HostKeyAlgorithms' ? temp_ssh_hostkeyalgorithms[]
'IgnoreRhosts' ? boolean
'IgnoreUserKnownHosts' ? boolean
'KerberosGetAFSToken' ? boolean
'KerberosOrLocalPasswd' ? boolean
'KerberosTicketCleanup' ? boolean
'KexAlgorithms' ? temp_ssh_kexalgorithms[]
'ListenAddress' ? type_hostport[]
'LoginGraceTime' ? long(0..)
'MACs' ? temp_ssh_MACs[]
'Match' ? sshd_config_match[]
'MaxStartups' ? string with match (SELF, "^[0-9]+(:[0-9]+:[0-9]+)?$")
'PermitUserEnvironment' ? boolean
'PidFile' ? absolute_file_path
'Port' ? long(1..)[]
'PrintLastLog' ? boolean
'PrintMotd' ? boolean
'StrictModes' ? boolean
'Subsystem' ? string{}
'SyslogFacility' ? string with match (SELF, "^(DAEMON|USER|AUTH|LOCAL[0-7])$")
'TCPKeepAlive' ? boolean
'UseDNS' ? boolean
'UsePAM' ? boolean
'VersionAddendum' ? string
'XAuthLocation' ? absolute_file_path
};

type sshd_config_file = {
'Match' ? sshd_config_match[]
'main' ? sshd_config_opts
};
8 changes: 8 additions & 0 deletions ncm-metaconfig/src/main/metaconfig/ssh/pan/server_config.pan
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
unique template metaconfig/ssh/server_config;

include 'metaconfig/ssh/schema';

bind "/software/components/metaconfig/services/{/etc/ssh/sshd_config}/contents" = sshd_config_file;

prefix "/software/components/metaconfig/services/{/etc/ssh/sshd_config}";
"module" = "ssh/server";
7 changes: 7 additions & 0 deletions ncm-metaconfig/src/main/metaconfig/ssh/server.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

[% INCLUDE metaconfig/ssh/server_attrs.tt data=main -%]

[% FOREACH mt IN Match -%]
Match [% mt.matches.join(' ') %]
[% INCLUDE metaconfig/ssh/server_attrs.tt data=mt FILTER indent %]
[% END -%]
35 changes: 35 additions & 0 deletions ncm-metaconfig/src/main/metaconfig/ssh/server_attrs.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[% spacelist = ['AcceptEnv', 'AllowGroups', 'AllowUsers', 'AuthenticationMethods', 'AuthorizedKeysFile', 'AuthorizedPrincipalsFile',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems the longest list; use this as the default list fallback

'DenyGroups', 'DenyUsers',
'IPQoS',
'PermitListen', 'PermitOpen',
'RekeyLimit' ] -%]
[% commalist = ['Ciphers', 'HostKeyAlgorithms', 'HostbasedAcceptedKeyTypes', 'KexAlgorithms', 'MACs', 'PubkeyAcceptedKeyTypes' ] -%]
[% multilinelist = ['HostKey', 'ListenAddress', 'Port' ] -%]

[%- FOREACH pair IN data.pairs -%]
[% NEXT IF pair.key == 'matches' -%]
[% IF pair.value.is_boolean -%]
[% pair.key %] [% pair.value ? 'Yes' : 'No' %]
[% NEXT -%]
[% END -%]
[% SWITCH pair.key -%]
[% CASE spacelist -%]
[% pair.key %] [% pair.value.join(' ') %]
[% CASE commalist -%]
[% pair.key %] [% pair.value.join(',') %]
[% CASE multilinelist -%]
[% FOREACH line IN pair.value -%]
[% pair.key %] [% line %]
[% END -%]
[% CASE 'Subsystem' -%]
[% FOREACH item IN pair.value.pairs -%]
Subsystem [% item.key %] [% item.value %]
[% END -%]
[% CASE 'SetEnv' -%]
[% FOREACH item IN pair.value.pairs -%]
SetEnv [% item.key %]="[% item.value %]"
[% END -%]
[% CASE -%]
[% pair.key %] [% pair.value %]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or do the boolean check here, and maybe add a the most common type pf list and have that here as default list format (saves you another static list)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[%             pair.key %] [% -%]
[-% IF pair.value.is_boolean -%]
[%- pair.value ? 'Yes' : 'No' -%]
[%- ELSIF CCM.is_list(pair.value) -%]
[%- pair.value.join(' ') -%]
[%- ELSE -%]
[%- pair.value -%]
[-% END -%]

[% END -%]
[% END -%]
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ include 'metaconfig/ssh/client_config';

prefix "/software/components/metaconfig/services/{/etc/ssh/ssh_config}/contents";

"main/AddKeysToAgent" = "confirm";
"main/AddressFamily" = "any";
"main/BatchMode" = true;
"main/BindAddress" = "192.168.1.1";
"main/BindInterface" = "eth0";
"main/CASignatureAlgorithms" = list("ssh-ed25519", "rsa-sha2-512");
"main/CanonicalDomains" = list(".com", ".eu");
"main/CanonicalizeFallbackLocal" = true;
"main/CanonicalizeHostname" = "always";
"main/CanonicalizePermittedCNAMEs" = list("*.a.example.com:*.b.example.com", "*.c.example.com");
"main/CertificateFile" = list("/absolute/path", "~/home/relative");
"main/ChallengeResponseAuthentication" = false;
"main/CheckHostIP" = true;
"main/Cipher" = "3des";
Expand All @@ -35,16 +39,23 @@ prefix "/software/components/metaconfig/services/{/etc/ssh/ssh_config}/contents"
"main/GatewayPorts" = false;
"main/GlobalKnownHostsFile" = list("/etc/ssh/ssh_known_hosts", "/etc/ssh/ssh_known_hosts2");
"main/GSSAPIAuthentication" = false;
"main/GSSAPIClientIdentity" = "custom";
"main/GSSAPIDelegateCredentials" = false;
"main/GSSAPIKeyExchange" = true;
"main/GSSAPIRenewalForcesRekey" = true;
"main/GSSAPIServerIdentity" = "server@principal";
"main/GSSAPITrustDns" = false;
"main/HashKnownHosts" = false;
"main/HostbasedAuthentication" = true;
"main/HostbasedKeyTypes" = list("*");
"main/HostKeyAlgorithms" = list("ssh-rsa-cert-v01@openssh.com", "ssh-dss-cert-v01@openssh.com");
"main/HostKeyAlias" = "hostname2";
"main/HostName" = "%h";
"main/IdentitiesOnly" = true;
"main/IdentityAgent" = "none";
"main/IdentityFile" = list("~/.ssh/identity", "~/.ssh/id_rsa", "~/.ssh/id_dsa");
"main/IgnoreUnknown" = list("IPQoS", "KbdInteractiveAuthentication");
"main/Include" = list("/absolute/path", "~/home/relative", "sshconf_relative");
"main/IPQoS" = "throughput";
"main/KbdInteractiveAuthentication" = true;
"main/KbdInteractiveDevices" = list("pam", "skey");
Expand All @@ -62,9 +73,12 @@ prefix "/software/components/metaconfig/services/{/etc/ssh/ssh_config}/contents"
"main/PreferredAuthentications" = list("gssapi-with-mic", "hostbased", "publickey");
"main/Protocol" = 2;
"main/ProxyCommand" = "ssh -q -W %h:%p gateway.example.com";
"main/ProxyJump" = list("user1@host1", "user2@host2");
"main/ProxyUseFdpass" = false;
"main/PubkeyAcceptedKeyTypes" = list("-ssh-rsa");
"main/PubkeyAuthentication" = true;
"main/RekeyLimit" = "1G";
"main/RemoteCommand" = "cat /etc/motd";
"main/RemoteForward" = "*";
"main/RequestTTY" = "force";
"main/RevokedHostKeys" = list("~/.ssh/revokedkeys.txt");
Expand All @@ -73,9 +87,11 @@ prefix "/software/components/metaconfig/services/{/etc/ssh/ssh_config}/contents"
"main/SendEnv" = list("LANG", "LC_CTYPE", "LC_NUMERIC", "LC_TIME");
"main/ServerAliveCountMax" = 4;
"main/ServerAliveInterval" = 0;
"main/SetEnv" = dict("LC_ALL", "C", "TERM", "xterm");
"main/StreamLocalBindMask" = "0177";
"main/StreamLocalBindUnlink" = false;
"main/StrictHostKeyChecking" = "ask";
"main/SyslogFacility" = "LOCAL1";
"main/TCPKeepAlive" = true;
"main/Tunnel" = "ethernet";
"main/TunnelDevice" = "tun0";
Expand Down
Loading