stanfordsnr / guardian-agent Goto Github PK
View Code? Open in Web Editor NEW[beta] Guardian Agent: secure ssh-agent forwarding for Mosh and SSH
License: BSD 3-Clause "New" or "Revised" License
[beta] Guardian Agent: secure ssh-agent forwarding for Mosh and SSH
License: BSD 3-Clause "New" or "Revised" License
sga-ssh's command line argument processing isn't quite compatible with ssh's, and this breaks rsync.
Take, for example:
export RSYNC_RSH=sga-ssh
rsync /file.ext some-server:/file.ext
This will result in an invocation of sga-ssh similar to the following:
sga-ssh some-server rsync --server -e.MisqZ . /file.ext
sga-ssh attempts to interpret the --server and -e arguments as arguments to itself, rather than part of the command to be executed on the target.
There's a pretty easy test to confirm this:
user@host ~ % sga-ssh some-server echo --version
v0.7.2-beta
user@host ~ % ssh some-server echo --version
--version
I'm trying to understand whether guardian-agent supports the following setup:
Local, trusted machine
-> Partially trusted jumphost
-> Admin server
-> End-machine
Not sure if I can 'chain' sga-guard or if there's some way.
I'm trying to run ansible from the 'admin server' against end machines.
ansible supports the variable:
ssh_executable="/usr/local/bin/sga-ssh"
But it appears:
I tried to run sga-guard
to forward to a remove server, which uses fish as default shell. This is the response I got:
Connecting to [email protected] to set up forwarding...
Failed to run remote stub: fish: Variables may not be used as commands. In fish, please define a function or use 'eval $SHELL'.
$SHELL -l -c "exec sga-stub"
^
Make sure that guardian agent is properly installed on the remote host
If I switch the default shell of the remote user ([email protected]) to /bin/bash
, the forwarding would be successful. That means the agent depends on the behaviour of specific shell environment, which is not ideal.
Please fix this if possible. Thanks.
Hi,
I'm connecting to a remote machine with some sort of connectivity issue and with regular OpenSSH I can prevent my connection from constantly dying by using -oServerAliveInterval=60. Unfortunately I notice that sga-ssh has no such option. Can you please add some connection-anti-idle functionality?
Thank you :)
Frank
If I sga-ssh
to a host and the resize my local terimal, the LINES/COLUMS variable doesn't update, nor does the values reported by stty size
etc.
Can sga-agent
be run as a daemon? Automatically started on startup? Perhaps something managed with systemd?
On a vanilla install (Mac OS Client, Debian oldoldstable server), setting up failed:
sga-guard --debug debian.host
Connecting to debian.host to set up forwarding...
2019/11/15 12:47:43 sshfwd.go:90: Listening on: /tmp/516057857/.guard.32719
Failed to run SSH forwarding: exit status 255
Control socket connect(/tmp/217233322/1298498081): No such file or directory
Mac was installed with brew, linux with apt as directed in the README.
How can this be fixed?
I'm trying to use sga-ssh to connect to a git repository on a custom port.
With the environment variables from sga-env.sh, this fails with:
fatal: ssh variant 'simple' does not support setting port
Adding export GIT_SSH_VARIANT=ssh
fixes this issue on the git side, but now sga-ssh just hangs.
This is the debug output:
% sga-ssh --debug [email protected] -p7999 git-upload-pack
2018/09/24 07:01:09 handshake.go:355: kex loop
2018/09/24 07:01:09 handshake.go:360: kex inner loop
2018/09/24 07:01:09 handshake.go:370: select exit: <-requestKex
2018/09/24 07:01:09 handshake.go:393: !sent: sending kexInit
2018/09/24 07:01:09 handshake.go:360: kex inner loop
2018/09/24 07:01:09 handshake.go:368: select exit: <-t.startKex
2018/09/24 07:01:09 handshake.go:416: entering keyexchange
2018/09/24 07:01:09 common.go:139: host key algorithms: server: [none], client: [none]
2018/09/24 07:01:09 delegated_client.go:582: KexCallback called
2018/09/24 07:01:09 handshake.go:355: kex loop
2018/09/24 07:01:09 handshake.go:360: kex inner loop
2018/09/24 07:01:12 delegated_client.go:634: [email protected] request denied, continuing
2018/09/24 07:01:12 delegated_client.go:641: Initiating Handoff Key Exchange
2018/09/24 07:01:12 handshake.go:326: requestKeyExchange, t.deferHostKeyVerification: %!b(bool=false)
2018/09/24 07:01:12 handshake.go:370: select exit: <-requestKex
2018/09/24 07:01:12 handshake.go:393: !sent: sending kexInit
2018/09/24 07:01:12 handshake.go:360: kex inner loop
2018/09/24 07:01:12 delegated_client.go:539: Finished copying transport data to agent
2018/09/24 07:01:12 handshake.go:368: select exit: <-t.startKex
2018/09/24 07:01:12 handshake.go:416: entering keyexchange
2018/09/24 07:01:12 common.go:139: host key algorithms: server: [ssh-rsa], client: [[email protected] [email protected] [email protected] [email protected] [email protected] [email protected] ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-rsa ssh-dss ssh-ed25519]
From https://www.openssh.com/agent-restrict.html :
OpenSSH 8.9 will include the ability to control how and where keys in ssh-agent may be used, both locally and when forwarded
This seems to be done on the protocol side by including the server host key in the auth request - is this enough that guardian-agent could use it instead of having to put sga-ssh everywhere?
Right now, it seems the guardian-agent only support global policies. Either you trust the intermediate server once, forever (on host or on everything) or you don't.
It would be nice to have temporary policies of more flexible trust life span, such as:
sga-guard
process's life.sga-guard
process ended (whichever shorter).sga-guard
will discard the policy.Are there plans to integrate this with blink on iOS?
I have sga-guard setup and running:
Connecting to jumphost to set up forwarding...
Forwarding to jumphost setup successfully. Waiting for incoming requests...
Request by jumphost to run '' on user@final-destination-host:my-nonstandard-port AUTO-APPROVED by policy
I can mosh to jumphost just fine.
Once on jumphost, I can ssh to servers just fine.
If I try to sga-ssh to a server, I get this:
sga-ssh final-destination-host -p my-nonstandard-port
failed to connect to final-destination-host:my-nonstandard-port: ssh: handshake failed: EOF
Debug output shows:
2017/12/23 09:12:05 handshake.go:355: kex loop
2017/12/23 09:12:05 handshake.go:360: kex inner loop
2017/12/23 09:12:05 handshake.go:370: select exit: <-requestKex
2017/12/23 09:12:05 handshake.go:393: !sent: sending kexInit
2017/12/23 09:12:05 handshake.go:360: kex inner loop
2017/12/23 09:12:05 handshake.go:368: select exit: <-t.startKex
2017/12/23 09:12:05 handshake.go:416: entering keyexchange
2017/12/23 09:12:05 common.go:139: host key algorithms: server: [none], client: [none]
2017/12/23 09:12:05 delegated_client.go:582: KexCallback called
2017/12/23 09:12:05 handshake.go:355: kex loop
2017/12/23 09:12:05 handshake.go:360: kex inner loop
2017/12/23 09:12:05 delegated_client.go:539: Finished copying transport data to agent
2017/12/23 09:12:05 delegated_client.go:554: Finished copying transport data from agent
2017/12/23 09:12:05 delegated_client.go:492: Finished copying ssh data from agent: %!s(<nil>)
2017/12/23 09:12:05 delegated_client.go:476: Error copying outgoing SSH data: io: read/write on closed pipe
2017/12/23 09:12:05 handshake.go:365: select exit: <-t.startKex NOT OK
2017/12/23 09:12:05 sga-ssh.go:146: sga-ssh: Failed to run on my-final-destination-host: failed to connect to final-destination-host:my-nonstandard-port: ssh: handshake failed: EOF
failed to connect to final-destination-host:my-nonstandard-port: ssh: handshake failed: EOF
And debug server-side on final-destination-host shows:
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: Connection from jumphost-ip port 53272 on final-destination-host-ip port my-nonstandard-port
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: Client protocol version 2.0; client software version Go
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: no match: Go
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: Enabling compatibility mode for protocol 2.0
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: Local version string SSH-2.0-OpenSSH_7.2p2
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: permanently_set_uid: 110/65534 [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: list_hostkey_types: ssh-rsa,rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519 [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: SSH2_MSG_KEXINIT sent [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: SSH2_MSG_KEXINIT received [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: kex: algorithm: [email protected] [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: kex: host key algorithm: (no match) [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: fatal: Unable to negotiate with jumphost port 53272: no matching host key type found. Their offer: [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: do_cleanup [preauth]
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: monitor_read_log: child log fd closed
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: do_cleanup
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: Killing privsep child 23529
Dec 23 06:16:03 final-destination-shorthost sshd[23528]: debug1: audit_event: unhandled event 12
I tried removing my entry from .ssh/known_hosts on jumphost. No change.
I should note that my SSH key is ed25519
I just changed to an RSA key. No change.
Hello.
Would it be possible to get an update on the status of this project? Is it done? Is there something lacking to remove the beta label?
It seems to be the encouraged method for ssh agent forwarding when using mosh, do you know if this is still the case?
An interesting use of agent forwarding, combined with a hardware-based SSH key (eg. Yubikey) that I discovered recently is to have sudo
on a remote machine depend on a signature provided by the installed SSH agent. This would be a great use case for guardian agent IMO. I'm guessing it would require writing a separate PAM module, since guardian agent doesn't provide SSH_AUTH_SOCK for the existing PAM module to query.
sga-ssh --debug -R 63697:localhost:46417 -N -vvv -o StrictHostKeyChecking=no
Unknown option: R
Unknown option: N
Unknown option: vvv
sga-ssh: unsupported option: StrictHostKeyChecking=no
I followed the brew instructions and got the following
sga-guard <hostname>
doesn't produce any output
Not sure what this one is, but they produce segmentation faults
> sga-guard-bin <hostname>
[1] 62556 segmentation fault sga-guard-bin <hostname>
> sga-ssh <hostname>
[1] 62609 segmentation fault sga-ssh <hostname>
I tried downloading go so I could install from source, but could not figure that out.
> go get github.com/StanfordSNR/guardian-agent/...
go: go.mod file not found in current directory or any parent directory.
'go get' is no longer supported outside a module.
To build and install a command, use 'go install' with a version,
like 'go install example.com/cmd@latest'
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.
> go install github.com/StanfordSNR/guardian-agent@latest
go: finding module for package github.com/hashicorp/yamux
go: finding module for package golang.org/x/crypto/ssh/terminal
go: finding module for package golang.org/x/sys/unix
go: finding module for package golang.org/x/crypto/ssh/agent
go: finding module for package github.com/sternhenri/interact
go: finding module for package golang.org/x/crypto/ssh
go: finding module for package github.com/howeyc/gopass
go: finding module for package golang.org/x/crypto/ssh/knownhosts
go: found github.com/hashicorp/yamux in github.com/hashicorp/yamux v0.1.1
go: found github.com/howeyc/gopass in github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef
go: found github.com/sternhenri/interact in github.com/sternhenri/interact v0.0.0-20170607043113-dfeb9ef20304
go: found golang.org/x/crypto/ssh in golang.org/x/crypto v0.5.0
go: found golang.org/x/crypto/ssh/agent in golang.org/x/crypto v0.5.0
go: found golang.org/x/crypto/ssh/knownhosts in golang.org/x/crypto v0.5.0
go: found golang.org/x/crypto/ssh/terminal in golang.org/x/crypto v0.5.0
go: found golang.org/x/sys/unix in golang.org/x/sys v0.4.0
package github.com/StanfordSNR/guardian-agent is not a main package
On rtr
, I'm running an ssh-agent and have password-less login to bigbox
. Running sga-guard
:
achin@rtr ~/tmp/09 $ sga-guard achin@bigbox
DISPLAY environment variable is not set. Using terminal for user prompts.
Connecting to achin@bigbox to set up forwarding...
Forwarding to achin@bigbox setup successfully. Waiting for incoming requests...
Now on bigbox
I run the following:
achin@bigbox ~ $ sga-ssh achin@bigbox
failed to run command: failed to create session: ssh: unexpected packet in response to channel open: <nil>
Meanwhile on rtr
:
Allow achin@bigbox to run '' on achin@bigbox:22?
1) Disallow
2) Allow once
3) Allow forever
4) Allow achin@bigbox to run any command on achin@bigbox:22 forever
? Answer 2
Request by achin@bigbox to run '' on achin@bigbox:22 APPROVED by user
Can't enforce permission for a single command. Allow achin@bigbox to run ANY COMMAND on achin@bigbox:22?
1) Disallow
2) Allow once
3) Allow forever
? Answer 2
Request by achin@bigbox to run ANY COMMAND on achin@bigbox:22 APPROVED by user
(the error from sga-ssh
didn't appear until i selected the "2" option the second time on rtr
)
Running v0.7.0-beta
on both machines
Currently, if more than one sga-guard session is initiated to the same (user,machine), each session overrides the unique symbolic link used for communication. When that latest session dies, the older session does not become active again.
We should perhaps use a directory of sockets, and sga-ssh will use the latest one (based on timestamp)
The homebrew formula no longer works, b/c the ssh-agent repository is not available.
Error: Failure while executing;
git clone https://github.com/theseal/homebrew-ssh-agent /usr/local/Homebrew/Library/Taps/theseal/homebrew-ssh-agent exited with 128.
When connecting from the intermediary to a new remote host, the local guardian agent should allow the user to add the remote host to the local known_hosts file.
Neither sga-guard
nor sga-ssh
expose a --version
option. You might find this helpful as you start to get wider usage (right now, if you asked a user "what version of guardian-agent are you running", it's not so easy to get an accurate answer)
When I tried to download the latest release using command-line tools, I wound up with an extra redirect link with a bunch of X-Amz parameters added as part of the URL.
When I tried to curl/wget that, I got an error about missing one or more of those parameters.
When I used the basic query (to curl for the latest release page, followed by grepping/cutting) to get the "real" URL and then opened that using links, I was able to download the tarball without any problems.
I know this is basically abandonware, but it seems worth mentioning in case anyone else runs across the problem in the future.
I frequently see errors like this, though the seq num varies. If I keep retrying, it will eventually succeed, and I never see it if I unset GIT_SSH_COMMAND
so that I'm not using sga-ssh
. The output from sga-guard
shows that the request was approved even when I get this error, and there are no errors printed on that end (only on the intermediary).
% git fetch -tp --all && git reset --hard @{u}
Fetching origin
ssh: MAC failure, expected seq num: 79
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
error: Could not fetch origin
Guardian (home laptop):
[edower@IT-USA-VX3493:~]1^0% sga-guard --version
v0.7.2-beta
[edower@IT-USA-VX3493:~]1^0% uname -a
Darwin IT-USA-VX3493 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33 PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64 i386 MacBookPro16,1 Darwin
Intermediary (remote dev VM):
[edower@edower:~]2^0% sga-ssh -V
v0.7.2-beta
[edower@edower:~]2^0% uname -a
Linux edower 5.8.0-1038-aws #40~20.04.1-Ubuntu SMP Thu Jun 17 13:25:28 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Testing on the macOS Beta results in:
โ sga-guard -V
fatal error: runtime: bsdthread_register error
runtime stack:
runtime.throw(0x12ac359, 0x21)
/usr/local/go/src/runtime/panic.go:596 +0x95 fp=0x5456750 sp=0x5456730
runtime.goenvs()
/usr/local/go/src/runtime/os_darwin.go:108 +0xa0 fp=0x5456780 sp=0x5456750
runtime.schedinit()
/usr/local/go/src/runtime/proc.go:486 +0xa1 fp=0x54567c0 sp=0x5456780
runtime.rt0_go(0x54567f0, 0x4, 0x54567f0, 0x1000000, 0x4, 0x5456bf8, 0x5456c06, 0x5456c1e, 0x5456c36, 0x0, ...)
/usr/local/go/src/runtime/asm_amd64.s:158 +0x183 fp=0x54567c8 sp=0x54567c0
This is guardian agent installed via homebrew.
Steps taken:
sga-guard user@remote
mosh user@remote
source sga-env.sh
on remotecd <repo> && git pull
on remoteOutput:
panic: d.nx != 0
goroutine 382 [running]:
crypto/sha256.(*digest).checkSum(0xc420115b80, 0x0, 0x0, 0x0, 0x0)
/usr/local/go/src/crypto/sha256/sha256.go:157 +0x29c
crypto/sha256.(*digest).Sum(0xc420142e80, 0xc4200e9600, 0x0, 0x20, 0x1a, 0x6, 0x6)
/usr/local/go/src/crypto/sha256/sha256.go:131 +0x69
crypto/hmac.(*hmac).Sum(0xc420413260, 0xc4200e9600, 0x0, 0x20, 0x6, 0x0, 0x0)
/usr/local/go/src/crypto/hmac/hmac.go:46 +0x56
github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh.(*streamPacketCipher).writePacket(0xc420142f00, 0xc40000004f, 0x7cb720, 0xc4201a0fc0, 0x7cb860, 0xc4200166f0, 0xc42011d000, 0x729, 0x729, 0x0, ...)
/home/dima/Projects/go/src/github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh/cipher.go:291 +0x314
github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh.(*connectionState).writePacket(0xc4203c5808, 0xc4201a0fc0, 0x7cb860, 0xc4200166f0, 0xc42011d000, 0x729, 0x729, 0xc42001c0a0, 0xc420115de8)
/home/dima/Projects/go/src/github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh/transport.go:198 +0xc1
github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh.(*transport).writePacket(0xc4203c57a0, 0xc42011d000, 0x729, 0x729, 0xc420115df8, 0xc42010c140)
/home/dima/Projects/go/src/github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh/transport.go:192 +0x75
github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh.(*handshakeTransport).pushPacket(0xc4203a3a00, 0xc42011d000, 0x729, 0x729, 0xc42006e480, 0xc42001c0d0)
/home/dima/Projects/go/src/github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh/handshake.go:308 +0x51
github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh.(*handshakeTransport).writePacket(0xc4203a3a00, 0xc42011d000, 0x729, 0x729, 0x0, 0x0)
/home/dima/Projects/go/src/github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh/handshake.go:687 +0x14f
github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh.(*proxy).Run.func2(0x0, 0x0, 0xc42049d040, 0xc42034f5c0)
/home/dima/Projects/go/src/github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh/proxy.go:214 +0x2bb
created by github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh.(*proxy).Run
/home/dima/Projects/go/src/github.com/StanfordSNR/guardian-agent/vendor/golang.org/x/crypto/ssh/proxy.go:231 +0xbb
Using the latest version from github, I cannot get tty allocation to work:
steved@fs2:~$ sga-ssh -t xubuntu tty
not a tty
Not specifying a command starts a login shell on the remote in a tty as expected, regardless of whether -t is specified or not.
Only anomaly here is fs2 (the intermediate machine) is an arm box.
Would be interested to know the status of this project - it looks very useful, but seems a bit dormant?
There are two machines in this scenario: rtr and bigbox.
On rtr:
achin@rtr ~/tmp/06 $ sga-guard achin@bigbox
DISPLAY environment variable is not set. Using terminal for user prompts.
Connecting to achin@bigbox to set up forwarding...
Password:
Forwarding to achin@bigbox setup successfully. Waiting for incoming requests...
Then on bigbox, normal ssh works:
achin@bigbox ~ $ ssh bigbox
You have old mail in folder /var/mail/achin.
achin@bigbox ~ $ exit
logout
Connection to bigbox closed.
achin@bigbox ~ $
But using sga-ssh
yields this:
achin@bigbox ~ $ sga-ssh bigbox
failed to connect to bigbox:22: ssh: handshake failed: ssh: disconnect, reason 0: knownhosts: key mismatch
and here is what sga-guard
is showing on rtr
:
Allow achin@bigbox to run '' on achin@bigbox:22?
1) Disallow
2) Allow once
3) Allow forever
4) Allow achin@bigbox to run any command on achin@bigbox:22 forever
? Answer 2
Request by achin@bigbox to run '' on achin@bigbox:22 APPROVED by user
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ecdsa-sha2-nistp256 key sent by the remote host is
10:88:b3:2e:53:db:8a:26:72:41:5c:f8:98:fc:98:a6.
Please contact your system administrator.
Add correct host key in /home/achin/.ssh/known_hosts to get rid of this message.
Host key verification failed.
I am confused why this error is appearing because sshing from rtr
to bigbox
is fine w.t.g host keys:
achin@rtr ~/tmp/06 $ ssh bigbox hostname
Password:
bigbox
Please package for debian and/or ubuntu. Ideally Ubuntu 18.04 (LTS).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.