oefenweb / ansible-postfix Goto Github PK
View Code? Open in Web Editor NEWAnsible role to set up postfix in Debian-like systems
License: MIT License
Ansible role to set up postfix in Debian-like systems
License: MIT License
I have setup my postfix install with this flag set encrypt, yet receiving host seen this message as not encrypted.
I have noticed it was not added to config due to missing: postfix_relaytls (which I don't have setup).
Adding smtp_tls_security_level = encrypt manually & restarting service (without postfix_relaytls) fixed it.
Is it actually relay specific setting?
In README.md, there is a variable called postfix_relayport. In the configuration template, this variable is called postfix_relayhost_port. Should be fixed in README.md
If I run the playbook with:
recipient_canonical_maps:
- recipient: root
- rewrite: [email protected]
Then I run the playbook with:
recipient_canonical_maps:
- recipient: postmaster
- rewrite: [email protected]
And then I inspect /etc/postfix/recipient_canonical_maps
$ cat /etc/postfix/recipient_canonical_maps
root [email protected]
postmaster [email protected]
Not sure which version of Postfix started that, but I get non-working postfix with fatal error while trying your playbook examples:
Feb 3 13:07:13 git postfix/smtpd[75989]: fatal: in parameter smtpd_relay_restrictions or smtpd_recipient_restrictions, specify at least one working instance of: reject_unauth_destination, defer_unauth_destination, reject, defer, defer_if_permit or check_relay_domains
So it seems
postfix_smtpd_recipient_restrictions:
- check_relay_domains
is mandatory for the simplest relay configuration.
Either try to download the role from Ansible Galaxy, which raises the error:
[WARNING]: - tersmitten.postfix was NOT installed successfully: - sorry,
tersmitten.postfix was not found on https://galaxy.ansible.com.
or navigate to https://galaxy.ansible.com/tersmitten/postfix
ansible-postfix role can be downloaded from Ansible Galaxy
ansible-postfix/tasks/main.yml
Line 70 in 123eb6a
The line above fails on Ansible 2.10.
A simple way to reproduce is with:
- name: Debug
hosts: all
tasks:
- debug:
msg: "Check is {{ ansible_check_mode }}"
no_log: not ansible_check_mode
TASK [d] *********************************************************************************
fatal: [vdev]: FAILED! => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result"}
Hi,
would you like to add an option to specify a type of virtual alias map?
By default, it's assumes hash, by If I want to specify regexp, it's ignored.
postfix_virtual_aliases_type: regexp
Set IP address to a.b.c.d on the server
Run this role
# grep inet_interfaces /etc/postfix/main.cf
inet_interfaces = a.b.c.d
Change IP address to a.b.c.e
Run the role again and apt fails at running newaliases step
Running newaliases
newaliases: fatal: parameter inet_interfaces: no local interface found for a.b.c.d
Looks like the fact is updated for ansible_default_ipv4.address but because apt install step is before the config for postfix the error occurs.
I have a relayhost configured and when I start postfix I get this error in mail.err:
Jul 17 16:13:25 luna postfix/smtpd[30323]: fatal: in parameter smtpd_relay_restrictions or smtpd_recipient_restrictions, specify at least one working instance of: reject_unauth_destination, defer_unauth_destination, reject, defer, defer_if_permit or check_relay_domains
After manually adding to main.cf at least:
smtpd_relay_restrictions = permit_sasl_authenticated,reject_unauth_destination
the error went away.
Could you please add this option?
Hi the team,
and thanks a lot for your awesome job on POSTFIX ansible role.
My post is not really an issue but a question.
In Postfix official documentation I saw that if I use
smtpd_helo_restrictions = reject_invalid_helo_hostname
Postfix recommands also to set up
smtpd_helo_required = yes
To avoid client to skip reject_invalid_helo_hostname
by not sending HELO or EHLO command
I can't find out in your code if this situation is taken care of.
I can't find out a role's variable called postfix_smtpd_helo_required
Can you help me please ?
Regards
Running role v3.5.0 on Debian 10.
$ cd /etc/postfix
$ ls -l
total 136
...
-rw------- 1 root root 98 Jun 4 15:44 sasl_passwd
-rw-r--r-- 1 root root 12288 Jun 4 15:44 sasl_passwd.db
$ strings sasl_passwd
strings: sasl_passwd: Permission denied
$ strings sasl_passwd.db
[email protected]:my-password
[smtp.gmail.com]:587
This seems to be because the postmap sasl_passwd
handler is using postmap -p
. This looks deliberate, since the other handlers don't. I think it's a mistake?
At the moment this role will only output smtp_tls_security_level
in the configuration file (/etc/postfix/main.cf
) when a postfix_relayhost
is configured, so it is not possible to use TLS without relayhost. It is not totally clear to me why there is a dependency between these variables. @tersmitten Let me know whether you are open for a change; I would like to create a pull request. ๐
Hello, thanks for creating a tag for 2.8.5! Would you please publish it to galaxy.ansible.com so that folks can more easily download it?
When creating entries in the virtual file, shouldn't those entries have a 'virtual-alias.domain anything' entry per virtual-alias.domain? The postfix virtual man page says:
The virtual-alias.domain anything entry is required for a virtual alias domain. Without this entry, mail is rejected with "relay access denied", or bounces with "mail loops back to myself".
ansible-postfix/tasks/main.yml
Lines 165 to 182 in 7afaba0
The role is not handling the transport_maps
exclusive. Means, once you set one transport map item, it will life forever, even if you remove the item from postfix_transport_maps
in your playbook.
One possible fix would be
--- /tmp/before.yml 2024-05-21 10:21:01.294611164 +0200
+++ /tmp/after.yml 2024-05-21 10:21:19.108152033 +0200
@@ -7,7 +7,7 @@
group: root
mode: 0644
create: true
- state: present
+ state: "{{ item.state | default('present') }}"
with_items: "{{ postfix_transport_maps }}"
notify:
- postmap transport_maps
That brings the possibility to drain single items from your postfix_transport_maps
.
Config, in a task:
- name: set up postfix for notifications
include_role:
name: Oefenweb.postfix
vars:
postfix_aliases:
- user: root
alias: "{{ admin_email }}"
postfix_relayhost: my.fqdn
postfix_inet_interfaces: localhost
Traceback:
The full traceback is:
File "/tmp/ansible_copy_payload_n0uqaa_r/ansible_copy_payload.zip/ansible/modules/files/copy.py", line 675, in main
File "/tmp/ansible_copy_payload_n0uqaa_r/ansible_copy_payload.zip/ansible/module_utils/basic.py", line 2231, in atomic_move
os.chown(b_src, dest_stat.st_uid, dest_stat.st_gid)
fatal: [ark]: FAILED! => {
"changed": false,
"checksum": "905f0ac0c93a01c9195fd9ccc556472bb6e8cb30",
"diff": [],
"invocation": {
"module_args": {
"_original_basename": "mailname.j2",
"attributes": null,
"backup": false,
"checksum": "905f0ac0c93a01c9195fd9ccc556472bb6e8cb30",
"content": null,
"delimiter": null,
"dest": "/etc/mailname",
"directory_mode": null,
"follow": false,
"force": true,
"group": "root",
"local_follow": null,
"mode": 420,
"owner": "root",
"regexp": null,
"remote_src": null,
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": "/lmb/home/cbarnes/.ansible/tmp/ansible-tmp-1594200472.29-9494-34511939755517/source",
"unsafe_writes": null,
"validate": null
}
},
"msg": "failed to copy: /lmb/home/cbarnes/.ansible/tmp/ansible-tmp-1594200472.29-9494-34511939755517/source to /etc/mailname",
"traceback": "Traceback (most recent call last):\n File \"/tmp/ansible_copy_payload_n0uqaa_r/ansible_copy_payload.zip/ansible/modules/files/copy.py\", line 675, in main\n File \"/tmp/ansible_copy_payload_n0uqaa_r/ansible_copy_payload.zip/ansible/module_utils/basic.py\", line 2231, in atomic_move\n os.chown(b_src, dest_stat.st_uid, dest_stat.st_gid)\nPermissionError: [Errno 13] Permission denied: b'/lmb/home/cbarnes/.ansible/tmp/ansible-tmp-1594200472.29-9494-34511939755517/source'\n"
}
Seems to be a privilege escalation issue - any thoughts?
Because /etc/aliases is managed in with the lineinfile module and a loop, the playbook has no way to know when an item has been removed. If no changes is detected on the other lines, the "new aliases" handler is not fired.
Maybe it could be possible to use a template instead ?
Role version is : v2.9.1
If someone change aliases for user with alias defined earlier, then he would get a several lines for single user in aliases file. Only one line per user is valid, if you have more, then you will get something like this in postfix logs:
Jun 18 22:28:47 admin postfix/postalias[14529]: warning: /etc/aliases.db: duplicate entry: "root"
Warn about bad directory/file ownership or permissions, and create missing directories
Hi,
It would be nice to specify the map database type per option if required.
IE there is a default postfix_default_database_type
but we have the ability to overwrite if required
Something like (this works)
{% if postfix_sender_canonical_maps %}
sender_canonical_maps = {{ postfix_sender_canonical_maps_database_type | default(postfix_default_database_type) }}:{{ postfix_sender_canonical_maps_file }}
{% endif %}
{% if postfix_recipient_canonical_maps %}
recipient_canonical_maps = {{ postfix_recipient_canonical_maps_database_type | default(postfix_default_database_type) }}:{{ postfix_recipient_canonical_maps_file }}
{% endif %}
this allows us to specify
postfix_sender_canonical_maps_database_type: regexp
postfix_recipient_canonical_maps_database_type: regexp
To only override those specific map types if required
Thanks
postmap fails with this error if you run it with the regexp hash type. It's not well documented, but it seems you shouldn't run postmap when using the regexp database type.
postalias: fatal: unsupported dictionary type: regexp. Is the postfix-regexp package installed?
Running the role with this configuration:
postfix_install:
- postfix
- postfix-pcre # needed for regexp database type
- mailutils
- libsasl2-2
- sasl2-bin
- libsasl2-modules
postfix_default_database_type: regexp
postfix_recipient_canonical_maps:
- recipient: /./
rewrite: root
Ansible output:
RUNNING HANDLER [oefenweb.postfix : new aliases] *************************************************************************************************************
fatal: [catalog-harvester-next1tf.internal.sandbox.datagov.us]: FAILED! => {"changed": true, "cmd": ["newaliases"], "delta": "0:00:01.008573", "end": "2020-06-19 22:31:53.948906", "msg": "non-zero return code", "rc": 1, "start": "2020-06-19 22:31:52.940333", "stderr": "postalias: fatal: unsupported dictionary type: regexp. Is the postfix-regexp package installed?", "stderr_lines": ["postalias: fatal: unsupported dictionary type: regexp. Is the postfix-regexp package installed?"], "stdout": "", "stdout_lines": []}
Hi,
After using this role with ubuntu-focal:
mail.log:
fatal: in parameter smtpd_relay_restrictions or smtpd_recipient_restrictions, specify at least one working instance of: reject_unauth_destination, defer_unauth_destination, reject, defer, defer_if_permit or check_relay_domains
Setting postfix_smtpd_relay_restrictions to postfix's default value solves the issue:
http://www.postfix.org/postconf.5.html#smtpd_relay_restrictions
Would you accept a PR to defaults and README.md?
Below is the full error I got.
Here are the vars I had defined in the playbook:
vars:
postfix_aliases:
- user: root
alias: [email protected]
postfix_relayhost: email-smtp.us-east-1.amazonaws.com
postfix_relaytls: true
postfix_inet_interfaces: true
# AWS IAM SES credentials (not access key):
postfix_sasl_user: "REDACTED"
postfix_sasl_password: "REDACTED"
Ansible 2.10.7, running against a Ubuntu 18.04 server
FAILED! => {"changed": false, "msg": "AnsibleError: Unexpected templating type error occurred on (# {{ ansible_managed }}\n\n# See /usr/share/postfix/main.cf.dist for a commented, more complete version\n\n# Debian specific: Specifying a file name will cause the first\n# line of that file to be used as the name. The Debian default\n# is /etc/mailname.\nmyorigin = {{ postfix_mailname_file }}\n\nsmtpd_banner = {{ postfix_smtpd_banner }}\nbiff = no\n\n# appending .domain is the MUA's job.\nappend_dot_mydomain = no\n\n# Uncomment the next line to generate \"delayed mail\" warnings\n#delay_warning_time = 4h\n\nreadme_directory = no\n\n{% if postfix_compatibility_level is defined %}\ncompatibility_level = {{ postfix_compatibility_level }}\n{% endif %}\n\n# TLS parameters\nsmtpd_tls_cert_file = {{ postfix_smtpd_tls_cert_file }}\nsmtpd_tls_key_file = {{ postfix_smtpd_tls_key_file }}\nsmtpd_use_tls=yes\nsmtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache\nsmtp_tls_session_cache_database = btree:${data_directory}/smtp_scache\n\n# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for\n# information on enabling SSL in the smtp client.\n\nmyhostname = {{ postfix_hostname }}\ndefault_database_type = {{ postfix_default_database_type }}\nalias_maps = {{ postfix_default_database_type }}:{{ postfix_aliases_file }}\nalias_database = {{ postfix_default_database_type }}:{{ postfix_aliases_file }}\n{% if postfix_virtual_aliases %}\nvirtual_alias_maps = {{ postfix_default_database_type }}:{{ postfix_virtual_aliases_file }}\n{% endif %}\n{% if postfix_sender_canonical_maps %}\nsender_canonical_maps = {{ postfix_sender_canonical_maps_database_type }}:{{ postfix_sender_canonical_maps_file }}\n{% endif %}\n{% if postfix_recipient_canonical_maps %}\nrecipient_canonical_maps = {{ postfix_recipient_canonical_maps_database_type }}:{{ postfix_recipient_canonical_maps_file }}\n{% endif %}\n{% if postfix_transport_maps %}\ntransport_maps = {{ postfix_transport_maps_database_type }}:{{ postfix_transport_maps_file }}\n{% endif %}\n{% if postfix_sender_dependent_relayhost_maps %}\nsender_dependent_relayhost_maps = {{ postfix_default_database_type }}:{{ postfix_sender_dependent_relayhost_maps_file }}\n{% endif %}\n{% if postfix_smtp_generic_maps %}\nsmtp_generic_maps = {{ postfix_smtp_generic_maps_database_type }}:{{ postfix_smtp_generic_maps_file }}\n{% endif %}\n{% if postfix_header_checks %}\nsmtp_header_checks = {{ postfix_header_checks_database_type }}:{{ postfix_header_checks_file }}\n{% endif %}\nmydestination = {{ postfix_mydestination | join(', ') }}\nmynetworks = {{ postfix_mynetworks | join(' ') }}\nmailbox_size_limit = 0\nrecipient_delimiter = +\n{% if postfix_inet_interfaces is string %}\ninet_interfaces = {{ postfix_inet_interfaces }}\n{% else %}\ninet_interfaces = {{ postfix_inet_interfaces | join(', ') }}\n{% endif %}\n{% if postfix_inet_protocols is string %}\ninet_protocols = {{ postfix_inet_protocols }}\n{% else %}\ninet_protocols = {{ postfix_inet_protocols | join(', ') }}\n{% endif %}\n\n{% if postfix_relayhost %}\n{% if postfix_relayhost_mxlookup %}\nrelayhost = {{ postfix_relayhost }}:{{ postfix_relayhost_port }}\n{% else %}\nrelayhost = [{{ postfix_relayhost }}]:{{ postfix_relayhost_port }}\n{% endif %}\n{% if postfix_sasl_auth_enable %}\nsmtp_sasl_auth_enable = {{ postfix_sasl_auth_enable | bool | ternary('yes', 'no') }}\nsmtp_sasl_password_maps = {{ postfix_default_database_type }}:{{ postfix_sasl_passwd_file }}\nsmtp_sasl_security_options = {{ postfix_sasl_security_options }}\nsmtp_sasl_tls_security_options = {{ postfix_sasl_tls_security_options }}\nsmtp_sasl_mechanism_filter = {{ postfix_sasl_mechanism_filter }}\n{% endif %}\n{% if postfix_relaytls %}\nsmtp_use_tls = {{ postfix_relaytls | bool | ternary('yes', 'no') }}\nsmtp_tls_security_level = {{ postfix_smtp_tls_security_level }}\nsmtp_tls_note_starttls_offer = {{ postfix_smtp_tls_note_starttls_offer | bool | ternary('yes', 'no') }}\n{% if postfix_smtp_tls_cafile is defined %}\nsmtp_tls_CAfile = {{ postfix_smtp_tls_cafile }}\n{% endif %}\n{% endif %}\n{% else %}\nrelayhost =\n{% endif %}\n\n{% if postfix_smtpd_client_restrictions is defined %}\nsmtpd_client_restrictions = {{ postfix_smtpd_client_restrictions | join(', ') }}\n{% endif %}\n{% if postfix_smtpd_helo_restrictions is defined %}\nsmtpd_helo_restrictions = {{ postfix_smtpd_helo_restrictions | join(', ') }}\n{% endif %}\n{% if postfix_smtpd_sender_restrictions is defined %}\nsmtpd_sender_restrictions = {{ postfix_smtpd_sender_restrictions | join(', ') }}\n{% endif %}\n{% if postfix_smtpd_recipient_restrictions is defined %}\nsmtpd_recipient_restrictions = {{ postfix_smtpd_recipient_restrictions | join(', ') }}\n{% endif %}\n{% if postfix_smtpd_relay_restrictions is defined %}\nsmtpd_relay_restrictions = {{ postfix_smtpd_relay_restrictions | join(', ') }}\n{% endif %}\n{% if postfix_smtpd_data_restrictions is defined %}\nsmtpd_data_restrictions = {{ postfix_smtpd_data_restrictions | join(', ') }}\n{% endif %}\n\nmessage_size_limit = {{ postfix_message_size_limit }}\n\n# Disable the SMTP VRFY command. This stops some techniques used to harvest email addresses.\ndisable_vrfy_command = {{ postfix_disable_vrfy_command | bool | ternary('yes', 'no') }}\n\n{% for raw_option in postfix_raw_options | default([]) %}\n{{ raw_option }}\n{% endfor %}\n): 'bool' object is not iterable"}
Hi, as a follow up to #57, what do you think about adding testing via molecule, so we can easily test centos & ubuntu?
Hi,
i cannot configure either smtp_tls_security_level nor smtpd_tls_security_level.
Currently i'm usng the raw_options:
postfix_raw_options:
Is possible to support the options?
Greetings Andy
Are the values of variables postfix_sender_canonical_maps_file and postfix_recipient_canonical_maps_file swapped in vars/main.yml?
Right now they look like this:
postfix_sender_canonical_maps_file: /etc/postfix/recipient_canonical_maps postfix_recipient_canonical_maps_file: /etc/postfix/sender_canonical_maps
Should it be like this instead?
postfix_sender_canonical_maps_file: /etc/postfix/sender_canonical_maps postfix_recipient_canonical_maps_file: /etc/postfix/recipient_canonical_maps
Currently STARTTLS for outgoing email is not enabled, as seen in headers of an email sent by a Postfix server configured by ansible-postfix
:
Received: from ansible-postfix-server (ansible-postfix-server [IPv6:redacted])
by mail.redacted with ESMTP
for <redacted>;
Default should be:
smtp_tls_security_level = may
Read more: http://www.postfix.org/postconf.5.html#smtp_tls_security_level
If run the playbook with this:
postfix_sender_canonical_maps:
- sender: "root"
rewrite: "[email protected]"
and then run it again with a change in postfix_sender_canonical_maps
:
postfix_sender_canonical_maps:
- sender: "postmaster"
rewrite: "[email protected]"
the postmap
command is not launch again and and /etc/postfix/postfix_sender_canonical_maps.db
not updated with new or modified entries.
In Postfix logs we can also see
warning: database /etc/postfix/sender_canonical_maps.db is older than source file /etc/postfix/sender_canonical_maps
If I run the playbook with postfix_default_database_type: regexp
and no aliases configured. I see this warning in the postfix logs:
postfix/local[25233]: warning: regexp map /etc/aliases, line 1: ignoring unrecognized request
It looks like even though database type is set to regexp
, /etc/aliases still contains postmaster:root
as if it was a hash table.
Input:
vars:
postfix_inet_interfaces:
- 'loopback-only'
Expected behaviour:
in /etc/postfix/main.cf:
inet_interfaces = loopback-only
inet_protocols = ipv4
Actual behaviour:
in /etc/postfix/main.cf:
inet_interfaces = ['loopback-only']
inet_protocols = ['ipv4']
I have this simple configuration:
root@kuki-vm:/home/kuki/ansible/myvps# cat playbooks/postfix.yml
---
- hosts: bettingVpsOn
roles:
- postfix
vars:
postfix_mydestination:
- mydomain.cz
- '$mydomain'
- localdomain
- localhost
- localhost.localdomain
postfix_virtual_aliases:
- virtual: [email protected]
alias: [email protected]
When I run the playbook: root@kuki-VM:/home/kuki/ansible/myvps# ansible-playbook ./playbooks/postfix.yml -i ./hosts
I have this error:
ERROR! the role 'postfix' was not found in /home/kuki/ansible/myvps/playbooks/roles:/root/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:/home/kuki/ansible/myvps/playbooks
The error appears to be in '/home/kuki/ansible/myvps/playbooks/postfix.yml': line 4, column 7, but maybe elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
roles:
- postfix
^ here
How to fix it? Thanks
I tryed install: ansible-galaxy install mrlesmithjr.postfix
but still is same error
There are a number of raw variables in this role which ansible shows as being deprecated.
TASK [postfix : configure aliases] *********************************************
[DEPRECATION WARNING]: Using bare variables is deprecated. Update your playbooks so that the environment value uses the full variable syntax
('{{postfix_aliases}}'). This feature will be removed in a future release. Deprecation warnings can be disabled by setting
deprecation_warnings=False in ansible.cfg.
If you're interested, I have a patch to update the bare variables to full variable syntax master...insanity54:insanity54-rawvar-patch
After updating to current version 2.4.0 my postfix didn't correctly authenticate to its relayhost.
The reason seems to be this line in aa3a416:
postfix_sasl_mechanism_filter: plain
I got this error message in mail.log
:
relay=a.b.c[x.x.x.x]:25, delay=151584, delays=151578/0.02/6.2/0, dsn=4.7.0, status=deferred (SASL authentication failed: server a.b.c[x.x.x.x] offered no compatible authentication mechanisms for this type of connection security)
Setting postfix_sasl_mechanism_filter: # empty
in my playbook solved the problem.
Is it really a good idea to set plain
as default value for postfix_sasl_mechanism_filter
?
I'd suggest empty value as default as this is also postfix' default.
Hi Mischa,
I need the /etc/postfix/generic table mapping for our setup.
(see http://www.postfix.org/generic.5.html)
I did all the necessary adaptions, but of course I'd love those to be included in the main branch.
I'm sorry I never done that before, are you considering this and if so, how can I provide you with the adaptions?
Thanks and all the best,
Eric
Hi
recipient_canonical_maps is defined twice , in main .cf if enabled
From templates/etc/postfix/main.cf.j2
{% if postfix_recipient_canonical_maps %}
recipient_canonical_maps = {{ postfix_default_database_type }}:{{ postfix_recipient_canonical_maps_file }}
{% endif %}
{% if postfix_recipient_canonical_maps %}
recipient_canonical_maps = hash:/etc/postfix/recipient_canonical_maps
{% endif %}
I have removed the last "if" from my local copy and it is working as expected
Thanks
Hello,
most mail providers don't let you send mails with a mail sender address not identical to the used account. Therefore one has to use sender_canonical_maps and create a file with the correct sender mail address for each existing user. It would be great if this could be implemented.
Basically this line in main.cf and the corresponding file followed by the correct commands to create a postfix database:
sender_canonical_maps = hash:/etc/postfix/sender_canonical
postmap /etc/postfix/sender_canonical
/etc/init.d/postfix restart
Thank you
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.