Wiki source code of OpenVPN
Version 2.1 by Sebastian Marsching on 2022/05/29 13:40
Hide last authors
| author | version | line-number | content |
|---|---|---|---|
| |
1.1 | 1 | {{toc/}} |
| 2 | |||
| 3 | # MTU issues with OpenVPN | ||
| 4 | |||
| 5 | When packages that are travelling through an OpenVPN tunnel seem to disappear or connections (seem to not react any more (e.g. a webpage keeps loading and loading), this issues might be related to problems with the [MTU](http://en.wikipedia.org/wiki/Maximum_transmission_unit) settings. | ||
| 6 | |||
| 7 | fragment 1300 | ||
| 8 | mssfix | ||
| 9 | |||
| 10 | You might have to change the fragment value, but 1300 should be okay for a first try. | ||
| 11 | |||
| |
2.1 | 12 | See also: [[Path MTU Discovery issues|doc:Miscellaneous.Network.IP.WebHome|anchor="HPathMTUDiscoveryIssues"]] |
| |
1.1 | 13 | |
| 14 | # Authenticating with Active Directory | ||
| 15 | |||
| 16 | The following script can be used with OpenVPNs `auth-user-pass-verify` directive. The script reads the username and password from a file, so the `via-file` method should be specified. This script needs a fairly modern version of the `ldap3` module (it has been tested with version 1.3.1). | ||
| 17 | |||
| 18 | ```python | ||
| 19 | #!/usr/bin/python | ||
| 20 | |||
| 21 | # Authenticates an OpenVPN user against an Active Directory using LDAP. | ||
| 22 | # The authentication is successful when the specified credentials can be used to | ||
| 23 | # successfully connect with the LDAP service and the user is a member of the | ||
| 24 | # specified group. The membership check includes transitive memberships. | ||
| 25 | # The username is expected to be the SAM account name (without the domain). | ||
| 26 | # The password is expected to be the raw (unhashed) password. | ||
| 27 | # The username and password are read from the file specified as the only | ||
| 28 | # parameter to this script so that do not have to be passed through | ||
| 29 | # potentially unsafe environment variables. | ||
| 30 | |||
| 31 | import io | ||
| 32 | import ldap3 | ||
| 33 | import sys | ||
| 34 | |||
| 35 | # Find the principal (LDAP object) for the specified SAM account name. | ||
| 36 | # The SAM account name must not include the domain. | ||
| 37 | def find_principal_by_sam_account_name(ldap_connection, base_dn, sam_account_name): | ||
| 38 | if not ldap_connection.search(search_base = base_dn, search_filter = ('(sAMAccountName=' + ldap3.utils.conv.escape_filter_chars(sam_account_name) + ')')): | ||
| 39 | return None | ||
| 40 | if len(ldap_connection.entries) == 0: | ||
| 41 | return None | ||
| 42 | return ldap_connection.entries[0] | ||
| 43 | |||
| 44 | # This method is useful if we need all groups that the user is a member of. | ||
| 45 | def find_all_groups_principal_is_member_of(ldap_connection, base_dn, principal_dn): | ||
| 46 | if not ldap_connection.search(search_base = base_dn, search_filter = ('(member:1.2.840.113556.1.4.1941:=' + ldap3.utils.dn.safe_dn(principal_dn) + ')'), attributes = ldap3.ALL_ATTRIBUTES): | ||
| 47 | return None | ||
| 48 | return ldap_connection.entries | ||
| 49 | |||
| 50 | # Checks whether the specified principal (identified by its DN) is a member of | ||
| 51 | # the specified group (identified by its SID). | ||
| 52 | def is_principal_member_of_group(ldap_connection, base_dn, principal_dn, group_sid): | ||
| 53 | if not ldap_connection.search(search_base = base_dn, search_filter = ('(&(objectSid=' + ldap3.utils.conv.escape_filter_chars(group_sid) + ')(member:1.2.840.113556.1.4.1941:=' + ldap3.utils.dn.safe_dn(principal_dn) + '))')): | ||
| 54 | return False | ||
| 55 | return len(ldap_connection.entries) != 0 | ||
| 56 | |||
| 57 | |||
| 58 | # We use short timeouts so that we can failover to another domain controller | ||
| 59 | # when the first one fails. | ||
| 60 | connect_timeout = 1.0 | ||
| 61 | receive_timeout = 1.0 | ||
| 62 | base_dn = 'dc=ad,dc=example,dc=com' | ||
| 63 | # We can restrict the tree in which the principal must reside. If no | ||
| 64 | # restrictions are desired, we can simply use the base DN. | ||
| 65 | principal_search_base_dn = base_dn | ||
| 66 | domain = 'WINDOMAIN' | ||
| 67 | group_sid = 'S-1-5-21-1234567890-1234567890-1234567890-1234' | ||
| 68 | ldap_servers = [ 'dc-1.ad.example.com', 'dc-2.ad.example.com' ] | ||
| 69 | |||
| 70 | if len(sys.argv) != 2: | ||
| 71 | sys.exit(1) | ||
| 72 | |||
| 73 | user_pass_filename = sys.argv[1] | ||
| 74 | try: | ||
| 75 | user_pass_file = io.open(user_pass_filename) | ||
| 76 | username = user_pass_file.readline().strip() | ||
| 77 | password = user_pass_file.readline().strip() | ||
| 78 | user_pass_file.close() | ||
| 79 | except: | ||
| 80 | sys.exit(1) | ||
| 81 | |||
| 82 | ldap_username = domain + '\\' + username | ||
| 83 | |||
| 84 | connection = None | ||
| 85 | server = None | ||
| 86 | for ldap_server in ldap_servers: | ||
| 87 | try: | ||
| 88 | server = ldap3.Server(ldap_server, port = 636, use_ssl = True, connect_timeout = connect_timeout) | ||
| 89 | connection = ldap3.Connection(server, user = ldap_username, password = password, auto_bind = True, receive_timeout = receive_timeout) | ||
| 90 | break | ||
| 91 | except: | ||
| 92 | server = None | ||
| 93 | connection = None | ||
| 94 | |||
| 95 | if connection == None: | ||
| 96 | sys.exit(1) | ||
| 97 | |||
| 98 | try: | ||
| 99 | user_entry = find_principal_by_sam_account_name(connection, principal_search_base_dn, username) | ||
| 100 | if user_entry == None: | ||
| 101 | sys.exit(1) | ||
| 102 | user_dn = user_entry.entry_get_dn() | ||
| 103 | is_group_member = is_principal_member_of_group(connection, base_dn, user_dn, group_sid) | ||
| 104 | except: | ||
| 105 | sys.exit(1) | ||
| 106 | |||
| 107 | if is_group_member: | ||
| 108 | sys.exit(0) | ||
| 109 | else: | ||
| 110 | sys.exit(1) | ||
| 111 | ``` |