Windows10で「OSError: raw write() returned invalid length...」的なエラーが出たら
site-packages以下にsitecustomize.pyの以下ファイルを作って置いてあげる。
import win_unicode_console win_unicode_console.enable()
このファイルは、最初に読み込んでくれるらしい。 https://docs.python.jp/3/library/site.html
win_unicode_console
自体は入れておいてあげる必要がある。
https://pypi.python.org/pypi/win_unicode_console
pip install win_unicode_console
でOK!
Minicondaとかで仮想環境作っているなら、
C:\Users\username\Miniconda3\envs\project1\Lib\site-packages
の下に上記のファイルを作ってあげればよい模様。
Conoha VPSでipv6利用時に気を付けたいPostfixの設定
ConohaのVPSは、ipv6が標準で、1個+16個ついてきて楽し気です。 が、思わずはまることもあります。
はまったこと
Postfixでipv6を有効にしていると、Gmailなんかにはipv6でつなぎに行くのですが、 その時に以下のようなエラーが記録されて送信できないことがあります。
Mar 8 06:25:09 conoha-vps postfix/cleanup[31246]: B58C1526A: message-id=<20180307212509.B1E605846@conoha-vps> Mar 8 06:25:09 conoha-vps postfix/qmgr[1703]: B58C1526A: from=<>, size=7598, nrcpt=1 (queue active) Mar 8 06:25:09 conoha-vps postfix/local[31254]: B1E605846: to=<root@conoha-vps>, relay=local, delay=0.03, delays=0.01/0/0/0.01, dsn=2.0.0, status=sent (forwarded as B58C1526A) Mar 8 06:25:11 conoha-vps postfix/smtp[31255]: B58C1526A: to=<xxxxxxxxxxx@gmail.com>, orig_to=<root@conoha-vps>, relay=gmail-smtp-in.l.google.com[2404:6800:4008:c01::1b]:25, delay=1.9, delays=0.01/0/1.2/0.7, dsn=5.7.1, status=bounced (host gmail-smtp-in.l.google.com[2404:6800:4008:c01::1b] said: 550-5.7.1 [2400:8500:1302:851:a150:95:xxx:xxx] The IP address sending this 550-5.7.1 message does not have a PTR record setup. As a policy, Gmail does not 550-5.7.1 accept messages from IPs with missing PTR records. Please visit 550-5.7.1 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.1 information. 13-v6si13594494ple.157 - gsmtp (in reply to end of DATA command)) Mar 8 06:25:11 conoha-vps postfix/qmgr[1703]: B58C1526A: removed
どうやら、ipv6のときは、IPの逆引き結果が登録されていて、なおかつ正引きと一致する必要があるようです。 標準のメインとなる1個のものに対しては一致するように登録していたのですが、+16個の方がそうではなかったので、ダメだったようです。
解決策
NICへのIP割り当てが以下のようになっていて、メインの2400:8500:1302:851:150:95:xxx:xxx
に対して適当な逆引きが設定されている(正引きと一致する)場合。
username@conoha-vps:~$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff inet 150.95.xxx.xxx/23 brd 150.95.xxx.255 scope global ens3 valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxxf/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxxe/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxxd/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxxc/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxxb/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxxa/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx9/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx8/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx7/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx6/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx5/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx4/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx3/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx2/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx1/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:a150:95:xxx:xxx0/64 scope global valid_lft forever preferred_lft forever inet6 2400:8500:1302:851:150:95:xxx:xxx/128 scope global valid_lft forever preferred_lft forever inet6 fe80::1:xxxx:xxxx:xxxx/64 scope link valid_lft forever preferred_lft forever
以下のように、/etc/postfix/main.cf
に追記してあげることで、該当のipv6アドレスからGmailサーバーなどipv6対応のメールサーバーにつなぎに行ってくれます。
smtp_bind_address6 = 2400:8500:1302:851:150:95:xxx:xxx
Chromeで旧シマンテック/ジオトラスト社のSSL証明書が信頼されたものでなくなる日に向けて...
昨年から、なんだかゴタゴタしていたやつ。 長めの期間で証明書を取得していると、再発行の該当になっているパターンがある。
信頼されなくなっちゃうパターン
ref: https://security.googleblog.com/2017/09/chromes-plan-to-distrust-symantec.html
対象1:2016年5月31日以前に発行されたSSLサーバ証明書(ラピッドSSL、クイックSSLプレミアム)
Chrome66からSSLサーバ証明書が無効化される(2018年4月17日に正式版リリース予定)
- 有効期間が2018年3月14日以前の場合は、対応不要
- 有効期間が2018年3月15日以降の場合は、2017年12月1日以降に再発行が必要
対象2:2016年6月1日2017年11月30日までに発行されたSSLサーバ証明書(ラピッドSSL、クイックSSLプレミアム、セキュア・サーバID、セキュア・サーバID EV、グローバル・サーバID)
Chrome70からSSLサーバ証明書が無効化される(2018年10月23日に正式版リリース予定)
- 有効期間が2018年9月12日以前の場合は、対応不要
- 有効期間が2018年9月13日以降の場合は、2017年12月1日以降に再発行が必要
簡易的なチェカースクリプト作った
管理対象の証明書がそれなりにあると、手作業チェックは漏れもありそうなので、確認の意味もこめて、チェックスクリプト(簡易版)を作ってみた。 Gist:kacchan822/check_cert_chrome.py 記事末尾にも載っけてある
以下の具合で、対象1または2で対応が必要だと、それぞれCRITICAL-1、CRITICAL-2と教えてくれる。対象1または2だけど、対応不要な場合も、一応WARNINGになる。
$ python3 check_cert_chrome.py www.example.com [ OK ] www.example.com: Cert was published at 2017-12-15 20:03:18. (Let's Encrypt, Let's Encrypt Authority X3) $ python3 check_cert_chrome.py www.example.com [ WARNING ] www.example.com: Cert was published at 2016-03-16 00:00:00; before 2016-05-31. (GeoTrust Inc., RapidSSL SHA256 CA) $ python3 check_cert_chrome.py www.example.com [ WARNING ] www.example.com: Cert was published at 2017-06-10 00:00:00; between 2016-06-01 and 2017-11-30. (Symantec Corporation, Symantec Class 3 Secure Server CA - G4) $ python3 check_cert_chrome.py www.example.com [ CRITICAL-1 ] www.example.com: Cert was published at 2016-03-16 00:00:00; before 2016-05-31. (GeoTrust Inc., RapidSSL SHA256 CA) $ python3 check_cert_chrome.py www.example.com [ CRITICAL-2 ] www.example.com: Cert was published at 2017-04-01 00:00:00; between 2016-06-01 and 2017-11-30. (Symantec Corporation, Symantec Class 3 Secure Server CA - G4)
すべての証明書を正しく判定できるわけではないので、あしからず…。
Gist:kacchan822/check_cert_chrome.py
#!/usr/bin/env python3 # This software is released under the MIT License. # http://opensource.org/licenses/mit-license.php import datetime import re import socket import ssl import sys timeout = 5 def get_cert(cn, port=443): """ return: dict {'OCSP': ('http://ocsp.int-x3.letsencrypt.org',), 'caIssuers': ('http://cert.int-x3.letsencrypt.org/',), 'issuer': ((('countryName', 'US'),), (('organizationName', "Let's Encrypt"),), (('commonName', "Let's Encrypt Authority X3"),)), 'notAfter': 'Mar 15 20:46:19 2018 GMT', 'notBefore': 'Dec 15 20:46:19 2017 GMT', 'serialNumber': '0467F6AD9DE0A1D776DC5688B22569094A49', 'subject': ((('commonName', 'ip.ksn.cloud'),),), 'subjectAltName': (('DNS', 'ip.ksn.cloud'), ('DNS', 'ip4.ksn.cloud'), ('DNS', 'ip6.ksn.cloud')), 'version': 3} """ context = ssl.create_default_context() try: with context.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_hostname=cn) as conn: conn.settimeout(timeout) conn.connect((cn, port)) return conn.getpeercert() except ssl.CertificateError as e: print('Errorr:', e) sys.exit(1) def _conv_dt(datetime_string): cert_date_format = '%b %d %H:%M:%S %Y %Z' return datetime.datetime.strptime(datetime_string, cert_date_format) def _get_cert_datetime(cert): return _conv_dt(cert['notBefore']), _conv_dt(cert['notAfter']) def check_issuer(cert): if cert.get('issuer'): issuer = {key_val[0][0]: key_val[0][1] for key_val in cert.get('issuer')} else: issuer = {} issuer['checkFlag'] = True if re.match(r'(Symantec|GeoTrust)', issuer.get('organizationName', '')): issuer['checkFlag'] = False if re.match(r'RapidSSL', issuer.get('commonName', '')): issuer['checkFlag'] = False return issuer def check_cert_datetime(cert): start, end = _get_cert_datetime(cert) if start < datetime.datetime(2016, 6, 1, 0, 0, 0): flag = 1 msg = 'Cert was published at {}; before 2016-05-31.' elif (datetime.datetime(2016, 5, 31, 23, 59, 59) < start and start < datetime.datetime(2017, 12, 1, 0, 0, 0)): flag = 2 msg = 'Cert was published at {}; between 2016-06-01 and 2017-11-30.' else: flag = 0 msg = 'Cert was published at {}.' return flag, msg.format(str(start)) def main(cn): try: cn, port = cn.strip().split(':') except ValueError: cn = cn.strip() port = 443 cert = get_cert(cn, int(port)) issuer = check_issuer(cert) cert_datetime = check_cert_datetime(cert) cert_start, cert_end = _get_cert_datetime(cert) if not issuer['checkFlag']: if (cert_datetime[0] == 1 and cert_end > datetime.datetime(2016, 6, 1, 0, 0, 0)): state_msg = 'CRITICAL-1' elif (cert_datetime[0] == 2 and cert_end > datetime.datetime(2018, 9, 12, 23, 59, 59)): state_msg = 'CRITICAL-2' else: state_msg = 'WARNING' else: state_msg = 'OK' message = '[{: ^12}] {}: {} ({}, {})'.format( state_msg, cn, cert_datetime[1], issuer.get('organizationName', '-'), issuer.get('commonName', '-') ) return message if __name__ == '__main__': print(main(sys.argv[1]))
参考
Django Class-based views でCSVダウンロードページの実装ではまったこと
公式ドキュメント Outputting CSV with Django | Django documentation | Django とか、GoDjangoの記事 Download CSV files via CSVResponseMixin - GoDjango とかあるからさくっと行けるだろうと思ったら、意外な落とし穴があった。
ダウンロードされるCSVファイルのファイル名をレスポンスヘッダーの"Content-Disposition"に入れてあげるのですが、Chromeさんだと特に何も考えず、
GoDjangoにある以下の書き方で、日本語名でも全く問題なく行ってくれました。
response = HttpResponse(content_type='text/csv') cd = 'attachment; filename="{0}"'.format(self.get_csv_filename()) response['Content-Disposition'] = cd
が残念ながら、Firefoxさんだとファイル名を受け取ってくれません。 Google先生に問い合わせると、先人がいらっしゃいました(PHPでの実装だけど)。
比較的最近のブラウザでは RFC 2231 形式に対応しているので、通常はこの形式を使えば問題は少ないだろう。
ということなので、 RFC 2231 - MIME Parameter Value and Encoded Word Extensions: Character Sets, Languages, and Continuations を読んだつもりになって、「python rfc2231」とかってググると、 19.1.14. email.utils: 多方面のユーティリティ — Python 3.6.5 ドキュメント 使っとけって出てくるので、使ってみたら無事解決。
たぶん使いまわしがきくであろうMixiにしておいてみた。
UTF-8なCSVをうまくExcelさんに読んでもらうためには、BOM付UTF-8にしないといけないらしく、それでも微妙にはまった(というのも↑Mixinは対応)。エンコーディング問題というのはどこへ行っても頭を悩ませる種ですね。。。