Skip to content

EdgeServers/remote-swan

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

remotSwan — StrongSwan IPsec VPN for Webmin

A Webmin module for managing StrongSwan IPsec site-to-site tunnels and road-warrior connections, through either the modern swanctl / vici stack or the legacy ipsec.conf / stroke stack — chosen automatically.

  • Version: 1.0
  • Category: Networking
  • Requires: Webmin ≥ 2.651, Perl ≥ 5.26, StrongSwan 5.9+ (strongswan-swanctl + charon-systemd, or the legacy strongswan metapackage)
  • OS: any Linux; developed and verified on Ubuntu 22.04 / StrongSwan 5.9.5

What it does

  • Reads and writes tunnel configuration in place — your comments, ordering and any directive the module does not manage are left untouched. It edits the lines it owns and nothing else.
  • Presents the editor as a VPN request form would: General → Phase 1 (IKE) → Phase 2 (IPsec) → Advanced, with a ? tooltip on every field naming the form row it maps to.
  • Manages tunnels (site-to-site and road-warrior, one or many traffic-selector pairs), pre-shared keys / EAP / X.509 credentials, address pools, and the daemon itself (start / stop / reload / boot).
  • Detects the dangerous "both stacks running" state and refuses to write until it is resolved, because two charon daemons fight over the same kernel IPsec state.

Installation

From the packaged module (recommended)

  1. In Webmin: Webmin → Webmin Configuration → Webmin Modules → Install Module → From uploaded file, and upload remotswan-1.0.wbm.gz.
  2. Or on the server command line:
    /usr/share/webmin/install-module.pl --acl root /path/to/remotswan-1.0.wbm.gz
    
  3. Open Webmin → Networking → StrongSwan IPsec VPN.

The banner at the top of the main page tells you which backend is in use, which systemd unit it belongs to, and the StrongSwan version. If it says StrongSwan is not installed when it is, see Debugging → "StrongSwan not installed" but it is below.

Installing StrongSwan first

Modern stack (what the module prefers):

apt install strongswan-swanctl charon-systemd
systemctl disable --now strongswan-starter   # if the legacy stack is present
systemctl enable  --now strongswan

Legacy stack only:

apt install strongswan

Never run both at once. apt install strongswan-swanctl does not remove the legacy strongswan-starter, and neither systemd unit declares Conflicts= with the other. Two charon daemons bind the same kernel netlink sockets and corrupt each other's policies. The module shows a red banner and blocks all writes when it sees both active.

Developer install (from a git checkout)

