Create Surfshark wireguard connection on OpenWrt easily

This thread is about a shell script developed by @yazdan that is effective, but still a work in process.

Please, show me the script @usernameTaken has provided here.

Please read the entire thread to understand the context of the thread.

Maybe my terminology was a bit loose but you know what I meant - if you read my full message. I have read this thread repeatedly - obviously I am trying to register my public key - as that has been the main sticking point for many here - not just me - if you have read the entire thread. So, if you have nothing constructive to reply with - just please go silent. I have determined ( I hope that ) Cookie & Bearer Token from the app is related to JWT - or as best I can determine on my thus far. Thanks for your assistance as I had so politely asked for.

Hey guys! In order to extract the cookie and bearer token from the app, I installed Charles Proxy on my Mac and configured SSL proxying. I had to install and trust the self-signed certificate from the Charles software to be able to view contents of the SSL traffic. Here are the instructions.

You can also setup SSL proxying using Telerik Fiddler or Wireshark.

Once you have Charles (or your software of choice) setup to view SSL traffic, open the Surfshark app and login with your credentials.

During the login sequence, look for a POST method with the URL of

If you are using Charles, right click on this URL and "Copy cURL request". Paste it into a text editor. Replace the value for pubKey with the Public Key in wg.json. Then copy the command into a terminal and run it to register the public key with the Surfshark API. Then, run with wg_reg_pubkey and wg_check_pubkey commented out


Thank you so very much for your well detailed, fully understandable and most kind explanation as to how to go about arriving at ultimate success of registering one's public key. I just wish that everyone here in this Forum Thread would mirror your most admirable traits and conduct. I am a retired English Teacher and not all are adept at conceptualizing and conveying complex instructions clearly and succinctly- you have achieved both here, For that, let me be among the first to say " I am most grateful ". I had asked for a way to implement something like this on Linux ; however, I now realize that there is no Linux App for SurfShark WireGuard. Anyway, thank you once again and enjoy the Holiday Season Peace

Thank you for those kind words @directnupe I always try to remember that forums like these are intended to be communities where people can help each other. There’s no place for rudenesses or criticizing someone that doesn’t understand something. I hope that everyone was able to generate working Wireguard configs. I’m happy to help Yazden with the script when he has some free time. Have a great holiday season and stay safe and healthy!

1 Like

Dear usernameTaken,
Well actually you have made my day - thank you for your graciousness and generosity of spirit as embodied in your kind message and sentiments - and most importantly as reflected in your thoughts below :

I always try to remember that forums like these are intended to 
be communities where people can help each other.

Exactly and Amen - if you are interested in DOQ or DNS OVER QUIC ( the latest and greatest ) - check out my tutorial on AGUARDHOME here : OpenWrt AdGuard Home 101 ( DNSMASQ ) and for any others here who are intrigued by the topic

Peace and Have a Safe Holidays

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.