OPNsense Forum

English Forums => Tutorials and FAQs => Topic started by: Monviech on September 07, 2023, 02:10:34 pm

Title: [How-To] IPsec Connections [new] - Roadwarriors with IKEv2 EAP-MSCHAPv2
Post by: Monviech on September 07, 2023, 02:10:34 pm
LATEST VERSION: https://docs.opnsense.org/manual/how-tos/ipsec-swanctl-rw-ikev2-eap-mschapv2.html

I will implement roadwarrior usecases with eap-mschapv2 into the opnsense docs:
https://github.com/opnsense/docs/pull/501

There's no way to autoconfigure DNS Servers yet. It has been merged into main, it will probably come soon with an Update: https://github.com/opnsense/core/pull/6864

I appreciate feedback :)

The version below won't receive updates anymore.



I'm using EAP-MSCHAPv2.
Wrote this quick and will improve it over time.

Method 1: Shared IP pool for all roadwarriors:

Benefit: Easy configuration and works with most clients out of the box.
Downside: You could configure multiple of these roadwarrior vpns with different pools, you shouldn't though from a security standpoint. All EAP Identities can authenticate against each of those vpns and pools, so you can't have tight access control. (For example if you have multiple different roadwarrior groups or even customers logging in). For that you should use method 2. (Down further)

SERVER:

OPNSENSE configuration:

- Import Certificate into System: Trust: Authorities and System: Trust: Certificates. The whole chain has to be there. If you use Let's Encrypt it will probably already be there. Your Client needs to trust the intermediate and root certificate.

You can also create your own selfsigned certificate:

System: Trust: Authorities
Descriptive name: IPsec CA
Method: Create an internal Certificate Authority
Key Type: RSA
Key length (bits): 2048
Digest Algorithm: SHA256
Lifetime (days): 3650      (10 years)
Country Code :  Your country code
State or Province :  Your state
City : Your City
Organization: Your organization
Email Address: Your Email address
Common Name: ipsec-ca

- export CA cert, you need it later for your clients

System: Trust: Certificates
Method: Create an internal Certificate
Descriptive name: vpn1.example.com
Certificate authority: IPsec CA
Type: Client Certificate
Key Type: RSA
Key lenght (bits): 2048
Digest Algorithm: SHA256
Lifetime (days): 365        (1 year)
Private key location: Save on this firewall
Country Code :  Your country code
State or Province :  Your state
City : Your City
Organization: Your organization
Email Address: Your Email address
Common Name: ipsec-ca
Common Name:  vpn1.example.com
Alternative Names:  vpn1.example.com

- Create an FQDN with your external DNS Provider. For Example "vpn1.example.com in A 203.0.113.1". This should match with the SubjectAlternateName (SAN) of your certificate. Wildcard is also accepted by Windows and IOS.

- Goto VPN: IPsec: Connections [new] and press + to add a new Connection, enable advanced mode
General Settings:
enabled: yes
Proposals: aes256-sha256-modp2048   (Disable default!)
Version: IKEv2
MOBIKE: Yes
Local addresses: vpn1.example.com
UDP encapsulation: Yes
Rekey time: 2400
DPD delay: 30
Pools: Nothing selected (will be added later)
Send cert req: Yes
Send certificate: Always
Keyingtries: 0
Description: roadwarrior-eap-mschapv2-p1

Hit Save

Go to the tab Pools on top and select + to create a new pool. This pool will be shared by all road warriors that connect to your VPN.

Edit Pool
enabled: yes
Name: pool-roadwarrior
Network: 172.16.203.0/24

Hit Save

Go back to the Tab roadwarrior-eap-mschapv2
Press Save

Now Edit the connection roadwarrior-eap-mschapv2 again and add the pool
Pools: pool-roadwarrior

NOTE: You can select multiple pools here. For example, you can also create a pool-roadwarrior-ipv6 and add an IPv6 address range to it. Choose /120 to create a pool of 256 IPv6 addresses. Then your roadwarrior will get an ipv4 and an ipv6 address.

Local Authentication:
Connection: roadwarrior-eap-mschapv2
Round: 0
Authentication: Public Key
Id: vpn1.example.com
Certificates: vpn1.example.com
Public Keys: Nothing Selected
Description: opnsense-cert

Remote Authentication:
Connection: roadwarrior-eap-mschapv2
Round: 0
Authentication: EAP-MSCHAPv2
Id:
EAP Id: %any
(Possible since version 23.7.4)
Certificates: Nothing selected
Description: remote-eap-mschapv2

