- 投稿日:2020-10-24T23:41:51+09:00
AtCoder Regular Contest 106のメモ
前置き
Atcoderをやってみたので、自分用のメモです。
あとから加筆・修正する予定です。問題
https://atcoder.jp/contests/arc106
A
Q_A.gopackage main import ( "fmt" ) func main() { var n int64 fmt.Scanf("%d", &n) var count_a int64 = 0 var count_b int64 = 0 var a, b int64 flag := true for a=3; a<=n; a=a*3{ count_a += 1 count_b = 0 for b=5; b<n-a+1; b=b*5{ count_b += 1 if (a+b == n) && flag{ flag = false fmt.Printf("%d %d\n", count_a, count_b) break } } } if flag{ fmt.Printf("%d\n", -1) } }B
覚えてたら後で書きます。
C
覚えてたら後で書きます。
D
覚えてたら後で書きます。
E
覚えてたら後で書きます。
F
覚えてたら後で書きます。
- 投稿日:2020-10-24T21:33:10+09:00
Golangのos/execパッケージを利用するときのCD
- たとえば、2つ上の階層にあるディレクトリにあるMakefileを実行したいとき
os.Chdir("./../../") cmd := exec.Command("make", "ENV_FILE=.env", "DOMAIN_NAME=vamdemic")
- 投稿日:2020-10-24T18:15:01+09:00
ポインタについてのメモ(Go)
ポインタがどういうものなのか分からなかったので調べた。
今回はGoで書いた。pointer_practice.gopackage main import ( "fmt" "math/rand" "time" ) func main() { x := 2 /* 変数xのアドレス(変数xが保存されているメモリの位置)を格納 */ /* ある変数のアドレスが格納された変数のことをポインタ変数という */ x_pointer := &x fmt.Println(x) fmt.Println(x_pointer) /* ポインタ変数x_pointerに格納されたアドレスに存在する値を取得 */ /* メモリをjsのオブジェクト、アドレスをオブジェクトのキーと考えると メモリ[アドレス(x_pointer)] = 2 というイメージ */ fmt.Println(*x_pointer) arg := 0 arg_pointer := &arg test_pointer(arg_pointer) /* 同じ結果になる */ fmt.Println(arg) fmt.Println(*arg_pointer) /* 値は書き換えられない(関数内では書き換えられるが、引数として設定した変数の値に影響はない) */ /* returnで関数内での書き換え結果を取得する */ test_val(arg) fmt.Println(arg) } /* ポインタ変数を引数として受け取る関数(ポインタ渡し) */ func test_pointer(val_pointer *int) { /* 時間を用いてseed(乱数のもと?)を作成 Seedがないと毎回同じ値を取得してしまう */ rand.Seed(time.Now().UnixNano()) /* ポインタ変数に格納されたアドレスを取得 -> 値の書き換えを行うメモリの位置をアドレスで指定 -> 指定したメモリの位置に保存されている値を0~999のランダムな数字に書き換える */ *val_pointer = rand.Intn(1000) } /* 値渡しの関数(いつもの) */ func test_val(val int) { rand.Seed(time.Now().UnixNano()) val = rand.Intn(1000) }値渡し(変数の値を受け取る)の関数とポインタ渡し(変数のアドレスを受け取る)の関数とでは、引数の型の設定方法が異なる。
/* 値渡しの関数 */ func(引数 引数の型) { /* 関数の処理 */ } /* ポインタ渡しの関数 型名の前に「*」が付く*/ func(引数 *引数の型) { /* 関数の処理 */ }
- 投稿日:2020-10-24T13:39:43+09:00
GolangでPKI入門 - 4
1. この記事の対象の人
- Golang で、証明書失効リスト( CRL )を作りたい人
2. 概要
この記事では、
1. Go で秘密鍵と証明書を生成
2. Go で失効させる証明書のリストを生成
3. Go で Issuing Distribution Point のExtensionを作成
4. Go で証明書失効リスト( CRL )を作成
5. OpenSSL で証明書失効リスト( CRL )の中身を確認
します。3. Golang で自己署名 CA 証明書と秘密鍵を作成
証明書失効リストを発行する自己署名 CA の「証明書」と「秘密鍵」を作成します。
詳細な説明は、GolangでPKI入門 - 2 を参照ください。
証明書失効リスト作成時に引数で必要になるので、秘密鍵はDER形式にしておきます。//PrivateKey of Self Sign CA Certificate privateCaKey, err := rsa.GenerateKey(rand.Reader, 2048) publicCaKey := privateCaKey.Public() //[RFC5280] subjectCa := pkix.Name{ CommonName: "ca01", OrganizationalUnit: []string{"Example Org Unit"}, Organization: []string{"Example Org"}, Country: []string{"JP"}, } caTpl := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: subjectCa, NotAfter: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC), NotBefore: time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC), IsCA: true, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign, BasicConstraintsValid: true, } //Self Sign CA Certificate caCertificate, err := x509.CreateCertificate(rand.Reader, caTpl, caTpl, publicCaKey, privateCaKey) //Convert to ASN.1 DER encoded form derCaCert, err = x509.ParseCertificate(caCertificate) if err != nil { log.Fatalf("ERROR:%v\n", err) }4. Golang で証明書失効リストを作成
失効させる証明書のリストを作成
var rcs []pkix.RevokedCertificate rc := pkix.RevokedCertificate{ SerialNumber: big.NewInt(100), RevocationTime: time.Now(), } rcs = append(rcs, rc) rc = pkix.RevokedCertificate{ SerialNumber: big.NewInt(108), RevocationTime: time.Now(), } rcs = append(rcs, rc)ここでは、シリアルが100と108の証明書を失効させます。
証明書失効リストの crlExtensions に Issuing Distribution Point を追加
Go で証明書失効リストを作成するときに利用する x509.RevocationList には、直接 Issuing Distribution Point を追加するFieldはありません。
別途 Issuing Distribution Point 用の構造体を作成して、Extensionに追加してやる必要があります。
RFC5280 では、Issuing Distribution Pointは以下のように定義されています。id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } IssuingDistributionPoint ::= SEQUENCE { distributionPoint [0] DistributionPointName OPTIONAL, onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, onlySomeReasons [3] ReasonFlags OPTIONAL, indirectCRL [4] BOOLEAN DEFAULT FALSE, onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } DistributionPointName ::= CHOICE { fullName [0] GeneralNames, nameRelativeToCRLIssuer [1] RelativeDistinguishedName } GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName GeneralName ::= CHOICE { otherName [0] OtherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER }上記に従い、issuingDistributionPoint と distributionPointName を 以下の Go の構造体として定義しました。
// RFC5280, 5.2.5 type issuingDistributionPoint struct { DistributionPoint distributionPointName `asn1:"optional,tag:0"` OnlyContainsUserCerts bool `asn1:"optional,tag:1"` OnlyContainsCACerts bool `asn1:"optional,tag:2"` OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"` IndirectCRL bool `asn1:"optional,tag:4"` OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"` } type distributionPointName struct { FullName []asn1.RawValue `asn1:"optional,tag:0"` RelativeName pkix.RDNSequence `asn1:"optional,tag:1"` }distributionPointName の FullName フィールドの型は GeneralNames です。
GeneralName の uniformResourceIdentifier で証明書失効リストの取得先を指定したいので、
asn1.RawValue 型で以下のように設定
Class: 2
Context-specific ( asn1.RawValue の定義による)
Tag: 6
GeneralName の6番目つまり uniformResourceIdentifier
Bytes: []byte("http://www.example.com/example.crl")
uniformResourceIdentifier の エンコーディングは IA5String です。ただ、crl への URI で使われている範囲の文字列は IA5String と UTF8 で同じbyteになるので直接 byte 配列として渡しています。dp := distributionPointName{ FullName: []asn1.RawValue{ {Tag: 6, Class: 2, Bytes: []byte("http://www.example.com/example.crl")}, }, }Extension に作成した IssuingDistributionPoint を設定します。
var oidExtensionIssuingDistributionPoint = []int{2, 5, 29, 28} idp := issuingDistributionPoint{ DistributionPoint: dp, } v, err := asn1.Marshal(idp) cdpExt := pkix.Extension{ Id: oidExtensionIssuingDistributionPoint, Critical: true, Value: v, }x509.RevocationList 構造体の設定
x509.RevocationList 構造体に設定したい値を入れていきます。
crlTpl := &x509.RevocationList{ SignatureAlgorithm: x509.SHA256WithRSA, RevokedCertificates: rcs, Number: big.NewInt(2), ThisUpdate: time.Now(), NextUpdate: time.Now().Add(24 * time.Hour), ExtraExtensions: []pkix.Extension{cdpExt}, }証明書失効リストを作成
証明書失効リストを発行します
var derCrl []byte derCrl, err = x509.CreateRevocationList(rand.Reader, crlTpl, derCaCert, privateCaKey) if err != nil { log.Fatalf("ERROR:%v\n", err) } f, err = os.Create("ca01.crl") if err != nil { log.Fatalf("ERROR:%v\n", err) } err = pem.Encode(f, &pem.Block{Type: "X509 CRL", Bytes: derCrl}) if err != nil { log.Fatalf("ERROR:%v\n", err) } err = f.Close()5. 証明書失効リストを確認する
発行した証明書失効リストを Openssl で確認します。設定した要素がすべて入っていますね。
$ openssl crl -inform pem -in example.crl -text Certificate Revocation List (CRL): Version 2 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: C = JP, O = Example Org, OU = Example Org Unit, CN = ca01 Last Update: Oct 24 04:16:04 2020 GMT Next Update: Oct 25 04:16:04 2020 GMT CRL extensions: X509v3 Authority Key Identifier: keyid:0A:42:8D:9B:23:A9:77:11:FF:FD:0F:CC:58:F4:36:F4:98:06:7F:28 X509v3 CRL Number: 2 X509v3 Issuing Distribution Point: critical Full Name: URI:http://www.example.com/example.crl Revoked Certificates: Serial Number: 64 Revocation Date: Oct 24 04:16:04 2020 GMT Serial Number: 6C Revocation Date: Oct 24 04:16:04 2020 GMT Signature Algorithm: sha256WithRSAEncryption 6c:0d:23:e8:50:bf:84:ae:10:85:3e:43:28:0f:43:fd:58:cb: 83:8c:7c:a8:5c:7d:78:71:f1:0c:03:97:43:88:8c:32:02:5c: a6:6c:e2:a4:7d:94:56:08:a8:9c:17:95:b4:be:11:bb:65:52: 43:25:de:c0:d5:d0:df:ac:0f:ca:8c:a7:23:82:19:12:e2:9d: 49:83:9e:ca:bc:2e:f3:60:79:39:47:cb:ed:17:52:25:9f:42: 26:9e:1b:67:5f:af:e1:3a:14:67:5f:4f:de:10:c5:32:03:7f: 40:a0:b6:bc:3f:05:33:73:91:0b:73:4e:f2:3c:be:b0:e4:63: e0:d0:81:6e:91:14:d9:04:35:21:3e:22:1e:31:bd:47:40:c9: 69:f0:e5:57:bc:c3:2c:ae:b8:06:38:35:f1:59:6f:45:2c:45: 08:2e:63:49:ab:f5:54:0b:54:d2:a8:fc:62:ea:a5:46:62:28: a9:89:76:96:cf:47:28:3d:81:c3:e9:fb:ce:54:a8:07:71:6d: c6:d8:b7:e7:33:b0:05:df:c4:79:56:e1:99:ed:9f:33:f8:15: b9:32:4e:82:4c:0c:a7:a5:23:d4:f7:e1:94:26:2b:e0:55:1a: 38:f6:72:21:a9:e0:29:06:80:9a:05:e3:43:c2:4a:dd:74:c6: d6:79:ec:9d -----BEGIN X509 CRL----- MIICKDCCARACAQEwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCSlAxFDASBgNV BAoTC0V4YW1wbGUgT3JnMRkwFwYDVQQLExBFeGFtcGxlIE9yZyBVbml0MQ0wCwYD VQQDEwRjYTAxFw0yMDEwMjQwNDE2MDRaFw0yMDEwMjUwNDE2MDRaMCgwEgIBZBcN MjAxMDI0MDQxNjA0WjASAgFsFw0yMDEwMjQwNDE2MDRaoGUwYzAfBgNVHSMEGDAW gBQKQo2bI6l3Ef/9D8xY9Db0mAZ/KDAKBgNVHRQEAwIBAjA0BgNVHRwBAf8EKjAo oCagJIYiaHR0cDovL3d3dy5leGFtcGxlLmNvbS9leGFtcGxlLmNybDANBgkqhkiG 9w0BAQsFAAOCAQEAbA0j6FC/hK4QhT5DKA9D/VjLg4x8qFx9eHHxDAOXQ4iMMgJc pmzipH2UVgionBeVtL4Ru2VSQyXewNXQ36wPyoynI4IZEuKdSYOeyrwu82B5OUfL 7RdSJZ9CJp4bZ1+v4ToUZ19P3hDFMgN/QKC2vD8FM3ORC3NO8jy+sORj4NCBbpEU 2QQ1IT4iHjG9R0DJafDlV7zDLK64Bjg18VlvRSxFCC5jSav1VAtU0qj8YuqlRmIo qYl2ls9HKD2Bw+n7zlSoB3Ftxti35zOwBd/EeVbhme2fM/gVuTJOgkwMp6Uj1Pfh lCYr4FUaOPZyIangKQaAmgXjQ8JK3XTG1nnsnQ== -----END X509 CRL-----6. コード
- 投稿日:2020-10-24T00:12:42+09:00
【Golang】Alpine Docker で実行すると error: stdlib.h: No such file or directory. "stdlib.h" が足りないと言われる
golang:alpine
の Docker イメージでgo run
やgo test
を実行するとstdlib.h: No such file or directory
のfatal
エラーで叱られる。「"golang" "alpine" fatal error: stdlib.h: No such file or directory」とググっても日本語で情報が出てこなかったので。
$ go test ./... Testing main package go: downloading ... ... # runtime/cgo exec: "gcc": executable file not found in $PATH FAIL github.com/KEINOS/Sample [build failed] FAIL github.com/KEINOS/Sample/hoge [build failed]
go version
: go version go1.15.3 linux/amd64uname -a
: Linux a7325e2dcf0f 4.19.76-linuxkit #1 SMP Tue May 26 11:42:35 UTC 2020 x86_64 Linuxcat /etc/os-release | grep PRETTY_NAME
: PRETTY_NAME="Alpine Linux v3.12"TL; DR
ミニマル・バージョン# Minimum, at-least-to-install packages to run/build apk add --no-cache gcc musl-dev横着バージョン(上記も含まれています)# Base meta-package to run/build (This includes the above packages as well) apk add --no-cache build-baseAlpine上で開発するなら、とりま入れておけパック# For Dev, better to be installed packages apk add --no-cache alpine-sdk build-baseTS; DR(上記に至るまで勉強したこと)
とあるマシンで Go言語(以下 Golang)のコードを触ってコンパイる必要があったのですが、Golang がインストールされていませんでした。しかも別途インストールすることが許されず。。。
しかし、Docker はインストールされていたので
golang:alpine
のイメージで試そうと思いました。ローカルのカレントディレクトリをコンテナの/goディレクトリにマウントしながら起動$ # Docker でマウントを取ってみる $ cd /path/to/the/repo $ docker run --rm -it -v $(pwd):/go golang:alpine /bin/sh ... /go #
$GOPATH/go.mod exists but should not
エラーまずはテストの実行です。動きません。
/go # go test main.go ./... $GOPATH/go.mod exists but should not /go # go mod download $GOPATH/go.mod exists but should not /go # go mod verify $GOPATH/go.mod exists but should not /go # go env $GOPATH/go.mod exists but should not /go # env HOSTNAME=xxxxxxxxxxxx SHLVL=1 HOME=/root TERM=xterm PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin GOPATH=/go PWD=/go GOLANG_VERSION=1.15.3
mod
がインストールされていないからか、と思えば違うようです。どうやら上記エラーはGOPATH
モードとモジュール・モードに関係しているようです。
- 参考文献
- Go: DepからGo Modulesへの移行 @ Qiita
確かに環境変数に
GO111MODULE
がありませんでした。そのためGOPATH
モードになっているからなのかと思いGO111MODULE=on
で「常にモジュール対応モードで動作する」ように設定して見たのですが、残念ながらダメでした。GO111MODULE=onで起動$ docker run --rm -it -v $(pwd):/go -e GO111MODULE=on golang:alpine /bin/sh /go # /go # echo $GO111MODULE on /go # go env $GOPATH/go.mod exists but should notGO v1.15 なので、そもそもデフォルトで
GO111MODULE
はauto
になっているようで、以下のリンクによれば回避策は2つ。
- 参考文献
/go
以外の別のディレクトリにソースコードをマウントして実行する。export GOPATH=
と環境変数の設定値を「空」にセットしてユーザーディレクトリ下にキャッシュさせる。原因はモジュール・モードなのに
GOPATH
で指定されたディレクトリに開発用のmod.go
付きのソースコードを置いたことでした。モジュール・モードの場合は
GOPATH
で指定されたディレクトリの下にモジュールをキャッシュして行きます。そのため、そのキャッシュ・ディレクトリにgo.mod
などの余計なファイルがあったことがエラーの原因でした。確かに「$GOPATH/go.mod exists but should not
」と「$GOPATH/go.mod
があるけど禁則事項です」と言ってます。とりあえず、
/go
でなく/app
にマウントすることにしました。ローカルのカレントディレクトリを/appにマウントしながら起動$ cd /path/to/the/repo $ docker run --rm -it -v $(pwd):/app golang:alpine /bin/sh /go # /go # # マウントしたディレクトリに移動 /go # cd /app /app # /app # # 今度は go env が表示された。GO111MODULEは空なので auto ∴ モジュールモード /app # go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build030913204=/tmp/go-build -gno-record-gcc-switches"
"gcc": executable file not found in $PATH
エラーそして気を取り直して、再テストです。先へ進んだものの、動きません。
テストの実行/app # go test ./... go: downloading github.com/... go: downloading github.com/... ... # runtime/cgo exec: "gcc": executable file not found in $PATH FAIL github.com/KEINOS/Hello-Cobra [build failed] FAIL github.com/KEINOS/Hello-Cobra/hoge [build failed]なんか
cgo
が怒っています。gcc
がない、と。えーcgo
使ってないんだけどなぁ。依存パッケージが使ってるのかしら。
- cgoを使ったCとGoのリンクの裏側 (1) @ Qiita
とりあえず
gcc
をインストールして再度テストして見ます。gccのインストール/app # # gcc 入ってない /app # gcc --version /bin/sh: gcc: not found /app # /app # # gcc 入れる /app # apk add --no-cache gcc ... /app # # gcc 入った /app # gcc --version gcc (Alpine 9.3.0) 9.3.0再テスト/app # go test ./... # runtime/cgo _cgo_export.c:3:10: fatal error: stdlib.h: No such file or directory 3 | #include <stdlib.h> | ^~~~~~~~~~ compilation terminated. FAIL github.com/KEINOS/Hello-Cobra [build failed] FAIL github.com/KEINOS/Hello-Cobra/hoge [build failed]やはり、どこかで
cgo
を使っているらしく、叱られます。今度はstdlib.h
が足りないら C です。どうやら依存パッケージがcgo
を使っているぽいです。GitHub にある Golang の Docker のリポジトリに Issue が上がってました。Alpine の
cgo
はmusl-dev
が必要らしいです。そういや、Alpine 系の Issue で良く見る。try adding
musl-dev
Not sure whygcc
doesnt depend on it.
(CGO does not seem to work on golang:1.6-alpine #86 @ GitHub より)それでは、再度テストをしてみます。動きました。
/app # # musl-dev 入れる /app # apk add --no-cache musl-dev ... /app # # テストする /app # go test ./... ok github.com/KEINOS/Sample 0.003s ok github.com/KEINOS/Sample/hoge 0.006s /app # # ?Yeah, the alpine images are designed to be minimal.
(CGO does not seem to work on golang:1.6-alpine #86 @ GitHub より)「そうさ。Alpine イメージは必要最低限になるように設計されてるのさ」と、ドキュメントを嫁と言ってます。
The main caveat to note is that it does use
musl libc
instead ofglibc
and friends, which can lead to unexpected behavior.
...
To minimize image size, additional related tools (such asgit
,gcc
, orbash
) are not included in Alpine-based images. Using this image as a base, add the things you need in your own Dockerfile (see thealpine
image description for examples of how to install packages if you are unfamiliar).
(golang:-alpine
| Quick reference | Golang | docker-library @ GitHub より)「何はともあれ忠告しておくと、Alpine は、
glibc
とその愉快な仲間たちの代わりに、挙動が読めないmusl libc
を使っているからね。」と、あります。そして、サイズを極力小さくするためgit
とかgcc
とかbash
すら入れていない、と。そういえば、そうでした。Alpine はコンテナ向けなので何も入っていないので、Dockerfile で必要な物を入れてあげて使う物だったのを失念しておりました。
「なんか、あれこれ
apk
パッケージを探してインストールするのも面倒だなぁ」と Ubuntu のとりあえず入れておけパック「build-essential
」みたいなものがないかググったら、「ビルドによく使われるものを集めたパッケージ」と「開発によく使われるものを集めたパッケージ」があるらしい。ビルドに必要な良くあるものパックapk add build-base開発に必要な良くあるものパックapk add alpine-sdk
- What is the alpine equivalent to build-essential? #24 | docker-alpine | gliderlabs @ GitHub
確かに、
gcc
やmusl-dev
を入れなくてもbuild-base
でいけた。参考文献
- GOPATH モードからモジュール対応モードへ移行せよ @ Qiita
- cgoを使ったCとGoのリンクの裏側 (1) @ Qiita
- [Golang]$GOPATH/go.mod exists but should notを回避する @ Selfnote
- When trying to build docker image, I get “gcc“: executable file not found in $PATH” @ StackOverflow
- CGO does not seem to work on golang:1.6-alpine #86 @ GitHub
- What is the alpine equivalent to build-essential? #24 | docker-alpine | gliderlabs @ GitHub
- Getting GOPATH error “go: cannot use path@version syntax in GOPATH mode” in Ubuntu 16.04 @ StackOverflow