GithubHelp home page GithubHelp logo

danmx / sigil Goto Github PK

View Code? Open in Web Editor NEW
69.0 4.0 13.0 421 KB

AWS SSM Session manager client

License: Apache License 2.0

Go 58.17% Starlark 40.33% Shell 0.99% Dockerfile 0.50%
aws aws-ssm cli session-manager ssh ssh-client ssm-agent ec2-instances aws-ssm-agent aws-ec2

sigil's People

Contributors

danmx avatar dependabot-preview[bot] avatar efpe avatar ekesken avatar fossabot avatar renovate-bot avatar sledigabel avatar zscholl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

sigil's Issues

Support Hashicorp Boundary

It looks that Hashicorp released similar service to AWS SSM called Boundary. It could be interesting opportunity to start multiprovider support.

Errors out without proper msg when EC2 Instance Connect upload fails due to lack of permissions

I've noticed that sigil was throwing an error without understandable message when failing to upload the key using ec2 instance connect api.

$ ssh xxx.compute.internal
DEBU[0000] session-manager-plugin is installed successfully in /usr/local/bin/session-manager-plugin  path=/usr/local/bin/session-manager-plugin plugin=session-manager-plugin
DEBU[0000] ssh inputs                                    gen-key-dir=/tmp/sigil/xxx.compute.internal/ gen-key-pair=true mfa= os-user=ec2-user port=22 profile= pub-key=/tmp/sigil/xxx.compute.internal/temp_key.pub region=eu-west-1 target=ip-xxx.compute.internal type=private-dns
DEBU[0000] Get MFA Token Provider                        token=
DEBU[0000] StartSSHInput                                 OSUser=ec2-user PortNumber=22 PublicKeyPath=/tmp/sigil/xxx.compute.internal/temp_key.pub pubKeyData="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAA9M9o3KyYtHd0AZQDbQiTB1ovGPr3EkTWxmtRdo1VHTo03RpRnXnlVUfItc1s57uR2eImKLfZr5SrIQnHGNfHTHw5fkubsSyCChu4jwnH2jF8GjduRc9ZdqkpweqV7Y+ELdNeUQfnJU3f2nJfoR1CWGLf8oiw9jT638Ge+B5ppi31UHDlVIncrt+RgumaLMGT0YrofzDIyPDWUlGhFDrzPKwWNArCHqSm7pkXSduBr0hxTvsAcGF2RM7bbq4d3IyDJzp32cYzwh3U320J5C6HvOu1dy3qX6eyizkYCupHAgBOkuC45c9BHwYamLzB+sp0aon4IcecxMXJYwzGW3ZQ9HbZAemxz4o65ErwRcnKG3fOqVnJEvEjW7NmtE8DA139gfHQumdStoUzKzBxucJ86uMl1+XWoDvJvINJQZNb0a0pNZIo7zkjb+O6RA2ryGIWY5ctx2d/q22IE3Ipd0ox1Ak04nHRlgEF9ynW1weEYuRwHfI77xn/Oyena3cHUNsBFxePXE+jy7f7fi/afNNBiRXrFeq8L5q8FPOG9073TLVD4II3wQ3431zDBHMjvQhbI7mZoEWnWHRTAFdB3jcNCld/lBbTIUFR89ZolhTCsIXAQqE4Txm3Tpx8Nc51/d45oXfEtDisXm90+JXUh2AgC1DXo6EekE2S+OK84Xqk9Z\n" target=ip-xxx.compute.internal targetType=private-dns
DEBU[0000] SendSSHPublicKey                              AvailabilityZone=eu-west-1a InstanceId=i-xxx InstanceOSUser=ec2-user SSHPublicKey="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAA9M9o3KyYtHd0AZQDbQiTB1ovGPr3EkTWxmtRdo1VHTo03RpRnXnlVUfItc1s57uR2eImKLfZr5SrIQnHGNfHTHw5fkubsSyCChu4jwnH2jF8GjduRc9ZdqkpweqV7Y+ELdNeUQfnJU3f2nJfoR1CWGLf8oiw9jT638Ge+B5ppi31UHDlVIncrt+RgumaLMGT0YrofzDIyPDWUlGhFDrzPKwWNArCHqSm7pkXSduBr0hxTvsAcGF2RM7bbq4d3IyDJzp32cYzwh3U320J5C6HvOu1dy3qX6eyizkYCupHAgBOkuC45c9BHwYamLzB+sp0aon4IcecxMXJYwzGW3ZQ9HbZAemxz4o65ErwRcnKG3fOqVnJEvEjW7NmtE8DA139gfHQumdStoUzKzBxucJ86uMl1+XWoDvJvINJQZNb0a0pNZIo7zkjb+O6RA2ryGIWY5ctx2d/q22IE3Ipd0ox1Ak04nHRlgEF9ynW1weEYuRwHfI77xn/Oyena3cHUNsBFxePXE+jy7f7fi/afNNBiRXrFeq8L5q8FPOG9073TLVD4II3wQ3431zDBHMjvQhbI7mZoEWnWHRTAFdB3jcNCld/lBbTIUFR89ZolhTCsIXAQqE4Txm3Tpx8Nc51/d45oXfEtDisXm90+JXUh2AgC1DXo6EekE2S+OK84Xqk9Z\n"
DEBU[0001] Checking if key exist                         err="<nil>" stat="&{temp_key 3239 384 {336391223 63731712297 0x27c7860} {16777220 33152 1 23368629 502 0 0 [0 0 0 0] {1596115497 336398100} {1596115497 336391223} {1596115497 336391223} {1596115497 336303797} 3239 8 4096 0 0 0 [0 0]}}"
DEBU[0001] Checking if key exist                         err="<nil>" stat="&{temp_key.pub 721 384 {335906913 63731712297 0x27c7860} {16777220 33152 1 23368628 502 0 0 [0 0 0 0] {1596115497 335932672} {1596115497 335906913} {1596115497 335906913} {1596115497 335689686} 721 8 4096 0 0 0 [0 0]}}"
Error: AuthException: Access Denied.
Usage:
  sigil ssh [flags]

Flags:
      --gen-key-dir string   the directory where temporary keys will be generated (default "/Users/danieliziourov/.sigil")
      --gen-key-pair         generate a temporary key pair that will be send and used. By default use /Users/danieliziourov/.sigil/temp_key as an identity file
  -h, --help                 help for ssh
      --os-user string       specify an instance OS user which will be using sent public key (default "ec2-user")
      --port uint            specify ssh port (default 22)
      --pub-key string       local public key that will be send to the instance, ignored when gen-key-pair is true
      --target string        specify the target depending on the type
      --type string          specify target type: instance-id/private-dns/name/name-tag (deprecated) (default "instance-id")

Global Flags:
  -c, --config string           full config file path, supported formats: json/yaml/toml/hcl/props
  -p, --config-profile string   pick the config profile (default "default")
      --log-level string        specify the log level: trace/debug/info/warn/error/fatal/panic (default "panic")
  -m, --mfa string              specify MFA token
      --profile string          specify AWS profile
  -r, --region string           specify AWS region

kex_exchange_identification: Connection closed by remote host

Parameterize Retry logic to Config

I've been experimenting with different retry logic configurations for different use cases. It would be nice to be able to specify the different retry options in a configuration file, environment variable, or flag (or all 3).

sigil/pkg/aws/aws.go

Lines 93 to 97 in c4ad289

numMaxRetries = 5
minRetryDelay = 10 * time.Millisecond
minThrottleDelay = 500 * time.Millisecond
maxRetryDelay = 1 * time.Second
maxThrottleDelay = 4 * time.Second

I currently have a retry policy that is much higher than the default, but I am not sure whether that should be a universal setting, so I would prefer not to force it on everyone using this tool:

image

Add MFA support

Add a tip to enter MFA code and used it if it's required for the aws profile being used

Support run command

Add support for run command also in a way so it can accept input from stdin

`name-tag` no longer works in `0.5.1`

I upgraded to 0.5.1 and I'm now unable to use the --type name-tag selector.

Was this deprecated?

SSH config

Host *.instance.by.tag
    User ssm_user
    IdentityFile ~/.sigil/temp_key
    ProxyCommand sh -c 'sigil ssh --type name-tag --target %h --port %p --pub-key "${HOME}"/.sigil/$(date +%%s)_key.pub --gen-key-pair --os-user ssm_user --profile my-profile --region us-east-1

Error message:

❯ ssh some.instance.by.tag
Error: unsupported target type: name-tag
Usage:
  sigil ssh [flags

Support multiplexing sessions with ControlPersist SSH options

We have some users that are using sigil with the ProxyCommand through tools like Ansible and Chef Knife. We're getting some ThrottlingExceptions as sessions and commands are exiting and entering too quickly.

CleanShot 2020-11-17 at 11 00 31@2x

In looking at this, what would make it possible to implement support for reusing SSH sessions? I would like our users to add this to their ~/.ssh/config but this doesn't appear to be supported:

Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600

https://puppet.com/blog/speed-up-ssh-by-reusing-connections/

CleanShot 2020-11-17 at 11 21 26@2x

Feature request: Interactive mode for list command

Allow users to list instances and pick which one they want to connect.

Example solution:
Set an interactive flag for list command that will print a list of instances with corresponding indexes. Follow it with a prompt for the index and then start a session for chosen instance.

File contention with multiple SSH sessions and --gen-key-pair

Related to #175, we also have some contention with settings that use the --gen-key-pair flag and a path:

Match User ubuntu* Host i-*
    IdentityFile ~/.sigil/%h/temp_key
    IdentitiesOnly yes
    ForwardAgent yes
    ProxyCommand sh -c 'sigil ssh --port %p --gen-key-pair --gen-key-dir "${HOME}"/.sigil/%h/ --os-user %r %h'

In connecting to multiple hosts at once, similar to Chef, Ansible, tmux-cssh, et al, we get connection errors:

no such identity: /Users/me/.sigil/i-11111111111111111/temp_key: No such file or directory

I'm not sure how to configure around this, as the ~/.ssh/config uses a static temp_key path, and I would need this file to be unique every time sigil is run.

RSA - SSH Public key is invalid

I recently upgraded to 0.5.2 and now I'm unable to SSH via an instance ID with this SSH config

Host i-* mi-*
    User ssm_user
    IdentityFile ~/.sigil/temp_key
    ProxyCommand sh -c 'sigil ssh --target %h --port %p --pub-key "${HOME}"/.sigil/temp_key.pub --gen-key-pair --os-user ssm_user'
    ForwardAgent yes

This is the error message I received:

❯ ssh i-xxxxxxx
Error: InvalidArgsException: Invalid input parameter received for Illegal argument exception: RSA - SSH Public key is invalid
Usage:
  sigil ssh [flags]

Flags:

And with debug logging set:

❯ ssh i-xxxxxxxxx
DEBU[0000] session-manager-plugin is installed successfully in /usr/local/bin/session-manager-plugin  path=/usr/local/bin/session-manager-plugin plugin=session-manager-plugin
DEBU[0000] ssh inputs                                    gen-key-dir=/Users/zscholl/.sigil gen-key-pair=true mfa= os-user=ssm_user port=22 profile= pub-key=/Users/zscholl/.sigil/temp_key.pub region= target=i-xxxxxxxxxx type=instance-id
DEBU[0000] Get MFA Token Provider                        token=
DEBU[0001] StartSSHInput                                 OSUser=ssm_user PortNumber=22 PublicKeyPath=/Users/zscholl/.sigil/temp_key.pub pubKeyData="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAAwy2lbcyu0im9kYQ3uXsLrW31hJwVcUYhJEgBUiutBznvRiNDIrnjz4rsAdw+A4JqZ8HeMBeDC2En3tGpMKCwNm+aAnvKXy7uum7O5yPbF0dzf0rkOIZot76dKIaUxrjugR0QVNT6v2p3BuXJhOXMxzK2EzM7AFQn+Q1Wqhg6t/Ts4lj+CVXf09iS9DnmQ+ye8YHTJSYaJuAHlpbnowSkAvAukTVD4ncG8wrc1+Vsm8ipjv8tyTtT9Wx54tXeITuDvgcwRBPCO1gCrDc5HIEPEE5e8jUxVf0d09XKJkU3jES4FUk85AMOzwS37bzXkaVmf9VqGP/RLfYRgZBF2MTNChkI+B+SFABW49IsO6FF7FaJwyHh6yvkofp3m9GdDiMqw8rNSY+Nx1V17uZLZlNqspjAUh/LtCSdZZPYVSoayIZycgBkn1utvXG+dxqnRjcDpFlFonusX71j1eNVJeewcQrh86JXSFENGTw1wBk7FW3JEr2srZX9UYwVaKis2OeUU97WTsouwR0BHhXBGtNFJS5Kv1W8SMgS82weEscCD1EOy9cpUzjnbUlf4OT4XVTtCP0tGKcflm64Q2D0WzxgCd/+7QSZa50FfZSACwqogMpxvOLwffsmh5GF7kfNIHEUQzUSP4PkPZ6pcSRvOLnnQ+fl97yIusYKYPFb0oZqrn\n" target=i-xxxxxxxxxxx targetType=instance-id
DEBU[0002] SendSSHPublicKey                              AvailabilityZone=us-west-2a InstanceId=i-xxxxxxx InstanceOSUser=ssm_user SSHPublicKey="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAAwy2lbcyu0im9kYQ3uXsLrW31hJwVcUYhJEgBUiutBznvRiNDIrnjz4rsAdw+A4JqZ8HeMBeDC2En3tGpMKCwNm+aAnvKXy7uum7O5yPbF0dzf0rkOIZot76dKIaUxrjugR0QVNT6v2p3BuXJhOXMxzK2EzM7AFQn+Q1Wqhg6t/Ts4lj+CVXf09iS9DnmQ+ye8YHTJSYaJuAHlpbnowSkAvAukTVD4ncG8wrc1+Vsm8ipjv8tyTtT9Wx54tXeITuDvgcwRBPCO1gCrDc5HIEPEE5e8jUxVf0d09XKJkU3jES4FUk85AMOzwS37bzXkaVmf9VqGP/RLfYRgZBF2MTNChkI+B+SFABW49IsO6FF7FaJwyHh6yvkofp3m9GdDiMqw8rNSY+Nx1V17uZLZlNqspjAUh/LtCSdZZPYVSoayIZycgBkn1utvXG+dxqnRjcDpFlFonusX71j1eNVJeewcQrh86JXSFENGTw1wBk7FW3JEr2srZX9UYwVaKis2OeUU97WTsouwR0BHhXBGtNFJS5Kv1W8SMgS82weEscCD1EOy9cpUzjnbUlf4OT4XVTtCP0tGKcflm64Q2D0WzxgCd/+7QSZa50FfZSACwqogMpxvOLwffsmh5GF7kfNIHEUQzUSP4PkPZ6pcSRvOLnnQ+fl97yIusYKYPFb0oZqrn\n"
DEBU[0003] Checking if key exist                         err="<nil>" stat="&{temp_key 3239 384 {184202224 63728079641 0x27be780} {16777220 33152 1 20417932 502 20 0 [0 0 0 0] {1592482841 184209669} {1592482841 184202224} {1592482841 184202224} {1592482841 184111401} 3239 8 4096 0 0 0 [0 0]}}"
DEBU[0003] Checking if key exist                         err="<nil>" stat="&{temp_key.pub 721 384 {181969810 63728079641 0x27be780} {16777220 33152 1 20417931 502 20 0 [0 0 0 0] {1592482841 181978329} {1592482841 181969810} {1592482841 181969810} {1592482841 181864329} 721 8 4096 0 0 0 [0 0]}}"
Error: InvalidArgsException: Invalid input parameter received for Illegal argument exception: RSA - SSH Public key is invalid

Homebrew fails to install

Tried to install with homebrew but I got the following error:

❯ brew install danmx/sigil/sigil
Running `brew update --preinstall`...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core and mshell/mshell).
==> New Formulae
bartib                                            ddh                                               sqls
==> Updated Formulae
Updated 340 formulae.

==> Tapping danmx/sigil
Cloning into '/opt/homebrew/Library/Taps/danmx/homebrew-sigil'...
remote: Enumerating objects: 74, done.
remote: Counting objects: 100% (48/48), done.
remote: Compressing objects: 100% (33/33), done.
remote: Total 74 (delta 28), reused 34 (delta 15), pack-reused 26
Receiving objects: 100% (74/74), 29.77 KiB | 2.13 MiB/s, done.
Resolving deltas: 100% (41/41), done.
Error: Invalid formula: /opt/homebrew/Library/Taps/danmx/homebrew-sigil/sigil.rb
sigil: Calling bottle :unneeded is disabled! There is no replacement.
Please report this issue to the danmx/sigil tap (not Homebrew/brew or Homebrew/core):
  /opt/homebrew/Library/Taps/danmx/homebrew-sigil/sigil.rb:10

Error: Cannot tap danmx/sigil: invalid syntax in tap!

Create Homebrew formula for easy install

Since this tool is actively developed (👍 ) with a lot of releases it takes time to update it all the time. Would you mind to create a homebrew formula for easier updates?

Filtering instance list by tags doesn't work

When configured only in configuration file sigil returns:

Error: unexpected end of JSON input

When using flags:

Error: ValidationException: Cannot mix tag or inventory filter with any other filter.
	status code: 400, request id: cfa71666-e8ed-41fc-aa5b-655cd0ebaa0b

ThrottlingException errors with SendSSHPublicKey

We are doing some work with Sigil and discovered a few cases where it fails to work because of some ThrottlingException error upstream in the AWS API:

I had added some debug logging in the binary to get this log output, but we have run into this on occassion when working with the tool for our EC2 connections.

