Open CzBiX opened 7 years ago
Where's authentication? This proposal will get GFW-ed within days. You just don't publish your server on the Internet.
只要使用了 HTTPS 并且保证 URL 的安全,GFW 就无从得知包括 URL 在内的信息。 这就拥有了一套实现简单的防窃听及认证机制。
我提出这个提案的目的主要是为了解决朋友之间共享服务器时能便捷的更新代理信息。 就算是中心化的代理站,我也想不到这个会带来什么新的问题。
朋友之间没有必要把 URL 公布到网上,代理站需要 URL 中的信息标识用户,更加不会公布 URL。
So you're using a secret in URL to reveal the secret key. The second secret is authentication. And also by doing this you have effectively introduced a handshake for the protocol.
One could work around the handshake and design a totally profitable protocol out of this:
I suggest to host a QR code on a secret HTTPS website. It should do the same trick.
@Mygod I still didn't get the point about add authentication over HTTPS, and a complicated way at that.
Keep URL secret should be enough. If it really needs, HTTP auth also works well. (URL like https://user:pwd@example.com
)
Your URL can't be a well-known fixed string. And therefore there will be a redundant second secret.
The point is that one can design a better protocol with a secret URL as a single entry point than forcing it into the current framework (i.e. as an SIP), not that per-user authentication is necessary.
@madeye So, I have to notify everyone about config changed. Users have to scan QR code many times to add servers, find out which one is the new server, which one should be removed.
Yeah, We need more manual action to live in the modern digital world.
I think my servers is not good and stable as you have. And some of my friends don't meet the tech requirements to across the wall.
Deeply hope similar feature implemented in the future.
@CzBiX I agree that this proposal would be useful in many scenarios. In fact, it's also easy to implement. I believe @Mygod can implement it in one line with Scala. 😄
However, to become a new feature, we need to consider more, for example, the authentication requirement here.
I think you can keep thinking about this proposal, polish your design and collect more feedback from the community. If so, I guess we would finally have a new SIP.
Reopen the issue to allow @CzBiX refine his proposal further.
@madeye Thanks.
Sorry, maybe I don't know much about Internet security, I still can't understand why we need more authentications. If I choose to share config via SS URI or QR code as before, it also is well-known info. What's the difference? Should we move password outside SS URI to force user input it?
In my mind, the client can use HTTPS cert to verify server's identity, and server identifies client use auth info exists in URL, the provider can choose which security level and the method they want:
/ss_config.json
is equivalent to a simple password, and service provider should avoid it.https://user:pwd@example.com/config
, or use token like https://example.com/config?token=xxxx
.Both of them easy to implement on server side, and easy to use because of just single URL on client side. Yes, I want to keep SS be easy to configure and use as it always does.
Here's my proposed change if you really want this kind of thing.
Current shadowsocks configuration involves many parameters. To add insult to injury, the introduction of plugins (#28) introduces even more configurations. As a result, new ss URLs are usually very bloated (#27).
All of this would be fine if the server never changes its configuration. However this is not likely to happen due to following reasons:
Whenever the server updates its configuration, due to the handshake-less nature of the protocol, every client has to update their configuration accordingly. This can be a tedious process if the number of clients is large. Ideally we want to use only one single pre-shared key to authenticate valid users.
First, the config file is published on the Internet following one of these two URL schemes with an optional front domain parameter as the fragment: (TODO: which one?)
a. https://<domain>/<hard-to-predict-path>.ssconf[#[front-domain.example][;remark]]
b. ssconf://<domain>/<hard-to-predict-path>[#[front-domain.example][;remark]]
The web server hosting this ssconf page could be:
It's recommended to put the web server behind a trusted CDN, preferably the ones supporting domain fronting.
Upon GET request, the web server validates whether the request is performed using HTTPS and returns an ordinary shadowsocks json configuration in that case. Example:
{
"server": "example.com",
"server_port": 1234,
"password": "example",
"method": "chacha20-ietf-poly1305",
"plugin": "xxx",
"plugin_opts": "xxxxx"
}
The remark
field returned from server will be ignored and the remark in URL fragment will be used instead. Multiple configurations should be put at different URLs.
Note that in order to guard against MitM attacks, the client now should be stateful to prevent country-scale certificate forgeries. Here's what a console ss-local
would look like:
$ ss-local [--storage /path/to/protected/storage] -l 1080 --fast-open [--other-client-params] <URL>
Certificate Information for <real-domain>
Issued to: <real-domain>
Issued by: DigiCert SHA2 Extended Validation Server CA
Other information...
This certificate looks good. Proceed to connect? [y]/n
INFO: using tcp fast open
INFO: initializing ciphers... chacha20-ietf-poly1305
INFO: tcp port reuse enabled
INFO: udprelay enabled
INFO: udp port reuse enabled
INFO: listening at 127.0.0.1:1080
/that-outcrop-infuriates-my-bud-my-shadowsocks-config-with-kcptun-as-plugin-japan-123.4.5.67-password-is-hello-world
or /A9FEF2E0C966D101B1C5E0CCB49DCC8BE251E1E6DB53F04E11EE54C2593E6BAD36184A8684B9CE1639D7BF09FEA50CB7047536C7B252FFDA60FEC61ACE6BF57D
./config/<secret-tag>
. No matter which one you choose, the server MUST return HTTP 404 if authentication fails instead of the traditional 401 or 403.I think it's better to use a separated program / script to handle this logic (like ss-manager, but for client side), and keep ss-local simple.
It's just a demo of usage, and also how complicated it is to implement securely. (it's nothing you can do with one line of scala)
Deleted
I like the idea of an online config. It can be blocked the same way the Shadowsocks server is. However, it's something you access rarely, so it will draw a lot less attention and it will be a lot harder to find and block.
I have some thoughts about practicality of the idea.
Quite often the most convenient is to have the config live where the server is, but that means the config will be blocked if the server is. But there are solutions:
One issue we ran into in Outline is that users often don't have a domain name. Which means they can't get a TLS certificate from Let's Encrypt. Because the certificates are self-signed, we need a certificate fingerprint to validate them. The configuration URL should provide a way to specify the TLS certificate fingerprint, so clients can validate certificates when content is served from an IP address.
Alternatively, you may publish the config somewhere else. You'll want to automate that, which means generating some API key and wiring your code properly, which is often not quite straightforward if you've never done it before.
Notice that you don't really need the fetch URL to be private. The config page can be encrypted and published publicly anywhere. It may even use HTTP, but you will want an AEAD cipher to validate authenticity. The config URL can have the secret key used to decrypt the content by the client. Ideally in the fragment, to make sure it's never sent to the host server.
Publishing requires you to trust the host, which is not true for many people, unless you use the encrypted approach above.
Some options for publishing:
An alternative to publishing a config is to use an editable URL Shortener. In that case it could redirect directly to the ss:// link.
As in the case of publishing, you need to trust the URL Shortener.
When you update the config, make sure the old configuration keeps running until all your clients have migrated to the new config. Otherwise you will break them.
A lots of people open issues for implement of this feature in Shadowsocks-NG
. Recently I consider it again.
host:port
pair. hard-to-predict-path
as @Mygod suggested.To be simple to edit, I show the example in yaml format:
# Identify the group of servers.
# Can be used by client to track the servers from same source.
# When update, the client can use the id to deleted the old servers
# which has been removed from the server list.
id: com.xxx.foo/bar
# Use the SIP002 format URL instead of a new spec. Keep it simple.
servers:
- ss://YWVzLTEyOC1nY206dGVzdA==@192.168.100.1:8888#Example1
- ss://cmM0LW1kNTpwYXNzd2Q=@192.168.100.1:8888/?plugin=obfs-local%3Bobfs%3Dhttp#Example2
- @fortuna has mention an AEAD cipher approach to serve the config file by http. But it's complex to implement it. Should be easy to implement it by all client and server.
The client could leverage the same crypto that the Shadowsocks protocol uses, so it's just a matter of reusing libraries. In Outline, we could use our ShadowsocksReader.
For the config URL, we should consider not only a front domain, but also a domain to resolve and get the IP address from. For example, we can resolve cloudflare.com
to get IPs for any cloudflare website.
These are the things we may need in what we give the client:
If we want to support self-signed certificates for users without a domain name:
I'd like see it become standard.
Shadowsocks-windows has another experiment few years ago: https://github.com/shadowsocks/shadowsocks-windows/tree/with_online_config
My concern:
ssconf://
, we can register protocol handler for it. (But we haven't do it for ss://
yet)We're finalizing the online config support with an extendable JSON format. Here's an example: https://gist.github.com/madeye/8451355b1baba312dae78a6c8ca4d1a1
Generally, we don't limit any custom fields in the JSON config file. The only required fields are:
{
"server": "198.199.101.152",
"server_port": 8388,
"password": "u1rRWTssNv0p",
"method": "aes-256-cfb",
"remarks": "Example 1"
}
More about authentication
X-Token
) is a popular option.Option 1 is easy, but require POST Option 2 only need GET and also easy to implement Option 3 is hard to implement but easy to manage
GET is good enough, as we're forcing HTTPS URL for any online config.
You can put any restful token in your URL for authentication.
The latest shadowsocks-android has already supported this SIP: https://github.com/shadowsocks/shadowsocks-android/releases/tag/v5.0.3
写了一个转换 ss:// 地址为可用于订阅的 json 库,可以参考: https://github.com/quekangkang/ss2json
@madeye what's the URL scheme you are using in ss-android?
@studentmain did you release this for ss-windows?
@madeye what's the URL scheme you are using in ss-android?
@studentmain did you release this for ss-windows?
We have plan to do that, but heavy refactor is expected...
@studentmain did you release this for ss-windows?
Now I'm preparing for it. Maybe few versions later. We will use HTTP scheme (http://
and https://
) for online config. BTW, ss-windows now registered ss://
URL handler on Windows system when user requested.
We have released shadowsocks-windows
v4.2.1.0, which contains support for SIP008 online configuration.
The original JSON format is extensible, flexible, and can be easily supported by clients. The basic file format added by @madeye, however, seems to exclusively favor shadowsocks-android over the neutral stance a standard should take. It also significantly and unnecessarily complicates the parsing process, which is already causing compatibility issues. Top-level JSON arrays are also known to have potential security vulnerabilities.
I propose we revert to the version of format drafted by the original author, currently listed as the extended file format as edited by @madeye, to become a simple yet highly extensible protocol standard, to minimize compatibility risks, to make it easier for servers and clients to implement.
Some typos in the original version are fixed in the following version:
{
"version": 1,
"servers": [
{
// Server UUID to distinguish between servers when updating.
"id": "27b8a625-4f4b-4428-9f0f-8a2317db7c79",
"remarks": "Name of the server",
"server": "example.com",
"server_port": 8388,
"password": "example",
"method": "chacha20-ietf-poly1305",
"plugin": "xxx",
"plugin_opts": "xxxxx"
},
// Another server
{
"id": "7842c068-c667-41f2-8f7d-04feece3cb67",
"remarks": "Name of the server",
"server": "example.com",
"server_port": 8388,
"password": "example",
"method": "chacha20-ietf-poly1305",
"plugin": "xxx",
"plugin_opts": "xxxxx"
}
]
// The above fields are mandatory.
// You may add custom fields.
}
ssconf://
protocol.I have mixed feelings about the proposed ssconf://
protocol. I understand that some client implementations would like to register a protocol handler for it. But personally I prefer it if we stick with the underlying protocol. It is no different than an ordinary http(s)://
link, after all.
@fortuna @alalamav I noticed Jigsaw-Code/outline-shadowsocksconfig added support for ssconf://
. We'd like to hear your opinions on it.
Currently shadowsocks-windows has support for both JSON formats. Qv2ray only supports the extended file format. Both clients have no support for ssconf://
.
As we work on shadowsocks-windows v5, the SIP008 support is likely to be reimplemented. And I'm seriously considering dropping support for the basic file format. I have discussed it with Qv2ray developers and some V2Fly members. They also believe the extended file format is the way to go.
There's no limit to add version
or servers
to your custom JSON config files.
The only requirement from this SIP is that each server config at least has the following fields in its JSON object:
{
"server": "198.199.101.152",
"server_port": 8388,
"password": "u1rRWTssNv0p",
"method": "aes-256-cfb",
"remarks": "Example 1"
}
Basically, you can add anything you like to your custom JSON config files.
Users will be benefited from having a standard extened file format.
Currently different implementations that support multi-servers have their extended file format, such as shadowsocks-rust, shadowsocks-libev, shadowsocks-android, shadowsocks-windows, ... Configuration files couldn't be shared between these implementations.
I personally agree what @database64128 and @CzBiX propose, server list is specified by a servers
key. Benefits are:
Fully compatible with basic file format. It would be a lot easier for both users and implementators to edit and parse the new extended file format.
Friendly for future iterations. In the future, it could be a lot easier to add more standard fields into this file format.
Keep basic file format simple. The basic file format contains mandatory fields for specifying a shadowsocks server, it is not wise to put too many unrelated, implementation defined, optional fields into that format. The servers
key in the proposed extended file format is basically the same as the basic file format.
Do we really need a basic file format? The proposed JSON document format (a.k.a extended file format) already covers the use case. We don't need a second format to cover cases where you only have a single server.
To have a better file format, we can follow the proposal originally from @CzBiX. I am not against that.
Just make sure each JSON object of a server config is following the requirement.
BTW, shadowsocks-android didn't assume any JSON config format now, it just iterates all the JSON object to retrieve server configs.
@madeye maybe it’s time to formalize the format by putting it on our website.
@database64128 PR is welcome. 😄
@madeye I wish I could, but the spec
folder points to the wiki repository, which I can't contribute to without push permissions. 😅
Okay, I forgot you need to add pages here: https://github.com/shadowsocks/shadowsocks-org/wiki
Thanks y'all for all the brilliant ideas! I just published the draft document of SIP008 Online Configuration Delivery to the wiki. Please review and share your thoughts. Once everything is cleared up, I'll publish the standard document to our website. 😄
https://github.com/shadowsocks/shadowsocks-org/wiki/SIP008-Online-Configuration-Delivery
// Optional fields for data usage:
"bytesUsed": 274877906944,
"bytesRemaining": 824633720832
Why do these two fields have different naming style?
@zonyitoo You got me! 😅 Changed to follow the naming convention of snake_case
.
We have started prototyping support for online config in the Outline clients. I agree with @database64128 in that standarizing this proposal will strengthen the Shadowsocks ecosytem. We would like for Outline clients to remain compatible with standard Shadowsocks servers. Here are some thoughts and needs that we have found so far:
I believe that registering a custom protocol provides a better user experience than manually adding the URL to the app. Additionally, Shadowsocks users are already familiar with the ss
protocol - ssconf
would use this fact as a building block.
I realize that URL interception is not possible in every platform, so clients should support manual import. The https
protocol cannot be registered since we don't know the hostname in advance.
We are exploring how to colocate an access service (online config server) to Outline servers. The current URL format works well for servers that have domain names and the corresponding (CA signed) certificates. Most Outline servers don't have domain names and rely on self-signed certificates for accessing the management API. In order to prevent MITM attacks, we communicate the certificate fingerprint out-of-band to the client, pin it, and validate upong connecting. This solution requires access to native networking APIs, since browsers (webviews) cannot easily pin custom certificates, but it has worked well for Outline servers, so we would like to extend it to online config.
In order communicate the certificate fingerprint to the client, we may want to define a more flexible URL format. We propose to encode arbitrary options in the URL tag, much like SIP002 plugin options:
ssconf://<domain>[:port]/<hard-to-predict-path>[#param0=val0;param1=val1...;]
The domain front can be encoded in the tag, along with the remark: #domain_front=example.com;remark=ss%20server;
With the new URL format we may avoid displaying a user prompt to confirm untrusted cetificates, as long as the certificate fingerprint is present. The client should terminate untrusted connections that don't provide a valid certificate fingerprint.
The proposal states that, "Clients should treat redirects (HTTP 3xx) as errors due to possible certificate issues. Web servers should handle URL changes internally."
HTTP redirects can be a way to migrate online configs. The server may redirect clients to an ssconf
URL in order to mitigate potential certificate issues. The client should persist the online config URL for permanent redirects.
I don't feel strongly about the JSON file formats proposed on this thread. As long as we are able to support mulitple servers, are backwards compatible with Shadowsocks naming conventions, and can arbitarily extend individual configurations. The more important aspect is that the format is standardized.
I'm interested in hearing your thoughts and in finding common ground to move this proposal forward. Thanks!
I would like to emphasize @alalamav 's case to support HTTP redirects. We ought to support that because it's easy and brings multiple benefits:
This is how it can work:
If the first redirect from the saved URL is a permanent redirect, we should replace the saved URL with the new one, to allow for migrations. We should only do this once we confirm the new config works.
A HTTPS url should be able to redirect to a ssconf:// so we can redirect to a location with a self-signed certificate. A HTTPS url should also be able to redirect to a ss:// location. In which case you can stop and use that config (same as if you entered the ss:// link in the first place).
We should specify when the client should fetch the config, so the server can migrate a user (for example, to rotate IPs) to a new config seamlessly, without interrupting the existing connections. Note that seamless transitions require supporting an old config until a client migrates to a new config.
A client app should fetch the config every time a client hits "Connect", and then again before the config expires. That means we need a way to communicate a config TTL or expiration time to the client somehow. For example, if you have a TTL of 2 hours, that means the server manager can stop supporting an old config 2 hours after a new config is made available. I recommend a TTL instead of an expiration time, because you can keep using the same value without updating.
A TTL can be used to stop serving idle configs and save resources.
A TTL can be used to force the user to refresh their config in a short period and the server can generate a new one whenever the TTL expires. This provides forward secrecy with Shadowsocks!
This TTL can be in the config somewhere or in a HTTP header. I'm still unsure where.
The proposed file format does not differentiate fields that are required from fields that are optional. This will prevent future extensibility. I need to know how to handle field I don't know of. Should I ignore, or should I abort? ID and remarks are optional. method and password are not. Plugin is also required. Outline doesn't support plugins, so we need to know if there's a plugin field we should reject the config. But Outline needs to know that the plugin field exists, and is required.
Perhaps we have a structure like:
{
"version": 1,
"servers": [
{
"id": "27b8a625-4f4b-4428-9f0f-8a2317db7c79",
"remarks": "Name of the server",
"proxy_config": {
"server": "example.com",
"server_port": 8388,
"password": "example",
"method": "chacha20-ietf-poly1305",
"plugin": "xxx",
"plugin_opts": "xxxxx"
}
},
{
"remarks": "Name of the server",
"proxy_config": {
"server": "example.com",
"server_port": 8388,
"password": "example",
"method": "chacha20-ietf-poly1305",
"plugin": "xxx",
"plugin_opts": "xxxxx"
}
}
]
}
And we define that every field under proxy_config
must be processed. If there's an unknown field, the server should be discarded.
ss://
association registered by shadowsocks-windows. Note that for shadowsocks-windows, ss://
association is opt-in.@database64128 We'll probably support the standard specified here, but we'll go further and extend it to support the advanced cases we need (self-signed certs, redirects, config rotation). If we can demonstrate it works well, then we can present back to the community for standardization.
On this proposed standard, It would be great to follow the caching directives whenever possible. In particular, The Cache-Control
header with max-age
is quite useful. There's a nice tutorial on HTTP caching at https://www.mnot.net/cache_docs/
@studentmain @database64128 @madeye What's the retrieval logic for shadowsocks-android and shadowsocks-windows?
In the upcoming shadowsocks-windows v5, users will be able to specify an interval for automatic retrieval. Redirects are followed.
If a server does not use a plugin, the plugin and plugin_opts should be empty or excluded from the server object.
"plugin"
and "plugin_opts"
shouldn't be mandatory, which will results in configuration files to have useless informations (tons of keys with empty values).
"plugin"
and"plugin_opts"
shouldn't be mandatory, which will results in configuration files to have useless informations (tons of keys with empty values).
The plugin
and plugin_opts
fields are not mandatory. But since empty strings are a clear representation of not using plugins, the standard document is being explicit and permissive about allowing empty strings to maximize compatibility.
How to convert an URL link to a shadowsocks config.json file?
ssr://MTg4LjExOS42NC4xNDk6ODgyNjpvcmlnaW46cmM0OnBsYWluOmJHNWpiaTV2Y21jZ05XaHkvP29iZnNwYXJhbT0mcHJvdG9wYXJhbT0mcmVtYXJrcz01TC1FNTcyWDVwYXY1cGF3NktXXzVMeXY1WWlwNUxxYVFVUk5RVTdudlpIbnU1d3hORGsmZ3JvdXA9WjJsMGFIVmlMbU52YlM5bWNtVmxabkU
Motivation
When the server changes SS config(such as address, port, method, password), the client can auto-update config without user interaction.
Overview
Basic file format
Basically, we follows the original JSON config format of shadowsocks. The required fields are:
To support multiple servers, put those server objects into one JSON array.
Extended file format
The JSON config can be extended with any client specific field. Any unsupported filed can be just ignored.
Remark
id
,server
,server_port
,password
,method
inservers
.TODO