sudo ln -s /path/to/remotSwan /usr/share/webmin/remotswan
sudo chmod +x /usr/share/webmin/remotswan/*.cgi /usr/share/webmin/remotswan/*.pl
# copy the per-OS config into place (install-module.pl does this for you normally):
sudo /usr/share/webmin/copyconfig.pl "$(sed -n 's/^os_type=//p' /etc/webmin/config)/$(sed -n 's/^real_os_type=//p' /etc/webmin/config)" \
     "$(sed -n 's/^os_version=//p' /etc/webmin/config)/$(sed -n 's/^real_os_version=//p' /etc/webmin/config)" \
     /usr/share/webmin /etc/webmin remotswan
# grant access, then restart:
sudo systemctl restart webmin

A symlinked module needs the copyconfig.pl step, and the module finds WebminCore.pm via SERVER_ROOT even when the symlink points out of the Webmin tree — but if you skip copyconfig.pl, %config is empty and detection cannot work.


Configuring a tunnel

The editor tabs mirror a VPN request form. In brief, for a typical PSK site-to-site link:

Tab Field Example
General IKE version IKEv2
Phase 1 (IKE) Local → Endpoint address 198.51.100.20
Phase 1 (IKE) Local / Remote → Authentication Pre-shared key
Phase 1 (IKE) Remote → Endpoint address 203.0.113.10
Phase 1 (IKE) IKE proposals aes256-sha512-ecp521
Phase 1 (IKE) Rekey IKE SA after 24h
Phase 2 (IPsec) Local networks 172.20.5.40/32
Phase 2 (IPsec) Remote networks 10.50.20.7/32, 10.50.10.11/32
Phase 2 (IPsec) ESP proposals aes256-sha512 (no DH group = No PFS)
Phase 2 (IPsec) Rekey IPsec SA after 1h

Then add the pre-shared key under Secrets and keys, with both peer addresses as its identities. There is a full worked example, including the traps, in the module's built-in help (the ? by the page title → Configuring a tunnel from a VPN request form).

Four things forms get wrong:

  1. No PFS means leaving the Diffie-Hellman group out of the ESP proposals — a group there turns PFS on.
  2. A form's single Lifetime is the rekey time, not the hard limit. Leave Delete … after on Default.
  3. A bare address in an encryption domain is a /32.
  4. Main / Aggressive mode does not exist in IKEv2 — ignore that row.

Every algorithm name is in Cipher suite reference below.

More than one network pair

The Phase 2 tab holds one block per traffic-selector pair. Some firewalls (FortiGate especially) create a separate Phase 2 per network, and expect the same from you: if a tunnel establishes but only one of several networks passes traffic, add the other pairs as their own Phase 2 blocks. Only the swanctl backend supports this; ipsec.conf is one pair per connection.


Cipher suite reference

A form gives you algorithms in vendor spelling — AES-256, SHA-512, DH Group 21. StrongSwan wants one hyphenated string. Getting one component wrong fails the whole negotiation, and the only symptom is NO_PROPOSAL_CHOSEN, so look everything up rather than guessing.

Phase 1   aes256-sha512-ecp521      encryption - hashing - DH group
Phase 2   aes256-sha512             encryption - authentication   (no group = No PFS)

Order is fixed. Multiple proposals are comma separated, and the peer picks one.

Phase 1 — Phase 1 (IKE)IKE proposals

Encryption Algorithm

Form says Use
DES desbroken, never use
3DES 3des — obsolete, only for old equipment
AES-128 / AES-192 / AES-256 aes128 / aes192 / aes256
AES-128-GCM / AES-256-GCM aes128gcm16 / aes256gcm16
ChaCha20-Poly1305 chacha20poly1305
Camellia-128 / -256 camellia128 / camellia256

Hashing Algorithm (the form may call it Authentication or Integrity)

Form says Use
MD5 md5broken, never use
SHA-1 sha1 — deprecated
SHA-256 / SHA-384 / SHA-512 sha256 / sha384 / sha512
AES-XCBC aesxcbc

Diffie-Hellman Group — forms give the number, so look up by number

Group Use Group Use
1 modp768broken 19 ecp256
2 modp1024 — weak 20 ecp384
5 modp1536 — weak 21 ecp521
14 modp2048 24 modp2048s256
15 modp3072 25 ecp192
16 modp4096 26 ecp224
17 modp6144 27 – 30 bp224 / bp256 / bp384 / bp512
18 modp8192 31 / 32 curve25519 / curve448

Phase 2 — Phase 2 (IPsec)ESP proposals

Same encryption and authentication keywords as above. Two rules decide the rest:

  • PFS is the DH group's presence, not a checkbox. Append a group to turn it on, leave it out for No PFS. Group numbers translate exactly as in Phase 1.
  • An AEAD cipher takes no separate authentication algorithm. aes256gcm16 is a complete proposal; aes256gcm16-sha256 is wrong. GCM already authenticates.

Encapsulation

One of the more confusing form rows, because it can mean any of four things — and they land in four different places:

Form says Means Where it goes
Encapsulation: ESP encrypts the payload Nowhere — StrongSwan uses ESP unless told otherwise
Encapsulation: AH authenticates only, no encryption Not supported — see Known limitations
Mode: Tunnel / Transport whether an inner IP header is added Phase 2 (IPsec)Mode
NAT-T / NAT traversal wraps ESP in UDP to cross NAT AdvancedForce UDP encapsulation — leave on Default, which auto-detects

Worked example

A form reading AES-256 / SHA-512 / DH Group 21, and AES-256 / SHA-512 / No PFS:

Phase 1 (IKE)     IKE proposals    aes256-sha512-ecp521
Phase 2 (IPsec)   ESP proposals    aes256-sha512

Confirm it negotiated by comparing charon's two lines — they must be identical:

received proposals:   IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521
configured proposals: IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521

Note charon echoes back its own internal names — sha512 appears as HMAC_SHA2_512_256 for integrity and PRF_HMAC_SHA2_512 for the pseudo-random function, and ecp521 as ECP_521. That is the same proposal, not a mismatch.

The full worked example, with the traps, is in the built-in help (the ? by the page title → Configuring a tunnel from a VPN request form).


Debugging

StrongSwan validates configuration at load time, not when the daemon starts — so systemctl start succeeding tells you almost nothing about whether your tunnels loaded. Most problems fall into a few buckets.

The module reports the wrong backend, or refuses to save

  • Red banner, "both stacks running": you have both strongswan.service (swanctl) and strongswan-starter.service (stroke) active. Pick one:
    systemctl disable --now strongswan-starter    # keep swanctl
    
    The banner offers a [Disable it] link that does this for the stack you are not using.
  • Yellow banner, "will conflict at boot": the idle stack is still enabled at boot. Same fix — disable it.

"StrongSwan not installed" but it is

The module's own settings were never copied into /etc/webmin/remotswan/config (common after a symlink install). The banner names this specifically. Run the copyconfig.pl command from Developer install above, or re-install through Webmin.

A tunnel will not come up

Work outward from the config:

  1. Does the config load at all?

    swanctl --load-all --noprompt        # modern
    ipsec reload                          # legacy
    

    Errors here are syntax or algorithm problems. The module's raw editor checks syntax before saving, but a hand edit outside it can still break the file.

  2. Did charon accept the connection?

    swanctl --list-conns                  # modern
    ipsec statusall                       # legacy
    

    If your tunnel is missing, it did not load — check the log (below).

  3. Bring it up and watch:

    swanctl --initiate --child <name>     # modern
    ipsec up <name>                       # legacy
    
  4. Read the log — this is where StrongSwan explains itself:

    journalctl -u strongswan.service -f            # modern (charon-systemd)
    journalctl -u strongswan-starter.service -f    # legacy
    # or, if it logs to syslog:
    tail -f /var/log/syslog | grep -Ei 'charon|ipsec'
    

Common causes, by symptom

Symptom Usual cause
NO_PROPOSAL_CHOSEN Phase 1 or Phase 2 algorithms do not match the peer. Compare the form exactly, including the PFS / DH group on Phase 2.
Tunnel establishes, no traffic Traffic selectors (encryption domains) do not match, or a bare IP was entered without /32, or the peer wants a separate Phase 2 per network.
AUTHENTICATION_FAILED Pre-shared key differs between ends (a trailing space is the classic), or the wrong identity.
Peer rejects our identity This host is behind NAT (e.g. a cloud instance with an Elastic IP) and is offering its private address. Set Phase 1 → Local → Identity to the public address. Check with ip addr.
IKE SA up, no child SA Phase 2 mismatch — look at PFS and the traffic selectors first.
Nothing on UDP 500/4500 Firewall. Allow UDP 500 and 4500 from the peer; on a cloud host this is a security-group rule.

Increase charon's log verbosity

Edit /etc/strongswan.d/charon-logging.conf (or use the module's raw editor on strongswan.conf), then reload:

charon {
    filelog {
        /var/log/charon.log {
            default = 2          # 0=silent .. 4=very verbose; 2 is a good debugging level
            ike = 2
            cfg = 2
        }
    }
}
swanctl --reload-settings     # apply the logging change without dropping tunnels

Manually driving StrongSwan (outside the UI)

Useful for verifying what the module did, or working when Webmin is unavailable. These require root (they talk to charon's control socket).

Modern stack — swanctl

Task Command
Load everything (conns, secrets, pools, certs) swanctl --load-all --noprompt
Load just connections swanctl --load-conns
Load just credentials swanctl --load-creds
Show configured connections swanctl --list-conns
Show live security associations swanctl --list-sas
Show installed kernel policies swanctl --list-pols
Bring one child up swanctl --initiate --child <name>
Tear one connection down swanctl --terminate --ike <name>
Force a rekey swanctl --rekey --ike <name>
Loaded certificates swanctl --list-certs
Address pool usage swanctl --list-pools
Daemon statistics swanctl --stats
Validate a specific file swanctl --load-conns --file /etc/swanctl/conf.d/x.conf

swanctl can only read files under /etc/swanctl/** — its AppArmor profile forbids anywhere else, and the error it gives ("failed to open config file") does not say so. This is why the module keeps new tunnels inside /etc/swanctl/conf.d/.

Legacy stack — ipsec

Task Command
Status summary ipsec status
Full status ipsec statusall
Reload config (keeps SAs) ipsec reload
Restart (drops all SAs) ipsec restart
Bring a conn up ipsec up <name>
Take a conn down ipsec down <name>
Version ipsec --version

Kernel-level (either stack)

The kernel is the ground truth for whether traffic is actually encrypted:

ip xfrm state          # the ESP/AH security associations, with packet/byte counters
ip xfrm policy         # the traffic-selector policies
ip -s xfrm state       # ...with cumulative counters — if these move, packets went through

A successful ping proves reachability; moving ESP counters prove encryption.

Config file locations

swanctl stroke
Main config /etc/swanctl/swanctl.conf /etc/ipsec.conf
Module writes tunnels to /etc/swanctl/conf.d/remotswan.conf /etc/ipsec.conf
Secrets in the config, secrets { } /etc/ipsec.secrets
Certificates /etc/swanctl/x509/, x509ca/, … /etc/ipsec.d/certs/, cacerts/, …
Private keys /etc/swanctl/private/, rsa/, … /etc/ipsec.d/private/
Daemon tuning /etc/strongswan.conf, /etc/strongswan.d/ same

Access control

Per-user rights are set under Webmin → Webmin Users. Notable defaults:

  • Secrets are denied by default (secrets = 0). Being able to edit tunnels does not grant the right to read every pre-shared key on the box.
  • The raw config editor is off by default (raw = 0).
  • Downloading a certificate never serves private-key material, from any directory, under any filename.

Testing (for developers)

The module ships with a test suite that runs without a live Webmin:

cd /usr/share/webmin/remotswan        # or your checkout
for t in test/*.pl; do perl "$t"; done

629 assertions across 15 suites cover the parsers, both backends' read/write paths, round-trip fidelity, the injection and path-traversal guards, and the secrets/certs security properties. Many are mutation-verified.

An end-to-end tunnel test builds two network namespaces, runs a real charon in each, and pings across so traffic must be encrypted by one and decrypted by the other (needs root):

sudo bash test/netns-tunnel.sh                          # single child
sudo RSWAN_CHILDREN=4 bash test/netns-tunnel.sh         # four traffic-selector pairs

Generating a handoff bundle

For a site-to-site PSK tunnel whose peer is a FortiGate, tools/s2s-handoff.pl turns a VPN request form into a two-sided handoff: the StrongSwan conf.d file (through the module's own writers, so it matches the UI byte-for-byte) plus the mirror-image FortiOS CLI, with a README. Edit the %form block and run it:

cd /usr/share/webmin/remotswan        # or your checkout
perl tools/s2s-handoff.pl /tmp/mytunnel

The defaults use RFC 5737 documentation addresses, so an unmodified run produces a safe, shareable example. The pre-shared key is never written — both sides carry a placeholder, so the key travels separately from the topology. Keep filled-in bundles (real addresses) out of the repo.


Known limitations

  • The editor manages two authentication rounds per side (matching ipsec.conf's leftauth/leftauth2); swanctl allows more, which need the raw editor.
  • AH is not supported. The editor writes esp_proposals only, so a peer that insists on AH rather than ESP needs the raw editor. ESP is what essentially every modern peer uses.
  • Road-warrior EAP-RADIUS setups beyond the basics, and XAuth, are best checked by hand after saving.
  • Converting ipsec.conf → swanctl (Convert to swanctl) is deliberately conservative: it never touches ipsec.conf, reports per tunnel what it cannot carry across, and does not convert secrets. Re-enter those afterwards.

File layout

module.info  config* config.info defaultacl     — Webmin module metadata
remotswan-lib.pl                                  — entry point, backend dispatch, model
service-lib.pl                                    — systemd unit discovery & control
swanctl-parse-lib.pl                              — nested-brace parser/writer
swanctl-backend.pl  stroke-backend.pl             — the two backends
cert-lib.pl                                        — certificate discovery/introspection
*.cgi                                              — the pages
lang/en  help/*.html  images/                      — text, help, icons
test/                                              — the test suite (not shipped)
tools/                                             — developer helpers (not shipped)

About

remotSwan is an open-source Webmin module for StrongSwan. Cost figures are indicative except where marked; verify current vendor and cloud pricing before budgeting.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages