Self-hosted service
bjdgyc/anylink avatar
bjdgyc/anylink

bjdgyc/anylink: an openconnect-protocol SSL VPN server in Go

AnyLink是一个企业级远程办公 ssl vpn 软件,可以支持多人同时在线使用。基于 openconnect 协议开发,并且借鉴了 ocserv 的开发思路,可以完全兼容 AnyConnect 客户端。

2,316 stars518 forksGoAGPL-3.0

At a glance

What is it?
AnyLink is a Go server that speaks the openconnect protocol so Cisco AnyConnect and OpenConnect clients can connect to it, with a web admin panel, LDAP and RADIUS authentication, TOTP and audit logging. The decision that shapes every deployment is link_mode, and the bridge modes that avoid NAT are the ones that will not work in your cloud.
Who is it for?
AnyLink is worth evaluating when the client estate is already Cisco AnyConnect and a licensed appliance is not on the table, because matching that client is the entire reason the server exists, and the protocol reference it implements is an IETF draft rather than a finished standard, which is the first thing to confirm against the clients you must support.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A server built to be spoken to by AnyConnect, on a draft protocol

AnyLink is a Go server for enterprise remote access, and its reason for existing is client compatibility rather than a new protocol. It implements the openconnect protocol as described in an IETF internet draft, the mavrogiannopoulos draft at revision 02, and it borrows its architecture from ocserv. The practical result is that Cisco AnyConnect clients connect to it, and the feature list also claims compatibility with OpenConnect clients. Two things follow from that positioning. The first is that the specification the server is written against is a draft rather than a published standard, so the wire behaviour is anchored to a document with a revision number, and anyone committing to it should read that draft against the specific client build they intend to support. The second is that encryption is TLS and DTLS, meaning the server wants an RSA or an ECC certificate, will accept a private self signed one, and can be given a free certificate from Let's Encrypt or TrustAsia. The feature list is long and specific, and the entries worth noting for a security review are the ones about restraint rather than connectivity: user activity auditing, IP access auditing with support for port ranges, traffic rate limiting, automatic allowlisting of the egress address, an IP ban feature against repeated failed logins, idle timeout disconnection, and an unfinished ipvtap bridge mode that is the single unchecked box on the list.

link_mode is the deployment decision: tun, arp_proxy, macvtap

The README is direct that one of the link_mode values has to be configured, and the guidance on which to pick is about data conversion rather than aesthetics. The clients send IP layer data, so a tun mode needs no conversion at all, and tun is what the README recommends trying first, with macvtap second. The tap approach, which does link layer to IP layer conversion in user space, is where the README says performance drops. That ordering is the whole architecture in one sentence, and it also explains why the bridge modes exist: a tun based NAT deployment gives every client an address behind a masquerade, while a bridge mode puts the client on the same segment as the server so traffic appears to come from a machine that is genuinely there. The bridge modes are where the constraints bite, and they are documented rather than discovered. arp_proxy is described as the higher performing and simpler option, requiring a network that supports ARP and can announce an ordinary intranet address, and it is explicitly listed as unusable in cloud environments, unusable where interface MAC addresses are whitelisted, and unusable on 802.1x authenticated networks. The macvtap route avoids the ARP dependency but needs the master interface placed in promiscuous mode. Since a virtual machine needs its own interface in promiscuous mode for the tap approach, the mode choice ends up being a property of the network fabric, not a preference.

Host preparation: iproute, iptables, root, and a firewalld that is switched off

The server expects to own the packet path, and the setup section says so without decoration. Two packages are the stated dependency, installed with iptables and iproute on CentOS or with iptables and iproute2 on Ubuntu. Beyond that, the process runs as root, the tun documentation walks through enabling IPv4 forwarding and then checking it, and the NAT section tells you to stop and disable the firewalld service outright:

bash
systemctl stop firewalld.service
systemctl disable firewalld.service

That is the single most consequential line in the setup documentation for anyone deploying this on a shared or managed host, because it does not narrow the firewall, it removes it. The reason is visible in the same block: newer versions set the NAT rules automatically, and the manual iptables commands underneath are kept as a fallback, including the masquerade rule on the outgoing interface and a forward accept rule, with instructions to replace eth0 with the host's own intranet interface. The alternative to NAT is a route, and the README documents that as the other half of a choose-one decision. Setting iptables_nat to false and adding a static route for the client network pointing at the AnyLink host moves the traffic out of NAT entirely, with a H3C switch command shown and a pointer to a public cloud documentation page for the equivalent VPC route table entry. That is the cleaner design for a network you also administer, and it is the one that leaves the firewalld question open rather than settled.

First run: the admin panel on 8800, and priming the certificate on 443

Getting to a first connection involves three things the README is unusually frank about. The first is where to get the software. Users without a programming background are pointed at the release page for a packaged tarball, and anyone building from source needs Docker installed up front, because the build scripts use it. The reference toolchain versions are listed in the build section as comments, and they are worth a look: go 1.24 alongside node v16.20.2 and yarn 1.22.19. Building produces a deployment directory that is run directly:

bash
git clone https://github.com/bjdgyc/anylink.git
cd anylink
bash build_web.sh
bash build.sh
cd anylink-deploy
sudo ./anylink

The second is the admin panel, which the README places at port 8800 with a default account and password pair of admin and 123456. Those defaults are printed in three separate places in the documentation, which means they will be known to anyone who has read it, so changing them is not an optional hardening step. The same section explains the mechanism for turning a password into what the server stores. The third is the certificate, and the first use procedure is a workaround rather than a shortcut. You are told to open the server's address on port 443 in a browser, accept whatever warning appears, and only then enter the host and port in the VPN client. The purpose is to make the browser remember the host as visited, and the README states the alternative plainly: for anything in production, get a proper certificate of the same PEM type nginx uses, since the test procedure otherwise depends on telling clients to switch off their protection against untrusted servers.

The tool subcommands and four database engines to choose from

Configuration is handled through a sample file that the README says carries detailed comments, at conf/server-sample.toml, and through a small set of subcommands on the binary itself:

bash
./anylink -h
./anylink tool -p 123456
./anylink tool -s
./anylink tool -d

Reading those in order tells you what the binary does for an operator. The help flag, then a command that takes a password and generates the admin credential from it, which is how the default above gets replaced, then a command that generates a JWT signing key, then a command that dumps every configuration item the binary recognises. That last one is the most useful and the least mentioned: the effective configuration can be printed from the binary rather than inferred from a sample file, which matters when a setting is not behaving and you need to know what the process actually loaded. Persistence runs through one of four engines, listed in a table with a working connection string for each: SQLite as a file under conf, MySQL over TCP with either utf8 or utf8mb4, Postgres with sslmode set to verify full, and SQL Server with a database name and a connection timeout. The schema is created by the server itself, so there is nothing to import, and the price of that convenience is stated as well: the database account needs DDL privileges, permanently, for what is otherwise an application schema. That is a real constraint for a managed database service with a restricted role.

proxy protocol and the trust decisions a VPN front door implies

Three features on the list change the security conversation rather than adding convenience, and each is easy to misread. Proxy protocol version 1 and 2 support means the server can accept the client's real address from a header instead of from the TCP connection, which is what you need behind a load balancer. The consequence is inherent to the mechanism: the header is a claim, so the feature is only safe when the server is genuinely unreachable except through a proxy you control, and there is no note in the README about restricting it to trusted sources. The second is automatic egress IP allowlisting, which turns the address the server itself appears to come from into something it can write into firewall rules, useful until the egress address is shared with something else. The third is client certificate binding, where a certificate is tied to a device so a credential cannot simply be moved to other hardware. Alongside these, the authentication list covers local accounts with TOTP, RADIUS, LDAP and automatic LDAP user sync, plus WeChat QR code login, which is an unusual entry in an enterprise VPN and worth questioning in a deployment outside China. Brute force protection is handled by IP banning, which means a shared egress address can lock out a whole office, and the README does not document a lockout duration. Auditing is thorough on paper, covering both user activity and IP access to ports and port ranges, and that is the feature to lean on when a security review asks what evidence exists after an incident.

