Create Surfshark wireguard connection on OpenWrt easily

after some debugging @yazdan script working fine for me,
the issue is when validating the public key to the api, the response is empty or bad request (only on the first time the script run) like @usernameTaken said, so the wg_reg_pubkey function not called.

you can force the register function by adding register=1 after the config_file variable at the top of file, and commenting the check public key at this line using # at the start line.

and thank you so much for your script @yazdan

1 Like

Much thanks to @usernameTaken and all the others who have helped with
me being able to get SurfShark WireGuard Connection on OpenWRT using @yazdan script.

@oanqa has helped everyone with his straightforward approach to registering the public key. I have not tried it but it certainly looks promising.

I followed @usernameTaken instructions. Just a few points if you decide to go that route.

1 - For " Charles Proxy Bearer Token Authentication " see here Charles Proxy App Auth Token

Bearer Token Authentication value is found under user entry when you go through - there will be " me " there - and If you are using Charles Proxy, then right click on this URL and "Copy cURL request".
Paste it into a text editor. as @usernameTaken instructed.

Lastly, when @usernameTaken says comment out these two entries below :

#wg_reg_pubkey and #wg_check_pubkey

they are found at the end of the script as @oanqa points out
in his post. at these two lines as depicted below :


Just tried to tidy up a bit of unfinished business. I am happy to have script - thanks to all
and Happy Holidays - Stay Safe

1 Like

I've been trying to get this to work for days now, i can't figure it out :sob:

I've done as suggested by @usernameTaken @oanqa and @directnupe
I've added register=1 and commented out #wg_reg_pubkey and #wg_check_pubkey

I'm getting public and private keys generated, both in my wg.json and in all my conf files.
I'm setting the interface up in Luci, following these pictures as yazdan linked earlier:

If i check my wireguard status in Luci, my first peer is connected and there was a handshake, but the second peer never connects.

And it looks like there is some traffic on the wg0 interface, although not much :stuck_out_tongue:

What am i doing wrong?

1 Like

The token.json you get from the SS server contains an expiry date that seems to point to failure.

Add the verbose -v switch to the curl statement in the wg_check_pubkey() function.

Add echo ""${now}" '<' "${expire_date}"" after the line:
if [ "${now}" '<' "${expire_date}" ] in the same function.

Don't remove any generated files, leave them alone - config.json, wg.json, surf_servers.json, selected_servers.json, token.json

Run the script. You will get a response from the curl POST (what got sent and the response status). The echo statement will return the ISO-8601 dateSPECseconds < and the expiry date from the token.json file. Let me know if the curl output indicates any failure, and the expiry date ouput you see.

Thanks for the response, could you post exactly how you mean this should look? i'm not quite getting your explanation.