...
time="2020-07-27T16:39:17-05:00" level=debug msg=SendSSHPublicKey error="ThrottlingException: Server is busy! Please retry after a moment." goString="{\n\n}" requestID=0xc00049a8a0
time="2020-07-27T16:39:17-05:00" level=debug msg="Checking if key exist" err="<nil>" stat="&{temp_key 3239 384 {382046935 63731482747 0x2653aa0} {16777220 33152 1 201576824 502 20 0 [0 0 0 0] {1595885947 382059695} {1595885947 382046935} {1595885947 382046935} {1595885947 381929663} 3239 8 4096 0 0 0 [0 0]}}"
time="2020-07-27T16:39:17-05:00" level=debug msg="Checking if key exist" err="<nil>" stat="&{temp_key.pub 721 384 {381659680 63731482747 0x2653aa0} {16777220 33152 1 201576823 502 20 0 [0 0 0 0] {1595885947 381679703} {1595885947 381659680} {1595885947 381659680} {1595885947 381485599} 721 8 4096 0 0 0 [0 0]}}"
Error: ThrottlingException: Server is busy! Please retry after a moment.
Usage:
  sigil ssh [flags]

Flags:
      --gen-key-dir string   the directory where temporary keys will be generated (default "/Users/nklauer/.sigil")
      --gen-key-pair         generate a temporary key pair that will be send and used. By default use /Users/nklauer/.sigil/temp_key as an identity file
  -h, --help                 help for ssh
      --os-user string       specify an instance OS user which will be using sent public key (default "ec2-user")
      --port uint            specify ssh port (default 22)
      --pub-key string       local public key that will be send to the instance, ignored when gen-key-pair is true
      --target string        specify the target depending on the type
      --type string          specify target type: instance-id/private-dns/name/name-tag (deprecated) (default "instance-id")

Global Flags:
  -c, --config string           full config file path, supported formats: json/yaml/toml/hcl/props
  -p, --config-profile string   pick the config profile (default "default")
      --log-level string        specify the log level: trace/debug/info/warn/error/fatal/panic (default "panic")
  -m, --mfa string              specify MFA token
      --profile string          specify AWS profile
  -r, --region string           specify AWS region

It would be nice to be able to handle and add some kind of exponential backoff to work around this limit. I don't know what our API limits are for this, but they appear pretty low.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): update dependency bazel to v4.2.3
  • chore(deps): update dependency bazel_gazelle to v0.30.0
  • chore(deps): update dependency io_bazel_rules_docker to v0.25.0
  • chore(deps): update dependency io_bazel_rules_go to v0.39.0
  • fix(deps): update module github.com/sirupsen/logrus to v1.9.0
  • fix(deps): update module github.com/spf13/cobra to v1.7.0
  • fix(deps): update module github.com/spf13/viper to v1.15.0
  • fix(deps): update module github.com/stretchr/testify to v1.8.2
  • chore(deps): update actions/cache action to v3
  • chore(deps): update actions/checkout action to v3
  • chore(deps): update bazelbuild/setup-bazelisk action to v2
  • chore(deps): update codecov/codecov-action action to v3
  • chore(deps): update dependency bazel to v6
  • chore(deps): update docker/login-action action to v2
  • chore(deps): update ubuntu docker tag to v22
  • fix(deps): update module gopkg.in/yaml.v2 to v3

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

bazel
WORKSPACE
  • io_bazel_rules_go v0.29.0
  • bazel_gazelle v0.24.0
  • io_bazel_rules_docker v0.20.0
  • com_github_danmx_bazel_tools 0.3.1
bazelisk
.bazelversion
  • bazel 4.2.1
dockerfile
.devcontainer/Dockerfile
  • ubuntu 21.04
github-actions
.github/workflows/linters.yaml
  • actions/checkout v2
  • bazelbuild/setup-bazelisk v1
  • actions/cache v2
.github/workflows/release.yaml
  • actions/checkout v2
  • actions/cache v2
  • docker/login-action v1
  • bazelbuild/setup-bazelisk v1
  • softprops/action-gh-release v1
.github/workflows/test-linux.yaml
  • actions/checkout v2
  • actions/cache v2
  • docker/login-action v1
  • bazelbuild/setup-bazelisk v1
  • codecov/codecov-action v2
.github/workflows/test-macos.yaml
  • actions/checkout v2
  • actions/cache v2
  • bazelbuild/setup-bazelisk v1
gomod
go.mod
  • go 1.17
  • github.com/aws/aws-sdk-go v1.41.14
  • github.com/golang/mock v1.6.0
  • github.com/mitchellh/go-homedir v1.1.0
  • github.com/sirupsen/logrus v1.8.1
  • github.com/spf13/cobra v1.2.1
  • github.com/spf13/viper v1.9.0
  • github.com/stretchr/testify v1.7.0
  • golang.org/x/crypto v0.0.0-20210921155107-089bfa567519@089bfa567519
  • gopkg.in/yaml.v2 v2.4.0

  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.