jhaals / ansible-vault Goto Github PK
View Code? Open in Web Editor NEWansible lookup plugin for secrets stored in Vault(by HashiCorp)
License: BSD 3-Clause "New" or "Revised" License
ansible lookup plugin for secrets stored in Vault(by HashiCorp)
License: BSD 3-Clause "New" or "Revised" License
There are certain backends that have endpoints that don't return the standard JSON format with a data
key, or even JSON at all. For example the PKI module's CA retrieval method returns the certificate bare as seen here. This causes the plugin to error out with a message like:
An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <type 'exceptions.ValueError'>, original message: No JSON object could be decoded
The plugin tries to preemptively interpret the response from the API as JSON here, then get the data
field here.
I think it would make sense to inspect the content type of the response and if it's not application/json
(eg. it's application/pkix-cert
in case of the CA retrieval) return it without any processing. The hardcoded data
lookup could also be relaxed as it looks like backends can return arbitrary data, not only the Vault standard response.
My playbook being a simple hello world playbook:
---
#
# This playbook is making the assumption that VAULT_ADDR is exported in the environment.
#
- hosts: localhost
gather_facts: false
connection: local
tasks:
- debug: msg="The username secret is:{{ lookup('vault', 'secret/internal/CI/test', 'username') }}"
Works fine, as long as I work around the mac issue with fork #60. So I tried Updating to Python 3.6.4 from python.org and it has uncovered a problem with getting my access token through this plugin.
fatal: [localhost]: FAILED! => {
"msg": "An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unable to retrieve personal token from vault: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str."
}
Here are my versions:
[:~/work/Official/lab-ops-ansible] integrate_vault(+14/-0)* ยฑ ansible --version
ansible 2.4.3.0
config file = /Users/snip/work/Official/lab-ops-ansible/ansible.cfg
configured module search path = ['/Users/snip/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/ansible
executable location = /Library/Frameworks/Python.framework/Versions/3.6/bin/ansible
python version = 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
It seems to have to do with the way the post is formed:
Line 171 in 7c4aedc
I think this stackoverflow makes a suggestion that perhaps instead of the urllib2 request we should use the request library : https://stackoverflow.com/questions/5440485/typeerror-post-data-should-be-bytes-or-an-iterable-of-bytes-it-cannot-be-str
Hi
i'm trying to find htpasswd for several users, but not all of then do have a htpasswd access, no entry in vault, so when they do not have, i get:
failed: [nginx-01] (item=user1) => {"failed": true, "msg": "AnsibleError: An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unable to read secret/nginx/live/htpasswd/user1 from vault: HTTP Error 404: Not Found", "user": "user1"}
and stop the file template.
My template have this:
{% for user,pass in ( lookup('vault', 'secret/nginx/'+ env +'/htpasswd/' + office ) | default('') ).iteritems() %} {{ user }}:{{ pass }} {% endfor %}
I would assume that with the | default('')
, the plugin would continue, but it just fails
So is any way for the | default('')
to work? if not, maybe adding a extra parameter, or starting the url with +
, to flag that the query can fail silently
Thanks for the help and for the plugin
using a template like:
{{ lookup('vault', 'pki/platform/any common_name=foo.example.com format=pem_bundle') }}
have set:
VAULT_ADDR=https://10.204.95.71:27003
VAULT_CAHOSTVERIFY=no
VAULT_SKIP_VERIFY=true
get:
fatal: [10.204.94.123]: FAILED! => {"changed": false, "failed": true, "msg": "AnsibleError: An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unable to read pki/platform/any from vault: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)>"}
Hi @jhaals , great plugin!
Would you mind adding some content to the README describing the install process?
Is possible to use environment variables in ansible playbook? Just like my example?
Because i cant get it work...
- hosts: localhost
vars:
vault1_env:
VAULT_ADDR: https://localhost:8200/
VAULT_TOKEN: my-token-id
VAULT_SKIP_VERIFY: True
tasks:
- debug: msg="{{ lookup('vault', 'secret/hello', 'value') }}"
- shell: echo VAULT_ADDR is $VAULT_ADDR, VAULT_TOKEN is $VAULT_TOKEN, VAULT_SKIP_VERIFY is $VAULT_SKIP_VERIFY
environment: "{{ vault1_env }}"
register: shellout
- debug: var=shellout
Hi,
Thanks for the plugin ๐
we are trying to load all secrets using lookup's, but these lookup's are present global level in group/all/xyz.yml file
these is eager loading file, how to delay lookup's in this file ? is there a way or any suggestions
now we facing because of eager loading lookup's
fatal: [127.0.0.1]: FAILED! => {"failed": true, "msg": "{{ my_creds.one_passwd }}: {{ lookup('hashi_vault', 'secret/{{ mysec }}/pswd/one') }}: Vault or GitHub authentication token missing. Specify with vault_token ansible variable or VAULT_TOKEN/VAULT_GITHUB_API_TOKEN environment variable or in $HOME/.vault-token (Current $HOME value is /home/hos)"}
If i remove lookup's from the global level and place into specific environment file then it is working as expected
we are using ansilbe: 2.1.1.0
generate keystore file with below command:
keytool -keystore keystore -import -alias cert -file cert.crt -trustcacerts
But after write it to vault and read it again, I found the file is changed.
# original
$ md5sum keystore
2a8231bf6b87d4f49615625d420e5894 keystore
# export from vault read
$ md5sum keystore
9151fd3262a8773f8cfc2d1790d37e52 keystore
I got error in java:
java.io.IOException: Invalid keystore format
Read the comments in hashicorp/vault#1286, I encode the keystore content with below command to avoid this issue, it works.
$ base64 keystore | vault write secret/keystore value=-
$ vault read -field=value secret/keystore | base64 -d > keystore.out
$ md5sum keystore*
2a8231bf6b87d4f49615625d420e5894 keystore
2a8231bf6b87d4f49615625d420e5894 keystore.out
But when read the content with ansible-vault
.
$ cat default/main.yml:
keystore: "{{ lookup('vault','secret/keystore', 'vault') | b64decode }}"
$ cat tasks/main.yml
- name: copy keystore files
copy: content="{{ keystore }}" dest="/etc/ssl/keystore"
It is changed:
$ md5sum keystore
de81adbb412edba3a630ba6f1bcb5d34 keystore
hello
api has changed in kv v2:
-need to add version parameter
-if v2 then
return [result['data']['data'][field]]
and add '/data' between engine mount point and path - 'v1/engine_mount_point**/data**/path'
ansible-vault version: 2.7.0
ansible version: 2.2.1 and 2.4.0
The cache doesn't seem to ever be used for us. I tried setting --forks=1
and even then I get no cache hits.
Injecting some print
s into the code I noticed that _vault_cache
is getting populated when there's a cache miss, but the next time the plugin is used _vault_cache
is reset to {}
. It seems like Ansible is re-importing the plugin every time it's called?
I don't know enough about Ansible plugin development to say whether this is an Ansible core issue or a plugin issue.
Here's a simple example:
https://github.com/vincer/vault-plugin-cache-broken
vault auth
puts a token in ~/.vault-token
, with no newline. However, if you change the token manually, it's easy to add a newline at the end (echo
does this by default, as do most text editors).
The vault
cli handles this gracefully, but ansible-vault sends the entire contents of the file along, including the newline, producing errors like this one:
fatal: [localhost]: FAILED! => {
"failed": true,
"msg": "An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unable to read secret/hello from vault: Invalid header value '<my token here>\\n'"
}
It would be great if ansible-vault just did a strip()
on the data it reads in.
Hi,
Trailing this and it seem to be failing with below error message, when using with vault.
`
curl -s -X GET -H "X-Vault-Token:$VAULT_TOKEN"
https://$VAULT_ADDR/v1/secret/hello
| jq .data.bar
"baz"
ansible-playbook plugin_test.yml
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [localhost] ****************************************************************************************************************************************************************************************************************************
TASK [Get a secret from Vault] **************************************************************************************************************************************************************************************************************
Friday 16 February 2018 12:07:27 +0000 (0:00:00.115) 0:00:00.115 *******
fatal: [localhost]: FAILED! => {"failed": true, "msg": "An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unable to retrieve personal token from vault: unknown url type: v1/auth/approle/login"}
[WARNING]: Failure using method (v2_runner_on_failed) in callback plugin (</usr/lib/python2.7/site-packages/ansible/plugins/callback/mail.CallbackModule object at 0x30d9e10>): [Errno 113] No route to host
PLAY RECAP **********************************************************************************************************************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
`
``
export VAULT_ADDR='vault.apps.ocpt.local'
export ANSIBLE_HASHICORP_VAULT_ROLE_ID=${ROLE_ID}
echo ${ANSIBLE_HASHICORP_VAULT_ROLE_ID}
export ANSIBLE_HASHICORP_VAULT_SECRET_ID=${SECRET_ID}
echo ${ANSIBLE_HASHICORP_VAULT_SECRET_ID}
curl -s -X POST -H "X-Vault-Token:$VAULT_TOKEN" -d '{"bar":"baz"}'
https://$VAULT_ADDR/v1/secret/hello
curl -s -X GET -H "X-Vault-Token:$VAULT_TOKEN"
https://$VAULT_ADDR/v1/secret/hello
| jq .data.bar
ansible-playbook plugin_test.yml
``
Actual curl displays the secret as you can see in error, however the play fails !
I'm trying to use the lookup plugin in Ansible 2.3 in order to look up secrets in my playbooks. However, when I try to run the playbooks, Ansible hangs in the 'gathering facts' phase.
ansible-playbook app.yml --extra-vars "target_hosts=app01"
TASK [Gathering Facts] *************************************************************************************************************************************************************************************
objc[38943]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[38943]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
---
- name: Configure App server
hosts: "{{ target_hosts| default('app-servers') }}"
vars:
runtime_env: production
api_key: "{{ lookup('vault','secret=secret/production/api_key','value') }}"
aws: "{{ lookup('vault', 'aws/creds/common_get_s3_build') }}"
environment:
AWS_ACCESS_KEY: "{{ aws.access_key }}"
AWS_SECRET_KEY: "{{ aws.secret_key }}"
pre_tasks:
- name: install dependencies for ansible
apt: name={{ item }} state=latest
with_items:
- python-psycopg2
- python-boto
roles:
- common
- { role: users, include_dev: false }
- postgres
- { role: production, server_role: app }
ansible.cfg
. . .
lookup_plugins = ./lookup_plugins
. . .
ls -al lookup_plugins/
total 0
drwxr-xr-x 3 btennant staff 96 Aug 10 16:34 .
drwxr-xr-x 48 btennant staff 1536 Aug 10 20:18 ..
drwxr-xr-x 9 btennant staff 288 Aug 10 16:17 jhaals.ansible-vault
I've set VAULT_ADDRESS=https://vault_host:8200 and I've set the VAULT_TOKEN.
I'm not certain what is producing this - when I run a sample job against my Vault instance (for a test variable at 'secret/hello', I get this error in ansible:
fatal: [vault_host]: FAILED! => {"failed": true, "msg": "Unable to read secret/hello from vault"}
and this error is issued by Vault itself:
2016/04/05 23:07:06 http: TLS handshake error from 192.168.130.34:57117: remote error: unknown certificate authority
I don't have any issues with other apps or API's (Ruby Vault API, curl commands, Vault commands, etc...), but... none of them are Python based and so I imagine there could be some config I need to set. I tried setting VAULT_CAPATH like I do for Vault CLI operations, but that didn't solve the issue.
If I run a curl command from the same host, I get this:
curl -k -X GET -H "X-Vault-Token: $VAULT_TOKEN" https://vault_host:8200/v1/secret/hello
{"lease_id":"","renewable":false,"lease_duration":86400,"data":{"value":"world"},"warnings":null,"auth":null}
I guess I'm just wondering if you can help me figure out if the issue is with the plugin or "somewhere else that I need to go RTFM on".
Thanks!
EDIT: The more I research, the more this seems like an Ansible issue, but I'll leave it open until I'm certain.
Summary:
Attempting to use this plugin on High Sierra is causing a python crash. Reproduced on both Python 2.7.14 and 3.6.2 (installed with brew). Unable to reproduce with other lookup plugins.
Reproduction:
A simple playbook with a debug step will do:
- hosts: localhost
tasks:
- debug:
msg: "{{ lookup('vault', 'secret/dev/test', 'hello') }}"
Symptoms:
In the terminal which executes this playbook, the output looks like:
PLAY [localhost] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [debug] *******************************************************************
objc[34976]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[34976]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
Additional notes:
This appears to be due to some low-level objective-C changes introduced in High Sierra. This article (http://sealiesoftware.com/blog/archive/2017/6/5/Objective-C_and_fork_in_macOS_1013.html) has more details. It also provides a workaround: Setting the environment variable OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
. Setting this before running ansible provides successful results.
I'll be honest, I have no clue what the "correct" fix is here, or even if it can be made in this plugin (as opposed to ansible or python itself). Mostly I'm documenting this here in case others come across this issue - I hope someone finds this useful. I'm content with using the environment variable for the time being.
Ansible version: 2.5.4
Jinja version: 2.10
ansible-vault version: 2.9.0 or master
Error was a <class 'ansible.errors.AnsibleError'>, original message: An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unable to retrieve personal token from vault: 'dict' object has no attribute 'encode'"
When using a dynamic secret backend that has multi-key values, such as the AWS secret backend which contains access_key
and secret_key
keys, the lookup plugin gets executed independently when referencing each key. This behavior is not seen when using templates, since Ansible is calling the template engine during the play and the {{ set
behavior DOES cache the results. Since the lookup plugin is called again, you cannot simply set a multi-key variable without getting mis-matched secrets.
consider the playbook:
---
- name: create standalone sandbox of rwell applications
hosts: localhost
vars:
aws_creds: "{{ lookup('vault', 'aws/creds/common_get_s3_build') }}"
tasks:
- debug:
var: aws_creds
- debug:
var: aws_creds.access_key
- debug:
var: aws_creds.secret_key
ok: [localhost] => {
"aws_creds": {
"access_key": "A",
"secret_key": "AA",
"security_token": null
}
}
ok: [localhost] => {
"aws_creds.access_key": "A"
}
ok: [localhost] => {
"aws_creds.secret_key": "AA"
}
ok: [localhost] => {
"aws_creds": {
"access_key": "A",
"secret_key": "AA",
"security_token": null
}
}
ok: [localhost] => {
"aws_creds.access_key": "B"
}
ok: [localhost] => {
"aws_creds.secret_key": "CC"
}
NOTE:
Access keys replaced by single-character identifiers.
Secret keys replaced by double-character identifiers matching corresponding access_key identifier.
Hi,
I inserted line from README into my playbook
- debug: msg="{{ lookup('vault', 'secret/foo', 'value') }}"
Bu I receive following error:
Unexpected error in during lookup: run() got multiple values for keyword argument 'inject'
Ansible 1.9.6
Am I doing something wrong or it's a bug?
Hi.
I just used your lookup plugin and it gives me this error:
TASK [Create skit_automata user] ****************************************************************************************************************************************************************************************************
fatal: [10.209.113.25]: FAILED! => {"failed": true, "msg": "An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <type 'exceptions.AttributeError'>, original message: 'list' object has no attribute 'split'"}
environment vars:
export VAULT_ADDR="https://127.0.0.1:8200/"
export VAULT_TOKEN="a3a51462-2884-b579-d769-93e7dad2dd75"
export ANSIBLE_HASHICORP_VAULT_USE_CACHE="no"
export VAULT_CACERT="/etc/ssl/certs/vault.pem"
Any idea?
Example:
lookup('vault', 'my/arbitrary/path param="My short sentence"')
This would resolve param
to be "My
. (Then an index error occurs when resolving the next term, so nothing is submitted)
I think this should be an easy fix using the shlex
module's split()
method. I'd be happy to submit a PR but I'd like your input first.
First of all, thanks for nice plugin!
Then, I was trying to do the following thing:
Any idea about how it can be done?
In theory I just need to store the key as a string and then write it to file, but could not find a way to do that yet..
Thanks
Hi,
I'm certain that I'm doing something wrong...
I'm using Molecule V2 to run on one of my roles inside my playbook. This particular role calls into vault to obtain a value. My issue is that I'm getting a cryptic python error and I'm stuck on where to debug the issue.
In molecule you can create a config file where you can customize certain configs such as a mock up ansible.cfg for example my molecule.yml file looks like this:
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
platforms:
- name: instance
image: centos:7
provisioner:
name: ansible
config_options:
defaults:
lookup_plugins : ../../../../../lookup_plugins
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8
Now if I do not set the lookup_plugin line above then I get an error that the playbook can't find vault... so I know ansible is able to find the plugin, however I'm getting this error when I run the converge command which is equivalent to running the playbook on just this role:
TASK [common : Obtain from vault the private ssh key for user ansible] *********
task path: /Users/snoby/work/Official/tropo-ops-proxy-ansible/roles/common/tasks/main.yml:36
fatal: [instance]: FAILED! => {
"msg": "An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <type 'exceptions.AttributeError'>, original message: 'exceptions.AttributeError' object has no attribute 'code'"
}
It seems like something can't dereference in the path where the code is for the plugin. Any suggestions?
I'm evaluating the use of these two vaults for use by an infrastructure team in an open source project.
It's unclear to me which difference/advantage applies to which vault in the section "What's the difference between ansible-vault and hashi_vault", and this is valuable information for an evaluation like mine.
Here are my guesses - if you can confirm, I'll submit a PR to clarify the README:
Thanks!
super plugin thank-you are you also aware of https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/lookup/hashi_vault.py merging yours into core ansible would be a huge win IMO
This seems similar to #3
FAILED! => {"failed": true, "msg": "Unable to read secret/ansible from vault: <urlopen error [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)>"}
brew install ansible
tls_min_version = "tls12"
set in vault.hcl
on servers (it's also the default now)The python version seems up to date, any other suggestion how I could address this?
Is there any possibility to do it? E.g. I have two users in secret/users
path and want to get their secrets and want to do it dynamically. I know, that I can define array with users or curl to get list of users from vault, but it's not convenient.
Hi would someone be able to take a quick look at the following issue and let me know if its possible or a limitation of the plugin.
Kind Regards,
K
ansible 2.4.3.0
config file = /Users/btennant/GitHub/DevOps_bencoffeed/ansible.cfg
configured module search path = [u'/Users/btennant/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /Library/Python/2.7/site-packages/ansible
executable location = /usr/local/bin/ansible
python version = 2.7.10 (default, Oct 6 2017, 22:29:07) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]
AND
ansible 2.5.0
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.5 (default, Aug 4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
ANSIBLE_SSH_ARGS(/var/lib/awx/projects/_7__test/ansible.cfg) = -C -o ControlMaster=auto -o ControlPe
DEFAULT_HOST_LIST(/var/lib/awx/projects/_7__test/ansible.cfg) = [u'/var/lib/awx/projects/_7__test/.a
DEFAULT_LOOKUP_PLUGIN_PATH(/var/lib/awx/projects/_7__test/ansible.cfg) = [u'/var/lib/awx/projects/_7
DEFAULT_ROLES_PATH(/var/lib/awx/projects/_7__test/ansible.cfg) = [u'/var/lib/awx/projects/_7__test/.
DEFAULT_SCP_IF_SSH(/var/lib/awx/projects/_7__test/ansible.cfg) = True
DEFAULT_STDOUT_CALLBACK(/var/lib/awx/projects/_7__test/ansible.cfg) = actionable
HOST_KEY_CHECKING(/var/lib/awx/projects/_7__test/ansible.cfg) = False
PARAMIKO_HOST_KEY_AUTO_ADD(/var/lib/awx/projects/_7__test/ansible.cfg) = True
PARAMIKO_LOOK_FOR_KEYS(/var/lib/awx/projects/_7__test/ansible.cfg) = False
Tested from CLI using Vagrant/Ansible on OS X High Sierra as well as via a hosted AWX container.
I'm attempting to follow instructions to use AppRole authentication.
I've set the environment variables mentioned in README.md
ANSIBLE_HASHICORP_VAULT_ROLE_ID
and
ANSIBLE_HASHICORP_VAULT_SECRET_ID
I've confirmed that I'm able to use the vault CLI to retrieve an approle token using the same role_id and secret_id. Additionally, i've ensured that I've set my secred_id ttl and max_num_uses to 0(infinite).
I've reproduced via Vagrant/Ansible locally on OS X High Sierra as well as via AWX containers.
OS X
TASK [users : Set SSH Keys for Ops Users and Task Users] ***********************
task path: /Users/btennant/GitHub/DevOps_bencoffeed/roles/users/tasks/main.yml:79
fatal: [ben-sandbox01]: FAILED! => {
"msg": "An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <type 'exceptions.AttributeError'>, original message: 'exceptions.AttributeError' object has no attribute 'code'"
}
AWX
fatal: [10.5.0.41]: FAILED! => {
"changed": false,
"msg": "AnsibleError: An unhandled exception occurred while running the lookup plugin 'vault'. Error was a <type 'exceptions.AttributeError'>, original message: 'exceptions.AttributeError' object has no attribute 'code'"
}
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.