AGPL-3.0, a Node 16 frontend build, and an upgrade that swaps a binary

The licence is AGPL-3.0, and for a server that is the version with the most reach, since it is the network copyleft that attaches when a modified version is made available to users interacting with it over a network. In practice the question is whether you intend to modify the server and expose the modified result, and that is a question for your own counsel rather than for this article. The ordinary case of running the released binary unmodified inside a company is a different situation from forking it, and the difference is worth having settled before anyone starts editing Go source. The upgrade path is refreshingly simple and refreshingly manual: back up the conf directory and the database, stop the service, replace the anylink binary with the newer one, and start it again. No migration tool is described, and the schema is generated by the server, so the backup is doing the work that an absent migration step would otherwise do. On cadence, the last push was on 2026-09-24 and the recent releases are v0.14.2 on 2026-03-19, v0.14.3 on 2026-05-19 and v0.14.4 on 2026-08-03, a patch line moving every few months while the version stays below one, which is a reasonable signal for infrastructure software and a reason to test the binary swap in a staging instance before an operator does it on the box that carries the VPN. The build toolchain is the part most likely to age badly: go 1.24 is current, while the node and yarn versions named in the build comments are several years old, and the repository also carries a China specific deployment script alongside the docker assets, hinting at a mirrored distribution path. Both repositories, GitHub and Gitee, are maintained in parallel, which matters for reach rather than for code.

Editorial conclusion

AnyLink is worth evaluating when the client estate is already Cisco AnyConnect and a licensed appliance is not on the table, because matching that client is the entire reason the server exists, and the protocol reference it implements is an IETF draft rather than a finished standard, which is the first thing to confirm against the clients you must support. Its tun mode with automatic NAT is the configuration that survives a cloud host, while the arp_proxy bridge needs a network that can announce an intranet address and is documented as unusable in cloud environments, on 802.1x networks and where interface MAC addresses are whitelisted, so pick the mode from the network you have rather than the performance you want. Two things must be handled before the first real user connects. Change the admin account, since the default panel credentials are published in the README, and replace the self signed certificate with one from a public authority, since the documented test procedure depends on telling clients not to block untrusted servers. Then check the AGPL-3.0 terms against how you intend to run it, and confirm the node and yarn versions in the build script still match what your frontend toolchain provides.

Frequently asked questions

Which VPN clients can connect to bjdgyc/anylink?

The server implements the openconnect protocol as described in an IETF draft at revision 02, and the feature list claims compatibility with both Cisco AnyConnect and OpenConnect clients. Since the reference is a draft rather than a published standard, check the specific client build you intend to use.

What are the default admin credentials and the admin port?

The README places the admin panel at https://host:8800 with the account admin and the password 123456, and also provides a tool subcommand, ./anylink tool -p, that generates the stored credential from a password you supply. Those defaults are printed throughout the documentation, so they should be replaced before the panel is reachable by anyone else.

Can I use the bridge modes on a cloud server?

Not the arp_proxy one. The README lists cloud environments, networks that whitelist interface MAC addresses, and 802.1x authenticated networks as environments where it cannot be used, because it depends on the host being able to announce an ordinary intranet address over ARP. The tun mode with NAT is the configuration that works in a cloud host.

Does the database schema have to be created by hand?

No. The README states the tables are generated automatically and need no manual import, which requires granting the database account DDL privileges. SQLite, MySQL, Postgres and SQL Server are the four documented options, each with an example connection string in the configuration section.

How is an upgrade performed?

Back up the conf directory and the database, stop the service, replace the anylink binary with the newer one and restart. No migration command is described, so the backup is what protects the data, and the recent releases moved from v0.14.2 to v0.14.4 across 2026.

Official sources

  1. bjdgyc/anylink on GitHub
  2. Issues
  3. License: AGPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bjdgyc-anylink.svg)](https://hysenlabs.com/projects/bjdgyc-anylink)