Children: (enable advanced mode)
enabled: yes
Mode: Tunnel
Policies: yes
Start action: Trap
Close action: None
DPD action: Clear
ESP proposals: aes256-sha256-modp2048   (Disable default!)
Local: 192.168.1.0/24     (Enter the Networks the Roadwarrior should access, for a split tunnel just 192.168.1.0/24... and for a full IPv4+IPv6 dual stack tunnel 0.0.0.0/0 ::/0. Don't forget Outbound NAT if you go for full tunnel)
Remote:       (Important, leave this empty)
Rekey time (s): 600
Description: roadwarrior-eap-mschapv2-p2

- Save
and another
- Save

VPN: IPsec: Pre-Shared Keys
Press +
Edit pre-shared-key:
Local Identifier: john@vpn1.example.com (The username of John)
Pre-Shared Key: 48o72g3h4ro8123g8r (Create your own its the password John will use)
Type: EAP

Apply

- Make sure that your Firewall accepts UDP 500 and UDP 4500 from all Source IPs and all Source Ports.
- Don't forget to allow 172.16.203.0/24 in the Firewall to the Network 192.168.1.0/24.

Here is a raw view of the configuration file, check your own to easily spot mistakes:

Note: The secret is hashed. The 0s is the hashing method.

Code: [Select]
/usr/local/etc/swanctl/swanctl.conf
Code: [Select]
connections {
    077f479a-9c8e-4b15-ae35-8f1d2fb1c4ad {
        proposals = aes256-sha256-modp2048
        unique = no
        aggressive = no
        version = 2
        mobike = yes
        local_addrs = vpn1.example.com
        encap = yes
        rekey_time = 2400
        dpd_delay = 30
        pools = pool-roadwarrior
        send_certreq = yes
        send_cert = always
        keyingtries = 0
        local-7f316b87-b8cc-4788-b6f3-3ee20a05811e {
            round = 0
            auth = pubkey
            id = vpn1.example.com
            certs = 64f6fcdf57ea7.crt
        }
        remote-d15c8d9d-7396-4642-b6ba-9d1f94625297 {
            round = 0
            auth = eap-mschapv2
            eap_id = %any
        }
        children {
            1371f735-5424-47fb-b564-b8b9e459e77b {
                esp_proposals = aes256-sha256-modp2048
                sha256_96 = no
                start_action = trap
                close_action = none
                dpd_action = clear
                mode = tunnel
                policies = yes
                local_ts = 192.168.1.0/24
                rekey_time = 600
                updown = /usr/local/opnsense/scripts/ipsec/updown_event.py --con                                                                                                             nection_child 1371f735-5424-47fb-b564-b8b9e459e77b
            }
        }
    }

pools {
    pool-roadwarrior {
        addrs = 172.16.203.0/24
    }

}

secrets {
    eap-36e3e573-2a1b-4837-bd35-7bfb8a17cb2a {
        id-0 = john@vpn1.example.com
        secret = 0sw435w34trgjftu4e6435t534t
        }
}


ADDITIONAL ROADWARRIORS:

- For additional Roadwarriors you can create new EAP Identifiers in Pre-Shared Keys.

CLIENTS:

WINDOWS 10+11
Code: [Select]
Add-VpnConnection -Name "vpn1.example.com" -ServerAddress vpn1.example.com -TunnelType "Ikev2"

Set-VpnConnectionIPsecConfiguration -ConnectionName "vpn1.example.com" -AuthenticationTransformConstants SHA256 -CipherTransformConstants AES256 -EncryptionMethod AES256 -IntegrityCheckMethod SHA256 -PfsGroup PFS2048 -DHGroup Group14 -PassThru -Force

Set-VpnConnection -Name "vpn1.example.com" -SplitTunneling $true

The root and intermediate certificate have to be in trusted root authorities in the windows certificate store.

Username: john@vpn1.example.com
Password: 48o72g3h4ro8123g8r

IOS:
- Import the CA certificate of your self signed or bought certificate into the ios certificate store
- Just use the inbuild VPN and add the server vpn1.example.com, remote ID vpn1.example.com, Local ID john@vpn1.example.com, username john@vpn1.example.com and password 48o72g3h4ro8123g8r.

Android:
The inbuild VPN client refuses to work somehow. But with the official strongswan app it's a breeze:
- Import the CA certificate of your self signed or bought certificate into the android certificate store
- Edit the options below in a new profile in the strongswan app:
Code: [Select]
Server: vpn1.example.com
VPN Typ: IKEv2 EAP
Username: john@vpn1.example.com
Password: 48o72g3h4ro8123g8r
CA-Certificate: choose the imported CA certificate

- Activate advanced mode:

DNS Server: Add your DNS server, for example the Unbound of your OPNsense firewall. Make sure you allow port53 tcp udp to "this firewall" in Firewall:Rules:IPsec.
IKEv2 Algorithms: aes256-sha256-modp2048
IPsec/ESP Algorithms: aes256-sha256-modp2048


Linux:
Strongswan supports this configuration, there cant be wildcard certificates. The CA Certs need to be in the trust store strongswan accesses. Just look at the config in swanctl.conf on the OPNsense and alter it to fit the other side. I'll write an example soon.

--------------------------------------------------------------------------------------------------------------------------------------------

Method 2: Static IP address per roadwarrior:

Goal: Every roadwarrior user always gets the same static IP.

Benefit: Tight security because every user can be controlled individually with firewall rules. The whole configuration is stored in one file (swanctl.conf). There are no other dependencies, so it won't break suddenly in the future.
Downside: Inbuild Windows VPN client doesn't like this configuration very much. Configuration needs a lot more time and might not scale to large user counts. For large user counts (>1000) it might be better to use eap-radius with user groups if thats implemented.


SERVER:

OPNSENSE configuration:

Create the Configuration the same as above, but change the following parameters:

- Create a pool with one ipv4 and optional one ipv6 address per roadwarrior

For example:

Name: pool-roadwarrior-john
Network: 172.16.203.1/32

Name: pool-roadwarrior-laura
Network: 172.16.203.2/32

- Each roadwarrior gets their own connection (phase 1 (connection) and phase 2 (children). That's because the remote authentication is changed from EAP Id: %any to the actual EAP id: john@vpn1.example.com or laura@vpn1.example.com. That means each roadwarrior is only able to connect to their own phase 1 and phase 2.

Example:

Remote Authentication:
Connection: roadwarrior-john-p1
Round: 0
Authentication: EAP-MSCHAPv2
Id:
EAP Id: john@vpn1.example.com
Certificates: Nothing selected
Description: remote-eap-john

Remote Authentication:
Connection: roadwarrior-laura-p1
Round: 0
Authentication: EAP-MSCHAPv2
Id:
EAP Id: laura@vpn1.example.com
Certificates: Nothing selected
Description: remote-eap-john

ADDITIONAL ROADWARRIORS:

For additional Roadwarriors you can clone the connection, and just add a new Pool with 172.16.203.2/32 etc... , edit the remote authentication eap_id to the new name, and create a new EAP Secret in Pre shared keys.

Here is a raw view of the configuration file with two roadwarriors, check your own to easily spot mistakes:

Code: [Select]
/usr/local/etc/swanctl/swanctl.conf
Code: [Select]
connections {
    077f479a-9c8e-4b15-ae35-8f1d2fb1c4ad {
        proposals = aes256-sha256-modp2048
        unique = no
        aggressive = no
        version = 2
        mobike = yes
        local_addrs = vpn1.example.com
        encap = yes
        rekey_time = 2400
        dpd_delay = 30
        pools = pool-roadwarrior-john
        send_certreq = yes
        send_cert = always
        keyingtries = 0
        local-7f316b87-b8cc-4788-b6f3-3ee20a05811e {
            round = 0
            auth = pubkey
            id = vpn1.example.com
            certs = 64f6fcdf57ea7.crt
        }
        remote-d15c8d9d-7396-4642-b6ba-9d1f94625297 {
            round = 0
            auth = eap-mschapv2
            eap_id = john@vpn1.example.com
        }
        children {
            1371f735-5424-47fb-b564-b8b9e459e77b {
                esp_proposals = aes256-sha256-modp2048
                sha256_96 = no
                start_action = trap
                close_action = none
                dpd_action = clear
                mode = tunnel
                policies = yes
                local_ts = 192.168.1.0/24
                rekey_time = 600
                updown = /usr/local/opnsense/scripts/ipsec/updown_event.py --con                                                                                                             nection_child 1371f735-5424-47fb-b564-b8b9e459e77b
            }
        }
     077f479a-9c8e-4b15-ae35-1213234abe121 {
        proposals = aes256-sha256-modp2048
        unique = no
        aggressive = no
        version = 2
        mobike = yes
        local_addrs = vpn1.example.com
        encap = yes
        rekey_time = 2400
        dpd_delay = 30
        pools = pool-roadwarrior-laura
        send_certreq = yes
        send_cert = always
        keyingtries = 0
        local-7f316b87-b8cc-4788-b6f3-3ee20a05811e {
            round = 0
            auth = pubkey
            id = vpn1.example.com
            certs = 64f6fcdf57ea7.crt
        }
        remote-d15c8d9d-7396-4642-b6ba-9d1f94625297 {
            round = 0
            auth = eap-mschapv2
            eap_id = laura@vpn1.example.com
        }
        children {
            1371f735-5424-47fb-b564-25242b3b23123b {
                esp_proposals = aes256-sha256-modp2048
                sha256_96 = no
                start_action = trap
                close_action = none
                dpd_action = clear
                mode = tunnel
                policies = yes
                local_ts = 192.168.1.0/24
                rekey_time = 600
                updown = /usr/local/opnsense/scripts/ipsec/updown_event.py --con                                                                                                             nection_child 1371f735-5424-47fb-b564-b8b9e459e77b
            }
        }
    }

pools {
    pool-roadwarrior-john {
        addrs = 172.16.203.1/32
    }
        pool-roadwarrior-laura {
        addrs = 172.16.203.2/32
    }

}

secrets {
    eap-36e3e573-2a1b-4837-bd35-7bfb8a17cb2a {
        id-0 = john@vpn1.example.com
        secret = 0sw435w34trgjftu4e6435t534t
        }
    eap-44e3e573-2a1b-4837-bd35-7625bca17231 {
        id-0 = laura@vpn1.example.com
        secret = 0s324389z67dsfb3b234xcv
        }
}


CLIENTS:

WINDOWS 10+11

Same configuration as above, though:

The inbuild Windows VPN client doesn't send it's local ID on the first authentication round. That means that John or Laura has to type their passwort twice before the connection establishes. You can mitigate one authentication round by saving the username and password into the vpn profile. Then they only have to input the password. Attention: If they press cancel or click outside of the authentication window, it will vanish and trying to connect again will fail until the PC is rebooted!

I recommend using the NCP client for professional deployments in this regard.

NCP Client (on Windows):
You have to put the intermediate and root certificate into "C:\programdata\ncp\secureclient\cacerts" in .pem format.

Here is an ini file of an example configuration of the rest. You have to add username and password if you don't want the popup when starting a connection. You can also set dns servers. And you don't have to enable split tunneling because the ike config mode pushes the traffic selectors automatically.

Code: [Select]
[GENERAL]
Export=1
Product=NCP Secure Entry Client
Version=13.14 Build 29669
Date=11.09.2023 09:30:42
[PROFILE1]
Name=vpn1.example.com
ConnMedia=21
UseForAuto=0
SeamRoaming=1
NotKeepVpn=0
BootProfile=0
UseRAS=0
SavePw=0
PhoneNumber=
DialerPhone=
ScriptFile=
HttpName=
HttpPw=
HttpScript=
Modem=
ComPort=1
Baudrate=57600
RelComPort=1
InitStr=
DialPrefix=
3GApnSrc=2
3GProvider=
APN=
3GPhone=
3GAuth=0
GprsATCmd=AT+CPIN=
GprsPin=""
BiometricAuth=0
PreAuthEap=0
PreAuthHttp=0
ConnMode=0
Timeout=0
TunnelTrafficMonitoring=0
TunnelTrafficMonitoringAddr=0.0.0.0
QoS=none
PkiConfig=
ExchMode=34
TunnelIpVersion=1
IKEv2Auth=3
IKE-Policy=automatic mode
IKEv2Policy=aes256-sha256
IkeDhGroup=14
IkeLTSec=000:00:40:00
IPSec-Policy=aes256-sha256
PFS=14
IPSecLTType=1
IpsecLTSec=000:00:10:00
IPSecLTKb=50000
UseComp=0
IkeIdType=3
IkeIdStr=john@vpn1.example.com
Gateway=vpn1.example.com
ConnType=1
UsePreShKey=0
XAUTH-Src=0
SplitOptionV4=1
UseTunnel=1
SplitOptionV6=1
VpnBypass=none
UseXAUTH=1
UseUdpEnc=500
UseUdpEncTmp=4500
DisDPD=0
DPDInterval=30
DPDRetrys=8
AntiReplay=0
PathFinder=0
UseRFC7427=1
RFC7427Padding=2
Ikev2AuthPrf=0
CertReqWithData=0
IpAddrAssign=0
IPAddress=
SubnetMask=
DNS1=
DNS2=
DomainName=
DomainInTunnel=
SubjectCert=
IssuerCert=
FingerPrint=
UseSHA1=0
Firewall=0
OnlyTunnel=0
RasOnlyTunnel=0
DNSActiv=1
DNS1Tmp=
DNS2Tmp=
[IKEV2POLICY1]
Ikev2Name=aes256-sha256
Ikev2Crypt=6
Ikev2PRF=5
Ikev2IntAlgo=12
[IPSECPOLICY1]
IPSecName=aes256-sha256
IpsecCrypt=6
IpsecAuth=5

IOS:

Same as above

Android:

Same as above

Linux:

Same as above

Now you have a secure VPN connection that only one user can connect to with username and password that always gets the same IP and works with Windows or iOS. It's easily cloned for more users. Please note that there us no IP-Masquerading (Outbound NAT) or DNS explained in this How-To. Its just the actual tunnel.