wg_check_pubkey() {
    data="{\"pubKey\": $wg_pub}"
    token=$(eval echo $token)
    curl_res=$(eval curl -H \"Authorization: Bearer $token\" -H \"Content-Type:>
    expire_date=$(echo $curl_res | jq '.expiresAt')
    expire_date=$(eval echo $expire_date)
    now=$(date -Iseconds --utc)
    if [ "${now}" '<' "${expire_date}" ]

Here you go.

wg_check_pubkey() {
    data="{\"pubKey\": $wg_pub}"
    token=$(eval echo $token)
    curl_res=$(eval curl -H \"Authorization: Bearer $token\" -H \"Content-Type: application/json\"  -d \'$data\' -X POST $url -v)    <--- Add -v switch here.
    expire_date=$(echo $curl_res | jq '.expiresAt')
    expire_date=$(eval echo $expire_date)
    now=$(date -Iseconds --utc)
    if [ "${now}" '<' "${expire_date}" ]
    echo ""${now}" '<' "${expire_date}""    <--- Add this line here

1 Like

If you face the validate 400 bad request problem (No need to register pubkey appears in output console) you don't have to do any sniffing and no cookie is needed, just run the registration command to send pubKey as follows (and no validation command needed)

curl -H "Authorization: Bearer XXXXXX" --data-binary "{\"pubKey\":\"YYYYYY\"}" --compressed "" -v


XXXXXX = The token tag value in token.json generated (pay attention to do not copy the renewToken tag as well by copy-paste)
YYYYYY = The pub tag value in wg.json generated

Do not execute this command from the router but directly from pc shell (linux or Mac). if the output looks like below the previously created configuration files will works (note that the expiration date is one year)


Be careful:

  • created configuration files appears with key=value format. In my case (GL.iNet router) I had to add spaces between = to make it work
  • for the password some special characters create problems (es: $). I had to force it with backslash ahead directly into the code, in the config it didn't work

Curious, I’ve been running this via SSH from a usb drive partition on the router without issue successfully using somewhat similar logic

Umm, It looks more like the same range I get 7 days to expiration

What is interesting to me is that the API has a mechanism that Updates the expiry date that would appear to be based on the current Pubkey still being active beyond that expiry date.

1 Like

when the expiration date is exceeded, the connection is still active, however, if the router is restarted, the interface will go offline and all the steps must be repeated from the beginning.

Any solution?

Happy new year!!!

Guys I don't know about you, wireguard has stopped working for me and there is no way to reconnect. The key expires in seconds.

What are you getting from the curl output? 409 Conflict ?


i have this error when execute SH

jq: error (at :1): Cannot index array with string "expiresAt"

not is possible key register.

In the script file i removed then line number 6 "set -e", after running the script many times it managed to connect wireguard.

That comes from the WG CHECK PUBKEY function. It usually comes from a malformed username/password it doesn't like. Just ran OK here. I comment out the


functions at the bottom of the script, then rm token.json, run the script, and then apply the workaround provided by @mazzhg.


set -e is intended to halt the script if there is a Login error.

true one year concession.

  • start date: Jul 14 00:00:00 2021 GMT
  • expire date: Jul 13 23:59:59 2022 GMT

I guess the next time my Wireguard connection is cut, it will only be necessary to rerun (curl -H "Authorization: Bearer eyJ0eX ......)

is that so?


Hi every one!

Sorry for answering late, it was and still is a busy period in my career, and I didn't have enough time and energy to debug it.

So I found the time and energy, and have done some improvement and made the script a little more user friendly

PS: I really appreciate that you show this much interest in this, and I especially thank those who have helped other when I was gone


Nice to see you back!

Thanks for the update - I'll run it through in the morrow.

1 Like

Thank you @yazdan !

I'm using PFSense. Handshaking was only working with the "" peer at first. After running the script many-many times with -f & -g then the second peer "" was fine. Not sure what the combination was, but after a few days of fiddling for a couple of hours a day it magically started to handshake. Now i'm running a cron job on a linux box nightly with -g to keep the cert registered.

I just have a weird issue with stability / connectivity. I route specific lan IPs to the wireguard interface. On a debian box when i try to run "apt-get update", it fails to connect to some locations. So, for now i switched it back to openvpn. Any idea what this could be?

Here is an example of the "apt-get update" connection failure that only occurs with wireguard; fine with openvpn.

Get:5 file:/var/cache/openmediavault/archives  Translation-en_GB
Ign:5 file:/var/cache/openmediavault/archives  Translation-en_GB
Hit:6 buster-backports InRelease
Hit:7 bullseye InRelease
Hit:9 shaitan InRelease
Hit:10 bullseye InRelease
Hit:11 bullseye-updates InRelease
Hit:12 buster-backports InRelease
Hit:13 bullseye-backports InRelease
Hit:14 stable InRelease
Err:15 shaitan InRelease
  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
Err:8 shaitan InRelease
  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
Err:16 bullseye InRelease
  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
Err:17 shaitan-testing InRelease
  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
Reading package lists... Done
W: Failed to fetch  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
W: Failed to fetch  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
W: Failed to fetch  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
W: Failed to fetch  Could not wait for server fd - select (11: Resource temporarily unavailable) [IP: 443]
W: Some index files failed to download. They have been ignored, or old ones used instead.

Btw, I noticed that in earlier comments it was stated that this config would not work with the Wireguard Client (, but i tested and it works for me.


1 Like

Any advance? I have the same problem... One of interfaces without received packages... Thanks for your work!

Hi @javirnof.

No improvement. I was really hoping that my weird wireguard issue with stability / connectivity with PFSense was related to the RC version. Even after upgrading to the official PFSense 2.6, which came with a minor update for the wireguard package (0.1.6_1), I'm having the same problem.

The only theory I have is that it has something to do with IPV6 being forced even though my ISP (TalkTalk) only issues me an IPV4 address and I have IPV6 disabled in PFSense. The errors when running an apt-get update show IPV4 addresses being resolved, so not sure; btw .. i'm using the Surfshark DNS's for the wireguard interface - and The other thought is that there is possibly an aggressive IDS rule on that does not like wireguard, but if this was the case then many people would be complaining.

Hoping someone can point me in the right direction.